def load_settings(self) -> None: """Load settings from the encrypted database.""" with open(self.file_name, 'rb') as f: ct_bytes = f.read() pt_bytes = auth_and_decrypt(ct_bytes, self.master_key.master_key, database=self.file_name) # Update settings based on plaintext byte string content for key in self.key_list: attribute = self.__getattribute__(key) if isinstance(attribute, bool): value = bytes_to_bool( pt_bytes[0]) # type: Union[bool, int, float] pt_bytes = pt_bytes[ENCODED_BOOLEAN_LENGTH:] elif isinstance(attribute, int): value = bytes_to_int(pt_bytes[:ENCODED_INTEGER_LENGTH]) pt_bytes = pt_bytes[ENCODED_INTEGER_LENGTH:] elif isinstance(attribute, float): value = bytes_to_double(pt_bytes[:ENCODED_FLOAT_LENGTH]) pt_bytes = pt_bytes[ENCODED_FLOAT_LENGTH:] else: raise CriticalError( "Invalid data type in settings default values.") setattr(self, key, value)
def load_settings(self) -> None: """Load settings from the encrypted database.""" pt_bytes = self.database.load_database() # Update settings based on plaintext byte string content for key in self.key_list: attribute = self.__getattribute__(key) if isinstance(attribute, bool): value = bytes_to_bool(pt_bytes[0]) # type: Union[bool, int, float] pt_bytes = pt_bytes[ENCODED_BOOLEAN_LENGTH:] elif isinstance(attribute, int): value = bytes_to_int(pt_bytes[:ENCODED_INTEGER_LENGTH]) pt_bytes = pt_bytes[ENCODED_INTEGER_LENGTH:] elif isinstance(attribute, float): value = bytes_to_double(pt_bytes[:ENCODED_FLOAT_LENGTH]) pt_bytes = pt_bytes[ENCODED_FLOAT_LENGTH:] else: raise CriticalError("Invalid data type in settings default values.") setattr(self, key, value)
def load_settings(self) -> None: """Load settings from encrypted database.""" ensure_dir(f'{DIR_USER_DATA}/') with open(self.file_name, 'rb') as f: ct_bytes = f.read() pt_bytes = auth_and_decrypt(ct_bytes, self.master_key.master_key) # Update settings based on plaintext byte string content for i, key in enumerate(self.key_list): attribute = self.__getattribute__(key) if isinstance(attribute, bool): value = bytes_to_bool( pt_bytes[0]) # type: Union[bool, int, float, str] pt_bytes = pt_bytes[1:] elif isinstance(attribute, int): value = bytes_to_int(pt_bytes[:8]) pt_bytes = pt_bytes[8:] elif isinstance(attribute, float): value = bytes_to_double(pt_bytes[:8]) pt_bytes = pt_bytes[8:] elif isinstance(attribute, str): value = bytes_to_str(pt_bytes[:1024]) pt_bytes = pt_bytes[ 1024:] # 255 * 4 = 1020. The four additional bytes is the UTF-32 BOM. else: raise CriticalError( "Invalid data type in settings default values.") setattr(self, key, value)
def test_bytes_to_double(self): self.assertEqual(bytes_to_double(bytes.fromhex('000000000000f03f')), 1.0) self.assertEqual(bytes_to_double(bytes.fromhex('9a9999999999f13f')), 1.1)
def test_bytes_to_double(self): self.assertEqual( bytes_to_double(binascii.unhexlify('000000000000f03f')), 1.0) self.assertEqual( bytes_to_double(binascii.unhexlify('9a9999999999f13f')), 1.1)
def process_message(ts: 'datetime', assembly_packet_ct: bytes, window_list: 'WindowList', packet_list: 'PacketList', contact_list: 'ContactList', key_list: 'KeyList', group_list: 'GroupList', settings: 'Settings', master_key: 'MasterKey') -> None: """Process received private / group message. Group management messages have automatic formatting and window redirection based on group configuration managed by user. """ assembly_packet, account, origin = decrypt_assembly_packet(assembly_packet_ct, window_list, contact_list, key_list) p_type = 'file' if assembly_packet[:1].isupper() else 'message' packet = packet_list.get_packet(account, origin, p_type) packet.add_packet(assembly_packet) if not packet.is_complete: return None if p_type == 'file': packet.assemble_and_store_file() if contact_list.get_contact(account).log_messages and settings.log_dummy_file_a_p: # Store placeholder data. for _ in packet.assembly_pt_list: place_holder = F_S_HEADER + bytes(255) write_log_entry(place_holder, account, settings, master_key, origin) if p_type == 'message': assembled = packet.assemble_message_packet() header = assembled[:1] assembled = assembled[1:] # Messages to group if header == GROUP_MESSAGE_HEADER: try: timestamp = bytes_to_double(assembled[:8]) except struct.error: raise FunctionReturn("Received an invalid group timestamp.") try: group_name = assembled[8:].split(US_BYTE)[0].decode() except (UnicodeError, IndexError): raise FunctionReturn("Group name had invalid encoding.") try: group_message = assembled[8:].split(US_BYTE)[1] except (ValueError, IndexError): raise FunctionReturn("Received an invalid group message.") if not group_list.has_group(group_name): raise FunctionReturn("Received message to unknown group.", output=False) window = window_list.get_window(group_name) group = group_list.get_group(group_name) if not group.has_member(account): raise FunctionReturn("Group message to group contact is not member of.", output=False) if window.has_contact(account): # All copies of group messages user sends to members contain same timestamp header. # This allows RxM to ignore copies of messages sent by the user. if origin == ORIGIN_USER_HEADER: if window.group_timestamp == timestamp: return None window.group_timestamp = timestamp window.print_new(ts, group_message.decode(), account, origin) if group_list.get_group(group_name).log_messages: for p in packet.assembly_pt_list: write_log_entry(p, account, settings, master_key, origin) return None # Messages to contact else: if header == PRIVATE_MESSAGE_HEADER: window = window_list.get_window(account) window.print_new(ts, assembled.decode(), account, origin) # Group management messages else: local_win = window_list.get_local_window() nick = contact_list.get_contact(account).nick group_name, *members = [f.decode() for f in assembled.split(US_BYTE)] # Ignore group management messages from user if origin == ORIGIN_USER_HEADER: return None if header == GROUP_MSG_INVITATION_HEADER: action = 'invited you to' if group_list.has_group(group_name) and group_list.get_group(group_name).has_member(account): action = 'joined' message = ["{} has {} group '{}'".format(nick, action, group_name)] # type: List[str] lw_msg = "{} has {} group '{}'".format(nick, action, group_name) # type: str # Print group management message if members: message[0] += " with following members:" known = [contact_list.get_contact(m).nick for m in members if contact_list.has_contact(m)] unknown = [m for m in members if not contact_list.has_contact(m)] just_len = len(max(known + unknown, key=len)) message += [" * {}".format(m.ljust(just_len)) for m in (known + unknown)] lw_msg += " with members " + ", ".join(known + unknown) box_print(message, head=1, tail=1) # Persistent message in cmd window local_win.print_new(ts, lw_msg, print_=False) elif header in [GROUP_MSG_ADD_NOTIFY_HEADER, GROUP_MSG_MEMBER_RM_HEADER]: action = "added following member(s) to" if header == GROUP_MSG_ADD_NOTIFY_HEADER else "removed following member(s) from" message_ = ["{} has {} group {}: ".format(nick, action, group_name)] # type: List[str] lw_msg_ = "{} has {} group {}: ".format(nick, action, group_name) # type: str if members: known = [contact_list.get_contact(m).nick for m in members if contact_list.has_contact(m)] unknown = [m for m in members if not contact_list.has_contact(m)] just_len = len(max(known + unknown, key=len)) lw_msg_ += ", ".join(known + unknown) message_ += [" * {}".format(m.ljust(just_len)) for m in (known + unknown)] box_print(message_, head=1, tail=1) local_win.print_new(ts, lw_msg_, print_=False) elif header == GROUP_MSG_EXIT_GROUP_HEADER: box_print(["{} has left group {}.".format(nick, group_name), '', 'Warning', "Unless you remove the contact from the group, they", "can still decrypt messages you send to the group."], head=1, tail=1) else: raise FunctionReturn(f"Message from had invalid header.") if contact_list.get_contact(account).log_messages: for p in packet.assembly_pt_list: write_log_entry(p, account, settings, master_key, origin)