示例#1
0
 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])
示例#2
0
    def run(self):
        self.print_help()
        while True:
            command = input('Enter command: ')
            if command == 'message':
                self.create_message()
            elif command == 'help':
                self.print_help()
            elif command == 'exit':
                try:
                    send_message(self.sock, self.create_exit_message())
                except BaseException:
                    pass
                LOGGER.info('Connection closed.')
                # Задержка неоходима, чтобы успело уйти сообщение о выходе
                time.sleep(DEF_TIMEOUT)
                break
            elif command == 'contacts':
                with database_lock:
                    contacts_list = self.database.get_contacts()
                for contact in contacts_list:
                    print(contact)

            elif command == 'edit':
                self.edit_contacts()

            elif command == 'history':
                self.print_history()

            else:
                print('Undefined command.')
                self.print_help()
示例#3
0
def user_list_request(sock, username):
    LOGGER.debug(f'Запрос списка известных пользователей {username}')
    req = {ACTION: USERS_REQUEST, TIME: time.time(), ACCOUNT_NAME: username}
    send_message(sock, req)
    ans = get_my_message(sock)
    if RESPONSE in ans and ans[RESPONSE] == 202:
        return ans[LIST_INFO]
    else:
        raise ServerError
示例#4
0
 def remove_contact(self, contact):
     logger.debug(f'Удаление контакта {contact}')
     req = {
         ACTION: REMOVE_CONTACT,
         TIME: time.time(),
         USER: self.username,
         ACCOUNT_NAME: contact
     }
     with socket_lock:
         send_message(self.transport, req)
         self.process_server_ans(get_my_message(self.transport))
示例#5
0
def contacts_list_request(sock, name):
    LOGGER.debug(f'Запрос контакт листа для пользователся {name}')
    req = {ACTION: GET_CONTACTS, TIME: time.time(), USER: name}
    LOGGER.debug(f'Сформирован запрос {req}')
    send_message(sock, req)
    ans = get_my_message(sock)
    LOGGER.debug(f'Получен ответ {ans}')
    if RESPONSE in ans and ans[RESPONSE] == 202:
        return ans[LIST_INFO]
    else:
        raise ServerError
示例#6
0
 def remove_contact(self, contact):
     '''Метод отправляющий на сервер сведения о удалении контакта.'''
     logger.debug(f'Deleting contact {contact}')
     req = {
         ACTION: REMOVE_CONTACT,
         TIME: time.time(),
         USER: self.username,
         ACCOUNT_NAME: contact
     }
     with socket_lock:
         send_message(self.transport, req)
         self.process_server_ans(get_message(self.transport))
示例#7
0
 def transport_shutdown(self):
     self.running = False
     message = {
         ACTION: EXIT,
         TIME: time.time(),
         ACCOUNT_NAME: self.username
     }
     with socket_lock:
         try:
             send_message(self.transport, message)
         except OSError:
             pass
     logger.debug('Транспорт завершает работу.')
     time.sleep(0.5)
示例#8
0
 def process_message(self, message, listen_socks):
     if message[DESTINATION] in self.names and self.names[
             message[DESTINATION]] in listen_socks:
         send_message(self.names[message[DESTINATION]], message)
         LOGGER.info(
             f'Отправлено сообщение пользователю {message[DESTINATION]} от пользователя {message[SENDER]}.'
         )
     elif message[DESTINATION] in self.names and self.names[
             message[DESTINATION]] not in listen_socks:
         raise ConnectionError
     else:
         LOGGER.error(
             f'Пользователь {message[DESTINATION]} не зарегистрирован на сервере, отправка сообщения невозможна.'
         )
示例#9
0
 def user_list_update(self):
     logger.debug(f'Запрос списка известных пользователей {self.username}')
     req = {
         ACTION: USERS_REQUEST,
         TIME: time.time(),
         ACCOUNT_NAME: self.username
     }
     with socket_lock:
         send_message(self.transport, req)
         ans = get_my_message(self.transport)
     if RESPONSE in ans and ans[RESPONSE] == 202:
         self.database.add_users(ans[LIST_INFO])
     else:
         logger.error('Не удалось обновить список известных пользователей.')
示例#10
0
 def contacts_list_update(self):
     '''Метод обновляющий с сервера список контактов.'''
     self.database.contacts_clear()
     logger.debug(f'Запрос контакт листа для пользователся {self.name}')
     req = {ACTION: GET_CONTACTS, TIME: time.time(), USER: self.username}
     logger.debug(f'Сформирован запрос {req}')
     with socket_lock:
         send_message(self.transport, req)
         ans = get_message(self.transport)
     logger.debug(f'Получен ответ {ans}')
     if RESPONSE in ans and ans[RESPONSE] == 202:
         for contact in ans[LIST_INFO]:
             self.database.add_contact(contact)
     else:
         logger.error('Не удалось обновить список контактов.')
