Пример #1
0
    def __create(self) -> None:
        if path.exists('server.db'):
            return
        try:
            exceptions.log_info("creating the db table")
            if not self._connect():
                raise exceptions.ConnectionException(
                    "Failed to connect the db.")
            client_tableStr = '''CREATE TABLE ClientTbl
                                              (ID VARBINARY(16) PRIMARY KEY,
                                               PublicKey VARBINARY(160) NOT NULL,
                                               UName VARCHAR(255) NOT NULL UNIQUE,
                                               LastSeen TEXT)'''
            message_table = '''CREATE TABLE message_table
                                              (ID INTEGER PRIMARY KEY AUTOINCREMENT,
                                               ToClient VARBINARY(16) ,
                                               FromClient VARBINARY(16) ,
                                               Type TEXT,
                                               Content BLOB,
                                               FOREIGN KEY(ToClient,FromClient) REFERENCES ClientTbl(ID,ID))'''
            curs = self._conn.cursor()
            curs.execute(client_tableStr)
            curs.execute(message_table)

        except sqlite3.Error as e:
            exceptions.log_error("failed to create the db tables.", e)
            print("failed creating the data base tables.")
        finally:
            curs.close()
Пример #2
0
 def accept(self):
     """accept a connection request from clients"""
     conn, self._target_address = self.__sock.accept()
     exceptions.log_info("accept connection: " + str(conn) + ", from: " +
                         str(self._target_address))
     conn.setblocking(False)
     self.__read(conn)
Пример #3
0
 def __read(self, conn: socket.socket) -> None:
     """read the data from the socket and handle the request, if valid.
         conn :param - connection from client
         Exception :raises - if connection broken or message is too big or
                             the data received isn't correlate with the protocol
         None :returns"""
     header_len = 22
     try:
         data = conn.recv(self.__max_pack__)  # Should be ready
         if len(data) < header_len:  # header_len should be <= len(data)
             print('closing', conn)
             self.__selector.unregister(conn)
             conn.close()
             return None
         fields = struct.unpack('<' + str(self.__UID_LEN__) + 's B B I',
                                data[:header_len])
         exceptions.log_info("received: " + str(fields) + "...")
         usr_id, ver, code, payload_size = fields
         data = data[header_len:]
         if ver != 2:  # change this condition to allow old version to get treatment
             raise exceptions.VersionException(
                 "Error: the client, version is: ", ver, ".")
         if code not in self.__requests_tbl.values():
             raise exceptions.InvalidCodeException(
                 "Error: the code, ", code, ", doesn't match any request.")
         if code == self.__requests_tbl['rgstr']:
             self.register(data, payload_size)
             return None
         elif code == self.__requests_tbl[
                 'usr_lst'] or code == self.__requests_tbl['pull_msg']:
             if payload_size:
                 raise exceptions.DataException("no payload expected!")
             if code == self.__requests_tbl['pull_msg']:
                 self.__send(self.__response_tbl['msgs_pulled'],
                             dbClasses.MsgTbl().pull(usr_id))
                 return None
             self.__send(self.__response_tbl['usr_lst_returned'],
                         dbClasses.ClientTbl().get_usrs(usr_id))
             return
         elif code == self.__requests_tbl['usr_key']:
             # other_uid = struct.unpack('>' + str(UID_LEN) + 's', data[:UID_LEN])
             self.__send(self.__response_tbl['usr_key_returned'],
                         dbClasses.ClientTbl().get_usr(
                             data[:self.__UID_LEN__]))  # [::-1]))
             return None
         # send msg
         self.__handle_message(conn, usr_id, data)
     except exceptions.ConnectionException as conExcept:  # as conExcept:
         exceptions.log_error(
             "while reading from " + str(self._target_address), conExcept)
         return
     except Exception as e:
         exceptions.log_error("", e)
         self.__send(self.__response_tbl['gnrl_err'])
     finally:
         if conn:
             conn.close()
Пример #4
0
def connect(address):
    if not address:
        return None
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        exceptions.log_info("trying to connect: " + str((address[0], 8080)))
        sock.connect((address[0], 8080))
    except Exception as e:
        raise exceptions.ConnectionException(e)
    return sock
