Esempio n. 1
0
def process_group_command(user_input: 'UserInput', contact_list: 'ContactList',
                          group_list: 'GroupList', settings: 'Settings',
                          queues: Dict[bytes, 'Queue']) -> None:
    """Parse group command and process it accordingly."""
    if settings.session_trickle:
        raise FunctionReturn("Command disabled during trickle connection.")

    params = user_input.plaintext

    try:
        command_type = params.split()[1]
    except IndexError:
        raise FunctionReturn("Invalid group command.")

    if command_type not in ['create', 'add', 'rm']:
        raise FunctionReturn("Invalid group command.")

    try:
        group_name = params.split()[2]
    except IndexError:
        raise FunctionReturn("No group name specified.")

    purp_members = params.split()[3:]

    # Swap specified nicks to rx_accounts
    for i, m in enumerate(purp_members):
        if m in contact_list.get_list_of_nicks():
            purp_members[i] = contact_list.get_contact(m).rx_account

    func = dict(create=group_create, add=group_add_member,
                rm=group_rm_member)[command_type]

    func(group_name, purp_members, group_list, contact_list, settings, queues)
Esempio n. 2
0
def export_logs(user_input:   'UserInput',
                window:       'Window',
                contact_list: 'ContactList',
                settings:     'Settings',
                c_queue:      'Queue',
                master_key:   'MasterKey') -> None:
    """Export log files to plaintext file on TxM/RxM.

    TxM only exports sent messages, RxM exports full conversation.
    """
    try:
        no_messages_str = user_input.plaintext.split()[1]
        if not no_messages_str.isdigit():
            raise FunctionReturn("Specified invalid number of messages to export.")
        no_messages = int(no_messages_str)
    except IndexError:
        no_messages = 0

    if not yes(f"Export logs for {window.name} in plaintext?", head=1, tail=1):
        raise FunctionReturn("Logfile export aborted.")

    packet = LOG_EXPORT_HEADER + window.uid.encode() + US_BYTE + int_to_bytes(no_messages)
    queue_command(packet, settings, c_queue)

    access_history(window, contact_list, settings, master_key, no_messages, export=True)
Esempio n. 3
0
def change_nick(user_input: 'UserInput', window: 'Window',
                contact_list: 'ContactList', group_list: 'GroupList',
                settings: 'Settings', c_queue: 'Queue') -> None:
    """Change nick of contact."""
    if window.type == 'group':
        raise FunctionReturn("Error: Group is selected.")

    try:
        nick = user_input.plaintext.split()[1]
    except IndexError:
        raise FunctionReturn("Error: No nick specified.")

    rx_acco = window.contact.rx_account
    success, error_msg = validate_nick(nick,
                                       (contact_list, group_list, rx_acco))
    if not success:
        raise FunctionReturn(error_msg)
    window.contact.nick = nick
    window.name = nick
    contact_list.store_contacts()

    packet = CHANGE_NICK_HEADER + rx_acco.encode() + US_BYTE + nick.encode()
    queue_command(packet, settings, c_queue)

    box_print(f"Changed {rx_acco} nick to {nick}.")
Esempio n. 4
0
def process_local_key(packet: bytes, contact_list: 'ContactList',
                      key_list: 'KeyList') -> None:
    """Decrypt local key packet, add local contact/keyset."""
    try:
        clear_screen()
        box_print(["Received encrypted local key"], tail=1)

        kdk = get_b58_key('localkey')

        try:
            pt = auth_and_decrypt(packet[1:], key=kdk, soft_e=True)
        except nacl.exceptions.CryptoError:
            raise FunctionReturn("Invalid key decryption key.", delay=1.5)

        key = pt[0:32]
        hek = pt[32:64]
        conf_code = pt[64:65]

        # Add local contact to contact list database
        contact_list.add_contact('local', 'local', 'local', bytes(32),
                                 bytes(32), False, False, True)

        # Add local contact to keyset database
        key_list.add_keyset('local', key, bytes(32), hek, bytes(32))
        box_print([f"Confirmation code for TxM: {conf_code.hex()}"], head=1)

    except KeyboardInterrupt:
        raise FunctionReturn("Local key setup aborted.", delay=1)
