Example #1
0
class Server(threading.Thread):
    """
    Основной класс сервера. Принимает содинения, словари - пакеты
    от клиентов, обрабатывает поступающие сообщения.
    Работает в качестве отдельного потока.
    """
    # Дескриптор порта и ip адреса
    port = Port()
    address = Address()

    def __init__(self, listen_address, listen_port, database):
        # Параментры подключения
        self.listen_address = listen_address
        self.listen_port = listen_port
        self.database = database
        # Сокет, через который будет осуществляться работа
        self.main_socket = None
        # Устанавливаем список клиентов и очередь сообщений
        self.clients = []
        # Сокеты
        self.listen_sockets = None
        self.error_sockets = None
        # Флаг продолжения работы
        self.running = True
        # Словарь имен пользователей и соответствующие им сокеты
        self.names = dict()
        # Конструктор предка
        super().__init__()

    def run(self):
        """Метод основной цикл потока"""

        self.start_socket()

        # Основной цикл программы сервера
        while self.running:
            try:
                client, client_address = self.main_socket.accept()
            except OSError:
                pass
            else:
                LOGGER.info(f'Клиент {client_address} подключился.')
                client.settimeout(5)
                self.clients.append(client)

            recv_lst = []
            send_lst = []
            err_lst = []

            # Проверка на наличие ждущих клиентов
            try:
                if self.clients:
                    recv_lst, self.listen_sockets, self.error_sockets = select.select(
                        self.clients, self.clients, [], 0)
            except OSError as e:
                LOGGER.error(f'Ошибка сокетов: {e}')

            # Принимаем сообщения и если они есть, то заполняем словарь, если
            # ошибка - отключаем клиента
            if recv_lst:
                for client_with_msg in recv_lst:
                    try:
                        self.process_client_message(get_message(client_with_msg), client_with_msg)
                    except (OSError, json.JSONDecodeError, TypeError) as e:
                        LOGGER.debug(f'Получение данных из исключения клиента.', exc_info=e)
                        self.clients.remove(client_with_msg)

    def start_socket(self):
        """Метод инициализатор сокета."""
        LOGGER.info(
            f'Сервер запущен. Порт для подключений: {self.listen_port}.'
            f'Адрес с котрого принимаются сообщения: {self.listen_address}. '
        )
        # Готовим сокет
        transport = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        transport.bind((self.listen_address, self.listen_port))
        transport.settimeout(0.5)
        # Слушаем порт
        self.main_socket = transport
        self.main_socket.listen(MAX_CONNECTIONS)

    def remove_client(self, client):
        """
        Функция обработчик клиента с которым прервана связь.
        Ищет клиента и удаляет его из списков и базы:
        :param client:
        :return:
        """
        LOGGER.info(f'Клиент {client.getpeername()} отключился от сервера.')
        for name in self.names:
            if self.names[name] == client:
                self.database.user_logout(name)
                del self.names[name]
                break
        self.clients.remove(client)
        client.close()

    def process_message(self, message):
        """
        Функция адресной отправки сообщения определённому клиенту. Принимает словарь сообщение,
        список зарегистрированых пользователей и слушающие сокеты. Ничего не возвращает.
        """
        if message[DESTINATION] in self.names and self.names[message[DESTINATION]] in self.listen_sockets:
            try:
                send_message(self.names[message[DESTINATION]], message)
                LOGGER.info(
                    f'Отправлено сообщение пользователю {message[DESTINATION]} '
                    f'от пользователя {message[SENDER]}.')
            except OSError:
                self.remove_client(self.names[message[DESTINATION]])

        elif message[DESTINATION] in self.names and self.names[message[DESTINATION]] not in self.listen_sockets:
            LOGGER.error(
                f'Связь с пользователем {message[DESTINATION]} потеряна. '
                f'Соединение закрыто, отправка сообщения невозможна.')
            self.remove_client(self.names[message[DESTINATION]])
        else:
            LOGGER.error(
                f'Пользователь {message[DESTINATION]} не зарегистрирован на сервере, '
                f'отправка сообщения невозможна.')

    @login_required
    def process_client_message(self, message, client):
        """ Обработчик сообщений от клиентов """
        LOGGER.debug(f'Разбор сообщения от клиента : {message}')
        # Если это сообщение о присутствии, принимаем и отвечаем
        if ACTION in message and message[ACTION] == PRESENCE and TIME in message and USER in message:
            # Если сообщение о присутствии то вызываем функцию авторизации.
            self.autorized_user(message, client)

        # Если это сообщение, то добавляем его в очередь
        elif ACTION in message and message[ACTION] == MESSAGE and TIME in message and DESTINATION in message \
                and SENDER in message and MESSAGE_TEXT in message and self.names[message[SENDER]] == client:
            if message[DESTINATION] in self.names:
                self.database.process_message(message[SENDER], message[DESTINATION])
                self.process_message(message)
                try:
                    send_message(client, RESPONSE_200)
                except OSError:
                    self.remove_client(client)
            else:
                response = RESPONSE_400
                response[ERROR] = 'Пользователь не зарегистрирован.'
                try:
                    send_message(client, response)
                except OSError:
                    pass
            return

        # Если клиент выходит
        elif ACTION in message and message[ACTION] == EXIT and ACCOUNT_NAME in message and \
                self.names[message[ACCOUNT_NAME]] == client:
            self.remove_client(client)
            LOGGER.info(f'Клиент {message[ACCOUNT_NAME]} коректно вышел')

        # Если запрос контактов
        elif ACTION in message and message[ACTION] == GET_CONTACTS and USER in message and \
                self.names[message[USER]] == client:
            response = RESPONSE_202
            response[LIST_INFO] = self.database.get_contacts(message[USER])
            try:
                send_message(client, response)
            except OSError:
                self.remove_client(client)

        # Если запрос на добавление контакта
        elif ACTION in message and message[ACTION] == ADD_CONTACT and ACCOUNT_NAME in message and USER in message \
                and self.names[message[USER]] == client:
            self.database.add_contact(message[USER], message[ACCOUNT_NAME])
            try:
                send_message(client, RESPONSE_200)
            except OSError:
                self.remove_client(client)

        # Если запрос на удаление контакта
        elif ACTION in message and message[ACTION] == REMOVE_CONTACT and ACCOUNT_NAME in message and USER in message \
                and self.names[message[USER]] == client:
            self.database.remove_contact(message[USER], message[ACCOUNT_NAME])
            try:
                send_message(client, RESPONSE_200)
            except OSError:
                self.remove_client(client)

        # Если запрос известных пользователей
        elif ACTION in message and message[ACTION] == USERS_REQUEST and ACCOUNT_NAME in message \
                and self.names[message[ACCOUNT_NAME]] == client:
            response = RESPONSE_202
            response[LIST_INFO] = [user[0] for user in self.database.users_list()]
            try:
                send_message(client, response)
            except OSError:
                self.remove_client(client)

        # Если запрос публичного ключа пользователя
        elif ACTION in message and message[ACTION] == PUBLIC_KEY_REQUEST and ACCOUNT_NAME in message:
            response = RESPONSE_511
            response[DATA] = self.database.get_public_key(message[ACCOUNT_NAME])
            if response[DATA]:
                try:
                    send_message(client, response)
                except OSError:
                    self.remove_client(client)
            else:
                response = RESPONSE_400
                response[ERROR] = 'Публичный ключ не найден для данного пользователя.'
                try:
                    send_message(client, response)
                except OSError:
                    self.remove_client(client)

        # Иначе отдаём Bad request
        else:
            response = RESPONSE_400
            response[ERROR] = 'Запрос некорректен.'
            try:
                send_message(client, response)
            except OSError:
                self.remove_client(client)

    def autorized_user(self, message, client):
        """
        Функция реализующая авторизацию пользователей.
        :param message:
        :param client:
        :return:
        """

        # Если имя пользователя уже занято то возвращаем 400
        LOGGER.debug(f'Авторизация для пользователя: {message[USER]}')

        if message[USER][ACCOUNT_NAME] not in self.names.keys():
            response = RESPONSE_400
            response[ERROR] = 'Имя пользователя уже занято.'
            try:
                LOGGER.debug(f'Имя занято {response}')
                send_message(client, response)
            except OSError:
                LOGGER.debug(f'Ошибка системы')
                pass
            self.clients.remove(client)
            client.close()

        # Проверяем что пользователь зарегистрирован на сервере.
        elif not self.database.check_user(message[USER][ACCOUNT_NAME]):
            response = RESPONSE_400
            response[ERROR] = 'Пользователь не зарегистрирован.'
            try:
                LOGGER.debug(f'Неизвестное имя пользователя {response}')
                send_message(client, response)
            except OSError:
                pass
            self.clients.remove(client)
            client.close()
        else:
            LOGGER.debug('Имя пользователя корректно. Проверяем пароль.')
            # Словарь - заготовка
            message_auth = RESPONSE_511
            # Набор байтов в hex представлении
            random_str = binascii.hexlify(os.urandom(64))
            # В словарь байты нельзя, декодируем (json.dumps -> TypeError)
            message_auth[DATA] = random_str.decode('ascii')
            # Создаём хэш пароля и связки с рандомной строкой, сохраняем
            # серверную версию ключа
            hash = hmac.new(self.database.get_hash(message[USER][ACCOUNT_NAME]), random_str)
            digest = hash.digest()
            LOGGER.debug(f'Auth message = {message_auth}')
            try:
                # Обмен с клиентом
                send_message(client, message_auth)
                ans = get_message(client)
            except OSError as err:
                LOGGER.debug('Ошибка авторизации:', exc_info=err)
                client.close()
                return
            client_digest = binascii.a2b_base64(ans[DATA])
            # Если ответ клиента корректный, то сохраняем его в список
            # пользователей.
            if RESPONSE in ans and ans[RESPONSE] == 511 and hmac.compare_digest(digest, client_digest):
                self.names[message[USER][ACCOUNT_NAME]] = client
                client_ip, client_port = client.getpeername()
                try:
                    send_message(client, RESPONSE_200)
                except OSError:
                    self.remove_client(message[USER][ACCOUNT_NAME])
                # добавляем пользователя в список активных и если у него изменился открытый ключ
                # сохраняем новый
                self.database.user_login(
                    message[USER][ACCOUNT_NAME],
                    client_ip,
                    client_port,
                    message[USER][PUBLIC_KEY])
            else:
                response = RESPONSE_400
                response[ERROR] = 'Неверный пароль.'
                try:
                    send_message(client, response)
                except OSError:
                    pass
                self.clients.remove(client)
                client.close()

    def service_update_lists(self):
        """
        Функция реализующая отправку сервисного сообщения "205" клиентам.
        :return:
        """
        for client in self.names:
            try:
                send_message(self.names[client], RESPONSE_205)
            except OSError:
                self.remove_client(self.names[client])
Example #2
0
class MessageProcessor(threading.Thread):
    '''
    Основной класс сервера. Принимает содинения, словари - пакеты
    от клиентов, обрабатывает поступающие сообщения.
    Работает в качестве отдельного потока.
    '''
    port = Port()

    def __init__(self, listen_address, listen_port, database):
        # Параментры подключения
        self.listen_address = listen_address
        self.listen_port = listen_port

        # БД сервера
        self.database = database

        self.sock = None

        # Список подключённых клиентов.
        self.clients = []

        # Сокеты
        self.listen_sockets = None
        self.error_sockets = None

        # Флаг продолжения работы
        self.running = True

        # Словарь содержащий сопоставленные имена и соответствующие им сокеты.
        self.names = dict()

        super().__init__()

    def run(self):
        '''основной цикл потока.'''
        self.init_socket()

        while self.running:
            try:
                client_sock, address = self.sock.accept()
            except OSError:
                pass
            else:
                server_logger.info(f'Установлено соедение  {address}')
                client_sock.settimeout(5)
                self.clients.append(client_sock)

            receive_lst = []
            send_lst = []
            err_lst = []

            try:
                if self.clients:
                    receive_lst, self.listen_sockets, self.error_sockets = \
                        select.select(self.clients, self.clients, [], 0)
            except OSError:
                server_logger.error("Ошибка работы с сокетами")

            # принимаем сообщения и если ошибка, исключаем клиента.
            if receive_lst:
                for client_with_message in receive_lst:
                    try:
                        self.client_requests_handler(
                            get_message(client_with_message),
                            client_with_message)
                    except (OSError, json.JSONDecodeError, TypeError) as err:
                        server_logger.debug(
                            f'Getting data from client exception.',
                            exc_info=err)
                        self.remove_client(client_with_message)

    def remove_client(self, client):
        '''
        обработчик клиента с которым прервана связь.
        Ищет клиента и удаляет его из списков и базы:
        '''
        server_logger.info(
            f'Клиент {client.getpeername()} отключился от сервера.')
        for name in self.names:
            if self.names[name] == client:
                self.database.logout(name)
                del self.names[name]
                break
        self.clients.remove(client)
        client.close()

    def init_socket(self):
        '''Инициализация сокета'''
        server_logger.info(
            f'Сервер запущен.'
            f'Адрес и порт :  {self.listen_port}, {self.listen_address} '
            f'Если адрес не указан, принимаются соединения с любых адресов.')

        # создаем сокет
        server_sock = socket(AF_INET, SOCK_STREAM)
        server_sock.bind((self.listen_address, self.listen_port))
        server_sock.settimeout(3)

        self.sock = server_sock
        self.sock.listen(5)

    def process_message(self, message):
        '''Отправка сообщения клиенту.'''
        if message['to'] in self.names and self.names[
                message['to']] in self.listen_sockets:
            try:
                send_message(self.names[message['to']], message)
                server_logger.info(
                    f'Отправлено сообщение пользователю {message["to"]} от пользователя {message["from"]}.'
                )
            except OSError:
                self.remove_client(message["to"])
        elif message["to"] in self.names and self.names[
                message["to"]] not in self.listen_sockets:
            server_logger.error(
                f'Связь с клиентом {message["to"]} была потеряна. Соединение закрыто, доставка невозможна.'
            )
            self.remove_client(self.names[message["to"]])
        else:
            server_logger.error(
                f'Пользователь {message["to"]} не зарегистрирован на сервере, отправка сообщения невозможна.'
            )

    # @login_required
    def client_requests_handler(self, message, client):
        """
        Валидация сообщения от клиента и
        отправка ответа сервера
        """

        server_logger.debug(f'Сообщение : {message} клиента {client}')
        # обрабатываем сообщение присутствие и регистрация пользователя
        if 'action' in message and message['action'] == 'presence' and 'time' in message \
                and 'user' in message:
            server_logger.debug(f"Принято и обработано presence сообщение")
            self.autorize_user(message, client)

        # если есть сообщение - то отправляем получателю
        elif 'action' in message and message['action'] == 'message' and 'time' in message \
                and 'message_text' in message and 'to' in message and 'from' in message \
                and self.names[message['from']] == client:
            server_logger.debug(
                f"Принято сообщение --- > проверка пользователя -- > отправка получателю"
            )
            if message['to'] in self.names:
                self.database.count_messages(message['from'], message['to'])
                self.process_message(message)
                try:
                    send_message(client, {'response': 200})
                except OSError:
                    self.remove_client(client)
            else:
                response = {'response': 400, 'error': None}
                response[
                    'error'] = 'Пользователь не зарегистрирован на сервере.'
                try:
                    send_message(client, response)
                except OSError:
                    pass
            return

        # если клиент выходит
        elif 'action' in message and message['action'] == 'exit' and 'account_name' in message and \
                self.names[message['account_name']] == client:
            self.remove_client(client)

        # запрос списка контактов
        elif 'action' in message and message['action'] == 'get_contacts' and 'user' in message and \
                self.names[message['user']] == client:
            response = {'response': 202, 'data_list': None}

            response['data_list'] = self.database.get_contacts(message['user'])
            try:
                send_message(client, response)
            except OSError:
                self.remove_client(client)

        # добавление контакта
        elif 'action' in message and message['action'] == 'add' and 'account_name' in message and 'user' in message \
                and self.names[message['user']] == client:
            self.database.add_contact(message['user'], message['account_name'])
            try:
                send_message(client, {"response": 200})
            except OSError:
                self.remove_client(client)

        # удаление контакта
        elif 'action' in message and message['action'] == 'remove' and 'account_name' in message and 'user' in message \
                and self.names[message['user']] == client:
            self.database.remove_contact(message['user'],
                                         message['account_name'])
            try:
                send_message(client, {"response": 200})
            except OSError:
                self.remove_client(client)

        # запрос списка всех пользователей
        elif 'action' in message and message['action'] == 'get_users' and 'account_name' in message \
                and self.names[message['account_name']] == client:
            response = {'response': 202, 'data_list': None}
            response['data_list'] = [
                user[0] for user in self.database.users_list()
            ]
            try:
                send_message(client, response)
            except OSError:
                self.remove_client(client)

        # запрос публичного ключа пользователя
        elif 'action' in message and message[
                'action'] == 'pubkey_need' and 'account_name' in message:
            response = {'response': 511, 'bin': None}
            response['bin'] = self.database.get_pubkey(message['account_name'])
            # может быть, что ключа ещё нет (пользователь никогда не логинился,
            # тогда шлём 400)
            if response['bin']:
                try:
                    send_message(client, response)
                except OSError:
                    self.remove_client(client)
            else:
                response = {"response": 400, "error": None}
                response[
                    "error"] = 'Нет публичного ключа для данного пользователя'
                try:
                    send_message(client, response)
                except OSError:
                    self.remove_client(client)

        else:
            response = {"response": 400, "error": None}
            response["error"] = 'Bad Request'
            try:
                send_message(client, response)
            except OSError:
                self.remove_client(client)

    def autorize_user(self, message, sock):
        '''Авторизация пользователя'''

        server_logger.debug(f'Start auth process for {message["user"]}')
        if message["user"]['account_name'] in self.names.keys():
            response = {"response": 400, "error": None}
            response["error"] = 'Имя пользователя уже занято.'
            server_logger.debug(f"Имя пользователя уже занято.")
            try:
                server_logger.debug(f'Username busy, sending {response}')
                send_message(sock, response)
            except OSError:
                server_logger.debug('OS Error')
                pass
            self.clients.remove(sock)
            sock.close()
        # Если пользователь не зарегистрирован - отправляем 400 ошибку
        elif not self.database.check_user(message["user"]['account_name']):
            server_logger.debug(
                f"Пользователь : {message['user']['account_name']}не зарегистрирован."
            )
            response = {"response": 400, "error": None}
            response["error"] = 'Пользователь не зарегистрирован.'
            try:
                send_message(sock, response)
                server_logger.debug(f'Unknown username, sending {response}')
            except OSError:
                pass
            self.clients.remove(sock)
            server_logger.debug(
                f"Пользователь удален из списка потенциальных клиентов")
            sock.close()
        else:
            server_logger.debug('Correct username, starting password check.')
            message_auth = {'response': 511, 'bin': None}

            random_str = binascii.hexlify(os.urandom(64))
            message_auth['bin'] = random_str.decode('ascii')
            hash = hmac.new(
                self.database.get_hash(message["user"]['account_name']),
                random_str, 'MD5')
            digest = hash.digest()
            server_logger.debug(f'Auth message = {message_auth}')
            try:
                send_message(sock, message_auth)
                ans = get_message(sock)
            except OSError as err:
                server_logger.debug('Error in auth, data:', exc_info=err)
                sock.close()
                return
            client_digest = binascii.a2b_base64(ans['bin'])
            # Если ответ клиента корректный, то сохраняем его в список
            # пользователей.
            if 'response' in ans and ans[
                    'response'] == 511 and hmac.compare_digest(
                        digest, client_digest):
                self.names[message["user"]['account_name']] = sock
                client_ip, client_port = sock.getpeername()
                server_logger.debug(
                    f"Ответ клиента корректный - клиент сохранен в список пользователей"
                )
                try:
                    send_message(sock, {"response": 200})
                except OSError:
                    self.remove_client(message["user"]['account_name'])
                # добавляем пользователя в список активных и если у него изменился открытый ключ
                # сохраняем новый
                self.database.login(message["user"]['account_name'], client_ip,
                                    client_port, message['user']['pubkey'])
            else:
                response = {"response": 400, "error": None}
                response["error"] = 'Неверный пароль.'
                try:
                    send_message(sock, response)
                except OSError:
                    pass
                self.clients.remove(sock)
                sock.close()

    def service_update_lists(self):
        '''Отправка 205 сообщения'''
        for client in self.names:
            try:
                send_message(self.names[client], {"response": 205})
            except OSError:
                self.remove_client(self.names[client])
