mirror of
https://github.com/rwinkhart/sshyp.git
synced 2026-09-02 23:27:17 -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 | ||
|
|
185baec03f | ||
|
|
f930ffe94a | ||
|
|
a6fb33705e | ||
|
|
6483ffe6cf | ||
|
|
33bc0fc10f | ||
|
|
58698c1c55 | ||
|
|
37075230cc | ||
|
|
7e9786fe60 | ||
|
|
2e51a1ff15 | ||
|
|
b1567d768b | ||
|
|
e487c381e7 | ||
|
|
8ce1f18ff9 | ||
|
|
3783230606 | ||
|
|
a3551cc042 | ||
|
|
5b9b6da341 | ||
|
|
9abb1c45bd | ||
|
|
ba1e2734be | ||
|
|
d40d1daab5 | ||
|
|
5255d61f61 | ||
|
|
9fc2aa69a3 | ||
|
|
7f4ddd2775 | ||
|
|
4a7234282b | ||
|
|
5fb5b5b108 | ||
|
|
1344aa23f6 | ||
|
|
2ac8e8e434 | ||
|
|
aa7768fd6e | ||
|
|
2835214d60 | ||
|
|
ee56a7a919 | ||
|
|
4ad516e156 | ||
|
|
3347c425d9 | ||
|
|
488b94e638 | ||
|
|
343bc40ddd | ||
|
|
3cc5b5633c | ||
|
|
43c10e74e2 | ||
|
|
95cc769c23 | ||
|
|
25d0ef5a21 | ||
|
|
1cb5386756 | ||
|
|
a00beeea45 | ||
|
|
8daf01e986 | ||
|
|
404f787b89 | ||
|
|
e7de5761bd | ||
|
|
9d94ba1eb9 | ||
|
|
0c78a32195 | ||
|
|
2813387577 | ||
|
|
3f86508f9c | ||
|
|
2834c6d67c | ||
|
|
29c5e60804 | ||
|
|
ba5f118048 | ||
|
|
05e9bcc478 | ||
|
|
7972040d5d | ||
|
|
d417ff48ba | ||
|
|
7dedfcac17 | ||
|
|
307bcc3faa | ||
|
|
d031e3c7ab | ||
|
|
4f8d088d0e | ||
|
|
b035e208d7 | ||
|
|
93647c0489 | ||
|
|
75b9f4f0a5 | ||
|
|
463df727e9 | ||
|
|
184a7c680d | ||
|
|
49abdc4222 | ||
|
|
72f322b896 | ||
|
|
5d0abb0537 | ||
|
|
183582efbb | ||
|
|
8e72ae1660 | ||
|
|
f154cbe400 | ||
|
|
ebb12e731f | ||
|
|
7fc84d5f9b | ||
|
|
e0f37af453 | ||
|
|
f90d502106 | ||
|
|
c7a8854f3f | ||
|
|
da03e1e5f9 | ||
|
|
43061b4fa0 | ||
|
|
fcfa2adbf1 | ||
|
|
28d0ba5448 | ||
|
|
6c2d51aea6 | ||
|
|
2322140af7 | ||
|
|
93bffe2587 | ||
|
|
8474807cd4 | ||
|
|
227924cd81 | ||
|
|
1040a6f2ed | ||
|
|
a1ea9d6831 | ||
|
|
f36a7f5672 | ||
|
|
fe54acd3b0 | ||
|
|
669c62510a | ||
|
|
97bce0cec7 | ||
|
|
709db8f6de | ||
|
|
273b3c97a5 | ||
|
|
657bc0291f | ||
|
|
4ec66f6352 | ||
|
|
d061ded756 | ||
|
|
d868476d35 | ||
|
|
e594a625d9 | ||
|
|
5fabdf5a86 | ||
|
|
c6951d8890 | ||
|
|
8e26916bd7 | ||
|
|
d981baab6c | ||
|
|
ddf5fc2bc8 | ||
|
|
2265207987 | ||
|
|
293f9b8145 | ||
|
|
46dc81f0eb | ||
|
|
4894bc48e0 | ||
|
|
0ca12cf4ab | ||
|
|
03f011ccd0 | ||
|
|
399d89f829 | ||
|
|
b14f925e53 | ||
|
|
f4bb0fb783 | ||
|
|
453aacadbc | ||
|
|
99379ec1b1 | ||
|
|
6288c45ba0 | ||
|
|
53bb3dcacf | ||
|
|
2747aff0bf | ||
|
|
aeb3df7aed | ||
|
|
1ef290b289 | ||
|
|
bdedc98494 | ||
|
|
60d3a674c4 | ||
|
|
ebf58b7770 | ||
|
|
7b3270d70f | ||
|
|
5e35bd7b3d | ||
|
|
c3aa63b585 | ||
|
|
fcca6321aa | ||
|
|
d799a9d149 | ||
|
|
2adee8978c | ||
|
|
63292e291d | ||
|
|
2525260b3d | ||
|
|
50c4aa9a96 | ||
|
|
a89b201fc7 | ||
|
|
64e56fb871 | ||
|
|
8062dad78d | ||
|
|
9eedb3c977 | ||
|
|
fc1c64a64d | ||
|
|
50ffbf2ec8 | ||
|
|
c42cfc548e |
@@ -1,7 +1,7 @@
|
||||

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

|
||||