Esempio n. 5
0
def new_psk(account: str, user: str, nick: str, contact_list: 'ContactList',
            settings: 'Settings', queues: Dict[bytes, 'Queue']) -> None:
    """Generate new pre-shared key for manual key delivery.

    :param account:      The contact's account name (e.g. [email protected])
    :param user:         The user's account name (e.g. [email protected])
    :param nick:         Nick of contact
    :param contact_list: Contact list object
    :param settings:     Settings object
    :param queues:       Dictionary of multiprocessing queues
    :return:             None
    """
    try:
        tx_key = keygen()
        tx_hek = keygen()
        salt = keygen()
        password = MasterKey.new_password("password for PSK")

        phase("Deriving key encryption key", head=2)
        kek, _ = argon2_kdf(password,
                            salt,
                            rounds=16,
                            memory=128000,
                            parallelism=1)
        phase('Done')

        ct_tag = encrypt_and_sign(tx_key + tx_hek, key=kek)
        store_d = ask_path_gui(f"Select removable media for {nick}", settings)
        f_name = f"{store_d}/{user}.psk - Give to {account}"

        try:
            with open(f_name, 'wb+') as f:
                f.write(salt + ct_tag)
        except PermissionError:
            raise FunctionReturn(
                "Error: Did not have permission to write to directory.")

        packet = KEY_EX_PSK_TX_HEADER \
                 + tx_key \
                 + tx_hek \
                 + account.encode() + US_BYTE +  nick.encode()

        queue_command(packet, settings, queues[COMMAND_PACKET_QUEUE])

        contact_list.add_contact(account, user, nick, bytes(32), bytes(32),
                                 settings.log_msg_by_default,
                                 settings.store_file_default,
                                 settings.n_m_notify_privacy)

        queues[KEY_MANAGEMENT_QUEUE].put(
            ('ADD', account, tx_key, bytes(32), tx_hek, bytes(32)))

        box_print([f"Successfully added {nick}."], head=1)
        clear_screen(delay=1)

    except KeyboardInterrupt:
        raise FunctionReturn("PSK generation aborted.")
Esempio n. 6
0
def contact_setting(user_input: 'UserInput', window: 'Window',
                    contact_list: 'ContactList', group_list: 'GroupList',
                    settings: 'Settings', c_queue: 'Queue') -> None:
    """Change logging, file reception, or message notification setting of (all) contact(s)."""
    try:
        parameters = user_input.plaintext.split()
        cmd_key = parameters[0]
        cmd_header = dict(logging=CHANGE_LOGGING_HEADER,
                          store=CHANGE_FILE_R_HEADER,
                          notify=CHANGE_NOTIFY_HEADER)[cmd_key]

        s_value = dict(on=b'e', off=b'd')[parameters[1]]
        b_value = dict(on=True, off=False)[parameters[1]]

    except (IndexError, KeyError):
        raise FunctionReturn("Error: Invalid command.")

    # If second parameter 'all' is included, apply setting for all contacts and groups
    try:
        target = b''
        if parameters[2] == 'all':
            cmd_value = s_value.upper()
        else:
            raise FunctionReturn("Error: Invalid command.")
    except IndexError:
        target = window.uid.encode()
        cmd_value = s_value + US_BYTE + target

    if target:
        if window.type == 'contact':
            if cmd_key == 'logging': window.contact.log_messages = b_value
            if cmd_key == 'store': window.contact.file_reception = b_value
            if cmd_key == 'notify': window.contact.notifications = b_value
            contact_list.store_contacts()

        if window.type == 'group':
            if cmd_key == 'logging': window.group.log_messages = b_value
            if cmd_key == 'store':
                for c in window:
                    c.file_reception = b_value
            if cmd_key == 'notify': window.group.notifications = b_value
            group_list.store_groups()

    else:
        for contact in contact_list:
            if cmd_key == 'logging': contact.log_messages = b_value
            if cmd_key == 'store': contact.file_reception = b_value
            if cmd_key == 'notify': contact.notifications = b_value
        contact_list.store_contacts()

        for group in group_list:
            if cmd_key == 'logging': group.log_messages = b_value
            if cmd_key == 'notify': group.notifications = b_value
        group_list.store_groups()

    packet = cmd_header + cmd_value
    queue_command(packet, settings, c_queue)
Esempio n. 7
0
    def select_tx_window(self,
                         settings: 'Settings',
                         queues: Dict[bytes, 'Queue'],
                         selection: str = None,
                         cmd: bool = False) -> None:
        """Select specified window or ask the user to specify one."""
        if selection is None:
            self.contact_list.print_contacts()
            self.group_list.print_groups()
            selection = input("Select recipient: ").strip()

        if selection in self.group_list.get_list_of_group_names():
            if cmd and settings.session_trickle and selection != self.uid:
                raise FunctionReturn(
                    "Can't change window during trickle connection.")

            self.group = self.group_list.get_group(selection)
            self.window_contacts = self.group.members
            self.name = self.group.name
            self.uid = self.name
            self.type = 'group'

            if self.window_contacts:
                self.imc_name = self.window_contacts[0].rx_account

        elif selection in self.contact_list.contact_selectors():

            if cmd and settings.session_trickle:
                contact = self.contact_list.get_contact(selection)
                if self.uid != contact.rx_account:
                    raise FunctionReturn(
                        "Can't change window during trickle connection.")

            self.contact = self.contact_list.get_contact(selection)
            self.window_contacts = [self.contact]
            self.name = self.contact.nick
            self.uid = self.contact.rx_account
            self.imc_name = self.contact.rx_account
            self.type = 'contact'

        else:
            raise FunctionReturn("Error: No contact/group was found.")

        if settings.session_trickle and not cmd:
            queues[WINDOW_SELECT_QUEUE].put(self.window_contacts)

        packet = WINDOW_CHANGE_HEADER + self.uid.encode()
        queue_command(packet, settings, queues[COMMAND_PACKET_QUEUE])

        clear_screen()