Example #3
0
class Server(threading.Thread, metaclass=ServerVerifier):
    port = Port()
    address = Host()

    def __init__(self, server_address, server_port, connections, database):
        self.address = server_address
        self.port = server_port
        self.connections = connections
        self.database = database
        super().__init__()
        self.clients = []
        self.messages = []
        self.names = dict()

    def autorize_user(self, message, sock):
        if message[USER][ACCOUNT_NAME] in self.names.keys():
            response = {RESPONSE: 400, ERROR: 'Имя пользователя уже занято'}
            try:
                send_message(sock, response)
            except OSError:
                pass
            self.clients.remove(sock)
            sock.close()
        # Проверяем что пользователь зарегистрирован на сервере.
        elif not self.database.check_user(message[USER][ACCOUNT_NAME]):
            response = {
                RESPONSE: 400,
                ERROR: 'Пользователь не зарегистрирован.'
            }
            try:
                send_message(sock, response)
            except OSError:
                pass
            self.clients.remove(sock)
            sock.close()
        else:
            # Иначе отвечаем 501 и проводим процедуру авторизации
            # Словарь - заготовка
            message_auth = {RESPONSE: 511, DATA: None}
            # Набор байтов в hex представлении
            random_str = binascii.hexlify(os.urandom(64))
            # В словарь байты нельзя, декодируем (json.dumps -> TypeError)
            message_auth[DATA] = random_str.decode('ascii')
            # Создаём хэш пароля и связки с рандомной строкой, сохраняем серверную версию ключа
            hash = hmac.new(
                self.database.get_hash(message[USER][ACCOUNT_NAME]),
                random_str)
            digest = hash.digest()
            try:
                # Обмен с клиентом
                send_message(sock, message_auth)
                ans = get_message(sock)
            except OSError:
                sock.close()
                return
            client_digest = binascii.a2b_base64(ans[DATA])
            # Если ответ клиента корректный, то сохраняем его в список пользователей.
            if RESPONSE in ans and ans[RESPONSE] == 511 and hmac.compare_digest(
                    digest, client_digest):
                self.names[message[USER][ACCOUNT_NAME]] = sock
                client_ip, client_port = sock.getpeername()
                try:
                    send_message(sock, {RESPONSE: 200})
                except OSError:
                    self.remove_client(message[USER][ACCOUNT_NAME])
                # добавляем пользователя в список активных и если у него изменился открытый ключ
                # сохраняем новый
                self.database.user_login(message[USER][ACCOUNT_NAME],
                                         client_ip, client_port,
                                         message[USER][PUBLIC_KEY])
            else:
                response = {RESPONSE: 400, ERROR: 'Неверный пароль.'}
                try:
                    send_message(sock, response)
                except OSError:
                    pass
                self.clients.remove(sock)
                sock.close()

    def init_socket(self):
        SERV = socket(AF_INET, SOCK_STREAM)
        try:
            SERV.bind((self.address, self.port))
        except OSError as err:
            print(err)
        SERV_LOG.debug('Запуск сервера')
        SERV.settimeout(0.5)
        self.sock = SERV
        self.sock.listen(self.connections)
        print(f'Сервер запущен {self.address}:{self.port}')

    def run(self):
        self.init_socket()
        while True:
            try:
                client, client_addr = self.sock.accept()
            except OSError:
                pass
            else:
                SERV_LOG.info(f'Подключился клиент: {client_addr}')
                self.clients.append(client)

            read_clients_lst = []
            send_clients_lst = []
            err_lst = []
            try:
                if self.clients:
                    read_clients_lst, send_clients_lst, err_lst = select(
                        self.clients, self.clients, [], 0)
            except OSError:
                pass
            if read_clients_lst:
                for client_with_message in read_clients_lst:
                    try:
                        self.do_answer(get_message(client_with_message),
                                       self.messages, client_with_message,
                                       self.clients, self.names)
                    except Exception:
                        SERV_LOG.info(
                            f'Клиент {client_with_message.getpeername()}'
                            f' отключился от сервера')
                        for name in self.names:
                            if self.names[name] == client_with_message:
                                self.database.user_logout(name)
                                del self.names[name]
                                break
                        self.clients.remove(client_with_message)
                        with conflag_lock:
                            new_connection = True
            for mess in self.messages:
                try:
                    self.do_message(mess, self.names, send_clients_lst)
                except Exception:
                    SERV_LOG.info(f'Связь с клиентом {mess[TO]} потеряна')
                    self.clients.remove(self.names[mess[TO]])
                    self.database.user_logout(mess[TO])
                    del self.names[mess[TO]]
            self.messages.clear()

    # @log
    def do_answer(self, message, message_list, client, clients, names):
        """Обрабатывает сообщение от клиента и готовит ответ"""
        global new_connection
        SERV_LOG.debug(f'Принято сообщение от клиента: {message}')
        if ACTION in message and message[ACTION] == PRESENCE \
                and TIME in message and USER in message:
            if message[USER][ACCOUNT_NAME] not in names.keys():
                names[message[USER][ACCOUNT_NAME]] = client
                client_ip, client_port = client.getpeername()
                self.database.user_login(message[USER][ACCOUNT_NAME],
                                         client_ip, client_port)
                send_message(client, {RESPONSE: 200})
                with conflag_lock:
                    new_connection = True
                SERV_LOG.debug('Ответ подготовлен: {RESPONSE: 200}')
            else:
                SERV_LOG.debug("Ответ подготовлен: {RESPONSE: 400\nERROR: \
'Имя пользователя уже занято'}")
                send_message(client, {
                    RESPONSE: 400,
                    ERROR: 'Имя пользователя уже занято'
                })
                clients.remove(client)
                client.close()
            return

        elif ACTION in message and message[ACTION] == MSG and TIME in message \
                and MESSAGE in message and TO in message:
            message_list.append(message)
            self.database.process_user_message(message[SENDER], message[TO])
            return

        elif ACTION in message and message[ACTION] == QUIT and \
                ACCOUNT_NAME in message:
            self.database.user_logout(message[ACCOUNT_NAME])
            SERV_LOG.debug('Удален клиент из таблицы активных клиентов')
            clients.remove(names[message[ACCOUNT_NAME]])
            names[message[ACCOUNT_NAME]].close()
            del names[message[ACCOUNT_NAME]]
            with conflag_lock:
                new_connection = True
            return

        elif ACTION in message and message[ACTION] == GET_CONTACTS and \
                USER in message and self.names[message[USER]] == client:
            response = {RESPONSE: 202}
            response[LIST_INFO] = self.database.get_contacts(message[USER])
            send_message(client, response)
            SERV_LOG.info(f'Отправлен ответ на запрос GET_CONTACTS {response}')

        elif ACTION in message and message[ACTION] == ADD_CONTACT and \
                ACCOUNT_NAME in message and USER in message \
                and self.names[message[USER]] == client:
            self.database.add_contact(message[USER], message[ACCOUNT_NAME])
            send_message(client, {RESPONSE: 200})

        elif ACTION in message and message[ACTION] == REMOVE_CONTACT and \
            ACCOUNT_NAME in message and USER in message and \
                self.names[message[USER]] == client:
            self.database.remove_contact(message[USER], message[ACCOUNT_NAME])
            send_message(client, {RESPONSE: 200})

        elif ACTION in message and message[ACTION] == USERS_REQUEST and \
            ACCOUNT_NAME in message and self.names[message[ACCOUNT_NAME]] ==\
                client:
            response[LIST_INFO] = [
                user[0] for user in self.database.users_list()
            ]
            send_message(client, response)
            SERV_LOG.info(
                f'Отправлен ответ на запрос USERS_REQUEST {response}')
        else:
            SERV_LOG.debug("Ответ подготовлен: {RESPONSE: 400\nERROR: \
'Bad request'}")
            send_message(client, {RESPONSE: 400, ERROR: 'Bad request'})
            return

    @log
    def do_message(self, message, names, socks):
        """Отправляет сообщение определённому клиенту"""
        if message[TO] in names and names[message[TO]] in socks:
            send_message(names[message[TO]], message)
            SERV_LOG.info(f'Отправлено сообщение пользователю {message[TO]} '
                          f' от пользователя {message[SENDER]}')
        elif message[TO] in names and names[message[TO]] not in socks:
            raise ConnectionError
        else:
            SERV_LOG.error(f'Пользователь {message[TO]} не зарегистрирован на \
на сервере. Отправка невозможна')
class Client:
    __slots__ = ('_addr', '_port', 'user',
                 'logger', 'socket', 'connected',
                 'listener', 'sender', 'encryptors', 'priv_key',
                 'storage', 'subs', 'answers')

    TCP = (AF_INET, SOCK_STREAM)
    addr = Addr('_addr')
    port = Port('_port')

    def __init__(self, addr, port):
        self.logger = logging.getLogger(log_config.LOGGER_NAME)
        self.addr = addr
        self.port = port
        self.connected = False
        self.subs = {201: [], 202: [], 203: [], 204: [], 205: []}
        self.answers = queue.Queue()
        self.encryptors = {}

    @property
    def username(self):
        return self.user.username

    def get_encryptor(self, contact):
        return self.encryptors[contact] if contact in self.encryptors else None

    def set_encryptor(self, contact, value):
        self.encryptors[contact] = value

    def set_user(self, username, password):
        self.user = User(username, password)
        self.storage = ClientStorage(username)

    def start(self):
        self.socket = socket(*self.TCP)
        start_txt = f'Connect to {self.addr}:{self.port} as {self.user.username}...'
        self.logger.debug(start_txt)
        print(start_txt)
        self.__connect()

    @try_except_wrapper
    def __connect(self):
        self.socket.connect((self.addr, self.port))
        self.connected = True
        print('Done')
        response = self.authorization()
        if response.code != OK:
            show_error(str(response.message))
            self.logger.warning(response)
            return

        self.listener = ClientThread(self.__listen_server, self.logger)
        self.listener.start()

    @try_except_wrapper
    def __send_request(self, request):
        if not self.connected:
            return
        self.logger.debug(request)
        send_data(self.socket, request)

    @try_except_wrapper
    def __get_response(self):
        if not self.connected:
            return
        response = get_data(self.socket)
        self.logger.debug(response)
        return response

    @try_except_wrapper
    def authorization(self):
        pr_req = Request(RequestAction.PRESENCE, self.user.username)
        self.__send_request(pr_req)
        resp = self.__get_response()
        if resp is None:
            return Response(SERVER_ERROR)
        if resp.code != AUTH:
            return resp
        enc_pass = encrypt_rsa(import_pub_key(resp.message.encode()), self.user.password)
        auth_req = Request(RequestAction.AUTH, enc_pass.decode())
        self.__send_request(auth_req)
        return self.__get_response()

    def get_chat_req(self, contact):
        req = Request(RequestAction.COMMAND, f'get_chat {self.user.username} {contact}')
        self.__send_request(req)

    def get_users_req(self):
        self.__send_request(Request(RequestAction.COMMAND, 'get_users'))

    def get_contacts_req(self):
        self.__send_request(Request(RequestAction.COMMAND, 'get_contacts'))

    @try_except_wrapper
    def start_chat(self, contact):
        key = self.storage.get_key(contact)
        if key is not None:
            self.set_encryptor(contact, ClientCrypt(key))
            # self.encryptor = ClientCrypt(key)

        prv, pub = gen_keys()
        self.priv_key = prv
        msg = Msg(pub.export_key().decode(), self.username, contact)
        start_req = Request(RequestAction.START_CHAT, msg)
        self.__send_request(start_req)

    @try_except_wrapper
    def accepting_chat(self, resp_mes):
        r_msg = Msg.from_formated(resp_mes)
        pub = import_pub_key(r_msg.text.encode())

        key = self.storage.get_key(r_msg.sender)
        if key is not None:
            encryptor = ClientCrypt(key)
            # self.encryptor = ClientCrypt(key)
        else:
            # self.encryptor = ClientCrypt.gen_secret(self.username, r_msg.sender)
            encryptor = ClientCrypt.gen_secret(self.username, r_msg.sender)
            self.storage.add_chat_key(r_msg.sender, encryptor.secret)
        self.set_encryptor(r_msg.sender, encryptor)
        enc_key = encrypt_rsa(pub, encryptor.secret)
        msg = Msg(enc_key.decode(), self.username, r_msg.sender)
        accept_req = Request(RequestAction.ACCEPT_CHAT, msg)
        self.__send_request(accept_req)

    @try_except_wrapper
    def accepted_chat(self, resp_mes):
        msg = Msg.from_formated(resp_mes)
        encryptor = self.get_encryptor(msg.sender)
        if encryptor is not None:
            return

        secret = decrypt_rsa(self.priv_key, msg.text.encode())
        self.set_encryptor(msg.sender, ClientCrypt(secret))
        self.storage.add_chat_key(msg.sender, secret)

    def add_contact(self, contact):
        if self.storage.get_contact(contact):
            return False
        self.storage.add_contact(contact)
        req = Request(RequestAction.COMMAND, f'add_contact {contact}')
        self.__send_request(req)

    def rem_contact(self, contact):
        self.storage.remove_contact(contact)
        req = Request(RequestAction.COMMAND, f'rem_contact {contact}')
        self.__send_request(req)

    def sync_contacts(self, contacts):
        for c in contacts:
            self.storage.append_contact(c)

    @try_except_wrapper
    def send_msg(self, text, to):
        encryptor = self.get_encryptor(to)
        text = encryptor.encript_msg(text.encode()).decode()
        msg = Msg(text, self.user, to)
        self.storage.add_message(msg.to, msg.text)
        request = Request(RequestAction.MESSAGE, msg)
        self.__send_request(request)

    def __listen_server(self):
        while self.connected:
            resp = get_data(self.socket)
            self.logger.debug(resp)
            if resp.type != RESPONSE:
                self.logger.warning(f'Received not RESPONSE:\n {resp}')
                continue
            if resp.code == ANSWER:
                self.answers.put(resp.message)
                print(f'server: {resp.message}')
            elif resp.code in self.subs.keys():
                for s in self.subs[resp.code]:
                    s(resp.message)
            else:
                print(resp.message)

    def subscribe(self, code, func):
        if code in self.subs.keys():
            self.subs[code].append(func)
        else:
            self[code] = [func]

    def parse_recv_message(self, msg):
        msg = Msg.from_formated(msg)
        encryptor = self.get_encryptor(msg.sender)
        msg.text = encryptor.decrypt_msg(msg.text.encode()).decode()
        return msg.sender, msg.text
