#!/usr/bin/python3

# TODO guarantee numbers in generated passwords
# TODO fix syncing for multi-word entries
# TODO combine all print-only functions into one and print based on argument passed to function
# TODO make new, colored list its own callable function
# TODO test /dev/shm security

# external modules

from os import environ, remove, system
from shutil import copy, move, rmtree
from sys import argv, exit as s_exit
import random
import sshync
import string


# utility functions


def argument_filter():  # filters arguments to usable text TODO move to global process
    _argument_list = argv
    i, _argument = 0, ''
    for _ in _argument_list:
        while i < len(_argument_list) - 1:
            i += 1
            _argument += _argument_list[i] + ' '
    return _argument[:-1]


def clear():
    print('\n' * 100)


def replace_line(file_name, line_num, text):  # replaces text in a given line with different text
    _lines = open(file_name, 'r').readlines()
    _lines[line_num] = text
    open(file_name, 'w').writelines(_lines)


def shm_gen():  # generates random folder in /dev/shm for security and returns random names for the folder and temp file
    _shm_folder_gen = ''.join(random.SystemRandom().choice(string.ascii_letters + string.digits)
                              for _ in range(random.randint(10, 30)))
    _shm_entry_gen = ''.join(random.SystemRandom().choice(string.ascii_letters + string.digits)
                             for _ in range(random.randint(10, 30)))
    system(f"mkdir -p /dev/shm/{_shm_folder_gen}")  # TODO generate with Python
    return _shm_folder_gen, _shm_entry_gen


def encrypt(_shm_folder, _shm_entry, _entry_name):
    system(f"gpg -r {str(gpg_id)} -e '/dev/shm/{_shm_folder}/{_shm_entry}'")
    move(f"/dev/shm/{_shm_folder}/{_shm_entry}.gpg", f"{directory}{_entry_name.replace('/', '', 1)}.gpg")
    rmtree(f"/dev/shm/{_shm_folder}")


def encrypt_folder(_shm_folder, _shm_entry, _folder, _folder_entry_name):
    system(f"gpg -r {str(gpg_id)} -e '/dev/shm/{_shm_folder}/{_shm_entry}'")
    move(f"/dev/shm/{_shm_folder}/{_shm_entry}.gpg", f"{directory}{_folder}/{_folder_entry_name}.gpg")
    rmtree(f"/dev/shm/{_shm_folder}")


def edit_note(_shm_folder, _shm_entry):
    lines = open(f"/dev/shm/{_shm_folder}/{_shm_entry}").readlines()
    open(f"/dev/shm/{_shm_folder}/{_shm_entry}", 'w').writelines(lines[0:3])
    open(f"/dev/shm/{_shm_folder}/{_shm_entry}-n", 'w').writelines(lines[4:-2])
    system(f"nano /dev/shm/{_shm_folder}/{_shm_entry}-n")
    edit_notes = open(f"/dev/shm/{_shm_folder}/{_shm_entry}-n").read()
    open(f"/dev/shm/{_shm_folder}/{_shm_entry}", 'a').write('\n' + edit_notes + '\n\n')


# argument-specific functions