Esempio n. 8
0
def rxm_load_psk(window: 'Window', contact_list: 'ContactList',
                 settings: 'Settings', c_queue: 'Queue') -> None:
    """Load PSK for selected contact on RxM."""
    if settings.session_trickle:
        raise FunctionReturn("Command disabled during trickle connection.")

    if window.type == 'group':
        raise FunctionReturn("Group is selected.")

    if contact_list.get_contact(window.uid).tx_fingerprint != bytes(32):
        raise FunctionReturn("Current key was exchanged with X25519.")

    packet = KEY_EX_PSK_RX_HEADER + window.uid.encode()
    queue_command(packet, settings, c_queue)
Esempio n. 9
0
def re_encrypt(previous_key: bytes, new_key: bytes, settings: 'Settings') -> None:
    """Re-encrypt database with a new master key."""
    ensure_dir(f'{DIR_USER_DATA}/')
    file_name = f'{DIR_USER_DATA}/{settings.software_operation}_logs'
    temp_name = f'{DIR_USER_DATA}/{settings.software_operation}_logs_temp'

    if not os.path.isfile(file_name):
        raise FunctionReturn(f"Error: Could not find '{file_name}'.")

    if os.path.isfile(temp_name):
        os.remove(temp_name)

    f_old = open(file_name, 'rb')
    f_new = open(temp_name, 'ab+')

    def read_entry():
        """Read log entry."""
        return f_old.read(1325)

    for ct_old in iter(read_entry, b''):
        pt_new = auth_and_decrypt(ct_old, key=previous_key)
        f_new.write(encrypt_and_sign(pt_new, key=new_key))

    f_old.close()
    f_new.close()

    os.remove(file_name)
    os.rename(temp_name, file_name)
Esempio n. 10
0
def fingerprints(window: 'Window') -> None:
    """Print domain separated fingerprints of shared secret on TxM."""
    if window.type == 'group':
        raise FunctionReturn('Group is selected.')

    if window.contact.tx_fingerprint == bytes(32):
        raise FunctionReturn(
            f"Key have been pre-shared with {window.name} and thus have no fingerprints."
        )

    clear_screen()
    print_fingerprints(window.contact.tx_fingerprint,
                       "   Your fingerprint (you read)   ")
    print_fingerprints(window.contact.rx_fingerprint,
                       "Contact's fingerprint (they read)")
    print('')
Esempio n. 11
0
def contact_setting(cmd_data: bytes, ts: 'datetime', window_list: 'WindowList',
                    contact_list: 'ContactList', group_list: 'GroupList',
                    setting_type: str) -> None:
    """Change contact/group related setting."""
    attr = dict(L='log_messages', F='file_reception',
                N='notifications')[setting_type]

    desc = dict(L='logging of messages',
                F='reception of files',
                N='message notifications')[setting_type]

    if cmd_data[:1].islower():

        setting, win_uid, = [f.decode() for f in cmd_data.split(US_BYTE)]

        if not window_list.has_window(win_uid):
            raise FunctionReturn(f"Error: Found no window for {win_uid}.")

        b_value, header = dict(e=(True, "Enabled"),
                               d=(False, "Disabled"))[setting]
        window = window_list.get_window(win_uid)
        trailer = f"for {window.type} {window.name}"

        if window.type == 'group' and setting_type == 'F':
            trailer = f"for members in group {window.name}"

        if window.type == 'group':
            group = group_list.get_group(win_uid)
            if setting_type == 'F':
                for c in contact_list:
                    c.file_reception = b_value
                contact_list.store_contacts()
            else:
                setattr(group, attr, b_value)
                group_list.store_groups()

        elif window.type == 'contact':
            contact = contact_list.get_contact(win_uid)
            setattr(contact, attr, b_value)
            contact_list.store_contacts()

    # For all
    else:
        setting = cmd_data[:1].decode()
        b_value, header = dict(E=(True, "Enabled"),
                               D=(False, "Disabled"))[setting]
        trailer = "for all contacts" + (' and groups'
                                        if setting_type != 'F' else '')

        for c in contact_list:
            setattr(c, attr, b_value)
        contact_list.store_contacts()

        if setting_type != 'F':
            for g in group_list:
                setattr(g, attr, b_value)
            group_list.store_groups()

    local_win = window_list.get_window('local')
    local_win.print_new(ts, f"{header} {desc} {trailer}.")
