Ejemplo n.º 1
0
    def process_server_ans(self, message):
        """
		Method to handle incoming messages from the server
		:param message:
		:return:
		"""
        client_log.debug(f'Разбор сообщения от сервера: {message}')

        # Если это подтверждение чего-либо
        if RESPONSE in message:
            if message[RESPONSE] == 200 or 202:
                return
            elif message[RESPONSE] == 400:
                raise ServerError(f'{message[ERROR]}')
            elif message[RESPONSE] == 205:
                self.user_list_update()
                self.contacts_list_update()
                self.message_205.emit()
            else:
                client_log.error(
                    f'Принят неизвестный код подтверждения {message[RESPONSE]}'
                )

        # Если это сообщение от пользователя добавляем в базу, даём сигнал о
        # новом сообщении
        elif ACTION in message and message[ACTION] == MESSAGE and SENDER in message and DESTINATION in message \
          and MESSAGE_TEXT in message and message[DESTINATION] == self.username:
            client_log.debug(
                f'Получено сообщение от пользователя {message[SENDER]}:{message[MESSAGE_TEXT]}'
            )
            self.new_message.emit(message)
Ejemplo n.º 2
0
	def set_active_user(self):
		"""
		A method for activating a chat with an interlocutor.
		:return:
		"""
		# Запрашиваем публичный ключ пользователя и создаём объект шифрования
		try:
			self.current_chat_key = self.transport.key_request(
				self.current_chat)
			client_log.debug(f'Загружен открытый ключ для {self.current_chat}')
			if self.current_chat_key:
				self.encryptor = PKCS1_OAEP.new(
					RSA.import_key(self.current_chat_key))
		except (OSError, json.JSONDecodeError):
			self.current_chat_key = None
			self.encryptor = None
			client_log.debug(f'Не удалось получить ключ для {self.current_chat}')
		
		# Если ключа нет то ошибка, что не удалось начать чат с пользователем
		if not self.current_chat_key:
			self.messages.warning(
				self, 'Ошибка', 'Для выбранного пользователя нет ключа шифрования.')
			return
		
		# Ставим надпись и активируем кнопки
		self.ui.label_new_message.setText(
			f'Введите сообщенние для {self.current_chat}:')
		self.ui.btn_clear.setDisabled(False)
		self.ui.btn_send.setDisabled(False)
		self.ui.text_message.setDisabled(False)
		
		# Заполняем окно историю сообщений по требуемому пользователю.
		self.history_list_update()
	def update_possible_contacts(self):
		"""
		Method for updating the list of possible contacts. Requests from the server
		list of known users and blots the contents of the window.
		:return:
		"""
		try:
			self.transport.user_list_update()
		except OSError:
			pass
		else:
			client_log.debug('Обновление списка пользователей с сервера выполнено')
			self.possible_contacts_update()
Ejemplo n.º 4
0
    def remove_contact(self, contact):
        """
		Method that sends information about deleting a contact to the server
		:param contact:
		:return:
		"""
        client_log.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_message(self.transport))
Ejemplo n.º 5
0
    def add_contact(self, contact):
        """
		A method that sends information about adding a contact to the server.
		:param contact:
		:return:
		"""
        client_log.debug(f'Создание контакта {contact}')
        req = {
            ACTION: ADD_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))
Ejemplo n.º 6
0
    def transport_shutdown(self):
        """
		Method notifying the server when the client is finished
		:return:
		"""
        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
        client_log.debug('Транспорт завершает работу.')
        time.sleep(0.5)
