summaryrefslogtreecommitdiff
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-rw-r--r--server/piztor/mesg_sender.py1
-rw-r--r--server/piztor/model.py61
-rw-r--r--server/piztor/ptp.rst422
-rw-r--r--server/piztor/server.py65
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)