mirror of
https://github.com/rwinkhart/sshyp.git
synced 2026-08-28 12:56:28 -04:00
671 lines
36 KiB
Python
Executable File
671 lines
36 KiB
Python
Executable File
#!/bin/python3
|
|
|
|
# external modules
|
|
|
|
from os import environ, listdir, path, remove, system, walk
|
|
from pathlib import Path
|
|
import random
|
|
from shutil import copy, get_terminal_size, move, rmtree
|
|
import sshync
|
|
import string
|
|
from sys import argv, exit as s_exit
|
|
from textwrap import fill
|
|
|
|
|
|
# utility functions
|
|
|
|
def entry_list_gen(): # generates and prints a list of all folders and entries
|
|
print('\n\u001b[38;5;0;48;5;15msshyp entries:\u001b[0m\n')
|
|
_entry_list, _color_alternator = [], 1
|
|
for _entry in sorted(listdir(path.expanduser('~/.password-pasture'))):
|
|
if Path(path.expanduser(f"~/.password-pasture/{_entry}")).is_file():
|
|
if _color_alternator == 1:
|
|
_entry_list.append(f"\u001b[38;5;255m{_entry.replace('.gpg', '')}\u001b[0m")
|
|
_color_alternator = 2
|
|
else:
|
|
_entry_list.append(f"\u001b[38;5;248m{_entry.replace('.gpg', '')}\u001b[0m")
|
|
_color_alternator = 1
|
|
_real = len(' '.join(_entry_list)) - (15 * len(_entry_list))
|
|
if _real <= get_terminal_size()[0]:
|
|
_width = len(' '.join(_entry_list))
|
|
else:
|
|
_width = (len(' '.join(_entry_list)) / (_real / get_terminal_size()[0]) - 50)
|
|
try:
|
|
print(fill(' '.join(_entry_list), width=_width) + '\n')
|
|
except ValueError:
|
|
pass
|
|
for _root, _directories, _files in walk(path.expanduser('~/.password-pasture')):
|
|
for _dir in sorted(_directories):
|
|
_inner_dir = f"{_root.replace(path.expanduser('~/.password-pasture'), '')}/{_dir}"
|
|
print(f"\n\u001b[38;5;15;48;5;238m{_inner_dir}/\u001b[0m")
|
|
_entry_list, _color_alternator = [], 1
|
|
for _s_root, _s_directories, _s_files in walk(path.expanduser(f"~/.password-pasture{_inner_dir}")):
|
|
for _entry in sorted(_s_files):
|
|
if _color_alternator == 1:
|
|
_entry_list.append(f"\u001b[38;5;255m{_entry.replace('.gpg', '')}\u001b[0m")
|
|
_color_alternator = 2
|
|
else:
|
|
_entry_list.append(f"\u001b[38;5;248m{_entry.replace('.gpg', '')}\u001b[0m")
|
|
_color_alternator = 1
|
|
_real = len(' '.join(_entry_list)) - (15 * len(_entry_list))
|
|
if _real <= get_terminal_size()[0]:
|
|
_width = len(' '.join(_entry_list))
|
|
else:
|
|
_width = (len(' '.join(_entry_list)) / (_real / get_terminal_size()[0]) - 50)
|
|
try:
|
|
print(fill(' '.join(_entry_list), width=_width) + '\n')
|
|
except ValueError:
|
|
print('\u001b[38;5;208m-empty directory!-\u001b[0m\n')
|
|
|
|
|
|
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 a random temporary folder 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)))
|
|
Path(tmp_dir + _shm_folder_gen).mkdir(0o700)
|
|
return _shm_folder_gen, _shm_entry_gen
|
|
|
|
|
|
def pass_gen():
|
|
_length = int(input('How many characters should be in the password? '))
|
|
_complexity = str(input('Should the password be simple (for compatibility) or complex (for security)? (s/C) '))
|
|
if _complexity.lower() == 's':
|
|
_complexity = string.ascii_letters + string.digits
|
|
else:
|
|
_complexity = string.ascii_letters + string.digits + string.punctuation
|
|
_gen = ''.join(random.SystemRandom().choice(_complexity) for _ in range(_length))
|
|
_min_special, _special = round(.2 * _length), 0
|
|
for _character in _gen:
|
|
if not _character.isalpha():
|
|
_special += 1
|
|
if _special < _min_special:
|
|
_gen = pass_gen()
|
|
return _gen
|
|
|
|
|
|
def encrypt(_shm_folder, _shm_entry, _location):
|
|
system(f"gpg -qr {str(gpg_id)} -e '{tmp_dir}{_shm_folder}/{_shm_entry}'")
|
|
move(f"{tmp_dir}{_shm_folder}/{_shm_entry}.gpg", f"{directory}{_location}.gpg")
|
|
rmtree(f"{tmp_dir}{_shm_folder}")
|
|
|
|
|
|
def edit_note(_shm_folder, _shm_entry):
|
|
lines = open(f"{tmp_dir}{_shm_folder}/{_shm_entry}").readlines()
|
|
open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'w').writelines(lines[0:3])
|
|
open(f"{tmp_dir}{_shm_folder}/{_shm_entry}-n", 'w').writelines(lines[4:-2])
|
|
system(f"nano {tmp_dir}{_shm_folder}/{_shm_entry}-n")
|
|
edit_notes = open(f"{tmp_dir}{_shm_folder}/{_shm_entry}-n").read()
|
|
open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'a').write('\n' + edit_notes + '\n\n')
|
|
|
|
|
|
# argument-specific functions
|
|
|
|
|
|
def tweak(): # runs configuration wizard
|
|
# storage/config directory creation
|
|
Path(path.expanduser('~/.password-pasture')).mkdir(0o700, parents=True, exist_ok=True)
|
|
Path(path.expanduser('~/.config/sshyp/devices')).mkdir(0o700, parents=True, exist_ok=True)
|
|
if not Path(f"{path.expanduser('~/.config/sshyp/tmp')}").exists():
|
|
if Path("/data/data/com.termux").exists():
|
|
system(f"ln -s '/data/data/com.termux/files/usr/tmp' {path.expanduser('~/.config/sshyp/tmp')}")
|
|
else:
|
|
system(f"ln -s /dev/shm {path.expanduser('~/.config/sshyp/tmp')}")
|
|
# device type configuration
|
|
_device_type = input('\nIs this installation for a client or for a server? (C/s) ')
|
|
if _device_type.lower() == 's':
|
|
open(path.expanduser('~/.config/sshyp/sshyp-device'), 'w').write(_device_type)
|
|
Path(path.expanduser('~/.config/sshyp/deleted')).mkdir(0o700, parents=True, exist_ok=True)
|
|
copy('/usr/bin/sshync.py', path.expanduser('~/'))
|
|
copy('/usr/bin/sshypRemote.py', path.expanduser('~/sshypRemote.py'))
|
|
print('\nMake sure the ssh service is running and properly configured.\n\nConfiguration complete!\n')
|
|
s_exit()
|
|
else:
|
|
# device type configuration
|
|
_device_type = 'c' # ensure device type flag is properly set
|
|
open(path.expanduser('~/.config/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.lower() != '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(path.expanduser('~/.config/sshyp/sshyp-gpg'), 'w').write(_gpg_id + '\n')
|
|
|
|
# lock file generation
|
|
open(path.expanduser('~/.config/sshyp/lock'), 'w')
|
|
system(f"gpg -qr {str(_gpg_id)} -e {path.expanduser('~/.config/sshyp/lock')}")
|
|
remove(f"{path.expanduser('~/.config/sshyp')}/lock")
|
|
|
|
# ssh key configuration
|
|
_ssh_gen = (input('\nMake sure the ssh service on the remote server is running and properly configured.'
|
|
'\n\nSync support requires a unique ssh key. Would you like to have this automatically '
|
|
'generated? (Y/n) '))
|
|
if _ssh_gen.lower() != 'n':
|
|
system('ssh-keygen -t ed25519 -f ~/.ssh/sshyp')
|
|
else:
|
|
print(f"\nEnsure that the key file you are using is located at {path.expanduser('~/.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(path.expanduser('~/.config/sshyp/sshyp.sshync'),
|
|
path.expanduser('~/.password-pasture/'), f"/home/{_username_ssh}/.password-pasture/",
|
|
path.expanduser('~/.ssh/sshyp'), _ip, _port, _username_ssh)
|
|
|
|
# device name configuration
|
|
for _name in listdir(path.expanduser('~/.config/sshyp/devices')): # remove existing device name
|
|
remove(f"{path.expanduser('~/.config/sshyp/devices/')}{_name}")
|
|
print('\nIMPORTANT: This name MUST be unique amongst your client devices. '
|
|
'\nThis is used to keep track of which devices have up-to-date databases.\n')
|
|
_client_device_name = str(input('What would you like to name this device? '))
|
|
open(f"{path.expanduser('~/.config/sshyp/devices/')}{_client_device_name}", 'w')
|
|
system(f"ssh -i '{path.expanduser('~/.ssh/sshyp')}' -p {_port} {_username_ssh}@{_ip} "
|
|
f"\"touch '/home/{_username_ssh}/.config/sshyp/devices/{_client_device_name}'\"")
|
|
|
|
print('\nConfiguration complete!\n')
|
|
|
|
|
|
def print_info(): # prints help text based on argument
|
|
if argument == 'help' or argument == '--help' or argument == '-h':
|
|
print('\n\u001b[38;5;0;48;5;15msshyp Copyright (C) 2021-2022 Randall Winkhart\u001b[0m\n')
|
|
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('\n\u001b[38;5;15;48;5;238mUSAGE: sshyp [OPTION [FLAG] [<entry name>]] | [/<entry name>]\u001b[0m\n')
|
|
print('\u001b[38;5;0;48;5;15mOptions:\u001b[0m')
|
|
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('\u001b[38;5;0;48;5;15mFlags:\u001b[0m')
|
|
print('\u001b[38;5;15;48;5;238madd:\u001b[0m')
|
|
print(' password/-p add a password entry')
|
|
print(' note/-n add a note entry')
|
|
print(' folder/-f add a new folder for entries')
|
|
print('\u001b[38;5;15;48;5;238medit:\u001b[0m')
|
|
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('\u001b[38;5;15;48;5;238mcopy:\u001b[0m')
|
|
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('\u001b[38;5;15;48;5;238mgen:\u001b[0m')
|
|
print(' update/-u generate a password for an existing entry\n')
|
|
print("\u001b[38;5;0;48;5;15mTip:\u001b[0m You can quickly read an entry with 'sshyp /<entry name>'!")
|
|
elif argument == 'version' or argument == '-v':
|
|
print('\n\u001b[38;5;0;48;5;15m////////////////////////////////////////////////////////\u001b[0m')
|
|
print('\u001b[38;5;0;48;5;15m/\u001b[38;5;15;48;5;238m '
|
|
'\u001b[38;5;0;48;5;15m/\u001b[0m')
|
|
print('\u001b[38;5;0;48;5;15m/\u001b[38;5;15;48;5;238m \u001b[38;5;0;48;5;15msshyp Copyright (C) 2021-2022 '
|
|
'Randall Winkhart\u001b[38;5;15;48;5;238m \u001b[38;5;0;48;5;15m/\u001b[0m')
|
|
print('\u001b[38;5;0;48;5;15m/\u001b[38;5;15;48;5;238m '
|
|
'\u001b[38;5;0;48;5;15m/\u001b[0m')
|
|
print('\u001b[38;5;0;48;5;15m/\u001b[38;5;15;48;5;238m \u001b[38;5;0;48;5;15mVersion 1.0'
|
|
'\u001b[38;5;15;48;5;238m \u001b[38;5;0;48;5;15m/\u001b[0m')
|
|
print('\u001b[38;5;0;48;5;15m/\u001b[38;5;15;48;5;238m \u001b[38;5;0;48;5;15mThe Polished Trotters '
|
|
'Update\u001b[38;5;15;48;5;238m \u001b[38;5;0;48;5;15m/\u001b[0m')
|
|
print('\u001b[38;5;0;48;5;15m/\u001b[38;5;15;48;5;238m '
|
|
'\u001b[38;5;0;48;5;15m/\u001b[0m')
|
|
print('\u001b[38;5;0;48;5;15m////////////////////////////////////////////////////////\u001b[0m\n')
|
|
elif argument == 'license':
|
|
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')
|
|
elif argument == 'add':
|
|
print('\n\u001b[38;5;15;48;5;238mUSAGE: sshyp add {FLAG {<entry name>}}\u001b[0m\n')
|
|
print('\u001b[38;5;0;48;5;15mFlags:\u001b[0m')
|
|
print('\u001b[38;5;15;48;5;238madd:\u001b[0m')
|
|
print(' password/-p add a password entry')
|
|
print(' note/-n add a note entry')
|
|
print(' folder/-f add a new folder for entries\n')
|
|
elif argument == 'edit':
|
|
print('\n\u001b[38;5;15;48;5;238mUSAGE: sshyp edit {FLAG {<entry name>}}\u001b[0m\n')
|
|
print('\u001b[38;5;0;48;5;15mFlags:\u001b[0m')
|
|
print('\u001b[38;5;15;48;5;238medit:\u001b[0m')
|
|
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')
|
|
elif argument == 'copy':
|
|
print('\n\u001b[38;5;15;48;5;238mUSAGE: sshyp copy {FLAG {<entry name>}}\u001b[0m\n')
|
|
print('\u001b[38;5;0;48;5;15mFlags:\u001b[0m')
|
|
print('\u001b[38;5;15;48;5;238mcopy:\u001b[0m')
|
|
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 no_arg(): # displays a list of entries and gives an option to select one for viewing
|
|
print("\nFor a list of usable commands, run 'sshyp help'.")
|
|
entry_list_gen()
|
|
_read_entry = str(input('Entry to read: '))
|
|
system(f"gpg -qd '{directory}{_read_entry.replace('/', '', 1)}.gpg'")
|
|
print()
|
|
|
|
|
|
def read_shortcut(): # shortcut to quickly read an entry
|
|
system(f"gpg -qd '{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')
|
|
# check for deletions
|
|
system(f"ssh -i '{path.expanduser('~/.ssh/sshyp')}' -p {port} {username_ssh}@{ip} \"python -c 'import sshypRemote"
|
|
f"; sshypRemote.deletion_check(\"'\"{client_device_name}\"'\")'\"")
|
|
system(f"sftp -p -q -i '{path.expanduser('~/.ssh/sshyp')}' -P {port} {username_ssh}@{ip}:"
|
|
f"'/home/{username_ssh}/.config/sshyp/deletion_database' {path.expanduser('~/.config/sshyp/')}")
|
|
try:
|
|
_deletion_database = open(path.expanduser('~/.config/sshyp/deletion_database')).readlines()
|
|
except (FileNotFoundError, IndexError):
|
|
print('\nThe deletion database does not exist or is corrupted.\n')
|
|
_deletion_database = None
|
|
s_exit()
|
|
for _file in _deletion_database:
|
|
if _file[:-1].endswith('/'):
|
|
try:
|
|
if silent_sync != 1:
|
|
print(f"\u001b[38;5;208m{_file[:-1]}\u001b[0m has been sheared, removing...")
|
|
rmtree(f"{path.expanduser('~/.password-pasture/')}{_file[:-1]}")
|
|
except FileNotFoundError:
|
|
if silent_sync != 1:
|
|
print('Folder does not exist locally.')
|
|
else:
|
|
try:
|
|
if silent_sync != 1:
|
|
print(f"\u001b[38;5;208m{_file[:-1]}\u001b[0m has been sheared, removing...")
|
|
remove(f"{path.expanduser('~/.password-pasture/')}{_file[:-1]}.gpg")
|
|
except (FileNotFoundError, IsADirectoryError):
|
|
if silent_sync != 1:
|
|
print('File does not exist locally.')
|
|
# check for new folders
|
|
system(f"ssh -i '{path.expanduser('~/.ssh/sshyp')}' -p {port} {username_ssh}@{ip} \"python -c 'import sshypRemote"
|
|
f"; sshypRemote.folder_check()'\"")
|
|
system(f"sftp -p -q -i '{path.expanduser('~/.ssh/sshyp')}' -P {port} {username_ssh}@{ip}:"
|
|
f"'/home/{username_ssh}/.config/sshyp/folder_database' {path.expanduser('~/.config/sshyp/')}")
|
|
try:
|
|
_folder_database = open(path.expanduser('~/.config/sshyp/folder_database')).readlines()
|
|
except (FileNotFoundError, IndexError):
|
|
print('\nThe folder database does not exist or is corrupted.\n')
|
|
_folder_database = None
|
|
s_exit()
|
|
for _folder in _folder_database:
|
|
if Path(f"{path.expanduser('~')}{_folder[:-1]}").is_dir():
|
|
pass
|
|
else:
|
|
print(f"\u001b[38;5;2m{_folder.replace('/.password-pasture/', '')[:-1]}/\u001b[0m does not exist locally, "
|
|
f"creating...")
|
|
Path(f"{path.expanduser('~')}{_folder[:-1]}").mkdir(0o700, parents=True, exist_ok=True)
|
|
# 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(path.expanduser('~/.config/sshyp/sshyp.sshync'))
|
|
|
|
|
|
def add_entry(): # adds a new entry
|
|
_shm_folder, _shm_entry = None, None # sets base-line values to avoid errors
|
|
if argument == 'add note' or argument == 'add -n' or argument == 'add password' or argument == 'add -p':
|
|
_entry_name = str(input('\nName of entry: '))
|
|
else:
|
|
_entry_name = argument.split(' ')
|
|
del _entry_name[:2]
|
|
_entry_name = ' '.join(_entry_name)
|
|
if _entry_name.startswith('/'):
|
|
_entry_name = _entry_name.replace('/', '', 1)
|
|
if argument.startswith('add note') or argument.startswith('add -n'):
|
|
_shm_folder, _shm_entry = shm_gen()
|
|
system(f"nano {tmp_dir}{_shm_folder}/{_shm_entry}-n")
|
|
_notes = open(f"{tmp_dir}{_shm_folder}/{_shm_entry}-n", 'r').read()
|
|
open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'w').writelines('\n\n\n\n' + _notes + '\n\n')
|
|
elif argument.startswith('add password') or argument.startswith('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) ')
|
|
_shm_folder, _shm_entry = shm_gen()
|
|
if _add_note.lower() == 'y':
|
|
system(f"nano {tmp_dir}{_shm_folder}/{_shm_entry}-n")
|
|
_notes = open(f"{tmp_dir}{_shm_folder}/{_shm_entry}-n", 'r').read()
|
|
else:
|
|
_notes = ''
|
|
open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'w').writelines(_username + '\n' + _password + '\n' + _url +
|
|
'\n\n' + _notes + '\n\n')
|
|
print('\n\u001b[38;5;0;48;5;15mEntry preview:\u001b[0m\n\n' + open(f"{tmp_dir}{_shm_folder}/{_shm_entry}",
|
|
'r').read())
|
|
encrypt(_shm_folder, _shm_entry, _entry_name)
|
|
|
|
|
|
def add_folder(): # creates a new folder
|
|
if argument == 'add folder' or argument == 'add -f':
|
|
_folder_name = str(input('Name of new folder: '))
|
|
else:
|
|
_folder_name = argument.split(' ')
|
|
del _folder_name[:2]
|
|
_folder_name = ' '.join(_folder_name)
|
|
if _folder_name.startswith('/'):
|
|
_folder_name = _folder_name.replace('/', '', 1)
|
|
Path(directory + _folder_name).mkdir(0o700)
|
|
system(f"ssh -i '{path.expanduser('~/.ssh/sshyp')}' -p {port} {username_ssh}@{ip} \"mkdir -p '{directory_ssh}"
|
|
f"{_folder_name}'\"")
|
|
|
|
|
|
def rename(): # renames an entry or folder
|
|
if argument == 'edit rename' or argument == 'edit relocate' or argument == 'edit -r':
|
|
entry_list_gen()
|
|
_entry_name = str(input('What would you like to rename/relocate? '))
|
|
else:
|
|
_entry_name = argument.split(' ')
|
|
del _entry_name[:2]
|
|
_entry_name = ' '.join(_entry_name)
|
|
_new_name = str(input('New Name: '))
|
|
if _entry_name.startswith('/'):
|
|
_entry_name = _entry_name.replace('/', '', 1)
|
|
if _entry_name.endswith('/'):
|
|
move(f"{directory}{_entry_name}", f"{directory}{_new_name}")
|
|
system(f"ssh -i '{path.expanduser('~/.ssh/sshyp')}' -p {port} {username_ssh}@{ip} \"mkdir -p '{directory_ssh}"
|
|
f"{_new_name}'\"")
|
|
else:
|
|
move(f"{directory}{_entry_name}.gpg", f"{directory}{_new_name}.gpg")
|
|
system(f"ssh -i '{path.expanduser('~/.ssh/sshyp')}' -p {port} {username_ssh}@{ip} \"python -c 'import sshypRemote; "
|
|
f"sshypRemote.delete(\"'\"{_entry_name}\"'\")'\"")
|
|
|
|
|
|
def edit(): # edits the contents of an entry
|
|
_shm_folder, _shm_entry = None, None # sets base-line values to avoid errors
|
|
if 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':
|
|
entry_list_gen()
|
|
_entry_name = str(input('What file would you like to edit? '))
|
|
else:
|
|
_entry_name = argument.split(' ')
|
|
del _entry_name[:2]
|
|
_entry_name = ' '.join(_entry_name)
|
|
if _entry_name.startswith('/'):
|
|
_entry_name = _entry_name.replace('/', '', 1)
|
|
if not Path(f"{directory}{_entry_name}.gpg").is_file():
|
|
print("\nYou are trying to edit a file that does not exist!\n")
|
|
s_exit()
|
|
if argument.startswith('edit username') or argument.startswith('edit -u'):
|
|
_detail = str(input('New Username: '))
|
|
_shm_folder, _shm_entry = shm_gen()
|
|
system(f"gpg -qd --output {tmp_dir}{_shm_folder}/{_shm_entry} '{directory}{_entry_name}.gpg'")
|
|
replace_line(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 0, _detail + '\n')
|
|
elif argument.startswith('edit password') or argument.startswith('edit -p'):
|
|
_detail = str(input('New Password: '))
|
|
_shm_folder, _shm_entry = shm_gen()
|
|
system(f"gpg -qd --output {tmp_dir}{_shm_folder}/{_shm_entry} '{directory}{_entry_name}.gpg'")
|
|
replace_line(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 1, _detail + '\n')
|
|
elif argument.startswith('edit url') or argument.startswith('edit -l'):
|
|
_detail = str(input('New URL: '))
|
|
_shm_folder, _shm_entry = shm_gen()
|
|
system(f"gpg -qd --output {tmp_dir}{_shm_folder}/{_shm_entry} '{directory}{_entry_name}.gpg'")
|
|
replace_line(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 2, _detail + '\n')
|
|
elif argument.startswith('edit note') or argument.startswith('edit -n'):
|
|
_shm_folder, _shm_entry = shm_gen()
|
|
system(f"gpg -qd --output {tmp_dir}{_shm_folder}/{_shm_entry} '{directory}{_entry_name}.gpg'")
|
|
edit_note(_shm_folder, _shm_entry)
|
|
remove(f"{directory}{_entry_name}.gpg")
|
|
print('\n\u001b[38;5;0;48;5;15mEntry preview:\u001b[0m\n\n' + open(f"{tmp_dir}{_shm_folder}/{_shm_entry}",
|
|
'r').read())
|
|
encrypt(_shm_folder, _shm_entry, _entry_name)
|
|
|
|
|
|
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
|
|
if argument == 'gen update' or argument == 'gen -u' or argument == 'gen':
|
|
if argument == 'gen update' or argument == 'gen -u':
|
|
entry_list_gen()
|
|
else:
|
|
print()
|
|
_entry_name = str(input('Name of service: '))
|
|
elif argument.startswith('gen update') or argument.startswith('gen -u'):
|
|
_entry_name = argument.split(' ')
|
|
del _entry_name[:2]
|
|
_entry_name = ' '.join(_entry_name)
|
|
else:
|
|
_entry_name = argument.split(' ')
|
|
del _entry_name[:1]
|
|
_entry_name = ' '.join(_entry_name)
|
|
if _entry_name.startswith('/'):
|
|
_entry_name = _entry_name.replace('/', '', 1)
|
|
if argument.startswith('gen update') or argument.startswith('gen -u'):
|
|
if not Path(f"{directory}{_entry_name}.gpg").is_file():
|
|
print("\nYou are trying to edit a file that does not exist!\n")
|
|
s_exit()
|
|
else:
|
|
_username = str(input('Username: '))
|
|
_password = pass_gen()
|
|
if not argument.startswith('gen update') and not argument.startswith('gen -u'):
|
|
_url = str(input('URL: '))
|
|
_add_note = input('Add a note to this entry? (y/N) ')
|
|
_shm_folder, _shm_entry = shm_gen()
|
|
if _add_note.lower() == 'y':
|
|
system(f"nano {tmp_dir}{_shm_folder}/{_shm_entry}-n")
|
|
_notes = open(f"{tmp_dir}{_shm_folder}/{_shm_entry}-n", 'r').read()
|
|
else:
|
|
_notes = ''
|
|
open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'w').writelines(_username + '\n' + _password + '\n' + _url + '\n\n'
|
|
+ _notes + '\n\n')
|
|
print('\n\u001b[38;5;0;48;5;15mEntry preview:\u001b[0m\n\n' + open(f"{tmp_dir}{_shm_folder}/{_shm_entry}",
|
|
'r').read())
|
|
encrypt(_shm_folder, _shm_entry, _entry_name)
|
|
else:
|
|
_shm_folder, _shm_entry = shm_gen()
|
|
system(f"gpg -qd --output {tmp_dir}{_shm_folder}/{_shm_entry} '{directory}{_entry_name}.gpg'")
|
|
replace_line(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 1, _password + '\n')
|
|
remove(f"{directory}{_entry_name}.gpg")
|
|
print('\n\u001b[38;5;0;48;5;15mEntry preview:\u001b[0m\n\n' + open(f"{tmp_dir}{_shm_folder}/{_shm_entry}",
|
|
'r').read())
|
|
encrypt(_shm_folder, _shm_entry, _entry_name)
|
|
|
|
|
|
def copy_data(): # copies a specified field of an entry to the clipboard
|
|
if 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':
|
|
entry_list_gen()
|
|
_read_entry = str(input('Entry to copy: '))
|
|
else:
|
|
_read_entry = argument.split(' ')
|
|
del _read_entry[:2]
|
|
_read_entry = ' '.join(_read_entry)
|
|
if not Path(f"{directory}{_read_entry}.gpg").is_file():
|
|
print("\nThe file does not exist!\n")
|
|
s_exit()
|
|
_shm_folder, _shm_entry = shm_gen()
|
|
system(f"gpg -qd --output {tmp_dir}{_shm_folder}/{_shm_entry} '{directory}{_read_entry}.gpg'")
|
|
_copy_line = open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'r').readlines()
|
|
if Path("/data/data/com.termux").exists(): # checks for Termux, Wayland, or X
|
|
if argument.startswith('copy username') or argument.startswith('copy -u'):
|
|
system('termux-clipboard-set ' + "'" + _copy_line[0].replace('\n', '').replace("'", "'\\''") + "'")
|
|
elif argument.startswith('copy password') or argument.startswith('copy -p'):
|
|
system('termux-clipboard-set ' + "'" + _copy_line[1].replace('\n', '').replace("'", "'\\''") + "'")
|
|
elif argument.startswith('copy url') or argument.startswith('copy -l'):
|
|
system('termux-clipboard-set ' + "'" + _copy_line[2].replace('\n', '').replace("'", "'\\''") + "'")
|
|
elif argument.startswith('copy note') or argument.startswith('copy -n'):
|
|
system('termux-clipboard-set ' + "'" + _copy_line[4].replace('\n', '').replace("'", "'\\''") + "'")
|
|
elif environ.get('WAYLAND_DISPLAY') == 'wayland-0':
|
|
if argument.startswith('copy username') or argument.startswith('copy -u'):
|
|
system('wl-copy ' + "'" + _copy_line[0].replace('\n', '').replace("'", "'\\''") + "'")
|
|
elif argument.startswith('copy password') or argument.startswith('copy -p'):
|
|
system('wl-copy ' + "'" + _copy_line[1].replace('\n', '').replace("'", "'\\''") + "'")
|
|
elif argument.startswith('copy url') or argument.startswith('copy -l'):
|
|
system('wl-copy ' + "'" + _copy_line[2].replace('\n', '').replace("'", "'\\''") + "'")
|
|
elif argument.startswith('copy note') or argument.startswith('copy -n'):
|
|
system('wl-copy ' + "'" + _copy_line[4].replace('\n', '').replace("'", "'\\''") + "'")
|
|
else:
|
|
if argument.startswith('copy username') or argument.startswith('copy -u'):
|
|
system('echo -n ' + "'" + _copy_line[0].replace('\n', '').replace("'", "'\\''") + "'" + ' | xclip -sel c')
|
|
elif argument.startswith('copy password') or argument.startswith('copy -p'):
|
|
system('echo -n ' + "'" + _copy_line[1].replace('\n', '').replace("'", "'\\''") + "'" + ' | xclip -sel c')
|
|
elif argument.startswith('copy url') or argument.startswith('copy -l'):
|
|
system('echo -n ' + "'" + _copy_line[2].replace('\n', '').replace("'", "'\\''") + "'" + ' | xclip -sel c')
|
|
elif argument.startswith('copy note') or argument.startswith('copy -n'):
|
|
system('echo -n ' + "'" + _copy_line[4].replace('\n', '').replace("'", "'\\''") + "'" + ' | xclip -sel c')
|
|
rmtree(f"{tmp_dir}{_shm_folder}")
|
|
|
|
|
|
def remove_data(): # deletes an entry from the server and flags it for local deletion on sync
|
|
if argument == 'shear' or argument == '-rm':
|
|
entry_list_gen()
|
|
_entry_name = str(input('What would you like to delete? '))
|
|
else:
|
|
_entry_name = argument.split(' ')
|
|
del _entry_name[:1]
|
|
_entry_name = ' '.join(_entry_name)
|
|
if _entry_name.startswith('/'):
|
|
_entry_name = _entry_name.replace('/', '', 1)
|
|
try:
|
|
system(f"gpg -qd --output {tmp_dir}sshyp.lock {path.expanduser('~/.config/sshyp/lock.gpg')}")
|
|
_unlock = open(f"{tmp_dir}sshyp.lock", 'r').readlines()
|
|
remove(f"{tmp_dir}sshyp.lock")
|
|
except FileNotFoundError:
|
|
print('\nAccess denied.\n')
|
|
s_exit()
|
|
system(f"ssh -i '{path.expanduser('~/.ssh/sshyp')}' -p {port} {username_ssh}@{ip} \"python -c 'import sshypRemote"
|
|
f"; sshypRemote.delete(\"'\"{_entry_name}\"'\")'\"")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
silent_sync = 0
|
|
# retrieve typed argument
|
|
argument_list, argument = argv, ''
|
|
del argument_list[0]
|
|
for _argument in argument_list:
|
|
argument += _argument + ' '
|
|
argument = argument[:-1]
|
|
|
|
# import saved userdata
|
|
if argument != 'tweak':
|
|
device_type = ''
|
|
tmp_dir = path.expanduser('~/.config/sshyp/tmp/')
|
|
try:
|
|
device_type = open(path.expanduser('~/.config/sshyp/sshyp-device')).read().strip()
|
|
if device_type == 'c':
|
|
gpg_id = open(path.expanduser('~/.config/sshyp/sshyp-gpg')).read().strip()
|
|
ssh_info = sshync.get_profile(path.expanduser('~/.config/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/'
|
|
client_device_name = listdir(path.expanduser('~/.config/sshyp/devices'))[0]
|
|
except FileNotFoundError:
|
|
if device_type.lower() != 's':
|
|
print('\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
|
|
print("Not all necessary configuration files are present. Please run 'sshyp tweak'!")
|
|
print('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n')
|
|
s_exit()
|
|
else:
|
|
try:
|
|
tweak()
|
|
s_exit()
|
|
except KeyboardInterrupt:
|
|
print('\n')
|
|
s_exit()
|
|
|
|
# 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 == 'help' or argument == '--help' or argument == '-h' or argument == 'license' or \
|
|
argument == 'version' or argument == '-v' or argument == 'add' or argument == 'edit' or argument == 'copy':
|
|
print_info()
|
|
elif argument.startswith('add note') or argument.startswith('add -n') or argument.startswith('add password') \
|
|
or argument.startswith('add -p'):
|
|
try:
|
|
add_entry()
|
|
except KeyboardInterrupt:
|
|
print('\n')
|
|
s_exit()
|
|
elif argument.startswith('add folder') or argument.startswith('add -f'):
|
|
try:
|
|
add_folder()
|
|
except KeyboardInterrupt:
|
|
print('\n')
|
|
s_exit()
|
|
elif argument.startswith('edit rename') or argument.startswith('edit relocate') or argument.startswith('edit -r'):
|
|
silent_sync = 1
|
|
try:
|
|
rename()
|
|
except KeyboardInterrupt:
|
|
print('\n')
|
|
s_exit()
|
|
elif argument.startswith('edit username') or argument.startswith('edit -u') or argument.startswith('edit password')\
|
|
or argument.startswith('edit -p') or argument.startswith('edit url') or argument.startswith('edit -l') or \
|
|
argument.startswith('edit note') or argument.startswith('edit -n'):
|
|
try:
|
|
edit()
|
|
except KeyboardInterrupt:
|
|
print('\n')
|
|
s_exit()
|
|
elif argument.startswith('gen'):
|
|
try:
|
|
gen()
|
|
except KeyboardInterrupt:
|
|
print('\n')
|
|
s_exit()
|
|
elif argument.startswith('copy username') or argument.startswith('copy -u') or argument.startswith('copy password')\
|
|
or argument.startswith('copy -p') or argument.startswith('copy url') or argument.startswith('copy -l')\
|
|
or argument.startswith('copy note') or argument.startswith('copy -n'):
|
|
try:
|
|
copy_data()
|
|
except KeyboardInterrupt:
|
|
print('\n')
|
|
s_exit()
|
|
elif argument.startswith('shear') or argument.startswith('-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 not argument.\
|
|
startswith('copy username') and not argument.startswith('copy -u') and not argument.\
|
|
startswith('copy password') and not argument.startswith('copy -p') and not argument.\
|
|
startswith('copy url') and not argument.startswith('copy -l') and not argument.\
|
|
startswith('copy note') and not argument.startswith('copy -n') and argument != 'copy' and argument \
|
|
!= 'version' and argument != '-v' and error == 0 and argument.startswith('/') is False:
|
|
sync()
|