def tweak():  # runs configuration wizard
    clear()

    # storage directory creation
    system(f"mkdir -p /home/{environ.get('USER')}/.password-pasture")

    # device type configuration
    _device_type = input('\nIs this installation for a client or for a server? (C/s) ')
    if _device_type == 'S' or _device_type == 's':
        open("/var/lib/sshyp/sshyp-device", 'w').write(_device_type)
        copy('/usr/bin/sshync.py', f"/home/{environ.get('USER')}/")
        print('\nMake sure the ssh service is running and properly configured.\n\nConfiguration complete!')
    else:
        # device type configuration
        _device_type = 'c'  # ensure device type flag is properly set
        open("/var/lib/sshyp/sshyp-device", 'w').write(_device_type)

        # gpg configuration
        _gpg_id = input('\nsshyp requires the use of a unique gpg key. Do you already have one that you are '
                        'willing to use? (y/N)')
        if _gpg_id != 'y' and _gpg_id != 'Y':
            print('\nA unique gpg key has been generated for you.')
            system('gpg --full-generate-key')
        _gpg_id = str(input('\nPlease input the ID of your gpg key: '))
        open("/var/lib/sshyp/sshyp-gpg", 'w').write(_gpg_id + '\n')

        # lock file generation
        try:
            open('/var/lib/sshyp/lock', 'x').write('')
        except FileExistsError:
            pass
        system(f"gpg -r {str(_gpg_id)} -e /var/lib/sshyp/lock")
        remove('/var/lib/sshyp/lock')

        # ssh key configuration
        _ssh_gen = (input('\nMake sure the ssh service on the remote server is running and properly configured.'
                          '\n\nrsync support requires a unique ssh key. Would you like to have this automatically '
                          'generated? (Y/n) '))
        if _ssh_gen != 'n' and _ssh_gen != 'N':
            system('ssh-keygen -t ed25519 -f ~/.ssh/sshyp')
        else:
            print(f"\nEnsure that the key file you are using is located at /home/{environ.get('USER')}/.ssh/sshyp")

        # ssh ip+port configuration
        print('\nExample input: 10.10.10.10:9999\n')
        _ip_port = str(input('What is the IP and ssh port of the remote server? '))
        _ip, _sep, _port = _ip_port.partition(':')

        # ssh user configuration
        _username_ssh = str(input('\nWhat is the username of the remote server? '))
        print(f"\nEntry directory: /home/{_username_ssh}/.password-pasture/")

        # sshync profile generation
        sshync.make_profile('/var/lib/sshyp/sshyp.sshync', f"/home/{environ.get('USER')}/.password-pasture/",
                            f"/home/{_username_ssh}/.password-pasture/", f"/home/{environ.get('USER')}/.ssh/sshyp",
                            _ip, _port, _username_ssh)
        print('\nConfiguration complete!')


def helper():  # displays help menu TODO update with text art, thematic arguments
    clear()
    print('\nsshyp  Copyright (C) 2021  Randall Winkhart')
    print("This is free software, and you are welcome to redistribute it under certain conditions;\nthis program comes "
          "with ABSOLUTELY NO WARRANTY;\ntype `sshyp license' for details.")
    print('\nUSAGE: sshyp [/<entry name>] [OPTION] [FLAG]\n')
    print('Options: ')
    print('help/--help/-h           bring up this menu')
    print('version/-v               display sshyp version info')
    print('tweak                    configure sshyp')
    print('add                      add an entry')
    print('gen                      generate a new password')
    print('edit                     edit an existing entry')
    print('copy                     copy details of an entry to your clipboard')
    print('shear/-rm                delete an existing entry')
    print('sync/-s                  manually sync the entry directory via sshync\n')
    print('Flags:')
    print('add:')
    print(' password/-p             add a password entry')
    print(' note/-n                 add a note entry')
    print(' folder/-f               add a new folder for entries')
    print('edit:')
    print(' rename/relocate/-r      rename or relocate an entry')
    print(' username/-u             change the username of an entry')
    print(' password/-p             change the password of an entry')
    print(' note/-n                 change the note attached to an entry')
    print(' url/-l                  change the url attached to an entry')
    print('copy:')
    print(' username/-u             copy the username of an entry to your clipboard')
    print(' password/-p             copy the password of an entry to your clipboard')
    print(' url/-l                  copy the URL of an entry to your clipboard')
    print(' note/-n                 copy the note of an entry to your clipboard')
    print('gen:')
    print(' update/-u               generate a password for an existing entry\n')
    print("Tip: You can quickly read an entry with 'sshyp /<entry name>'!")
    print("When specifying entry names, do not include the file extension! '.gpg' is assumed!\n")


def version():  # displays version information TODO update with text art
    print('\n////////////////////////////////////////////////////////')
    print('/                                                      /')
    print('/     sshyp  Copyright (C) 2021  Randall Winkhart      /')
    print('/                                                      /')
    print('/               Version 2021.11.15.fr2                 /')
    print('/                The Rewritten Update                  /')
    print('/                                                      /')
    print('////////////////////////////////////////////////////////\n')