Example #5
0
class Server(threading.Thread, metaclass=ServerVerifier):
    server_port = Port()

    def __init__(self, SETTINGS, address, port):
        self.address = address
        self.server_port = port
        self.socket = socket(AF_INET, SOCK_STREAM)
        self.socket.bind((self.address, self.server_port))
        self.socket.listen(SETTINGS['MAX_CONNECTION'])
        self.socket.settimeout(0.1)
        self.db = ServerDB(SETTINGS['DATABASE'])
        self.new_connection = False
        self.clients_info = {}
        self.clients_hash = {}
        super().__init__()

    def presence(self, message, sock):
        """Метод реализующий идентификацию пользователей.
        """
        # Если имя пользователя уже занято то возвращаем 400
        if message["user"]["account_name"] in self.db.get_active_username():
            print('Имя пользователя уже занято.')
            response = RESPONSE_400
            response['error'] = 'Имя пользователя уже занято.'
            try:
                logger.debug(f'Username busy, sending {response}')
                send_message(sock, response)
            except OSError:
                logger.debug('OS Error')
                pass
            if self.clients_info.get(sock):
                self.clients_info.pop(sock)
        # Проверяем что пользователь зарегистрирован на сервере.
        elif not self.db.check_user(message["user"]["account_name"]):
            print('Пользователь не зарегистрирован.')
            response = RESPONSE_400
            response['error'] = 'Пользователь не зарегистрирован.'
            try:
                logger.debug(f'Unknown username, sending {response}')
                send_message(sock, response)
            except OSError:
                pass
            if self.clients_info.get(sock):
                self.clients_info.pop(sock)
        else:
            logger.debug('Correct username, starting passwd check.')
            print('Correct username, starting passwd check.')
            # Иначе отвечаем 511 и высылаем запрос на авторизацию
            # Набор байтов в hex представлении
            random_str = binascii.hexlify(os.urandom(64))
            message_auth = {'response': 511, 'data': random_str.decode('ascii')}
            # Создаём хэш пароля и связки с рандомной строкой
            hash = hmac.new(self.db.get_hash(message["user"]["account_name"]), random_str, 'MD5')
            digest = hash.digest()
            logger.debug(f'Auth message = {message_auth}')
            print(f'Auth message = {message_auth}')
            try:
                # Обмен с клиентом
                send_message(sock, message_auth)
                self.clients_hash[message["user"]["account_name"]] = digest
            except OSError as err:
                logger.debug('Error in auth, data:', exc_info=err)
                sock.close()
                return

    def autorize_user(self, message, sock):
        """Метод реализующий аутентификацию и авторизацию пользователей."""
        client_digest = binascii.a2b_base64(message['data'])
        # Если ответ клиента корректный, то сохраняем его в список
        # пользователей.
        digest = self.clients_hash[message["username"]]
        if 'response' in message and message['response'] == 511 and hmac.compare_digest(digest, client_digest):
            self.clients_info[sock] = message["username"]
            client_ip, client_port = sock.getpeername()
            try:
                send_success_code(sock)
            except OSError:
                self.db.user_logout(message["username"])
            # добавляем пользователя в список активных и если у него изменился открытый ключ
            # сохраняем новый
            self.db.user_login(message["username"], client_ip, client_port, message["keys"])
            print('Корректный пароль')
        else:
            response = RESPONSE_400
            response['error'] = 'Неверный пароль.'
            try:
                send_message(sock, response)
            except OSError:
                pass
            print('Неверный пароль')
            self.db.user_logout(message["username"])
            if self.clients_info.get(sock):
                self.clients_info.pop(sock)

    def read_requests(self, r_clients, w_clients, all_clients_dict):
        """Читаем накопишившиеся запросы на чтение"""
        responses = {}  # Словарь ответов сервера вида {сокет: запрос}

        for sock in r_clients:
            try:
                data = sock.recv(1024).decode('utf-8')
                responses[sock] = json.loads(data)
                if responses[sock]['action'] == 'presence':
                    print(f"Клиент {sock.getpeername()[0]} {responses[sock]['user']['account_name']} вошел")
                    all_clients_dict[sock] = responses[sock]['user']['account_name']
                if responses[sock]['action'] == 'add_contact':
                    self.db.add_contact(responses[sock]['from'], responses[sock]['to'])
                if responses[sock]['action'] == 'del_contact':
                    self.db.remove_contact(responses[sock]['from'], responses[sock]['to'])

            except:
                print(f' r Клиент {all_clients_dict[sock]} {sock.getpeername()} отключился')
                self.db.user_logout(all_clients_dict[sock])
                client_quit_req = {
                    sock: {'action': 'quit', 'username': all_clients_dict[sock], "time": int(time.time())}}
                # оправляем всем сообщение что пользователь отключился
                self.write_responses(client_quit_req, w_clients, all_clients_dict)
                if all_clients_dict.get(sock):
                    all_clients_dict.pop(sock)

        return responses

    def write_responses(self, requests, w_clients, all_clients_dict):
        """Отправляем накопишившиеся запросы на отправку"""
        for sock in w_clients:
            for _, request in requests.items():
                try:
                    print(request, sock)
                    # Подготовить и отправить всем слушающм клиентам ответ в чат или личным сообщением
                    if request['action'] == 'get_contacts':
                        if request['user_login'] == all_clients_dict[sock]:
                            # print(self.db.contact_list(request['user_login']))
                            send_contact_list(sock, self.db.contact_list(request['user_login']))
                        else:
                            continue

                    elif request['action'] == 'public_key_request':
                        key = self.db.get_pubkey(request['username'])
                        print(key)
                        send_user_keys(sock, key)

                    elif request['action'] == 'get_users':
                        if request['user_login'] == all_clients_dict[sock]:
                            send_users_list(sock, [x[0] for x in self.db.users_list()])
                        else:
                            continue
                    elif request['action'] == 'quit':
                        send_message(sock, request)
                        continue

                    elif request['action'] == 'msg' and request['to'] == all_clients_dict[sock]:
                        print(f"Сообщение от пользователя {request['from']} пользователю {request['to']}")
                        self.db.user_send_message(request['from'], request['to'])
                        send_message(sock, request)

                    elif request['action'] == 'presence' and request['user']['account_name'] == all_clients_dict[sock]:
                        self.new_connection = True
                        self.presence(request, sock)

                    elif request['action'] == 'join' and request['username'] == all_clients_dict[sock]:
                        self.new_connection = True
                        self.autorize_user(request, sock)
                    else:
                        continue
                except:  # Сокет недоступен, клиент отключился
                    print(f' w Клиент {sock.fileno()} {sock.getpeername()} отключился')
                    self.db.user_logout(sock.fileno())
                    sock.close()
                    if all_clients_dict.get(sock):
                        all_clients_dict.pop(sock)

    def run(self):
        """
        Основной цикл потока. Оюработка пришедших запросов.
        """
        print('Сервер запущен')
        while True:

            try:
                conn, addr = self.socket.accept()  # Проверка подключений
            except OSError as e:
                pass  # timeout вышел
            else:
                print("Получен запрос на соединение от %s" % str(addr))
                self.clients_info[conn] = ''
            finally:
                # Проверить наличие событий ввода-вывода
                wait = 10
                r = []
                w = []
                try:
                    r, w, e = select.select(list(self.clients_info.keys()), list(self.clients_info.keys()), [], wait)
                except:
                    pass  # Ничего не делать, если какой-то клиент отключился

                requests = self.read_requests(r, w, self.clients_info)  # Сохраним запросы клиентов
                if requests:
                    self.write_responses(requests, w, self.clients_info)