Esempio n. 12
0
File: files.py Progetto: barleyj/tfc
 def get_file_size(self) -> None:
     """Get size of file."""
     size_bytes = os.path.getsize(self.path)
     if size_bytes == 0:
         raise FunctionReturn(
             "Error: Target file is empty. No file was sent.")
     self.size = File.readable_size(size_bytes)
Esempio n. 13
0
File: files.py Progetto: barleyj/tfc
 def header_length_check(self) -> None:
     """Ensure that file header fits the first packet."""
     header = US_BYTE.join(
         [self.name,
          bytearray(8), self.size, self.time_l, US_BYTE])
     if len(header) > 254:
         raise FunctionReturn(
             "Error: File name is too long. No file was sent.")
Esempio n. 14
0
def change_master_key(user_input:   'UserInput',
                      contact_list: 'ContactList',
                      group_list:   'GroupList',
                      settings:     'Settings',
                      queues:       Dict[bytes, 'Queue'],
                      master_key:   'MasterKey') -> None:
    """Change master key on TxM/RxM."""
    try:
        if settings.session_trickle:
            raise FunctionReturn("Command disabled during trickle connection.")

        try:
            device = user_input.plaintext.split()[1]
        except IndexError:
            raise FunctionReturn("No target system specified.")

        if device.lower() not in ['tx', 'txm', 'rx', 'rxm']:
            raise FunctionReturn("Invalid target system.")

        if device.lower() in ['rx', 'rxm']:
            queue_command(CHANGE_MASTER_K_HEADER, settings, queues[COMMAND_PACKET_QUEUE])
            print('')
            return None

        old_master_key = master_key.master_key[:]
        master_key.new_master_key()
        new_master_key = master_key.master_key

        ensure_dir(f'{DIR_USER_DATA}/')
        file_name = f'{DIR_USER_DATA}/{settings.software_operation}_logs'
        if os.path.isfile(file_name):
            phase("Re-encrypting log-file")
            re_encrypt(old_master_key, new_master_key, settings)
            phase("Done")

        queues[KEY_MANAGEMENT_QUEUE].put(('KEY', master_key))

        settings.store_settings()
        contact_list.store_contacts()
        group_list.store_groups()

        box_print("Master key successfully changed.", head=1)
        clear_screen(delay=1.5)
    except KeyboardInterrupt:
        raise FunctionReturn("Password change aborted.")
Esempio n. 15
0
def remove_contact(user_input: 'UserInput', window: 'Window',
                   contact_list: 'ContactList', group_list: 'GroupList',
                   settings: 'Settings', queues: Dict[bytes, 'Queue']) -> None:
    """Remove contact on TxM/RxM."""
    if settings.session_trickle:
        raise FunctionReturn("Command disabled during trickle connection.")

    try:
        selection = user_input.plaintext.split()[1]
    except IndexError:
        raise FunctionReturn("Error: No account specified.")

    if not yes(f"Remove {selection} completely?", head=1):
        raise FunctionReturn("Removal of contact aborted.")

    # Load account if user enters nick
    if selection in contact_list.get_list_of_nicks():
        selection = contact_list.get_contact(selection).rx_account

    packet = CONTACT_REMOVE_HEADER + selection.encode()
    queue_command(packet, settings, queues[COMMAND_PACKET_QUEUE])

    if selection in contact_list.get_list_of_accounts():
        queues[KEY_MANAGEMENT_QUEUE].put(('REM', selection))
        contact_list.remove_contact(selection)
        box_print(f"Removed {selection} from contacts.", head=1, tail=1)
    else:
        box_print(f"TxM has no {selection} to remove.", head=1, tail=1)

    if any([g.remove_members([selection]) for g in group_list]):
        box_print(f"Removed {selection} from group(s).", tail=1)

    for c in window:
        if selection == c.rx_account:
            if window.type == 'contact':
                window.deselect()
            elif window.type == 'group':
                window.update_group_win_members(group_list)

                # If last member from group is removed, deselect group.
                # This is not done in update_group_win_members because
                # It would prevent selecting the empty group for group
                # related commands such as notifications.
                if not window.window_contacts:
                    window.deselect()
Esempio n. 16
0
def select_window(user_input: 'UserInput', window: 'Window',
                  settings: 'Settings', queues: Dict[bytes, 'Queue']) -> None:
    """Select new window for messages."""
    try:
        selection = user_input.plaintext.split()[1]
    except (IndexError, TypeError):
        raise FunctionReturn("Invalid recipient.")

    window.select_tx_window(settings, queues, selection, cmd=True)