def no_arg():  # displays a list of entries and gives an option to select one for viewing TODO new, colored list
    print("\nFor a list of usable commands, run 'sshyp help'.\n")
    system(f"cd {directory}; ls -1 *")
    _read_entry = str(input('\nEntry to read: '))
    clear()
    system(f"gpg -d '{directory}{_read_entry.replace('/', '', 1)}.gpg'")
    print()


def show_license():  # displays licensing information
    print('\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU General'
          '\nPublic License as published by the Free Software Foundation, either version 3 of the License,\nor '
          '(at your option) any later version.\n'
          '\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\nwithout even '
          'the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\nSee the GNU General Public '
          'License for more details.\n\nhttps://opensource.org/licenses/GPL-3.0\n')


def read_shortcut():  # shortcut to quickly read an entry TODO may not work w/nested
    if argument.replace('/', '', 1).__contains__('/'):
        _folder, _sep, _folder_entry = argument.replace('/', '', 1).partition('/')
        system(f"gpg -d '{directory}{_folder}/{_folder_entry}.gpg'")  # TODO EXAMPLE of fix for multi-word entry names
    else:
        system(f"gpg -d '{directory}{argument.replace('/', '', 1)}.gpg'")


def sync():  # calls sshync to sync changes to the user's server
    print('\nSyncing entries with the server device...\n')
    # set permissions before uploading
    system('find ' + directory + ' -type d -exec chmod -R 700 {} +')
    system('find ' + directory + ' -type f -exec chmod -R 600 {} +')
    sshync.run_profile('/var/lib/sshyp/sshyp.sshync')


def add_info():  # displays 'add' syntax
    print('\nUSAGE: sshyp add [FLAG]')
    print('Flags:')
    print('add:')
    print(' password/-p             add a password entry')
    print(' note/-n                 add a note entry')
    print(' folder/-f               add a new folder for entries\n')


def add_entry():  # adds a new entry
    _entry_name = str(input('\nName of entry: '))
    _shm_folder, _shm_entry = shm_gen()
    if argument == 'add note' or argument == 'add -n':
        system(f"nano /dev/shm/{_shm_folder}/{_shm_entry}-n")
        _notes = open(f"/dev/shm/{_shm_folder}/{_shm_entry}-n", 'r').read()
        open(f"/dev/shm/{_shm_folder}/{_shm_entry}", 'w').writelines('\n\n\n\n' + _notes + '\n\n')
    if argument == 'add password' or argument == 'add -p':
        _username = str(input('Username: '))
        _password = str(input('Password: '))
        _url = str(input('URL: '))
        _add_note = input('Add a note to this entry? (y/N) ')
        if _add_note == 'y' or _add_note == 'Y':
            system(f"nano /dev/shm/{_shm_folder}/{_shm_entry}-n")
            _notes = open(f"/dev/shm/{_shm_folder}/{_shm_entry}-n", 'r').read()
        else:
            _notes = ''
        open(f"/dev/shm/{_shm_folder}/{_shm_entry}", 'w').writelines(_username + '\n' + _password + '\n' + _url + '\n\n'
                                                                     + _notes + '\n\n')
    if _entry_name.startswith('/'):
        if _entry_name.replace('/', '', 1).__contains__('/'):
            _folder, _sep, _folder_entry = _entry_name.replace('/', '', 1).partition('/')  # TODO may not work w/nested
            encrypt_folder(_shm_folder, _shm_entry, _folder, _folder_entry)
            system(f"gpg -d '{directory}{_folder}/{_folder_entry}.gpg'")
        else:
            encrypt(_shm_folder, _shm_entry, _entry_name)
            system(f"gpg -d '{directory}{_entry_name.replace('/', '', 1)}.gpg'")
    else:
        encrypt(_shm_folder, _shm_entry, _entry_name)
        system(f"gpg -d '{directory}{_entry_name}.gpg'")