示例#11
0
def remove_contact(sock, username, contact):
    LOGGER.debug(f'Создание контакта {contact}')
    req = {
        ACTION: REMOVE_CONTACT,
        TIME: time.time(),
        USER: username,
        ACCOUNT_NAME: contact
    }
    send_message(sock, req)
    ans = get_my_message(sock)
    if RESPONSE in ans and ans[RESPONSE] == 200:
        pass
    else:
        raise ServerError('Ошибка удаления клиента')
    print('Удачное удаление')
示例#12
0
 def key_request(self, user):
     '''Метод запрашивающий с сервера публичный ключ пользователя.'''
     logger.debug(f'Public key request for {user}')
     req = {
         ACTION: PUBLIC_KEY_REQUEST,
         TIME: time.time(),
         ACCOUNT_NAME: user
     }
     with socket_lock:
         send_message(self.transport, req)
         ans = get_message(self.transport)
     if RESPONSE in ans and ans[RESPONSE] == 511:
         return ans[DATA]
     else:
         logger.error(f'Unable to get key for {user}.')
示例#13
0
    def send_message(self, to, message):
        message_dict = {
            ACTION: MESSAGE,
            SENDER: self.username,
            DESTINATION: to,
            TIME: time.time(),
            MESSAGE_TEXT: message
        }
        logger.debug(f'Сформирован словарь сообщения: {message_dict}')

        # Необходимо дождаться освобождения сокета для отправки сообщения
        with socket_lock:
            send_message(self.transport, message_dict)
            self.process_server_ans(get_my_message(self.transport))
            logger.info(f'Отправлено сообщение для пользователя {to}')
示例#14
0
 def user_list_update(self):
     '''Метод обновляющий с сервера список пользователей.'''
     logger.debug(f'Запрос списка известных пользователей {self.username}')
     req = {
         ACTION: USERS_REQUEST,
         TIME: time.time(),
         ACCOUNT_NAME: self.username
     }
     with socket_lock:
         send_message(self.transport, req)
         ans = get_message(self.transport)
     if RESPONSE in ans and ans[RESPONSE] == 202:
         self.database.add_users(ans[LIST_INFO])
     else:
         logger.error('Unable to update known users list.')
示例#15
0
    def create_message(self):
        to = input('Введите получателя сообщения: ')
        message = input('Введите сообщение для отправки: ')
        message_dict = {
            ACTION: MESSAGE,
            SENDER: self.account_name,
            DESTINATION: to,
            TIME: time.time(),
            MESSAGE_TEXT: message
        }
        LOGGER.debug(f'Сформирован словарь сообщения: {message_dict}')

        with database_lock:
            self.database.save_message(self.account_name, to, message)

        try:
            send_message(self.sock, message_dict)
            LOGGER.info(f'Отправлено сообщение для пользователя {to}')
        except BaseException:
            LOGGER.critical('Потеряно соединение с сервером.')
            exit(1)
示例#16
0
    def connection_init(self, port, ip):
        # Инициализация сокета и сообщение серверу о нашем появлении
        self.transport = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        # Таймаут необходим для освобождения сокета.
        self.transport.settimeout(5)

        # Соединяемся, 5 попыток соединения, флаг успеха ставим в True если
        # удалось
        connected = False
        for i in range(2):
            logger.info(f'Попытка подключения №{i + 1}')
            try:
                self.transport.connect((ip, port))
            except (OSError, ConnectionRefusedError):
                pass
            else:
                connected = True
                break
            time.sleep(1)

        # Если соединится не удалось - исключение
        if not connected:
            logger.critical('Не удалось установить соединение с сервером')
            raise ServerError('Не удалось установить соединение с сервером')

        logger.debug('Установлено соединение с сервером')

        # Посылаем серверу приветственное сообщение и получаем ответ что всё
        # нормально или ловим исключение.
        try:
            with socket_lock:
                send_message(self.transport, self.create_presence())
                self.process_server_ans(get_my_message(self.transport))
        except (OSError, json.JSONDecodeError):
            logger.critical('Потеряно соединение с сервером!')
            raise ServerError('Потеряно соединение с сервером!')

        # Раз всё хорошо, сообщение о установке соединения.
        logger.info('Соединение с сервером успешно установлено.')