|
||||
[](https://github.com/rwinkhart/sshyp/releases)
|
||||
|
||||
[](https://github.com/rwinkhart/sshyp/actions/workflows/codeql-analysis.yml)
|
||||
@@ -21,6 +21,8 @@ It is your responsibility to assess the security and stability of "sshyp" before
|
||||
I am not responsible for any data loss or breaches of your information resulting from the use of "sshyp".
|
||||
"sshyp" is a new project that is constantly being updated, and though safety and security are priorities, they cannot be guaranteed.
|
||||
|
||||
Always check the [known bugs](https://github.com/rwinkhart/sshyp/wiki/Known-Bugs) list before updating or installing sshyp.
|
||||
|
||||
# Mission Statement
|
||||
sshyp aims to make it as simple as possible to manage passwords and notes via CLI across multiple devices in a secure, self-hosted fashion.
|
||||
|
||||
@@ -38,11 +40,13 @@ What sshyp can do:
|
||||
|
||||
Please see the [installation guide](https://github.com/rwinkhart/sshyp/wiki/Installation) in the sshyp wiki for directions specific to your distribution/OS.
|
||||
|
||||
Pre-built packages exist for Haiku, FreeBSD, Alpine Linux, Arch Linux, Debian/Ubuntu Linux, Fedora Linux, and Termux. These can be downloaded from the releases page.
|
||||
Pre-built packages exist for Haiku, FreeBSD, Alpine Linux, Debian/Ubuntu Linux, Fedora Linux, Termux, and WSL. These can be downloaded from the releases page.
|
||||
|
||||
Support for additional environments, such as MacOS and OpenBSD, is coming soon.
|
||||
Additionally, sshyp is distributed on the AUR.
|
||||
|
||||
Extensions are available in the [sshyp-labs](https://github.com/rwinkhart/sshyp-labs) repository.
|
||||
Extensions can be installed from the `sshyp tweak` menu on most supported platforms.
|
||||
|
||||
For Haiku and Termux, extensions must instead be installed through the [system package manager](https://github.com/rwinkhart/sshyp-labs/releases).
|
||||
|
||||
# Building
|
||||
A packaging script is included in the root directory of the repo in order to package sshyp for your distribution. To package sshyp from source, simply run:
|
||||
@@ -59,39 +63,24 @@ Haiku and Fedora packaging must be done on their own respective distributions.
|
||||
|
||||
The AUR version and the packages attached to the release tags were already packaged using this script.
|
||||
|
||||
Currently, the script can create packages for Haiku, FreeBSD, Alpine Linux (APKBUILD), Arch Linux (PKGBUILD), Debian/Ubuntu Linux, Fedora Linux, Termux, and generic.
|
||||
Currently, the script can create packages for Haiku, FreeBSD, Alpine Linux (APKBUILD), Arch Linux (PKGBUILD), Debian/Ubuntu Linux, Fedora Linux, Termux, and WSL.
|
||||
|
||||
# Usage
|
||||
Upon initial installation (on both the server and client devices), be sure to run:
|
||||
Upon initial installation (on both the server and client devices), run `sshyp init` to configure the settings necessary for sshyp to function.
|
||||
|
||||
```
|
||||
sshyp tweak
|
||||
```
|
||||
|
||||
This command will allow you to configure the settings necessary for sshyp to function. To ensure configuration compatibility, it is a good idea to run 'sshyp tweak' after each major update.
|
||||
In order to configure optional settings or change already configured settings, run `sshyp tweak`.
|
||||
|
||||
Please note that decrypting and reading entries is disabled on server devices for security reasons. Only devices configured as clients can use the GPG key to decrypt entries.
|
||||
|
||||
All available options can be found with:
|
||||
|
||||
```
|
||||
sshyp help
|
||||
```
|
||||
|
||||
Or alternatively, in the man page:
|
||||
|
||||
```
|
||||
man sshyp
|
||||
```
|
||||
All available options can be found with `sshyp help`, or alternatively, in the man page.
|
||||
|
||||
# Roadmap
|
||||
Short-term Goals:
|
||||
|
||||
- MacOS support
|
||||
- improved temporary directory security
|
||||
- a minimal GUI app - being made as an [extension](https://github.com/rwinkhart/sshyp-labs)
|
||||
- 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:
|
||||
|
||||
- a fun surprise
|
||||
- migrate from gpg to a better-suited utility focused on symmetric cipher encryption
|
||||
- seize the thrones, shear the humans
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
git add -f extra lib/sshyp.py lib/sshync.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 push
|
||||
|
||||
@@ -1,3 +1,135 @@
|
||||
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
|
||||
07/12/2023
|
||||
|
||||
the fortified flock update
|
||||
|
||||
this release implements new curses-based TUI configuration menus and improves the security of sshyp
|
||||
|
||||
compatibility-breaking changes:
|
||||
|
||||
- a completely new configuration system calls for a new configuration file
|
||||
^ before using sshyp v1.5.0 for the first time, 'sshyp init' will need to be ran to create the
|
||||
new configuration file - the old configuration files can be safely deleted
|
||||
^ 'sshyp tweak' has been repurposed as the configuration menu for changing individual options,
|
||||
rather than re-doing setup entirely - after initialization, this is the one you want to use
|
||||
- the new extension manager is replacing the old method of installing extensions as system packages
|
||||
^ any older extensions you have installed should be uninstalled to prevent conflicts
|
||||
^ extensions are now installed and removed through the 'sshyp tweak' menu's extension manager
|
||||
^ this does not yet apply to Haiku and Termux, which will continue to install extensions using
|
||||
the previous method
|
||||
- all clients and servers must be updated to this release for the folder renaming bug fix to work
|
||||
^ failure to update all devices will result in errors and/or potential data loss
|
||||
- various packaging changes
|
||||
^ x-clip and wl-clipboard are now marked as optional dependencies, so the correct tool will
|
||||
need to be installed as needed - a warning has been added for this if neither package is installed
|
||||
^ the changelog no longer ships with sshyp - it is still available on GitHub
|
||||
|
||||
user-facing features:
|
||||
|
||||
- the old configuration menu, 'sshyp tweak', has been split into two new curses-based TUI menus
|
||||
^ 'sshyp init' is for first-time setup/initialization
|
||||
^ 'sshyp tweak' can be used at any time to quickly adjust individual settings
|
||||
^ whitelist management tools have been moved to the new 'sshyp tweak' menu
|
||||
^ clients and servers now each have their own dedicated 'sshyp tweak' menu
|
||||
^ the new config file is in .INI format, making it easier to edit the config without sshyp
|
||||
- added a 'sshyp tweak' option for re-encrypting all entries with a new gpg key
|
||||
- added a security advisory when enabling quick-unlock to ensure the user understands potential risks
|
||||
- the user is now warned if the clipboard tool relevant to their platform is not installed
|
||||
- passwords are now hidden by default in the entry reader
|
||||
^ they can be displayed by appending '--show' or '-s' to the end of the command
|
||||
- input is now hidden when adding/editing a password in an entry
|
||||
- entries are no longer re-encrypted and synced if the note editor is quit without saving
|
||||
|
||||
|
||||
fixes/optimizations:
|
||||
|
||||
- a tmpfs is no longer used for decrypting entries unless editing notes
|
||||
^ data is now decrypted directly into sshyp and written to entries pre-encrypted
|
||||
^ this makes reading entries much more secure
|
||||
^ editing entries is similarly secure to before - greater improvements coming in a future update
|
||||
- folder renaming has been re-enabled and now functions as intended
|
||||
- some unnecessarily verbose outputs were silenced and/or made to appear more cleanly
|
||||
- FreeBSD packaging fixes
|
||||
^ no longer incorrectly includes and uses the logic for the Termux clipboard
|
||||
^ now specifies 'python3' dependency instead of 'python'
|
||||
- many lists provided to the subprocess module have been swapped with tuples
|
||||
- more correct and clear language is used to describe options and arguments in the help menus
|
||||
- properly display an error when attempting to copy blank fields from entries
|
||||
- shebangs have been removed from libraries not meant to be run directly
|
||||
|
||||
other factors of note:
|
||||
|
||||
- with the release of Debian 12 Bookworm, sshyp is once again fully supported on vanilla Debian
|
||||
^ previously, it was only working correctly in offline mode due to Debian 11's old OpenSSH package
|
||||
- this is the biggest release of sshyp ever
|
||||
^ it includes LOTS of minor changes and optimizations not included in the patch notes summary
|
||||
^ as such, keep an eye out for new bugs!
|
||||
- the next major release of sshyp may be even larger...
|
||||
^ but it is also likely very far away
|
||||
^ GnuPG is great, but it is inherently incompatible with sshyp's future security model
|
||||
^ if all goes according to plan, GnuPG will be replaced in the next release
|
||||
^ assuming this happens, this next release will be crowned v2.0.0
|
||||
^ sshyp v1.5.X is expected to be the latest stable release for a longer amount of time than usual
|
||||
^ sshyp v1.5.X may receive security/bug fix patches after the release of v2.0.0, as needed
|
||||
|
||||
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
||||
|
||||
sshyp v1.4.3
|
||||
06/08/2023
|
||||
|
||||
the argumentative agronomist update - patch three
|
||||
|
||||
IMPORTANT: the next major release of sshyp, v1.5.0, will make breaking changes that will require
|
||||
running through 'sshyp tweak' again - also starting with v1.5.0, a copy of the changelog will no
|
||||
longer be packaged with sshyp (it will still be available on GitHub) and clipboard tools
|
||||
(x-clip and wl-clipboard) will be set to optional dependencies ('sshyp tweak' will warn of this)
|
||||
|
||||
this is a highly unconventional release featuring backports to fix critical bugs found during the
|
||||
development of the upcoming v1.5.0
|
||||
|
||||
fixes:
|
||||
|
||||
- folder renaming has been disabled to prevent a severe data loss bug from occuring
|
||||
^ this has been properly fixed for v1.5.0, but the fix requires all clients and servers
|
||||
to be updated - to avoid breaking compatibility with 1.4.X, folder renaming was simply disabled
|
||||
for the time being
|
||||
- backported fix for syncing over IPv6
|
||||
- backported fix for multiple versions of Powershell being called under WSL
|
||||
- backported fix for checking /lib/sshyp instead of /usr/lib/sshyp
|
||||
|
||||
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
||||
|
||||
sshyp v1.4.2
|
||||
04/30/2023
|
||||
|
||||
|
||||
+73
-56
@@ -1,14 +1,17 @@
|
||||
.TH sshyp 1 "08 June 2023" "v1.4.3" "sshyp man page"
|
||||
.TH sshyp 1 "24 October 2023" "v1.5.1" "sshyp man page"
|
||||
.SH NAME
|
||||
\fBsshyp\fR - Simple, self-hosted, synchronized password management for UNIX(-like) systems. Alternative to (and compatible with) pass/password-store.
|
||||
.SH SYNOPSIS
|
||||
Client Usage: sshyp [</entry name> [option] [flag]] [option]
|
||||
Client Usage: sshyp [/<entry name> [argument] [option]] | [argument]
|
||||
|
||||
Server Usage: sshyp <option> [flag] [device id]
|
||||
Server Usage: sshyp <argument>
|
||||
.SH DESCRIPTION
|
||||
sshyp is a lightweight password and note management program written in Python. sshyp is used via CLI and is self-hosted with a client-server model. sshyp expects that you have access to a personal server with a properly configured ssh server, ideally accepting remote connections.
|
||||
.SH EXAMPLES (CLIENT)
|
||||
Setting up sshyp for the first time or altering its configuration (also recommended after major updates):
|
||||
Setting up sshyp for the first time:
|
||||
sshyp init
|
||||
|
||||
Altering the existing sshyp configuration:
|
||||
sshyp tweak
|
||||
|
||||
Viewing the entry database:
|
||||
@@ -17,6 +20,12 @@ Viewing the entry database:
|
||||
Reading an existing entry saved as '~/.local/share/sshyp/development/github.gpg':
|
||||
sshyp /development/github
|
||||
|
||||
Removing an existing folder saved as '~/.local/share/sshyp/social':
|
||||
sshyp /social shear
|
||||
|
||||
Reading the same entry without hiding the password:
|
||||
sshyp /development/github --show
|
||||
|
||||
Copying the password of an existing entry saved as '~/.local/share/sshyp/financial/bank.gpg':
|
||||
sshyp /financial/bank copy -p
|
||||
|
||||
@@ -31,63 +40,43 @@ Creating a note-only entry saved as '~/.local/share/sshyp/notes/test note.gpg':
|
||||
|
||||
Manually syncing entries with the server:
|
||||
sshyp sync
|
||||
|
||||
Removing an existing folder saved as '~/.local/share/sshyp/social':
|
||||
sshyp /social/ shear
|
||||
.SH EXAMPLES (SERVER)
|
||||
Setting up the quick-unlock whitelist:
|
||||
sshyp whitelist setup
|
||||
|
||||
Checking the quick-unlock whitelist status of all registered client devices:
|
||||
sshyp whitelist list
|
||||
|
||||
Adding a device with the id 'laptop-im|lAoa;&;r&o:ez((g/Dz;EVb.nO_' to the quick-unlock whitelist:
|
||||
sshyp whitelist add 'laptop-im|lAoa;&;r&o:ez((g/Dz;EVb.nO_'
|
||||
|
||||
Removing a device with the id 'laptop-im|lAoa;&;r&o:ez((g/Dz;EVb.nO_' from the quick-unlock whitelist:
|
||||
sshyp whitelist del 'laptop-im|lAoa;&;r&o:ez((g/Dz;EVb.nO_'
|
||||
.SH ARGUMENTS (CLIENT)
|
||||
help/-h bring up the help menu
|
||||
version/-v display sshyp version info
|
||||
init set up sshyp
|
||||
tweak change configuration options/manage extensions and updates
|
||||
add add an entry
|
||||
gen generate a new password
|
||||
edit edit an existing entry
|
||||
copy copy details of an entry to your clipboard
|
||||
shear delete an existing entry
|
||||
sync manually sync the entry directory via sshync
|
||||
.SH OPTIONS (CLIENT)
|
||||
help/-h bring up the help menu
|
||||
version/-v display sshyp version info
|
||||
tweak configure sshyp
|
||||
add add an entry
|
||||
gen generate a new password
|
||||
edit edit an existing entry
|
||||
copy copy details of an entry to your clipboard
|
||||
shear delete an existing entry
|
||||
sync manually sync the entry directory via sshync
|
||||
.SH FLAGS (CLIENT)
|
||||
add:
|
||||
password/-p add a password entry
|
||||
note/-n add a note entry
|
||||
folder/-f add a new folder for entries
|
||||
password/-p add a password entry
|
||||
note/-n add a note entry
|
||||
folder/-f add a new folder for entries
|
||||
|
||||
edit:
|
||||
rename/relocate/-r rename or relocate an entry
|
||||
username/-u change the username of an entry
|
||||
password/-p change the password of an entry
|
||||
note/-n change the note attached to an entry
|
||||
url/-l change the url attached to an entry
|
||||
rename/relocate/-r rename or relocate an entry
|
||||
username/-u change the username of an entry
|
||||
password/-p change the password of an entry
|
||||
note/-n change the note attached to an entry
|
||||
url/-l change the url attached to an entry
|
||||
|
||||
copy:
|
||||
username/-u copy the username of an entry to your clipboard
|
||||
password/-p copy the password of an entry to your clipboard
|
||||
url/-l copy the URL of an entry to your clipboard
|
||||
note/-n copy the note of an entry to your clipboard
|
||||
username/-u copy the username of an entry to your clipboard
|
||||
password/-p copy the password of an entry to your clipboard
|
||||
url/-l copy the URL of an entry to your clipboard
|
||||
note/-n copy the note of an entry to your clipboard
|
||||
|
||||
gen:
|
||||
update/-u generate a password for an existing entry
|
||||
.SH OPTIONS (SERVER)
|
||||
help/-h bring up this menu
|
||||
version/-v display sshyp version info
|
||||
tweak configure sshyp
|
||||
whitelist manage the quick-unlock whitelist
|
||||
.SH FLAGS (SERVER)
|
||||
whitelist:
|
||||
setup set up the quick-unlock whitelist
|
||||
list/-l view all registered device ids and their quick-unlock whitelist status
|
||||
add whitelist a device id for quick-unlock
|
||||
del remove a device id from the quick-unlock whitelist
|
||||
update/-u generate a password for an existing entry
|
||||
.SH ARGUMENTS (SERVER)
|
||||
help/-h bring up this menu
|
||||
version/-v display sshyp version info
|
||||
init set up sshyp
|
||||
tweak change configuration options/manage extensions and updates
|
||||
.SH SETUP
|
||||
sshyp operates on a client-server model, and thus requires you to have access to your own server (whether it be a physical home server or a cloud rental) with remote access via SSH.
|
||||
|
||||
@@ -97,19 +86,45 @@ Server setup:
|
||||
Configure an OpenSSH server (if sshyp was installed from the Debian package, openssh-server and openssh-sftp-server must be installed manually - if sshyp was installed from the Fedora package, openssh-server must be installed manually)
|
||||
Allow remote access to the SSH server w/public key authentication (disabling password-only authentication recommended)
|
||||
Install sshyp
|
||||
Run "sshyp tweak" and set the device type as server
|
||||
Optionally, run "sshyp whitelist setup" and configure quick-unlock
|
||||
Run "sshyp init" and set the device type as server
|
||||
Optionally, configure a quick-unlock pin from the tweak menu
|
||||
Done!
|
||||
|
||||
Client setup:
|
||||
Install sshyp
|
||||
Run "sshyp tweak" and follow the prompts
|
||||
Run "sshyp init" and follow the prompts
|
||||
Copy the generated public SSH key (located at ~/.ssh/sshyp.pub) to the server using preferred method (manually adding to authorized_keys, ssh-copy-id, etc.)
|
||||
If you already have entries on the server, sync them using "sshyp sync"
|
||||
Optionally, shell completions (both Bash and ZSH) can be enabled with your shell's respective method
|
||||
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.
|
||||
.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
|
||||
0 - no error
|
||||
|
||||
@@ -122,5 +137,7 @@ Please note that the server setup intentionally does not allow the reading of en
|
||||
4 - decryption/encryption error
|
||||
|
||||
5 - server connection error
|
||||
|
||||
6 - other error
|
||||
.SH AUTHOR
|
||||
Randall Winkhart (https://github.com/rwinkhart)
|
||||
|
||||
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
|
||||
Executable → Regular
+38
-34
@@ -1,9 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
from os import listdir, remove, walk
|
||||
from os.path import expanduser, isdir, getmtime, join
|
||||
from os.path import expanduser, isdir, isfile, getmtime, join
|
||||
from subprocess import CalledProcessError, PIPE, run
|
||||
from sys import exit as s_exit
|
||||
home = expanduser("~")
|
||||
home = expanduser('~')
|
||||
|
||||
|
||||
# PORT START SSHYNC-REMOTE
|
||||
@@ -33,7 +32,7 @@ def remote_list_gen(_client_device_name, _remote_dir):
|
||||
|
||||
# HYBRID
|
||||
# deletes a file or folder and/or marks it for deletion upon syncing
|
||||
def delete(_file_path, _target_database):
|
||||
def delete(_file_path, _target_database, _silent):
|
||||
from shutil import rmtree
|
||||
_directory = f"{home}/.local/share/sshyp/"
|
||||
try:
|
||||
@@ -42,7 +41,8 @@ def delete(_file_path, _target_database):
|
||||
else:
|
||||
remove(f"{_directory}{_file_path}.gpg")
|
||||
except FileNotFoundError:
|
||||
print(f"location does not exist {_target_database}")
|
||||
if not _silent:
|
||||
print(f"location does not exist {_target_database}")
|
||||
if _target_database == 'remotely':
|
||||
for _device_name in listdir(f"{home}/.config/sshyp/devices"):
|
||||
open(f"{home}/.config/sshyp/deleted/" + _file_path.replace('/', '\x1e') + '\x1f' + _device_name, 'w')
|
||||
@@ -67,9 +67,9 @@ def get_local_data(_directory, _device):
|
||||
# captures and returns all necessary data from the remote server
|
||||
def remote_list_fetch(_user_data):
|
||||
try:
|
||||
_remote_data = run(['ssh', '-i', _user_data[5], '-p', _user_data[2], f"{_user_data[0]}@{_user_data[1]}",
|
||||
_remote_data = run(('ssh', '-i', _user_data[5], '-p', _user_data[2], f"{_user_data[0]}@{_user_data[1]}",
|
||||
f'cd /usr/lib/sshyp; python3 -c \'from sshync import remote_list_gen; remote_list_gen'
|
||||
f'("{_user_data[6]}", "{_user_data[4]}")\''], stdout=PIPE, text=True, check=True
|
||||
f'("{_user_data[6]}", "{_user_data[4]}")\''), stdout=PIPE, text=True, check=True
|
||||
).stdout.split('\x1d')
|
||||
except CalledProcessError:
|
||||
print('\n\u001b[38;5;9merror: failed to connect to the remote server\u001b[0m\n')
|
||||
@@ -84,11 +84,15 @@ def remote_list_fetch(_user_data):
|
||||
|
||||
# checks for and acts upon files and folders marked for deletion
|
||||
def deletion_sync(_deletion_database, _silent):
|
||||
_synced = False
|
||||
for _file in _deletion_database:
|
||||
if _file != '':
|
||||
if not _silent:
|
||||
_synced = True
|
||||
print(f"\u001b[38;5;208m{_file}\u001b[0m has been sheared, removing...")
|
||||
delete(_file, 'locally')
|
||||
delete(_file, 'locally', _silent)
|
||||
if _synced:
|
||||
print()
|
||||
|
||||
|
||||
# creates matches of remote folders on the local client
|
||||
@@ -117,25 +121,21 @@ def sort_titles_mods(_list_1, _list_2):
|
||||
return _title_list_2_sorted, _mod_list_2_sorted
|
||||
|
||||
|
||||
# creates a sshync job profile
|
||||
def make_profile(_profile_dir, _local_dir, _remote_dir, _identity, _ip, _port, _user):
|
||||
open(_profile_dir, 'w').write(f"{_user}\n{_ip}\n{_port}\n{_local_dir}\n{_remote_dir}\n{_identity}\n")
|
||||
|
||||
|
||||
# returns a list of data read from a sshync job profile
|
||||
def get_profile(_profile_dir):
|
||||
try:
|
||||
_profile_data = open(_profile_dir).readlines()
|
||||
except (FileNotFoundError, IndexError):
|
||||
from configparser import ConfigParser
|
||||
_profile_data = ConfigParser(interpolation=None)
|
||||
if isfile(_profile_dir):
|
||||
_profile_data.read(_profile_dir)
|
||||
else:
|
||||
print('\n\u001b[38;5;9merror: the profile does not exist or is corrupted\u001b[0m\n')
|
||||
_profile_data = None
|
||||
s_exit(2)
|
||||
_user = _profile_data[0].rstrip()
|
||||
_ip = _profile_data[1].rstrip()
|
||||
_port = _profile_data[2].rstrip()
|
||||
_local_dir = _profile_data[3].rstrip()
|
||||
_remote_dir = _profile_data[4].rstrip()
|
||||
_identity = _profile_data[5].rstrip()
|
||||
_user = _profile_data.get('SSHYNC', 'user')
|
||||
_ip = _profile_data.get('SSHYNC', 'ip')
|
||||
_port = _profile_data.get('SSHYNC', 'port')
|
||||
_local_dir = _profile_data.get('SSHYNC', 'local_dir')
|
||||
_remote_dir = _profile_data.get('SSHYNC', 'remote_dir')
|
||||
_identity = _profile_data.get('SSHYNC', 'identity_file')
|
||||
_client_device_id = listdir(f"{home}/.config/sshyp/devices")[0].rstrip()
|
||||
return _user, _ip, _port, _local_dir, _remote_dir, _identity, _client_device_id
|
||||
|
||||
@@ -154,27 +154,31 @@ def run_profile(_profile_dir, _silent):
|
||||
_index_local = sort_titles_mods(_remote_titles_mods, get_local_data(_user_data[3], 'client'))
|
||||
_index_remote = sort_titles_mods(_index_local, _remote_titles_mods)
|
||||
# sync new and updated files
|
||||
_i = -1
|
||||
_i, _synced = -1, False
|
||||
for _title in _index_local[0]:
|
||||
_i += 1
|
||||
if _title in _index_remote[0]:
|
||||
# compare mod times and sync
|
||||
if int(_index_local[1][_i]) > int(_index_remote[1][_i]):
|
||||
_synced = True
|
||||
print(f"\u001b[38;5;4m{_title[:-4]}\u001b[0m is newer locally, uploading...")
|
||||
run(['scp', '-pqs', '-P', _user_data[2], '-i', _user_data[5], _user_data[3] + _title,
|
||||
f"{_user_data[0]}@[{_user_data[1]}]:{_user_data[4]}{'/'.join(_title.split('/')[:-1]) + '/'}"])
|
||||
run(('scp', '-pqs', '-P', _user_data[2], '-i', _user_data[5], _user_data[3] + _title,
|
||||
f"{_user_data[0]}@[{_user_data[1]}]:{_user_data[4]}{_title}"))
|
||||
elif int(_index_local[1][_i]) < int(_index_remote[1][_i]):
|
||||
_synced = True
|
||||
print(f"\u001b[38;5;2m{_title[:-4]}\u001b[0m is newer remotely, downloading...")
|
||||
run(['scp', '-pqs', '-P', _user_data[2], '-i', _user_data[5],
|
||||
f"{_user_data[0]}@[{_user_data[1]}]:{_user_data[4]}{_title}",
|
||||
f"{_user_data[3]}{'/'.join(_title.split('/')[:-1]) + '/'}"])
|
||||
run(('scp', '-pqs', '-P', _user_data[2], '-i', _user_data[5],
|
||||
f"{_user_data[0]}@[{_user_data[1]}]:{_user_data[4]}{_title}", f"{_user_data[3]}{_title}"))
|
||||
else:
|
||||
_synced = True
|
||||
print(f"\u001b[38;5;4m{_title[:-4]}\u001b[0m is not on remote server, uploading...")
|
||||
run(['scp', '-pqs', '-P', _user_data[2], '-i', _user_data[5], _user_data[3] + _title,
|
||||
f"{_user_data[0]}@[{_user_data[1]}]:{_user_data[4]}{'/'.join(_title.split('/')[:-1]) + '/'}"])
|
||||
run(('scp', '-pqs', '-P', _user_data[2], '-i', _user_data[5], _user_data[3] + _title,
|
||||
f"{_user_data[0]}@[{_user_data[1]}]:{_user_data[4]}{_title}"))
|
||||
for _title in _index_remote[0]:
|
||||
if _title not in _index_local[0]:
|
||||
_synced = True
|
||||
print(f"\u001b[38;5;2m{_title[:-4]}\u001b[0m is not in local directory, downloading...")
|
||||
run(['scp', '-pqs', '-P', _user_data[2], '-i', _user_data[5],
|
||||
f"{_user_data[0]}@[{_user_data[1]}]:{_user_data[4]}{_title}",
|
||||
f"{_user_data[3]}{'/'.join(_title.split('/')[:-1]) + '/'}"])
|
||||
run(('scp', '-pqs', '-P', _user_data[2], '-i', _user_data[5],
|
||||
f"{_user_data[0]}@[{_user_data[1]}]:{_user_data[4]}{_title}", f"{_user_data[3]}{_title}"))
|
||||
if _synced:
|
||||
print()
|
||||
|
||||
+352
-528
File diff suppressed because it is too large
Load Diff
+616
@@ -0,0 +1,616 @@
|
||||
from configparser import ConfigParser
|
||||
from curses import A_REVERSE, KEY_DOWN, KEY_UP, curs_set, newwin
|
||||
from curses.textpad import rectangle, Textbox
|
||||
from os import environ, listdir, remove
|
||||
from os.path import exists, expanduser, isfile
|
||||
from pathlib import Path
|
||||
from random import randint
|
||||
from shutil import get_terminal_size, which
|
||||
from subprocess import PIPE, run
|
||||
# PORT START UNAME-IMPORT-STWEAK
|
||||
from os import uname
|
||||
# PORT END UNAME-IMPORT-STWEAK
|
||||
home, sshyp_data, stdscr = expanduser('~'), ConfigParser(interpolation=None), None
|
||||
if isfile(f"{home}/.config/sshyp/sshyp.ini"):
|
||||
_exists_flag = True
|
||||
sshyp_data.read(f"{home}/.config/sshyp/sshyp.ini")
|
||||
else:
|
||||
_exists_flag = False
|
||||
|
||||
|
||||
# writes data stored in ConfigParser to the correct config file
|
||||
def write_config(_sshyp_data=sshyp_data):
|
||||
with open(f"{home}/.config/sshyp/sshyp.ini", 'w') as configfile:
|
||||
_sshyp_data.write(configfile)
|
||||
|
||||
|
||||
# creates a radio selection between the provided options
|
||||
def curses_radio(_options, _pretext):
|
||||
curs_set(0)
|
||||
_selected = 0
|
||||
while True:
|
||||
stdscr.clear()
|
||||
stdscr.addstr(0, 0, _pretext)
|
||||
for _i, _option in enumerate(_options):
|
||||
_y = _i + _pretext.count('\n') + 2
|
||||
if _i == _selected:
|
||||
stdscr.addstr(_y, 0, "[*] " + _option, A_REVERSE)
|
||||
else:
|
||||
stdscr.addstr(_y, 0, "[ ] " + _option)
|
||||
stdscr.refresh()
|
||||
_key = stdscr.getch()
|
||||
# update _selected based on user input
|
||||
if _key == KEY_UP:
|
||||
_selected = (_selected-1) % len(_options)
|
||||
elif _key == KEY_DOWN:
|
||||
_selected = (_selected+1) % len(_options)
|
||||
elif _key == ord('\n'):
|
||||
break
|
||||
stdscr.refresh()
|
||||
curs_set(1)
|
||||
return _selected
|
||||
|
||||
|
||||
# creates a text-box input
|
||||
def curses_text(_pretext):
|
||||
stdscr.clear()
|
||||
stdscr.addstr(0, 0, _pretext)
|
||||
_term_columns = get_terminal_size()[0]
|
||||
_editwin = newwin(1, _term_columns-2, 3, 1)
|
||||
rectangle(stdscr, 2, 0, 4, _term_columns-1)
|
||||
stdscr.refresh()
|
||||
_box = Textbox(_editwin)
|
||||
# let the user edit until ctrl+g/enter is struck
|
||||
_box.edit()
|
||||
# return resulting contents
|
||||
return _box.gather().strip()
|
||||
|
||||
|
||||
# device+sync type selection
|
||||
def install_type():
|
||||
_offline_mode = 'false'
|
||||
# PORT START TWEAK-DEVTYPE
|
||||
_install_type = curses_radio(('client (ssh-synchronized)', 'client (offline)', 'server'),
|
||||
'device + sync type configuration')
|
||||
# PORT END TWEAK-DEVTYPE
|
||||
if _install_type == 2:
|
||||
_dev_type = 'server'
|
||||
Path(f"{home}/.config/sshyp/deleted").mkdir(mode=0o700, exist_ok=True)
|
||||
Path(f"{home}/.config/sshyp/whitelist").mkdir(mode=0o700, exist_ok=True)
|
||||
curses_radio(['okay'], 'make sure the ssh service is running and properly configured')
|
||||
else:
|
||||
_dev_type = 'client'
|
||||
if _install_type == 1:
|
||||
_offline_mode = 'true'
|
||||
if not sshyp_data.has_section('CLIENT-GENERAL'):
|
||||
sshyp_data.add_section('CLIENT-GENERAL')
|
||||
sshyp_data.set('CLIENT-GENERAL', 'offline_mode_enabled', _offline_mode)
|
||||
if not sshyp_data.has_section('GENERAL'):
|
||||
sshyp_data.add_section('GENERAL')
|
||||
sshyp_data.set('GENERAL', 'device_type', _dev_type)
|
||||
write_config()
|
||||
return _dev_type, _offline_mode
|
||||
|
||||
|
||||
# gpg configuration
|
||||
def gpg_config():
|
||||
# gpg key selection
|
||||
_uid_list = [_item for _item in run(('gpg', '-k', '--with-colons'),
|
||||
stdout=PIPE, text=True).stdout.splitlines() if _item.startswith('uid')]
|
||||
_named_uid_list = []
|
||||
for _uid in _uid_list:
|
||||
_named_uid_list.append(_uid.split(':')[9].replace('\\x3a', ':').replace('\\x5c', '\\'))
|
||||
_named_uid_list.append('auto-generate')
|
||||
_gpg_id_sel = curses_radio(_named_uid_list, 'gpg key selection')
|
||||
if _gpg_id_sel == len(_named_uid_list)-1:
|
||||
if not isfile(f"{home}/.config/sshyp/gpg-gen"):
|
||||
open(f"{home}/.config/sshyp/gpg-gen", 'w').writelines([
|
||||
'Key-Type: 1\n', 'Key-Length: 4096\n', 'Key-Usage: sign encrypt\n', 'Name-Real: sshyp\n',
|
||||
'Name-Comment: gpg-sshyp\n', 'Name-Email: github.com/rwinkhart/sshyp\n',
|
||||
'Expire-Date: 0'])
|
||||
run(('gpg', '-q', '--batch', '--generate-key', f"{home}/.config/sshyp/gpg-gen"))
|
||||
remove(f"{home}/.config/sshyp/gpg-gen")
|
||||
_gpg_id = run(('gpg', '-k', '--with-colons'), stdout=PIPE, text=True).stdout.splitlines()[-1].split(':')[9]
|
||||
else:
|
||||
_gpg_id = _named_uid_list[_gpg_id_sel]
|
||||
|
||||
# lock file generation
|
||||
if isfile(f"{home}/.config/sshyp/lock.gpg"):
|
||||
remove(f"{home}/.config/sshyp/lock.gpg")
|
||||
open(f"{home}/.config/sshyp/lock", 'w')
|
||||
run(('gpg', '-qr', _gpg_id, '-e', f"{home}/.config/sshyp/lock"))
|
||||
remove(f"{home}/.config/sshyp/lock")
|
||||
|
||||
if not sshyp_data.has_section('CLIENT-GENERAL'):
|
||||
sshyp_data.add_section('CLIENT-GENERAL')
|
||||
sshyp_data.set('CLIENT-GENERAL', 'gpg_id', _gpg_id)
|
||||
write_config()
|
||||
|
||||
|
||||
# text editor configuration
|
||||
def editor_config(_env_mode):
|
||||
if _env_mode:
|
||||
# set default text editor to value of EDITOR environment variable, otherwise default to vi
|
||||
if 'EDITOR' in environ:
|
||||
_editor = environ['EDITOR']
|
||||
else:
|
||||
_editor = 'vi'
|
||||
else:
|
||||
_editor = curses_text('enter the name of your preferred text editor:\n\n\n\n\n(ctrl+g/enter to confirm)'
|
||||
'\n\nthis will be used for writing notes\n\nexample input: vim')
|
||||
if not sshyp_data.has_section('CLIENT-GENERAL'):
|
||||
sshyp_data.add_section('CLIENT-GENERAL')
|
||||
sshyp_data.set('CLIENT-GENERAL', 'text_editor', _editor)
|
||||
write_config()
|
||||
|
||||
|
||||
# ssh+sshync configuration
|
||||
def ssh_config():
|
||||
# private key selection/generation
|
||||
_keys = []
|
||||
# ensure ~/.ssh directory exists
|
||||
Path(f"{home}/.ssh").mkdir(mode=0o700, exist_ok=True)
|
||||
for _file in listdir(f"{home}/.ssh"):
|
||||
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}"):
|
||||
_keys.append(f"{home}/.ssh/{_file}")
|
||||
_keys.extend(['auto-generate', 'other (type the location)'])
|
||||
_key_selected_num = curses_radio(_keys, 'which private ssh key would you like to use for sshyp?')
|
||||
_gen_index = len(_keys)-2
|
||||
if _key_selected_num >= _gen_index:
|
||||
_ssh_key = expanduser(curses_text('enter the location for your private ssh key:\n\n\n\n\n(ctrl+g/enter to '
|
||||
'confirm)\n\nexample input:\n\n~/.ssh/privkey'))
|
||||
if _key_selected_num == _gen_index:
|
||||
_passphrase = curses_text('enter your desired ssh keyfile passphrase:\n\n\n\n\n(ctrl+g/enter to confirm)'
|
||||
'\n\ntip: you can leave this blank to use the keyfile without a passphrase')
|
||||
run(('ssh-keygen', '-q', '-t', 'ed25519', '-N', _passphrase, '-f', _ssh_key))
|
||||
else:
|
||||
_ssh_key = _keys[_key_selected_num]
|
||||
|
||||
# ssh+sshync configuration
|
||||
_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.10:22\n ipv6: user@[2000:2000:2000:2000'
|
||||
':2000:2000:2000:2000]:22\n domain: user@mydomain.com:22')
|
||||
_uiport_split = _uiport.split('@')
|
||||
_username_ssh = _uiport_split[0]
|
||||
_iport = _uiport_split[1].lstrip('[').replace(']', '').rsplit(':', 1)
|
||||
|
||||
if not sshyp_data.has_section('SSHYNC'):
|
||||
sshyp_data.add_section('SSHYNC')
|
||||
sshyp_data.set('SSHYNC', 'user', _username_ssh)
|
||||
sshyp_data.set('SSHYNC', 'ip', _iport[0])
|
||||
sshyp_data.set('SSHYNC', 'port', _iport[1])
|
||||
sshyp_data.set('SSHYNC', 'local_dir', f"{home}/.local/share/sshyp/")
|
||||
sshyp_data.set('SSHYNC', 'remote_dir', f"/home/{_username_ssh}/.local/share/sshyp/")
|
||||
sshyp_data.set('SSHYNC', 'identity_file', _ssh_key)
|
||||
write_config()
|
||||
return _iport[1], _username_ssh, _iport[0], _ssh_key
|
||||
|
||||
|
||||
# device id configuration
|
||||
def dev_id_config(_ip, _username_ssh, _port, _identity):
|
||||
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 '
|
||||
'id must be unique amongst your client devices\n\nthis is used to keep track of '
|
||||
'database syncing and quick-unlock permissions\n')
|
||||
_device_id_suffix = string_gen('f', randint(24, 48))
|
||||
_device_id = _device_id_prefix + '-' + _device_id_suffix
|
||||
# remove existing device ids
|
||||
for _id in listdir(f"{home}/.config/sshyp/devices"):
|
||||
remove(f"{home}/.config/sshyp/devices/{_id}")
|
||||
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, _identity, sshyp_data)
|
||||
|
||||
|
||||
# quick-unlock configuration
|
||||
def quick_unlock_config(_default):
|
||||
if _default:
|
||||
_enabled = 'false'
|
||||
else:
|
||||
_quick_unlock_sel = curses_radio(('no', 'yes'), 'WARNING: quick-unlock is only as secure as the environment you'
|
||||
' use it in\n\nquick-unlock allows you to use a shorter version'
|
||||
' of your gpg key passphrase and\nrequires a constant '
|
||||
'connection to your sshyp server to authenticate\n\na '
|
||||
'compromised program on your computer could scan the process '
|
||||
'list in the\nbrief period during decryption to retrieve the '
|
||||
'necessary information to decrypt your entries\n\nenable '
|
||||
'quick-unlock?')
|
||||
if _quick_unlock_sel == 1:
|
||||
_enabled = 'true'
|
||||
else:
|
||||
_enabled = 'false'
|
||||
if not sshyp_data.has_section('CLIENT-ONLINE'):
|
||||
sshyp_data.add_section('CLIENT-ONLINE')
|
||||
sshyp_data.set('CLIENT-ONLINE', 'quick_unlock_enabled', _enabled)
|
||||
write_config()
|
||||
return _enabled
|
||||
|
||||
|
||||
# re-encrypt/optimize all entries
|
||||
def refresh_encryption():
|
||||
_directory = f"{home}/.local/share/sshyp"
|
||||
|
||||
# warn the user of potential data loss and prompt to continue
|
||||
_proceed = curses_radio(('no', 'yes'), "WARNING: proceeding with this action will remove/overwrite any directories"
|
||||
f" matching the following:\n\n{home}/.local/share/sshyp.old\n{home}/.local/"
|
||||
"share/sshyp.new\n\nare you sure you wish to re-encrypt all entries?")
|
||||
if _proceed != 1:
|
||||
return 3
|
||||
|
||||
# set new gpg key
|
||||
gpg_config()
|
||||
|
||||
from os import walk
|
||||
from os.path import isdir
|
||||
from shutil import move, rmtree
|
||||
from sshyp import decrypt, encrypt
|
||||
|
||||
# prompt for unlock and display do not close warning
|
||||
decrypt(None)
|
||||
curses_radio(['okay'], 'entry optimization may take some time - select "okay" to start - '
|
||||
'do not terminate this process!')
|
||||
|
||||
# remove existing conflicts
|
||||
for _extension in ('.new', '.old'):
|
||||
if exists(f"{_directory}{_extension}"):
|
||||
rmtree(f"{_directory}{_extension}")
|
||||
|
||||
# decrypt, optimize, and re-encrypt each entry with the newly selected key
|
||||
if isdir(_directory):
|
||||
_gpg_id = sshyp_data.get('CLIENT-GENERAL', 'gpg_id')
|
||||
for _root, _dirs, _files in sorted(walk(_directory, topdown=True)):
|
||||
for _filename in _files:
|
||||
Path(_root.replace(_directory, _directory + '.new', 1)).mkdir(0o700, parents=True, exist_ok=True)
|
||||
encrypt(decrypt(f"{_root}/{_filename[:-4]}"),
|
||||
f"{_root.replace(_directory, _directory + '.new', 1)}/{_filename[:-4]}", _gpg_id)
|
||||
|
||||
# create a backup of the original version and activate the new version
|
||||
move(_directory, _directory + '.old')
|
||||
move(_directory + '.new', _directory)
|
||||
return 1
|
||||
else:
|
||||
return 2
|
||||
|
||||
|
||||
# PORT START WHITELIST-SERVER
|
||||
# takes input from the user to set up quick-unlock pin
|
||||
def whitelist_setup():
|
||||
_gpg_password_temp = str(curses_text('full gpg passphrase:\n\n\n\n\n(ctrl+g/enter to confirm)'))
|
||||
_half_length = int(len(_gpg_password_temp)/2)
|
||||
try:
|
||||
_short_password_length = int(curses_text(f"quick unlock pin length ({_half_length}):\n\n\n\n\n(ctrl+g/enter "
|
||||
"to confirm)\n\npin must be half the length of the gpg passphrase "
|
||||
"or less\n\ncannot be a negative number"))
|
||||
if not 0 <= _short_password_length <= _half_length:
|
||||
_short_password_length = _half_length
|
||||
except ValueError:
|
||||
_short_password_length = _half_length
|
||||
_i, _quick_unlock_password, _quick_unlock_password_excluded = 0, '', ''
|
||||
for _char in _gpg_password_temp:
|
||||
if _i % 2 == 1 and _i < _short_password_length*2:
|
||||
_quick_unlock_password += _char
|
||||
else:
|
||||
_quick_unlock_password_excluded += _char
|
||||
_i += 1
|
||||
|
||||
# create assembly key
|
||||
open(f"{home}/.config/sshyp/gpg-gen", 'w').writelines([
|
||||
'Key-Type: 1\n', 'Key-Length: 4096\n', 'Key-Usage: sign encrypt\n', 'Name-Real: sshyp\n',
|
||||
'Name-Comment: gpg-sshyp-whitelist\n', 'Name-Email: github.com/rwinkhart/sshyp\n', 'Expire-Date: 0'])
|
||||
run(('gpg', '-q', '--pinentry-mode', 'loopback', '--batch', '--generate-key', '--passphrase',
|
||||
_quick_unlock_password, f"{home}/.config/sshyp/gpg-gen"))
|
||||
remove(f"{home}/.config/sshyp/gpg-gen")
|
||||
_gpg_id = run(('gpg', '-k', '--with-colons'), stdout=PIPE, text=True).stdout.splitlines()[-1].split(':')[9]
|
||||
|
||||
# encrypt excluded with the assembly key
|
||||
from sshyp import encrypt
|
||||
encrypt(_quick_unlock_password_excluded, f"{home}/.config/sshyp/excluded", _gpg_id)
|
||||
curses_radio(['okay, I have it memorized'], f"your quick-unlock pin: {_quick_unlock_password}")
|
||||
|
||||
|
||||
# adds or removes quick-unlock whitelisted device ids
|
||||
def whitelist_manage(_action):
|
||||
_whitelisted_ids = listdir(f"{home}/.config/sshyp/whitelist")
|
||||
_device_ids = listdir(f"{home}/.config/sshyp/devices")
|
||||
|
||||
# a value of True indicates adding
|
||||
if _action:
|
||||
_unwhitelisted_ids = []
|
||||
for _id in _device_ids:
|
||||
if _id not in _whitelisted_ids:
|
||||
_unwhitelisted_ids.append(_id)
|
||||
_unwhitelisted_ids.append('cancel')
|
||||
_add_id = curses_radio(_unwhitelisted_ids, 'id to add to whitelist:')
|
||||
if _add_id == len(_unwhitelisted_ids)-1:
|
||||
return
|
||||
open(f"{home}/.config/sshyp/whitelist/{_unwhitelisted_ids[_add_id]}", 'w').write('')
|
||||
else:
|
||||
_whitelisted_choices = _whitelisted_ids + ['cancel']
|
||||
_del_id = curses_radio(_whitelisted_choices, 'id to remove from whitelist:')
|
||||
if _del_id == len(_whitelisted_choices)-1:
|
||||
return
|
||||
remove(f"{home}/.config/sshyp/whitelist/{_whitelisted_ids[_del_id]}")
|
||||
|
||||
# prune deleted device ids from whitelist
|
||||
for _id in _whitelisted_ids:
|
||||
if _id not in _device_ids:
|
||||
remove(f"{home}/.config/sshyp/whitelist/{_id}")
|
||||
|
||||
|
||||
# runs quick-unlock configuration menu
|
||||
def whitelist_menu():
|
||||
while True:
|
||||
_choice = curses_radio(('setup/create pin', 'add to whitelist', 'remove from whitelist',
|
||||
'BACK'), 'quick-unlock/whitelist management')
|
||||
if _choice == 0:
|
||||
whitelist_setup()
|
||||
elif _choice == 1:
|
||||
whitelist_manage(True)
|
||||
elif _choice == 2:
|
||||
whitelist_manage(False)
|
||||
else:
|
||||
break
|
||||
# PORT END WHITELIST-SERVER
|
||||
|
||||
|
||||
# PORT START TWEAK-EXTEND-FUNCTIONS
|
||||
# downloads/updates extensions
|
||||
def extension_downloader():
|
||||
from os import chmod
|
||||
from tempfile import gettempdir
|
||||
from urllib.request import urlopen, urlretrieve
|
||||
_file_data = urlopen("https://raw.githubusercontent.com/rwinkhart/sshyp-labs/main/pointers/v1.5.1").read()
|
||||
_pointer = ConfigParser(interpolation=None)
|
||||
_pointer.read_string(_file_data.decode('utf-8'))
|
||||
_extensions = _pointer.sections()
|
||||
_extensions.append('CANCEL')
|
||||
_choice = curses_radio(_extensions, 'select an extension for more info')
|
||||
if _choice == len(_extensions)-1:
|
||||
return False
|
||||
_selected = _extensions[_choice]
|
||||
_choice = curses_radio(('no', 'yes'), f"description: {_pointer.get(_selected, 'desc')}\n\nusage: "
|
||||
f"{_pointer.get(_selected, 'usage')}\n\ninstall {_selected}?")
|
||||
# if installing the extension...
|
||||
if _choice == 1:
|
||||
# download extension files to temporary directory
|
||||
_exe_dir, _ini_dir = f"{gettempdir()}/sshyp_exe", f"{gettempdir()}/sshyp_ini"
|
||||
_ext_exe = urlretrieve(_pointer.get(_selected, 'exe'), _exe_dir)
|
||||
_ext_ini = urlretrieve(_pointer.get(_selected, 'ini'), _ini_dir)
|
||||
# set permissions under active user
|
||||
chmod(_exe_dir, 0o755)
|
||||
chmod(_ini_dir, 0o644)
|
||||
return _selected
|
||||
return False
|
||||
|
||||
|
||||
# uninstalls/deletes selected extensions
|
||||
def extension_remover():
|
||||
_installed = []
|
||||
for _extension in listdir('/usr/lib/sshyp/extensions'):
|
||||
_installed.append(_extension[:-4])
|
||||
_installed.append('CANCEL')
|
||||
_choice = curses_radio(_installed, 'select an extension to uninstall')
|
||||
if _choice == len(_installed)-1:
|
||||
return False
|
||||
_sure = curses_radio(('no', 'yes'), f"are you sure you want to remove {_installed[_choice]}?")
|
||||
if _sure == 0:
|
||||
return False
|
||||
return _installed[_choice]
|
||||
|
||||
|
||||
# provides options for managing extensions
|
||||
def extension_menu():
|
||||
_ext_name, _action = False, False
|
||||
# determine which of the supported privilege escalation utilities is installed
|
||||
if which('doas') is not None:
|
||||
_escalator = 'doas'
|
||||
elif which('sudo') is not None:
|
||||
_escalator = 'sudo'
|
||||
else:
|
||||
# throw an error if no supported privilege escalation utility is found
|
||||
raise ChildProcessError
|
||||
while True:
|
||||
_choice = curses_radio(('download/update extensions', 'remove extensions', 'BACK'), 'extension management')
|
||||
if _choice == 0:
|
||||
_ext_name = extension_downloader()
|
||||
if _ext_name:
|
||||
# True represents installation
|
||||
_action = True
|
||||
break
|
||||
elif _choice == 1:
|
||||
_ext_name = extension_remover()
|
||||
if _ext_name:
|
||||
break
|
||||
else:
|
||||
break
|
||||
return _ext_name, _escalator, _action
|
||||
# PORT END TWEAK-EXTEND-FUNCTIONS
|
||||
|
||||
|
||||
# runs secondary configuration menu
|
||||
def global_menu(_scr, _device_type, _top_message):
|
||||
global stdscr
|
||||
# only set global stdscr if running as entry point
|
||||
if not isinstance(_scr, bool):
|
||||
stdscr = _scr
|
||||
|
||||
while True:
|
||||
_options, _choice, _exit_signal = ['change device/synchronization types'], 0, False
|
||||
if _device_type == 'client':
|
||||
_options.extend(['change gpg key', 're-configure ssh(ync)', 'change device name',
|
||||
'[OPTIONAL, RECOMMENDED] set custom text editor',
|
||||
'[OPTIONAL] enable/disable quick-unlock',
|
||||
'[OPTIONAL] re-encrypt/optimize entries',
|
||||
'[OPTIONAL] extension management'])
|
||||
else:
|
||||
_options.extend(['manage quick-unlock/whitelist'])
|
||||
_options.extend(['EXIT/DONE'])
|
||||
_choice += curses_radio(_options, _top_message)
|
||||
|
||||
if _choice == 0:
|
||||
_dev_sync_types = install_type()
|
||||
# if switching to client mode...
|
||||
if _dev_sync_types[0] == 'client':
|
||||
# ...and gpg settings are missing
|
||||
if not sshyp_data.has_option('CLIENT-GENERAL', 'gpg_id'):
|
||||
gpg_config()
|
||||
# ...and text editor settings are missing
|
||||
if not sshyp_data.has_option('CLIENT-GENERAL', 'text_editor'):
|
||||
editor_config(True)
|
||||
# ...and online (synced) mode is enabled...
|
||||
if _dev_sync_types[1] == 'false':
|
||||
# ...and quick-unlock settings are missing
|
||||
if not sshyp_data.has_option('CLIENT-ONLINE', 'quick_unlock_enabled'):
|
||||
quick_unlock_config(True)
|
||||
# set to None to check if modified later
|
||||
_ip, _username_ssh, _port, _identity = None, None, None, None
|
||||
# ...and there is no sshync config present
|
||||
if not sshyp_data.has_section('SSHYNC'):
|
||||
_ip, _username_ssh, _port, _identity = ssh_config()
|
||||
# ...and there is no device ID present
|
||||
if not listdir(f"{home}/.config/sshyp/devices"):
|
||||
if None in (_ip, _username_ssh, _port):
|
||||
_ip, _username_ssh, _port, _identity = ssh_config()
|
||||
dev_id_config(_ip, _username_ssh, _port, _identity)
|
||||
# ...or ssh_error is missing
|
||||
elif not sshyp_data.has_option('CLIENT-ONLINE', 'ssh_error'):
|
||||
sshyp_data.set('CLIENT-ONLINE', 'ssh_error', '1')
|
||||
write_config()
|
||||
_device_type = _dev_sync_types[0]
|
||||
elif _choice == 1:
|
||||
if _device_type == 'client':
|
||||
gpg_config()
|
||||
else:
|
||||
whitelist_menu()
|
||||
elif _choice == 2:
|
||||
if _device_type == 'client':
|
||||
ssh_config()
|
||||
else:
|
||||
_exit_signal = True
|
||||
elif _choice == 3:
|
||||
if not sshyp_data.has_section('SSHYNC'):
|
||||
ssh_config()
|
||||
dev_id_config(sshyp_data.get('SSHYNC', 'ip'), sshyp_data.get('SSHYNC', 'user'),
|
||||
sshyp_data.get('SSHYNC', 'port'), sshyp_data.get('SSHYNC', 'identity_file'))
|
||||
elif _choice == 4:
|
||||
editor_config(False)
|
||||
elif _choice == 5:
|
||||
_enabled = quick_unlock_config(False)
|
||||
if _enabled == 'true':
|
||||
curses_radio(['okay'], 'quick-unlock has been enabled client-side - in order for this feature to '
|
||||
'function,\nyou must first log in to the sshyp server and run:\n\nsshyp tweak\n'
|
||||
'\nfrom there you can create a quick-unlock pin and add this device to the '
|
||||
'whitelist')
|
||||
elif _choice == 6:
|
||||
_success = refresh_encryption()
|
||||
if _success == 1:
|
||||
curses_radio(['okay'], "a backup of your previous entry directory has been created:\n\n"
|
||||
f"{home}/.local/share/sshyp.old")
|
||||
elif _success == 2:
|
||||
curses_radio(['okay'], '\u001b[38;5;9merror: re-encryption failed: entry directory not found\u001b[0m')
|
||||
elif _choice == 7:
|
||||
# PORT START TWEAK-EXTEND-OPTION
|
||||
_ext_name, _escalator, _action = extension_menu()
|
||||
# if root is needed for extension management...
|
||||
if _ext_name:
|
||||
return _ext_name, _escalator, _action
|
||||
# PORT END TWEAK-EXTEND-OPTION
|
||||
else:
|
||||
_exit_signal = True
|
||||
if _exit_signal:
|
||||
break
|
||||
return None, None, None
|
||||
|
||||
|
||||
# runs initial configuration wizard
|
||||
def initial_setup(_scr):
|
||||
global stdscr
|
||||
stdscr = _scr
|
||||
|
||||
# required directory creation
|
||||
Path(f"{home}/.config/sshyp/devices").mkdir(mode=0o700, parents=True, exist_ok=True)
|
||||
Path(f"{home}/.local/share/sshyp").mkdir(mode=0o700, parents=True, exist_ok=True)
|
||||
|
||||
# removal of old config files
|
||||
if _exists_flag:
|
||||
sshyp_data.clear()
|
||||
|
||||
# curses menu tree
|
||||
# device+sync type selection
|
||||
_dev_sync_types = install_type()
|
||||
|
||||
if _dev_sync_types[0] == 'client':
|
||||
|
||||
# gpg configuration
|
||||
gpg_config()
|
||||
|
||||
# text editor configuration (automated)
|
||||
editor_config(True)
|
||||
|
||||
# quick-unlock configuration (disabled by default)
|
||||
quick_unlock_config(True)
|
||||
|
||||
# online (synchronized mode) configuration
|
||||
if _dev_sync_types[1] != 'true':
|
||||
|
||||
# ssh+sshync configuration
|
||||
_ip, _username_ssh, _port, _identity = ssh_config()
|
||||
|
||||
# device id configuration
|
||||
dev_id_config(_ip, _username_ssh, _port, _identity)
|
||||
|
||||
# PORT START CLIPTOOL
|
||||
# check for clipboard tool and display warning if missing
|
||||
if uname()[0] in ('Linux', 'FreeBSD') and 'WSL_DISTRO_NAME' not in environ \
|
||||
and not exists("/data/data/com.termux"):
|
||||
_display_server, _clipboard_tool, _clipboard_package = None, None, None
|
||||
if 'WAYLAND_DISPLAY' in environ:
|
||||
_display_server, _clipboard_tool, _clipboard_package = 'Wayland', 'wl-copy', 'wl-clipboard'
|
||||
elif 'DISPLAY' in environ:
|
||||
_display_server, _clipboard_tool, _clipboard_package = 'X11', 'xclip', 'xclip'
|
||||
if _display_server is not None and which(_clipboard_tool) is None:
|
||||
curses_radio(['okay'], f'WARNING: you are using {_display_server} and "{_clipboard_tool}" is not '
|
||||
'present - \ncopying entry fields will not function until '
|
||||
f'"{_clipboard_package}" is installed')
|
||||
# PORT END CLIPTOOL
|
||||
|
||||
# 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:')
|
||||
|
||||
|
||||
# runs the specified entry function (menu start point) within a curses wrapper
|
||||
def wrapped_entry(_gm_device_type, _gm_top_message='configuration options:'):
|
||||
from curses import wrapper, use_default_colors
|
||||
# a boolean value represents init
|
||||
if isinstance(_gm_device_type, bool):
|
||||
wrapper(lambda _wrap_stdscr: (use_default_colors(), initial_setup(_wrap_stdscr)))
|
||||
# any other value will be provided as the global menu device type
|
||||
else:
|
||||
_repeat = True
|
||||
while _repeat:
|
||||
try:
|
||||
_ext_name, _escalator, _action = \
|
||||
wrapper(lambda _wrap_stdscr: (use_default_colors(),
|
||||
global_menu(_wrap_stdscr, _gm_device_type, _gm_top_message)))[1]
|
||||
except ChildProcessError:
|
||||
print("\n\u001b[38;5;9merror: privilege escalation required\n\nneither 'doas' nor 'sudo' were found in "
|
||||
"the system's $PATH\u001b[0m\n")
|
||||
return
|
||||
# only run if privilege escalation is needed
|
||||
if _action is not None:
|
||||
if _action:
|
||||
# install with privilege escalation (outside of curses)
|
||||
from tempfile import gettempdir
|
||||
_exe_dir, _ini_dir = f"{gettempdir()}/sshyp_exe", f"{gettempdir()}/sshyp_ini"
|
||||
run((_escalator, 'chown', 'root:root', _exe_dir, _ini_dir))
|
||||
run((_escalator, 'mv', _exe_dir, f"/usr/lib/sshyp/{_ext_name}"))
|
||||
run((_escalator, 'mv', _ini_dir, f"/usr/lib/sshyp/extensions/{_ext_name}.ini"))
|
||||
else:
|
||||
# uninstall with privilege escalation (outside of curses)
|
||||
run((_escalator, 'rm', '-I', f"/usr/lib/sshyp/{_ext_name}",
|
||||
f"/usr/lib/sshyp/extensions/{_ext_name}.ini"))
|
||||
else:
|
||||
_repeat = False
|
||||
+95
-83
@@ -1,12 +1,14 @@
|
||||
#!/bin/sh
|
||||
|
||||
version=$(sed -n '1{p;q}' share/doc/sshyp/changelog | cut -c8-)
|
||||
version=$(head -n1 extra/changelog-archive/2023 | cut -c8-)
|
||||
if [ -z "$2" ]; then
|
||||
revision=1
|
||||
else
|
||||
revision="$2"
|
||||
fi
|
||||
|
||||
mkdir -p port-jobs/working
|
||||
|
||||
_create_generic_linux() {
|
||||
printf '\npackaging as generic (Linux)...\n'
|
||||
mkdir -p output/linuxtemp/usr/bin \
|
||||
@@ -15,8 +17,9 @@ _create_generic_linux() {
|
||||
output/linuxtemp/usr/share/bash-completion/completions \
|
||||
output/linuxtemp/usr/share/zsh/functions/Completion/Unix
|
||||
# START PORT
|
||||
cp -r lib/. port-jobs/working/
|
||||
cp lib/* port-jobs/working/
|
||||
cd port-jobs
|
||||
./CLIPTOOL.py LINUX
|
||||
./CLIPBOARD.py LINUX
|
||||
./UNAME.py LINUX
|
||||
./COMMENTS.py ALL
|
||||
@@ -33,14 +36,14 @@ _create_generic_linux() {
|
||||
gzip output/linuxtemp/usr/share/man/man1/sshyp.1
|
||||
XZ_OPT=-e6 tar -C output/linuxtemp -cvJf output/GENERIC-LINUX-sshyp-"$version".tar.xz usr/
|
||||
rm -rf output/linuxtemp
|
||||
sha512="$(sha512sum output/GENERIC-LINUX-sshyp-"$version".tar.xz | awk '{print $1;}')"
|
||||
sha512="$(sha512sum output/GENERIC-LINUX-sshyp-"$version".tar.xz | cut -d' ' -f1)"
|
||||
printf '\ngeneric (Linux) packaging complete\n\n'
|
||||
} &&
|
||||
|
||||
_create_pkgbuild() {
|
||||
printf '\ngenerating PKGBUILD...\n'
|
||||
if [ "$1" = 'Deb' ]; then
|
||||
local source='https://github.com/rwinkhart/sshyp/releases/download/v"$pkgver"/UBUNTU-sshyp_"$pkgver"-"$pkgrel"_all.deb'
|
||||
local source='https://github.com/rwinkhart/sshyp/releases/download/v"$pkgver"/DEBIAN-sshyp_"$pkgver"-"$pkgrel"_all.deb'
|
||||
local decomp_target='data.tar.xz'
|
||||
else
|
||||
local source='https://github.com/rwinkhart/sshyp/releases/download/v"$pkgver"/GENERIC-LINUX-sshyp-"$pkgver".tar.xz'
|
||||
@@ -54,8 +57,12 @@ pkgdesc='A light-weight, self-hosted, synchronized password manager'
|
||||
url='https://github.com/rwinkhart/sshyp'
|
||||
arch=('any')
|
||||
license=('GPL-3.0-only')
|
||||
depends=(python gnupg openssh xclip wl-clipboard)
|
||||
optdepends=('bash-completion: bash completion support')
|
||||
depends=(python gnupg openssh)
|
||||
optdepends=(
|
||||
'wl-clipboard: wayland clipboard support'
|
||||
'xclip: x11 clipboard support'
|
||||
'bash-completion: bash completion support'
|
||||
)
|
||||
source=(\""$source"\")
|
||||
sha512sums=('"$sha512"')
|
||||
|
||||
@@ -69,8 +76,8 @@ package() {
|
||||
_create_apkbuild() {
|
||||
printf '\ngenerating APKBUILD...\n'
|
||||
if [ "$1" = 'Deb' ]; then
|
||||
local source="https://github.com/rwinkhart/sshyp/releases/download/v\"\$pkgver\"/UBUNTU-sshyp_\"\$pkgver\"-"$revision"_all.deb"
|
||||
local sumsname="UBUNTU-sshyp_\"\$pkgver\"-"$revision"_all.deb"
|
||||
local source="https://github.com/rwinkhart/sshyp/releases/download/v\"\$pkgver\"/DEBIAN-sshyp_\"\$pkgver\"-"$revision"_all.deb"
|
||||
local sumsname="DEBIAN-sshyp_\"\$pkgver\"-"$revision"_all.deb"
|
||||
local processing='mkdir -p "$pkgdir"
|
||||
7z x "$srcdir"/* -o"$srcdir"
|
||||
tar -xf "$srcdir"/data.tar -C "$pkgdir"
|
||||
@@ -95,7 +102,7 @@ options=!check
|
||||
url='https://github.com/rwinkhart/sshyp'
|
||||
arch='noarch'
|
||||
license='GPL-3.0-only'
|
||||
depends='python3 gnupg openssh xclip wl-clipboard'
|
||||
depends='python3 gnupg openssh'
|
||||
source=\""$source"\"
|
||||
|
||||
package() {
|
||||
@@ -137,37 +144,38 @@ provides {
|
||||
requires {
|
||||
gnupg
|
||||
openssh
|
||||
python310
|
||||
python3.11
|
||||
}
|
||||
urls {
|
||||
\"https://github.com/rwinkhart/sshyp\"
|
||||
}
|
||||
" > output/haikutemp/.PackageInfo
|
||||
# START PORT
|
||||
cp -r lib/. port-jobs/working/
|
||||
cp lib/* port-jobs/working/
|
||||
cd port-jobs
|
||||
./SHEBANG.sh
|
||||
./RMSERVER.py
|
||||
./RMEXTMAN.py
|
||||
./CLIPTOOL.py
|
||||
./CLIPBOARD.py HAIKU
|
||||
./COMMENTS.py ALL
|
||||
./UNAME.py TMP
|
||||
./COMMENTS.py ALL
|
||||
./BLANKS.py
|
||||
./TABS.sh TABS
|
||||
cd ..
|
||||
mv port-jobs/working/* output/haikutemp/lib/sshyp/
|
||||
# END PORT
|
||||
ln -s /system/lib/sshyp/sshyp.py output/haikutemp/bin/sshyp
|
||||
cp -r share/doc/sshyp/. output/haikutemp/documentation/packages/sshyp/
|
||||
cp -r share/licenses/sshyp/. output/haikutemp/documentation/packages/sshyp/
|
||||
cp extra/completion.bash output/haikutemp/data/bash-completion/completions/sshyp
|
||||
cp extra/completion.zsh output/haikutemp/data/zsh/site-functions/_sshyp
|
||||
cp extra/manpage output/haikutemp/documentation/man/man1/sshyp.1
|
||||
gzip output/haikutemp/documentation/man/man1/sshyp.1
|
||||
cd output/haikutemp
|
||||
package create -b HAIKU-sshyp-client-"$version"-"$revision"_all.hpkg
|
||||
package add HAIKU-sshyp-client-"$version"-"$revision"_all.hpkg bin lib documentation data
|
||||
package create -b HAIKU-sshyp_client-"$version"-"$revision"_all.hpkg
|
||||
package add HAIKU-sshyp_client-"$version"-"$revision"_all.hpkg bin lib documentation data
|
||||
cd ../..
|
||||
mv output/haikutemp/HAIKU-sshyp-client-"$version"-"$revision"_all.hpkg output/
|
||||
mv output/haikutemp/HAIKU-sshyp_client-"$version"-"$revision"_all.hpkg output/
|
||||
rm -rf output/haikutemp
|
||||
printf '\nHaiku packaging complete\n\n'
|
||||
} &&
|
||||
@@ -180,33 +188,17 @@ _create_deb() {
|
||||
output/debiantemp/sshyp_"$version"-"$revision"_all/usr/share/man/man1 \
|
||||
output/debiantemp/sshyp_"$version"-"$revision"_all/usr/share/bash-completion/completions \
|
||||
output/debiantemp/sshyp_"$version"-"$revision"_all/usr/share/zsh/functions/Completion/Unix
|
||||
printf "Package: sshyp
|
||||
Version: $version
|
||||
Section: utils
|
||||
Architecture: all
|
||||
Maintainer: Randall Winkhart <idgr at tutanota dot com>
|
||||
Description: A light-weight, self-hosted, synchronized password manager
|
||||
" > output/debiantemp/sshyp_"$version"-"$revision"_all/DEBIAN/control
|
||||
if [ "$1" = 'Debian' ]; then
|
||||
printf "Depends: python3, gnupg, openssh-client, xclip, wl-clipboard
|
||||
" >> output/debiantemp/sshyp_"$version"-"$revision"_all/DEBIAN/control
|
||||
else
|
||||
printf "Depends: python3, gnupg, openssh-client
|
||||
" >> output/debiantemp/sshyp_"$version"-"$revision"_all/DEBIAN/control
|
||||
fi
|
||||
printf "Suggests: bash-completion
|
||||
Priority: optional
|
||||
Installed-Size: 71680
|
||||
" >> output/debiantemp/sshyp_"$version"-"$revision"_all/DEBIAN/control
|
||||
# START PORT
|
||||
cp -r lib/. port-jobs/working/
|
||||
cp lib/* port-jobs/working/
|
||||
cd port-jobs
|
||||
if [ "$1" = 'Debian' ]; then
|
||||
./CLIPTOOL.py LINUX
|
||||
./CLIPBOARD.py LINUX
|
||||
special=UBUNTU
|
||||
special=DEBIAN
|
||||
else
|
||||
./CLIPTOOL.py
|
||||
./CLIPBOARD.py WSL
|
||||
special=WSL-ONLY-UBUNTU
|
||||
special=WSL-ONLY-DEBIAN
|
||||
fi
|
||||
./UNAME.py LINUX
|
||||
./COMMENTS.py ALL
|
||||
@@ -221,22 +213,61 @@ Installed-Size: 71680
|
||||
cp extra/completion.zsh output/debiantemp/sshyp_"$version"-"$revision"_all/usr/share/zsh/functions/Completion/Unix/_sshyp
|
||||
cp extra/manpage output/debiantemp/sshyp_"$version"-"$revision"_all/usr/share/man/man1/sshyp.1
|
||||
gzip output/debiantemp/sshyp_"$version"-"$revision"_all/usr/share/man/man1/sshyp.1
|
||||
size=$(du --block-size=1024 -s output/debiantemp/sshyp_"$version"-"$revision"_all | cut -f1)
|
||||
printf "Package: sshyp
|
||||
Version: $version
|
||||
Section: utils
|
||||
Architecture: all
|
||||
Maintainer: Randall Winkhart <idgr at tutanota dot com>
|
||||
Description: A light-weight, self-hosted, synchronized password manager
|
||||
Priority: optional
|
||||
Installed-Size: $size
|
||||
Depends: python3, gnupg, openssh-client
|
||||
" > 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/
|
||||
mv output/debiantemp/sshyp_"$version"-"$revision"_all.deb output/"$special"-sshyp_"$version"-"$revision"_all.deb
|
||||
rm -rf output/debiantemp
|
||||
sha512="$(sha512sum output/"$special"-sshyp_"$version"-"$revision"_all.deb | awk '{print $1;}')"
|
||||
sha512="$(sha512sum output/"$special"-sshyp_"$version"-"$revision"_all.deb | cut -d' ' -f1)"
|
||||
printf "\n$1 packaging complete\n\n"
|
||||
} &&
|
||||
|
||||
_create_termux() {
|
||||
printf '\npackaging for Termux...\n'
|
||||
mkdir -p output/termuxtemp/sshyp_"$version"-"$revision"_all_termux/DEBIAN \
|
||||
output/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/lib/sshyp/extensions \
|
||||
output/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/bin \
|
||||
output/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/man/man1 \
|
||||
output/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/bash-completion/completions \
|
||||
output/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/zsh/site-functions
|
||||
printf "Package: sshyp
|
||||
mkdir -p output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/DEBIAN \
|
||||
output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/lib/sshyp/extensions \
|
||||
output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/bin \
|
||||
output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/man/man1 \
|
||||
output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/bash-completion/completions \
|
||||
output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/zsh/site-functions
|
||||
# START PORT
|
||||
cp lib/* port-jobs/working/
|
||||
cd port-jobs
|
||||
./RMSERVER.py
|
||||
./RMEXTMAN.py
|
||||
./CLIPTOOL.py
|
||||
./CLIPBOARD.py TERMUX
|
||||
./UNAME.py TERMUX
|
||||
./COMMENTS.py ALL
|
||||
./BLANKS.py
|
||||
./TABS.sh TABS
|
||||
cd ..
|
||||
mv port-jobs/working/* output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/lib/sshyp/
|
||||
# END PORT
|
||||
ln -s /data/data/com.termux/files/usr/lib/sshyp/sshyp.py output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/bin/sshyp
|
||||
cp -r share output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/
|
||||
cp extra/completion.bash output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/bash-completion/completions/sshyp
|
||||
cp extra/completion.zsh output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/zsh/site-functions/_sshyp
|
||||
cp extra/manpage output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/man/man1/sshyp.1
|
||||
gzip output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/man/man1/sshyp.1
|
||||
size=$(du --block-size=1024 -s output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux | cut -f1)
|
||||
printf "Package: sshyp-client
|
||||
Version: $version
|
||||
Section: utils
|
||||
Architecture: all
|
||||
@@ -245,27 +276,10 @@ Description: A light-weight, self-hosted, synchronized password manager
|
||||
Depends: python, gnupg, openssh, termux-api, termux-am
|
||||
Suggests: bash-completion
|
||||
Priority: optional
|
||||
Installed-Size: 71680
|
||||
" > output/termuxtemp/sshyp_"$version"-"$revision"_all_termux/DEBIAN/control
|
||||
# START PORT
|
||||
cp -r lib/. port-jobs/working/
|
||||
cd port-jobs
|
||||
./CLIPBOARD.py TERMUX
|
||||
./UNAME.py TERMUX
|
||||
./COMMENTS.py ALL
|
||||
./BLANKS.py
|
||||
./TABS.sh TABS
|
||||
cd ..
|
||||
mv port-jobs/working/* output/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/lib/sshyp/
|
||||
# END PORT
|
||||
ln -s /data/data/com.termux/files/usr/lib/sshyp/sshyp.py output/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/bin/sshyp
|
||||
cp -r share output/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/
|
||||
cp extra/completion.bash output/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/bash-completion/completions/sshyp
|
||||
cp extra/completion.zsh output/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/zsh/site-functions/_sshyp
|
||||
cp extra/manpage output/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/man/man1/sshyp.1
|
||||
gzip output/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/man/man1/sshyp.1
|
||||
dpkg-deb --build --root-owner-group -z6 -Sextreme -Zxz output/termuxtemp/sshyp_"$version"-"$revision"_all_termux/
|
||||
mv output/termuxtemp/sshyp_"$version"-"$revision"_all_termux.deb output/TERMUX-sshyp_"$version"-"$revision"_all_termux.deb
|
||||
Installed-Size: $size
|
||||
" > output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/DEBIAN/control
|
||||
dpkg-deb --build --root-owner-group -z6 -Sextreme -Zxz output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/
|
||||
mv output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux.deb output/
|
||||
rm -rf output/termuxtemp
|
||||
printf '\nTermux packaging complete\n\n'
|
||||
} &&
|
||||
@@ -287,8 +301,8 @@ BuildArch: noarch
|
||||
License: GPL-3.0-only
|
||||
URL: https://github.com/rwinkhart/sshyp
|
||||
Source0: GENERIC-FEDORA-sshyp-"$version".tar.xz
|
||||
Requires: python gnupg openssh-clients wl-clipboard
|
||||
Recommends: bash-completion
|
||||
Requires: python gnupg openssh-clients
|
||||
Recommends: wl-clipboard xclip bash-completion openssh-server
|
||||
%%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.
|
||||
%%install
|
||||
@@ -298,16 +312,18 @@ cp -r %%{_sourcedir}/usr %%{buildroot}
|
||||
/usr/bin/sshyp
|
||||
/usr/lib/sshyp/sshyp.py
|
||||
/usr/lib/sshyp/sshync.py
|
||||
/usr/lib/sshyp/stweak.py
|
||||
/usr/lib/sshyp/extensions/
|
||||
/usr/share/bash-completion/completions/sshyp
|
||||
/usr/share/zsh/site-functions/_sshyp
|
||||
%%license /usr/share/licenses/sshyp/license
|
||||
%%doc
|
||||
/usr/share/doc/sshyp/changelog
|
||||
/usr/share/man/man1/sshyp.1.gz
|
||||
" > ~/rpmbuild/SPECS/sshyp.spec
|
||||
# START PORT
|
||||
cp -r lib/. port-jobs/working/
|
||||
cp lib/* port-jobs/working/
|
||||
cd port-jobs
|
||||
./CLIPTOOL.py LINUX
|
||||
./CLIPBOARD.py LINUX
|
||||
./UNAME.py LINUX
|
||||
./COMMENTS.py ALL
|
||||
@@ -349,33 +365,29 @@ maintainer: <idgr at tutanota dot com>
|
||||
www: https://github.com/rwinkhart/sshyp
|
||||
prefix: /
|
||||
\"deps\" : {
|
||||
\"python\" : {
|
||||
\"origin\" : \"lang/python\"
|
||||
\"python3\" : {
|
||||
\"origin\" : \"lang/python3\"
|
||||
},
|
||||
\"gnupg\" : {
|
||||
\"origin\" : \"security/gnupg\"
|
||||
},
|
||||
\"xclip\" : {
|
||||
\"origin\" : \"x11/xclip\"
|
||||
},
|
||||
\"wl-clipboard\" : {
|
||||
\"origin\" : \"x11/wl-clipboard\"
|
||||
},
|
||||
},
|
||||
" > output/freebsdtemp/+MANIFEST
|
||||
printf "/usr/bin/sshyp
|
||||
/usr/lib/sshyp/sshync.py
|
||||
/usr/lib/sshyp/sshyp.py
|
||||
/usr/lib/sshyp/sshync.py
|
||||
/usr/lib/sshyp/stweak.py
|
||||
/usr/local/share/bash-completion/completions/sshyp
|
||||
/usr/local/share/zsh/site-functions/_sshyp
|
||||
/usr/share/doc/sshyp/changelog
|
||||
/usr/share/licenses/sshyp/license
|
||||
/usr/share/man/man1/sshyp.1.gz
|
||||
@dir /usr/lib/sshyp/extensions/
|
||||
" > output/freebsdtemp/plist
|
||||
# START PORT
|
||||
cp -r lib/. port-jobs/working/
|
||||
cp lib/* port-jobs/working/
|
||||
cd port-jobs
|
||||
./CLIPBOARD.py TERMUX
|
||||
./CLIPTOOL.py LINUX
|
||||
./CLIPBOARD.py BSD
|
||||
./UNAME.py TMP
|
||||
./COMMENTS.py ALL
|
||||
./BLANKS.py
|
||||
@@ -460,6 +472,6 @@ case "$1" in
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
printf '\nusage: package.sh [target] <revision>\n\ntargets:\n mainline: pkgbuild pkgbuild-deb apkbuild apkbuild-deb fedora debian wsl haiku freebsd\n experimental: termux\n groups: buildable-arch buildable-arch-deb\n\n'
|
||||
printf '\nusage: package.sh [target] <revision>\n\ntargets:\n full support: pkgbuild pkgbuild-deb apkbuild apkbuild-deb fedora debian wsl freebsd\n client-only: haiku termux\n groups: buildable-arch buildable-arch-deb\n\n'
|
||||
;;
|
||||
esac
|
||||
|
||||
+45
-33
@@ -3,48 +3,60 @@ import re
|
||||
from sys import argv, exit as s_exit
|
||||
|
||||
# read arguments
|
||||
arguments = argv[1:]
|
||||
arguments, clip_replacement, clear_replacement = argv[1:], None, None
|
||||
|
||||
# define replacement text depending on arguments
|
||||
if len(arguments) > 0:
|
||||
if arguments[0] == 'WSL':
|
||||
replacement = """run(['powershell.exe', '-c', "Set-Clipboard '" + _copy_line[_index].rstrip().replace("'", "''") + "'"])
|
||||
Popen("sleep 30; powershell.exe -c Set-Clipboard ''", shell=True, stdout=DEVNULL, stderr=DENVULL)"""
|
||||
clip_replacement = """run(('powershell.exe', '-c', "Set-Clipboard '" + _copy_subject.replace("'", "''") + "'"))
|
||||
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':
|
||||
replacement = """run(['pbcopy'], stdin=Popen(['printf', _copy_line[_index].rstrip().replace('\\\\\\', '\\\\\\\\\\\\\\').replace('%', '%%')],
|
||||
stdout=PIPE).stdout)
|
||||
Popen("sleep 30; printf '' | pbcopy", shell=True)"""
|
||||
clip_replacement = """run('pbcopy', stdin=Popen(('printf', '%b', _copy_subject.replace('\\\\\\', '\\\\\\\\\\\\\\')), stdout=PIPE).stdout)
|
||||
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'mac'))"""
|
||||
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':
|
||||
replacement = """run(['clipboard', '-c', _copy_line[_index].rstrip()])
|
||||
Popen('sleep 30; clipboard -r', shell=True)"""
|
||||
clip_replacement = """run(('clipboard', '-c', _copy_subject))
|
||||
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':
|
||||
replacement = """run(['termux-clipboard-set', _copy_line[_index].rstrip()])
|
||||
Popen("sleep 30; termux-clipboard-set ''", shell=True)"""
|
||||
elif arguments[0] == 'BSD':
|
||||
replacement = """run(['xclip', '-sel', 'c'], stdin=Popen(['printf', _copy_line[_index].rstrip().replace('\\', '\\\\')
|
||||
.replace('%', '%%')], stdout=PIPE).stdout)
|
||||
Popen("sleep 30; printf '' | xclip -sel c", shell=True)"""
|
||||
elif arguments[0] == 'LINUX':
|
||||
replacement = """if 'WAYLAND_DISPLAY' in environ:
|
||||
run(['wl-copy', _copy_line[_index].rstrip()])
|
||||
Popen('sleep 30; wl-copy -c', shell=True)
|
||||
clip_replacement = """run(('termux-clipboard-set', _copy_subject))
|
||||
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'termux'))"""
|
||||
clear_replacement = """hash_paste.update(run('termux-clipboard-get', stdout=PIPE).stdout.strip())
|
||||
if argv[1] == hash_paste.hexdigest():
|
||||
run(("termux-clipboard-set", "''"))"""
|
||||
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:
|
||||
run(['xclip', '-sel', 'c'], stdin=Popen(['printf', _copy_line[_index].rstrip().replace('\\\\\\', '\\\\\\\\\\\\\\')
|
||||
.replace('%', '%%')], stdout=PIPE).stdout)
|
||||
Popen("sleep 30; printf '' | xclip -sel c", shell=True)"""
|
||||
run(('xclip', '-sel', 'c'), stdin=Popen(('printf', '%b', _copy_subject.replace('\\\\\\', '\\\\\\\\\\\\\\')), stdout=PIPE).stdout)
|
||||
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'x11'))"""
|
||||
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:
|
||||
s_exit()
|
||||
|
||||
# define PORT target
|
||||
string1 = '# PORT START CLIPBOARD'
|
||||
string2 = '# PORT END CLIPBOARD'
|
||||
targets = (('CLIPBOARD', 'sshyp.py', '', '', clip_replacement),
|
||||
('CLIPCLEAR', 'clipclear.py', '\n', '', clear_replacement))
|
||||
|
||||
# read input file
|
||||
text = open('working/sshyp.py', 'r').read()
|
||||
|
||||
# find and replace the defined PORT target
|
||||
regex = re.compile(f"{string1}.*?{string2}", re.DOTALL)
|
||||
new_text = re.sub(regex, replacement, text)
|
||||
|
||||
# write updated text
|
||||
open('working/sshyp.py', 'w').write(new_text)
|
||||
for target in targets:
|
||||
# read text from target file
|
||||
text = open(f"working/{target[1]}", 'r').read()
|
||||
# compile regex and modify text
|
||||
regex = re.compile(f"{target[2]}# PORT START {target[0]}.*?# PORT END {target[0]}{target[3]}", re.DOTALL)
|
||||
new_text = re.sub(regex, target[4], text)
|
||||
# write updated text to target file
|
||||
open(f"working/{target[1]}", 'w').write(new_text)
|
||||
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env python3
|
||||
import re
|
||||
from sys import argv
|
||||
|
||||
# read arguments
|
||||
arguments = argv[1:]
|
||||
|
||||
# define PORT target
|
||||
string1 = '# PORT START CLIPTOOL'
|
||||
string2 = '# PORT END CLIPTOOL'
|
||||
|
||||
# define replacement text depending on arguments
|
||||
if len(arguments) > 0:
|
||||
regex = re.compile(f"{string1}.*?{string2}", re.DOTALL)
|
||||
replacement = """# check for clipboard tool and display warning if missing
|
||||
_display_server, _clipboard_tool, _clipboard_package = None, None, None
|
||||
if 'WAYLAND_DISPLAY' in environ:
|
||||
_display_server, _clipboard_tool, _clipboard_package = 'Wayland', 'wl-copy', 'wl-clipboard'
|
||||
elif 'DISPLAY' in environ:
|
||||
_display_server, _clipboard_tool, _clipboard_package = 'X11', 'xclip', 'xclip'
|
||||
if _display_server is not None and which(_clipboard_tool) is None:
|
||||
curses_radio(['okay'], f'''WARNING: you are using {_display_server} and "{_clipboard_tool}" is not present -
|
||||
copying entry fields will not function until "{_clipboard_package}" is installed'''
|
||||
)"""
|
||||
else:
|
||||
regex = re.compile(f"{string1}.*?{string2}\n", re.DOTALL)
|
||||
replacement = ''
|
||||
|
||||
# read input file
|
||||
text = open('working/stweak.py', 'r').read()
|
||||
|
||||
# find and replace the defined PORT target
|
||||
new_text = re.sub(regex, replacement, text)
|
||||
|
||||
# write updated text
|
||||
open('working/stweak.py', 'w').write(new_text)
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env python3
|
||||
import re
|
||||
|
||||
targets = (('TWEAK-EXTEND-FUNCTIONS', 'stweak.py', '\n', '\n\n', ''),
|
||||
('TWEAK-EXTEND-OPTION', 'stweak.py', '', '', "curses_radio(['okay'], "
|
||||
"'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 "
|
||||
"at https://github.com/rwinkhart/sshyp-labs/releases')"))
|
||||
|
||||
for target in targets:
|
||||
# read text from target file
|
||||
text = open(f"working/{target[1]}", 'r').read()
|
||||
# compile regex and modify text
|
||||
regex = re.compile(f"{target[2]}# PORT START {target[0]}.*?# PORT END {target[0]}{target[3]}", re.DOTALL)
|
||||
new_text = re.sub(regex, target[4], text)
|
||||
# write updated text to target file
|
||||
open(f"working/{target[1]}", 'w').write(new_text)
|
||||
+13
-77
@@ -1,82 +1,18 @@
|
||||
#!/usr/bin/env python3
|
||||
import re
|
||||
|
||||
# SSHYNC-REMOTE
|
||||
# define PORT target
|
||||
string1 = '# PORT START SSHYNC-REMOTE'
|
||||
string2 = '# PORT END SSHYNC-REMOTE'
|
||||
devtype_replacement = """_install_type = curses_radio(('client (ssh-synchronized)', 'client (offline)'),
|
||||
'device + sync type configuration')"""
|
||||
|
||||
# read input file
|
||||
text = open('working/sshync.py', 'r').read()
|
||||
targets = (('SSHYNC-REMOTE', 'sshync.py', '\n', '\n\n', ''), ('WHITELIST-SERVER', 'stweak.py', '\n', '\n\n', ''),
|
||||
('TWEAK-DEVTYPE', 'stweak.py', '', '', devtype_replacement),
|
||||
('ARGS-SERVER', 'sshyp.py', '', '\n\n ', ''), ('HELP-SERVER', 'sshyp.py', '', '\n', ''))
|
||||
|
||||
# find and replace the defined PORT target
|
||||
regex = re.compile(f"\n{string1}.*?{string2}\n\n", re.DOTALL)
|
||||
new_text = re.sub(regex, '', text)
|
||||
|
||||
# write updated text
|
||||
open('working/sshync.py', 'w').write(new_text)
|
||||
|
||||
# WHITELIST-SERVER
|
||||
# define PORT target
|
||||
string1 = '# PORT START WHITELIST-SERVER'
|
||||
string2 = '# PORT END WHITELIST-SERVER'
|
||||
|
||||
# read input file
|
||||
text = open('working/sshyp.py', 'r').read()
|
||||
|
||||
# find and replace the defined PORT target
|
||||
regex = re.compile(f"\n{string1}.*?{string2}\n\n", re.DOTALL)
|
||||
new_text = re.sub(regex, '', text)
|
||||
|
||||
# write updated text
|
||||
open('working/sshyp.py', 'w').write(new_text)
|
||||
|
||||
# TWEAK-DEVTYPE
|
||||
# define PORT target
|
||||
string1 = '# PORT START TWEAK-DEVTYPE'
|
||||
string2 = '# PORT END TWEAK-DEVTYPE'
|
||||
|
||||
# set replacement text
|
||||
replacement = """if True:
|
||||
_sshyp_data = ['client']
|
||||
Path(f"{home}/.local/share/sshyp").mkdir(mode=0o700, parents=True, exist_ok=True)"""
|
||||
|
||||
# read input file
|
||||
text = open('working/sshyp.py', 'r').read()
|
||||
|
||||
# find and replace the defined PORT target
|
||||
regex = re.compile(f"{string1}.*?{string2}", re.DOTALL)
|
||||
new_text = re.sub(regex, replacement, text)
|
||||
|
||||
# write updated text
|
||||
open('working/sshyp.py', 'w').write(new_text)
|
||||
|
||||
# ARGS-SERVER
|
||||
# define PORT target
|
||||
string1 = '# PORT START ARGS-SERVER'
|
||||
string2 = '# PORT END ARGS-SERVER'
|
||||
|
||||
# read input file
|
||||
text = open('working/sshyp.py', 'r').read()
|
||||
|
||||
# find and replace the defined PORT target
|
||||
regex = re.compile(f"{string1}.*?{string2}\n\n ", re.DOTALL)
|
||||
new_text = re.sub(regex, '', text)
|
||||
|
||||
# write updated text
|
||||
open('working/sshyp.py', 'w').write(new_text)
|
||||
|
||||
# HELP-SERVER
|
||||
# define PORT target
|
||||
string1 = '# PORT START HELP-SERVER'
|
||||
string2 = '# PORT END HELP-SERVER'
|
||||
|
||||
# read input file
|
||||
text = open('working/sshyp.py', 'r').read()
|
||||
|
||||
# find and replace the defined PORT target
|
||||
regex = re.compile(f"{string1}.*?{string2}\n", re.DOTALL)
|
||||
new_text = re.sub(regex, '', text)
|
||||
|
||||
# write updated text
|
||||
open('working/sshyp.py', 'w').write(new_text)
|
||||
for target in targets:
|
||||
# read text from target file
|
||||
text = open(f"working/{target[1]}", 'r').read()
|
||||
# compile regex and modify text
|
||||
regex = re.compile(f"{target[2]}# PORT START {target[0]}.*?# PORT END {target[0]}{target[3]}", re.DOTALL)
|
||||
new_text = re.sub(regex, target[4], text)
|
||||
# write updated text to target file
|
||||
open(f"working/{target[1]}", 'w').write(new_text)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#!/bin/sh
|
||||
if [ "$1" = 'HAIKU' ]; then
|
||||
sed -i '1 s/.*/#!\/bin\/env\ python3.10/' ./working/sshync.py
|
||||
sed -i '1 s/.*/#!\/bin\/env\ python3.10/' ./working/sshyp.py
|
||||
if [ "$1" = 'PREP' ]; then
|
||||
sed -i '1 s/.*/#!\/bin\/env\ python3.11/' ./*.py
|
||||
else
|
||||
sed -i '1 s/.*/#!\/bin\/env\ python3.11/' ./working/sshyp.py ./working/clipclear.py
|
||||
fi
|
||||
|
||||
+3
-1
@@ -2,9 +2,11 @@
|
||||
if [ "$1" = 'TABS' ]; then
|
||||
unexpand -t 3,7,11,15,19,23,27,31,35,39,43,47,51,55,59,63,67,71,75,79,83,87,91,95,99,103,107,111,115,119,123 working/sshyp.py > working/sshyp.py.new
|
||||
unexpand -t 3,7,11,15,19,23,27,31,35,39,43,47,51,55,59,63,67,71,75,79,83,87,91,95,99,103,107,111,115,119,123 working/sshync.py > working/sshync.py.new
|
||||
unexpand -t 3,7,11,15,19,23,27,31,35,39,43,47,51,55,59,63,67,71,75,79,83,87,91,95,99,103,107,111,115,119,123 working/stweak.py > working/stweak.py.new
|
||||
mv working/sshyp.py.new working/sshyp.py
|
||||
mv working/sshync.py.new working/sshync.py
|
||||
chmod +x working/sshyp.py working/sshync.py
|
||||
mv working/stweak.py.new working/stweak.py
|
||||
chmod +x working/sshyp.py working/sshync.py working/stweak.py
|
||||
elif [ "$(grep -qP '\t' "$1" && echo TABS)" = 'TABS' ]; then
|
||||
printf "$1 contains tab characters...\n"
|
||||
read -rp 'replace with (4) spaces? (Y/n) ' replace
|
||||
|
||||
+9
-43
@@ -1,47 +1,13 @@
|
||||
#!/usr/bin/env python3
|
||||
import re
|
||||
from sys import argv, exit as s_exit
|
||||
|
||||
# read arguments
|
||||
arguments = argv[1:]
|
||||
targets = (('UNAME-IMPORT-SSHYP', 'sshyp.py', ''), ('UNAME-IMPORT-STWEAK', 'stweak.py', ''))
|
||||
|
||||
# define replacement text depending on arguments
|
||||
if len(arguments) > 0:
|
||||
if arguments[0] == 'TMP':
|
||||
replacement = 'symlink("/tmp", f"{home}/.config/sshyp/tmp")'
|
||||
elif arguments[0] == 'TERMUX':
|
||||
replacement = 'symlink("/data/data/com.termux/files/usr/tmp", f"{home}/.config/sshyp/tmp")'
|
||||
elif arguments[0] == 'LINUX':
|
||||
replacement = 'symlink("/dev/shm", f"{home}/.config/sshyp/tmp")'
|
||||
else:
|
||||
s_exit()
|
||||
|
||||
# UNAME-IMPORT
|
||||
# define PORT target
|
||||
string1 = '# PORT START UNAME-IMPORT'
|
||||
string2 = '# PORT END UNAME-IMPORT'
|
||||
|
||||
# read input file
|
||||
text = open('working/sshyp.py', 'r').read()
|
||||
|
||||
# find and replace the defined PORT target
|
||||
regex = re.compile(f"{string1}.*?{string2}\n", re.DOTALL)
|
||||
new_text = re.sub(regex, '', text)
|
||||
|
||||
# write updated text
|
||||
open('working/sshyp.py', 'w').write(new_text)
|
||||
|
||||
# UNAME-TMP
|
||||
# define PORT target
|
||||
string1 = '# PORT START UNAME-TMP'
|
||||
string2 = '# PORT END UNAME-TMP'
|
||||
|
||||
# read input file
|
||||
text = open('working/sshyp.py', 'r').read()
|
||||
|
||||
# find and replace the defined PORT target
|
||||
regex = re.compile(f"{string1}.*?{string2}", re.DOTALL)
|
||||
new_text = re.sub(regex, replacement, text)
|
||||
|
||||
# write updated text
|
||||
open('working/sshyp.py', 'w').write(new_text)
|
||||
for target in targets:
|
||||
# read text from target file
|
||||
text, special = open(f"working/{target[1]}", 'r').read(), ''
|
||||
# compile regex and modify text
|
||||
regex = re.compile(f"# PORT START {target[0]}.*?# PORT END {target[0]}\n", re.DOTALL)
|
||||
new_text = re.sub(regex, target[2], text)
|
||||
# write updated text to target file
|
||||
open(f"working/{target[1]}", 'w').write(new_text)
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
sshyp v1.4.3
|
||||
06/08/2023
|
||||
|
||||
the argumentative agronomist update - patch three
|
||||
|
||||
IMPORTANT: the next major release of sshyp, v1.5.0, will make breaking changes that will require
|
||||
running through 'sshyp tweak' again - also starting with v1.5.0, a copy of the changelog will no
|
||||
longer be packaged with sshyp (it will still be available on GitHub) and clipboard tools
|
||||
(x-clip and wl-clipboard) will be set to optional dependencies ('sshyp tweak' will warn of this)
|
||||
|
||||
this is a highly unconventional release featuring backports to fix critical bugs found during the
|
||||
development of the upcoming v1.5.0
|
||||
|
||||
fixes:
|
||||
|
||||
- folder renaming has been disabled to prevent a severe data loss bug from occuring
|
||||
^ this has been properly fixed for v1.5.0, but the fix requires all clients and servers
|
||||
to be updated - to avoid breaking compatibility with 1.4.X, folder renaming was simply disabled
|
||||
for the time being
|
||||
- backported fix for syncing over IPv6
|
||||
- backported fix for multiple versions of Powershell being called under WSL
|
||||
- backported fix for checking /lib/sshyp instead of /usr/lib/sshyp
|
||||
|
||||
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
||||
|
||||
The full history of changes to "sshyp" can be found on the following page:
|
||||
https://github.com/rwinkhart/sshyp/tree/main/extra/changelog-archive/
|
||||
Reference in New Issue
Block a user