Ejemplo n.º 7
0
    def key_request(self, user):
        """
		Method that requests the public key of the user from the server
		:param user:
		:return:
		"""
        client_log.debug(f'Запрос публичного ключа для {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:
            client_log.error(f'Не удалось получить ключ собеседника{user}.')
Ejemplo n.º 8
0
    def send_message(self, to, message):
        """
		Method sending messages to the server for the user
		:param to:
		:param message:
		:return:
		"""
        message_dict = {
            ACTION: MESSAGE,
            SENDER: self.username,
            DESTINATION: to,
            TIME: time.time(),
            MESSAGE_TEXT: message
        }
        client_log.debug(f'Сформирован словарь сообщения: {message_dict}')
        # Необходимо дождаться освобождения сокета для отправки сообщения
        with socket_lock:
            send_message(self.transport, message_dict)
            self.process_server_ans(get_message(self.transport))
            client_log.info(f'Отправлено сообщение для пользователя {to}')
Ejemplo n.º 9
0
    def user_list_update(self):
        """
		Method that updates the list of users from the server.
		:return:
		"""
        client_log.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:
            client_log.error(
                'Не удалось обновить список известных пользователей.')
Ejemplo n.º 10
0
    def run(self):
        """
		Method containing the main cycle of the transport stream
		:return:
		"""
        client_log.debug('Запущен процесс - приёмник собщений с сервера.')
        while self.running:
            # Отдыхаем секунду и снова пробуем захватить сокет.
            # если не сделать тут задержку, то отправка может достаточно долго
            # ждать освобождения сокета.
            time.sleep(1)
            message = None
            with socket_lock:
                try:
                    self.transport.settimeout(0.5)
                    message = get_message(self.transport)
                except OSError as err:
                    if err.errno:
                        client_log.critical(f'Потеряно соединение с сервером.')
                        self.running = False
                        self.connection_lost.emit()
                # Проблемы с соединением
                except (ConnectionError, ConnectionAbortedError,
                        ConnectionResetError, json.JSONDecodeError, TypeError):
                    client_log.debug(f'Потеряно соединение с сервером.')
                    self.running = False
                    self.connection_lost.emit()
                finally:
                    self.transport.settimeout(5)

            # Если сообщение получено, то вызываем функцию обработчик:
            if message:
                client_log.debug(f'Принято сообщение с сервера: {message}')
                self.process_server_ans(message)
Ejemplo n.º 11
0
	def send_message(self):
		"""
		The function of sending a message to the current interlocutor.
		Implements message encryption and sending.
		"""
		# Текст в поле, проверяем что поле не пустое затем забирается сообщение
		# и поле очищается
		message_text = self.ui.text_message.toPlainText()
		self.ui.text_message.clear()
		if not message_text:
			return
		# Шифруем сообщение ключом получателя и упаковываем в base64.
		message_text_encrypted = self.encryptor.encrypt(
			message_text.encode('utf8'))
		message_text_encrypted_base64 = base64.b64encode(
			message_text_encrypted)
		try:
			self.transport.send_message(
				self.current_chat,
				message_text_encrypted_base64.decode('ascii'))
			pass
		except ServerError as err:
			self.messages.critical(self, 'Ошибка', err.text)
		except OSError as err:
			if err.errno:
				self.messages.critical(
					self, 'Ошибка', 'Потеряно соединение с сервером!')
				self.close()
			self.messages.critical(self, 'Ошибка', 'Таймаут соединения!')
		except (ConnectionResetError, ConnectionAbortedError):
			self.messages.critical(
				self, 'Ошибка', 'Потеряно соединение с сервером!')
			self.close()
		else:
			self.database.save_message(self.current_chat, 'out', message_text)
			client_log.debug(
				f'Отправлено сообщение для {self.current_chat}: {message_text}')
			self.history_list_update()
Ejemplo n.º 12
0
    def contacts_list_update(self):
        """
		Method updating the list of contacts from the server.
		:return:
		"""
        self.database.contacts_clear()
        client_log.debug(f'Запрос контакт листа для пользователся {self.name}')
        req = {ACTION: GET_CONTACTS, TIME: time.time(), USER: self.username}
        client_log.debug(f'Сформирован запрос {req}')
        with socket_lock:
            send_message(self.transport, req)
            ans = get_message(self.transport)
        client_log.debug(f'Получен ответ {ans}')
        if RESPONSE in ans and ans[RESPONSE] == 202:
            for contact in ans[LIST_INFO]:
                self.database.add_contact(contact)
        else:
            client_log.error('Не удалось обновить список контактов.')
Ejemplo n.º 13
0
    def connection_init(self, port, ip):
        """
		Method responsible for establishing a connection to the server
		:param port:
		:param ip:
		:return:
		"""
        # Инициализация сокета и сообщение серверу о нашем появлении
        self.transport = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

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

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

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

        client_log.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)

        client_log.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
                }
            }
            client_log.debug(f"Presense message = {presense}")
            # Отправляем серверу приветственное сообщение.
            try:
                send_message(self.transport, presense)
                ans = get_message(self.transport)
                client_log.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:
                client_log.debug(f'Connection error.', exc_info=err)
                raise ServerError('Сбой соединения в процессе авторизации.')
Ejemplo n.º 14
0
	parser.add_argument('-n', '--name', default=None, nargs='?')
	parser.add_argument('-p', '--password', default='', nargs='?')
	namespace = parser.parse_args(sys.argv[1:])
	server_address = namespace.addr
	server_port = namespace.port
	client_name = namespace.name
	client_passwd = namespace.password
	
	return server_address, server_port, client_name, client_passwd


# Основная функция клиента
if __name__ == '__main__':
	# Загружаем параметы коммандной строки
	server_address, server_port, client_name, client_passwd = arg_parser()
	client_log.debug('Args loaded')
	
	# Создаём клиентское приложение
	client_app = QApplication(sys.argv)
	
	# Если имя пользователя не было указано в командной строке то запросим его
	start_dialog = UserNameDialog()
	if not client_name or not client_passwd:
		client_app.exec_()
		# Если пользователь ввёл имя и нажал ОК, то сохраняем введённое и
		# удаляем объект, иначе выходим
		if start_dialog.ok_pressed:
			client_name = start_dialog.client_name.text()
			client_passwd = start_dialog.client_passwd.text()
			client_log.debug(f'Using USERNAME = {client_name}, PASSWD = {client_passwd}.')
		else: