· 8 years ago · Nov 21, 2017, 03:48 PM
1from multiprocessing import Process
2import socket,time,sqlite3,time,os,random
3try:
4 import serv_settings
5except:
6 f = open("serv_settings.py","w")
7 t = '''
8IP_ADDRESS = ""
9
10PORT = 9090
11
12KEY_DECAY_TIME = 3600
13
14ONLINE_CONFIRM_TIME = 600
15
16LOGGING = 1
17
18LOGFOLDER = "logs"
19
20MAX_CLIENTS = 100
21 '''
22 f.write(t)
23 f.close()
24 import serv_settings
25
26
27
28def error_p(errmsg):
29 print("Error: "+errmsg)
30
31def logmsg(logmsg):
32 a = time.strftime("%H-%M %d %h %Y")
33 print(a+": "+logmsg)
34
35def add_nulls(dlen,data):
36 to_ret = data
37 if(len(data)<dlen):
38 dif = dlen-len(data)
39 to_ret = "0"*dif+to_ret
40 return to_ret
41
42class logging_db:
43 def __init__(self):
44 cur,connection = self.connect_to_db()
45 cur.execute('''
46 CREATE TABLE IF NOT EXISTS errlogs(
47 ADDR TEXT,
48 DATE TEXT,
49 ERROR TEXT)
50 ''')
51 cur.execute('''
52 CREATE TABLE IF NOT EXISTS traffics(
53 ADDR TEXT,
54 TYPE TEXT,
55 AMOUNT INTEGER)
56 ''')#amount in bytes
57 connection.commit()
58 connection.close()
59 #todo
60 #make traffic counting
61 #make error logging here
62 #traffic counted by dates and users
63 #tables must be wiped sometimes))))00000
64
65 def add_traffic(self,addr,ttype,amount):
66 cur,connection = self.connect_to_db()
67 cur.execute("SELECT AMOUNT FROM traffics WHERE ADDR = ? AND TYPE = ?",(addr,ttype))
68 d = cur.fetchone()
69 if(d!=None):
70 num = d[0]
71 num+=amount
72 cur.execute("UPDATE traffics SET AMOUNT = ? WHERE ADDR = ? AND TYPE = ?",(num,addr,ttype))
73 else:
74 cur.execute("INSERT INTO traffics VALUES (?,?,?)",(addr,ttype,amount))
75 connection.commit()
76 connection.close()
77
78 def add_error(self,addr,error):
79 date = time.time()
80 cur,connection = self.connect_to_db()
81 cur.execute("INSERT INTO errlogs VALUES (?,?,?)",(addr,str(date),error))
82 connection.commit()
83 connection.close()
84
85 def connect_to_db(self):
86 try:
87 connection = sqlite3.connect(serv_settings.LOGFOLDER+"/"+"log.db")
88 except:
89 os.mkdir(serv_settings.LOGFOLDER)
90 connection = sqlite3.connect(serv_settings.LOGFOLDER+"/"+"log.db")
91 cur = connection.cursor()
92 return(cur,connection)
93
94
95
96
97class dbwork:
98 #1)login,password,id - logindata
99 #2)id,key,time_to_change_key - keys
100 #3)id,time_to_offline - onlines
101 #4)id,unread_messages - messages
102 def __init__(self):
103 cur,connection = self.connect_to_db()
104 #creating tables
105 cur.execute('''
106 CREATE TABLE IF NOT EXISTS logindata(
107 ID INTEGER PRIMARY KEY,
108 LOGIN TEXT,
109 PASSWORD TEXT)
110 ''')
111 cur.execute('''
112 CREATE TABLE IF NOT EXISTS keys(
113 ID INTEGER,
114 KEY INTEGER,
115 DTIME INTEGER)
116 ''')
117 cur.execute('''
118 CREATE TABLE IF NOT EXISTS onlines(
119 ID INTEGER PRIMARY KEY,
120 OTIME INTEGER)
121 ''')
122 cur.execute('''
123 CREATE TABLE IF NOT EXISTS messages(
124 ID INTEGER,
125 MESSAGE BLOB)
126 ''')#ID of the user to recive message
127 connection.commit()
128 connection.close()
129
130 def connect_to_db(self):
131 connection = sqlite3.connect("chatdb.db")
132 cur = connection.cursor()
133 return(cur,connection)
134
135 def generate_key(self,ID): #generates a new key for given ID
136 random.seed()
137 cur,connection = self.connect_to_db()
138 key = random.randint(10000000,99999999)
139 ok = 0
140 while(ok==0): #generating a unique key for messaging
141 cur.execute("SELECT * FROM keys WHERE KEY = ?",(key,))
142 if(cur.fetchone()==None):
143 ok = 1
144 break
145 else:
146 key = random.randint(10000000,99999999)
147 cur.execute("SELECT * FROM keys WHERE ID = ?",(ID,)) #checking if the
148 dtime = time.time()+serv_settings.KEY_DECAY_TIME #ID in table
149 if(cur.fetchone()==None):
150 cur.execute("INSERT INTO keys VALUES (?,?,?)",(ID,key,dtime))
151 else:
152 cur.execute("UPDATE keys SET KEY = ?, DTIME = ? WHERE ID = ?",(key,dtime,ID))
153 connection.commit()
154 connection.close()
155 return(key)
156
157 def get_messages(self,ID):
158 cur,connection = self.connect_to_db()
159 cur.execute("SELECT MESSAGE FROM messages WHERE ID = ?",(ID,))
160 msgs = cur.fetchall()
161 msgdata = b""
162 if(msgs==[]):
163 connection.close()
164 return(0)
165 else:
166 for msg in msgs:
167 message = msg[0]
168 msgdata+=message
169 cur.execute("DELETE FROM messages WHERE ID = ?",(ID,))
170 connection.commit()
171 connection.close()
172 return(msgdata)
173
174 def get_ID_by_login(self,login):
175 cur,connection = self.connect_to_db()
176 cur.execute("SELECT ID FROM logindata WHERE LOGIN = ?",(login,))
177 ID = cur.fetchone()[0]
178 connection.close()
179 return(ID)
180
181 def get_key(self,ID): #returns a key for given ID
182 cur,connection = self.connect_to_db()
183 cur.execute("SELECT KEY FROM keys WHERE ID = ?",(ID,))
184 key = cur.fetchone()
185 if(key!=None):
186 key=key[0]
187 connection.close()
188 return(key)
189
190 def get_key_dtime(self,ID):
191 cur,connection = self.connect_to_db()
192 cur.execute("SELECT DTIME FROM keys WHERE ID = ?",(ID,))
193 dtime = cur.fetchone()
194 if(dtime!=None):
195 dtime=dtime[0]
196 connection.close()
197 return(dtime)
198
199 def get_ID_by_key(self,key):
200 cur,connection = self.connect_to_db()
201 cur.execute("SELECT ID, DTIME FROM keys WHERE key = ?",(key,))
202 uid = cur.fetchone()
203 connection.close()
204 if(uid==None):
205 return(-1)#no such key
206 else:
207 dtime = uid[1]
208 uid = uid[0]
209 if(dtime<time.time()):
210 return(-2)#timed out key
211 else:
212 return(uid)
213
214 def update_user_online(self,ID):
215 otime = time.time()+serv_settings.ONLINE_CONFIRM_TIME
216 cur,connection = self.connect_to_db()
217 cur.execute("REPLACE INTO onlines VALUES(?,?)",(ID,otime))
218 connection.commit()
219 connection.close()
220
221 def get_users_online(self):
222 ctime = time.time()
223 cur,connection = self.connect_to_db()
224 cur.execute("SELECT ID FROM onlines WHERE OTIME > ?",(ctime,))
225 onlineIDs = cur.fetchall()
226 onlines = []
227 if(onlineIDs == []):
228 return([])
229 else:
230 for oid in onlineIDs:
231 onlines.append(oid[0])
232 return(onlines)
233
234 def add_message(self,ID,msg):
235 cur,connection = self.connect_to_db()
236 cur.execute("INSERT INTO messages VALUES(?,?)",(ID,msg))
237 connection.commit()
238 connection.close()
239
240 def login(self,login,password):
241 cur,connection = self.connect_to_db()
242 cur.execute("SELECT * FROM logindata WHERE LOGIN = ?",(login,))
243 udata = cur.fetchone()
244 if(udata==None):
245 cur.execute("INSERT INTO logindata VALUES (NULL,?,?)",(login,password))
246 connection.commit()
247 connection.close()
248 ID = self.get_ID_by_login(login)
249 key = self.generate_key(ID)
250 self.update_user_online(ID)
251 return([0,key]) #OK - new registered
252 else:
253 if(udata[2]==password):
254 connection.close()
255 ID = self.get_ID_by_login(login)
256 key = self.generate_key(ID)
257 self.update_user_online(ID)
258 return([0,key]) #OK - ok login&pwd
259 else:
260 connection.close()
261 return([1,0]) #login already exists(it means - password incorrect)
262
263
264class client_job:
265
266 def send_close(self,data):
267 self.send_msg(data)
268 self.conn.close()
269 def send_msg(self,data):
270 count = 0
271 try:
272 try:
273 self.conn.send(data.encode("utf-8"))
274 count = len(data.encode("utf-8"))
275 except:
276 self.conn.send(data)
277 count = len(data)
278 except:
279 self.write_log("error sending to: "+str(self.addr))
280 return
281 self.count_traffic(count,"out")
282
283 def answer_ask_chk(self):
284 self.ID = self.database.get_ID_by_key(self.key)
285 if(self.ID>0):
286 dtime = self.database.get_key_dtime(self.ID)
287 dtime-=time.time()
288 dtime = add_nulls(4,str(dtime))
289 self.database.update_user_online(self.ID)
290 self.send_msg("OK"+dtime)
291 return(-1)
292 else:
293 if(self.ID==-1):
294 self.error = 5 #wrong key
295 else:
296 self.error = 6 #key timed out
297 return(0)
298
299 def read_ask_msg(self,data):
300 try:
301 ask_m = data.decode("utf-8")
302 except:
303 self.error = 1 #encoding failure
304 return(0)
305 if(len(ask_m)!=11):
306 self.error = 3 #message badly structured
307 return(0)
308 mtype = ask_m[:3]
309 if(mtype=="CHK"):
310 try:
311 self.key = int(ask_m[3:])
312 except:
313 self.error = 5
314 return(0)
315 return self.answer_ask_chk()
316 if(mtype=="MES"):
317 try:
318 self.protocol = int(ask_m[3:7])
319 ret_bytes = int(ask_m[7:])
320 except:
321 self.error = 1
322 return(0)
323 return ret_bytes
324 else:
325 self.error = 2 #unknown initiation
326 return(0)
327
328 def check_key_ID(self):
329 realID = self.database.get_ID_by_key(self.key)
330 if(realID==self.ID):
331 return(1)
332 else:
333 if(realID==-1):
334 self.error = 5
335 return(0)
336 else:
337 self.error = 6
338 return(0)
339
340
341 def process_message(self,trg_ID,message):
342 if(self.check_key_ID()==1):
343 if(self.database.get_key(trg_ID)==None):
344 self.error = 9
345 return(0)
346 else:
347 msglen = str(add_nulls(4,str(len(message)))).encode("utf-8")
348 sender = str(add_nulls(8,str(self.ID))).encode("utf-8")
349 dbmsg = sender+msglen+message #add a sender's ID and msglen
350 self.database.add_message(trg_ID,dbmsg)
351 self.database.update_user_online(self.ID)
352 msg = "OK"+add_nulls(16,"")
353 self.send_msg(msg)
354 return(-1)
355 else:
356 self.error = 5
357 return(0)
358
359 def read_msg(self,data,len_m):
360 if(len(data)!=len_m):
361 self.error = 3
362 return(0)
363 try:
364 mtype = data[:3].decode("utf-8")
365 except:
366 self.error = 1
367 return(0)
368 if(data[-3:].decode("utf-8")!="FIN"):
369 self.error = 3
370 return(0)
371 if(mtype=="MSG"):#messages can be not a unicode str
372 if(self.protocol==1):
373 try:
374 self.ID = int(data[3:11].decode("utf-8"))
375 self.key = int(data[11:19].decode("utf-8"))
376 trg_ID = int(data[19:27].decode("utf-8"))
377 except:
378 self.error = 3
379 return(0)
380 msg = data[27:-3]
381 return(self.process_message(trg_ID,msg))
382 else:
383 self.error = 4 #protocol mismatch
384 return(0)
385 try:
386 cl_data = data.decode("utf-8")
387 except:
388 self.error = 1
389 return(0)
390 if(cl_data[-3:]!="FIN"):
391 self.error = 3
392 return(0)
393 mtype = cl_data[:3]
394 if(mtype=="LOG"):
395 if(self.protocol==1):
396 try:
397 llen = int(cl_data[3:7])
398 plen = int(cl_data[7:11])
399 except:
400 self.error = 3
401 return(0)
402 self.login = cl_data[11:11+llen]
403 self.password = cl_data[11+llen:11+llen+plen]
404 result = self.database.login(self.login,self.password)
405 if(result[0]==0):
406 self.key = result[1]
407 self.ID = self.database.get_ID_by_key(self.key)
408 if(self.ID<0):
409 self.error = 7 #internal error???? must not happen
410 return(0)
411 else:
412 msg = "OK"+add_nulls(8,str(self.key))+add_nulls(8,str(self.ID))
413 self.send_msg(msg)
414 return(-1)
415 else:
416 self.error = 8 #wrong password for existing login
417 return(0)
418 else:
419 self.error = 4
420 return(0)
421 if(mtype=="UPD"):
422 if(self.protocol==1):
423 try:
424 self.key = int(cl_data[3:11])
425 except:
426 self.error = 5
427 return(0)
428 self.ID = self.database.get_ID_by_key(self.key)
429 if(self.ID>0):
430 msgdata = self.database.get_messages(self.ID)
431 if(msgdata==0):
432 self.error = 10
433 return(0)
434 else:
435 msgdata = msgdata
436 msg = "OK"+add_nulls(16,str(len(msgdata)))
437 self.database.update_user_online(self.ID)
438 self.send_msg(msg)
439 self.send_msg(msgdata)
440 return(-1)
441 else:
442 self.error = 5
443 return(0)
444 else:
445 self.error = 4
446 return(0)
447 if(mtype=="WOL"):
448 if(self.protocol==1):
449 onlines = self.database.get_users_online()
450 if(onlines==[]):
451 self.error = 11
452 return(0)
453 outmsg = ""
454 for oid in onlines:
455 outmsg+=add_nulls(8,str(oid))
456 outmsg = outmsg.encode("utf-8")
457 lenmsg = len(outmsg)
458 msg = "OK"+add_nulls(16,str(lenmsg))
459 self.send_msg(msg)
460 self.send_msg(outmsg)
461 return(-1)
462 else:
463 self.error = 4
464 return(0)
465
466 def write_log(self,errtext):
467 if(serv_settings.LOGGING==1 and self.error!=10 and self.error!=11):
468 self.log.add_error(self.addr[0],errtext)
469 outstr="Error ID: "+str(self.error)+" "
470 outstr+="Error text: "+errtext+"\n"
471 print(outstr)
472
473 def count_traffic(self,num,traffic_type):
474 if(serv_settings.LOGGING==1):
475 self.log.add_traffic(self.addr[0],traffic_type,num)
476
477
478 def work_with_client(self,conn,addr):
479 self.log = logging_db()
480 self.database = dbwork()
481 self.conn = conn
482 self.addr = addr
483 self.ID = ""
484 self.key = ""
485 self.login = ""
486 self.password = ""
487 self.protocol = 0
488 self.error = 0 #zero is for unknown error
489 try:
490 data = self.conn.recv(11)
491 except:
492 print(1)
493 to_recieve = self.read_ask_msg(data)
494 if(to_recieve==0):
495 bmsg = "BA"+add_nulls(4,str(self.error))
496 self.send_close(bmsg.encode("utf-8"))
497 self.write_log("initiation bad from: "+str(self.addr))
498 return
499 elif(to_recieve==-1):
500 self.conn.close()
501 return
502 else:
503 self.send_msg("OK0000".encode("utf-8"))
504 data = conn.recv(to_recieve)
505 to_recieve = self.read_msg(data,to_recieve)
506 if(to_recieve==0):
507 bmsg = "BA"+add_nulls(16,str(self.error))
508 self.send_close(bmsg.encode("utf-8"))
509 self.write_log("shit with message from: "+str(self.addr))
510 else:
511 self.conn.close()
512
513#KEEP IN MIND THAT MULTIPROCESS DOES NOT WORK IN IDLE, START THE SERVER
514#USING A DOUBLECLICK ON PY FILE
515
516def start_process(conne,addre):
517 job = client_job()
518 job.work_with_client(conne,addre)
519
520if(__name__=="__main__"):
521 sock = socket.socket()
522 sock.bind((serv_settings.IP_ADDRESS, serv_settings.PORT))
523 sock.listen(serv_settings.MAX_CLIENTS)
524 while True:
525 conn, addr = sock.accept()
526 answ = Process(target=start_process,args=(conn,addr))
527 answ.start()
528 answ.join()