class MessageProcessor(threading.Thread): """ Основной класс сервера. Принимает содинения, словари - пакеты от клиентов, обрабатывает поступающие сообщения. Работает в качестве отдельного потока. """ port = Port() def __init__(self, listen_address, listen_port, database): self.addr = listen_address self.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, client_address = self.sock.accept() except OSError: pass else: logger.info(f'Установлено соедение с ПК {client_address}') client.settimeout(5) self.clients.append(client) recv_data_lst = [] send_data_lst = [] err_lst = [] try: if self.clients: recv_data_lst, self.listen_sockets, self.error_sockets = select.select( self.clients, self.clients, [], 0) except OSError as err: logger.error(f'Ошибка работы с сокетами: {err.errno}') if recv_data_lst: for client_with_message in recv_data_lst: try: self.process_client_message( get_message(client_with_message), client_with_message) except (OSError, json.JSONDecodeError, TypeError): self.remove_client(client_with_message) def remove_client(self, client): """ Метод обработчик клиента с которым прервана связь. Ищет клиента и удаляет его из списков и базы: """ 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 init_socket(self): """Метод инициализатор сокета.""" logger.info( f'Запущен сервер, порт для подключений: {self.port} , адрес с которого принимаются подключения: {self.addr}. Если адрес не указан, принимаются соединения с любых адресов.' ) transport = socket.socket(socket.AF_INET, socket.SOCK_STREAM) transport.bind((self.addr, self.port)) transport.settimeout(0.5) self.sock = transport self.sock.listen(MAX_CONNECTIONS) 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]} не зарегистрирован на сервере, отправка сообщения невозможна.' ) @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.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]) 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) else: response = RESPONSE_400 response[ERROR] = 'Запрос некорректен.' try: send_message(client, response) except OSError: self.remove_client(client) def autorize_user(self, message, sock): """Метод реализующий авторизцию пользователей.""" if message[USER][ACCOUNT_NAME] in self.names.keys(): response = RESPONSE_400 response[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 response[ERROR] = 'Пользователь не зарегистрирован.' try: send_message(sock, response) except OSError: pass self.clients.remove(sock) sock.close() else: message_auth = RESPONSE_511 random_str = binascii.hexlify(os.urandom(64)) 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 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])
class MessageProcessor(threading.Thread): """ Основной класс сервера. Принимает содинения, словари - пакеты от клиентов, обрабатывает поступающие сообщения. Работает в качестве отдельного потока. """ port = Port() def __init__(self, listen_address, listen_port, database): # параментры подключения self.addr = listen_address self.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, client_address = self.sock.accept() except OSError: pass # timeout вышел else: server_logger.info( f'Установлено соединение с: {str(client_address)}') client.settimeout(5) self.clients.append(client) recv_data_lst = [] send_data_lst = [] err_lst = [] # проверяем на наличие ждущих клиентов try: if self.clients: recv_data_lst, self.listen_sockets, self.error_sockets = \ select.select(self.clients, self.clients, [], 0) except OSError as err: server_logger.error(f'Ошибка работы с сокетами: {err.errno}') # принимаем сообщения и если ошибка, исключаем клиента if recv_data_lst: for client_with_message in recv_data_lst: try: self.process_client_message( get_message( client_with_message, CONFIGS), client_with_message, CONFIGS) 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.user_logout(name) del self.names[name] break self.clients.remove(client) client.close() def init_socket(self): """Метод инициализатор сокета.""" server_logger.info( f'Запущен сервер, порт для подключений: {self.port} ,' f' адрес с которого принимаются подключения: {self.addr}.' f' Если адрес не указан, принимаются соединения с любых адресов.') # сервер создаёт сокет transport = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # привязывает сокет к IP-адресу и порту машины transport.bind((self.addr, self.port)) # Таймаут для операций с сокетом (0.5 секунды) transport.settimeout(0.5) self.sock = transport # готов принимать соединения self.sock.listen(CONFIGS.get('MAX_CONNECTIONS')) @log def process_message(self, message): """ Метод отправки сообщения клиенту. """ if message[CONFIGS.get('TO_USER')] in self.names and self.names[ message[CONFIGS.get('TO_USER')]] in self.listen_sockets: try: send_message( self.names[message[CONFIGS.get('TO_USER')]], message, CONFIGS ) server_logger.info( f'Отправлено сообщение пользователю ' f'{message[CONFIGS.get("TO_USER")]} ' f'от пользователя {message[CONFIGS.get("FROM_USER")]}.') except OSError: self.remove_client(message[CONFIGS.get('TO_USER')]) elif message[CONFIGS.get('TO_USER')] in self.names and self.names[ message[CONFIGS.get('TO_USER')]] not in self.listen_sockets: server_logger.error( f'Связь с клиентом {message[CONFIGS.get("TO_USER")]} ' f'была потеряна. ' f'Соединение закрыто, доставка невозможна.') self.remove_client(self.names[message[CONFIGS.get('TO_USER')]]) else: server_logger.error( f'Пользователь {message[CONFIGS.get("TO_USER")]} ' f'не зарегистрирован на сервере, ' f'отправка сообщения невозможна.') @login_required # метод проверки сообщения клиента def process_client_message(self, message, client, CONFIGS): """Метод - отбработчик поступающих сообщений.""" server_logger.debug(f'Обработка сообщения от клиента: {message}') # если это сообщение о присутствии, принимаем и отвечаем if CONFIGS.get('ACTION') in message and \ message[CONFIGS.get('ACTION')] == CONFIGS.get('PRESENCE') and \ CONFIGS.get('TIME') in message and \ CONFIGS.get('USER') in message: # Если сообщение о присутствии то вызываем функцию авторизации. self.authorize_user(message, client) # Если это сообщение, то отправляем его получателю. elif CONFIGS.get('ACTION') in message and \ message[CONFIGS.get('ACTION')] == CONFIGS.get('MESSAGE') and \ CONFIGS.get('TO_USER') in message and \ CONFIGS.get('TIME') in message and \ CONFIGS.get('FROM_USER') in message and \ CONFIGS.get('MESSAGE_TEXT') in message and \ self.names[message[CONFIGS.get('FROM_USER')]] == client: if message[CONFIGS.get('TO_USER')] in self.names: self.database.process_message(message[CONFIGS.get( 'FROM_USER')], message[CONFIGS.get('TO_USER')]) self.process_message(message) try: send_message(client, RESPONSE_200, CONFIGS) except OSError: self.remove_client(client) else: response = RESPONSE_400 response[CONFIGS.get( 'ERROR')] = 'Пользователь не зарегистрирован на сервере.' try: send_message(client, response, CONFIGS) except OSError: pass return # если клиент выходит elif CONFIGS.get('ACTION') in message and \ message[CONFIGS.get('ACTION')] == CONFIGS.get('EXIT') and \ CONFIGS.get('ACCOUNT_NAME') in message and \ self.names[message[CONFIGS.get('ACCOUNT_NAME')]] == client: self.remove_client(client) # если это запрос контакт-листа elif CONFIGS.get('ACTION') in message and \ message[CONFIGS.get('ACTION')] == CONFIGS.get('GET_CONTACTS') \ and CONFIGS.get('USER') in message and \ self.names[message[CONFIGS.get('USER')]] == client: response = RESPONSE_202 response[CONFIGS.get('LIST_INFO')] = self.database.get_contacts( message[CONFIGS.get('USER')]) try: send_message(client, response, CONFIGS) except OSError: self.remove_client(client) # если это добавление контакта elif CONFIGS.get('ACTION') in message and \ message[CONFIGS.get('ACTION')] == CONFIGS.get('ADD_CONTACT') \ and CONFIGS.get("ACCOUNT_NAME") in message and \ CONFIGS.get('USER') in message and \ self.names[message[CONFIGS.get('USER')]] == client: self.database.add_contact(message[CONFIGS.get( 'USER')], message[CONFIGS.get("ACCOUNT_NAME")]) try: send_message(client, RESPONSE_200, CONFIGS) except OSError: self.remove_client(client) # если это удаление контакта elif CONFIGS.get('ACTION') in message and \ message[CONFIGS.get('ACTION')] == \ CONFIGS.get('REMOVE_CONTACT') and \ CONFIGS.get('ACCOUNT_NAME') in message and \ CONFIGS.get('USER') in message and \ self.names[message[CONFIGS.get('USER')]] == client: self.database.remove_contact(message[CONFIGS.get( 'USER')], message[CONFIGS.get('ACCOUNT_NAME')]) try: send_message(client, RESPONSE_200, CONFIGS) except OSError: self.remove_client(client) # если это запрос известных пользователей elif CONFIGS.get('ACTION') in message and \ message[CONFIGS.get('ACTION')] == CONFIGS.get('USERS_REQUEST')\ and CONFIGS.get('ACCOUNT_NAME') in message and \ self.names[message[CONFIGS.get('ACCOUNT_NAME')]] == client: response = RESPONSE_202 response[CONFIGS.get('LIST_INFO')] = \ [user[0] for user in self.database.users_list()] try: send_message(client, response, CONFIGS) except OSError: self.remove_client(client) # Если это запрос публичного ключа пользователя elif CONFIGS.get('ACTION') in message and \ message[CONFIGS.get('ACTION')] == \ CONFIGS.get('PUBLIC_KEY_REQUEST') and \ CONFIGS.get('ACCOUNT_NAME') in message: response = RESPONSE_511 response[CONFIGS.get('DATA')] = self.database.get_pubkey( message[CONFIGS.get('ACCOUNT_NAME')]) # может быть, что ключа ещё нет (пользователь никогда не логинился, # тогда шлём 400) if response[CONFIGS.get('DATA')]: try: send_message(client, response, CONFIGS) except OSError: self.remove_client(client) else: response = RESPONSE_400 response[CONFIGS.get('ERROR')] = \ 'Нет публичного ключа для данного пользователя' try: send_message(client, response, CONFIGS) except OSError: self.remove_client(client) # иначе отдаём Bad request else: response = RESPONSE_400 response[CONFIGS.get('ERROR')] = 'Запрос некорректен.' try: send_message(client, response, CONFIGS) except OSError: self.remove_client(client) def authorize_user(self, message, sock): """Метод реализующий авторизцию пользователей.""" # Если имя пользователя уже занято то возвращаем 400 server_logger.debug( f'Start auth process for {message[CONFIGS.get("USER")]}') if message[CONFIGS.get('USER')][CONFIGS.get( 'ACCOUNT_NAME')] in self.names.keys(): response = RESPONSE_400 response[CONFIGS.get('ERROR')] = 'Имя пользователя уже занято.' try: server_logger.debug(f'Username busy, sending {response}') send_message(sock, response, CONFIGS) except OSError: server_logger.debug('OS Error') pass self.clients.remove(sock) sock.close() # Проверяем что пользователь зарегистрирован на сервере. elif not self.database.check_user(message[CONFIGS.get('USER')] [CONFIGS.get('ACCOUNT_NAME')]): response = RESPONSE_400 response[CONFIGS.get('ERROR')] = 'Пользователь не зарегистрирован.' try: server_logger.debug(f'Unknown username, sending {response}') send_message(sock, response, CONFIGS) except OSError: pass self.clients.remove(sock) sock.close() else: server_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[CONFIGS.get('DATA')] = random_str.decode('ascii') # Создаём хэш пароля и связки с рандомной строкой, сохраняем # серверную версию ключа hash = hmac.new(self.database.get_hash(message[CONFIGS.get( 'USER')][CONFIGS.get('ACCOUNT_NAME')]), random_str, 'MD5') digest = hash.digest() server_logger.debug(f'Auth message = {message_auth}') try: # Обмен с клиентом send_message(sock, message_auth, CONFIGS) ans = get_message(sock, CONFIGS) except OSError as err: server_logger.debug('Error in auth, data:', exc_info=err) sock.close() return client_digest = binascii.a2b_base64(ans[CONFIGS.get('DATA')]) # Если ответ клиента корректный, то сохраняем его в список # пользователей. if CONFIGS.get('RESPONSE') in ans and \ ans[CONFIGS.get('RESPONSE')] == 511 and \ hmac.compare_digest(digest, client_digest): self.names[message[CONFIGS.get( 'USER')][CONFIGS.get('ACCOUNT_NAME')]] = sock client_ip, client_port = sock.getpeername() try: send_message(sock, RESPONSE_200, CONFIGS) except OSError: self.remove_client(message[CONFIGS.get( 'USER')][CONFIGS.get('ACCOUNT_NAME')]) # добавляем пользователя в список активных и если у него # изменился открытый ключ сохраняем новый self.database.user_login( message[CONFIGS.get('USER')][CONFIGS.get('ACCOUNT_NAME')], client_ip, client_port, message[CONFIGS.get('USER')][CONFIGS.get('PUBLIC_KEY')]) else: response = RESPONSE_400 response[CONFIGS.get('ERROR')] = 'Неверный пароль.' try: send_message(sock, response, CONFIGS) 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, CONFIGS) except OSError: self.remove_client(self.names[client])
class MessageProcessor(threading.Thread, metaclass=ServerMaker): port = Port() def __init__(self, listen_address, listen_port, database): # Параментры подключения self.addr = listen_address self.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, client_address = self.sock.accept() except OSError: pass else: logger.info(f'Установлено соедение с ПК {client_address}') client.settimeout(5) self.clients.append(client) recv_data_lst = [] send_data_lst = [] err_lst = [] # Проверяем на наличие ждущих клиентов try: if self.clients: recv_data_lst, self.listen_sockets, self.error_sockets = select.select( self.clients, self.clients, [], 0) except OSError as err: logger.error(f'Ошибка работы с сокетами: {err.errno}') # принимаем сообщения и если ошибка, исключаем клиента. if recv_data_lst: for client_with_message in recv_data_lst: try: self.process_client_message(get_message(client_with_message), client_with_message) except (OSError, json.JSONDecodeError, TypeError): self.remove_client(client_with_message) # Функция обработчик клиента с которым потеряна связь # ищет клиента в словаре клиентов и удаляет его со списков и базы: def remove_client(self, client): 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 init_socket(self): logger.info( f'Запущен сервер, порт для подключений: {self.port} , адрес с которого принимаются подключения: {self.addr}. Если адрес не указан, принимаются соединения с любых адресов.') # Готовим сокет transport = socket.socket(socket.AF_INET, socket.SOCK_STREAM) transport.bind((self.addr, self.port)) transport.settimeout(0.5) # Начинаем слушать сокет. self.sock = transport self.sock.listen(MAX_CONNECTIONS) # Функция адресной отправки сообщения определённому клиенту. Принимает словарь сообщение, список зарегистрированых # пользователей и слушающие сокеты. Ничего не возвращает. 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]} не зарегистрирован на сервере, отправка сообщения невозможна.') # Обработчик сообщений от клиентов, принимает словарь - сообщение от клиента, проверяет корректность, отправляет # словарь-ответ в случае необходимости. @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.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) # Функция авторизации пользователя на сервере def autorize_user(self, message, sock): # Если имя пользователя уже занято то возвращаем 400 if message[USER][ACCOUNT_NAME] in self.names.keys(): response = RESPONSE_400 response[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 response[ERROR] = 'Пользователь не зарегистрирован.' try: send_message(sock, response) except OSError: pass self.clients.remove(sock) sock.close() else: # Иначе отвечаем 501 и проводим процедуру авторизации # Словарь - заготовка 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() 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 response[ERROR] = 'Неверный пароль.' try: send_message(sock, response) except OSError: pass self.clients.remove(sock) sock.close() # Функция - отправляет сервисное сообщение 205 с требованием клиентам обновить списки def service_update_lists(self): for client in self.names: try: send_message(self.names[client], RESPONSE_205) except OSError: self.remove_client(self.names[client])
class MessageProcessor(threading.Thread, metaclass=ServerMaker): port = Port() def __init__(self, listen_address, listen_port, database): self.addr = listen_address self.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): """Basic function to run server.""" self.init_socket() while self.running: try: client, client_address = self.sock.accept() except OSError: pass else: logger.info(f'Applied connection from {client_address}') client.settimeout(5) self.clients.append(client) recv_data_lst = [] send_data_lst = [] err_lst = [] try: if self.clients: recv_data_lst, self.listen_sockets, self.error_sockets = select.select( self.clients, self.clients, [], 0) except OSError as err: logger.error(f'Socket Error: {err.errno}') if recv_data_lst: for client_with_message in recv_data_lst: try: self.process_client_message( get_message(client_with_message), client_with_message) except (OSError, json.JSONDecodeError, TypeError): self.remove_client(client_with_message) def remove_client(self, client): """Delete client from server.""" logger.info( f'Client {client.getpeername()} has been disconnected from server') 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 init_socket(self): """Create socket object.""" logger.info( f'Server is running {self.port}, connection address: {self.addr}.\nIf address is empty all connection applied' ) transport = socket.socket(socket.AF_INET, socket.SOCK_STREAM) transport.bind((self.addr, self.port)) transport.settimeout(0.5) self.sock = transport self.sock.listen(MAX_CONNECTIONS) def process_message(self, message): """Sending message function. Receive message fom user and tries to send it to another user. """ 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 has been send to {message[DESTINATION]} from user {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'Connection with client {message[DESTINATION]} has been lost. Connection closed.' ) self.remove_client(self.names[message[DESTINATION]]) else: logger.error( f"User {message[DESTINATION]} is not online or not registered, failed to send message" ) @login_required def process_client_message(self, message, client): """Client's message parser. Tries to receive message from client, parse it and creating an answer. """ logger.debug(f'Check user message : {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] = "'User is not online or not registered" 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]) if response[DATA]: try: send_message(client, response) except OSError: self.remove_client(client) else: response = RESPONSE_400 response[ERROR] = 'No public key for this user' try: send_message(client, response) except OSError: self.remove_client(client) else: response = RESPONSE_400 response[ERROR] = 'Incorrect request' try: send_message(client, response) except OSError: self.remove_client(client) def autorize_user(self, message, sock): """Authorize function. Receive a values from client. Send a result of authorizing. """ if message[USER][ACCOUNT_NAME] in self.names.keys(): response = RESPONSE_400 response[ERROR] = 'Username is already exists' 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 response[ERROR] = "User is not registered" try: send_message(sock, response) except OSError: pass self.clients.remove(sock) sock.close() else: message_auth = RESPONSE_511 random_str = binascii.hexlify(os.urandom(64)) 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 response[ERROR] = 'Incorrect password' try: send_message(sock, response) except OSError: pass self.clients.remove(sock) sock.close() def service_update_lists(self): """Updating clients list function.""" for client in self.names: try: send_message(self.names[client], RESPONSE_205) except OSError: self.remove_client(self.names[client])