def add_folder():  # creates a new folder TODO create folder on server, as well
    _folder_name = str(input('Name of new folder: '))
    system(f"mkdir '{directory}{_folder_name}'")  # TODO create using Python
    system(f"ssh -p {port} {username_ssh}@{ip} \"mkdir -p '{directory_ssh}{_folder_name}'\"")


def edit_info():  # displays 'edit' syntax
    print('\nUSAGE: sshyp edit [FLAG]')
    print('Flags:')
    print('edit:')
    print(' rename/relocate/-r      rename or relocate an entry')
    print(' username/-u             change the username of an entry')
    print(' password/-p             change the password of an entry')
    print(' url/-l                  change the url attached to an entry')
    print(' note/-n                 change the note attached to an entry\n')


def rename():  # renames an entry or folder TODO delete original from server to prevent re-downloading
    print()
    system(f"cd {directory}; ls -1 *")  # TODO replace with new list
    _entry_name = str(input('\nWhat would you like to rename/relocate? '))
    _new_name = str(input('New Name: '))
    if _entry_name.startswith('/'):
        if _entry_name.replace('/', '', 1).__contains__('/'):
            _folder, _sep, _folder_entry = _entry_name.replace('/', '', 1).partition('/')
            move(f"{directory}{_folder}/{_folder_entry}.gpg", f"{directory}/{_new_name}.gpg")
        else:
            move(f"{directory}{_entry_name.replace('/', '', 1)}.gpg", f"{directory}{_new_name.replace('/', '', 1)}.gpg")
    else:
        move(f"{directory}{_entry_name}.gpg", f"{directory}{_new_name}.gpg")


def edit():  # edits the contents of an entry
    print()
    system(f"cd {directory}; ls -1 *")  # TODO replace with new list
    _entry_name = str(input('\nWhat file would you like to edit? '))
    _shm_folder, _shm_entry = shm_gen()
    if _entry_name.startswith('/'):
        if _entry_name.replace('/', '', 1).__contains__('/'):
            _folder, _sep, _folder_entry = _entry_name.replace('/', '', 1).partition('/')
            system(f"gpg -d --output /dev/shm/{_shm_folder}/{_shm_entry} '{directory}{_folder}/{_folder_entry}.gpg'")
        else:
            system(f"gpg -d --output /dev/shm/{_shm_folder}/{_shm_entry} '{directory}{_entry_name.replace('/', '', 1)}"
                   f".gpg'")
    else:
        system(f"gpg -d --output /dev/shm/{_shm_folder}/{_shm_entry} '{directory}{_entry_name}.gpg'")
    if argument == 'edit username' or argument == 'edit -u':
        _detail = str(input('New Username: '))
        replace_line(f"/dev/shm/{_shm_folder}/{_shm_entry}", 0, _detail + '\n')
    if argument == 'edit password' or argument == 'edit -p':
        _detail = str(input('New Password: '))
        replace_line(f"/dev/shm/{_shm_folder}/{_shm_entry}", 1, _detail + '\n')
    if argument == 'edit url' or argument == 'edit -l':
        _detail = str(input('New URL: '))
        replace_line(f"/dev/shm/{_shm_folder}/{_shm_entry}", 2, _detail + '\n')
    if argument == 'edit note' or argument == 'edit -n':
        edit_note(_shm_folder, _shm_entry)
    if _entry_name.startswith('/'):
        if _entry_name.replace('/', '', 1).__contains__('/'):
            _folder, _sep, _folder_entry = _entry_name.replace('/', '', 1).partition('/')
            remove(f"{directory}{_folder}/{_folder_entry}.gpg")
            encrypt_folder(_shm_folder, _shm_entry, _folder, _folder_entry)
            system(f"gpg -d '{directory}{_folder}/{_folder_entry}.gpg'")
        else:
            remove(f"{directory}{_entry_name.replace('/', '', 1)}.gpg")
            encrypt(_shm_folder, _shm_entry, _entry_name)
            system(f"gpg -d '{directory}{_entry_name.replace('/', '', 1)}.gpg'")
    else:
        remove(f"{directory}{_entry_name}.gpg")
        encrypt(_shm_folder, _shm_entry, _entry_name)
        system(f"gpg -d '{directory}{_entry_name}.gpg'")


