mirror of
https://github.com/rwinkhart/sshyp.git
synced 2026-09-03 07:37:16 -04:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f5a2c8f3c | ||
|
|
386044d081 | ||
|
|
c651c41a41 | ||
|
|
7cb245edde | ||
|
|
edf31f9a7c | ||
|
|
362b1aa025 | ||
|
|
5633520d82 | ||
|
|
a487bd561a | ||
|
|
cbc0538e82 | ||
|
|
5c589de4f2 | ||
|
|
44392467e5 | ||
|
|
18aacf648b | ||
|
|
ac621a8a73 | ||
|
|
98f76ac1ab | ||
|
|
bdff85f024 | ||
|
|
2bcabe8597 | ||
|
|
7259a8732c | ||
|
|
20cdc27b88 | ||
|
|
2f6bdb8d2e | ||
|
|
eb4fae3e9e | ||
|
|
30afbafcbf | ||
|
|
e010180219 | ||
|
|
1f4e7d5cae | ||
|
|
67983da22e | ||
|
|
aa0bd017c9 | ||
|
|
5a4e952d4d | ||
|
|
db05e9b05e | ||
|
|
397ad7d409 | ||
|
|
5af02866b4 | ||
|
|
e8267f838a | ||
|
|
e4fbcd6825 | ||
|
|
9ba4b8d724 | ||
|
|
0b73d4016f |
@@ -1,7 +1,7 @@
|
|||||||

|

|
||||||
|
|
||||||
[](https://github.com/rwinkhart/sshyp/releases)
|
[](https://github.com/rwinkhart/sshyp/releases)
|
||||||

|

|
||||||
[](https://github.com/rwinkhart/sshyp/releases)
|
[](https://github.com/rwinkhart/sshyp/releases)
|
||||||
|
|
||||||
[](https://github.com/rwinkhart/sshyp/actions/workflows/codeql-analysis.yml)
|
[](https://github.com/rwinkhart/sshyp/actions/workflows/codeql-analysis.yml)
|
||||||
@@ -77,9 +77,10 @@ All available options can be found with `sshyp help`, or alternatively, in the m
|
|||||||
# Roadmap
|
# Roadmap
|
||||||
Short-term Goals:
|
Short-term Goals:
|
||||||
|
|
||||||
- migrate from gpg to a better-suited utility focused on symmetric cipher encryption
|
- implement a method for tracking how long it has been since a password has been changed (password aging)
|
||||||
|
- find and fix more bugs
|
||||||
|
|
||||||
Long-term Goals:
|
Long-term Goals:
|
||||||
|
|
||||||
- improve OS compatibility
|
- migrate from gpg to a better-suited utility focused on symmetric cipher encryption
|
||||||
- seize the thrones, shear the humans
|
- seize the thrones, shear the humans
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
git add -f extra lib/sshyp.py lib/sshync.py lib/stweak.py port-jobs share LICENSE README.md package.sh commit.sh .gitignore
|
git add -f extra lib/sshyp.py lib/sshync.py lib/stweak.py lib/clipclear.py port-jobs share LICENSE README.md package.sh commit.sh .gitignore
|
||||||
git commit -m "$1"
|
git commit -m "$1"
|
||||||
git push
|
git push
|
||||||
|
|||||||
@@ -1,3 +1,34 @@
|
|||||||
|
sshyp v1.5.1
|
||||||
|
10/24/2023
|
||||||
|
|
||||||
|
the fortified flock update - patch one
|
||||||
|
|
||||||
|
this release fixes both long and short-standing bugs that managed to escape discovery in testing;
|
||||||
|
it also addresses some issues that first-time users may be put-off by upon encountering
|
||||||
|
|
||||||
|
user-facing features:
|
||||||
|
|
||||||
|
- the clipboard is now only cleared if the contents of the clipboard still match what was placed onto it by sshyp
|
||||||
|
^ this means that sshyp will no longer remove data from the clipboard that it did not place there
|
||||||
|
- a warning/explanation is now provided when the user attempts to sync in offline mode
|
||||||
|
- a readable error is now provided if the user attempts to edit a note and the specified text editor cannot be found
|
||||||
|
- the optional configuration menu is now explained when encountered during "sshyp init"
|
||||||
|
- the man page now includes common troubleshooting information
|
||||||
|
|
||||||
|
fixes/optimizations:
|
||||||
|
|
||||||
|
- custom, user-provided ssh keyfiles are now properly utilized in all cases
|
||||||
|
^ previously, using custom ssh keyfiles would break various features
|
||||||
|
- clipboard contents are no longer leaked via the process list on Wayland
|
||||||
|
- interpolation has been disabled for ConfigParser, allowing "%" to be stored in configuration files
|
||||||
|
- gpg keys with ":" or "\" in their names are now properly written to the configuration file
|
||||||
|
- copy_id_check() and whitelist_verify() are now safely callable from extensions
|
||||||
|
- the ssh key selection will no longer see "known_hosts.old" as a valid keyfile
|
||||||
|
- when possible, data from the configuration file is now read as boolean values
|
||||||
|
- the Haiku package has been updated to use Python 3.11
|
||||||
|
|
||||||
|
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
||||||
|
|
||||||
sshyp v1.5.0
|
sshyp v1.5.0
|
||||||
07/12/2023
|
07/12/2023
|
||||||
|
|
||||||
|
|||||||
+27
-1
@@ -1,4 +1,4 @@
|
|||||||
.TH sshyp 1 "12 July 2023" "v1.5.0" "sshyp man page"
|
.TH sshyp 1 "24 October 2023" "v1.5.1" "sshyp man page"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
\fBsshyp\fR - Simple, self-hosted, synchronized password management for UNIX(-like) systems. Alternative to (and compatible with) pass/password-store.
|
\fBsshyp\fR - Simple, self-hosted, synchronized password management for UNIX(-like) systems. Alternative to (and compatible with) pass/password-store.
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
@@ -99,6 +99,32 @@ Client setup:
|
|||||||
Done!
|
Done!
|
||||||
|
|
||||||
Please note that the server setup intentionally does not allow the reading of entries (it does not allow adding a gpg decryption key). For security purposes, only clients can read entries.
|
Please note that the server setup intentionally does not allow the reading of entries (it does not allow adding a gpg decryption key). For security purposes, only clients can read entries.
|
||||||
|
.SH TROUBLESHOOTING
|
||||||
|
.B Clipboard not clearing:
|
||||||
|
|
||||||
|
The intended way for sshyp to interact with the system clipboard is for it to clear it 30 seconds after copying a field. Unfortunately, this does not work by default on all systems due to the prevalence of clipboard managers.
|
||||||
|
|
||||||
|
Clipboard managers save a history of what has been copied to the clipboard, which is already a big enough issue on its own for people who copy sensitive information to their clipboard. Some clipboard managers simply will not allow the clipboard to be empty and will replace its contents with the last copied item if you attempt to clear it. One such naughty clipboard manager is KDE Klipper, which comes packaged into KDE Plasma and is typically enabled by default on most distributions. Due to this behavior, KDE Klipper breaks sshyp's clipboard clearing functionality and should not be left enabled.
|
||||||
|
|
||||||
|
It is likely other popular clipboard managers exhibit this behavior as well (Klipper is just the one I first noticed it with). Clipboard managers should not be enabled by default in any environment or distribution due to their potential security implications.
|
||||||
|
|
||||||
|
.B Shell completions not working:
|
||||||
|
ZSH:
|
||||||
|
Make sure your ~/.zshrc contains the following:
|
||||||
|
autoload -Uz compinit && compinit
|
||||||
|
Restart your shell
|
||||||
|
|
||||||
|
Bash:
|
||||||
|
Install your distribution's 'bash-completion' package and restart your shell
|
||||||
|
OR
|
||||||
|
Source the completion script manually by adding the following to your ~/.bashrc (then restart your shell):
|
||||||
|
Linux:
|
||||||
|
source /usr/share/bash-completion/completions/sshyp
|
||||||
|
FreeBSD:
|
||||||
|
source /usr/local/share/bash-completion/completions/sshyp
|
||||||
|
Haiku:
|
||||||
|
source /system/data/bash-completion/completions/sshyp
|
||||||
|
|
||||||
.SH EXIT CODES
|
.SH EXIT CODES
|
||||||
0 - no error
|
0 - no error
|
||||||
|
|
||||||
|
|||||||
Executable
+42
@@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from hashlib import sha512
|
||||||
|
from subprocess import PIPE, run
|
||||||
|
from sys import argv
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
# set period of time to wait before attempting to clear clipboard
|
||||||
|
sleep(30)
|
||||||
|
# enable the storing of clipboard contents hash for later comparison
|
||||||
|
hash_paste = sha512()
|
||||||
|
|
||||||
|
# PORT START CLIPCLEAR
|
||||||
|
if argv[2] == 'wsl':
|
||||||
|
hash_paste.update(run(('powershell.exe', '-c', 'Get-Clipboard'), stdout=PIPE).stdout.strip())
|
||||||
|
if argv[1] == hash_paste.hexdigest():
|
||||||
|
run(('powershell.exe', '-c', 'Set-Clipboard'))
|
||||||
|
|
||||||
|
elif argv[2] == 'wayland':
|
||||||
|
hash_paste.update(run('wl-paste', stdout=PIPE).stdout.strip())
|
||||||
|
if argv[1] == hash_paste.hexdigest():
|
||||||
|
run(('wl-copy', '-c'))
|
||||||
|
|
||||||
|
elif argv[2] == 'haiku':
|
||||||
|
hash_paste.update(run(('clipboard', '-p'), stdout=PIPE).stdout.strip())
|
||||||
|
if argv[1] == hash_paste.hexdigest():
|
||||||
|
run(('clipboard', '-r'))
|
||||||
|
|
||||||
|
elif argv[2] == 'mac':
|
||||||
|
hash_paste.update(run('pbpaste', stdout=PIPE).stdout.strip())
|
||||||
|
if argv[1] == hash_paste.hexdigest():
|
||||||
|
run('pbcopy', input=b'')
|
||||||
|
|
||||||
|
elif argv[2] == 'termux':
|
||||||
|
hash_paste.update(run('termux-clipboard-get', stdout=PIPE).stdout.strip())
|
||||||
|
if argv[1] == hash_paste.hexdigest():
|
||||||
|
run(("termux-clipboard-set", "''"))
|
||||||
|
|
||||||
|
elif argv[2] == 'x11':
|
||||||
|
hash_paste.update(run(('xclip', '-o', '-sel', 'c'), stdout=PIPE).stdout.strip())
|
||||||
|
if argv[1] == hash_paste.hexdigest():
|
||||||
|
run(('xclip', '-i', '/dev/null', '-sel', 'c'))
|
||||||
|
# PORT END CLIPCLEAR
|
||||||
+1
-1
@@ -124,7 +124,7 @@ def sort_titles_mods(_list_1, _list_2):
|
|||||||
# returns a list of data read from a sshync job profile
|
# returns a list of data read from a sshync job profile
|
||||||
def get_profile(_profile_dir):
|
def get_profile(_profile_dir):
|
||||||
from configparser import ConfigParser
|
from configparser import ConfigParser
|
||||||
_profile_data = ConfigParser()
|
_profile_data = ConfigParser(interpolation=None)
|
||||||
if isfile(_profile_dir):
|
if isfile(_profile_dir):
|
||||||
_profile_data.read(_profile_dir)
|
_profile_data.read(_profile_dir)
|
||||||
else:
|
else:
|
||||||
|
|||||||
+51
-37
@@ -123,7 +123,11 @@ def edit_note(_note_lines, _exit_on_match=False):
|
|||||||
with NamedTemporaryFile(mode='w+') as _tmp:
|
with NamedTemporaryFile(mode='w+') as _tmp:
|
||||||
_tmp.write(_joined_note_lines)
|
_tmp.write(_joined_note_lines)
|
||||||
_tmp.seek(0)
|
_tmp.seek(0)
|
||||||
run((editor, _tmp.name))
|
try:
|
||||||
|
run((editor, _tmp.name))
|
||||||
|
except FileNotFoundError:
|
||||||
|
print(f"\n\u001b[38;5;9merror: the configured text editor ({editor}) cannot be found on this system\n\n"
|
||||||
|
f"please either install the editor or re-configure the active editor using 'sshyp tweak'\u001b[0m\n")
|
||||||
_tmp.seek(0)
|
_tmp.seek(0)
|
||||||
_new_note = _tmp.read().rstrip()
|
_new_note = _tmp.read().rstrip()
|
||||||
if _exit_on_match and _joined_note_lines == _new_note:
|
if _exit_on_match and _joined_note_lines == _new_note:
|
||||||
@@ -143,8 +147,8 @@ def decrypt(_entry_dir, _quick_verify=None, _quick_pass=None):
|
|||||||
_contents = None
|
_contents = None
|
||||||
|
|
||||||
# check quick-unlock status, fetch passphrase
|
# check quick-unlock status, fetch passphrase
|
||||||
if _quick_verify == 'true':
|
if _quick_verify:
|
||||||
_quick_pass = whitelist_verify(port, username_ssh, ip, client_device_id)
|
_quick_pass = whitelist_verify(port, username_ssh, ip, client_device_id, identity)
|
||||||
else:
|
else:
|
||||||
if _quick_pass is None:
|
if _quick_pass is None:
|
||||||
_quick_pass = False
|
_quick_pass = False
|
||||||
@@ -180,14 +184,14 @@ def decrypt(_entry_dir, _quick_verify=None, _quick_pass=None):
|
|||||||
|
|
||||||
|
|
||||||
# checks the user's whitelist status and fetches the full gpg key password if possible
|
# checks the user's whitelist status and fetches the full gpg key password if possible
|
||||||
def whitelist_verify(_port, _username_ssh, _ip, _client_device_id):
|
def whitelist_verify(_port, _username_ssh, _ip, _client_device_id, _identity):
|
||||||
try:
|
try:
|
||||||
run(('gpg', '--pinentry-mode', 'cancel', '-qd', '--output', '/dev/null',
|
run(('gpg', '--pinentry-mode', 'cancel', '-qd', '--output', '/dev/null',
|
||||||
f"{home}/.config/sshyp/lock.gpg"), stderr=DEVNULL, check=True)
|
f"{home}/.config/sshyp/lock.gpg"), stderr=DEVNULL, check=True)
|
||||||
return False
|
return False
|
||||||
except CalledProcessError:
|
except CalledProcessError:
|
||||||
_i, _full_password = 0, ''
|
_i, _full_password = 0, ''
|
||||||
_server_whitelist = run(('ssh', '-i', f"{home}/.ssh/sshyp", '-p', _port, f"{_username_ssh}@{_ip}",
|
_server_whitelist = run(('ssh', '-i', _identity, '-p', _port, f"{_username_ssh}@{_ip}",
|
||||||
f'python3 -c \'from os import listdir; print(*listdir("/home/{_username_ssh}'
|
f'python3 -c \'from os import listdir; print(*listdir("/home/{_username_ssh}'
|
||||||
f'/.config/sshyp/whitelist"))\''), stdout=PIPE, text=True).stdout.rstrip().split()
|
f'/.config/sshyp/whitelist"))\''), stdout=PIPE, text=True).stdout.rstrip().split()
|
||||||
for _device_id in _server_whitelist:
|
for _device_id in _server_whitelist:
|
||||||
@@ -195,7 +199,7 @@ def whitelist_verify(_port, _username_ssh, _ip, _client_device_id):
|
|||||||
from getpass import getpass
|
from getpass import getpass
|
||||||
_quick_unlock_password = getpass(prompt='\nquick-unlock pin: ')
|
_quick_unlock_password = getpass(prompt='\nquick-unlock pin: ')
|
||||||
_quick_unlock_password_excluded = \
|
_quick_unlock_password_excluded = \
|
||||||
run(('ssh', '-i', f"{home}/.ssh/sshyp", '-p', _port, f"{_username_ssh}@{_ip}",
|
run(('ssh', '-i', _identity, '-p', _port, f"{_username_ssh}@{_ip}",
|
||||||
f"gpg --pinentry-mode loopback --passphrase '{_quick_unlock_password}' "
|
f"gpg --pinentry-mode loopback --passphrase '{_quick_unlock_password}' "
|
||||||
f"-qd ~/.config/sshyp/excluded.gpg"), stdout=PIPE, text=True).stdout.rstrip()
|
f"-qd ~/.config/sshyp/excluded.gpg"), stdout=PIPE, text=True).stdout.rstrip()
|
||||||
while _i < len(_quick_unlock_password_excluded):
|
while _i < len(_quick_unlock_password_excluded):
|
||||||
@@ -256,22 +260,22 @@ def line_edit(_lines, _edit_data, _edit_line):
|
|||||||
|
|
||||||
|
|
||||||
# attempts to connect to the user's server via ssh to register the device for syncing
|
# attempts to connect to the user's server via ssh to register the device for syncing
|
||||||
def copy_id_check(_port, _username_ssh, _ip, _client_device_id, _sshyp_data):
|
def copy_id_check(_port, _username_ssh, _ip, _client_device_id, _identity, _sshyp_data):
|
||||||
from stweak import write_config
|
from stweak import write_config
|
||||||
if not _sshyp_data.has_section('CLIENT-ONLINE'):
|
if not _sshyp_data.has_section('CLIENT-ONLINE'):
|
||||||
_sshyp_data.add_section('CLIENT-ONLINE')
|
_sshyp_data.add_section('CLIENT-ONLINE')
|
||||||
try:
|
try:
|
||||||
run(('ssh', '-o', 'ConnectTimeout=3', '-i', f"{home}/.ssh/sshyp", '-p', _port, f"{_username_ssh}@{_ip}",
|
run(('ssh', '-o', 'ConnectTimeout=3', '-i', _identity, '-p', _port, f"{_username_ssh}@{_ip}",
|
||||||
f'python3 -c \'from pathlib import Path; Path("/home/{_username_ssh}/.config/sshyp/devices/'
|
f'python3 -c \'from pathlib import Path; Path("/home/{_username_ssh}/.config/sshyp/devices/'
|
||||||
f'{_client_device_id}").touch(mode=0o400, exist_ok=True)\''), stderr=DEVNULL, check=True)
|
f'{_client_device_id}").touch(mode=0o400, exist_ok=True)\''), stderr=DEVNULL, check=True)
|
||||||
except CalledProcessError:
|
except CalledProcessError:
|
||||||
print('\n\u001b[38;5;9mwarning: ssh connection could not be made - ensure the public key (~/.ssh/sshyp.pub) is '
|
print(f'\n\u001b[38;5;9mwarning: ssh connection could not be made - ensure the public key ({_identity}) is '
|
||||||
'registered on the remote server and that the entered ip, port, and username are correct\n\nsyncing '
|
'registered on the remote server and that the entered ip, port, and username are correct\n\nsyncing '
|
||||||
'functionality will be disabled until this is addressed\u001b[0m\n')
|
'functionality will be disabled until this is addressed\u001b[0m\n')
|
||||||
_sshyp_data.set('CLIENT-ONLINE', 'ssh_error', '1')
|
_sshyp_data.set('CLIENT-ONLINE', 'ssh_error', 'true')
|
||||||
write_config(_sshyp_data)
|
write_config(_sshyp_data)
|
||||||
return True
|
return True
|
||||||
_sshyp_data.set('CLIENT-ONLINE', 'ssh_error', '0')
|
_sshyp_data.set('CLIENT-ONLINE', 'ssh_error', 'false')
|
||||||
write_config(_sshyp_data)
|
write_config(_sshyp_data)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -290,7 +294,7 @@ def print_info():
|
|||||||
}}-}}-*]{4*' '}`..'..'{9*' '}\u001b[0m♥♥♥\u001b[0m{9*' '}`..'..'{6*' '}|
|
}}-}}-*]{4*' '}`..'..'{9*' '}\u001b[0m♥♥♥\u001b[0m{9*' '}`..'..'{6*' '}|
|
||||||
{4*' '}◎-◎{4*' '}//{3*' '}\\\\{10*' '}\u001b[38;5;9m♥\u001b[0m{10*' '}//{3*' '}\\\\{5*' '}/|\\""")
|
{4*' '}◎-◎{4*' '}//{3*' '}\\\\{10*' '}\u001b[38;5;9m♥\u001b[0m{10*' '}//{3*' '}\\\\{5*' '}/|\\""")
|
||||||
print(f"{_border}{_blank}\n\u001b[38;5;7;48;5;8m\\\u001b[38;5;15;48;5;15m{18*' '}\u001b[38;5;15;48;5;8msshyp "
|
print(f"{_border}{_blank}\n\u001b[38;5;7;48;5;8m\\\u001b[38;5;15;48;5;15m{18*' '}\u001b[38;5;15;48;5;8msshyp "
|
||||||
f"version 1.5.0\u001b[38;5;15;48;5;15m{18*' '}\u001b[38;5;7;48;5;8m/\u001b[0m")
|
f"version 1.5.1\u001b[38;5;15;48;5;15m{18*' '}\u001b[38;5;7;48;5;8m/\u001b[0m")
|
||||||
print(f"\u001b[38;5;7;48;5;8m\\\u001b[38;5;15;48;5;15m{14*' '}\u001b[38;5;15;48;5;8mthe fortified flock"
|
print(f"\u001b[38;5;7;48;5;8m\\\u001b[38;5;15;48;5;15m{14*' '}\u001b[38;5;15;48;5;8mthe fortified flock"
|
||||||
f" update\u001b[38;5;15;48;5;15m{15*' '}\u001b[38;5;7;48;5;8m/\u001b[0m\n{_blank}")
|
f" update\u001b[38;5;15;48;5;15m{15*' '}\u001b[38;5;7;48;5;8m/\u001b[0m\n{_blank}")
|
||||||
print(f"\u001b[38;5;7;48;5;8m\\\u001b[38;5;15;48;5;15m{9*' '}\u001b[38;5;15;48;5;8mcopyright 2021-2023 ",
|
print(f"\u001b[38;5;7;48;5;8m\\\u001b[38;5;15;48;5;15m{9*' '}\u001b[38;5;15;48;5;8mcopyright 2021-2023 ",
|
||||||
@@ -424,7 +428,7 @@ def add_entry():
|
|||||||
def add_folder():
|
def add_folder():
|
||||||
Path(directory + entry_name).mkdir(mode=0o700, parents=True, exist_ok=True)
|
Path(directory + entry_name).mkdir(mode=0o700, parents=True, exist_ok=True)
|
||||||
if not ssh_error:
|
if not ssh_error:
|
||||||
run(('ssh', '-i', f"{home}/.ssh/sshyp", '-p', port, f"{username_ssh}@{ip}",
|
run(('ssh', '-i', identity, '-p', port, f"{username_ssh}@{ip}",
|
||||||
f'python3 -c \'from pathlib import Path; Path("{directory_ssh}{entry_name}")'
|
f'python3 -c \'from pathlib import Path; Path("{directory_ssh}{entry_name}")'
|
||||||
f'.mkdir(mode=0o700, parents=True, exist_ok=True)\''))
|
f'.mkdir(mode=0o700, parents=True, exist_ok=True)\''))
|
||||||
|
|
||||||
@@ -453,13 +457,13 @@ def rename():
|
|||||||
# if renaming a folder
|
# if renaming a folder
|
||||||
if not ssh_error:
|
if not ssh_error:
|
||||||
Path(f"{directory}{_new_name}").mkdir(mode=0o700, parents=True, exist_ok=True)
|
Path(f"{directory}{_new_name}").mkdir(mode=0o700, parents=True, exist_ok=True)
|
||||||
run(('ssh', '-i', f"{home}/.ssh/sshyp", '-p', port, f"{username_ssh}@{ip}",
|
run(('ssh', '-i', identity, '-p', port, f"{username_ssh}@{ip}",
|
||||||
f'python3 -c \'from pathlib import Path; Path("{directory_ssh}{entry_name}")'
|
f'python3 -c \'from pathlib import Path; Path("{directory_ssh}{entry_name}")'
|
||||||
f'.rename(Path("{directory_ssh}{_new_name}"))\''))
|
f'.rename(Path("{directory_ssh}{_new_name}"))\''))
|
||||||
else:
|
else:
|
||||||
move(f"{directory}{entry_name}", f"{directory}{_new_name}")
|
move(f"{directory}{entry_name}", f"{directory}{_new_name}")
|
||||||
if not ssh_error:
|
if not ssh_error:
|
||||||
run(('ssh', '-i', f"{home}/.ssh/sshyp", '-p', port, f"{username_ssh}@{ip}",
|
run(('ssh', '-i', identity, '-p', port, f"{username_ssh}@{ip}",
|
||||||
f'cd /usr/lib/sshyp; python3 -c \'from sshync import delete; delete("{entry_name}", "remotely", True)\''))
|
f'cd /usr/lib/sshyp; python3 -c \'from sshync import delete; delete("{entry_name}", "remotely", True)\''))
|
||||||
|
|
||||||
|
|
||||||
@@ -517,6 +521,7 @@ def gen():
|
|||||||
|
|
||||||
# copies a specified field of an entry to the clipboard
|
# copies a specified field of an entry to the clipboard
|
||||||
def copy_data():
|
def copy_data():
|
||||||
|
from hashlib import sha512
|
||||||
from subprocess import Popen
|
from subprocess import Popen
|
||||||
# ensure the copy target is an entry
|
# ensure the copy target is an entry
|
||||||
target_type_check(entry_name, True, True)
|
target_type_check(entry_name, True, True)
|
||||||
@@ -533,33 +538,38 @@ def copy_data():
|
|||||||
# ensure field is not blank
|
# ensure field is not blank
|
||||||
if _copy_subject == '':
|
if _copy_subject == '':
|
||||||
raise IndexError
|
raise IndexError
|
||||||
|
|
||||||
|
# store hashed _copy_subject for later comparison
|
||||||
|
_hash = sha512()
|
||||||
|
_hash.update(_copy_subject.encode('utf-8'))
|
||||||
|
|
||||||
# PORT START CLIPBOARD
|
# PORT START CLIPBOARD
|
||||||
# WSL clipboard detection
|
# WSL clipboard detection
|
||||||
if 'WSL_DISTRO_NAME' in environ:
|
if 'WSL_DISTRO_NAME' in environ:
|
||||||
run(('powershell.exe', '-c', "Set-Clipboard '" + _copy_subject.replace("'", "''") + "'"))
|
run(('powershell.exe', '-c', "Set-Clipboard '" + _copy_subject.replace("'", "''") + "'"))
|
||||||
Popen("sleep 30; powershell.exe -c Set-Clipboard ''", shell=True, stdout=DEVNULL, stderr=DEVNULL)
|
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'wsl'), stdout=DEVNULL,
|
||||||
|
stderr=DEVNULL)
|
||||||
# Wayland clipboard detection
|
# Wayland clipboard detection
|
||||||
elif 'WAYLAND_DISPLAY' in environ:
|
elif 'WAYLAND_DISPLAY' in environ:
|
||||||
run(('wl-copy', _copy_subject))
|
run('wl-copy', stdin=Popen(('printf', '%b', _copy_subject.replace('\\', '\\\\')), stdout=PIPE).stdout)
|
||||||
Popen('sleep 30; wl-copy -c', shell=True)
|
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'wayland'))
|
||||||
# Haiku clipboard detection
|
# Haiku clipboard detection
|
||||||
elif uname()[0] == 'Haiku':
|
elif uname()[0] == 'Haiku':
|
||||||
run(('clipboard', '-c', _copy_subject))
|
run(('clipboard', '-c', _copy_subject))
|
||||||
Popen('sleep 30; clipboard -r', shell=True)
|
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'haiku'))
|
||||||
# MacOS clipboard detection
|
# MacOS clipboard detection
|
||||||
elif uname()[0] == 'Darwin':
|
elif uname()[0] == 'Darwin':
|
||||||
run('pbcopy', stdin=Popen(('printf', _copy_subject.replace('\\', '\\\\').replace('%', '%%')), stdout=PIPE)
|
run('pbcopy', stdin=Popen(('printf', '%b', _copy_subject.replace('\\', '\\\\')), stdout=PIPE).stdout)
|
||||||
.stdout)
|
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'mac'))
|
||||||
Popen("sleep 30; printf '' | pbcopy", shell=True)
|
|
||||||
# Termux (Android) clipboard detection
|
# Termux (Android) clipboard detection
|
||||||
elif isdir("/data/data/com.termux"):
|
elif isdir("/data/data/com.termux"):
|
||||||
run(('termux-clipboard-set', _copy_subject))
|
run(('termux-clipboard-set', _copy_subject))
|
||||||
Popen("sleep 30; termux-clipboard-set ''", shell=True)
|
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'termux'))
|
||||||
# X11 clipboard detection
|
# X11 clipboard detection
|
||||||
elif 'DISPLAY' in environ:
|
elif 'DISPLAY' in environ:
|
||||||
run(('xclip', '-sel', 'c'), stdin=Popen(('printf', _copy_subject.replace('\\', '\\\\')
|
run(('xclip', '-sel', 'c'), stdin=Popen(('printf', '%b', _copy_subject.replace('\\', '\\\\')), stdout=PIPE)
|
||||||
.replace('%', '%%')), stdout=PIPE).stdout)
|
.stdout)
|
||||||
Popen("sleep 30; printf '' | xclip -sel c", shell=True)
|
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'x11'))
|
||||||
else:
|
else:
|
||||||
print('\n\u001b[38;5;9merror: clipboard tool could not be determined\n\nnote that the clipboard does not '
|
print('\n\u001b[38;5;9merror: clipboard tool could not be determined\n\nnote that the clipboard does not '
|
||||||
'function in a raw tty\u001b[0m\n')
|
'function in a raw tty\u001b[0m\n')
|
||||||
@@ -570,7 +580,7 @@ def copy_data():
|
|||||||
def remove_data():
|
def remove_data():
|
||||||
decrypt(None, _quick_verify=quick_unlock_enabled)
|
decrypt(None, _quick_verify=quick_unlock_enabled)
|
||||||
if not ssh_error:
|
if not ssh_error:
|
||||||
run(('ssh', '-i', f"{home}/.ssh/sshyp", '-p', port, f"{username_ssh}@{ip}",
|
run(('ssh', '-i', identity, '-p', port, f"{username_ssh}@{ip}",
|
||||||
f'cd /usr/lib/sshyp; python3 -c \'from sshync import delete; delete("{entry_name}", "remotely", True)\''))
|
f'cd /usr/lib/sshyp; python3 -c \'from sshync import delete; delete("{entry_name}", "remotely", True)\''))
|
||||||
else:
|
else:
|
||||||
offline_delete(entry_name, 'locally', silent_sync)
|
offline_delete(entry_name, 'locally', silent_sync)
|
||||||
@@ -581,7 +591,7 @@ def extension_runner():
|
|||||||
_output_com, _extension_dir = None, realpath(__file__).rsplit('/', 1)[0] + '/extensions/'
|
_output_com, _extension_dir = None, realpath(__file__).rsplit('/', 1)[0] + '/extensions/'
|
||||||
if isdir(_extension_dir):
|
if isdir(_extension_dir):
|
||||||
for _extension in listdir(_extension_dir):
|
for _extension in listdir(_extension_dir):
|
||||||
_extension_config = ConfigParser()
|
_extension_config = ConfigParser(interpolation=None)
|
||||||
_extension_config.read(_extension_dir + _extension)
|
_extension_config.read(_extension_dir + _extension)
|
||||||
_input_com = _extension_config.get('config', 'input').split()
|
_input_com = _extension_config.get('config', 'input').split()
|
||||||
if _input_com == arguments[arg_start:]:
|
if _input_com == arguments[arg_start:]:
|
||||||
@@ -603,7 +613,7 @@ if __name__ == "__main__":
|
|||||||
ssh_error, success_flag, sync_flag, silent_sync, pass_show = False, False, False, False, False
|
ssh_error, success_flag, sync_flag, silent_sync, pass_show = False, False, False, False, False
|
||||||
|
|
||||||
# set to avoid PEP8 warnings
|
# set to avoid PEP8 warnings
|
||||||
arg_start, device_type = None, None
|
arg_start, device_type, offline_mode_enabled = None, None, None
|
||||||
|
|
||||||
# retrieve typed argument
|
# retrieve typed argument
|
||||||
arguments = argv[1:]
|
arguments = argv[1:]
|
||||||
@@ -624,27 +634,28 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
# import saved userdata
|
# import saved userdata
|
||||||
try:
|
try:
|
||||||
sshyp_data = ConfigParser()
|
sshyp_data = ConfigParser(interpolation=None)
|
||||||
sshyp_data.read(f"{home}/.config/sshyp/sshyp.ini")
|
sshyp_data.read(f"{home}/.config/sshyp/sshyp.ini")
|
||||||
device_type = sshyp_data.get('GENERAL', 'device_type')
|
device_type = sshyp_data.get('GENERAL', 'device_type')
|
||||||
if device_type == 'client':
|
if device_type == 'client':
|
||||||
directory = f"{home}/.local/share/sshyp/"
|
directory = f"{home}/.local/share/sshyp/"
|
||||||
gpg_id = sshyp_data.get('CLIENT-GENERAL', 'gpg_id')
|
gpg_id = sshyp_data.get('CLIENT-GENERAL', 'gpg_id')
|
||||||
editor = sshyp_data.get('CLIENT-GENERAL', 'text_editor')
|
editor = sshyp_data.get('CLIENT-GENERAL', 'text_editor')
|
||||||
offline_mode_enabled = sshyp_data.get('CLIENT-GENERAL', 'offline_mode_enabled')
|
offline_mode_enabled = sshyp_data.getboolean('CLIENT-GENERAL', 'offline_mode_enabled')
|
||||||
if offline_mode_enabled == 'true':
|
if offline_mode_enabled:
|
||||||
ssh_error = True
|
ssh_error = True
|
||||||
quick_unlock_enabled = 'false'
|
quick_unlock_enabled = False
|
||||||
else:
|
else:
|
||||||
quick_unlock_enabled = sshyp_data.get('CLIENT-ONLINE', 'quick_unlock_enabled')
|
quick_unlock_enabled = sshyp_data.getboolean('CLIENT-ONLINE', 'quick_unlock_enabled')
|
||||||
username_ssh = sshyp_data.get('SSHYNC', 'user')
|
username_ssh = sshyp_data.get('SSHYNC', 'user')
|
||||||
ip = sshyp_data.get('SSHYNC', 'ip')
|
ip = sshyp_data.get('SSHYNC', 'ip')
|
||||||
port = sshyp_data.get('SSHYNC', 'port')
|
port = sshyp_data.get('SSHYNC', 'port')
|
||||||
directory_ssh = sshyp_data.get('SSHYNC', 'remote_dir')
|
directory_ssh = sshyp_data.get('SSHYNC', 'remote_dir')
|
||||||
|
identity = sshyp_data.get('SSHYNC', 'identity_file')
|
||||||
client_device_id = listdir(f"{home}/.config/sshyp/devices")[0]
|
client_device_id = listdir(f"{home}/.config/sshyp/devices")[0]
|
||||||
ssh_error = int(sshyp_data.get('CLIENT-ONLINE', 'ssh_error'))
|
ssh_error = sshyp_data.getboolean('CLIENT-ONLINE', 'ssh_error')
|
||||||
if ssh_error == 1:
|
if ssh_error:
|
||||||
ssh_error = copy_id_check(port, username_ssh, ip, client_device_id, sshyp_data)
|
ssh_error = copy_id_check(port, username_ssh, ip, client_device_id, identity, sshyp_data)
|
||||||
except (FileNotFoundError, NoSectionError, NoOptionError):
|
except (FileNotFoundError, NoSectionError, NoOptionError):
|
||||||
print(f"\n{73*'!'}")
|
print(f"\n{73*'!'}")
|
||||||
print("not all necessary configurations have been made - please run 'sshyp init'")
|
print("not all necessary configurations have been made - please run 'sshyp init'")
|
||||||
@@ -714,6 +725,9 @@ if __name__ == "__main__":
|
|||||||
sync()
|
sync()
|
||||||
elif (arg_count > 0 and arguments[0] == 'sync') or (arg_count > 1 and arguments[1] == 'shear'):
|
elif (arg_count > 0 and arguments[0] == 'sync') or (arg_count > 1 and arguments[1] == 'shear'):
|
||||||
sync('\n')
|
sync('\n')
|
||||||
|
elif arg_count > 0 and arguments[0] == 'sync' and offline_mode_enabled:
|
||||||
|
print("\n\u001b[38;5;9mwarning: sshyp is currently configured in offline mode - ssh synchronization is "
|
||||||
|
"disabled\u001b[0m\n")
|
||||||
|
|
||||||
# PORT START ARGS-SERVER
|
# PORT START ARGS-SERVER
|
||||||
# server arguments
|
# server arguments
|
||||||
|
|||||||
+17
-15
@@ -10,7 +10,7 @@ from subprocess import PIPE, run
|
|||||||
# PORT START UNAME-IMPORT-STWEAK
|
# PORT START UNAME-IMPORT-STWEAK
|
||||||
from os import uname
|
from os import uname
|
||||||
# PORT END UNAME-IMPORT-STWEAK
|
# PORT END UNAME-IMPORT-STWEAK
|
||||||
home, sshyp_data, stdscr = expanduser('~'), ConfigParser(), None
|
home, sshyp_data, stdscr = expanduser('~'), ConfigParser(interpolation=None), None
|
||||||
if isfile(f"{home}/.config/sshyp/sshyp.ini"):
|
if isfile(f"{home}/.config/sshyp/sshyp.ini"):
|
||||||
_exists_flag = True
|
_exists_flag = True
|
||||||
sshyp_data.read(f"{home}/.config/sshyp/sshyp.ini")
|
sshyp_data.read(f"{home}/.config/sshyp/sshyp.ini")
|
||||||
@@ -99,7 +99,7 @@ def gpg_config():
|
|||||||
stdout=PIPE, text=True).stdout.splitlines() if _item.startswith('uid')]
|
stdout=PIPE, text=True).stdout.splitlines() if _item.startswith('uid')]
|
||||||
_named_uid_list = []
|
_named_uid_list = []
|
||||||
for _uid in _uid_list:
|
for _uid in _uid_list:
|
||||||
_named_uid_list.append(_uid.split(':')[9])
|
_named_uid_list.append(_uid.split(':')[9].replace('\\x3a', ':').replace('\\x5c', '\\'))
|
||||||
_named_uid_list.append('auto-generate')
|
_named_uid_list.append('auto-generate')
|
||||||
_gpg_id_sel = curses_radio(_named_uid_list, 'gpg key selection')
|
_gpg_id_sel = curses_radio(_named_uid_list, 'gpg key selection')
|
||||||
if _gpg_id_sel == len(_named_uid_list)-1:
|
if _gpg_id_sel == len(_named_uid_list)-1:
|
||||||
@@ -151,7 +151,7 @@ def ssh_config():
|
|||||||
# ensure ~/.ssh directory exists
|
# ensure ~/.ssh directory exists
|
||||||
Path(f"{home}/.ssh").mkdir(mode=0o700, exist_ok=True)
|
Path(f"{home}/.ssh").mkdir(mode=0o700, exist_ok=True)
|
||||||
for _file in listdir(f"{home}/.ssh"):
|
for _file in listdir(f"{home}/.ssh"):
|
||||||
if not _file.startswith('.') and _file not in ('known_hosts', 'authorized_keys') \
|
if not _file.startswith('.') and _file not in ('known_hosts', 'known_hosts.old', 'authorized_keys') \
|
||||||
and not _file.endswith('.pub') and isfile(f"{home}/.ssh/{_file}"):
|
and not _file.endswith('.pub') and isfile(f"{home}/.ssh/{_file}"):
|
||||||
_keys.append(f"{home}/.ssh/{_file}")
|
_keys.append(f"{home}/.ssh/{_file}")
|
||||||
_keys.extend(['auto-generate', 'other (type the location)'])
|
_keys.extend(['auto-generate', 'other (type the location)'])
|
||||||
@@ -184,11 +184,11 @@ def ssh_config():
|
|||||||
sshyp_data.set('SSHYNC', 'remote_dir', f"/home/{_username_ssh}/.local/share/sshyp/")
|
sshyp_data.set('SSHYNC', 'remote_dir', f"/home/{_username_ssh}/.local/share/sshyp/")
|
||||||
sshyp_data.set('SSHYNC', 'identity_file', _ssh_key)
|
sshyp_data.set('SSHYNC', 'identity_file', _ssh_key)
|
||||||
write_config()
|
write_config()
|
||||||
return _iport[1], _username_ssh, _iport[0]
|
return _iport[1], _username_ssh, _iport[0], _ssh_key
|
||||||
|
|
||||||
|
|
||||||
# device id configuration
|
# device id configuration
|
||||||
def dev_id_config(_ip, _username_ssh, _port):
|
def dev_id_config(_ip, _username_ssh, _port, _identity):
|
||||||
from sshyp import copy_id_check, string_gen
|
from sshyp import copy_id_check, string_gen
|
||||||
_device_id_prefix = curses_text('name this device:\n\n\n\n\n(ctrl+g/enter to confirm)\n\nimportant: this '
|
_device_id_prefix = curses_text('name this device:\n\n\n\n\n(ctrl+g/enter to confirm)\n\nimportant: this '
|
||||||
'id must be unique amongst your client devices\n\nthis is used to keep track of '
|
'id must be unique amongst your client devices\n\nthis is used to keep track of '
|
||||||
@@ -200,7 +200,7 @@ def dev_id_config(_ip, _username_ssh, _port):
|
|||||||
remove(f"{home}/.config/sshyp/devices/{_id}")
|
remove(f"{home}/.config/sshyp/devices/{_id}")
|
||||||
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
|
# test server connection and attempt to register device id
|
||||||
copy_id_check(_ip, _username_ssh, _port, _device_id, sshyp_data)
|
copy_id_check(_ip, _username_ssh, _port, _device_id, _identity, sshyp_data)
|
||||||
|
|
||||||
|
|
||||||
# quick-unlock configuration
|
# quick-unlock configuration
|
||||||
@@ -360,8 +360,8 @@ def extension_downloader():
|
|||||||
from os import chmod
|
from os import chmod
|
||||||
from tempfile import gettempdir
|
from tempfile import gettempdir
|
||||||
from urllib.request import urlopen, urlretrieve
|
from urllib.request import urlopen, urlretrieve
|
||||||
_file_data = urlopen("https://raw.githubusercontent.com/rwinkhart/sshyp-labs/main/pointers/v1.5.0").read()
|
_file_data = urlopen("https://raw.githubusercontent.com/rwinkhart/sshyp-labs/main/pointers/v1.5.1").read()
|
||||||
_pointer = ConfigParser()
|
_pointer = ConfigParser(interpolation=None)
|
||||||
_pointer.read_string(_file_data.decode('utf-8'))
|
_pointer.read_string(_file_data.decode('utf-8'))
|
||||||
_extensions = _pointer.sections()
|
_extensions = _pointer.sections()
|
||||||
_extensions.append('CANCEL')
|
_extensions.append('CANCEL')
|
||||||
@@ -464,15 +464,15 @@ def global_menu(_scr, _device_type, _top_message):
|
|||||||
if not sshyp_data.has_option('CLIENT-ONLINE', 'quick_unlock_enabled'):
|
if not sshyp_data.has_option('CLIENT-ONLINE', 'quick_unlock_enabled'):
|
||||||
quick_unlock_config(True)
|
quick_unlock_config(True)
|
||||||
# set to None to check if modified later
|
# set to None to check if modified later
|
||||||
_ip, _username_ssh, _port = None, None, None
|
_ip, _username_ssh, _port, _identity = None, None, None, None
|
||||||
# ...and there is no sshync config present
|
# ...and there is no sshync config present
|
||||||
if not sshyp_data.has_section('SSHYNC'):
|
if not sshyp_data.has_section('SSHYNC'):
|
||||||
_ip, _username_ssh, _port = ssh_config()
|
_ip, _username_ssh, _port, _identity = ssh_config()
|
||||||
# ...and there is no device ID present
|
# ...and there is no device ID present
|
||||||
if not listdir(f"{home}/.config/sshyp/devices"):
|
if not listdir(f"{home}/.config/sshyp/devices"):
|
||||||
if None in (_ip, _username_ssh, _port):
|
if None in (_ip, _username_ssh, _port):
|
||||||
_ip, _username_ssh, _port = ssh_config()
|
_ip, _username_ssh, _port, _identity = ssh_config()
|
||||||
dev_id_config(_ip, _username_ssh, _port)
|
dev_id_config(_ip, _username_ssh, _port, _identity)
|
||||||
# ...or ssh_error is missing
|
# ...or ssh_error is missing
|
||||||
elif not sshyp_data.has_option('CLIENT-ONLINE', 'ssh_error'):
|
elif not sshyp_data.has_option('CLIENT-ONLINE', 'ssh_error'):
|
||||||
sshyp_data.set('CLIENT-ONLINE', 'ssh_error', '1')
|
sshyp_data.set('CLIENT-ONLINE', 'ssh_error', '1')
|
||||||
@@ -492,7 +492,7 @@ def global_menu(_scr, _device_type, _top_message):
|
|||||||
if not sshyp_data.has_section('SSHYNC'):
|
if not sshyp_data.has_section('SSHYNC'):
|
||||||
ssh_config()
|
ssh_config()
|
||||||
dev_id_config(sshyp_data.get('SSHYNC', 'ip'), sshyp_data.get('SSHYNC', 'user'),
|
dev_id_config(sshyp_data.get('SSHYNC', 'ip'), sshyp_data.get('SSHYNC', 'user'),
|
||||||
sshyp_data.get('SSHYNC', 'port'))
|
sshyp_data.get('SSHYNC', 'port'), sshyp_data.get('SSHYNC', 'identity_file'))
|
||||||
elif _choice == 4:
|
elif _choice == 4:
|
||||||
editor_config(False)
|
editor_config(False)
|
||||||
elif _choice == 5:
|
elif _choice == 5:
|
||||||
@@ -555,10 +555,10 @@ def initial_setup(_scr):
|
|||||||
if _dev_sync_types[1] != 'true':
|
if _dev_sync_types[1] != 'true':
|
||||||
|
|
||||||
# ssh+sshync configuration
|
# ssh+sshync configuration
|
||||||
_ip, _username_ssh, _port = ssh_config()
|
_ip, _username_ssh, _port, _identity = ssh_config()
|
||||||
|
|
||||||
# device id configuration
|
# device id configuration
|
||||||
dev_id_config(_ip, _username_ssh, _port)
|
dev_id_config(_ip, _username_ssh, _port, _identity)
|
||||||
|
|
||||||
# PORT START CLIPTOOL
|
# PORT START CLIPTOOL
|
||||||
# check for clipboard tool and display warning if missing
|
# check for clipboard tool and display warning if missing
|
||||||
@@ -576,6 +576,8 @@ def initial_setup(_scr):
|
|||||||
# PORT END CLIPTOOL
|
# PORT END CLIPTOOL
|
||||||
|
|
||||||
# run optional configuration menu
|
# run optional configuration menu
|
||||||
|
curses_radio(['okay'], 'required configuration complete\n\na menu for additional (optional) configuration will be '
|
||||||
|
'displayed\n\nthis menu can be safely exited at any time')
|
||||||
wrapped_entry(_dev_sync_types[0], 'additional configuration options:')
|
wrapped_entry(_dev_sync_types[0], 'additional configuration options:')
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+11
-5
@@ -144,7 +144,7 @@ provides {
|
|||||||
requires {
|
requires {
|
||||||
gnupg
|
gnupg
|
||||||
openssh
|
openssh
|
||||||
python310
|
python3.11
|
||||||
}
|
}
|
||||||
urls {
|
urls {
|
||||||
\"https://github.com/rwinkhart/sshyp\"
|
\"https://github.com/rwinkhart/sshyp\"
|
||||||
@@ -220,11 +220,17 @@ Section: utils
|
|||||||
Architecture: all
|
Architecture: all
|
||||||
Maintainer: Randall Winkhart <idgr at tutanota dot com>
|
Maintainer: Randall Winkhart <idgr at tutanota dot com>
|
||||||
Description: A light-weight, self-hosted, synchronized password manager
|
Description: A light-weight, self-hosted, synchronized password manager
|
||||||
Depends: python3, gnupg, openssh-client
|
|
||||||
Suggests: wl-clipboard, xclip, bash-completion
|
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Installed-Size: $size
|
Installed-Size: $size
|
||||||
|
Depends: python3, gnupg, openssh-client
|
||||||
" > output/debiantemp/sshyp_"$version"-"$revision"_all/DEBIAN/control
|
" > output/debiantemp/sshyp_"$version"-"$revision"_all/DEBIAN/control
|
||||||
|
if [ "$1" = 'Debian' ]; then
|
||||||
|
printf "Suggests: wl-clipboard, xclip, bash-completion, openssh-server
|
||||||
|
" >> output/debiantemp/sshyp_"$version"-"$revision"_all/DEBIAN/control
|
||||||
|
else
|
||||||
|
printf "Suggests: bash-completion, openssh-server
|
||||||
|
" >> output/debiantemp/sshyp_"$version"-"$revision"_all/DEBIAN/control
|
||||||
|
fi
|
||||||
dpkg-deb --build --root-owner-group -z6 -Sextreme -Zxz output/debiantemp/sshyp_"$version"-"$revision"_all/
|
dpkg-deb --build --root-owner-group -z6 -Sextreme -Zxz output/debiantemp/sshyp_"$version"-"$revision"_all/
|
||||||
mv output/debiantemp/sshyp_"$version"-"$revision"_all.deb output/"$special"-sshyp_"$version"-"$revision"_all.deb
|
mv output/debiantemp/sshyp_"$version"-"$revision"_all.deb output/"$special"-sshyp_"$version"-"$revision"_all.deb
|
||||||
rm -rf output/debiantemp
|
rm -rf output/debiantemp
|
||||||
@@ -296,7 +302,7 @@ License: GPL-3.0-only
|
|||||||
URL: https://github.com/rwinkhart/sshyp
|
URL: https://github.com/rwinkhart/sshyp
|
||||||
Source0: GENERIC-FEDORA-sshyp-"$version".tar.xz
|
Source0: GENERIC-FEDORA-sshyp-"$version".tar.xz
|
||||||
Requires: python gnupg openssh-clients
|
Requires: python gnupg openssh-clients
|
||||||
Recommends: wl-clipboard xclip bash-completion
|
Recommends: wl-clipboard xclip bash-completion openssh-server
|
||||||
%%description
|
%%description
|
||||||
sshyp is a password-store compatible CLI password manager available for UNIX(-like) systems - its primary goal is to make syncing passwords and notes across devices as easy as possible via CLI.
|
sshyp is a password-store compatible CLI password manager available for UNIX(-like) systems - its primary goal is to make syncing passwords and notes across devices as easy as possible via CLI.
|
||||||
%%install
|
%%install
|
||||||
@@ -381,7 +387,7 @@ printf "/usr/bin/sshyp
|
|||||||
cp lib/* port-jobs/working/
|
cp lib/* port-jobs/working/
|
||||||
cd port-jobs
|
cd port-jobs
|
||||||
./CLIPTOOL.py LINUX
|
./CLIPTOOL.py LINUX
|
||||||
./CLIPBOARD.py LINUX
|
./CLIPBOARD.py BSD
|
||||||
./UNAME.py TMP
|
./UNAME.py TMP
|
||||||
./COMMENTS.py ALL
|
./COMMENTS.py ALL
|
||||||
./BLANKS.py
|
./BLANKS.py
|
||||||
|
|||||||
+45
-29
@@ -3,44 +3,60 @@ import re
|
|||||||
from sys import argv, exit as s_exit
|
from sys import argv, exit as s_exit
|
||||||
|
|
||||||
# read arguments
|
# read arguments
|
||||||
arguments, replacement = argv[1:], None
|
arguments, clip_replacement, clear_replacement = argv[1:], None, None
|
||||||
|
|
||||||
# define replacement text depending on arguments
|
# define replacement text depending on arguments
|
||||||
if len(arguments) > 0:
|
if len(arguments) > 0:
|
||||||
if arguments[0] == 'WSL':
|
if arguments[0] == 'WSL':
|
||||||
replacement = """run(('powershell.exe', '-c', "Set-Clipboard '" + _copy_subject.replace("'", "''") + "'"))
|
clip_replacement = """run(('powershell.exe', '-c', "Set-Clipboard '" + _copy_subject.replace("'", "''") + "'"))
|
||||||
Popen("sleep 30; powershell.exe -c Set-Clipboard ''", shell=True, stdout=DEVNULL, stderr=DEVNULL)"""
|
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'wsl'), stdout=DEVNULL, stderr=DEVNULL)"""
|
||||||
|
clear_replacement = """hash_paste.update(run(('powershell.exe', '-c', 'Get-Clipboard'), stdout=PIPE).stdout.strip())
|
||||||
|
if argv[1] == hash_paste.hexdigest():
|
||||||
|
run(('powershell.exe', '-c', 'Set-Clipboard'))"""
|
||||||
elif arguments[0] == 'MAC':
|
elif arguments[0] == 'MAC':
|
||||||
replacement = """run('pbcopy', stdin=Popen(('printf', _copy_subject.replace('\\\\\\', '\\\\\\\\\\\\\\')
|
clip_replacement = """run('pbcopy', stdin=Popen(('printf', '%b', _copy_subject.replace('\\\\\\', '\\\\\\\\\\\\\\')), stdout=PIPE).stdout)
|
||||||
.replace('%', '%%')), stdout=PIPE).stdout)
|
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'mac'))"""
|
||||||
Popen("sleep 30; printf '' | pbcopy", shell=True)"""
|
clear_replacement = """hash_paste.update(run('pbpaste', stdout=PIPE).stdout.strip())
|
||||||
|
if argv[1] == hash_paste.hexdigest():
|
||||||
|
run('pbcopy', input=b'')"""
|
||||||
elif arguments[0] == 'HAIKU':
|
elif arguments[0] == 'HAIKU':
|
||||||
replacement = """run(('clipboard', '-c', _copy_subject))
|
clip_replacement = """run(('clipboard', '-c', _copy_subject))
|
||||||
Popen('sleep 30; clipboard -r', shell=True)"""
|
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'haiku'))"""
|
||||||
|
clear_replacement = """hash_paste.update(run(('clipboard', '-p'), stdout=PIPE).stdout.strip())
|
||||||
|
if argv[1] == hash_paste.hexdigest():
|
||||||
|
run(('clipboard', '-r'))"""
|
||||||
elif arguments[0] == 'TERMUX':
|
elif arguments[0] == 'TERMUX':
|
||||||
replacement = """run(('termux-clipboard-set', _copy_subject))
|
clip_replacement = """run(('termux-clipboard-set', _copy_subject))
|
||||||
Popen("sleep 30; termux-clipboard-set ''", shell=True)"""
|
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'termux'))"""
|
||||||
elif arguments[0] == 'LINUX':
|
clear_replacement = """hash_paste.update(run('termux-clipboard-get', stdout=PIPE).stdout.strip())
|
||||||
replacement = """if 'WAYLAND_DISPLAY' in environ:
|
if argv[1] == hash_paste.hexdigest():
|
||||||
run(('wl-copy', _copy_subject))
|
run(("termux-clipboard-set", "''"))"""
|
||||||
Popen('sleep 30; wl-copy -c', shell=True)
|
elif arguments[0] in ('LINUX', 'BSD'):
|
||||||
|
clip_replacement = """if 'WAYLAND_DISPLAY' in environ:
|
||||||
|
run('wl-copy', stdin=Popen(('printf', '%b', _copy_subject.replace('\\\\\\', '\\\\\\\\\\\\\\')), stdout=PIPE).stdout)
|
||||||
|
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'wayland'))
|
||||||
else:
|
else:
|
||||||
run(('xclip', '-sel', 'c'), stdin=Popen(('printf', _copy_subject
|
run(('xclip', '-sel', 'c'), stdin=Popen(('printf', '%b', _copy_subject.replace('\\\\\\', '\\\\\\\\\\\\\\')), stdout=PIPE).stdout)
|
||||||
.replace('\\\\\\', '\\\\\\\\\\\\\\').replace('%', '%%')), stdout=PIPE).stdout)
|
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'x11'))"""
|
||||||
Popen("sleep 30; printf '' | xclip -sel c", shell=True)"""
|
clear_replacement = """if argv[2] == 'wayland':
|
||||||
|
hash_paste.update(run('wl-paste', stdout=PIPE).stdout.strip())
|
||||||
|
if argv[1] == hash_paste.hexdigest():
|
||||||
|
run(('wl-copy', '-c'))
|
||||||
|
else:
|
||||||
|
hash_paste.update(run(('xclip', '-o', '-sel', 'c'), stdout=PIPE).stdout.strip())
|
||||||
|
if argv[1] == hash_paste.hexdigest():
|
||||||
|
run(('xclip', '-i', '/dev/null', '-sel', 'c'))"""
|
||||||
else:
|
else:
|
||||||
s_exit()
|
s_exit()
|
||||||
|
|
||||||
# define PORT target
|
targets = (('CLIPBOARD', 'sshyp.py', '', '', clip_replacement),
|
||||||
string1 = '# PORT START CLIPBOARD'
|
('CLIPCLEAR', 'clipclear.py', '\n', '', clear_replacement))
|
||||||
string2 = '# PORT END CLIPBOARD'
|
|
||||||
|
|
||||||
# read input file
|
for target in targets:
|
||||||
text = open('working/sshyp.py', 'r').read()
|
# read text from target file
|
||||||
|
text = open(f"working/{target[1]}", 'r').read()
|
||||||
# find and replace the defined PORT target
|
# compile regex and modify text
|
||||||
regex = re.compile(f"{string1}.*?{string2}", re.DOTALL)
|
regex = re.compile(f"{target[2]}# PORT START {target[0]}.*?# PORT END {target[0]}{target[3]}", re.DOTALL)
|
||||||
new_text = re.sub(regex, replacement, text)
|
new_text = re.sub(regex, target[4], text)
|
||||||
|
# write updated text to target file
|
||||||
# write updated text
|
open(f"working/{target[1]}", 'w').write(new_text)
|
||||||
open('working/sshyp.py', 'w').write(new_text)
|
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import re
|
import re
|
||||||
|
|
||||||
devtype_replacement = """_install_type = curses_radio(('client (ssh-synchronized)', 'client (offline)'),
|
|
||||||
'device + sync type configuration')"""
|
|
||||||
|
|
||||||
targets = (('TWEAK-EXTEND-FUNCTIONS', 'stweak.py', '\n', '\n\n', ''),
|
targets = (('TWEAK-EXTEND-FUNCTIONS', 'stweak.py', '\n', '\n\n', ''),
|
||||||
('TWEAK-EXTEND-OPTION', 'stweak.py', '', '', "curses_radio(['okay'], "
|
('TWEAK-EXTEND-OPTION', 'stweak.py', '', '', "curses_radio(['okay'], "
|
||||||
"'extension management is not supported on this platform\\\\n\\\\ninstead, you may install and manage "
|
"'extension management is not supported on this platform\\\\n\\\\ninstead, you may install and manage "
|
||||||
"extensions through your system package manager\\\\n\\\\nofficial extension packages are available "
|
"extensions through your system package manager\\\\n\\\\nofficial extension packages are available "
|
||||||
"at https://github.com/rwinkhart/sshyp-labs/releases')"))
|
"at https://github.com/rwinkhart/sshyp-labs/releases')"))
|
||||||
|
|
||||||
for target in targets:
|
for target in targets:
|
||||||
# read text from target file
|
# read text from target file
|
||||||
text = open(f"working/{target[1]}", 'r').read()
|
text = open(f"working/{target[1]}", 'r').read()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
if [ "$1" = 'PREP' ]; then
|
if [ "$1" = 'PREP' ]; then
|
||||||
sed -i '1 s/.*/#!\/bin\/env\ python3.10/' ./*.py
|
sed -i '1 s/.*/#!\/bin\/env\ python3.11/' ./*.py
|
||||||
else
|
else
|
||||||
sed -i '1 s/.*/#!\/bin\/env\ python3.10/' ./working/sshyp.py
|
sed -i '1 s/.*/#!\/bin\/env\ python3.11/' ./working/sshyp.py ./working/clipclear.py
|
||||||
fi
|
fi
|
||||||
|
|||||||
Reference in New Issue
Block a user