Example #6
0
class Server:
    __slots__ = ('bind_addr', '_port', 'logger', 'socket', 'clients', 'users',
                 'storage', 'commands', 'listener')

    TCP = (AF_INET, SOCK_STREAM)
    TIMEOUT = 5

    port = Port('_port')

    def __init__(self, bind_addr, port):
        self.logger = logging.getLogger(log_config.LOGGER_NAME)
        self.bind_addr = bind_addr
        self.port = port
        self.clients = []
        self.users = {}
        self.storage = ServerStorage()
        self.__init_commands()

    def __init_commands(self):
        self.commands = {
            'get_users': self.storage.get_users_online,
            'add_contact': self.storage.add_contact,
            'rem_contact': self.storage.remove_contact,
            'get_contacts': self.storage.get_contacts,
            'get_chat': self.storage.get_chat_str
        }

    def start(self, request_count=5):
        self.socket = socket(*self.TCP)
        self.socket.settimeout(0.5)
        self.socket.bind((self.bind_addr, self.port))
        self.logger.info(
            f'Config server port - {self.port}| Bind address - {self.bind_addr}'
        )

        self.socket.listen(request_count)
        # self.__listen()

        self.listener = ServerThread(self.__listen, self.logger)
        self.listener.start()
        # self.__console()

    def __console(self):
        while True:
            msg = input('Enter command:\n')
            if msg.upper() == 'Q':
                break
            if msg[0] == '#':
                msg = msg[1:]

            command, *args = msg.split(' ')
            if command in self.commands:
                res = self.commands[command](*args)
                print(res)

    def __listen(self):
        self.logger.info('Start listen')
        while True:
            try:
                client, addr = self.socket.accept()
            except OSError:
                pass
            except Exception as ex:
                self.logger.error(ex)
            else:
                self.logger.info(f'Connection from {addr}')
                self.clients.append(client)

            i_clients, o_clients = [], []
            try:
                i_clients, o_clients, ex = select(self.clients, self.clients,
                                                  [], self.TIMEOUT)
            except OSError:
                pass
            except Exception as ex:
                self.logger.error(ex)

            requests = self.__get_requests(i_clients)
            if requests:
                self.__send_responses(requests, o_clients)

    @try_except_wrapper
    def __get_requests(self, i_clients):
        requests = {}
        for client in i_clients:
            try:
                request = get_data(client)
                requests[client] = request

                if request.action == RequestAction.PRESENCE:
                    if request.body in self.users:
                        requests.pop(client)
                        send_data(client, Response(CONFLICT))
                        self.clients.remove(client)
                    else:
                        self.users[request.body] = client
                        self.storage.login_user(request.body,
                                                client.getpeername()[0])
                elif request.action == RequestAction.QUIT:
                    self.__client_disconnect(client)
            except (ConnectionError, ValueError):
                self.__client_disconnect(client)
            except Exception as e:
                raise e
        return requests

    @try_except_wrapper
    def __send_responses(self, requests, o_clients):

        for client, i_req in requests.items():
            other_clients = [c for c in o_clients if c != client]
            self.logger.info(client)
            self.logger.info(i_req)

            if i_req.action == RequestAction.PRESENCE:
                self.__send_to_client(client, Response(OK))
                self.__send_to_all(other_clients,
                                   Response(CONNECTED, i_req.body))
            elif i_req.action == RequestAction.QUIT:
                self.__client_disconnect(client)
            elif i_req.action == RequestAction.MESSAGE:
                msg = Msg.from_dict(i_req.body)
                self.storage.user_stat_update(msg.sender, ch_sent=1)
                if msg.to.upper() != 'ALL' and msg.to in self.users:
                    self.storage.user_stat_update(msg.to, ch_recv=1)
                    self.storage.add_message(msg.sender, msg.to, msg.text)
                    self.__send_to_client(self.users[msg.to],
                                          Response(LETTER, str(msg)))
                else:
                    self.__send_to_all(other_clients,
                                       Response(LETTER, str(msg)))
                    for u in self.storage.get_users_online():
                        if str(u) == msg.sender:
                            continue
                        self.storage.user_stat_update(str(u), ch_recv=1)
                        self.storage.add_message(msg.sender, str(u), msg.text)

            elif i_req.action == RequestAction.COMMAND:
                command, *args = i_req.body.split()
                user = [u for u, c in self.users.items() if c == client].pop()
                if len(args) < 1 or args[0] != user:
                    args.insert(0, user)
                o_resp = self.__execute_command(command, *args)
                self.__send_to_client(client, o_resp)
            else:
                self.__send_to_client(client, Response(INCORRECT_REQUEST))
                self.logger.error(f'Incorrect request:\n {i_req}')

    @try_except_wrapper
    def __send_to_client(self, client, resp):
        try:
            self.logger.debug(resp)
            send_data(client, resp)
        except ConnectionError:
            self.__client_disconnect(client)
        except Exception as e:
            raise e

    def __send_to_all(self, clients, resp):
        for cl in clients:
            self.__send_to_client(cl, resp)

    @try_except_wrapper
    def __client_disconnect(self, client):
        self.clients.remove(client)
        disconnected_user = [u for u, c in self.users.items()
                             if c == client].pop()
        self.users.pop(disconnected_user)
        self.storage.logout_user(disconnected_user)
        disconnection_response = Response(DISCONNECTED, disconnected_user)
        self.logger.debug(disconnection_response)
        for cl in self.clients:
            send_data(cl, disconnection_response)

    def __execute_command(self, command, *args):
        if command in self.commands:
            answer = self.commands[command](*args)
            if answer is False:
                return Response(SERVER_ERROR, 'Command error')
            elif isinstance(answer, list):
                answer = [str(a) for a in answer]
                return Response(ANSWER, answer)
            elif answer is None:
                return Response(ANSWER, 'Done')
            return Response(ANSWER, answer)
        else:
            return Response(INCORRECT_REQUEST, 'Command not found')