Esempio n. 17
0
def export_file(settings: 'Settings', gateway: 'Gateway'):
    """Encrypt and export file to NH.

    This is a faster method of sending large files. It is used together with '/fi' import_file
    command that loads ciphertext to RxM for later decryption. Key is generated automatically
    so that bad passwords by users do not affect security of ciphertexts.

    As use of this command reveals use of TFC, it is disabled during trickle connection.
    """
    if settings.session_trickle:
        raise FunctionReturn("Command disabled during trickle connection.")

    path = ask_path_gui("Select file to export...", settings, get_file=True)
    name = path.split('/')[-1]
    data = bytearray()
    data.extend(str_to_bytes(name))

    if not os.path.isfile(path):
        raise FunctionReturn("Error: File not found.")

    if os.path.getsize(path) == 0:
        raise FunctionReturn("Error: Target file is empty. No file was sent.")

    phase("Reading data")
    with open(path, 'rb') as f:
        data.extend(f.read())
    phase("Done")

    phase("Compressing data")
    comp  = bytes(zlib.compress(bytes(data), level=9))
    phase("Done")

    phase("Encrypting data")
    file_key = keygen()
    file_ct  = encrypt_and_sign(comp, key=file_key)
    phase("Done")

    phase("Exporting data")
    transmit(EXPORTED_FILE_CT_HEADER + file_ct, settings, gateway)
    phase("Done")

    box_print([f"Decryption key for file {name}:", '', b58encode(file_key)], head=1, tail=1)
Esempio n. 18
0
def change_setting(user_input:   'UserInput',
                   contact_list: 'ContactList',
                   group_list:   'GroupList',
                   settings:     'Settings',
                   c_queue:      'Queue',
                   gateway:      'Gateway') -> None:
    """Change setting on TxM / RxM."""
    try:
        key = user_input.plaintext.split()[1]
    except IndexError:
        raise FunctionReturn("No setting specified.")

    try:
        _ = user_input.plaintext.split()[2]
    except IndexError:
        raise FunctionReturn("No value for setting specified.")

    value = ' '.join(user_input.plaintext.split()[2:])

    if key not in settings.key_list:
        raise FunctionReturn(f"Invalid setting {key}.")

    if settings.session_trickle:
        if key in ['e_correction_ratio', 'serial_iface_speed']:
            raise FunctionReturn("Change of setting disabled during trickle connection.")

    settings.change_setting(key, value, contact_list, group_list)

    if key == 'e_correction_ratio':
        time.sleep(0.5)
        transmit(UNENCRYPTED_PACKET_HEADER + UNENCRYPTED_EC_RATIO + value.encode(), settings, gateway)

    if key == 'serial_iface_speed':
        time.sleep(0.5)
        transmit(UNENCRYPTED_PACKET_HEADER + UNENCRYPTED_BAUDRATE + value.encode(), settings, gateway)

    if key == 'disable_gui_dialog':
        time.sleep(0.5)
        transmit(UNENCRYPTED_PACKET_HEADER + UNENCRYPTED_GUI_DIALOG + value.encode(), settings, gateway)

    packet = CHANGE_SETTING_HEADER + key.encode() + US_BYTE + value.encode()
    queue_command(packet, settings, c_queue)
Esempio n. 19
0
def process_imported_file(ts:          'datetime',
                          packet:      bytes,
                          window_list: 'WindowList'):
    """Decrypt and store imported file."""
    while True:
        try:
            print('')
            key = get_b58_key('imported_file')
            phase("Decrypting file", head=1)
            file_pt = auth_and_decrypt(packet[1:], key, soft_e=True)
            phase("Done")
            break
        except nacl.exceptions.CryptoError:
            c_print("Invalid decryption key. Try again.", head=2)
            print_on_previous_line(reps=6, delay=1.5)
        except KeyboardInterrupt:
            raise FunctionReturn("File import aborted.")

    try:
        phase("Decompressing file")
        file_dc = zlib.decompress(file_pt)
        phase("Done")
    except zlib.error:
        raise FunctionReturn("Decompression of file data failed.")

    try:
        f_name  = bytes_to_str(file_dc[:1024])
    except UnicodeError:
        raise FunctionReturn("Received file had an invalid name.")

    if not f_name.isprintable():
        raise FunctionReturn("Received file had an invalid name.")

    f_data     = file_dc[1024:]
    final_name = store_unique(f_data, DIR_IMPORTED, f_name)

    message = "Stored imported file to {}/{}".format(DIR_IMPORTED, final_name)
    box_print(message, head=1)

    local_win = window_list.get_local_window()
    local_win.print_new(ts, message, print_=False)
