diff options
-rw-r--r-- | server/piztor/mesg_sender.py | 1 | ||||
-rw-r--r-- | server/piztor/model.py | 61 | ||||
-rw-r--r-- | server/piztor/ptp.rst | 422 | ||||
-rw-r--r-- | server/piztor/server.py | 65 |
4 files changed, 310 insertions, 239 deletions
diff --git a/server/piztor/mesg_sender.py b/server/piztor/mesg_sender.py index e3ad6e4..e9135bd 100644 --- a/server/piztor/mesg_sender.py +++ b/server/piztor/mesg_sender.py @@ -21,3 +21,4 @@ if len(argv) == 4: uid, token = user_auth(username, password) send_text_mesg(token, username, mesg) +logout(token, username) diff --git a/server/piztor/model.py b/server/piztor/model.py index 41b9a88..460aa38 100644 --- a/server/piztor/model.py +++ b/server/piztor/model.py @@ -1,4 +1,5 @@ -from sqlalchemy import Column, Integer, String, Float, ForeignKey, Boolean +from sqlalchemy import Table, Column +from sqlalchemy import Integer, String, Float, ForeignKey, Boolean from sqlalchemy.dialects.mysql import BLOB, TINYINT from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relationship, backref @@ -11,24 +12,46 @@ MAX_USERNAME_SIZE = 20 MAX_PASSWORD_SIZE = 20 _table_typical_settings = { - 'mysql_engine' : 'InnoDB', - 'mysql_charset' : 'utf8', + 'mysql_engine' : 'InnoDB', + 'mysql_charset' : 'utf8', 'mysql_auto_increment' : '1'} class _TableName: # avoid typoes UserModel = 'users' LocationInfo = 'location_info' UserAuth = 'user_auth' + CompanyInfo = 'comp_info' + SectionInfo = 'sec_info' + CompanySub = 'comp_sub' + SectionSub = 'sec_sub' + +comp_sub_assoc = Table(_TableName.CompanySub, Base.metadata, + Column('uid', Integer, ForeignKey(_TableName.UserModel + '.id')), + Column('comp_id', Integer, ForeignKey(_TableName.CompanyInfo + '.id'))) + +sec_sub_assoc = Table(_TableName.SectionSub, Base.metadata, + Column('uid', Integer, ForeignKey(_TableName.UserModel + '.id')), + Column('sec_id', Integer, ForeignKey(_TableName.SectionInfo + '.id'))) class UserModel(Base): __tablename__ = _TableName.UserModel __table_args__ = _table_typical_settings id = Column(Integer, primary_key = True) - sec_id = Column(TINYINT) - comp_id = Column(TINYINT) - username = Column(String(MAX_USERNAME_SIZE), + username = Column(String(MAX_USERNAME_SIZE), unique = True, nullable = False) + sex = Column(Boolean, nullable = False) + sec_no = Column(TINYINT) + comp_id = Column(TINYINT, ForeignKey(_TableName.CompanyInfo + '.id')) + sec_id = Column(TINYINT, ForeignKey(_TableName.SectionInfo + '.id')) + + comp_sub = relationship(_TableName.CompanyInfo, + secondary = comp_sub_assoc, + backref = "subscribers") + sec_sub = relationship(_TableName.SectionInfo, + secondary = sec_sub_assoc, + backref = "subscribers") + location = None auth = None sec = None @@ -37,11 +60,11 @@ class LocationInfo(Base): __tablename__ = _TableName.LocationInfo __table_args__ = _table_typical_settings - uid = Column(Integer, ForeignKey(_TableName.UserModel + '.id'), + uid = Column(Integer, ForeignKey(_TableName.UserModel + '.id'), primary_key = True) lat = Column(Float(precesion = 64), nullable = False) lng = Column(Float(precesion = 64), nullable = False) - user = relationship("UserModel", uselist = False, + user = relationship("UserModel", uselist = False, backref = backref("location", uselist = False, cascade = "all, delete-orphan")) @@ -68,7 +91,7 @@ class UserAuth(Base): salt = Column(BLOB) token = Column(BLOB) - user = relationship("UserModel", uselist = False, + user = relationship("UserModel", uselist = False, backref = backref("auth", uselist = False, cascade = "all, delete-orphan")) @@ -92,3 +115,23 @@ class UserAuth(Base): def get_token(self): return self.token + +class CompanyInfo(Base): + __tablename__ = _TableName.CompanyInfo + __table_args__ = _table_typical_settings + + id = Column(TINYINT, primary_key = True) + marker_lat = Column(Float(precesion = 64), nullable = False) + market_lng = Column(Float(precesion = 64), nullable = False) + + subscribers = None + +class SectionInfo(Base): + __tablename__ = _TableName.SectionInfo + __table_args__ = _table_typical_settings + + id = Column(TINYINT, primary_key = True) + marker_lat = Column(Float(precesion = 64), nullable = False) + market_lng = Column(Float(precesion = 64), nullable = False) + + subscribers = None diff --git a/server/piztor/ptp.rst b/server/piztor/ptp.rst index 1dc4f66..19eab65 100644 --- a/server/piztor/ptp.rst +++ b/server/piztor/ptp.rst @@ -1,224 +1,242 @@ -Piztor Transmission Protocol v1.0 +Piztor Transmission Protocol v1.0b ---------------------------------- -- General - - - Request - - :: - - +---4b---+---1b---+-------?b--------+ - | LENGTH | OPT_ID | SPECIFIC DATA | - +--int---+-uchar--+-----------------+ - - - Response - - :: - - +---4b---+---1b---+------?b---------+ - | LENGTH | OPT_ID | SPECIFIC DATA | - +--int---+-uchar--+-----------------+ - - Notice: - - - In following sections, ``LENGTH`` part is left out for clarity. - - ``PADDING`` has value ``0``. - - ``string`` type structure: +- Pull + - General + + - Request + :: - - +-------?b-------+---------+ - | STRING_CONTENT | PADDING | - +----------------+---------+ - - - ``AUTH_HEAD`` structure: - + + +---4b---+---1b---+-------?b--------+ + | LENGTH | OPT_ID | SPECIFIC_DATA | + +--int---+-uchar--+-----------------+ + + - Response + :: + + +---4b---+---1b---+------?b---------+ + | LENGTH | OPT_ID | SPECIFIC_DATA | + +--int---+-uchar--+-----------------+ + + Notice: + + - In following sections, ``LENGTH`` part is left out for clarity. + - ``PADDING`` has value ``0``. + - ``string`` type structure: + + :: + + +-------?b-------+---------+ + | STRING_CONTENT | PADDING | + +----------------+---------+ + + - ``AUTH_HEAD`` structure: + + :: + + +----32b-----+----?b----+ + | USER_TOKEN | USERNAME | + +----raw-----+--string--+ + + - Authentication + + - Request + + :: + + +--1b---+-----?b---+----?b----+ + | 0x00 | USERNAME | PASSWORD | + +-uchar-+--string--+--string--+ + + - Response + + :: + + +--1b---+---1b---+---4b----+----32b-----+ + | 0x00 | STATUS | USER_ID | USER_TOKEN | + +-uchar-+--uchar-+---int---+----raw-----+ + + ``STATUS`` : + + - ``0x00`` for success + - ``0x01`` for failure + + - Location Update + + - Request + + :: + + +--1b---+-----?b------+----8b------+------8b-----+ + | 0x01 | AUTH_HEAD | LATITUDE | LONGITUDE | + +-uchar-+-------------+---double---+---double----+ + + - Response + + :: + + +--1b---+---1b---+ + | 0x01 | STATUS | + +-uchar-+--uchar-+ + + ``STATUS`` : + + - ``0x00`` for success + - ``0x01`` for invalid token + + - Location Information + + - Request + + :: + + +--1b---+------?b------+------2b-----+ + | 0x02 | AUTH_HEAD | GROUP_ID | + +-uchar-+--------------+-------------+ + + ``GROUP_ID``: + + :: + + +---1b----+---1b---+ + | COMP_ID | SEC_ID | + +--uchar--+-uchar--+ + + - Response + + :: + + +--1b---+---1b---+------20b-------+-----+ + | 0x02 | STATUS | LOCATION_ENTRY | ... | + +-uchar-+-uchar--+----------------+-----+ + + ``LOCATION_ENTRY`` : + + :: + + +---4b----+----8b----+-----8b----+ + | USER_ID | LATITUDE | LONGITUDE | + +---int---+--double--+--double---+ + + - User Information + + - Request + + :: + + +--1b---+------?b------+------4b-----+ + | 0x03 | AUTH_HEAD | USER_ID | + +-uchar-+--------------+-----int-----+ + + - Response + + :: + + +--1b---+---1b---+------?b-----+-----+ + | 0x03 | STATUS | UINFO_ENTRY | ... | + +-uchar-+-uchar--+-------------+-----+ + + ``UINFO_ENTRY`` : + + :: + + +----1b----+-----?b-----+ + | INFO_KEY | INFO_VALUE | + +--uchar---+------------+ + + ``INFO_KEY`` : + + :``0x00``: gid (value is a 2-byte ``GROUP_ID``) + :``0x01``: sex (value is a 1-byte ``boolean``: ``0x01`` for male, ``0x00`` for female) + + - User Logout + + - Request + + :: + + +--1b--+-----?b------+ + | 0x04 | AUTH_HEAD | + +------+-------------+ + + - Response + + :: + + +--1b--+---1b---+ + | 0x04 | STATUS | + +------+--------+ + + - Open Push Tunnel + + - Request + + :: + + +--1b--+-----?b------+ + | 0x05 | AUTH_HEAD | + +------+-------------+ + + - Response + + :: + + +--1b--+---1b---+ + | 0x05 | STATUS | + +------+--------+ + + - Send Text Message + + - Request + + :: + + +--1b--+----?b-----+----?b----+ + | 0x06 | AUTH_HEAD | MESSAGE | + +------+-----------+--string--+ + + - Response + + :: + + +--1b--+---1b---+ + | 0x06 | STATUS | + +------+--------+ - +----32b-----+----?b----+ - | USER_TOKEN | USERNAME | - +----raw-----+--string--+ - -- Authentication - - - Request - - :: - - +--1b---+-----?b---+----?b----+ - | 0x00 | USERNAME | PASSWORD | - +-uchar-+--string--+--string--+ +- Push Notification - - Response + - General Request :: - - +--1b---+---1b---+---4b----+----32b-----+ - | 0x00 | STATUS | USER_ID | USER_TOKEN | - +-uchar-+--uchar-+---int---+----raw-----+ - - ``STATUS`` : - - - ``0x00`` for success - - ``0x01`` for failure - -- Location Update - - Request + +---1b---+-------32b--------+-------?b------+ + | OPT_ID | PUSH_FINGERPRINT | SPECIFIC_DATA | + +--------+------------------+---------------+ - :: - - +--1b---+-----?b------+----8b------+------8b-----+ - | 0x01 | AUTH_HEAD | LATITUDE | LONGITUDE | - +-uchar-+-------------+---double---+---double----+ - - - Response + - Acknowledgement :: - +--1b---+---1b---+ - | 0x01 | STATUS | - +-uchar-+--uchar-+ - - ``STATUS`` : - - - ``0x00`` for success - - ``0x01`` for invalid token - -- Location Information - - - Request + +---1b---+-------32b---------+ + | OPT_ID | PUSH_FINGERPRINT | + +--------+-------------------+ + + - Text Message :: - +--1b---+------?b------+------2b-----+ - | 0x02 | AUTH_HEAD | GROUP_ID | - +-uchar-+--------------+-------------+ + ----+----?b----+ + ... | MESSAGE | + ----+--string--+ - ``GROUP_ID``: + - User Location Update :: - +---1b----+---1b---+ - | COMP_ID | SEC_ID | - +--uchar--+-uchar--+ - - - Response - - :: - - +--1b---+---1b---+------20b-------+-----+ - | 0x02 | STATUS | LOCATION_ENTRY | ... | - +-uchar-+-uchar--+----------------+-----+ - - ``LOCATION_ENTRY`` : - - :: - - +---4b----+----8b----+-----8b----+ - | USER_ID | LATITUDE | LONGITUDE | - +---int---+--double--+--double---+ - -- User Information - - - Request - - :: - - +--1b---+------?b------+------4b-----+ - | 0x03 | AUTH_HEAD | USER_ID | - +-uchar-+--------------+-----int-----+ - - - Response - - :: - - +--1b---+---1b---+------?b-----+-----+ - | 0x03 | STATUS | UINFO_ENTRY | ... | - +-uchar-+-uchar--+-------------+-----+ - - ``UINFO_ENTRY`` : - - :: - - +----1b----+-----?b-----+ - | INFO_KEY | INFO_VALUE | - +--uchar---+------------+ - - ``INFO_KEY`` : - - :``0x00``: gid (value is a 2-byte ``GROUP_ID``) - :``0x01``: sex (value is a 1-byte ``boolean``: ``0x01`` for male, ``0x00`` for female) - -- User Logout - - - Request - - :: - - +--1b--+-----?b------+ - | 0x04 | AUTH_HEAD | - +------+-------------+ - - - Response - - :: - - +--1b--+---1b---+ - | 0x04 | STATUS | - +------+--------+ - -- Open Push Tunnel - - - Request - - :: - - +--1b--+-----?b------+ - | 0x05 | AUTH_HEAD | - +------+-------------+ - - - Response - - :: - - +--1b--+---1b---+ - | 0x05 | STATUS | - +------+--------+ - -- Send Text Message - - - Request - - :: - - +--1b--+----?b-----+----?b----+ - | 0x06 | AUTH_HEAD | MESSAGE | - +------+-----------+--string--+ - - - Response - - :: - - +--1b--+---1b---+ - | 0x06 | STATUS | - +------+--------+ - -- Push - - - Acknowledgement - - :: - - +--1b--+-------32b---------+ - | 0x00 | PUSH_FINGERPRINT | - +------+-------------------+ - - - Text Message - - :: - - +--1b--+-------32b---------+----?b----+ - | 0x00 | PUSH_FINGERPRINT | MESSAGE | - +------+-------------------+--string--+ + ----+-------?b-------+ + ... | LOCATION_ENTRY | + ----+----------------+ diff --git a/server/piztor/server.py b/server/piztor/server.py index a58a301..5635242 100644 --- a/server/piztor/server.py +++ b/server/piztor/server.py @@ -19,6 +19,9 @@ from model import * def get_hex(data): return "".join([hex(ord(c))[2:].zfill(2) for c in data]) +def get_sec_id(comp_id, sec_no): + return comp_id * 256 + sec_no + db_path = "root:helloworld@localhost/piztor" #db_path = "piztor.sqlite" FORMAT = "%(asctime)-15s %(message)s" @@ -64,19 +67,20 @@ class _StatusCode: class PushData(object): from hashlib import sha256 - def __init__(self, data): - self.data = data + def pack(self, optcode, data): self.finger_print = sha256(data).digest() + buff = struct.pack("!B32s", optcode, self.finger_print) + buff += data + buff = struc.pack("!L", _SectionSize.LENGTH + len(buff)) + buff + self.data = data class PushTextMesgData(PushData): def __init__(self, mesg): - self.finger_print = sha256(mesg).digest() - logger.info("Mesg: %s", mesg) - buff = struct.pack("!B32s", 0x00, self.finger_print) - buff += mesg - buff += chr(0) - buff = struct.pack("!L", _SectionSize.LENGTH + len(buff)) + buff - self.data = buff + self.pack(0x00, mesg + chr(0)) + +class PushLocationData(PushData): + def __init__(self, uid, lat, lng): + self.pack(0x01, struct.pack("!dd", lat, lng)) class PushTunnel(object): @@ -84,9 +88,9 @@ class PushTunnel(object): self.pending = deque() self.conn = None - def __del__(self): + def close(self): if self.conn: - self.conn.loseConnection() + self.conn.transport.loseConnection() def add(self, pdata): logger.info("-- Push data enqued --") @@ -97,27 +101,28 @@ class PushTunnel(object): length, optcode, fingerprint = struct.unpack("!LB32s", data) if front.finger_print != fingerprint: raise PiztorError - logger.info("-- Push data confirmed --") + logger.info("-- Push data confirmed by client --") self.push() def push(self): print "Pushing via " + str(self) print "Pending size: " + str(len(self.pending)) - print self.conn + logger.info("Pushing...") if (self.conn is None) or len(self.pending) == 0: return front = self.pending.popleft() self.pending.appendleft(front) - print get_hex(front.data) self.conn.transport.write(front.data) def connect(self, conn): - print conn conn.tunnel = self + if self.conn: # only one long-connection per user + self.conn.transport.loseConnection() self.conn = conn - def on_connection_lost(self): - self.conn = None + def on_connection_lost(self, conn): + if conn == self.conn: + self.conn = None class RequestHandler(object): push_tunnels = dict() @@ -298,13 +303,13 @@ class LocationInfoHandler(RequestHandler): username, tail = RequestHandler.trunc_padding(tr_data[32:]) if username is None: raise struct.error - comp_id, sec_id = struct.unpack("!BB", tail) + comp_id, sec_no = struct.unpack("!BB", tail) except struct.error: raise BadReqError("Location request: Malformed request body") logger.info("Trying to request locatin with " \ - "(token = {0}, comp_id = {1}, sec_id = {2})" \ - .format(get_hex(token), comp_id, sec_id)) + "(token = {0}, comp_id = {1}, sec_no = {2})" \ + .format(get_hex(token), comp_id, sec_no)) uauth = RequestHandler.get_uauth(token, username, self.session) # Auth failure @@ -314,13 +319,13 @@ class LocationInfoHandler(RequestHandler): _OptCode.location_info, _StatusCode.failure) - if sec_id == 0xff: # All members in the company + if sec_no == 0xff: # All members in the company ulist = self.session.query(UserModel) \ .filter(UserModel.comp_id == comp_id).all() else: + sec_id = get_sec_id(comp_id, sec_no) ulist = self.session.query(UserModel) \ - .filter(and_(UserModel.comp_id == comp_id, - UserModel.sec_id == sec_id)).all() + .filter(UserModel.sec_id == sec_id).all() reply = struct.pack( "!LBB", self._response_size(len(ulist)), @@ -334,7 +339,7 @@ class LocationInfoHandler(RequestHandler): return reply def pack_gid(user): - return struct.pack("!BB", user.comp_id, user.sec_id) + return struct.pack("!BB", user.comp_id, user.sec_no) def pack_sex(user): return struct.pack("!B", 0x01 if user.sex else 0x00) @@ -436,7 +441,10 @@ class UserLogoutHandler(RequestHandler): return struct.pack("!LBB", self._response_size, _OptCode.user_logout, _StatusCode.failure) - del RequestHandler.push_tunnels[uauth.uid] + pt = RequestHandler.push_tunnels + uid = uauth.uid + pt[uid].close() + del pt[uid] uauth.regen_token() logger.info("User Logged out successfully!") self.session.commit() @@ -525,15 +533,16 @@ class SendTextMessageHandler(RequestHandler): pt = RequestHandler.push_tunnels u = uauth.user + sec_id = get_sec_id(u.comp_id, u.sec_no) ulist = self.session.query(UserModel) \ - .filter(and_(UserModel.comp_id == u.comp_id, - UserModel.sec_id == u.sec_id)).all() + .filter(UserModel.sec_id == sec_id).all() for user in ulist: uid = user.id if pt.has_key(uid): tunnel = pt[uid] tunnel.add(PushTextMesgData(mesg)) + tunnel.push() logger.info("Sent text mesg successfully!") return struct.pack("!LBB", self._response_size, _OptCode.send_text_mesg, @@ -621,7 +630,7 @@ class PTP(Protocol, TimeoutMixin): def connectionLost(self, reason): if self.tunnel: - self.tunnel.on_connection_lost() + self.tunnel.on_connection_lost(self) logger.info("The connection is lost") self.setTimeout(None) |