示例#17
0
 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]} от пользователя {message[SENDER]}.'
             )
         except OSError:
             self.remove_client(message[DESTINATION])
     elif message[DESTINATION] in self.names and self.names[
             message[DESTINATION]] not in self.listen_sockets:
         logger.error(
             f'Связь с клиентом {message[DESTINATION]} была потеряна. Соединение закрыто, доставка невозможна.'
         )
         self.remove_client(self.names[message[DESTINATION]])
     else:
         logger.error(
             f'Пользователь {message[DESTINATION]} не зарегистрирован на сервере, отправка сообщения невозможна.'
         )
示例#18
0
def main():
    print('Client started')

    server_address, server_port, client_name = get_args()

    if not client_name:
        client_name = input('Введите имя пользователя: ')
    else:
        print(f'Клиентский модуль запущен с именем: {client_name}')

    LOGGER.info(
        f'Запущен клиент с парамертами: адрес сервера: {server_address} , порт: {server_port}, имя пользователя: {client_name}'
    )

    try:
        transport = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        transport.settimeout(DEF_TIMEOUT)

        transport.connect((server_address, server_port))
        send_message(transport, create_presence(client_name))
        answer = process_response_ans(get_my_message(transport))
        LOGGER.info(
            f'Установлено соединение с сервером. Ответ сервера: {answer}')
        print(f'Установлено соединение с сервером.')
    except json.JSONDecodeError:
        LOGGER.error('Не удалось декодировать полученную Json строку.')
        exit(1)
    except ServerError as error:
        LOGGER.error(
            f'При установке соединения сервер вернул ошибку: {error.text}')
        exit(1)
    except ReqFieldMissingError as missing_error:
        LOGGER.error(
            f'В ответе сервера отсутствует необходимое поле {missing_error.missing_field}'
        )
        exit(1)
    except (ConnectionRefusedError, ConnectionError):
        LOGGER.critical(
            f'Не удалось подключиться к серверу {server_address}:{server_port}, конечный компьютер отверг запрос на подключение.'
        )
        exit(1)
    else:

        database = ClientDB(client_name)
        database_load(transport, database, client_name)

        module_sender = ClientSender(client_name, transport, database)
        module_sender.daemon = True
        module_sender.start()
        LOGGER.debug('Запущены процессы')

        module_receiver = ClientReader(client_name, transport, database)
        module_receiver.daemon = True
        module_receiver.start()

        while True:
            time.sleep(1)
            if module_receiver.is_alive() and module_sender.is_alive():
                continue
            break
示例#19
0
    def connection_init(self, port, ip):
        '''Метод отвечающий за устанновку соединения с сервером.'''
        # Инициализация сокета и сообщение серверу о нашем появлении
        self.transport = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        # Таймаут необходим для освобождения сокета.
        self.transport.settimeout(5)

        # Соединяемся, 5 попыток соединения, флаг успеха ставим в True если
        # удалось
        connected = False
        for i in range(5):
            logger.info(f'Попытка подключения №{i + 1}')
            try:
                self.transport.connect((ip, port))
            except (OSError, ConnectionRefusedError):
                pass
            else:
                connected = True
                logger.debug("Connection established.")
                break
            time.sleep(1)

        # Если соединится не удалось - исключение
        if not connected:
            logger.critical('Не удалось установить соединение с сервером')
            raise ServerError('Не удалось установить соединение с сервером')

        logger.debug('Starting auth dialog.')

        # Запускаем процедуру авторизации
        # Получаем хэш пароля
        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)

        logger.debug(f'Passwd hash ready: {passwd_hash_string}')

        # Получаем публичный ключ и декодируем его из байтов
        pubkey = self.keys.publickey().export_key().decode('ascii')

        # Авторизируемся на сервере
        with socket_lock:
            presense = {
                ACTION: PRESENCE,
                TIME: time.time(),
                USER: {
                    ACCOUNT_NAME: self.username,
                    PUBLIC_KEY: pubkey
                }
            }
            logger.debug(f"Presense message = {presense}")
            # Отправляем серверу приветственное сообщение.
            try:
                send_message(self.transport, presense)
                ans = get_message(self.transport)
                logger.debug(f'Server response = {ans}.')
                # Если сервер вернул ошибку, бросаем исключение.
                if RESPONSE in ans:
                    if ans[RESPONSE] == 400:
                        raise ServerError(ans[ERROR])
                    elif ans[RESPONSE] == 511:
                        # Если всё нормально, то продолжаем процедуру
                        # авторизации.
                        ans_data = ans[DATA]
                        hash = hmac.new(passwd_hash_string,
                                        ans_data.encode('utf-8'), 'MD5')
                        digest = hash.digest()
                        my_ans = RESPONSE_511
                        my_ans[DATA] = binascii.b2a_base64(digest).decode(
                            'ascii')
                        send_message(self.transport, my_ans)
                        self.process_server_ans(get_message(self.transport))
            except (OSError, json.JSONDecodeError) as err:
                logger.debug(f'Connection error.', exc_info=err)
                raise ServerError('Сбой соединения в процессе авторизации.')