def gen():  # generates a password for a new or an existing entry
    _username, _url, _notes = None, None, None  # sets base-line values to avoid errors
    _shm_folder, _shm_entry = shm_gen()
    if argument == 'gen update' or argument == 'gen -u':
        print()
        system(f"cd {directory}; ls -1 *")  # TODO replace with new list
    _entry_name = str(input('\nName of service: '))
    if argument != 'gen update' and argument != 'gen -u':
        _username = str(input('Username: '))
    _pass_length = int(input('How many characters should be in the password? '))
    _pass_type = str(input('Should the password be simple (for compatibility) or complex (for security)? (s/C) '))
    if _pass_type != 's':
        _pass_type = string.ascii_letters + string.digits + string.punctuation
    if _pass_type == 's':
        _pass_type = string.ascii_letters + string.digits
    _pass_gen = ''.join(random.SystemRandom().choice(_pass_type) for _ in range(_pass_length))
    if argument != 'gen update' and argument != 'gen -u':
        _url = str(input('URL: '))
        _notes = str(input('Additional notes: '))  # TODO update w/new notes system
    if argument != 'gen update' and argument != 'gen -u':
        open(f"/dev/shm/{_shm_folder}/{_shm_entry}", 'w').writelines(_username + '\n' + _pass_gen + '\n' + _url +
                                                                     '\n\n' + _notes + '\n\n')
    if argument != 'gen update' and argument != 'gen -u':
        if _entry_name.startswith('/'):
            if _entry_name.replace('/', '', 1).__contains__('/'):
                _folder, _sep, _folder_entry = _entry_name.replace('/', '', 1).partition('/')  # TODO nested
                encrypt_folder(_shm_folder, _shm_entry, _folder, _folder_entry)
                system(f"gpg -d '{directory}{_folder}/{_folder_entry}.gpg'")
            else:
                encrypt(_shm_folder, _shm_entry, _entry_name)
                system(f"gpg -d '{directory}{_entry_name.replace('/', '', 1)}.gpg'")
        else:
            encrypt(_shm_folder, _shm_entry, _entry_name)
            system(f"gpg -d '{directory}{_entry_name}.gpg'")
    else:
        if _entry_name.startswith('/'):
            if _entry_name.replace('/', '', 1).__contains__('/'):
                _folder, _sep, _folder_entry = _entry_name.replace('/', '', 1).partition(
                    '/')  # TODO may not work w/nested
                system(f"gpg -d --output /dev/shm/{_shm_folder}/{_shm_entry} '{directory}{_folder}/{_folder_entry}"
                       f".gpg'")
                replace_line(f"/dev/shm/{_shm_folder}/{_shm_entry}", 1, _pass_gen + '\n')
                remove(f"{directory}{_folder}/{_folder_entry.replace('/', '', 1)}.gpg")
                encrypt_folder(_shm_folder, _shm_entry, _folder, _folder_entry)
                system(f"gpg -d '{directory}{_folder}/{_folder_entry}.gpg'")
            else:
                system(f"gpg -d --output /dev/shm/{_shm_folder}/{_shm_entry} '{directory}"
                       f"{_entry_name.replace('/', '', 1)}.gpg'")
                replace_line(f"/dev/shm/{_shm_folder}/'{_shm_entry}", 1, _pass_gen + '\n')
                remove(f"{directory}{_entry_name.replace('/', '', 1)}.gpg")
                encrypt(_shm_folder, _shm_entry, _entry_name)
                system(f"gpg -d '{directory}{_entry_name.replace('/', '', 1)}.gpg'")
        else:
            system(f"gpg -d --output /dev/shm/{_shm_folder}/{_shm_entry} '{directory}{_entry_name}.gpg'")
            replace_line(f"/dev/shm/{_shm_folder}/{_shm_entry}", 1, _pass_gen + '\n')
            remove(f"{directory}{_entry_name}.gpg")
            encrypt(_shm_folder, _shm_entry, _entry_name)
            system(f"gpg -d '{directory}{_entry_name}.gpg'")