Esempio n. 20
0
def add_new_contact(contact_list: 'ContactList', group_list: 'GroupList',
                    settings: 'Settings', queues: Dict[bytes, 'Queue'],
                    gateway: 'Gateway') -> None:
    """Prompt for contact account details and initialize desired key exchange method."""
    try:
        if settings.session_trickle:
            raise FunctionReturn("Command disabled during trickle connection.")

        if len(contact_list) >= settings.m_number_of_accnts:
            raise FunctionReturn(
                f"Error: TFC settings only allow {settings.m_number_of_accnts} accounts."
            )

        clear_screen()
        c_print("Add new contact", head=1)

        acco = box_input("Contact account", tail=1,
                         validator=validate_account).strip()
        user = box_input("Your account", tail=1,
                         validator=validate_account).strip()
        defn = acco.split('@')[0].capitalize()
        nick = box_input(f"Contact nick [{defn}]",
                         default=defn,
                         tail=1,
                         validator=validate_nick,
                         validator_args=(contact_list, group_list,
                                         acco)).strip()
        keyx = box_input("Key exchange ([ECDHE],PSK) ",
                         default='ECDHE',
                         tail=1,
                         validator=validate_key_exchange).strip()

        if keyx.lower() in 'ecdhe':
            start_key_exchange(acco, user, nick, contact_list, settings,
                               queues, gateway)

        elif keyx.lower() in 'psk':
            new_psk(acco, user, nick, contact_list, settings, queues)

    except KeyboardInterrupt:
        raise FunctionReturn("Contact creation aborted.")
Esempio n. 21
0
def change_setting(cmd_data: bytes, ts: 'datetime', window_list: 'WindowList',
                   contact_list: 'ContactList', group_list: 'GroupList',
                   settings: 'Settings') -> None:
    """Change TFC setting."""
    key, value = [f.decode() for f in cmd_data.split(US_BYTE)]

    if key not in settings.key_list:
        raise FunctionReturn(f"Invalid setting {key}.")

    settings.change_setting(key, value, contact_list, group_list)
    local_win = window_list.get_local_window()
    local_win.print_new(ts, f"Changed setting {key} to {value}.")
Esempio n. 22
0
def remove_group(cmd_data: bytes, ts: 'datetime', window_list: 'WindowList',
                 group_list: 'GroupList') -> None:
    """Remove group."""
    group_name = cmd_data.decode()

    if group_name not in group_list.get_list_of_group_names():
        raise FunctionReturn(f"RxM has no group {group_name} to remove.")

    group_list.remove_group(group_name)

    box_print(f"Removed group {group_name}", head=1, tail=1)
    local_win = window_list.get_window('local')
    local_win.print_new(ts, f"Removed group {group_name}.", print_=False)
Esempio n. 23
0
def group_create(cmd_data: bytes, ts: 'datetime', window_list: 'WindowList',
                 contact_list: 'ContactList', group_list: 'GroupList',
                 settings: 'Settings') -> None:
    """Create a new group."""
    fields = [f.decode() for f in cmd_data.split(US_BYTE)]
    group_name = fields[0]

    purpaccs = set(fields[1:])
    accounts = set(contact_list.get_list_of_accounts())

    accepted = list(accounts & purpaccs)
    rejected = list(purpaccs - accounts)

    if len(accepted) > settings.m_members_in_group:
        raise FunctionReturn(
            "Error: TFC settings only allow {} members per group.".format(
                settings.m_members_in_group))

    if len(group_list) == settings.m_number_of_groups:
        raise FunctionReturn(
            "Error: TFC settings only allow {} groups.".format(
                settings.m_number_of_groups))

    a_contacts = [contact_list.get_contact(c) for c in accepted]
    group_list.add_group(group_name, settings.log_msg_by_default,
                         settings.n_m_notify_privacy, a_contacts)

    g_mgmt_print('new_g', accepted, contact_list, group_name)
    g_mgmt_print('unkwn', rejected, contact_list, group_name)

    # Reset members in window.
    window = window_list.get_window(group_name)
    window.window_contacts = a_contacts
    window.message_log = []
    window.unread_messages = 0

    local_win = window_list.get_window('local')
    local_win.print_new(ts, f"Created new group {group_name}.", print_=False)