Пример #5
0
 def get_usrs(self, uid):
     self.update(uid)
     if self._connect():
         try:
             exceptions.log_info("loading the users list.")
             cur = self._conn.cursor()
             cur.execute("SELECT ID, UName FROM ClientTbl WHERE ID <> ?",
                         [uid])
             return cur.fetchall()
         except Exception as ex:
             exceptions.log_error("failed to get clients from the db.", ex)
         finally:
             if cur:
                 cur.close()
             if self._conn:
                 self._conn.close()
     return []
Пример #6
0
 def register(self, data: str, payloadSize: int):
     name, pub_key = 256, 160
     if payloadSize not in range(pub_key + 1, pub_key + name):
         raise exceptions.RegistrationException(
             "the payload size is out of range, " + str(payloadSize))
     fields = struct.unpack(
         '<' + str(payloadSize - pub_key) + 's ' + str(pub_key) + 's',
         data[:payloadSize])
     # fields = [x[::-1] for x in fields]
     name, pub_key = fields
     usr_id = uuid.uuid4()
     client = dbClasses.ClientTbl()
     if not client.insert(usr_id.bytes, name.lower(), pub_key):
         raise exceptions.RegistrationException("the user with id: ",
                                                usr_id, "already exist")
     self.__send(self.__response_tbl['rgstr_succeeded'], usr_id)
     exceptions.log_info("The client " + name + " just register.")
     return
Пример #7
0
 def __handle_content(self, data: bytes, content_size: int,
                      conn: socket.socket):
     content = data
     left_to_read = content_size - len(data)
     if left_to_read > 0:
         rec_cnt = len(data)
         BLOCK_SIZE = 1 << 14
         while content_size > rec_cnt:
             bytes_to_read = min(BLOCK_SIZE, abs(content_size - rec_cnt))
             tmp = conn.recv(bytes_to_read)
             if not tmp:
                 break
             content += tmp
             rec_cnt += len(tmp)
         exceptions.log_info("received " + str(len(content)) +
                             ' bytes, from ' + str(content_size) +
                             ' bytes.')
     elif left_to_read < 0:
         content = content[:content_size]
     return struct.unpack('<' + str(len(content)) + 's', content)
Пример #8
0
 def insert(self, usr_id, uname, pubkey):
     if self._connect():
         try:
             exceptions.log_info("inserting the user, to the db")
             cur = self._conn.cursor()
             cur.execute(
                 "INSERT INTO ClientTbl(ID,PublicKey,UName,LastSeen) VALUES(?,?,?,?)",
                 [usr_id, pubkey, uname,
                  datetime.now()])
             self._conn.commit()
             return True
         except Exception as ex:
             exceptions.log_error("failed to insert client to the db.", ex)
             self._conn.rollback()
         finally:
             if cur:
                 cur.close()
             if self._conn:
                 self._conn.close()
     return False
Пример #9
0
 def insert(self, from_usr, to_usr, msg_type, msg_cntnt) -> int:
     ClientTbl().update(from_usr)
     exceptions.log_info("inserting message to the db")
     if self._connect():
         try:
             cur = self._conn.cursor()
             cur.execute(
                 "INSERT INTO message_table(FromClient, ToClient, Type, Content) VALUES(?,?,?,?)",
                 [from_usr, to_usr,
                  str(msg_type), msg_cntnt])
             self._conn.commit()
             cur.execute("SELECT COUNT(ID) FROM message_table")
             return cur.fetchone()[0]
         except Exception as ex:
             exceptions.log_error("failed to insert message to the db", ex)
             self._conn.rollback()
         finally:
             if cur:
                 cur.close()
             if self._conn:
                 self._conn.close()
     return 0
Пример #10
0
 def __handle_message(self, conn: socket.socket, usr_id: bytes,
                      data: bytes) -> None:
     """handle message from the server's client and save it until the target pull it
         conn :param - connection from client
         usr_id :param - source client uuid
         data :param - the message data
         Exception :raises - if connection broken or message is too big or data isn't correlate with the protocol
         None :returns"""
     exceptions.log_info("start to handle message.")
     other_uid, msg_type, content_size = self.__handle_header(data)
     data = data[
         self.__UID_LEN__ +
         5:]  # ToDo: move to using index instead of copy substring into data
     exceptions.log_info("the message info is: " +
                         str((msg_type, content_size)))
     self.__handle_msg_type(msg_type, content_size, usr_id, other_uid)
     content = self.__handle_content(data, content_size, conn)
     msg_id = dbClasses.MsgTbl().insert(usr_id, other_uid, msg_type,
                                        content[0])
     if msg_id == 0:
         self.__send(self.__response_tbl['gnrl_err'])
         return
     self.__send(self.__response_tbl['msg_received'], [other_uid, msg_id])