def copy_info():  # displays 'copy' syntax
    print('\nUSAGE: sshyp copy [FLAG]')
    print('Flags:')
    print('copy:')
    print(' username/-u             copy the username of an entry to your clipboard')
    print(' password/-p             copy the password of an entry to your clipboard')
    print(' url/-l                  copy the URL of an entry to your clipboard')
    print(' note/-n                 copy the note of an entry to your clipboard\n')


def copy_data():  # copies a specified field of an entry to the clipboard
    print()
    system(f"cd {directory}; ls -1 *")  # TODO replace with new list
    _read_entry = str(input('\nEntry to copy: '))
    _shm_folder, _shm_entry = shm_gen()
    system(f"gpg -d --output /dev/shm/{_shm_folder}/{_shm_entry} '{directory}{_read_entry}.gpg'")
    _copy_line = open(f"/dev/shm/{_shm_folder}/{_shm_entry}", 'r').readlines()
    if environ.get('WAYLAND_DISPLAY') == 'wayland-0':  # checks if the user is using Wayland or X for clipboard
        if argument == 'copy username' or argument == 'copy -u':
            system('wl-copy ' + "'" + _copy_line[0].replace('\n', '').replace("'", "'\\''") + "'")
        elif argument == 'copy password' or argument == 'copy -p':
            system('wl-copy ' + "'" + _copy_line[1].replace('\n', '').replace("'", "'\\''") + "'")
        elif argument == 'copy url' or argument == 'copy -l':
            system('wl-copy ' + "'" + _copy_line[2].replace('\n', '').replace("'", "'\\''") + "'")
        elif argument == 'copy note' or argument == 'copy -n':
            system('wl-copy ' + "'" + _copy_line[4].replace('\n', '').replace("'", "'\\''") + "'")
    else:
        if argument == 'copy username' or argument == 'copy -u':
            system('echo -n ' + "'" + _copy_line[0].replace('\n', '').replace("'", "'\\''") + "'" + ' | xclip -sel c')
        elif argument == 'copy password' or argument == 'copy -p':
            system('echo -n ' + "'" + _copy_line[1].replace('\n', '').replace("'", "'\\''") + "'" + ' | xclip -sel c')
        elif argument == 'copy url' or argument == 'copy -l':
            system('echo -n ' + "'" + _copy_line[2].replace('\n', '').replace("'", "'\\''") + "'" + ' | xclip -sel c')
        elif argument == 'copy note' or argument == 'copy -n':
            system('echo -n ' + "'" + _copy_line[4].replace('\n', '').replace("'", "'\\''") + "'" + ' | xclip -sel c')
    rmtree(f"/dev/shm/{_shm_folder}")


def remove_data():  # deletes an entry or folder from both the client and the server
    print()
    system(f"cd {directory}; ls -1 *")  # TODO replace with new list
    _entry_name = str(input('\nWhat would you like to delete? '))
    try:
        system('gpg -d --output /dev/shm/sshyp.lock /var/lib/sshyp/lock.gpg')
        _unlock = open('/dev/shm/sshyp.lock', 'r').readlines()
        remove('/dev/shm/sshyp.lock')
    except FileNotFoundError:
        print('\nAccess denied.\n')
        s_exit()
    if _entry_name.startswith('/'):
        if _entry_name.replace('/', '', 1).__contains__('/'):
            if device_type == 'c':
                system(f"ssh -p {port} {username_ssh}@{ip} \"rm -rf '{directory_ssh}{_entry_name.replace('/', '', 1)}"
                       f".gpg'\"")
            remove(f"{directory}{_entry_name.replace('/', '', 1)}.gpg")
        else:
            if device_type == 'c':
                system(f"ssh -p {port} {username_ssh}@{ip} \"rm -rf '{directory_ssh}{_entry_name.replace('/', '', 1)}"
                       f"'\"")
            rmtree(f"{directory}{_entry_name.replace('/', '', 1)}")
    else:
        if device_type == 'c':
            system(f"ssh -p {port} {username_ssh}@{ip} \"rm -rf '{directory_ssh}{_entry_name}.gpg'\"")
        remove(f"{directory}{_entry_name}.gpg")


