From 71cb89b264fe02b131d32a5a497571d70f31197b Mon Sep 17 00:00:00 2001 From: Teddy Date: Mon, 2 Sep 2013 14:46:19 +0800 Subject: .. --- server/piztor/easy_import.py | 2 +- server/piztor/game_server.py | 1006 ++++++++++++++++++++++++++++++++++++++++++ server/piztor/server.py | 1006 ------------------------------------------ 3 files changed, 1007 insertions(+), 1007 deletions(-) create mode 100644 server/piztor/game_server.py delete mode 100644 server/piztor/server.py (limited to 'server') diff --git a/server/piztor/easy_import.py b/server/piztor/easy_import.py index b614315..2a050d8 100644 --- a/server/piztor/easy_import.py +++ b/server/piztor/easy_import.py @@ -2,7 +2,7 @@ from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from model import * -path = "root:helloworld@localhost/piztor" +path = "root:helloworld@localhost/piztor2" class UserData: def __init__(self, username, nickname, password, diff --git a/server/piztor/game_server.py b/server/piztor/game_server.py new file mode 100644 index 0000000..cc1bd89 --- /dev/null +++ b/server/piztor/game_server.py @@ -0,0 +1,1006 @@ +from twisted.internet.protocol import Protocol +from twisted.internet.protocol import Factory +from twisted.internet.endpoints import TCP4ServerEndpoint +from twisted.protocols.policies import TimeoutMixin + +from sqlalchemy import create_engine, and_ +from sqlalchemy.orm import sessionmaker +from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound + +from collections import deque +from time import time + +import struct +import os +import logging + +from exc import * +from model import * + +def get_hex(data): + return "".join([hex(ord(c))[2:].zfill(2) for c in data]) + +db_path = "root:helloworld@localhost/piztor2" +#db_path = "piztor.sqlite" +FORMAT = "%(asctime)-15s %(message)s" +logging.basicConfig(format = FORMAT) +logger = logging.getLogger('piztor_server') +logger.setLevel(logging.INFO) +engine = create_engine('mysql://' + db_path, echo = False, pool_size = 1024) + +class _PermCode: + normal = 0x00 + section = 0x01 + company = 0x02 + +class _SectionSize: + LENGTH = 4 + OPT_ID = 1 + STATUS = 1 + USER_ID = 4 + USER_TOKEN = 32 + GROUP_ID = 2 + LATITUDE = 8 + LONGITUDE = 8 + PADDING = 1 + DEADLINE = 4 + MARKER_ID = 1 + +_MAX_AUTH_HEAD_SIZE = _SectionSize.USER_TOKEN + \ + MAX_USERNAME_SIZE + \ + _SectionSize.PADDING +_HEADER_SIZE = _SectionSize.LENGTH + \ + _SectionSize.OPT_ID + +_MAX_TEXT_MESG_SIZE = 1024 +_MAX_SUB_LIST_SIZE = 10 +_MAX_PENDING_PUSH = 10 +_INIT_MARKER_NUM = 5 + +class _OptCode: + user_auth = 0x00 + update_location = 0x01 + user_info = 0x02 + update_subscription = 0x03 + user_logout = 0x04 + open_push_tunnel = 0x05 + send_text_mesg = 0x06 + set_marker = 0x07 + change_password = 0x08 + check_in = 0x09 + game_start = 0x0A + +class _StatusCode: + sucess = 0x00 + auth_fail = 0x01 + insuf_lvl = 0x02 + wrong_pass = 0x03 + grp_not_found = 0x04 + checkin_fail = 0x05 + +class PushData(object): + from hashlib import sha256 + def pack(self, optcode, data): + self.finger_print = sha256(data + struct.pack("!d", time())).digest() + buff = struct.pack("!B32s", optcode, self.finger_print) + buff += data + buff = struct.pack("!L", _SectionSize.LENGTH + len(buff)) + buff + self.data = buff + +class PushTextMesgData(PushData): + def __init__(self, mesg): + self.pack(0x00, mesg + chr(0)) + +class PushLocationData(PushData): + def __init__(self, uid, lat, lng): + self.pack(0x01, struct.pack("!Ldd", uid, lat, lng)) + +class PushMarkerData(PushData): + def __init__(self, perm, lat, lng, deadline, mid, score): + self.pack(0x02, struct.pack("!BddlBL", perm, lat, lng, deadline, + mid, score)) +class PushMarkerRemovalData(PushData): + def __init__(self, mid): + self.pack(0x03, struct.pack("!B", mid)) + +class PushScoreData(PushData): + def __init__(self, score1, score2): + self.pack(0x04, struct.pack("!LL", score1, score2)) + +class PushTunnel(object): + def __init__(self, uid): + self.uid = uid + self.pending = deque() + self.conn = None + self.blocked = False + + def close(self): + print "closing TUNNEL" + if self.conn: + print "loseConn called" + self.conn.transport.loseConnection() + + def add(self, pdata): + #logger.info("-- Push data enqued --") + self.pending.append(pdata) + if not self.blocked and len(self.pending) > _MAX_PENDING_PUSH: + #logger.info("-- Push queue is full, discarded an obsolete push --") + self.pending.popleft() # discard old push + + def on_receive(self, data): + front = self.pending.popleft() + length, optcode, fingerprint = struct.unpack("!LB32s", data) + if front.finger_print != fingerprint: + raise PiztorError + #logger.info("-- Push data confirmed by client %s --", str(self.uid)) + self.blocked = False + self.push() + + def push(self): + if self.blocked: + return + print "Pushing via " + str(self.uid) + print "Pending size: " + str(len(self.pending)) + #logger.info("Pushing...") + if (self.conn is None) or len(self.pending) == 0: + return + front = self.pending.popleft() + self.pending.appendleft(front) + self.conn.transport.write(front.data) + #logger.info("-- Wrote push: %s --", get_hex(front.data)) + self.blocked = True + + def clear(self): + self.pending.clear() + + def connect(self, conn): + self.clear() + conn.tunnel = self + if self.conn: # only one long-connection per user + self.conn.transport.loseConnection() + self.conn = conn + + def on_connection_lost(self, conn): + print "TUNNEL closed" + if conn == self.conn: + self.conn = None + +def pack_uid(user): + return struct.pack("!L", user.id) + +def pack_username(user): + buff = user.username + buff += chr(0) + return buff + +def pack_nickname(user): + buff = user.nickname + buff += chr(0) + return buff + +def pack_sex(user): + return struct.pack("!B", 0x01 if user.sex else 0x00) + +def pack_gid(user): + return struct.pack("!H", user.sec_id) + +def pack_lat(user): + return struct.pack("!d", user.location.lat) + +def pack_lng(user): + return struct.pack("!d", user.location.lng) + +def pack_perm(user): + return struct.pack("!B", user.perm) + +class RequestHandler(object): + push_tunnels = dict() + def __init__(self): + Session = sessionmaker(bind = engine) + self.session = Session() + + def __del__(self): + self.session.close() + + def check_size(self, tr_data): + if len(tr_data) > self._max_tr_data_size: + raise BadReqError("Authentication: Request size is too large") + + @classmethod + def get_uauth(cls, token, username, session): + try: + uauth = session.query(UserAuth) \ + .filter(UserAuth.token == token).one() + + if uauth.user.username != username: + logger.warning("Token and username mismatch") + return None + uid = uauth.uid + if not cls.push_tunnels.has_key(uid): + cls.push_tunnels[uid] = PushTunnel(uid) + return uauth + + except NoResultFound: + logger.warning("Incorrect token") + return None + + except MultipleResultsFound: + raise DBCorruptionError() + + @classmethod + def trunc_padding(cls, data): + leading = bytes() + for i in xrange(len(data)): + ch = data[i] + if ch == '\x00': + return (leading, data[i + 1:]) + else: + leading += ch + # padding not found + return (None, data) + + def pack(self, data): + return struct.pack("!LB", + _SectionSize.LENGTH + \ + _SectionSize.OPT_ID + \ + len(data), self._optcode) + data + + _code_map = { 0x01 : pack_uid, + 0x02 : pack_username, + 0x03 : pack_nickname, + 0x04 : pack_sex, + 0x05 : pack_gid, + 0x06 : pack_lat, + 0x07 : pack_lng, + 0x08 : pack_perm} + + @classmethod + def pack_info_entry(cls, user, entry_code): + pack_method = cls._code_map[entry_code] + info_key = entry_code + return struct.pack("!B", info_key) + pack_method(user) + + @classmethod + def pack_user_entry(cls, user): + buff = bytes() + for entry_code in cls._code_map: + buff += cls.pack_info_entry(user, entry_code) + buff += chr(0) + return buff + + @classmethod + def pack_sub_list(cls, user): + buff = bytes() + for grp in user.sub: + buff += struct.pack("!H", grp.id) + buff += chr(0) + return buff + + @classmethod + def unpack_sub_list(cls, data): + res = list() + idx = 0 + end = len(data) - 1 + while idx < end: + res.append(struct.unpack("!H", + data[idx:idx + _SectionSize.GROUP_ID])[0]) + idx += _SectionSize.GROUP_ID + return res + + +class UserAuthHandler(RequestHandler): + + _optcode = _OptCode.user_auth + _max_tr_data_size = MAX_USERNAME_SIZE + \ + _SectionSize.PADDING + \ + MAX_PASSWORD_SIZE + \ + _SectionSize.PADDING + + _failed_response = \ + lambda self: self.pack(struct.pack("!B", _StatusCode.auth_fail)) + + + def handle(self, tr_data, conn): + self.check_size(tr_data) + logger.info("Reading auth data...") + pos = -1 + for i in xrange(0, len(tr_data)): + if tr_data[i] == '\x00': + pos = i + break + if pos == -1: + raise BadReqError("Authentication: Malformed request body") + + username = tr_data[0:pos] + password = tr_data[pos + 1:-1] + logger.info("Trying to login with " \ + "(username = {0}, password = {1})" \ + .format(username, password)) + + try: + user = self.session.query(UserModel) \ + .filter(UserModel.username == username).one() + except NoResultFound: + logger.info("No such user: {0}".format(username)) + return self._failed_response() + + except MultipleResultsFound: + raise DBCorruptionError() + + uauth = user.auth + if uauth is None: + raise DBCorruptionError() + if not uauth.check_password(password): + logger.info("Incorrect password: {0}".format(password)) + return self._failed_response() + else: + logger.info("Logged in sucessfully: {0}".format(username)) + pt = RequestHandler.push_tunnels + uid = uauth.uid + if pt.has_key(uid): # close the old push tunnel + pt[uid].close() + del pt[uid] + uauth.regen_token() + #logger.info("New token generated: " + get_hex(uauth.token)) + self.session.commit() + # STATUS | USER_TOKEN + reply = struct.pack("!B32s", _StatusCode.sucess, uauth.token) + # USER_ENTRY + reply += RequestHandler.pack_user_entry(user) + reply += RequestHandler.pack_sub_list(user) + score1 = self.session.query(GroupInfo) \ + .filter(GroupInfo.id == 5889).one().score + score2 = self.session.query(GroupInfo) \ + .filter(GroupInfo.id == 5890).one().score + + for mrk in self.session.query(MarkerInfo).all(): + if mrk.status != MARKER_DISPLAYED: + continue + reply += struct.pack("!BddlBL", 2, mrk.lat, mrk.lng, 0x7fffffff, + mrk.id, mrk.score) + reply += struct.pack("!LL", score1, score2) + return self.pack(reply) + + +class UpdateLocationHandler(RequestHandler): + + _optcode = _OptCode.update_location + _max_tr_data_size = _MAX_AUTH_HEAD_SIZE + \ + _SectionSize.LATITUDE + \ + _SectionSize.LONGITUDE + + def handle(self, tr_data, conn): + self.check_size(tr_data) + logger.info("Reading location update data...") + try: + token, = struct.unpack("!32s", tr_data[:32]) + username, tail = RequestHandler.trunc_padding(tr_data[32:]) + if username is None: + raise struct.error + lat, lng = struct.unpack("!dd", tail) + except struct.error: + raise BadReqError("Update location: Malformed request body") + + logger.info("Trying to update location with " + "(token = {0}, username = {1}, lat = {2}, lng = {3})"\ + .format(get_hex(token), username, lat, lng)) + + uauth = RequestHandler.get_uauth(token, username, self.session) + # Authentication failure + if uauth is None: + logger.warning("Authentication failure") + return self.pack(struct.pack("!B", _StatusCode.auth_fail)) + + loc = uauth.user.location + loc.lat = lat + loc.lng = lng + + self.session.commit() + logger.info("Location is updated sucessfully") + + pt = RequestHandler.push_tunnels + u = uauth.user + comp = self.session.query(GroupInfo) \ + .filter(GroupInfo.id == u.comp_id).one() + sec = self.session.query(GroupInfo) \ + .filter(GroupInfo.id == u.sec_id).one() + + pdata = PushLocationData(u.id, lat, lng) + for user in comp.subscribers: + uid = user.id + if pt.has_key(uid): + tunnel = pt[uid] + tunnel.add(pdata) + tunnel.push() + + return self.pack(struct.pack("!B", _StatusCode.sucess)) + + +class UserInfoHandler(RequestHandler): + + _optcode = _OptCode.user_info + _max_tr_data_size = _MAX_AUTH_HEAD_SIZE + \ + _SectionSize.GROUP_ID + + _failed_response = \ + lambda self : self.pack(struct.pack("!B", _StatusCode.auth_fail)) + + + def handle(self, tr_data, conn): + self.check_size(tr_data) + logger.info("Reading user info request data...") + try: + token, = struct.unpack("!32s", tr_data[:32]) + username, tail = RequestHandler.trunc_padding(tr_data[32:]) + if username is None: + raise struct.error + gid, = struct.unpack("!H", tail) + except struct.error: + raise BadReqError("User info request: Malformed request body") + +# logger.info("Trying to get user info with " \ +# "(token = {0}, gid = {1})" \ +# .format(get_hex(token), gid)) + + uauth = RequestHandler.get_uauth(token, username, self.session) + # Auth failure + if uauth is None: + logger.warning("Authentication failure") + return self._failed_response() + # TODO: check the relationship between user and quser + u = uauth.user + + grp = self.session.query(UserModel) \ + .filter(UserModel.sec_id == gid).all() + grp += self.session.query(UserModel) \ + .filter(UserModel.comp_id == gid).all() + + reply = struct.pack("!B", _StatusCode.sucess) + for user in grp: + reply += RequestHandler.pack_user_entry(user) + return self.pack(reply) + +class UpdateSubscription(RequestHandler): + + _optcode = _OptCode.update_subscription + _max_tr_data_size = _MAX_AUTH_HEAD_SIZE + \ + _SectionSize.GROUP_ID * _MAX_SUB_LIST_SIZE + \ + _SectionSize.PADDING + + def _find_group(self, gid): + q = self.session.query(GroupInfo).filter(GroupInfo.id == gid) + entry = q.first() + if not entry: + raise BadReqError("Group not found") + self.session.commit() + return entry + + def handle(self, tr_data, conn): + + self.check_size(tr_data) + logger.info("Reading update subscription data...") + try: + token, = struct.unpack("!32s", tr_data[:32]) + username, tail = RequestHandler.trunc_padding(tr_data[32:]) + if username is None: + raise struct.error + sub_list = RequestHandler.unpack_sub_list(tail) + except struct.error: + raise BadReqError("Update Subscription: Malformed request body") + +# logger.info("Trying to update subscription with " +# "(token = {0}, username = {1}, grps = {2})"\ +# .format(get_hex(token), username, str(sub_list))) + + uauth = RequestHandler.get_uauth(token, username, self.session) + # Authentication failure + if uauth is None: + logger.warning("Authentication failure") + return self.pack(struct.pack("!B", _StatusCode.auth_fail)) + + try: + uauth.user.sub = map(self._find_group, sub_list) + except BadReqError: + return self.pack(struct.pack("!B", _StatusCode.grp_not_found)) + self.session.commit() + logger.info("Subscription is updated sucessfully") + + return self.pack(struct.pack("!B", _StatusCode.sucess)) + +class UserLogoutHandler(RequestHandler): + + _optcode = _OptCode.user_logout + _max_tr_data_size = _MAX_AUTH_HEAD_SIZE + + def handle(self, tr_data, conn): + self.check_size(tr_data) + logger.info("Reading user logout data...") + try: + token, = struct.unpack("!32s", tr_data[:32]) + username, tail = RequestHandler.trunc_padding(tr_data[32:]) + if username is None: + raise struct.error + except struct.error: + raise BadReqError("User logout: Malformed request body") + +# logger.info("Trying to logout with " +# "(token = {0}, username = {1})"\ +# .format(get_hex(token), username)) + + uauth = RequestHandler.get_uauth(token, username, self.session) + # Authentication failure + if uauth is None: + logger.warning("Authentication failure") + return self.pack(struct.pack("!B", _StatusCode.auth_fail)) + + pt = RequestHandler.push_tunnels + u = uauth.user + loc = u.location + loc.lat = NOT_A_LAT + loc.lng = NOT_A_LNG + + comp = self.session.query(GroupInfo) \ + .filter(GroupInfo.id == u.comp_id).one() + sec = self.session.query(GroupInfo) \ + .filter(GroupInfo.id == u.sec_id).one() + + pdata = PushLocationData(u.id, loc.lat, loc.lng) + for user in comp.subscribers: + uid = user.id + if uid == uauth.uid: continue + if pt.has_key(uid): + tunnel = pt[uid] + tunnel.add(pdata) + tunnel.push() + + for user in sec.subscribers: + uid = user.id + if uid == uauth.uid: continue + if pt.has_key(uid): + tunnel = pt[uid] + tunnel.add(pdata) + tunnel.push() + + 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() + return self.pack(struct.pack("!B", _StatusCode.sucess)) + +class OpenPushTunnelHandler(RequestHandler): + + _optcode = _OptCode.open_push_tunnel + _max_tr_data_size = _MAX_AUTH_HEAD_SIZE + + def handle(self, tr_data, conn): + self.check_size(tr_data) + logger.info("Reading open push tunnel data...") + try: + token, = struct.unpack("!32s", tr_data[:32]) + username, tail = RequestHandler.trunc_padding(tr_data[32:]) + if username is None: + raise struct.error + except struct.error: + raise BadReqError("Open push tunnel: Malformed request body") + +# logger.info("Trying to open push tunnel with " +# "(token = {0}, username = {1})"\ +# .format(get_hex(token), username)) + + uauth = RequestHandler.get_uauth(token, username, self.session) + # Authentication failure + if uauth is None: + logger.warning("Authentication failure") + return self.pack(struct.pack("!B", _StatusCode.auth_fail)) + + tunnel = RequestHandler.push_tunnels[uauth.uid] + tunnel.connect(conn) + + logger.info("Push tunnel opened successfully!") + return self.pack(struct.pack("!B", _StatusCode.sucess)) + +class SendTextMessageHandler(RequestHandler): + + _optcode = _OptCode.send_text_mesg + _max_tr_data_size = _MAX_AUTH_HEAD_SIZE + \ + _MAX_TEXT_MESG_SIZE + \ + _SectionSize.PADDING + + def handle(self, tr_data, conn): + self.check_size(tr_data) + logger.info("Reading send text mesg data...") + try: + token, = struct.unpack("!32s", tr_data[:32]) + username, tail = RequestHandler.trunc_padding(tr_data[32:]) + mesg = tail[:-1] + if username is None: + raise struct.error + except struct.error: + raise BadReqError("Send text mesg: Malformed request body") + +# logger.info("Trying to send text mesg with " +# "(token = {0}, username = {1})"\ +# .format(get_hex(token), username)) + + uauth = RequestHandler.get_uauth(token, username, self.session) + # Authentication failure + if uauth is None: + logger.warning("Authentication failure") + return self.pack(struct.pack("!B", _StatusCode.auth_fail)) + + pt = RequestHandler.push_tunnels + u = uauth.user + ulist = self.session.query(UserModel) \ + .filter(UserModel.sec_id == u.sec_id).all() + + for user in ulist: + uid = user.id + if uid == uauth.uid: continue + if pt.has_key(uid): + tunnel = pt[uid] + tunnel.add(PushTextMesgData(mesg)) + tunnel.push() + logger.info("Sent text mesg successfully!") + return self.pack(struct.pack("!B", _StatusCode.sucess)) + +class SetMarkerHandler(RequestHandler): + + _optcode = _OptCode.set_marker + _max_tr_data_size = _MAX_AUTH_HEAD_SIZE + \ + _SectionSize.LATITUDE + \ + _SectionSize.LONGITUDE + \ + _SectionSize.DEADLINE + @classmethod + def push_new_marker(cls, u, session): + left = session.query(MarkerInfo) \ + .filter(MarkerInfo.status == MARKER_FRESH).all() + if len(left) == 0: + logger.warn("All markers have been used!") + return + marker = left[0] + marker.status = MARKER_DISPLAYED + session.commit() + pdata = PushMarkerData(perm = 2, lat = marker.lat, \ + lng = marker.lng, \ + deadline = 0x7fffffff, \ + mid = marker.id, \ + score = marker.score) + ulist = session.query(UserModel) \ + .filter(UserModel.comp_id == u.comp_id).all() + + pt = RequestHandler.push_tunnels + for user in ulist: + uid = user.id + if pt.has_key(uid): + tunnel = pt[uid] + tunnel.add(pdata) + tunnel.push() + + + def handle(self, tr_data, conn): + self.check_size(tr_data) + logger.info("Reading set marker data...") + try: + token, = struct.unpack("!32s", tr_data[:32]) + username, tail = RequestHandler.trunc_padding(tr_data[32:]) + lat, lng, deadline, mid, score = struct.unpack("!ddlBL", tail) + if username is None: + raise struct.error + except struct.error: + raise BadReqError("Set marker: Malformed request body") + + logger.info("Trying to set marker with " + "(token = {0}, username = {1})"\ + .format(get_hex(token), username)) + + uauth = RequestHandler.get_uauth(token, username, self.session) + # Authentication failure + if uauth is None: + logger.warning("Authentication failure") + return self.pack(struct.pack("!B", _StatusCode.auth_fail)) + + SetMarkerHandler.push_new_marker(uauth.user, self.session) + + logger.info("Set marker successfully!") + return self.pack(struct.pack("!B", _StatusCode.sucess)) + +class ChangePasswordHandler(RequestHandler): + + _optcode = _OptCode.change_password + _max_tr_data_size = _MAX_AUTH_HEAD_SIZE + \ + MAX_PASSWORD_SIZE + \ + _SectionSize.PADDING + \ + MAX_PASSWORD_SIZE + \ + _SectionSize.PADDING + + def handle(self, tr_data, conn): + self.check_size(tr_data) + logger.info("Reading change password data...") + try: + token, = struct.unpack("!32s", tr_data[:32]) + username, tail = RequestHandler.trunc_padding(tr_data[32:]) + if username is None: + raise struct.error + old_pass, tail = RequestHandler.trunc_padding(tail) + new_pass = tail[:-1] + except struct.error: + raise BadReqError("User logout: Malformed request body") + +# logger.info("Trying to change password with " +# "(token = {0}, username = {1}, old_pass = {2}, new_pass = {3})"\ +# .format(get_hex(token), username, old_pass, new_pass)) + + uauth = RequestHandler.get_uauth(token, username, self.session) + # Authentication failure + if uauth is None: + logger.warning("Authentication failure") + return self.pack(struct.pack("!B", _StatusCode.auth_fail)) + if not uauth.check_password(old_pass): + return self.pack(struct.pack("!B", _StatusCode.wrong_pass)) + uauth.set_password(new_pass) + self.session.commit() + logger.info("Password changed successfully!") + + pt = RequestHandler.push_tunnels + uid = uauth.uid + pt[uid].close() + del pt[uid] + uauth.regen_token() + + return self.pack(struct.pack("!B", _StatusCode.sucess)) + +class CheckInHandler(RequestHandler): + + _optcode = _OptCode.check_in + _max_tr_data_size = _MAX_AUTH_HEAD_SIZE + \ + _SectionSize.MARKER_ID + + def handle(self, tr_data, conn): + self.check_size(tr_data) + logger.info("Reading check-in data...") + try: + token, = struct.unpack("!32s", tr_data[:32]) + username, tail = RequestHandler.trunc_padding(tr_data[32:]) + if username is None: + raise struct.error + mid, = struct.unpack("!B", tail) + except struct.error: + raise BadReqError("Check-in: Malformed request body") + + logger.info("Trying to check-in with " + "(token = {0}, username = {1}, mid = {2})"\ + .format(get_hex(token), username, str(mid))) + + uauth = RequestHandler.get_uauth(token, username, self.session) + # Authentication failure + if uauth is None: + logger.warning("Authentication failure") + return self.pack(struct.pack("!B", _StatusCode.auth_fail)) + + try: + marker = self.session.query(MarkerInfo) \ + .filter(MarkerInfo.id == mid).one() + except NoResultFound: + raise BadReqError("Check-in: no such marker") + + if marker.status != MARKER_DISPLAYED: + return self.pack(struct.pack("!B", _StatusCode.checkin_fail)) + marker.status = MARKER_CHECKED + + pt = RequestHandler.push_tunnels + u = uauth.user + + comp = self.session.query(GroupInfo) \ + .filter(GroupInfo.id == u.comp_id).one() + sec = self.session.query(GroupInfo) \ + .filter(GroupInfo.id == u.sec_id).one() + + sec.score += marker.score + logger.info("Player {0} got score: {1}".format(username, str(marker.score))) + + score1 = self.session.query(GroupInfo) \ + .filter(GroupInfo.id == 5889).one().score + score2 = self.session.query(GroupInfo) \ + .filter(GroupInfo.id == 5890).one().score + + pdata = PushMarkerRemovalData(mid) + for user in comp.subscribers: + uid = user.id + if pt.has_key(uid): + tunnel = pt[uid] + tunnel.add(pdata) + tunnel.add(PushScoreData(score1, score2)) + tunnel.push() + + SetMarkerHandler.push_new_marker(u, self.session) + + logger.info("User checked in successfully!") + self.session.commit() + return self.pack(struct.pack("!B", _StatusCode.sucess)) + +class GameStartHandler(RequestHandler): + + _optcode = _OptCode.game_start + _max_tr_data_size = _MAX_AUTH_HEAD_SIZE + + def handle(self, tr_data, conn): + self.check_size(tr_data) + logger.info("Reading game-start data...") + try: + token, = struct.unpack("!32s", tr_data[:32]) + username, tail = RequestHandler.trunc_padding(tr_data[32:]) + if username is None: + raise struct.error + except struct.error: + raise BadReqError("Game-start: Malformed request body") + + logger.info("Trying to game-start with " + "(token = {0}, username = {1})"\ + .format(get_hex(token), username)) + + uauth = RequestHandler.get_uauth(token, username, self.session) + # Authentication failure + if uauth is None: + logger.warning("Authentication failure") + return self.pack(struct.pack("!B", _StatusCode.auth_fail)) + + + pt = RequestHandler.push_tunnels + u = uauth.user + + if u.perm != 2: + logger.warning("Insufficient privilege") + return self.pack(struct.pack("!B", _StatusCode.insuf_lvl)) + + team1 = self.session.query(GroupInfo) \ + .filter(GroupInfo.id == 5889).one() + team2 = self.session.query(GroupInfo) \ + .filter(GroupInfo.id == 5890).one() + + team1.score = 0 + team2.score = 0 + + for mrk in self.session.query(MarkerInfo).all(): + mrk.status = MARKER_FRESH + + users1 = self.session.query(UserModel) \ + .filter(UserModel.sec_id == team1.id) + + users2 = self.session.query(UserModel) \ + .filter(UserModel.sec_id == team2.id) + + for i in xrange(_INIT_MARKER_NUM): + SetMarkerHandler.push_new_marker(u, self.session) + + comp = self.session.query(GroupInfo) \ + .filter(GroupInfo.id == u.comp_id).one() + + for user in comp.subscribers: + uid = user.id + if pt.has_key(uid): + tunnel = pt[uid] + tunnel.add(PushScoreData(team1.score, team2.score)) + tunnel.push() + + logger.info("GAME START!") + self.session.commit() + return self.pack(struct.pack("!B", _StatusCode.sucess)) + + +class PTP(Protocol, TimeoutMixin): + + handlers = [UserAuthHandler, + UpdateLocationHandler, + UserInfoHandler, + UpdateSubscription, + UserLogoutHandler, + OpenPushTunnelHandler, + SendTextMessageHandler, + SetMarkerHandler, + ChangePasswordHandler, + CheckInHandler, + GameStartHandler] + + handler_num = len(handlers) + + _MAX_REQUEST_SIZE = _HEADER_SIZE + \ + max([h._max_tr_data_size for h in handlers]) + + @classmethod + def check_header(cls, header): + return 0 <= header < cls.handler_num + + def __init__(self, factory): + self.buff = bytes() + self.length = -1 + self.factory = factory + self.tunnel = None + + def timeoutConnection(self): + logger.info("The connection times out") + self.transport.loseConnection() + + def connectionMade(self): + logger.info("A new connection is made") + self.setTimeout(self.factory.timeout) + + def response(self, buff): + try: + h = PTP.handlers[self.optcode]() + reply = h.handle(buff[5:], self) +# logger.info("Wrote: %s", get_hex(reply)) + self.transport.write(reply) + if self.tunnel: + logger.info("Blocking the client...") + self.tunnel.push() + self.length = -1 + return + self.transport.loseConnection() + except BadReqError as e: + logger.warn("Rejected a bad request: %s", str(e)) + self.transport.loseConnection() + except DBCorruptionError: + logger.error("*** Database corruption ***") + + + def dataReceived(self, data): + self.buff += data + self.resetTimeout() + logger.info("Buffer length is now: %d", len(self.buff)) + if len(self.buff) <= 4: + return + try: + if self.length == -1: + try: + self.length, self.optcode = struct.unpack("!LB", self.buff[:5]) + if not PTP.check_header(self.optcode): # invalid header + raise struct.error + except struct.error: + raise BadReqError("Malformed request header") + if self.length > PTP._MAX_REQUEST_SIZE: + print self.length, PTP._MAX_REQUEST_SIZE + raise BadReqError("The size of remaining part is too big") + + if len(self.buff) >= self.length: + buff = self.buff[:self.length] + self.buff = self.buff[self.length:] + if self.tunnel: # received push response + self.tunnel.on_receive(buff) + self.length = -1 + return + self.setTimeout(None) + reactor.callFromThread(self.response, buff) + #self.response(buff) + except BadReqError as e: + logger.warn("Rejected a bad request: %s", str(e)) + self.transport.loseConnection() + + def connectionLost(self, reason): + if self.tunnel: + self.tunnel.on_connection_lost(self) + logger.info("The connection is lost") + self.setTimeout(None) + +class PTPFactory(Factory): + def __init__(self, timeout = 10): + self.timeout = timeout + def buildProtocol(self, addr): + return PTP(self) + +if os.name!='nt': + from twisted.internet import epollreactor + epollreactor.install() +else: + from twisted.internet import iocpreactor + iocpreactor.install() + +from twisted.internet import reactor + +f = PTPFactory() +f.protocol = PTP +reactor.listenTCP(2224, f) +logger.warning("The server is lanuched") +reactor.run() diff --git a/server/piztor/server.py b/server/piztor/server.py deleted file mode 100644 index cbd80e4..0000000 --- a/server/piztor/server.py +++ /dev/null @@ -1,1006 +0,0 @@ -from twisted.internet.protocol import Protocol -from twisted.internet.protocol import Factory -from twisted.internet.endpoints import TCP4ServerEndpoint -from twisted.protocols.policies import TimeoutMixin - -from sqlalchemy import create_engine, and_ -from sqlalchemy.orm import sessionmaker -from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound - -from collections import deque -from time import time - -import struct -import os -import logging - -from exc import * -from model import * - -def get_hex(data): - return "".join([hex(ord(c))[2:].zfill(2) for c in data]) - -db_path = "root:helloworld@localhost/piztor" -#db_path = "piztor.sqlite" -FORMAT = "%(asctime)-15s %(message)s" -logging.basicConfig(format = FORMAT) -logger = logging.getLogger('piztor_server') -logger.setLevel(logging.INFO) -engine = create_engine('mysql://' + db_path, echo = False, pool_size = 1024) - -class _PermCode: - normal = 0x00 - section = 0x01 - company = 0x02 - -class _SectionSize: - LENGTH = 4 - OPT_ID = 1 - STATUS = 1 - USER_ID = 4 - USER_TOKEN = 32 - GROUP_ID = 2 - LATITUDE = 8 - LONGITUDE = 8 - PADDING = 1 - DEADLINE = 4 - MARKER_ID = 1 - -_MAX_AUTH_HEAD_SIZE = _SectionSize.USER_TOKEN + \ - MAX_USERNAME_SIZE + \ - _SectionSize.PADDING -_HEADER_SIZE = _SectionSize.LENGTH + \ - _SectionSize.OPT_ID - -_MAX_TEXT_MESG_SIZE = 1024 -_MAX_SUB_LIST_SIZE = 10 -_MAX_PENDING_PUSH = 10 -_INIT_MARKER_NUM = 2 - -class _OptCode: - user_auth = 0x00 - update_location = 0x01 - user_info = 0x02 - update_subscription = 0x03 - user_logout = 0x04 - open_push_tunnel = 0x05 - send_text_mesg = 0x06 - set_marker = 0x07 - change_password = 0x08 - check_in = 0x09 - game_start = 0x0A - -class _StatusCode: - sucess = 0x00 - auth_fail = 0x01 - insuf_lvl = 0x02 - wrong_pass = 0x03 - grp_not_found = 0x04 - checkin_fail = 0x05 - -class PushData(object): - from hashlib import sha256 - def pack(self, optcode, data): - self.finger_print = sha256(data + struct.pack("!d", time())).digest() - buff = struct.pack("!B32s", optcode, self.finger_print) - buff += data - buff = struct.pack("!L", _SectionSize.LENGTH + len(buff)) + buff - self.data = buff - -class PushTextMesgData(PushData): - def __init__(self, mesg): - self.pack(0x00, mesg + chr(0)) - -class PushLocationData(PushData): - def __init__(self, uid, lat, lng): - self.pack(0x01, struct.pack("!Ldd", uid, lat, lng)) - -class PushMarkerData(PushData): - def __init__(self, perm, lat, lng, deadline, mid, score): - self.pack(0x02, struct.pack("!BddlBL", perm, lat, lng, deadline, - mid, score)) -class PushMarkerRemovalData(PushData): - def __init__(self, mid): - self.pack(0x03, struct.pack("!B", mid)) - -class PushScoreData(PushData): - def __init__(self, score1, score2): - self.pack(0x04, struct.pack("!LL", score1, score2)) - -class PushTunnel(object): - def __init__(self, uid): - self.uid = uid - self.pending = deque() - self.conn = None - self.blocked = False - - def close(self): - print "closing TUNNEL" - if self.conn: - print "loseConn called" - self.conn.transport.loseConnection() - - def add(self, pdata): - #logger.info("-- Push data enqued --") - self.pending.append(pdata) - if not self.blocked and len(self.pending) > _MAX_PENDING_PUSH: - #logger.info("-- Push queue is full, discarded an obsolete push --") - self.pending.popleft() # discard old push - - def on_receive(self, data): - front = self.pending.popleft() - length, optcode, fingerprint = struct.unpack("!LB32s", data) - if front.finger_print != fingerprint: - raise PiztorError - #logger.info("-- Push data confirmed by client %s --", str(self.uid)) - self.blocked = False - self.push() - - def push(self): - if self.blocked: - return - print "Pushing via " + str(self.uid) - print "Pending size: " + str(len(self.pending)) - #logger.info("Pushing...") - if (self.conn is None) or len(self.pending) == 0: - return - front = self.pending.popleft() - self.pending.appendleft(front) - self.conn.transport.write(front.data) - #logger.info("-- Wrote push: %s --", get_hex(front.data)) - self.blocked = True - - def clear(self): - self.pending.clear() - - def connect(self, conn): - self.clear() - conn.tunnel = self - if self.conn: # only one long-connection per user - self.conn.transport.loseConnection() - self.conn = conn - - def on_connection_lost(self, conn): - print "TUNNEL closed" - if conn == self.conn: - self.conn = None - -def pack_uid(user): - return struct.pack("!L", user.id) - -def pack_username(user): - buff = user.username - buff += chr(0) - return buff - -def pack_nickname(user): - buff = user.nickname - buff += chr(0) - return buff - -def pack_sex(user): - return struct.pack("!B", 0x01 if user.sex else 0x00) - -def pack_gid(user): - return struct.pack("!H", user.sec_id) - -def pack_lat(user): - return struct.pack("!d", user.location.lat) - -def pack_lng(user): - return struct.pack("!d", user.location.lng) - -def pack_perm(user): - return struct.pack("!B", user.perm) - -class RequestHandler(object): - push_tunnels = dict() - def __init__(self): - Session = sessionmaker(bind = engine) - self.session = Session() - - def __del__(self): - self.session.close() - - def check_size(self, tr_data): - if len(tr_data) > self._max_tr_data_size: - raise BadReqError("Authentication: Request size is too large") - - @classmethod - def get_uauth(cls, token, username, session): - try: - uauth = session.query(UserAuth) \ - .filter(UserAuth.token == token).one() - - if uauth.user.username != username: - logger.warning("Token and username mismatch") - return None - uid = uauth.uid - if not cls.push_tunnels.has_key(uid): - cls.push_tunnels[uid] = PushTunnel(uid) - return uauth - - except NoResultFound: - logger.warning("Incorrect token") - return None - - except MultipleResultsFound: - raise DBCorruptionError() - - @classmethod - def trunc_padding(cls, data): - leading = bytes() - for i in xrange(len(data)): - ch = data[i] - if ch == '\x00': - return (leading, data[i + 1:]) - else: - leading += ch - # padding not found - return (None, data) - - def pack(self, data): - return struct.pack("!LB", - _SectionSize.LENGTH + \ - _SectionSize.OPT_ID + \ - len(data), self._optcode) + data - - _code_map = { 0x01 : pack_uid, - 0x02 : pack_username, - 0x03 : pack_nickname, - 0x04 : pack_sex, - 0x05 : pack_gid, - 0x06 : pack_lat, - 0x07 : pack_lng, - 0x08 : pack_perm} - - @classmethod - def pack_info_entry(cls, user, entry_code): - pack_method = cls._code_map[entry_code] - info_key = entry_code - return struct.pack("!B", info_key) + pack_method(user) - - @classmethod - def pack_user_entry(cls, user): - buff = bytes() - for entry_code in cls._code_map: - buff += cls.pack_info_entry(user, entry_code) - buff += chr(0) - return buff - - @classmethod - def pack_sub_list(cls, user): - buff = bytes() - for grp in user.sub: - buff += struct.pack("!H", grp.id) - buff += chr(0) - return buff - - @classmethod - def unpack_sub_list(cls, data): - res = list() - idx = 0 - end = len(data) - 1 - while idx < end: - res.append(struct.unpack("!H", - data[idx:idx + _SectionSize.GROUP_ID])[0]) - idx += _SectionSize.GROUP_ID - return res - - -class UserAuthHandler(RequestHandler): - - _optcode = _OptCode.user_auth - _max_tr_data_size = MAX_USERNAME_SIZE + \ - _SectionSize.PADDING + \ - MAX_PASSWORD_SIZE + \ - _SectionSize.PADDING - - _failed_response = \ - lambda self: self.pack(struct.pack("!B", _StatusCode.auth_fail)) - - - def handle(self, tr_data, conn): - self.check_size(tr_data) - logger.info("Reading auth data...") - pos = -1 - for i in xrange(0, len(tr_data)): - if tr_data[i] == '\x00': - pos = i - break - if pos == -1: - raise BadReqError("Authentication: Malformed request body") - - username = tr_data[0:pos] - password = tr_data[pos + 1:-1] - logger.info("Trying to login with " \ - "(username = {0}, password = {1})" \ - .format(username, password)) - - try: - user = self.session.query(UserModel) \ - .filter(UserModel.username == username).one() - except NoResultFound: - logger.info("No such user: {0}".format(username)) - return self._failed_response() - - except MultipleResultsFound: - raise DBCorruptionError() - - uauth = user.auth - if uauth is None: - raise DBCorruptionError() - if not uauth.check_password(password): - logger.info("Incorrect password: {0}".format(password)) - return self._failed_response() - else: - logger.info("Logged in sucessfully: {0}".format(username)) - pt = RequestHandler.push_tunnels - uid = uauth.uid - if pt.has_key(uid): # close the old push tunnel - pt[uid].close() - del pt[uid] - uauth.regen_token() - #logger.info("New token generated: " + get_hex(uauth.token)) - self.session.commit() - # STATUS | USER_TOKEN - reply = struct.pack("!B32s", _StatusCode.sucess, uauth.token) - # USER_ENTRY - reply += RequestHandler.pack_user_entry(user) - reply += RequestHandler.pack_sub_list(user) - score1 = self.session.query(GroupInfo) \ - .filter(GroupInfo.id == 5889).one().score - score2 = self.session.query(GroupInfo) \ - .filter(GroupInfo.id == 5890).one().score - - for mrk in self.session.query(MarkerInfo).all(): - if mrk.status != MARKER_DISPLAYED: - continue - reply += struct.pack("!BddlBL", 2, mrk.lat, mrk.lng, 0x7fffffff, - mrk.id, mrk.score) - reply += struct.pack("!LL", score1, score2) - return self.pack(reply) - - -class UpdateLocationHandler(RequestHandler): - - _optcode = _OptCode.update_location - _max_tr_data_size = _MAX_AUTH_HEAD_SIZE + \ - _SectionSize.LATITUDE + \ - _SectionSize.LONGITUDE - - def handle(self, tr_data, conn): - self.check_size(tr_data) - logger.info("Reading location update data...") - try: - token, = struct.unpack("!32s", tr_data[:32]) - username, tail = RequestHandler.trunc_padding(tr_data[32:]) - if username is None: - raise struct.error - lat, lng = struct.unpack("!dd", tail) - except struct.error: - raise BadReqError("Update location: Malformed request body") - - logger.info("Trying to update location with " - "(token = {0}, username = {1}, lat = {2}, lng = {3})"\ - .format(get_hex(token), username, lat, lng)) - - uauth = RequestHandler.get_uauth(token, username, self.session) - # Authentication failure - if uauth is None: - logger.warning("Authentication failure") - return self.pack(struct.pack("!B", _StatusCode.auth_fail)) - - loc = uauth.user.location - loc.lat = lat - loc.lng = lng - - self.session.commit() - logger.info("Location is updated sucessfully") - - pt = RequestHandler.push_tunnels - u = uauth.user - comp = self.session.query(GroupInfo) \ - .filter(GroupInfo.id == u.comp_id).one() - sec = self.session.query(GroupInfo) \ - .filter(GroupInfo.id == u.sec_id).one() - - pdata = PushLocationData(u.id, lat, lng) - for user in comp.subscribers: - uid = user.id - if pt.has_key(uid): - tunnel = pt[uid] - tunnel.add(pdata) - tunnel.push() - - return self.pack(struct.pack("!B", _StatusCode.sucess)) - - -class UserInfoHandler(RequestHandler): - - _optcode = _OptCode.user_info - _max_tr_data_size = _MAX_AUTH_HEAD_SIZE + \ - _SectionSize.GROUP_ID - - _failed_response = \ - lambda self : self.pack(struct.pack("!B", _StatusCode.auth_fail)) - - - def handle(self, tr_data, conn): - self.check_size(tr_data) - logger.info("Reading user info request data...") - try: - token, = struct.unpack("!32s", tr_data[:32]) - username, tail = RequestHandler.trunc_padding(tr_data[32:]) - if username is None: - raise struct.error - gid, = struct.unpack("!H", tail) - except struct.error: - raise BadReqError("User info request: Malformed request body") - -# logger.info("Trying to get user info with " \ -# "(token = {0}, gid = {1})" \ -# .format(get_hex(token), gid)) - - uauth = RequestHandler.get_uauth(token, username, self.session) - # Auth failure - if uauth is None: - logger.warning("Authentication failure") - return self._failed_response() - # TODO: check the relationship between user and quser - u = uauth.user - - grp = self.session.query(UserModel) \ - .filter(UserModel.sec_id == gid).all() - grp += self.session.query(UserModel) \ - .filter(UserModel.comp_id == gid).all() - - reply = struct.pack("!B", _StatusCode.sucess) - for user in grp: - reply += RequestHandler.pack_user_entry(user) - return self.pack(reply) - -class UpdateSubscription(RequestHandler): - - _optcode = _OptCode.update_subscription - _max_tr_data_size = _MAX_AUTH_HEAD_SIZE + \ - _SectionSize.GROUP_ID * _MAX_SUB_LIST_SIZE + \ - _SectionSize.PADDING - - def _find_group(self, gid): - q = self.session.query(GroupInfo).filter(GroupInfo.id == gid) - entry = q.first() - if not entry: - raise BadReqError("Group not found") - self.session.commit() - return entry - - def handle(self, tr_data, conn): - - self.check_size(tr_data) - logger.info("Reading update subscription data...") - try: - token, = struct.unpack("!32s", tr_data[:32]) - username, tail = RequestHandler.trunc_padding(tr_data[32:]) - if username is None: - raise struct.error - sub_list = RequestHandler.unpack_sub_list(tail) - except struct.error: - raise BadReqError("Update Subscription: Malformed request body") - -# logger.info("Trying to update subscription with " -# "(token = {0}, username = {1}, grps = {2})"\ -# .format(get_hex(token), username, str(sub_list))) - - uauth = RequestHandler.get_uauth(token, username, self.session) - # Authentication failure - if uauth is None: - logger.warning("Authentication failure") - return self.pack(struct.pack("!B", _StatusCode.auth_fail)) - - try: - uauth.user.sub = map(self._find_group, sub_list) - except BadReqError: - return self.pack(struct.pack("!B", _StatusCode.grp_not_found)) - self.session.commit() - logger.info("Subscription is updated sucessfully") - - return self.pack(struct.pack("!B", _StatusCode.sucess)) - -class UserLogoutHandler(RequestHandler): - - _optcode = _OptCode.user_logout - _max_tr_data_size = _MAX_AUTH_HEAD_SIZE - - def handle(self, tr_data, conn): - self.check_size(tr_data) - logger.info("Reading user logout data...") - try: - token, = struct.unpack("!32s", tr_data[:32]) - username, tail = RequestHandler.trunc_padding(tr_data[32:]) - if username is None: - raise struct.error - except struct.error: - raise BadReqError("User logout: Malformed request body") - -# logger.info("Trying to logout with " -# "(token = {0}, username = {1})"\ -# .format(get_hex(token), username)) - - uauth = RequestHandler.get_uauth(token, username, self.session) - # Authentication failure - if uauth is None: - logger.warning("Authentication failure") - return self.pack(struct.pack("!B", _StatusCode.auth_fail)) - - pt = RequestHandler.push_tunnels - u = uauth.user - loc = u.location - loc.lat = NOT_A_LAT - loc.lng = NOT_A_LNG - - comp = self.session.query(GroupInfo) \ - .filter(GroupInfo.id == u.comp_id).one() - sec = self.session.query(GroupInfo) \ - .filter(GroupInfo.id == u.sec_id).one() - - pdata = PushLocationData(u.id, loc.lat, loc.lng) - for user in comp.subscribers: - uid = user.id - if uid == uauth.uid: continue - if pt.has_key(uid): - tunnel = pt[uid] - tunnel.add(pdata) - tunnel.push() - - for user in sec.subscribers: - uid = user.id - if uid == uauth.uid: continue - if pt.has_key(uid): - tunnel = pt[uid] - tunnel.add(pdata) - tunnel.push() - - 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() - return self.pack(struct.pack("!B", _StatusCode.sucess)) - -class OpenPushTunnelHandler(RequestHandler): - - _optcode = _OptCode.open_push_tunnel - _max_tr_data_size = _MAX_AUTH_HEAD_SIZE - - def handle(self, tr_data, conn): - self.check_size(tr_data) - logger.info("Reading open push tunnel data...") - try: - token, = struct.unpack("!32s", tr_data[:32]) - username, tail = RequestHandler.trunc_padding(tr_data[32:]) - if username is None: - raise struct.error - except struct.error: - raise BadReqError("Open push tunnel: Malformed request body") - -# logger.info("Trying to open push tunnel with " -# "(token = {0}, username = {1})"\ -# .format(get_hex(token), username)) - - uauth = RequestHandler.get_uauth(token, username, self.session) - # Authentication failure - if uauth is None: - logger.warning("Authentication failure") - return self.pack(struct.pack("!B", _StatusCode.auth_fail)) - - tunnel = RequestHandler.push_tunnels[uauth.uid] - tunnel.connect(conn) - - logger.info("Push tunnel opened successfully!") - return self.pack(struct.pack("!B", _StatusCode.sucess)) - -class SendTextMessageHandler(RequestHandler): - - _optcode = _OptCode.send_text_mesg - _max_tr_data_size = _MAX_AUTH_HEAD_SIZE + \ - _MAX_TEXT_MESG_SIZE + \ - _SectionSize.PADDING - - def handle(self, tr_data, conn): - self.check_size(tr_data) - logger.info("Reading send text mesg data...") - try: - token, = struct.unpack("!32s", tr_data[:32]) - username, tail = RequestHandler.trunc_padding(tr_data[32:]) - mesg = tail[:-1] - if username is None: - raise struct.error - except struct.error: - raise BadReqError("Send text mesg: Malformed request body") - -# logger.info("Trying to send text mesg with " -# "(token = {0}, username = {1})"\ -# .format(get_hex(token), username)) - - uauth = RequestHandler.get_uauth(token, username, self.session) - # Authentication failure - if uauth is None: - logger.warning("Authentication failure") - return self.pack(struct.pack("!B", _StatusCode.auth_fail)) - - pt = RequestHandler.push_tunnels - u = uauth.user - ulist = self.session.query(UserModel) \ - .filter(UserModel.sec_id == u.sec_id).all() - - for user in ulist: - uid = user.id - if uid == uauth.uid: continue - if pt.has_key(uid): - tunnel = pt[uid] - tunnel.add(PushTextMesgData(mesg)) - tunnel.push() - logger.info("Sent text mesg successfully!") - return self.pack(struct.pack("!B", _StatusCode.sucess)) - -class SetMarkerHandler(RequestHandler): - - _optcode = _OptCode.set_marker - _max_tr_data_size = _MAX_AUTH_HEAD_SIZE + \ - _SectionSize.LATITUDE + \ - _SectionSize.LONGITUDE + \ - _SectionSize.DEADLINE - @classmethod - def push_new_marker(cls, u, session): - left = session.query(MarkerInfo) \ - .filter(MarkerInfo.status == MARKER_FRESH).all() - if len(left) == 0: - logger.warn("All markers have been used!") - return - marker = left[0] - marker.status = MARKER_DISPLAYED - session.commit() - pdata = PushMarkerData(perm = 2, lat = marker.lat, \ - lng = marker.lng, \ - deadline = 0x7fffffff, \ - mid = marker.id, \ - score = marker.score) - ulist = session.query(UserModel) \ - .filter(UserModel.comp_id == u.comp_id).all() - - pt = RequestHandler.push_tunnels - for user in ulist: - uid = user.id - if pt.has_key(uid): - tunnel = pt[uid] - tunnel.add(pdata) - tunnel.push() - - - def handle(self, tr_data, conn): - self.check_size(tr_data) - logger.info("Reading set marker data...") - try: - token, = struct.unpack("!32s", tr_data[:32]) - username, tail = RequestHandler.trunc_padding(tr_data[32:]) - lat, lng, deadline, mid, score = struct.unpack("!ddlBL", tail) - if username is None: - raise struct.error - except struct.error: - raise BadReqError("Set marker: Malformed request body") - - logger.info("Trying to set marker with " - "(token = {0}, username = {1})"\ - .format(get_hex(token), username)) - - uauth = RequestHandler.get_uauth(token, username, self.session) - # Authentication failure - if uauth is None: - logger.warning("Authentication failure") - return self.pack(struct.pack("!B", _StatusCode.auth_fail)) - - SetMarkerHandler.push_new_marker(uauth.user, self.session) - - logger.info("Set marker successfully!") - return self.pack(struct.pack("!B", _StatusCode.sucess)) - -class ChangePasswordHandler(RequestHandler): - - _optcode = _OptCode.change_password - _max_tr_data_size = _MAX_AUTH_HEAD_SIZE + \ - MAX_PASSWORD_SIZE + \ - _SectionSize.PADDING + \ - MAX_PASSWORD_SIZE + \ - _SectionSize.PADDING - - def handle(self, tr_data, conn): - self.check_size(tr_data) - logger.info("Reading change password data...") - try: - token, = struct.unpack("!32s", tr_data[:32]) - username, tail = RequestHandler.trunc_padding(tr_data[32:]) - if username is None: - raise struct.error - old_pass, tail = RequestHandler.trunc_padding(tail) - new_pass = tail[:-1] - except struct.error: - raise BadReqError("User logout: Malformed request body") - -# logger.info("Trying to change password with " -# "(token = {0}, username = {1}, old_pass = {2}, new_pass = {3})"\ -# .format(get_hex(token), username, old_pass, new_pass)) - - uauth = RequestHandler.get_uauth(token, username, self.session) - # Authentication failure - if uauth is None: - logger.warning("Authentication failure") - return self.pack(struct.pack("!B", _StatusCode.auth_fail)) - if not uauth.check_password(old_pass): - return self.pack(struct.pack("!B", _StatusCode.wrong_pass)) - uauth.set_password(new_pass) - self.session.commit() - logger.info("Password changed successfully!") - - pt = RequestHandler.push_tunnels - uid = uauth.uid - pt[uid].close() - del pt[uid] - uauth.regen_token() - - return self.pack(struct.pack("!B", _StatusCode.sucess)) - -class CheckInHandler(RequestHandler): - - _optcode = _OptCode.check_in - _max_tr_data_size = _MAX_AUTH_HEAD_SIZE + \ - _SectionSize.MARKER_ID - - def handle(self, tr_data, conn): - self.check_size(tr_data) - logger.info("Reading check-in data...") - try: - token, = struct.unpack("!32s", tr_data[:32]) - username, tail = RequestHandler.trunc_padding(tr_data[32:]) - if username is None: - raise struct.error - mid, = struct.unpack("!B", tail) - except struct.error: - raise BadReqError("Check-in: Malformed request body") - - logger.info("Trying to check-in with " - "(token = {0}, username = {1}, mid = {2})"\ - .format(get_hex(token), username, str(mid))) - - uauth = RequestHandler.get_uauth(token, username, self.session) - # Authentication failure - if uauth is None: - logger.warning("Authentication failure") - return self.pack(struct.pack("!B", _StatusCode.auth_fail)) - - try: - marker = self.session.query(MarkerInfo) \ - .filter(MarkerInfo.id == mid).one() - except NoResultFound: - raise BadReqError("Check-in: no such marker") - - if marker.status != MARKER_DISPLAYED: - return self.pack(struct.pack("!B", _StatusCode.checkin_fail)) - marker.status = MARKER_CHECKED - - pt = RequestHandler.push_tunnels - u = uauth.user - - comp = self.session.query(GroupInfo) \ - .filter(GroupInfo.id == u.comp_id).one() - sec = self.session.query(GroupInfo) \ - .filter(GroupInfo.id == u.sec_id).one() - - sec.score += marker.score - logger.info("Player {0} got score: {1}".format(username, str(marker.score))) - - score1 = self.session.query(GroupInfo) \ - .filter(GroupInfo.id == 5889).one().score - score2 = self.session.query(GroupInfo) \ - .filter(GroupInfo.id == 5890).one().score - - pdata = PushMarkerRemovalData(mid) - for user in comp.subscribers: - uid = user.id - if pt.has_key(uid): - tunnel = pt[uid] - tunnel.add(pdata) - tunnel.add(PushScoreData(score1, score2)) - tunnel.push() - - SetMarkerHandler.push_new_marker(u, self.session) - - logger.info("User checked in successfully!") - self.session.commit() - return self.pack(struct.pack("!B", _StatusCode.sucess)) - -class GameStartHandler(RequestHandler): - - _optcode = _OptCode.game_start - _max_tr_data_size = _MAX_AUTH_HEAD_SIZE - - def handle(self, tr_data, conn): - self.check_size(tr_data) - logger.info("Reading game-start data...") - try: - token, = struct.unpack("!32s", tr_data[:32]) - username, tail = RequestHandler.trunc_padding(tr_data[32:]) - if username is None: - raise struct.error - except struct.error: - raise BadReqError("Game-start: Malformed request body") - - logger.info("Trying to game-start with " - "(token = {0}, username = {1})"\ - .format(get_hex(token), username)) - - uauth = RequestHandler.get_uauth(token, username, self.session) - # Authentication failure - if uauth is None: - logger.warning("Authentication failure") - return self.pack(struct.pack("!B", _StatusCode.auth_fail)) - - - pt = RequestHandler.push_tunnels - u = uauth.user - - if u.perm != 2: - logger.warning("Insufficient privilege") - return self.pack(struct.pack("!B", _StatusCode.insuf_lvl)) - - team1 = self.session.query(GroupInfo) \ - .filter(GroupInfo.id == 5889).one() - team2 = self.session.query(GroupInfo) \ - .filter(GroupInfo.id == 5890).one() - - team1.score = 0 - team2.score = 0 - - for mrk in self.session.query(MarkerInfo).all(): - mrk.status = MARKER_FRESH - - users1 = self.session.query(UserModel) \ - .filter(UserModel.sec_id == team1.id) - - users2 = self.session.query(UserModel) \ - .filter(UserModel.sec_id == team2.id) - - for i in xrange(_INIT_MARKER_NUM): - SetMarkerHandler.push_new_marker(u, self.session) - - comp = self.session.query(GroupInfo) \ - .filter(GroupInfo.id == u.comp_id).one() - - for user in comp.subscribers: - uid = user.id - if pt.has_key(uid): - tunnel = pt[uid] - tunnel.add(PushScoreData(team1.score, team2.score)) - tunnel.push() - - logger.info("GAME START!") - self.session.commit() - return self.pack(struct.pack("!B", _StatusCode.sucess)) - - -class PTP(Protocol, TimeoutMixin): - - handlers = [UserAuthHandler, - UpdateLocationHandler, - UserInfoHandler, - UpdateSubscription, - UserLogoutHandler, - OpenPushTunnelHandler, - SendTextMessageHandler, - SetMarkerHandler, - ChangePasswordHandler, - CheckInHandler, - GameStartHandler] - - handler_num = len(handlers) - - _MAX_REQUEST_SIZE = _HEADER_SIZE + \ - max([h._max_tr_data_size for h in handlers]) - - @classmethod - def check_header(cls, header): - return 0 <= header < cls.handler_num - - def __init__(self, factory): - self.buff = bytes() - self.length = -1 - self.factory = factory - self.tunnel = None - - def timeoutConnection(self): - logger.info("The connection times out") - self.transport.loseConnection() - - def connectionMade(self): - logger.info("A new connection is made") - self.setTimeout(self.factory.timeout) - - def response(self, buff): - try: - h = PTP.handlers[self.optcode]() - reply = h.handle(buff[5:], self) -# logger.info("Wrote: %s", get_hex(reply)) - self.transport.write(reply) - if self.tunnel: - logger.info("Blocking the client...") - self.tunnel.push() - self.length = -1 - return - self.transport.loseConnection() - except BadReqError as e: - logger.warn("Rejected a bad request: %s", str(e)) - self.transport.loseConnection() - except DBCorruptionError: - logger.error("*** Database corruption ***") - - - def dataReceived(self, data): - self.buff += data - self.resetTimeout() - logger.info("Buffer length is now: %d", len(self.buff)) - if len(self.buff) <= 4: - return - try: - if self.length == -1: - try: - self.length, self.optcode = struct.unpack("!LB", self.buff[:5]) - if not PTP.check_header(self.optcode): # invalid header - raise struct.error - except struct.error: - raise BadReqError("Malformed request header") - if self.length > PTP._MAX_REQUEST_SIZE: - print self.length, PTP._MAX_REQUEST_SIZE - raise BadReqError("The size of remaining part is too big") - - if len(self.buff) >= self.length: - buff = self.buff[:self.length] - self.buff = self.buff[self.length:] - if self.tunnel: # received push response - self.tunnel.on_receive(buff) - self.length = -1 - return - self.setTimeout(None) - reactor.callFromThread(self.response, buff) - #self.response(buff) - except BadReqError as e: - logger.warn("Rejected a bad request: %s", str(e)) - self.transport.loseConnection() - - def connectionLost(self, reason): - if self.tunnel: - self.tunnel.on_connection_lost(self) - logger.info("The connection is lost") - self.setTimeout(None) - -class PTPFactory(Factory): - def __init__(self, timeout = 10): - self.timeout = timeout - def buildProtocol(self, addr): - return PTP(self) - -if os.name!='nt': - from twisted.internet import epollreactor - epollreactor.install() -else: - from twisted.internet import iocpreactor - iocpreactor.install() - -from twisted.internet import reactor - -f = PTPFactory() -f.protocol = PTP -reactor.listenTCP(2224, f) -logger.warning("The server is lanuched") -reactor.run() -- cgit v1.2.3-70-g09d2