Пример #1
0
    def run(self):
        """Main client transport cycle thread method.

        Receives incoming messages, passes them to the processing method and watches over the server connection."""

        CLIENT_LOG.debug('Server message transport launched.')
        while self.running:
            time.sleep(1)
            with socket_lock:
                try:
                    self._socket.settimeout(0.5)
                    message = receive_message(self._socket, CLIENT_LOG)
                except OSError as err:
                    if err.errno:
                        CLIENT_LOG.critical('Lost connection to the server!')
                        self.running = False
                        self.connection_lost.emit()
                except (ConnectionError, ConnectionResetError,
                        ConnectionAbortedError, json.JSONDecodeError,
                        TypeError):
                    CLIENT_LOG.critical('Lost connection to the server!')
                    self.running = False
                    self.connection_lost.emit()
                else:
                    CLIENT_LOG.debug(f'Got a message: {message}')
                    self.process_server_answer(message)
                finally:
                    self._socket.settimeout(5)
Пример #2
0
 def update_possible_contacts(self):
     """Method for updating a complete user list from the server."""
     try:
         self.transport.user_list_update()
     except OSError:
         pass
     else:
         CLIENT_LOG.debug('Updated userlist from the server')
         self.list_possible_contacts()
Пример #3
0
    def del_contact(self, contact):
        """Method for informing a server about contact removal."""

        CLIENT_LOG.debug(f'Removing a contact {contact}')
        req = self.form_edit_contact_message(contact, op='/del')

        with socket_lock:
            send_message(req, self._socket, CLIENT_LOG)
            self.process_server_answer(
                receive_message(self._socket, CLIENT_LOG))
Пример #4
0
    def form_users_request(self):
        """Method for forming a server users request."""

        req_obj = {
            JIM.ACTION: JIM.Actions.GET_USERS,
            JIM.TIME: int(datetime.now().timestamp()),
            JIM.USER_LOGIN: self.username,
        }

        CLIENT_LOG.debug(f'Formed users request for "{self.username}"')
        return req_obj
Пример #5
0
    def form_contacts_request(self):
        """Method for forming a client contacts request."""

        req_obj = {
            JIM.ACTION: JIM.Actions.GET_CONTACTS,
            JIM.TIME: int(datetime.now().timestamp()),
            JIM.USER_LOGIN: self.username,
        }

        CLIENT_LOG.debug(f'Formed contacts request for "{self.username}"')
        return req_obj
Пример #6
0
    def transport_shutdown(self):
        """Method for informing a server about a client shutdown."""

        self.running = False
        req = self.form_exit_message()

        with socket_lock:
            try:
                send_message(req, self._socket, CLIENT_LOG)
            except OSError:
                pass

        CLIENT_LOG.debug('Transport shutting down.')
        time.sleep(0.5)
Пример #7
0
    def form_auth_message(self, acc_password):
        """Method for forming an authentication request."""

        auth_obj = {
            JIM.ACTION: JIM.Actions.AUTH,
            JIM.TIME: int(datetime.now().timestamp()),
            JIM.USER: {
                JIM.UserData.ACCOUNT_NAME: self.username,
                JIM.UserData.PASSWORD: acc_password,
            }
        }

        CLIENT_LOG.debug(f'Formed AUTH for user "{self.username}"')
        return auth_obj
Пример #8
0
    def form_text_message(self, destination, content):
        """Method for forming a client-to-client text message."""

        message_obj = {
            JIM.ACTION: JIM.Actions.MESSAGE,
            JIM.TIME: int(datetime.now().timestamp()),
            JIM.TO: destination,
            JIM.FROM: self.username,
            JIM.MESSAGE: content,
        }

        CLIENT_LOG.debug(
            f'Formed MESSAGE from user "{self.username}" to "{destination}": "{content}"'
        )
        return message_obj
Пример #9
0
    def user_list_update(self):
        """Method for performing a user list update."""

        CLIENT_LOG.debug(
            f'Requesting a list of known users for {self.username}')
        req = self.form_users_request()

        with socket_lock:
            send_message(req, self._socket, CLIENT_LOG)
            ans = receive_message(self._socket, CLIENT_LOG)

        if JIM.RESPONSE in ans\
                and ans[JIM.RESPONSE] == ResCodes.ACCEPTED:
            self._db.add_known_users(ans[JIM.DATA_LIST])
        else:
            CLIENT_LOG.error('Failed to update a list of known users!')
Пример #10
0
    def form_join_message(self, chat):
        """Method for forming a chat join request.

        (unused for now)"""

        if chat[0] != '#':
            return None
        join_obj = {
            JIM.ACTION: JIM.Actions.JOIN,
            JIM.TIME: int(datetime.now().timestamp()),
            JIM.ROOM: chat,
        }

        CLIENT_LOG.debug(
            f'Formed JOIN for user "{self.username}" to chat "{chat}"')
        return join_obj
Пример #11
0
    def form_presence_message(self, status='Online'):
        """Method for forming a client presence message."""

        presence_obj = {
            JIM.ACTION: JIM.Actions.PRESENCE,
            JIM.TIME: int(datetime.timestamp(datetime.now())),
            JIM.TYPE: JIM.TypeData.STATUS,
            JIM.USER: {
                JIM.UserData.ACCOUNT_NAME: self.username,
                JIM.UserData.STATUS: status,
                JIM.UserData.PUBLIC_KEY: self.pubkey,
            },
        }

        CLIENT_LOG.debug(f'Formed PRESENCE for user "{self.username}": ""')
        return presence_obj
Пример #12
0
    def contact_list_update(self):
        """Method for performing a contact list update."""

        CLIENT_LOG.debug(
            f'Requesting contact list for the user {self.username}')
        req = self.form_contacts_request()

        with socket_lock:
            send_message(req, self._socket, CLIENT_LOG)
            ans = receive_message(self._socket, CLIENT_LOG)

        if JIM.RESPONSE in ans\
                and ans[JIM.RESPONSE] == ResCodes.ACCEPTED:
            for contact in ans[JIM.DATA_LIST]:
                self._db.add_contact(contact)
        else:
            CLIENT_LOG.error('Failed to update a contact list!')
Пример #13
0
    def form_edit_contact_message(self, contact_name, op='/add'):
        """Method for forming an add/remove contact request."""

        op_value = JIM.Actions.ADD_CONTACT\
            if op == '/add'\
            else JIM.Actions.DEL_CONTACT
        edit_obj = {
            JIM.ACTION: op_value,
            JIM.TIME: int(datetime.now().timestamp()),
            JIM.USER_LOGIN: self.username,
            JIM.USER_ID: contact_name,
        }

        CLIENT_LOG.debug(
            f'Formed "{op_value}" message for "{contact_name}" from "{self.username}"'
        )
        return edit_obj
Пример #14
0
    def make_key_request(self, user):
        """Method for performing a public key request"""

        CLIENT_LOG.debug(f'Requesting a public key for {user}')
        req = {
            JIM.ACTION: JIM.Actions.PUBLIC_KEY_REQUEST,
            JIM.TIME: int(datetime.now().timestamp()),
            JIM.USER: {
                JIM.UserData.ACCOUNT_NAME: user,
            },
        }
        with socket_lock:
            send_message(req, self._socket, CLIENT_LOG)
            ans = receive_message(self._socket, CLIENT_LOG)
        if JIM.RESPONSE in ans and ans[JIM.RESPONSE] == ResCodes.AUTH_PROCESS:
            return ans[JIM.DATA]
        else:
            CLIENT_LOG.error(f"Couldn't retrieve a public key for '{user}'")
Пример #15
0
    def process_server_answer(self, message):
        """Method for checking a server answer and performing corresponding actions."""

        CLIENT_LOG.debug(f'Parsing server message: {message}')

        if JIM.RESPONSE in message:
            if message[JIM.RESPONSE] == ResCodes.OK:
                return
            elif message[JIM.RESPONSE] == ResCodes.JSON_ERROR:
                raise ServerError(f'{message[JIM.ERROR]}')
            elif message[JIM.RESPONSE] == ResCodes.LIST_UPDATE:
                self.user_list_update()
                self.contact_list_update()
                self.message_205.emit()
            else:
                CLIENT_LOG.debug(
                    f'Unknown response code detected {message[JIM.RESPONSE]}')

        elif JIM.ACTION in message\
                and message[JIM.ACTION] == JIM.Actions.MESSAGE\
                and JIM.FROM in message\
                and JIM.TO in message \
                and JIM.MESSAGE in message\
                and message[JIM.TO] == self.username:
            CLIENT_LOG.debug(
                f'Got a message from user - {message[JIM.FROM]}:{message[JIM.MESSAGE]}'
            )
            self.new_message.emit(message)
Пример #16
0
    def connection_init(self, port, ip):
        """Method for starting a server connection.

        Opens socket, connects to the server over network, performs handshake and authorization."""

        self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        self._socket.settimeout(5)

        connected = False
        for i in range(5):
            CLIENT_LOG.info(f'Connecting - try #{i + 1}')
            try:
                self._socket.connect((ip, port))
            except (OSError, ConnectionRefusedError):
                pass
            else:
                connected = True
                break
            time.sleep(1)

        if not connected:
            CLIENT_LOG.critical(
                f"Couldn't connect to the server ({ip}:{port}).")
            raise ServerError("Couldn't connect to the server!")

        CLIENT_LOG.debug(
            f'Connection with server {ip}:{port} established, starting AUTH process.'
        )

        passwd_bytes = self.password.encode('utf-8')
        salt = self.username.lower().encode('utf-8')
        passwd_hash = hashlib.pbkdf2_hmac('sha512', passwd_bytes, salt, 10000)
        passwd_hash_string = binascii.hexlify(passwd_hash)
        CLIENT_LOG.debug(f'Passwd hash ready: {passwd_hash_string}')

        self.pubkey = self.keys.publickey().export_key().decode('ascii')

        try:
            with socket_lock:
                send_message(self.form_presence_message(), self._socket,
                             CLIENT_LOG)
                ans = receive_message(self._socket, CLIENT_LOG)
                if JIM.RESPONSE in ans:
                    if ans[JIM.RESPONSE] == 400:
                        raise ServerError(ans[JIM.ERROR])
                    elif ans[JIM.RESPONSE] == ResCodes.AUTH_PROCESS:
                        ans_data = ans[JIM.DATA]
                        hash = hmac.new(passwd_hash_string,
                                        ans_data.encode('utf-8'), 'MD5')
                        digest = hash.digest()
                        to_send = form_response(ResCodes.AUTH_PROCESS)
                        to_send[JIM.DATA] = binascii.b2a_base64(digest).decode(
                            'ascii')
                        send_message(to_send, self._socket, CLIENT_LOG)
                        self.process_server_answer(
                            receive_message(self._socket, CLIENT_LOG))
                # self.process_server_answer(
                #     receive_message(self._socket, CLIENT_LOG))
        except (OSError, json.JSONDecodeError) as err:
            CLIENT_LOG.critical('Connection lost.', exc_info=err)
            raise ServerError('Server connection lost!')

        CLIENT_LOG.info('Server received our presence message.')
Пример #17
0
    return server_address, server_port, client_name, client_passwd


if __name__ == '__main__':
    address, port, username, password = arg_parser()

    app = QApplication(sys.argv)

    init_dialog = UserNameDialog()
    if not username or not password:
        app.exec_()
        if init_dialog.ok_pressed:
            username = init_dialog.client_name.text()
            password = init_dialog.client_passwd.text()
            CLIENT_LOG.debug(f'Using name = {username}, pw = {password}')
        else:
            exit(0)

    CLIENT_LOG.info(
        f'Started a client, params: {address}:{port} (@{username})')

    dir_path = os.path.dirname(os.path.realpath(__file__))
    key_file = os.path.join(dir_path, f'{username}.key')
    if not os.path.exists(key_file):
        keys = RSA.generate(2048, os.urandom)
        with open(key_file, 'wb') as key_f:
            key_f.write(keys.export_key())
    else:
        with open(key_file, 'rb') as key_f:
            keys = RSA.import_key(key_f.read())