# program start sequence

# import saved userdata
device_type = ''
try:
    device_type = open('/var/lib/sshyp/sshyp-device').read().strip()
    if device_type == 'c':
        gpg_id = open('/var/lib/sshyp/sshyp-gpg').read().strip()
        ssh_info = sshync.get_profile('/var/lib/sshyp/sshyp.sshync')
        username_ssh = ssh_info[0].replace('\n', '')
        ip = ssh_info[1].replace('\n', '')
        port = ssh_info[2].replace('\n', '')
        directory = str(ssh_info[3].replace('\n', ''))
        directory_ssh = '/home/' + username_ssh + '/.password-pasture/'
except FileNotFoundError:
    if device_type != 's' and device_type != 'S':
        print('\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
        print("Not all necessary configuration files are present. Please run 'sshyp tweak'!")
        print('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n')
    s_exit()

# retrieve typed argument
argument = argument_filter()

# run function based on argument
error = 0  # create an error flag to determine if syncing should occur at end
if argument.startswith('/'):
    read_shortcut()
elif argument == '':
    try:
        no_arg()
    except KeyboardInterrupt:
        print('\n')
        s_exit()
elif argument == 'tweak':
    try:
        tweak()
    except KeyboardInterrupt:
        print('\n')
        s_exit()
elif argument == 'help' or argument == '--help' or argument == '-h':
    helper()
elif argument == 'license':
    show_license()
elif argument == 'version' or argument == '-v':
    version()
elif argument == 'add':
    add_info()
elif argument == 'add note' or argument == 'add -n' or argument == 'add password' or argument == 'add -p':
    try:
        add_entry()
    except KeyboardInterrupt:
        print('\n')
        s_exit()
elif argument == 'add folder' or argument == 'add -f':
    try:
        add_folder()
    except KeyboardInterrupt:
        print('\n')
        s_exit()
elif argument == 'edit':
    edit_info()
elif argument == 'edit rename' or argument == 'edit relocate' or argument == 'edit -r':
    try:
        rename()
    except KeyboardInterrupt:
        print('\n')
        s_exit()
elif argument == 'edit username' or argument == 'edit -u' or argument == 'edit password' or argument == 'edit -p' or \
        argument == 'edit url' or argument == 'edit -l' or argument == 'edit note' or argument == 'edit -n':
    try:
        edit()
    except KeyboardInterrupt:
        print('\n')
        s_exit()
elif argument == 'gen' or argument == 'gen update' or argument == 'gen -u':
    try:
        gen()
    except KeyboardInterrupt:
        print('\n')
        s_exit()
elif argument == 'copy':
    copy_info()
elif argument == 'copy username' or argument == 'copy -u' or argument == 'copy password' or argument == 'copy -p' or \
        argument == 'copy url' or argument == 'copy -l' or argument == 'copy note' or argument == 'copy -n':
    try:
        copy_data()
    except KeyboardInterrupt:
        print('\n')
        s_exit()
elif argument == 'remove' or argument == '-rm':
    try:
        remove_data()
    except KeyboardInterrupt:
        print('\n')
        s_exit()
elif argument == 'sync' or argument == '-s':
    sync()
else:
    error = 1  # set error flag to 1 to stop syncing
    print("\nArgument error! Please run 'sshyp help' for a list of usable commands.\n")
    s_exit()

# sync at end of program if any changes were made
if argument != '' and argument != 'help' and argument != '--help' and argument != '-h' and argument != 'license' and \
        argument != 'add' and argument != 'sync' and argument != 'edit' and argument != 'copy username' and argument \
        != 'copy -u' and argument != 'copy password' and argument != 'copy -p' and argument != 'copy url' and argument \
        != 'copy -l' and argument != 'copy note' and argument != 'copy -n' and argument != 'copy' and error == 0 and \
        argument.startswith('/') is False:
    sync()