class Client:
    __slots__ = ('addr', '_port', 'logger', 'socket', 'connected', 'listener',
                 'sender', 'storage', 'subs', 'answers')

    TCP = (AF_INET, SOCK_STREAM)
    USER = User(f'Test{random.randint(0, 1000)}')

    port = Port('_port')

    def __init__(self, addr, port):
        self.logger = logging.getLogger(log_config.LOGGER_NAME)
        self.addr = addr
        self.port = port
        self.connected = False
        self.subs = {201: [], 202: [], 203: []}
        self.answers = queue.Queue()
        self.__reg_resp_console()
        # name = input('Set name (enter generate name):\n')
        # if len(name) > 0:
        #     self.USER.username = name
        # self.storage = ClientStorage(self.USER.username)

    @property
    def username(self):
        return self.USER.username

    @username.setter
    def username(self, value):
        self.USER.username = value
        self.storage = ClientStorage(self.USER.username)

    def start(self):
        self.socket = socket(*self.TCP)
        start_txt = f'Connect to {self.addr}:{self.port} as {self.USER}...'
        self.logger.debug(start_txt)
        print(start_txt)
        self.__connect()

    def __reg_resp_console(self):
        self.subscribe(201, lambda m: print(f'{m} connected'))
        self.subscribe(202, lambda m: print(f'{m} disconnected'))
        self.subscribe(203, lambda m: print(m))

    @try_except_wrapper
    def __connect(self):
        self.socket.connect((self.addr, self.port))
        self.connected = True
        print('Done')
        print_help()
        response = self.presence()
        if response.code != OK:
            self.logger.warning(response)
            return

        self.listener = ClientThread(self.__listen_server, self.logger)
        self.listener.start()

        # self.__console()

        # self.sender = ClientThread(self.send_msg, self.logger)
        # self.sender.start()
        # self.sender.join()

    @try_except_wrapper
    def __send_request(self, request):
        if not self.connected:
            return
        self.logger.debug(request)
        send_data(self.socket, request)

    @try_except_wrapper
    def __get_response(self):
        if not self.connected:
            return
        response = get_data(self.socket)
        self.logger.debug(response)
        return response

    def presence(self):
        request = Request(RequestAction.PRESENCE, self.USER)
        self.__send_request(request)
        return self.__get_response()

    def __console(self):
        while self.connected:
            msg = input('Enter message:\n')
            if msg.upper() == 'Q':
                break
            if msg[0] == '!':
                self.__execute_local_command(msg[1:])
                continue
            if msg[0] == '#':
                request = Request(RequestAction.COMMAND, msg[1:])
                self.parse_command(request.body)
            else:
                msg = Msg(msg, self.USER)
                msg.parse_msg()
                self.storage.add_message(msg.to, msg.text)
                request = Request(RequestAction.MESSAGE, msg)
            self.__send_request(request)

    def get_chat_req(self, contact):
        req = Request(RequestAction.COMMAND,
                      f'get_chat {self.USER.username} {contact}')
        self.__send_request(req)

    def get_users_req(self):
        self.__send_request(Request(RequestAction.COMMAND, 'get_users'))

    def get_contacts_req(self):
        self.__send_request(Request(RequestAction.COMMAND, 'get_contacts'))

    def add_contact(self, contact):
        if self.storage.get_contact(contact):
            return False
        self.storage.add_contact(contact)
        req = Request(RequestAction.COMMAND, f'add_contact {contact}')
        self.__send_request(req)

    def rem_contact(self, contact):
        self.storage.remove_contact(contact)
        req = Request(RequestAction.COMMAND, f'rem_contact {contact}')
        self.__send_request(req)

    def sync_contacts(self, contacts):
        for c in contacts:
            self.storage.append_contact(c)

    def send_msg(self, text, to):
        msg = Msg(text, self.USER, to)
        self.storage.add_message(msg.to, msg.text)
        request = Request(RequestAction.MESSAGE, msg)
        self.__send_request(request)

    def parse_command(self, command):
        command, *args = command.split(' ')
        if command == 'add_contact':
            self.storage.add_contact(args[0])
        elif command == 'rem_contact':
            self.storage.remove_contact(args[0])

    def __execute_local_command(self, command):
        if command == 'help':
            print_help()
        elif command == 'set_name':
            name = input('Set new name')
            self.USER.username = name
            self.__send_request(Request(RequestAction.PRESENCE, self.USER))
        elif command == 'reconnect':
            self.start()
        else:
            print('Command not found')

    def __listen_server(self):
        while self.connected:
            resp = get_data(self.socket)
            self.logger.debug(resp)
            if resp.type != RESPONSE:
                self.logger.warning(f'Received not RESPONSE:\n {resp}')
                continue
            if resp.code == 101:
                self.answers.put(resp.message)
                print(f'server: {resp.message}')
            elif resp.code in self.subs.keys():
                for s in self.subs[resp.code]:
                    s(resp.message)
            else:
                print(resp.message)

    def subscribe(self, code, func):
        if code in self.subs.keys():
            self.subs[code].append(func)
        else:
            self[code] = [func]