def update_users_list(self): """Метод обновления списка пользователей. Отправляет запрос на сервер, по получении овтета - обновляет БД""" send_message(self.client_socket, self.get_users_msg()) CLIENT_LOGGER.info(f'Отправлено get_users сообщение') answer = self.get_response_safe() if answer is not False: self.database.load_known_users(answer[ALERT])
def update_contacts_list(self): """Метод обновления списка контактов. Отправляет запрос на сервер, по получении овтета - обновляет БД""" send_message(self.client_socket, self.get_contacts_msg()) CLIENT_LOGGER.info(f'Отправлено get_contacts сообщение') answer = self.get_response_safe() if answer is not False: for contact in answer[ALERT]: self.database.add_contact(contact)
def pubkey_request(self, username): """Метод отправки запроса публичного ключа и получения ответа""" send_message(self.client_socket, self.get_pubkey_msg(username)) ans = self.get_response_safe() if RESPONSE in ans and ans[RESPONSE] == RESPCODE_AUTH_REQUIRED: return ans[KEY] else: CLIENT_LOGGER.error(f'Не удалось получить ключ собеседника {username}.')
def preauthorize_user(self, name, client_socket, pubkey): """Метод для предавторизации пользователя""" if name in self.client_names.keys(): SERVER_LOGGER.error('Имя пользователя уже занято') response = protocol.SERVER_RESPONSE_BAD_REQUEST response[ALERT] = 'Имя пользователя уже занято' send_message(client_socket, response) self.clients.remove(client_socket) client_socket.close() return elif not self.database.check_user(name): SERVER_LOGGER.error('Пользователь не зарегистрирован') response = protocol.SERVER_RESPONSE_BAD_REQUEST response[ALERT] = 'Пользователь не зарегистрирован' send_message(client_socket, response) self.clients.remove(client_socket) client_socket.close() return message = AUTHENTICATE_REQUIRED_MSG rand_msg = binascii.hexlify(os.urandom(64)) message[DATA] = rand_msg.decode('ascii') print(self.database.get_passwd(name)) hash_pwd = hmac.new(self.database.get_passwd(name), rand_msg, 'MD5') print(hash_pwd) digest = hash_pwd.digest() print(digest) send_message(client_socket, message) ans = get_message(client_socket) client_digest = binascii.a2b_base64(ans[USER][PASSWORD]) if ACTION in ans and ans[ACTION] == AUTHENTICATE: if hmac.compare_digest(digest, client_digest): SERVER_LOGGER.debug(f'Ответ на {PRESENCE} корректный') # self.messages.append(('', create_login_message(msg[USER][ACCOUNT_NAME]))) self.client_names[name] = client_socket cli_ip, cli_port = client_socket.getpeername() self.database.user_login(name, cli_ip, cli_port, pubkey) return RESPCODE_OK else: SERVER_LOGGER.error('Пароль не верен') response = protocol.SERVER_RESPONSE_BAD_REQUEST response[ALERT] = 'Пароль не верен' send_message(client_socket, response) self.clients.remove(client_socket) client_socket.close() return else: SERVER_LOGGER.error('Некорректный запрос на аутентификацию!') response = protocol.SERVER_RESPONSE_BAD_REQUEST response[ALERT] = 'Некорректный запрос на аутентификацию!' send_message(client_socket, response) self.clients.remove(client_socket) client_socket.close() return
def run(self): """Основной цикл работы Сервера""" self.__init_socket() while True: try: client_sock, addr = self.socket.accept() except OSError: pass else: SERVER_LOGGER.info(f'Входящее подключение с адреса: {addr}') self.clients.append(client_sock) recv_data_lst = [] send_data_lst = [] err_lst = [] try: if self.clients: recv_data_lst, send_data_lst, err_lst = select( self.clients, self.clients, [], 0) except OSError: pass if recv_data_lst: for client in recv_data_lst: try: inc_msg = get_message(client) SERVER_LOGGER.debug('Получено сообщение:' f'{inc_msg}') resp_code = self.process_incoming_message( inc_msg, client) if resp_code is not None: resp_msg = create_response(resp_code) SERVER_LOGGER.info('Отправлен ответ:' f'{resp_msg}') send_message(client, resp_msg) except ValueError as e: _error = f'Ошибка декодирования сообщения от клиента {e}' SERVER_LOGGER.error(_error) send_message( client, create_response(RESPCODE_SERVER_ERROR, _error)) except Exception as e: SERVER_LOGGER.error( f'Клиент {client.getpeername()} отключился! {e}') print(e) self.clients.remove(client) if send_data_lst and self.messages: for msg in self.messages: try: self.process_message(msg, send_data_lst) except Exception as e: SERVER_LOGGER.info( f'Обработка сообщения прошла неуспешно! {e}') self.messages.clear()
def process_message(self, msg, conn_socks, add_contact=1): """Обработка сообщения. Отправка его адресату, если адресата нет - отправка всем активным клиентам.""" msg_body = msg[1] if TO in msg_body: if msg_body[TO] in self.client_names: if self.client_names[msg_body[TO]] in conn_socks: try: send_message(self.client_names[msg_body[TO]], msg_body) SERVER_LOGGER.debug( f'Сообщение {msg_body} было успешно отправлено юзеру {msg_body[TO]}' ) if msg_body[FROM] != 'system': self.database.process_message( msg_body[FROM], msg_body[TO]) if add_contact: self.database.add_user_contact( msg_body[FROM], msg_body[TO]) self.database.add_user_contact( msg_body[TO], msg_body[FROM]) return except: # todo: При внезапном разрыве соединения, новым клиентам не удается подключиться SERVER_LOGGER.error(f'Клиент отключился') del self.client_names[msg_body[TO]] else: # todo: При внезапном разрыве соединения, новым клиентам не удается подключиться и не чистится имя SERVER_LOGGER.error( f'Соединение с {self.client_names[msg_body[TO]].getpeername()} разорвано!' ) self.clients.remove(self.client_names[msg_body[TO]]) self.database.user_logout(msg_body[TO]) del self.client_names[msg_body[TO]] else: SERVER_LOGGER.error( f'пользователь {msg_body[TO]} не зарегистрирован в чате') return else: for name in self.client_names.keys(): if msg[1][FROM] == name: continue msg[1][TO] = name self.process_message(msg, conn_socks, add_contact=0) return
def __init_connection(self, ip, port): """Метод инициализации подключения к серверу""" self.client_socket = socket(AF_INET, SOCK_STREAM) try: self.client_socket.connect((ip, port)) CLIENT_LOGGER.info(f'Исходящее подключение к серверу: {ip}:{port}') except OSError as e: CLIENT_LOGGER.critical(e) sys.exit(1) else: send_message(self.client_socket, self.create_presence()) CLIENT_LOGGER.info(f'Отправлено presence сообщение') answer = self.get_response_safe() if not answer: exit(1) elif RESPONSE in answer and not answer[RESPONSE] == RESPCODE_AUTH_REQUIRED: exit(1) else: send_message(self.client_socket, self.create_authenticate()) CLIENT_LOGGER.info(f'Отправлено authenticate сообщение') time.sleep(3) CLIENT_LOGGER.info(f'Прошло 3 секунды...') if not self.get_response_safe(): exit(1)
def test_send_incorrect_message(self): test_socket = TestSocket(self.test_msg_send) send_message(test_socket, self.test_msg_send) self.assertRaises(ValueError, send_message, test_socket, self.test_incorrect_msg_send)
def test_send_ok_message(self): test_socket = TestSocket(self.test_msg_send) send_message(test_socket, self.test_msg_send) self.assertEqual(test_socket.encoded_msg, test_socket.received_msg)
def process_incoming_message(self, msg, client=None): """Метод обработки входящего сообщения""" if ACTION in msg: if msg[ACTION] == PRESENCE: if msg.keys() != protocol.PRESENCE_MSG_CLIENT.keys(): SERVER_LOGGER.error( 'В запросе присутствуют лишние поля или отсутствуют нужные!' ) return RESPCODE_BAD_REQ if msg[USER].keys() != protocol.PRESENCE_MSG_CLIENT[USER].keys( ): SERVER_LOGGER.error(f'В запросе неверный объект {USER}!') return RESPCODE_BAD_REQ SERVER_LOGGER.debug( f'Сообщение {PRESENCE} корректное. Проверка пользователя') return self.preauthorize_user(msg[USER][ACCOUNT_NAME], client, msg[USER][PUBLIC_KEY]) elif msg[ACTION] == GET_PUBLIC_KEY: if msg.keys() != protocol.GET_PUBKEY_REQ_MSG.keys(): SERVER_LOGGER.error( 'В запросе присутствуют лишние поля или отсутствуют нужные!' ) return RESPCODE_BAD_REQ SERVER_LOGGER.debug(f'Сообщение {GET_PUBLIC_KEY} корректное') pubkey = self.database.get_pubkey(msg[USER]) if pubkey: send_message(client, create_pubkey_message(pubkey)) else: send_message( client, create_response( RESPCODE_BAD_REQ, f'Публичный ключ для пользователя {msg[USER]} не найден' )) return elif msg[ACTION] == MSG: if msg.keys() != protocol.CHAT_MSG_CLIENT.keys(): if msg.keys() != protocol.CHAT_USER_MSG_CLIENT.keys(): SERVER_LOGGER.error( 'В запросе присутствуют лишние поля или отсутствуют нужные!' ) print(protocol.CHAT_MSG_CLIENT.keys()) return RESPCODE_BAD_REQ SERVER_LOGGER.debug(f'Сообщение {MSG} корректное') self.messages.append((msg[FROM], msg)) return elif msg[ACTION] == GET_USERS: if msg.keys() != protocol.GET_USERS_MSG.keys(): SERVER_LOGGER.error( 'В запросе присутствуют лишние поля или отсутствуют нужные!' ) return RESPCODE_BAD_REQ SERVER_LOGGER.debug(f'Сообщение {GET_USERS} корректное') users = self.database.users_list() send_message(client, create_users_contacts_message(users)) return elif msg[ACTION] == GET_CONTACTS: if msg.keys() != protocol.GET_USER_CONTACTS_MSG.keys(): SERVER_LOGGER.error( 'В запросе присутствуют лишние поля или отсутствуют нужные!' ) return RESPCODE_BAD_REQ SERVER_LOGGER.debug(f'Сообщение {GET_CONTACTS} корректное') contacts = self.database.get_user_contact_list(msg[USER_LOGIN]) send_message(client, create_users_contacts_message(contacts)) return elif msg[ACTION] == ADD_CONTACT: if msg.keys() != protocol.ADD_USER_CONTACT_MSG.keys(): SERVER_LOGGER.error( 'В запросе присутствуют лишние поля или отсутствуют нужные!' ) return RESPCODE_BAD_REQ self.database.add_user_contact(msg[USER_LOGIN], msg[USER_ID]) SERVER_LOGGER.debug(f'Сообщение {ADD_CONTACT} корректное') return RESPCODE_OK elif msg[ACTION] == REMOVE_CONTACT: if msg.keys() != protocol.REMOVE_USER_CONTACT_MSG.keys(): SERVER_LOGGER.error( 'В запросе присутствуют лишние поля или отсутствуют нужные!' ) return RESPCODE_BAD_REQ self.database.remove_user_contact(msg[USER_LOGIN], msg[USER_ID]) SERVER_LOGGER.debug(f'Сообщение {REMOVE_CONTACT} корректное') return RESPCODE_OK elif msg[ACTION] == EXIT: SERVER_LOGGER.debug(f'Клиент {msg[FROM]} покинул чатик') # self.messages.append(('', create_logout_message(msg[FROM]))) self.database.user_logout(msg[FROM]) del self.client_names[msg[FROM]] return else: SERVER_LOGGER.error( f'Такое значение {ACTION} {msg[ACTION]} не поддерживается!' ) return RESPCODE_BAD_REQ else: SERVER_LOGGER.error(f'{ACTION} отсутствует в сообщении') return RESPCODE_BAD_REQ
def remove_contact(self, user_contact): """Метод отправки запроса на удаление контакта""" return send_message(self.client_socket, self.create_remove_contact_msg(user_contact))
def add_contact(self, user_contact): """Метод отправки запроса на добавление контакта""" return send_message(self.client_socket, self.create_add_contact_msg(user_contact))
def exit_(self): """Метод отправки сообщения на выход пользователя из чата""" return send_message(self.client_socket, self.create_exit_message())
def send_message(self, user_to, message_txt): """Метод отправки созданного сообщения""" return send_message(self.client_socket, self.create_message(user_to, message_txt))