示例#20
0
    def process_client_message(self, message, client):
        LOGGER.debug(f'Message fom client : {message}')
        # 1 ###################################################################
        if ACTION in message and message[
                ACTION] == PRESENCE and TIME in message and USER in message:
            print(
                "ACTION in message and message[ACTION] == PRESENCE and TIME in message and USER in message:"
            )
            print(f"names : {self.names}")
            if message[USER][ACCOUNT_NAME] not in self.names.keys():
                self.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
            else:
                response = RESPONSE_400
                response[ERROR] = 'Имя пользователя уже занято.'
                send_message(client, response)
                self.clients.remove(client)
                client.close()
            return

        # 2 ###################################################################
        elif ACTION in message and message[ACTION] == MESSAGE and DESTINATION in message and TIME in message \
                and SENDER in message and MESSAGE_TEXT in message and self.names[message[SENDER]] == client:
            if message[DESTINATION] in self.names:
                self.messages.append(message)
                self.database.process_message(message[SENDER],
                                              message[DESTINATION])
                send_message(client, RESPONSE_200)
            else:
                response = RESPONSE_400
                response[ERROR] = 'Пользователь не зарегистрирован на сервере.'
                send_message(client, response)
            return

        # 3 ################################################################
        elif ACTION in message and message[ACTION] == EXIT and ACCOUNT_NAME in message \
                and self.names[message[ACCOUNT_NAME]] == client:

            self.database.user_logout(message[ACCOUNT_NAME])
            self.clients.remove(self.names[message[ACCOUNT_NAME]])
            self.names[message[ACCOUNT_NAME]].close()
            del self.names[message[ACCOUNT_NAME]]
            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)

        ############
        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 = RESPONSE_202
            response[LIST_INFO] = [
                user[0] for user in self.database.users_list()
            ]
            send_message(client, response)

        ############
        else:
            response = RESPONSE_400
            response[ERROR] = 'Запрос некорректен.'
            send_message(client, response)
            return
示例#21
0
    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.autorize_user(message, client)

        # Если это сообщение, то отправляем его получателю.
        elif ACTION in message and message[ACTION] == MESSAGE and DESTINATION in message and TIME 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)

        # Если это запрос контакт-листа
        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_pubkey(message[ACCOUNT_NAME])
            # может быть, что ключа ещё нет (пользователь никогда не логинился,
            # тогда шлём 400)
            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)
示例#22
0
 def autorize_user(self, message, sock):
     '''Метод реализующий авторизцию пользователей.'''
     # Если имя пользователя уже занято то возвращаем 400
     logger.debug(f'Start auth process for {message[USER]}')
     if message[USER][ACCOUNT_NAME] in self.names.keys():
         response = RESPONSE_400
         response[ERROR] = 'Имя пользователя уже занято.'
         try:
             logger.debug(f'Username busy, sending {response}')
             send_message(sock, response)
         except OSError:
             logger.debug('OS Error')
             pass
         self.clients.remove(sock)
         sock.close()
     # Проверяем что пользователь зарегистрирован на сервере.
     elif not self.database.check_user(message[USER][ACCOUNT_NAME]):
         response = RESPONSE_400
         response[ERROR] = 'Пользователь не зарегистрирован.'
         try:
             logger.debug(f'Unknown username, sending {response}')
             send_message(sock, response)
         except OSError:
             pass
         self.clients.remove(sock)
         sock.close()
     else:
         logger.debug('Correct username, starting passwd check.')
         # Иначе отвечаем 511 и проводим процедуру авторизации
         # Словарь - заготовка
         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, 'MD5')
         digest = hash.digest()
         logger.debug(f'Auth message = {message_auth}')
         try:
             # Обмен с клиентом
             send_message(sock, message_auth)
             ans = get_message(sock)
         except OSError as err:
             logger.debug('Error in auth, data:', exc_info=err)
             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
             response[ERROR] = 'Неверный пароль.'
             try:
                 send_message(sock, response)
             except OSError:
                 pass
             self.clients.remove(sock)
             sock.close()