import sqlalchemy
import SocketServer, socket, select
import struct
import os
from sqlalchemy import create_engine
from sqlalchemy import Column, Integer, String, Float
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from random import randint
engine = create_engine('sqlite:///t.sqlite', echo = False)
Base = declarative_base()
Session = sessionmaker(bind=engine)
def get_hex(data):
return "".join([hex(ord(c))[2:].zfill(2) for c in data])
class PiztorError(Exception):
def __init__(self, msg):
self.err_msg = msg
def __str__(self, msg):
return self.err_msg
class ConnectionError(PiztorError):
pass
class ReqReadError(ConnectionError):
def __init__(self):
super(ReqReadError, self).__init__("Error while reading request")
class ReqInvalidError(ConnectionError):
def __init__(self):
super(ReqInvalidError, self).__init__("Invalid request")
class TokenInvalidError(ConnectionError):
def __init__(self):
super(TokenInvalidError, self).__init__("Invalid token")
class DataManager(object):
def __init__(self, piz_srv):
self.piz_srv = piz_srv
class UserManager(DataManager):
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key = True)
gid = Column(Integer)
username = Column(String)
password = Column(String)
token = Column(Integer)
def get_user_by_token(self, token):
session = Session()
User = UserManager.User
entries = session.query(User).filter(User.token == token).all()
if len(entries) == 0:
raise TokenInvalidError()
return entries[0]
def authentication_handle(self, opt_type, data):
print "Parsing User Data"
pos = -1
for i in xrange(0, len(data)):
if data[i] == '\0':
print i
if pos != -1:
raise ReqInvalidError()
pos = i
break
if pos == -1:
raise ReqInvalidError()
username = data[0:pos]
password = data[pos + 1:]
print "Trying to login with following info:"
print (username, password)
session = Session()
entries = session.query(UserManager.User). \
filter(UserManager.User.username == username).all()
if len(entries) == 0:
return struct.pack("!BLB", 0, 0, 1)
entry = entries[0]
if entry.password != password: # Auth failed
print "Login failed!"
return struct.pack("!BLB", 0, 0, 1)
else: # Succeeded
print "Logged in sucessfully!"
entry.token = randint(0, 2147483647)
session.commit()
return struct.pack("!BLB", 0, entry.token, 0)
class MesgManager(DataManager):
def mesg_sending_handle(self, opt_type, data):
print "Parsing Mesg Data"
try:
if len(data) < 8:
raise ReqInvalidError()
sender_token, recv_id = struct.unpack("!LL", data[:8])
msg = data[8:]
print (sender_token, recv_id, msg)
return struct.pack("!B", 1)
except struct.error:
raise ReqInvalidError()
class LocationManager(DataManager):
class LocationInfo(Base):
__tablename__ = "location_info"
uid = Column(Integer, primary_key = True)
lat = Column(Float(precesion = 64))
lng = Column(Float(precesion = 64))
# More: last_update
def location_update_handle(self, opt_type, data):
print "Parsing a Location Update"
try:
if len(data) < 8:
raise ReqInvalidError()
sender_token, lat, lng = struct.unpack("!Ldd", data)
print "Updating location data with following info:"
print (sender_token, lat, lng)
user = self.piz_srv. \
user_mgr.get_user_by_token(sender_token)
session = Session()
LInfo = LocationManager.LocationInfo
q = session.query(LInfo).filter(LInfo.uid == user.id)
entry = q.first()
entry.lat = lat
entry.lng = lng
session.commit()
print "Location update succeeded!"
return struct.pack("!BB", 2, 0)
except TokenInvalidError:
print "Location update failed!"
return struct.pack("!BB", 2, 1)
except struct.error:
raise ReqInvalidError()
def location_request_handle(self, opt_type, data):
print "Parsing a Location Request"
try:
if len(data) != 8:
raise ReqInvalidError()
sender_token, gid = struct.unpack("!LL", data)
print "Requesting location data with following info:"
print (sender_token, gid)
session = Session()
UInfo = UserManager.User
LInfo = LocationManager.LocationInfo
user_list = session.query(UInfo).filter(UInfo.gid == gid).all()
reply = struct.pack("!BL", 3, len(user_list))
for user in user_list:
loc = session.query(LInfo).filter(LInfo.uid == user.id).first()
reply += struct.pack("!Ldd", user.id, loc.lat, loc.lng)
print get_hex(reply)
return reply
except struct.error:
raise ReqInvalidError()
class PiztorServer():
class GenericHandler(SocketServer.StreamRequestHandler):
def handle(self):
sock = self.request
sock.settimeout(10)
# sock.setblocking(0)
data = ""
try:
while True:
# ready = select.select([sock], [], [], 10)
# if not ready[0]:
# raise ReqReadError()
buff = sock.recv(4096)
if len(buff) == 0:
break # terminated
else:
data += buff
sock.shutdown(socket.SHUT_RD)
print "Got the data:" + get_hex(data)
if len(data) < 1:
print "invalid length"
raise ReqInvalidError()
opt_id = struct.unpack("!B", data[0])[0]
print opt_id
reply = self.piz_srv.mgr_map[opt_id](opt_id, data[1:])
sock.sendall(reply)
finally:
sock.close()
class ForkingEchoServer(SocketServer.ForkingMixIn, SocketServer.TCPServer):
pass
def __init__(self, host, port):
PiztorServer.GenericHandler.piz_srv = self
srv = PiztorServer.ForkingEchoServer((host, port),
PiztorServer.GenericHandler)
srv.request_queue_size = 100
# srv.timeout = 2
self.server = srv
self.user_mgr = UserManager(self)
self.mesg_mgr = MesgManager(self)
self.location_mgr = LocationManager(self)
self.mgr_map = [ self.user_mgr.authentication_handle,
self.mesg_mgr.mesg_sending_handle,
self.location_mgr.location_update_handle,
self.location_mgr.location_request_handle]
Base.metadata.create_all(engine)
def run(self):
try:
self.server.serve_forever()
except KeyboardInterrupt:
print "Exiting..."
self.server.shutdown()
print "Server shutdown"
if __name__ == "__main__":
ps = PiztorServer("192.168.1.101", 9990)
ps.run()