Esempio n. 24
0
def process_command(user_input:   'UserInput',
                    window:       'Window',
                    settings:     'Settings',
                    queues:       Dict[bytes, 'Queue'],
                    contact_list: 'ContactList',
                    group_list:   'GroupList',
                    gateway:      'Gateway',
                    master_key:   'MasterKey') -> None:
    """Process command based on user input."""
    c = COMMAND_PACKET_QUEUE
    #    Keyword          Function to run         (                                      Parameters                                      )
    #    ---------------------------------------------------------------------------------------------------------------------------------
    d = {'about':        (print_about,                                                                                                   ),
         'add':          (add_new_contact,                             contact_list, group_list, settings, queues,    gateway            ),
         'clear':        (clear_screens,                       window,                           settings, queues[c], gateway            ),
         'cmd':          (rxm_show_cmd_win,                    window,                           settings, queues[c]                     ),
         'cm':           (cancel_packet,           user_input, window,                           settings, queues                        ),
         'cf':           (cancel_packet,           user_input, window,                           settings, queues                        ),
         'exit':         (exit_tfc,                                                              settings, queues[c], gateway            ),
         'export':       (export_logs,             user_input, window, contact_list,             settings, queues[c],          master_key),
         'fingerprints': (fingerprints,                        window                                                                    ),
         'fe':           (export_file,                                                           settings,              gateway          ),
         'fi':           (import_file,                                                           settings,              gateway          ),
         'fw':           (rxm_display_f_win,                   window,                           settings, queues[c]                     ),
         'group':        (process_group_command,   user_input,         contact_list, group_list, settings, queues                        ),
         'help':         (print_help,                                                            settings                                ),
         'history':      (print_logs,              user_input, window, contact_list,             settings, queues[c],          master_key),
         'localkey':     (new_local_key,                               contact_list,             settings, queues,      gateway          ),
         'logging':      (contact_setting,         user_input, window, contact_list, group_list, settings, queues[c]                     ),
         'msg':          (select_window,           user_input, window,                           settings, queues                        ),
         'names':        (print_recipients,                            contact_list, group_list,                                         ),
         'nick':         (change_nick,             user_input, window, contact_list, group_list, settings, queues[c]                     ),
         'notify':       (contact_setting,         user_input, window, contact_list, group_list, settings, queues[c]                     ),
         'passwd':       (change_master_key,       user_input,         contact_list, group_list, settings, queues,             master_key),
         'psk':          (rxm_load_psk,                        window, contact_list,             settings, queues[c]                     ),
         'reset':        (reset_screens,                       window,                           settings, queues[c], gateway            ),
         'rm':           (remove_contact,          user_input, window, contact_list, group_list, settings, queues                        ),
         'set':          (change_setting,          user_input,         contact_list, group_list, settings, queues[c], gateway            ),
         'settings':     (settings.print_settings,                                                                                       ),
         'store':        (contact_setting,         user_input, window, contact_list, group_list, settings, queues[c]                     ),
         'unread':       (rxm_display_unread,                                                    settings, queues[c]                     )}  # type: Dict[str, Any]

    cmd_key = user_input.plaintext.split()[0]
    if cmd_key not in d:
        raise FunctionReturn(f"Invalid command '{cmd_key}'.")

    from_dict  = d[cmd_key]
    func       = from_dict[0]
    parameters = from_dict[1:]
    func(*parameters)
Esempio n. 25
0
    def change_setting(self, key: str, value: str, contact_list: 'ContactList',
                       group_list: 'GroupList') -> None:
        """Parse, update and store new setting value."""
        attribute = self.__getattribute__(key)

        if isinstance(attribute, bool):
            value_ = value
            value = value.lower().capitalize()
            if value not in ['True', 'False']:
                raise FunctionReturn(f"Invalid value {value_}.")

        elif isinstance(attribute, int):
            if not value.isdigit() or eval(value) < 0 or eval(
                    value) > 7378697629483820640:
                raise FunctionReturn(f"Invalid value {value}.")

        elif isinstance(attribute, float):
            if not isinstance(eval(value), float) or eval(value) < 0.0:
                raise FunctionReturn(f"Invalid value {value}.")
            try:
                double_to_bytes(eval(value))
            except struct.error:
                raise FunctionReturn(f"Invalid value {value}.")

        elif isinstance(attribute, str):
            if len(value) > 255:
                raise FunctionReturn(
                    f"Setting must be shorter than 256 chars.")

        else:
            raise CriticalError("Invalid attribute type in settings.")

        self.validate_key_value_pair(key, value, contact_list, group_list)

        value = value if isinstance(attribute, str) else eval(value)
        setattr(self, key, value)
        self.store_settings()
Esempio n. 26
0
def change_nick(cmd_data: bytes, ts: 'datetime', window_list: 'WindowList',
                contact_list: 'ContactList', group_list: 'GroupList') -> None:
    """Change contact nick."""
    account, nick = [f.decode() for f in cmd_data.split(US_BYTE)]
    success, error_msg = validate_nick(nick,
                                       (contact_list, group_list, account))
    if not success:
        raise FunctionReturn(error_msg)

    c_window = window_list.get_window(account)
    c_window.name = nick
    contact = contact_list.get_contact(account)
    contact.nick = nick
    contact_list.store_contacts()

    cmd_win = window_list.get_local_window()
    cmd_win.print_new(ts, f"Changed {account} nick to {nick}.")
