Esempio n. 1
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()
Esempio n. 2
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))
Esempio n. 3
0
    def send_message(self, to, content):
        """Method for forming a text message to be sent to a target user."""

        req = self.form_text_message(to, content)

        with socket_lock:
            send_message(req, self._socket, CLIENT_LOG)
            self.process_server_answer(
                receive_message(self._socket, CLIENT_LOG))
            CLIENT_LOG.info(f'Sent a message to the user {to}')
Esempio n. 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
Esempio n. 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
Esempio n. 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)
Esempio n. 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
Esempio n. 8
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)
Esempio n. 9
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
Esempio n. 10
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
Esempio n. 11
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!')
Esempio n. 12
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
Esempio n. 13
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!')
Esempio n. 14
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
Esempio n. 15
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}'")
Esempio n. 16
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)
Esempio n. 17
0
    def __init__(self, port, ip_address, db, username, passwd, keys):
        """Initializes client-server network interface, connects and authenticates with the server.

        :type db: :class:`messenger.client.client_database.ClientBase`
        """

        threading.Thread.__init__(self)
        QObject.__init__(self)

        self._db = db
        self.username = username
        self.password = passwd
        self._socket = None
        self.keys = keys
        self.pubkey = None
        self.connection_init(port, ip_address)

        try:
            self.user_list_update()
            self.contact_list_update()
        except OSError as err:
            if err.errno:
                CLIENT_LOG.critical('Connection lost.')
                raise ServerError('Server connection lost!')
            CLIENT_LOG.error('Timed out while updating user lists!')
        except json.JSONDecodeError:
            CLIENT_LOG.critical('Connection lost.')
            raise ServerError('Server connection lost!')

        self.running = True
Esempio n. 18
0
def arg_parser():
    """ CLI parameters parser.

    Returns address, port (validated), username, password for user
    """
    parser = argparse.ArgumentParser()
    parser.add_argument('addr', default=DEFAULT_ADDRESS, nargs='?')
    parser.add_argument('port', default=DEFAULT_PORT, type=int, nargs='?')
    parser.add_argument('-n', '--name', default=None, nargs='?')
    parser.add_argument('-p', '--password', default='', nargs='?')
    namespace = parser.parse_args(sys.argv[1:])
    server_address = namespace.addr
    server_port = namespace.port
    client_name = namespace.name
    client_passwd = namespace.password

    if not 1023 < server_port < 65536:
        CLIENT_LOG.critical(
            f'Unusable port number provided: {server_port}. Use ports between {MIN_PORT_NUMBER} and {MAX_PORT_NUMBER}.'
        )
        exit(1)

    return server_address, server_port, client_name, client_passwd
Esempio n. 19
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.')
Esempio n. 20
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())