Пример #11
0
 def pull(self, usr_id):
     ClientTbl().update(usr_id)
     exceptions.log_info("pulling messages from the db")
     if self._connect():
         try:
             cur = self._conn.cursor()
             cur.execute(
                 "SELECT FromClient, ID, Type, Content FROM message_table WHERE ToClient = ?",
                 [usr_id])
             pulled_msgs = cur.fetchall()
             # id_lst = [x[1] for x in pulled_msgs]
             # cur.execute("DELETE FROM message_table WHERE ID IN ({})".format(", ".join("?" * len(id_lst))), id_lst)
             self._conn.commit()
             return pulled_msgs
         except Exception as ex:
             exceptions.log_error(
                 "failed to pull client messages from the db.", ex)
             self._conn.rollback()
         finally:
             if cur:
                 cur.close()
             if self._conn:
                 self._conn.close()
     return ""
Пример #12
0
 def __send(self, code, payload=None):
     """send the response to the client
         code :param - response code
         payload :param - response payload
         Exception :raises  if failed to connect or message is too big or
                            the response data isn't correlate with the protocol
         the length of the response :returns"""
     exceptions.log_info("sending the response: " + str(code))
     if not self._target_address:
         raise Exception("error: client address lost, can't send messages.")
     if code not in self.__response_tbl.values():
         raise exceptions.InvalidCodeException(
             f"Error: the code, {code}, is not valid.")
     to_send = struct.pack('<B H', self.__VER__,
                           code)  # pack the version and the respond code
     if code == self.__response_tbl['gnrl_err']:
         to_send += struct.pack('<I', 0)
     elif code == self.__response_tbl[
             'msgs_pulled'] or code == self.__response_tbl[
                 'usr_lst_returned']:
         tmp = b''
         if code == self.__response_tbl['usr_lst_returned']:
             for rec in payload:
                 name_len = len(rec[1])
                 tmp += struct.pack(
                     '<' + str(self.__UID_LEN__) + 's' + str(name_len) +
                     's ' + str(self.__NAME_LEN__ - name_len) + 'x', rec[0],
                     rec[1])
         else:
             for rec in payload:
                 tmp += struct.pack(
                     '<' + str(self.__UID_LEN__) + 's I c I ' +
                     str(len(rec[3])) + 's', rec[0], rec[1],
                     int(rec[2]).to_bytes(1, 'little'), len(rec[3]), rec[3])
         to_send += struct.pack('<I', len(tmp)) + tmp
     elif code != self.__response_tbl['gnrl_err'] and not payload:
         raise exceptions.MissingDataException(
             "Error: expect to receive some data to send, but received None."
         )
     elif code == self.__response_tbl['msg_received']:
         tmp = struct.pack('<' + str(self.__UID_LEN__) + 's I', payload[0],
                           payload[1])
         to_send += struct.pack('<I', len(tmp)) + tmp
     elif code == self.__response_tbl['usr_key_returned']:
         to_send += struct.pack(
             '<I ' + str(self.__UID_LEN__) + 's ' +
             str(self.__PUB_KEY_SIZE__) + 's',
             self.__PUB_KEY_SIZE__ + self.__UID_LEN__, *payload)
     elif code == self.__response_tbl['rgstr_succeeded']:
         to_send += struct.pack('< I ' + str(self.__UID_LEN__) + 's',
                                self.__UID_LEN__, payload.bytes)  # [::-1])
     padding_size = self.__max_pack__ - len(to_send)
     if padding_size > 0:
         to_send += struct.pack(str(padding_size) + 'x')
     sock: socket
     try:
         sock = connect(self._target_address)
     except Exception as e:
         exceptions.log_info(f"failed to connect to {self._target_address}",
                             e)
     exceptions.log_info("sending " + str(len(to_send)) + " bytes.")
     if sock.sendall(to_send) == 0:
         raise exceptions.BrokenConnectionException(
             "socket connection broken.")
     sock.close()
     return len(to_send)