Esempio n. 27
0
def process_received_file(payload: bytes, nick: str) -> None:
    """Process received file assembly packets"""
    try:
        f_name, _, _, f_data = payload.split(US_BYTE)
    except ValueError:
        raise FunctionReturn("Received file had invalid structure.")

    try:
        f_name_d = f_name.decode()
    except UnicodeError:
        raise FunctionReturn("Received file had an invalid name.")

    if not f_name_d.isprintable():
        raise FunctionReturn("Received file had an invalid name.")

    try:
        f_data = base64.b85decode(f_data)
    except (binascii.Error, ValueError):
        raise FunctionReturn("Received file had invalid encoding.")

    file_ct  = f_data[:-32]
    file_key = f_data[-32:]
    if len(file_key) != 32:
        raise FunctionReturn("Received file had an invalid key.")

    try:
        file_pt = auth_and_decrypt(file_ct, file_key, soft_e=True)
    except nacl.exceptions.CryptoError:
        raise FunctionReturn("Decryption of file data failed.")

    try:
        file_dc = zlib.decompress(file_pt)
    except zlib.error:
        raise FunctionReturn("Decompression of file data failed.")

    if len(file_dc) == 0:
        raise FunctionReturn("Received file did not contain data.")

    f_dir      = f'{DIR_RX_FILES}/{nick}'
    final_name = store_unique(file_dc, f_dir, f_name_d)
    box_print(["Stored file from {} as {}.".format(nick, final_name)])
Esempio n. 28
0
    def validate_key_value_pair(key: str, value: str,
                                contact_list: 'ContactList',
                                group_list: 'GroupList') -> None:
        """Check values of some settings in closer detail."""
        if key in [
                'm_members_in_group', 'm_number_of_groups',
                'm_number_of_accnts'
        ]:
            if eval(value) % 10 != 0:
                raise FunctionReturn(
                    "Database padding settings must be divisible by 10.")

        if key == 'm_members_in_group':
            min_size = round_up(group_list.largest_group())
            if eval(value) < min_size:
                raise FunctionReturn(
                    f"Can't set max number of members lower than {min_size}.")

        if key == 'm_number_of_groups':
            min_size = round_up(len(group_list))
            if eval(value) < min_size:
                raise FunctionReturn(
                    f"Can't set max number of groups lower than {min_size}.")

        if key == 'm_number_of_accnts':
            min_size = round_up(len(contact_list))
            if eval(value) < min_size:
                raise FunctionReturn(
                    f"Can't set max number of contacts lower than {min_size}.")

        if key == 'serial_iface_speed':
            if eval(value) not in serial.Serial().BAUDRATES:
                raise FunctionReturn("Specified baud rate is not supported.")
            c_print("Baud rate will change on restart.", head=1, tail=1)

        if key == 'e_correction_ratio':
            if not value.isdigit() or eval(value) < 1:
                raise FunctionReturn(
                    "Invalid value for error correction ratio.")
            c_print("Error correction ratio will change on restart.",
                    head=1,
                    tail=1)

        if key in ['rxm_serial_adapter', 'txm_serial_adapter']:
            c_print("Interface will change on restart.", head=1, tail=1)

        if key in [
                'trickle_connection', 'trickle_stat_delay',
                'trickle_rand_delay'
        ]:
            c_print("Trickle setting will change on restart.", head=1, tail=1)
Esempio n. 29
0
File: path.py Progetto: barleyj/tfc
def ask_path_gui(prompt_msg: str,
                 settings: 'Settings',
                 get_file: bool = False) -> str:
    """Prompt PSK path with Tkinter dialog. Fallback to CLI if not available.

    :param prompt_msg: Directory selection prompt
    :param settings:   Settings object
    :param get_file:   When True, prompts for path to file instead of directory
    :return:           Selected directory / file
    """
    try:
        import _tkinter
        from tkinter import filedialog, Tk

        try:
            if settings.disable_gui_dialog:
                raise _tkinter.TclError

            root = Tk()
            root.withdraw()

            if get_file:
                f_path = filedialog.askopenfilename(title=prompt_msg)
            else:
                f_path = filedialog.askdirectory(title=prompt_msg)

            root.destroy()

            if not f_path:
                t = "File" if get_file else "Path"
                raise FunctionReturn(t + " selection aborted.")

            return f_path

        except _tkinter.TclError:
            return ask_path_cli(prompt_msg, get_file)

    # Fallback to CLI if Tkinter is not installed
    except ImportError:
        if 0:  # Remove warnings
            _tkinter, filedialog, Tk = None, None, None
            _, _, _ = _tkinter, filedialog, Tk
        return ask_path_cli(prompt_msg, get_file)
Esempio n. 30
0
def print_logs(user_input:   'UserInput',
               window:       'Window',
               contact_list: 'ContactList',
               settings:     'Settings',
               c_queue:      'Queue',
               master_key:   'MasterKey') -> None:
    """Print log files on screen."""
    try:
        no_messages_str = user_input.plaintext.split()[1]
        if not no_messages_str.isdigit():
            raise FunctionReturn("Specified invalid number of messages to print.")
        no_messages = int(no_messages_str)
    except IndexError:
        no_messages = 0

    packet = LOG_DISPLAY_HEADER + window.uid.encode() + US_BYTE + int_to_bytes(no_messages)
    queue_command(packet, settings, c_queue)

    access_history(window, contact_list, settings, master_key, no_messages)