Added basic functionality for new tweak menu + clean-up and optimization of all config menus

Former-commit-id: 59ecc6e80136bd1482aee48cff1b2b1167679836
Former-commit-id: 26043f564acf54fb97bdb3036ce48d57ee786f97
This commit is contained in:
2023-05-13 15:53:46 -04:00
parent ebf58b7770
commit 60d3a674c4
+107 -95
View File
@@ -1,37 +1,36 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from curses import curs_set, newwin, A_REVERSE, KEY_UP, KEY_DOWN, noecho, nocbreak, endwin, initscr, cbreak from curses import A_REVERSE, KEY_DOWN, KEY_UP, cbreak, curs_set, endwin, initscr, newwin, noecho, nocbreak
from curses.textpad import rectangle, Textbox from curses.textpad import rectangle, Textbox
from os import environ, listdir, remove, symlink from os import environ, listdir, remove, symlink
from os.path import exists, expanduser, isfile
from pathlib import Path from pathlib import Path
from random import randint from random import randint
from re import sub from re import sub
from shutil import get_terminal_size, which from shutil import get_terminal_size, which
from sshync import make_profile from sshync import make_profile
from sshyp import copy_id_check, string_gen from sshyp import copy_id_check, string_gen
from subprocess import run, PIPE from subprocess import PIPE, run
# PORT START UNAME-IMPORT # PORT START UNAME-IMPORT
from os import uname from os import uname
from os.path import exists, expanduser, isfile
# PORT END UNAME-IMPORT # PORT END UNAME-IMPORT
home = expanduser("~") home, stdscr, sshyp_data = expanduser("~"), initscr(), []
sshyp_data = []
# creates a radio selection between the provided options # creates a radio selection between the provided options
def settings_radio(_stdscr, _options, _pretext): def curses_radio(_options, _pretext):
curs_set(0) curs_set(0)
_selected = 0 _selected = 0
while True: while True:
_stdscr.clear() stdscr.clear()
_stdscr.addstr(0, 0, _pretext) stdscr.addstr(0, 0, _pretext)
for _i, _option in enumerate(_options): for _i, _option in enumerate(_options):
_y = _i + 2 _y = _i + 2
if _i == _selected: if _i == _selected:
_stdscr.addstr(_y, 0, "[*] " + _option, A_REVERSE) stdscr.addstr(_y, 0, "[*] " + _option, A_REVERSE)
else: else:
_stdscr.addstr(_y, 0, "[ ] " + _option) stdscr.addstr(_y, 0, "[ ] " + _option)
_stdscr.refresh() stdscr.refresh()
_key = _stdscr.getch() _key = stdscr.getch()
# update _selected based on user input # update _selected based on user input
if _key == KEY_UP: if _key == KEY_UP:
_selected = (_selected - 1) % len(_options) _selected = (_selected - 1) % len(_options)
@@ -39,19 +38,19 @@ def settings_radio(_stdscr, _options, _pretext):
_selected = (_selected + 1) % len(_options) _selected = (_selected + 1) % len(_options)
elif _key == ord('\n'): elif _key == ord('\n'):
break break
_stdscr.refresh() stdscr.refresh()
curs_set(1) curs_set(1)
return _selected return _selected
# creates a text-box input # creates a text-box input
def settings_text(_stdscr, _pretext): def curses_text(_pretext):
_stdscr.clear() stdscr.clear()
_stdscr.addstr(0, 0, _pretext) stdscr.addstr(0, 0, _pretext)
_term_columns = get_terminal_size()[0] _term_columns = get_terminal_size()[0]
_editwin = newwin(1, _term_columns - 2, 3, 1) _editwin = newwin(1, _term_columns - 2, 3, 1)
rectangle(_stdscr, 2, 0, 4, _term_columns - 1) rectangle(stdscr, 2, 0, 4, _term_columns - 1)
_stdscr.refresh() stdscr.refresh()
_box = Textbox(_editwin) _box = Textbox(_editwin)
# let the user edit until ctrl+g/enter is struck # let the user edit until ctrl+g/enter is struck
_box.edit() _box.edit()
@@ -60,36 +59,36 @@ def settings_text(_stdscr, _pretext):
# cleanly exit curses # cleanly exit curses
def settings_terminate(_stdscr): def curses_terminate():
nocbreak() nocbreak()
_stdscr.keypad(False) # TODO Needed??
noecho()
endwin() endwin()
# device+sync type selection # device+sync type selection
def install_type(_stdscr): def install_type():
_offline_mode = False _offline_mode = False
# PORT START TWEAK-DEVTYPE # PORT START TWEAK-DEVTYPE
_install_type = settings_radio(_stdscr, ('server', 'client (ssh-synchronized)', 'client (offline)'), _install_type = curses_radio(('server', 'client (ssh-synchronized)', 'client (offline)'),
'device + sync type configuration') 'device + sync type configuration')
if _install_type == 0: if _install_type == 0:
_dev_type = 'server' _dev_type = 'server'
Path(f"{home}/.config/sshyp/deleted").mkdir(mode=0o700, exist_ok=True) Path(f"{home}/.config/sshyp/deleted").mkdir(mode=0o700, exist_ok=True)
Path(f"{home}/.config/sshyp/whitelist").mkdir(mode=0o700, exist_ok=True) Path(f"{home}/.config/sshyp/whitelist").mkdir(mode=0o700, exist_ok=True)
settings_terminate(_stdscr) curses_terminate()
print(f"\nmake sure the ssh service is running and properly configured") print(f"\nmake sure the ssh service is running and properly configured")
else: else:
_dev_type = 'client' _dev_type = 'client'
if _install_type == 2: if _install_type == 2:
_offline_mode = True _offline_mode = True
if isfile(f"{home}/.config/sshyp/sshyp.sshync"):
remove(f"{home}/.config/sshyp/sshyp.sshync")
Path(f"{home}/.local/share/sshyp").mkdir(mode=0o700, parents=True, exist_ok=True) Path(f"{home}/.local/share/sshyp").mkdir(mode=0o700, parents=True, exist_ok=True)
# PORT END TWEAK-DEVTYPE # PORT END TWEAK-DEVTYPE
return _dev_type, _offline_mode return _dev_type, _offline_mode
# gpg configuration # gpg configuration
def gpg_config(_stdscr): def gpg_config():
# gpg key selection # gpg key selection
_uid_list = [_item for _item in run(['gpg', '-k', '--with-colons'], _uid_list = [_item for _item in run(['gpg', '-k', '--with-colons'],
stdout=PIPE, text=True).stdout.splitlines() if _item.startswith('uid')] stdout=PIPE, text=True).stdout.splitlines() if _item.startswith('uid')]
@@ -97,7 +96,7 @@ def gpg_config(_stdscr):
for _uid in _uid_list: for _uid in _uid_list:
_clean_uid_list.append(sub(r':+', ':', _uid).split(':')[4]) _clean_uid_list.append(sub(r':+', ':', _uid).split(':')[4])
_clean_uid_list.append('auto-generate') _clean_uid_list.append('auto-generate')
_gpg_id_sel = settings_radio(_stdscr, _clean_uid_list, 'gpg key selection') _gpg_id_sel = curses_radio(_clean_uid_list, 'gpg key selection')
_gpg_id = _clean_uid_list[_gpg_id_sel] _gpg_id = _clean_uid_list[_gpg_id_sel]
if _gpg_id == 'auto-generate': if _gpg_id == 'auto-generate':
print('\na unique gpg key is being generated for you...') print('\na unique gpg key is being generated for you...')
@@ -113,19 +112,19 @@ def gpg_config(_stdscr):
# text editor configuration # text editor configuration
def editor_config(_stdscr): def editor_config():
_editor = settings_text(_stdscr, 'enter the name of your preferred text editor:\n\n\n\n\n' _editor = curses_text('enter the name of your preferred text editor:\n\n\n\n\n'
'(ctrl+g/enter to confirm)\n\nexample input: vim') '(ctrl+g/enter to confirm)\n\nexample input: vim')
return _editor return _editor
# ssh+sshync configuration # ssh+sshync configuration
def ssh_config(_stdscr): def ssh_config():
# ssh+sshync configuration # ssh+sshync configuration
_uiport = settings_text(_stdscr, 'enter the username, ip, and ssh port of your sshyp server:\n\n\n\n\n(' _uiport = curses_text('enter the username, ip, and ssh port of your sshyp server:\n\n\n\n\n('
'ctrl+g/enter to confirm)\n\nexample inputs:\n\n ipv4: user@10.10.10.' 'ctrl+g/enter to confirm)\n\nexample inputs:\n\n ipv4: user@10.10.10.'
'10:22\n ipv6: user@[2000:2000:2000:2000:2000:2000:2000:2000]:22\n ' '10:22\n ipv6: user@[2000:2000:2000:2000:2000:2000:2000:2000]:22\n '
'domain: user@mydomain.com:22').lstrip('[').replace(']', '') 'domain: user@mydomain.com:22').lstrip('[').replace(']', '')
_uiport_split = _uiport.split('@') _uiport_split = _uiport.split('@')
_username_ssh = _uiport_split[0] _username_ssh = _uiport_split[0]
_iport = _uiport_split[1].rsplit(':', 1) _iport = _uiport_split[1].rsplit(':', 1)
@@ -138,22 +137,24 @@ def ssh_config(_stdscr):
# device id configuration # device id configuration
def dev_id_config(_stdscr): def dev_id_config(_ip, _username_ssh, _port):
for _id in listdir(f"{home}/.config/sshyp/devices"): for _id in listdir(f"{home}/.config/sshyp/devices"):
remove(f"{home}/.config/sshyp/devices/{_id}") remove(f"{home}/.config/sshyp/devices/{_id}")
_device_id_prefix = settings_text(_stdscr, 'name this device:\n\n\n\n\n(ctrl+g/enter to confirm)\n\n' _device_id_prefix = curses_text('name this device:\n\n\n\n\n(ctrl+g/enter to confirm)\n\n'
'important:\u001b[0m this id \u001b[4;1mmust\u001b[0m be ' 'important:\u001b[0m this id \u001b[4;1mmust\u001b[0m be '
'unique amongst your client devices\n\nthis is used to keep ' 'unique amongst your client devices\n\nthis is used to keep '
'track of database syncing and quick-unlock permissions\n') 'track of database syncing and quick-unlock permissions\n')
_device_id_suffix = string_gen('f', randint(24, 48)) _device_id_suffix = string_gen('f', randint(24, 48))
_device_id = _device_id_prefix + '-' + _device_id_suffix _device_id = _device_id_prefix + '-' + _device_id_suffix
open(f"{home}/.config/sshyp/devices/{_device_id}", 'w') open(f"{home}/.config/sshyp/devices/{_device_id}", 'w')
# test server connection and attempt to register device id
copy_id_check(_ip, _username_ssh, _port, _device_id)
return _device_id return _device_id
# quick-unlock configuration # quick-unlock configuration
def quick_unlock_config(_stdscr): def quick_unlock_config():
_quick_unlock_sel = settings_radio(_stdscr, ('yes', 'no'), 'enable quick-unlock?') _quick_unlock_sel = curses_radio(('yes', 'no'), 'enable quick-unlock?')
if _quick_unlock_sel == 0: if _quick_unlock_sel == 0:
_enabled = 'yes' _enabled = 'yes'
else: else:
@@ -161,6 +162,45 @@ def quick_unlock_config(_stdscr):
return _enabled return _enabled
# runs secondary configuration menu - clients only
def global_menu(_post_setup):
# curses initialization
noecho()
cbreak()
stdscr.keypad(True)
_options, _choice = [], 4
try:
if not _post_setup:
_options.extend(['change device/synchronization types', 'change gpg key', 're-configure ssh',
'change device name'])
_message, _choice = 'all configuration options:', 0
_options.extend(['[OPTIONAL, RECOMMENDED] set custom text editor',
'[OPTIONAL, RECOMMENDED] enable/disable quick-unlock',
'[OPTIONAL, NOT IMPLEMENTED] su security mode',
'[OPTIONAL, NOT IMPLEMENTED] extensions and updates', 'exit/done'])
_message = 'required configuration complete - additional configuration options:'
_choice += curses_radio(_options, _message)
if _choice == 0:
install_type()
elif _choice == 1:
gpg_config()
elif _choice == 2:
ssh_config()
elif _choice == 3:
dev_id_config()
elif _choice == 4:
editor_config()
elif _choice == 5:
quick_unlock_config()
else:
pass
curses_terminate()
except KeyboardInterrupt:
curses_terminate()
# runs initial configuration wizard # runs initial configuration wizard
def initial_setup(): def initial_setup():
# config directory creation # config directory creation
@@ -178,21 +218,19 @@ def initial_setup():
# PORT END UNAME-TMP # PORT END UNAME-TMP
# curses initialization # curses initialization
_stdscr = initscr()
noecho()
cbreak() cbreak()
_stdscr.keypad(True) stdscr.keypad(True)
# curses menu tree # curses menu tree
try: try:
# device+sync type selection # device+sync type selection
_dev_sync_types = install_type(_stdscr) _dev_sync_types = install_type()
sshyp_data.append(_dev_sync_types[0]) sshyp_data.append(_dev_sync_types[0])
if _dev_sync_types[0] == 'client': if _dev_sync_types[0] == 'client':
# gpg configuration # gpg configuration
sshyp_data.append(gpg_config(_stdscr)) sshyp_data.append(gpg_config())
# lock file generation (requires gpg configuration) # lock file generation (requires gpg configuration)
if isfile(f"{home}/.config/sshyp/lock.gpg"): if isfile(f"{home}/.config/sshyp/lock.gpg"):
@@ -201,32 +239,27 @@ def initial_setup():
run(['gpg', '-qr', str(sshyp_data[1]), '-e', f"{home}/.config/sshyp/lock"]) run(['gpg', '-qr', str(sshyp_data[1]), '-e', f"{home}/.config/sshyp/lock"])
remove(f"{home}/.config/sshyp/lock") remove(f"{home}/.config/sshyp/lock")
# text editor configuration # set default text editor to value of EDITOR environment variable, otherwise default to nano
sshyp_data.append(editor_config(_stdscr)) if 'EDITOR' in environ:
sshyp_data.append(environ['EDITOR'])
else:
sshyp_data.append('nano')
# online (synchronized mode) configuration # online (synchronized mode) configuration
if not _dev_sync_types[1]: if not _dev_sync_types[1]:
# ssh+sshync configuration # ssh+sshync configuration
_ip, _username_ssh, _port = ssh_config(_stdscr) _ip, _username_ssh, _port = ssh_config()
# device id configuration # device id configuration
_device_id = dev_id_config(_stdscr) _device_id = dev_id_config(_ip, _username_ssh, _port)
# quick-unlock configuration
sshyp_data.append(quick_unlock_config(_stdscr))
# cleanly exit curses # cleanly exit curses
settings_terminate(_stdscr) curses_terminate()
# test server connection and attempt to register device id
copy_id_check(_ip, _username_ssh, _port, _device_id)
else: else:
if isfile(f"{home}/.config/sshyp/sshyp.sshync"):
remove(f"{home}/.config/sshyp/sshyp.sshync")
# cleanly exit curses # cleanly exit curses
settings_terminate(_stdscr) curses_terminate()
# PORT START CLIPTOOL # PORT START CLIPTOOL
# check for clipboard tool and display warning if missing # check for clipboard tool and display warning if missing
@@ -241,39 +274,18 @@ def initial_setup():
f'"{_clipboard_package}" is installed\u001b[0m') f'"{_clipboard_package}" is installed\u001b[0m')
# PORT END CLIPTOOL # PORT END CLIPTOOL
# write main config file (sshyp-data)
with open(f"{home}/.config/sshyp/sshyp-data", 'w') as _config_file:
_lines = 0
for _item in sshyp_data:
_lines += 1
_config_file.write(str(_item) + '\n')
while _lines < 4:
_lines += 1
_config_file.write('n')
global_menu(True)
except KeyboardInterrupt: except KeyboardInterrupt:
# cleanly exit curses # cleanly exit curses
settings_terminate(_stdscr) curses_terminate()
# write main config file (sshyp-data)
with open(f"{home}/.config/sshyp/sshyp-data", 'w') as _config_file:
_lines = 0
for _item in sshyp_data:
_lines += 1
_config_file.write(str(_item) + '\n')
while _lines < 4:
_lines += 1
_config_file.write('n')
print('\nconfiguration complete\n')
# runs secondary configuration menu
def global_menu(_post_setup):
# curses initialization
_stdscr = initscr()
noecho()
cbreak()
_stdscr.keypad(True)
# a client device is assumed, the server should never show this menu
_options = []
if not _post_setup:
_options = ['change device/synchronization types', 'change gpg key', 're-configure ssh', 'change device name']
_message = 'all configuration options'
_options.extend(['[OPTIONAL, RECOMMENDED] set custom text editor',
'[OPTIONAL, RECOMMENDED] enable/disable quick-unlock',
'[OPTIONAL, NOT IMPLEMENTED] su security mode',
'[OPTIONAL, NOT IMPLEMENTED] extensions and updates', 'exit'])
_message = 'additional configuration options'
_choice = settings_radio(_stdscr, _options, _message)