mirror of
https://github.com/rwinkhart/sshyp.git
synced 2026-09-03 07:37:16 -04:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f5a2c8f3c | ||
|
|
386044d081 | ||
|
|
c651c41a41 | ||
|
|
7cb245edde | ||
|
|
edf31f9a7c | ||
|
|
362b1aa025 | ||
|
|
5633520d82 | ||
|
|
a487bd561a | ||
|
|
cbc0538e82 | ||
|
|
5c589de4f2 | ||
|
|
44392467e5 | ||
|
|
18aacf648b | ||
|
|
ac621a8a73 | ||
|
|
98f76ac1ab | ||
|
|
bdff85f024 | ||
|
|
2bcabe8597 | ||
|
|
7259a8732c | ||
|
|
20cdc27b88 | ||
|
|
2f6bdb8d2e | ||
|
|
eb4fae3e9e | ||
|
|
30afbafcbf | ||
|
|
e010180219 | ||
|
|
1f4e7d5cae | ||
|
|
67983da22e | ||
|
|
aa0bd017c9 | ||
|
|
5a4e952d4d | ||
|
|
db05e9b05e | ||
|
|
397ad7d409 | ||
|
|
5af02866b4 | ||
|
|
e8267f838a | ||
|
|
e4fbcd6825 | ||
|
|
9ba4b8d724 | ||
|
|
0b73d4016f | ||
|
|
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 | ||
|
|
575156f2e6 | ||
|
|
1d2ffef6b1 | ||
|
|
7fbfc50c0b | ||
|
|
7fe4e16fec | ||
|
|
42791f3129 | ||
|
|
b44f63b7fb | ||
|
|
6b80744903 | ||
|
|
d9d389335a | ||
|
|
8041d03833 | ||
|
|
bbb361e261 | ||
|
|
ddf3f21034 | ||
|
|
0539a3d182 | ||
|
|
d5fc92b5a3 | ||
|
|
ba6f485892 | ||
|
|
24ab17ce01 | ||
|
|
a1e1d9b0f6 | ||
|
|
9640066243 | ||
|
|
af655b1292 | ||
|
|
d48a1af167 | ||
|
|
e35f9246ac | ||
|
|
9d41523c32 | ||
|
|
1f21ad571e | ||
|
|
f9aaf16e13 | ||
|
|
6c2a2313fb | ||
|
|
9027aee323 | ||
|
|
89b203c6a6 | ||
|
|
3c877d6e4a | ||
|
|
c26975bdab | ||
|
|
b04a7d2e45 | ||
|
|
df262f4f37 | ||
|
|
17ce194b48 | ||
|
|
a13f20db76 | ||
|
|
b2cfa5d8f2 | ||
|
|
98a10cc0cf | ||
|
|
d08e6fee53 | ||
|
|
2d31aafa36 | ||
|
|
65e5c31163 | ||
|
|
4a1752f613 | ||
|
|
47b000f416 | ||
|
|
727937772f | ||
|
|
a03b250599 | ||
|
|
29e7eb0f4c | ||
|
|
ce928aba57 | ||
|
|
006f30a26c | ||
|
|
c65409c453 | ||
|
|
1c68226ab9 | ||
|
|
bd7995fa39 | ||
|
|
6b71b81853 | ||
|
|
dd8c63631f | ||
|
|
72f4bcca7d | ||
|
|
7abf5c1641 | ||
|
|
8b205b78a7 | ||
|
|
7e24234c15 | ||
|
|
8a0dd91201 | ||
|
|
92f26c47af | ||
|
|
d706bb4452 | ||
|
|
da3938e80b | ||
|
|
9063d02cf1 | ||
|
|
8fe88a9b14 | ||
|
|
478d10384f | ||
|
|
154905792a | ||
|
|
d9873df2d1 | ||
|
|
0c85703a73 | ||
|
|
41c9d0a22b | ||
|
|
aa65ecf8fb | ||
|
|
432a5f1e5a | ||
|
|
03c8bdbf74 | ||
|
|
1e0aa8aa39 | ||
|
|
924d066676 | ||
|
|
07cfc7ddaf | ||
|
|
6e0f19e8b8 | ||
|
|
d2ee90a52c | ||
|
|
3f4173b4fd | ||
|
|
fc94bfd774 | ||
|
|
0226180339 | ||
|
|
ed9c69248b | ||
|
|
902e041f51 | ||
|
|
648bed832f | ||
|
|
843dff6527 | ||
|
|
4f409f62d6 | ||
|
|
21550ddef2 | ||
|
|
c7dfb6a419 | ||
|
|
cc5d54914c | ||
|
|
b090c24d59 | ||
|
|
d1a994a103 | ||
|
|
6584686b2c | ||
|
|
0109816d3d | ||
|
|
30962b8cb2 | ||
|
|
fbf5ac02a4 | ||
|
|
6649fce7d1 | ||
|
|
6375a086cc | ||
|
|
43a53070a8 | ||
|
|
16f1225621 | ||
|
|
99cf9b7413 | ||
|
|
7d26170d7f | ||
|
|
6907c10f81 | ||
|
|
a5d36bf6b4 | ||
|
|
24ae771b85 | ||
|
|
0429070b5d | ||
|
|
6aa9a663b5 | ||
|
|
2c17a6b711 | ||
|
|
b649ae2c16 | ||
|
|
a0b533d9dd | ||
|
|
4c78ffb0de | ||
|
|
a2f2525df1 | ||
|
|
eefedde98c | ||
|
|
12cbcdd9af | ||
|
|
5219bf0448 | ||
|
|
a91c48533c | ||
|
|
e22066f015 | ||
|
|
d1cc6fe3e6 | ||
|
|
f9ad813ce6 | ||
|
|
bee3149c74 | ||
|
|
ccee566a22 | ||
|
|
e511159a6c | ||
|
|
f5bbe9516c | ||
|
|
6bbb84e1c0 | ||
|
|
39dbc1c83d | ||
|
|
1d969cc1f1 | ||
|
|
c5dd735f7a | ||
|
|
bf8adfc1c1 | ||
|
|
764d165ad4 | ||
|
|
9928a43d6a | ||
|
|
25121f3a5c | ||
|
|
2c78a1456c | ||
|
|
6173a11710 | ||
|
|
52e41dfab8 | ||
|
|
8b0f25479d | ||
|
|
0a210d0395 | ||
|
|
d29bee2d45 | ||
|
|
052a489bec | ||
|
|
b458265fd1 | ||
|
|
c916a738a2 | ||
|
|
9c5b007946 | ||
|
|
ff1c984024 | ||
|
|
164c719b90 | ||
|
|
d49ce1bb87 | ||
|
|
800a1181f8 | ||
|
|
d9414dbcdf | ||
|
|
0869b83505 | ||
|
|
7dfa12012a | ||
|
|
921630f5a8 | ||
|
|
627c6a1a14 | ||
|
|
a9f4435493 | ||
|
|
7d639ecca9 | ||
|
|
eefe5e39b5 | ||
|
|
c28b836c2a | ||
|
|
b53370a3e2 | ||
|
|
9776961f2a | ||
|
|
1a35aaf33b | ||
|
|
66b15ac22c | ||
|
|
56db2e65f8 | ||
|
|
1be67d404b | ||
|
|
a279819260 | ||
|
|
578f7dee14 | ||
|
|
5bec4fdaa9 | ||
|
|
5f903966da | ||
|
|
cfd551ef7c | ||
|
|
4d22b5b5a8 | ||
|
|
46d5f8d515 | ||
|
|
02ee2a5c0e | ||
|
|
fb1c3844a8 | ||
|
|
213ca41582 | ||
|
|
b8dc5c4f89 | ||
|
|
574637392a | ||
|
|
c6f31da6d4 | ||
|
|
0f4c57bec3 | ||
|
|
51b1b8c6ec | ||
|
|
5f0a40efc0 | ||
|
|
05c034652f | ||
|
|
1d185b1723 | ||
|
|
bca2af0ed8 | ||
|
|
d301cf6298 | ||
|
|
89c9a03cc1 | ||
|
|
3392e3ceea | ||
|
|
176ee623cf | ||
|
|
42abb7b674 | ||
|
|
3a6655e977 | ||
|
|
9d1f0a065b | ||
|
|
8298966d8f | ||
|
|
d4d9fb88fd | ||
|
|
2beeb1dc38 | ||
|
|
86ad4e5cad | ||
|
|
e55c812a06 | ||
|
|
a0351d19d3 | ||
|
|
90c15bcda8 | ||
|
|
cb754c5ba7 | ||
|
|
9979de26d0 | ||
|
|
4207ecb555 | ||
|
|
8bcfd3fcd8 | ||
|
|
78588f686e | ||
|
|
4753bb4c37 | ||
|
|
81ccdf71ea | ||
|
|
67eb1a44ca | ||
|
|
8af7de10a7 | ||
|
|
3108a072dd | ||
|
|
65be0eaf6f | ||
|
|
bb34025c31 | ||
|
|
278231fe40 | ||
|
|
7356777e50 | ||
|
|
5a33eb26f0 | ||
|
|
d796c48ede | ||
|
|
b1fd934b96 | ||
|
|
9848d38a0b | ||
|
|
2ab409e95e | ||
|
|
64d46f1746 | ||
|
|
e9c787cfe3 | ||
|
|
c200b5bbec | ||
|
|
b6593e609b | ||
|
|
32b0c39fb6 | ||
|
|
6b8e24b0b2 | ||
|
|
2d47f42414 | ||
|
|
a773056202 | ||
|
|
097aca8c43 | ||
|
|
275b07e53f | ||
|
|
46d539f386 | ||
|
|
0adf00d81e | ||
|
|
46b212988f | ||
|
|
3e8772786a | ||
|
|
3a01157cb3 | ||
|
|
079a1412ae | ||
|
|
359edcd46c | ||
|
|
60c2920f3c | ||
|
|
a3cd6a1f17 | ||
|
|
8c2b7df1da | ||
|
|
994eaee49b | ||
|
|
8f0eb1134a | ||
|
|
c95643ca89 | ||
|
|
35ea8bf7d7 | ||
|
|
60431b623c | ||
|
|
185ae4ebff | ||
|
|
1e290a1f26 | ||
|
|
71157633df | ||
|
|
03487348ea | ||
|
|
2d20ddbb97 | ||
|
|
21287ca57f | ||
|
|
612f288904 | ||
|
|
0fbe084ff7 | ||
|
|
fdf90e043a | ||
|
|
b89158d0bb | ||
|
|
dede7cf8ac | ||
|
|
6d3ba2714b | ||
|
|
09991a2a30 | ||
|
|
c5884024de | ||
|
|
1e2166ddaf | ||
|
|
86487b8ae5 | ||
|
|
fa05312d49 | ||
|
|
71d942ffae | ||
|
|
cc1a099363 | ||
|
|
d6342faf84 | ||
|
|
c7cae5303a | ||
|
|
1d80cb08ce | ||
|
|
8163aee868 | ||
|
|
7c061f561f | ||
|
|
339d20bc3e | ||
|
|
434f5f85cd | ||
|
|
22d9605781 | ||
|
|
d644f8df01 | ||
|
|
551789636d | ||
|
|
9ab175a8d8 | ||
|
|
e5a4d80e3b | ||
|
|
0920dcf004 | ||
|
|
e18b3b4d0a | ||
|
|
ffccc88a1d | ||
|
|
e1ec834e5c |
@@ -0,0 +1 @@
|
|||||||
|
*
|
||||||
@@ -1,13 +1,17 @@
|
|||||||

|

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

|
||||||
|
[](https://github.com/rwinkhart/sshyp/releases)
|
||||||
|
|
||||||
[](https://github.com/rwinkhart/sshyp/actions/workflows/codeql-analysis.yml)
|
[](https://github.com/rwinkhart/sshyp/actions/workflows/codeql-analysis.yml)
|
||||||
|
|
||||||
|
pronounced as: 'sheep', 'shēp'
|
||||||
|
|
||||||
sshyp is a very simple self-hosted, synchronized password manager for UNIX(-like) systems (currently Haiku/FreeBSD/Linux).
|
sshyp is a very simple self-hosted, synchronized password manager for UNIX(-like) systems (currently Haiku/FreeBSD/Linux).
|
||||||
|
|
||||||
sshyp is compatible with entries created by pass/password-store, as its original goal was to be like pass/password-store, but far more user-friendly to synchronize with a self-hosted server.
|
sshyp is compatible with entries created by pass/password-store, as its original goal was to be like pass/password-store, but far more user-friendly to synchronize with a self-hosted server.
|
||||||
|
|
||||||
sshyp is (as of writing) the only password-store compatible CLI password manager available for Haiku.
|
|
||||||
|
|
||||||
sshyp makes use of a custom sftp wrapper, called sshync (ssh+sync), to reliably sync user entries with a local or remote server.
|
sshyp makes use of a custom sftp wrapper, called sshync (ssh+sync), to reliably sync user entries with a local or remote server.
|
||||||
|
|
||||||
The name "sshyp" is a combination of its syncing library, "sshync", and "passwords".
|
The name "sshyp" is a combination of its syncing library, "sshync", and "passwords".
|
||||||
@@ -17,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".
|
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.
|
"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
|
# 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.
|
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.
|
||||||
|
|
||||||
@@ -24,77 +30,57 @@ What sshyp can do:
|
|||||||
|
|
||||||
- securely manage a collection of encrypted passwords and notes via CLI
|
- securely manage a collection of encrypted passwords and notes via CLI
|
||||||
- generate new, secure passwords to the user's choice in length and complexity
|
- generate new, secure passwords to the user's choice in length and complexity
|
||||||
- securely sync said passwords and notes seamlessly between devices
|
- securely sync said passwords and notes seamlessly between devices (or just manage them offline)
|
||||||
- utilize [extensions](https://github.com/rwinkhart/sshyp-labs) to interact with your entries is additional ways (such as generating TOTP keys)
|
- utilize [extensions](https://github.com/rwinkhart/sshyp-labs) to interact with your entries is additional ways (such as generating TOTP keys or managing your entries in a GUI)
|
||||||
- everything above with entries created by pass/password-store!
|
- everything above with entries created by pass/password-store!
|
||||||
- everything above on Haiku, FreeBSD, Linux, and Termux (an Android terminal emulator)!
|
- everything above on Haiku, FreeBSD, Linux, and Termux!
|
||||||
|
|
||||||
What sshyp will likely do:
|
|
||||||
|
|
||||||
- (planned v1.2.0) everything it already does, but also in a GUI for Linux/Haiku
|
|
||||||
|
|
||||||
What sshyp definitely won't do:
|
|
||||||
|
|
||||||
- Non-UNIX(-like) support, e.g. Windows (I'd be happy to link to third-party ports, if someone were to make them)
|
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
**Important:** *Shell completions (both Bash and ZSH) may require additional configuration on some distributions - please see [this page](https://github.com/rwinkhart/sshyp/wiki/Completions) of the wiki for support.*
|
||||||
|
|
||||||
Please see the [installation guide](https://github.com/rwinkhart/sshyp/wiki/Installation) in the sshyp wiki for directions specific to your distribution/OS.
|
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.
|
||||||
|
|
||||||
Requests for additional distribution/OS support can be filed as issues.
|
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
|
# Building
|
||||||
Since sshyp is written entirely in Python, it doesn't need to be compiled. It does, however, need to be packaged for installation.
|
|
||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
```
|
```
|
||||||
git clone https://github.com/rwinkhart/sshyp.git
|
git clone https://github.com/rwinkhart/sshyp.git
|
||||||
cd sshyp
|
cd sshyp
|
||||||
./package.sh
|
./package.sh [target] <revision>
|
||||||
```
|
```
|
||||||
|
|
||||||
The packaging script has been tested on Arch Linux with "dpkg" as a dependency for Debian/Ubuntu and Termux packaging and "freebsd-pkg" as a dependency for FreeBSD packaging.
|
The packaging script has been tested on Arch Linux with "dpkg" as a dependency for Debian/Ubuntu/Termux packaging and "freebsd-pkg" as a dependency for FreeBSD packaging.
|
||||||
|
|
||||||
Haiku and Fedora packaging must be done on their own respective distributions.
|
Haiku and Fedora packaging must be done on their own respective distributions.
|
||||||
|
|
||||||
The AUR version and the packages attatched to the release tags were already packaged using this script.
|
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
|
# 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.
|
||||||
|
|
||||||
```
|
In order to configure optional settings or change already configured settings, run `sshyp tweak`.
|
||||||
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.
|
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.
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
All available options can be found with:
|
|
||||||
|
|
||||||
```
|
|
||||||
sshyp --help
|
|
||||||
```
|
|
||||||
|
|
||||||
Or alternatively, in the manpage:
|
|
||||||
|
|
||||||
```
|
|
||||||
man sshyp
|
|
||||||
```
|
|
||||||
|
|
||||||
# Roadmap
|
# Roadmap
|
||||||
Short-term Goals:
|
Short-term Goals:
|
||||||
|
|
||||||
- create minimal GUI app (Linux x86_64, Linux aarch64)
|
- implement a method for tracking how long it has been since a password has been changed (password aging)
|
||||||
- various optimizations/bug fixes
|
- find and fix more bugs
|
||||||
|
|
||||||
Long-term Goals:
|
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
|
- seize the thrones, shear the humans
|
||||||
|
|||||||
-104
@@ -1,104 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
# external modules
|
|
||||||
|
|
||||||
from os import path, system, walk
|
|
||||||
from os.path import expanduser, getmtime, join
|
|
||||||
from pathlib import Path
|
|
||||||
from sys import exit as s_exit
|
|
||||||
|
|
||||||
|
|
||||||
# utility functions
|
|
||||||
|
|
||||||
def get_titles_mods(_directory, _destination, _user_data): # fetches and returns lists of titles and their mod times
|
|
||||||
_title_list, _mod_list = [], []
|
|
||||||
Path(path.expanduser('~/.config/sshync')).mkdir(0o700, parents=True, exist_ok=True) # create config directory
|
|
||||||
# local fetching
|
|
||||||
if _destination == 'l':
|
|
||||||
open(expanduser('~/.config/sshync/database'), 'w').write('') # blanks out database file
|
|
||||||
for _root, _directories, _files in walk(_directory):
|
|
||||||
for _filename in _files:
|
|
||||||
_title_list.append(join(_root.replace(_directory, '', 1), _filename))
|
|
||||||
_mod_list.append(int(getmtime(join(_root, _filename))))
|
|
||||||
for _title in _title_list:
|
|
||||||
open(expanduser('~/.config/sshync/database'), 'a').write(str(_title) + '\n')
|
|
||||||
open(expanduser('~/.config/sshync/database'), 'a').write('^&*\n')
|
|
||||||
for _mod in _mod_list:
|
|
||||||
open(expanduser('~/.config/sshync/database'), 'a').write(str(_mod) + '\n')
|
|
||||||
# remote fetching
|
|
||||||
if _destination == 'r':
|
|
||||||
system(f"ssh -i '{_user_data[5]}' -p {_user_data[2]} {_user_data[0]}@{_user_data[1]} \"cd /bin; python -c 'impo"
|
|
||||||
f"rt sshync; sshync.get_titles_mods(\"'\"{_user_data[4]}\"'\", \"'\"l\"'\", \"'\"{_user_data}\"'\")'\"")
|
|
||||||
system(f"scp -pqs -P {_user_data[2]} -i '{_user_data[5]}' {_user_data[0]}@{_user_data[1]}:"
|
|
||||||
f"'/home/{_user_data[0]}/.config/sshync/database' '{expanduser('~/.config/sshync/')}'")
|
|
||||||
_titles, _sep, _mods = '*&^'.join(open(expanduser('~/.config/sshync/database')).readlines()).replace('\n', '')\
|
|
||||||
.partition('^&*')
|
|
||||||
_title_list, _mod_list = _titles.split('*&^')[:-1], _mods.split('*&^')[1:]
|
|
||||||
return _title_list, _mod_list
|
|
||||||
|
|
||||||
|
|
||||||
def sort_titles_mods(_list_1, _list_2): # creates and returns two lists (of titles and mod times) generated by sorting
|
|
||||||
# information from two different, provided lists
|
|
||||||
_title_list_2_sorted, _mod_list_2_sorted = [], []
|
|
||||||
# title sorting
|
|
||||||
for _title in _list_1[0]:
|
|
||||||
if _title in _list_2[0]:
|
|
||||||
_title_list_2_sorted.append(_title)
|
|
||||||
for _title in _list_2[0]:
|
|
||||||
if _title not in _title_list_2_sorted:
|
|
||||||
_title_list_2_sorted.append(_title)
|
|
||||||
# mod time sorting
|
|
||||||
for _title in _title_list_2_sorted:
|
|
||||||
_mod_list_2_sorted.append(_list_2[1][_list_2[0].index(_title)])
|
|
||||||
return _title_list_2_sorted, _mod_list_2_sorted
|
|
||||||
|
|
||||||
|
|
||||||
def make_profile(_profile_dir, _local_dir, _remote_dir, _identity, _ip, _port, _user): # creates a sshync job profile
|
|
||||||
open(_profile_dir, 'w').write(_user + '\n' + _ip + '\n' + _port + '\n' + _local_dir + '\n' + _remote_dir + '\n' +
|
|
||||||
_identity + '\n')
|
|
||||||
|
|
||||||
|
|
||||||
def get_profile(_profile_dir): # returns a list of data read from a sshync job profile
|
|
||||||
try:
|
|
||||||
_profile_data = open(_profile_dir).readlines()
|
|
||||||
except (FileNotFoundError, IndexError):
|
|
||||||
print('\n\u001b[38;5;9merror: the profile does not exist or is corrupted.\u001b[0m\n')
|
|
||||||
_profile_data = None
|
|
||||||
s_exit(1)
|
|
||||||
# extract data from profile
|
|
||||||
_user = _profile_data[0].replace('\n', '')
|
|
||||||
_ip = _profile_data[1].replace('\n', '')
|
|
||||||
_port = _profile_data[2].replace('\n', '')
|
|
||||||
_local_dir = _profile_data[3].replace('\n', '')
|
|
||||||
_remote_dir = _profile_data[4].replace('\n', '')
|
|
||||||
_identity = _profile_data[5].replace('\n', '')
|
|
||||||
return _user, _ip, _port, _local_dir, _remote_dir, _identity
|
|
||||||
|
|
||||||
|
|
||||||
def run_profile(_profile_dir): # runs a sshync job profile
|
|
||||||
_user_data = get_profile(_profile_dir)
|
|
||||||
_remote_titles_mods_saver = get_titles_mods(_user_data[4], 'r', _user_data) # saved to prevent re-walking directory
|
|
||||||
_index_l = sort_titles_mods(_remote_titles_mods_saver, get_titles_mods(_user_data[3], 'l', _user_data))
|
|
||||||
_index_r = sort_titles_mods(_index_l, _remote_titles_mods_saver)
|
|
||||||
_i = -1
|
|
||||||
for _title in _index_l[0]:
|
|
||||||
_i += 1
|
|
||||||
if _title in _index_r[0]:
|
|
||||||
# compare mod times and sync
|
|
||||||
if int(_index_l[1][_i]) > int(_index_r[1][_i]):
|
|
||||||
print(f"\u001b[38;5;4m{_title[:-4]}\u001b[0m is newer locally, uploading...")
|
|
||||||
system(f"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]) + '/'}'")
|
|
||||||
elif int(_index_l[1][_i]) < int(_index_r[1][_i]):
|
|
||||||
print(f"\u001b[38;5;2m{_title[:-4]}\u001b[0m is newer remotely, downloading...")
|
|
||||||
system(f"scp -pqs -P {_user_data[2]} -i '{_user_data[5]}' '{_user_data[0]}@{_user_data[1]}:"
|
|
||||||
f"{_user_data[4]}{_title}' '{_user_data[3]}{'/'.join(_title.split('/')[:-1]) + '/'}'")
|
|
||||||
else:
|
|
||||||
print(f"\u001b[38;5;4m{_title[:-4]}\u001b[0m is not on remote server, uploading...")
|
|
||||||
system(f"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]) + '/'}'")
|
|
||||||
for _title in _index_r[0]:
|
|
||||||
if _title not in _index_l[0]:
|
|
||||||
print(f"\u001b[38;5;2m{_title[:-4]}\u001b[0m is not in local directory, downloading...")
|
|
||||||
system(f"scp -pqs -P {_user_data[2]} -i '{_user_data[5]}' '{_user_data[0]}@{_user_data[1]}:"
|
|
||||||
f"{_user_data[4]}{_title}' '{_user_data[3]}{'/'.join(_title.split('/')[:-1]) + '/'}'")
|
|
||||||
-765
@@ -1,765 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
# external modules
|
|
||||||
|
|
||||||
from os import environ, listdir, path, remove, system, uname, walk
|
|
||||||
from pathlib import Path
|
|
||||||
from random import randint, SystemRandom
|
|
||||||
from shutil import get_terminal_size, move, rmtree
|
|
||||||
import sshync
|
|
||||||
import string
|
|
||||||
from subprocess import CalledProcessError, Popen, PIPE, run
|
|
||||||
from sys import argv, exit as s_exit
|
|
||||||
from textwrap import fill
|
|
||||||
|
|
||||||
|
|
||||||
# BELOW - utility functions
|
|
||||||
|
|
||||||
def entry_list_gen(_directory=path.expanduser('~/.local/share/sshyp/')): # generates and prints full entry list
|
|
||||||
print('\n\u001b[38;5;0;48;5;15msshyp entries:\u001b[0m\n')
|
|
||||||
_entry_list, _color_alternator = [], 1
|
|
||||||
for _entry in sorted(listdir(_directory)):
|
|
||||||
if Path(f"{_directory}{_entry}").is_file():
|
|
||||||
if _color_alternator == 1:
|
|
||||||
_entry_list.append(f"{_entry.replace('.gpg', '')}")
|
|
||||||
_color_alternator = 2
|
|
||||||
else:
|
|
||||||
_entry_list.append(f"\u001b[38;5;8m{_entry.replace('.gpg', '')}\u001b[0m")
|
|
||||||
_color_alternator = 1
|
|
||||||
_real = len(' '.join(_entry_list)) - (5.5 * len(_entry_list))
|
|
||||||
if _real <= get_terminal_size()[0]:
|
|
||||||
_width = len(' '.join(_entry_list))
|
|
||||||
else:
|
|
||||||
_width = (len(' '.join(_entry_list)) / (_real / get_terminal_size()[0]) - 25)
|
|
||||||
try:
|
|
||||||
print(fill(' '.join(_entry_list), width=_width) + '\n')
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
for _root, _directories, _files in walk(_directory):
|
|
||||||
for _dir in sorted(_directories):
|
|
||||||
_inner_dir = f"{_root.replace(_directory, '')}/{_dir}"
|
|
||||||
print(f"\u001b[38;5;15;48;5;238m{_inner_dir}/\u001b[0m")
|
|
||||||
_entry_list, _color_alternator = [], 1
|
|
||||||
for _s_root, _s_directories, _s_files in walk(f"{_directory[:-1]}{_inner_dir}"):
|
|
||||||
for _entry in sorted(_s_files):
|
|
||||||
if _color_alternator == 1:
|
|
||||||
_entry_list.append(f"{_entry.replace('.gpg', '')}")
|
|
||||||
_color_alternator = 2
|
|
||||||
else:
|
|
||||||
_entry_list.append(f"\u001b[38;5;8m{_entry.replace('.gpg', '')}\u001b[0m")
|
|
||||||
_color_alternator = 1
|
|
||||||
_real = len(' '.join(_entry_list)) - (5.5 * len(_entry_list))
|
|
||||||
if _real <= get_terminal_size()[0]:
|
|
||||||
_width = len(' '.join(_entry_list))
|
|
||||||
else:
|
|
||||||
_width = (len(' '.join(_entry_list)) / (_real / get_terminal_size()[0]) - 25)
|
|
||||||
try:
|
|
||||||
print(fill(' '.join(_entry_list), width=_width) + '\n')
|
|
||||||
except ValueError:
|
|
||||||
print('\u001b[38;5;9m-empty directory-\u001b[0m\n')
|
|
||||||
|
|
||||||
|
|
||||||
def entry_reader(_decrypted_entry): # displays the contents of an entry in a readable format
|
|
||||||
_entry_lines, _notes_flag = open(_decrypted_entry, 'r').readlines(), 0
|
|
||||||
print()
|
|
||||||
for _num in range(len(_entry_lines)):
|
|
||||||
try:
|
|
||||||
if _num == 0 and _entry_lines[1] != '\n':
|
|
||||||
print(f"\u001b[38;5;15;48;5;238musername:\u001b[0m\n{_entry_lines[1].strip()}\n")
|
|
||||||
elif _num == 1 and _entry_lines[0] != '\n':
|
|
||||||
print(f"\u001b[38;5;15;48;5;238mpassword:\u001b[0m\n{_entry_lines[0].strip()}\n")
|
|
||||||
elif _num == 2 and _entry_lines[2] != '\n':
|
|
||||||
print(f"\u001b[38;5;15;48;5;238murl:\u001b[0m\n{_entry_lines[_num].strip()}\n")
|
|
||||||
elif _num >= 3 and _entry_lines[_num] != '\n' and _notes_flag != 1:
|
|
||||||
_notes_flag = 1
|
|
||||||
print(f"\u001b[38;5;15;48;5;238mnotes:\u001b[0m\n{_entry_lines[_num].strip()}")
|
|
||||||
elif _num >= 3 and _notes_flag == 1:
|
|
||||||
print(_entry_lines[_num].replace('\n', ''))
|
|
||||||
if _notes_flag == 1:
|
|
||||||
try:
|
|
||||||
_line_test = _entry_lines[_num + 1]
|
|
||||||
except IndexError:
|
|
||||||
print()
|
|
||||||
except IndexError:
|
|
||||||
if _num == 0:
|
|
||||||
print(f"\u001b[38;5;15;48;5;238mpassword:\u001b[0m\n{_entry_lines[0]}\n")
|
|
||||||
|
|
||||||
|
|
||||||
def entry_name_fetch(_entry_name_location): # fetches and returns entry name from user input or from provided argument
|
|
||||||
if type(_entry_name_location) == str:
|
|
||||||
entry_list_gen()
|
|
||||||
_entry_name = str(input(_entry_name_location))
|
|
||||||
else:
|
|
||||||
_entry_name_split = argument.split(' ')
|
|
||||||
del _entry_name_split[:_entry_name_location]
|
|
||||||
_entry_name = ' '.join(_entry_name_split)
|
|
||||||
if _entry_name.startswith('/'):
|
|
||||||
return _entry_name.replace('/', '', 1)
|
|
||||||
else:
|
|
||||||
return _entry_name
|
|
||||||
|
|
||||||
|
|
||||||
def shm_gen(_tmp_dir=path.expanduser('~/.config/sshyp/tmp/')): # creates a temporary directory for entry editing
|
|
||||||
_shm_folder_gen = ''.join(SystemRandom().choice(string.ascii_letters + string.digits)
|
|
||||||
for _ in range(randint(10, 30)))
|
|
||||||
_shm_entry_gen = ''.join(SystemRandom().choice(string.ascii_letters + string.digits)
|
|
||||||
for _ in range(randint(10, 30)))
|
|
||||||
Path(_tmp_dir + _shm_folder_gen).mkdir(0o700)
|
|
||||||
return _shm_folder_gen, _shm_entry_gen
|
|
||||||
|
|
||||||
|
|
||||||
def pass_gen(): # generates and returns a random password based on user-specified options
|
|
||||||
def _pass_gen_function(__complexity, __length):
|
|
||||||
if __complexity.lower() == 's':
|
|
||||||
__character_pool = string.ascii_letters + string.digits
|
|
||||||
else:
|
|
||||||
__character_pool = string.ascii_letters + string.digits + string.punctuation
|
|
||||||
__gen = ''.join(SystemRandom().choice(__character_pool) for _ in range(__length))
|
|
||||||
__min_special, __special = round(.2 * __length), 0
|
|
||||||
for __character in __gen:
|
|
||||||
if not __character.isalpha():
|
|
||||||
__special += 1
|
|
||||||
if __special < __min_special:
|
|
||||||
__gen = _pass_gen_function(__complexity, __length)
|
|
||||||
return __gen
|
|
||||||
try:
|
|
||||||
_length = int(input('password length: '))
|
|
||||||
except ValueError:
|
|
||||||
print(f"\n\u001b[38;5;9merror: a non-integer value was input for password length\u001b[0m\n")
|
|
||||||
_gen = pass_gen()
|
|
||||||
return _gen
|
|
||||||
if _length > 840:
|
|
||||||
_length = 840
|
|
||||||
print('\n\u001b[38;5;9mpassword length has been limited to the maximum of 840 characters\u001b[0m\n')
|
|
||||||
_complexity = str(input('password complexity - simple (for compatibility) or complex (for security)? (s/C) '))
|
|
||||||
_gen = _pass_gen_function(_complexity, _length)
|
|
||||||
return _gen
|
|
||||||
|
|
||||||
|
|
||||||
def encrypt(_entry_dir, _shm_folder, _shm_entry, _gpg_com, _gpg_id, _tmp_dir=path.expanduser('~/.config/sshyp/tmp/')):
|
|
||||||
# encrypts an entry and cleans up the temporary files
|
|
||||||
system(f"{_gpg_com} -qr {str(_gpg_id)} -e '{_tmp_dir}{_shm_folder}/{_shm_entry}'")
|
|
||||||
move(f"{_tmp_dir}{_shm_folder}/{_shm_entry}.gpg", f"{_entry_dir}.gpg")
|
|
||||||
rmtree(f"{_tmp_dir}{_shm_folder}")
|
|
||||||
|
|
||||||
|
|
||||||
def decrypt(_entry_dir, _shm_folder, _shm_entry, _gpg_command, _tmp_dir=path.expanduser('~/.config/sshyp/tmp/')):
|
|
||||||
# decrypts an entry to a temporary directory
|
|
||||||
if _shm_folder == 0 and _shm_entry == 0:
|
|
||||||
_command = f"{_gpg_command} -qd --output /dev/null {path.expanduser('~/.config/sshyp/lock.gpg')}"
|
|
||||||
else:
|
|
||||||
_command = f"{_gpg_command} -qd --output {_tmp_dir}{_shm_folder}/{_shm_entry} {_entry_dir}.gpg"
|
|
||||||
try:
|
|
||||||
run(_command, shell=True, stderr=PIPE, check=True, close_fds=True)
|
|
||||||
except CalledProcessError:
|
|
||||||
print(f"\n\u001b[38;5;9merror: could not decrypt - ensure the correct gpg key is present\u001b[0m\n")
|
|
||||||
s_exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
def optimized_edit(_lines, _edit_data, _edit_line): # ensures an edited entry is optimized for best compatibility
|
|
||||||
while len(_lines) < _edit_line + 1:
|
|
||||||
_lines += ['\n']
|
|
||||||
if _edit_data is not None:
|
|
||||||
_lines[_edit_line] = _edit_data.strip('\n').rstrip() + '\n'
|
|
||||||
for _num in range(len(_lines)):
|
|
||||||
if not _lines[_num].endswith('\n'):
|
|
||||||
_lines[_num] += '\n'
|
|
||||||
for _num in reversed(range(len(_lines))):
|
|
||||||
if _lines[_num] == '\n':
|
|
||||||
_lines = _lines[:-1]
|
|
||||||
elif _lines[_num].endswith('\n'):
|
|
||||||
_lines[_num] = _lines[_num].rstrip()
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
return _lines
|
|
||||||
|
|
||||||
|
|
||||||
def edit_note(_shm_folder, _shm_entry, _lines): # edits the note attached to an entry
|
|
||||||
_reg_lines = _lines[0:3]
|
|
||||||
open(f"{tmp_dir}{_shm_folder}/{_shm_entry}-n", 'w').writelines(_lines[3:])
|
|
||||||
system(f"{editor} {tmp_dir}{_shm_folder}/{_shm_entry}-n")
|
|
||||||
_new_notes = open(f"{tmp_dir}{_shm_folder}/{_shm_entry}-n").readlines()
|
|
||||||
while len(_reg_lines) < 3:
|
|
||||||
_reg_lines += ['\n']
|
|
||||||
_noted_lines = _reg_lines + _new_notes
|
|
||||||
return _noted_lines
|
|
||||||
|
|
||||||
|
|
||||||
def copy_name_check(_port, _username_ssh, _ip, _client_device_name):
|
|
||||||
# attempts to connect to the user's server via ssh to register the device for syncing
|
|
||||||
_command = f"ssh -o ConnectTimeout=3 -i '{path.expanduser('~/.ssh/sshyp')}' -p {_port} {_username_ssh}@{_ip} " \
|
|
||||||
f"\"touch '/home/{_username_ssh}/.config/sshyp/devices/{_client_device_name}'\""
|
|
||||||
try:
|
|
||||||
run(_command, shell=True, stderr=PIPE, check=True, close_fds=True)
|
|
||||||
except CalledProcessError:
|
|
||||||
print('\n\u001b[38;5;9mwarning: ssh connection could not be made - ensure the public key (~/.ssh/sshyp.pub) is '
|
|
||||||
'registered on the remote server and that the entered ip, port, and username are correct\n\nsyncing '
|
|
||||||
'functionality will be disabled until this is addressed\u001b[0m\n')
|
|
||||||
open(path.expanduser('~/.config/sshyp/ssh-error'), 'w').write('1')
|
|
||||||
return 1
|
|
||||||
open(path.expanduser('~/.config/sshyp/ssh-error'), 'w').write('0')
|
|
||||||
return 0
|
|
||||||
|
|
||||||
# BELOW - argument-specific functions
|
|
||||||
|
|
||||||
|
|
||||||
def tweak(): # runs configuration wizard
|
|
||||||
_divider = f"\n{'=' * (get_terminal_size()[0] - int((.5 * get_terminal_size()[0])))}\n\n"
|
|
||||||
# storage/config directory creation
|
|
||||||
Path(path.expanduser('~/.local/share/sshyp')).mkdir(0o700, parents=True, exist_ok=True)
|
|
||||||
Path(path.expanduser('~/.config/sshyp/devices')).mkdir(0o700, parents=True, exist_ok=True)
|
|
||||||
if not Path(f"{path.expanduser('~/.config/sshyp/tmp')}").exists():
|
|
||||||
if uname()[0] == 'Haiku' or uname()[0] == 'FreeBSD':
|
|
||||||
system(f"ln -s /tmp {path.expanduser('~/.config/sshyp/tmp')}")
|
|
||||||
elif Path("/data/data/com.termux").exists():
|
|
||||||
system(f"ln -s '/data/data/com.termux/files/usr/tmp' {path.expanduser('~/.config/sshyp/tmp')}")
|
|
||||||
else:
|
|
||||||
system(f"ln -s /dev/shm {path.expanduser('~/.config/sshyp/tmp')}")
|
|
||||||
# device type configuration
|
|
||||||
_device_type = input('\nclient or server installation? (C/s) ')
|
|
||||||
if _device_type.lower() == 's':
|
|
||||||
open(path.expanduser('~/.config/sshyp/sshyp-device'), 'w').write(_device_type)
|
|
||||||
Path(path.expanduser('~/.config/sshyp/deleted')).mkdir(0o700, parents=True, exist_ok=True)
|
|
||||||
print(f"\n\u001b[4;1mmake sure the ssh service is running and properly configured\u001b[0m\n"
|
|
||||||
f"{_divider}configuration complete\n")
|
|
||||||
s_exit(0)
|
|
||||||
else:
|
|
||||||
# device type configuration
|
|
||||||
_device_type = 'c' # ensure device type flag is properly set
|
|
||||||
open(path.expanduser('~/.config/sshyp/sshyp-device'), 'w').write(_device_type)
|
|
||||||
|
|
||||||
# gpg configuration
|
|
||||||
_gpg_id = input(f"{_divider}sshyp requires the use of a unique gpg key - use an (e)xisting key or (g)enerate "
|
|
||||||
f"a new one? (E/g) ")
|
|
||||||
if _gpg_id.lower() != 'g':
|
|
||||||
system(f"{gpg} -k")
|
|
||||||
_gpg_id = str(input('gpg key id: '))
|
|
||||||
else:
|
|
||||||
print('\na unique gpg key is being generated for you...')
|
|
||||||
if not Path(path.expanduser('~/.config/sshyp/gpg-gen')).is_file():
|
|
||||||
open(path.expanduser('~/.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: https://github.com/rwinkhart/sshyp\n', 'Expire-Date: 0'])
|
|
||||||
if uname()[0] == 'Haiku':
|
|
||||||
run(gpg + ' --batch --generate-key --passphrase ' + "'" +
|
|
||||||
input('\ngpg passphrase: ') + "'" + " '" +
|
|
||||||
path.expanduser('~/.config/sshyp/gpg-gen') + "'", shell=True)
|
|
||||||
else:
|
|
||||||
run(f"{gpg} --batch --generate-key '{path.expanduser('~/.config/sshyp/gpg-gen')}'", shell=True)
|
|
||||||
remove(path.expanduser('~/.config/sshyp/gpg-gen'))
|
|
||||||
_gpg_id = run(f"{gpg} -k", shell=True, stdout=PIPE, text=True).stdout.split('\n')[-4].strip()
|
|
||||||
|
|
||||||
# lock file generation
|
|
||||||
if Path(path.expanduser('~/.config/sshyp/lock.gpg')).is_file():
|
|
||||||
remove(path.expanduser('~/.config/sshyp/lock.gpg'))
|
|
||||||
open(path.expanduser('~/.config/sshyp/lock'), 'w')
|
|
||||||
system(f"{gpg} -qr {str(_gpg_id)} -e {path.expanduser('~/.config/sshyp/lock')}")
|
|
||||||
remove(path.expanduser('~/.config/sshyp/lock'))
|
|
||||||
|
|
||||||
# ssh key configuration
|
|
||||||
_ssh_gen = (input(f"{_divider}make sure the ssh service on the remote server is running and properly "
|
|
||||||
f"configured\n\nsync support requires a unique ssh key - would you like to have this "
|
|
||||||
f"automatically generated? (Y/n) "))
|
|
||||||
if _ssh_gen.lower() != 'n':
|
|
||||||
if uname()[0] == 'Haiku':
|
|
||||||
Path(f"{path.expanduser('~')}/.ssh").mkdir(0o700, exist_ok=True)
|
|
||||||
system('ssh-keygen -t ed25519 -f ~/.ssh/sshyp')
|
|
||||||
else:
|
|
||||||
print(f"\n\u001b[4;1mensure that the key file you are using is located at "
|
|
||||||
f"{path.expanduser('~/.ssh/sshyp')}\u001b[0m")
|
|
||||||
|
|
||||||
# ssh ip+port configuration
|
|
||||||
_ip_port = str(input(f"{_divider}example input: 10.10.10.10:9999\n\nip and ssh port of the remote server: "))
|
|
||||||
_ip, _sep, _port = _ip_port.partition(':')
|
|
||||||
|
|
||||||
# ssh user configuration
|
|
||||||
_username_ssh = str(input('\nusername of the remote server: '))
|
|
||||||
|
|
||||||
# sshyp-only data storage
|
|
||||||
open(path.expanduser('~/.config/sshyp/sshyp-data'), 'w')\
|
|
||||||
.write(_gpg_id + '\n' + input(f"{_divider}example input: vim\n\npreferred text editor: "))
|
|
||||||
|
|
||||||
# sshync profile generation
|
|
||||||
sshync.make_profile(path.expanduser('~/.config/sshyp/sshyp.sshync'),
|
|
||||||
path.expanduser('~/.local/share/sshyp/'), f"/home/{_username_ssh}/.local/share/sshyp/",
|
|
||||||
path.expanduser('~/.ssh/sshyp'), _ip, _port, _username_ssh)
|
|
||||||
|
|
||||||
# device name configuration
|
|
||||||
for _name in listdir(path.expanduser('~/.config/sshyp/devices')): # remove existing device name
|
|
||||||
remove(f"{path.expanduser('~/.config/sshyp/devices/')}{_name}")
|
|
||||||
print(f"{_divider}\u001b[4;1mimportant:\u001b[0m this name \u001b[4;1mmust\u001b[0m be unique amongst your "
|
|
||||||
f"client devices\n\nthis is used to keep track of which devices have up-to-date databases\n")
|
|
||||||
_client_device_name = str(input('device name: '))
|
|
||||||
open(f"{path.expanduser('~/.config/sshyp/devices/')}{_client_device_name}", 'w')
|
|
||||||
copy_name_check(_port, _username_ssh, _ip, _client_device_name)
|
|
||||||
|
|
||||||
print(f"{_divider}configuration complete\n")
|
|
||||||
|
|
||||||
|
|
||||||
def print_info(): # prints help text based on argument
|
|
||||||
if argument_list[1] == 'help' or argument_list[1] == '--help' or argument_list[1] == '-h':
|
|
||||||
print('\n\u001b[1msshyp copyright (c) 2021-2022 randall winkhart\u001b[0m\n')
|
|
||||||
print("this is free software, and you are welcome to redistribute it under certain conditions;\nthis program "
|
|
||||||
"comes with absolutely no warranty;\ntype `sshyp license' for details")
|
|
||||||
print('\n\u001b[1musage:\u001b[0m sshyp [option [flag] [<entry name>]] | [/<entry name>]\n')
|
|
||||||
print('\u001b[1moptions:\u001b[0m')
|
|
||||||
print('help/--help/-h bring up this menu')
|
|
||||||
print('version/-v display sshyp version info')
|
|
||||||
print('tweak configure sshyp')
|
|
||||||
print('add add an entry')
|
|
||||||
print('gen generate a new password')
|
|
||||||
print('edit edit an existing entry')
|
|
||||||
print('copy copy details of an entry to your clipboard')
|
|
||||||
print('shear/-rm delete an existing entry')
|
|
||||||
print('sync/-s manually sync the entry directory via sshync\n')
|
|
||||||
print('\u001b[1mflags:\u001b[0m')
|
|
||||||
print('add:')
|
|
||||||
print(' password/-p add a password entry')
|
|
||||||
print(' note/-n add a note entry')
|
|
||||||
print(' folder/-f add a new folder for entries')
|
|
||||||
print('edit:')
|
|
||||||
print(' rename/relocate/-r rename or relocate an entry')
|
|
||||||
print(' username/-u change the username of an entry')
|
|
||||||
print(' password/-p change the password of an entry')
|
|
||||||
print(' note/-n change the note attached to an entry')
|
|
||||||
print(' url/-l change the url attached to an entry')
|
|
||||||
print('copy:')
|
|
||||||
print(' username/-u copy the username of an entry to your clipboard')
|
|
||||||
print(' password/-p copy the password of an entry to your clipboard')
|
|
||||||
print(' url/-l copy the url of an entry to your clipboard')
|
|
||||||
print(' note/-n copy the note of an entry to your clipboard')
|
|
||||||
print('gen:')
|
|
||||||
print(' update/-u generate a password for an existing entry\n')
|
|
||||||
print("\u001b[1mtip:\u001b[0m you can quickly read an entry with 'sshyp /<entry name>'")
|
|
||||||
elif argument_list[1] == 'version' or argument_list[1] == '-v':
|
|
||||||
print('\nsshyp is a simple, self-hosted, sftp-synchronized password manager\nfor unix(-like) systems (haiku/'
|
|
||||||
'freebsd/linux/termux)\n\nsshyp is a viable alternative to (and compatible with) pass/password-store\n')
|
|
||||||
print(" .. \u001b[38;5;9m♥♥ ♥♥\u001b[0m ..\n .''.''/()\\ \u001b[38;5;13m"
|
|
||||||
"♥♥♥♥♥♥♥\u001b[0m /()\\''.''.\n * : \u001b[38;5;9m♥♥♥♥♥\u001b[0m : *"
|
|
||||||
"\n `..'..' \u001b[38;5;13m♥♥♥\u001b[0m `..'..'\n // \\\\ "
|
|
||||||
"\u001b[38;5;9m♥\u001b[0m // \\\\")
|
|
||||||
print('\u001b[38;5;7;48;5;8m<><><><><><><><><><><><><><><><><><><><><><><><><><><><>\u001b[0m')
|
|
||||||
print('\u001b[38;5;7;48;5;8m/\u001b[38;5;15;48;5;15m '
|
|
||||||
'\u001b[38;5;7;48;5;8m/\u001b[0m')
|
|
||||||
print('\u001b[38;5;7;48;5;8m/\u001b[38;5;15;48;5;15m \u001b[38;5;15;48;5;8msshyp copyright (c) 2021-2022 '
|
|
||||||
'randall winkhart\u001b[38;5;15;48;5;15m \u001b[38;5;7;48;5;8m/\u001b[0m')
|
|
||||||
print('\u001b[38;5;7;48;5;8m/\u001b[38;5;15;48;5;15m '
|
|
||||||
'\u001b[38;5;7;48;5;8m/\u001b[0m')
|
|
||||||
print('\u001b[38;5;7;48;5;8m/\u001b[38;5;15;48;5;15m \u001b[38;5;15;48;5;8mversion 1.1.2'
|
|
||||||
'\u001b[38;5;15;48;5;15m \u001b[38;5;7;48;5;8m/\u001b[0m')
|
|
||||||
print('\u001b[38;5;7;48;5;8m/\u001b[38;5;15;48;5;15m \u001b[38;5;15;48;5;8mthe sheecrets '
|
|
||||||
'update\u001b[38;5;15;48;5;15m \u001b[38;5;7;48;5;8m/\u001b[0m')
|
|
||||||
print('\u001b[38;5;7;48;5;8m/\u001b[38;5;15;48;5;15m '
|
|
||||||
'\u001b[38;5;7;48;5;8m/\u001b[0m')
|
|
||||||
print('\u001b[38;5;7;48;5;8m<><><><><><><><><><><><><><><><><><><><><><><><><><><><>\u001b[0m\n')
|
|
||||||
print('see https://github.com/rwinkhart/sshyp for more information\n')
|
|
||||||
elif argument_list[1] == 'license':
|
|
||||||
print('\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU '
|
|
||||||
'General\nPublic License as published by the Free Software Foundation, either version 3 of the License,'
|
|
||||||
'\nor (at your option) any later version.\n\nThis program is distributed in the hope that it will be '
|
|
||||||
'useful, but WITHOUT ANY WARRANTY;\nwithout even the implied warranty of MERCHANTABILITY or FITNESS FOR A'
|
|
||||||
' PARTICULAR PURPOSE.\nSee the GNU General Public License for more details.'
|
|
||||||
'\n\nhttps://opensource.org/licenses/GPL-3.0\n')
|
|
||||||
elif argument_list[1] == 'add':
|
|
||||||
print('\n\u001b[1musage:\u001b[0m sshyp add [flag [<entry name>]]\u001b[0m\n')
|
|
||||||
print('\u001b[1mflags:\u001b[0m')
|
|
||||||
print('add:')
|
|
||||||
print(' password/-p add a password entry')
|
|
||||||
print(' note/-n add a note entry')
|
|
||||||
print(' folder/-f add a new folder for entries\n')
|
|
||||||
elif argument_list[1] == 'edit':
|
|
||||||
print('\n\u001b[1musage:\u001b[0m sshyp edit [flag [<entry name>]]\u001b[0m\n')
|
|
||||||
print('\u001b[1mflags:\u001b[0m')
|
|
||||||
print('edit:')
|
|
||||||
print(' rename/relocate/-r rename or relocate an entry')
|
|
||||||
print(' username/-u change the username of an entry')
|
|
||||||
print(' password/-p change the password of an entry')
|
|
||||||
print(' url/-l change the url attached to an entry')
|
|
||||||
print(' note/-n change the note attached to an entry\n')
|
|
||||||
elif argument_list[1] == 'copy':
|
|
||||||
print('\n\u001b[1musage:\u001b[0m sshyp copy [flag [<entry name>]]\u001b[0m\n')
|
|
||||||
print('\u001b[1mflags:\u001b[0m')
|
|
||||||
print('copy:')
|
|
||||||
print(' username/-u copy the username of an entry to your clipboard')
|
|
||||||
print(' password/-p copy the password of an entry to your clipboard')
|
|
||||||
print(' url/-l copy the url of an entry to your clipboard')
|
|
||||||
print(' note/-n copy the note of an entry to your clipboard\n')
|
|
||||||
|
|
||||||
|
|
||||||
def no_arg(): # displays a list of entries and gives an option to select one for viewing
|
|
||||||
print("\nfor a list of usable commands, run 'sshyp help'")
|
|
||||||
_entry_name = entry_name_fetch('entry to read: ')
|
|
||||||
if not Path(f"{directory}{_entry_name}.gpg").exists():
|
|
||||||
print(f"\n\u001b[38;5;9merror: entry ({_entry_name}) does not exist\u001b[0m\n")
|
|
||||||
s_exit(1)
|
|
||||||
_shm_folder, _shm_entry = shm_gen()
|
|
||||||
decrypt(directory + _entry_name, _shm_folder, _shm_entry, gpg)
|
|
||||||
entry_reader(f"{tmp_dir}{_shm_folder}/{_shm_entry}")
|
|
||||||
rmtree(f"{tmp_dir}{_shm_folder}")
|
|
||||||
|
|
||||||
|
|
||||||
def read_shortcut(): # shortcut to quickly read an entry
|
|
||||||
if not Path(f"{directory}{argument.replace('/', '', 1)}.gpg").exists():
|
|
||||||
print(f"\n\u001b[38;5;9merror: entry ({argument.replace('/', '', 1)}) does not exist\u001b[0m\n")
|
|
||||||
s_exit(1)
|
|
||||||
_shm_folder, _shm_entry = shm_gen()
|
|
||||||
decrypt(directory + argument.replace('/', '', 1), _shm_folder, _shm_entry, gpg)
|
|
||||||
entry_reader(f"{tmp_dir}{_shm_folder}/{_shm_entry}")
|
|
||||||
rmtree(f"{tmp_dir}{_shm_folder}")
|
|
||||||
|
|
||||||
|
|
||||||
def sync(): # calls sshync to sync changes to the user's server
|
|
||||||
print('\nsyncing entries with the server device...\n')
|
|
||||||
# check for deletions
|
|
||||||
system(f"ssh -i '{path.expanduser('~/.ssh/sshyp')}' -p {port} {username_ssh}@{ip} \"cd /bin; python -c "
|
|
||||||
f"'import sshypRemote; sshypRemote.deletion_check(\"'\"{client_device_name}\"'\")'\"")
|
|
||||||
system(f"scp -pqs -P {port} -i '{path.expanduser('~/.ssh/sshyp')}' {username_ssh}@{ip}:'/home/{username_ssh}"
|
|
||||||
f"/.config/sshyp/deletion_database' {path.expanduser('~/.config/sshyp/')}")
|
|
||||||
try:
|
|
||||||
_deletion_database = open(path.expanduser('~/.config/sshyp/deletion_database')).readlines()
|
|
||||||
except (FileNotFoundError, IndexError):
|
|
||||||
print('\n\u001b[38;5;9merror: the deletion database does not exist or is corrupted\u001b[0m\n')
|
|
||||||
_deletion_database = None
|
|
||||||
s_exit(1)
|
|
||||||
for _file in _deletion_database:
|
|
||||||
try:
|
|
||||||
if silent_sync != 1:
|
|
||||||
print(f"\u001b[38;5;208m{_file[:-1]}\u001b[0m has been sheared, removing...")
|
|
||||||
if _file[:-1].endswith('/'):
|
|
||||||
rmtree(f"{directory}{_file[:-1]}")
|
|
||||||
else:
|
|
||||||
remove(f"{directory}{_file[:-1]}.gpg")
|
|
||||||
except FileNotFoundError:
|
|
||||||
if silent_sync != 1:
|
|
||||||
print('location does not exist locally')
|
|
||||||
# check for new folders
|
|
||||||
system(f"ssh -i '{path.expanduser('~/.ssh/sshyp')}' -p {port} {username_ssh}@{ip} \"cd /bin; python -c "
|
|
||||||
f"'import sshypRemote; sshypRemote.folder_check()'\"")
|
|
||||||
system(f"scp -pqs -P {port} -i '{path.expanduser('~/.ssh/sshyp')}' {username_ssh}@{ip}:'/home/{username_ssh}"
|
|
||||||
f"/.config/sshyp/folder_database' {path.expanduser('~/.config/sshyp/')}")
|
|
||||||
try:
|
|
||||||
_folder_database = open(path.expanduser('~/.config/sshyp/folder_database')).readlines()
|
|
||||||
except (FileNotFoundError, IndexError):
|
|
||||||
print('\n\u001b[38;5;9merror: the folder database does not exist or is corrupted\u001b[0m\n')
|
|
||||||
_folder_database = None
|
|
||||||
s_exit(1)
|
|
||||||
for _folder in _folder_database:
|
|
||||||
if Path(f"{path.expanduser('~')}{_folder[:-1]}").is_dir():
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
print(f"\u001b[38;5;2m{_folder.replace('/.local/share/sshyp/', '')[:-1]}/\u001b[0m does not exist locally, "
|
|
||||||
f"creating...")
|
|
||||||
Path(f"{path.expanduser('~')}{_folder[:-1]}").mkdir(0o700, parents=True, exist_ok=True)
|
|
||||||
# set permissions before uploading
|
|
||||||
system('find ' + directory + ' -type d -exec chmod -R 700 {} +')
|
|
||||||
system('find ' + directory + ' -type f -exec chmod -R 600 {} +')
|
|
||||||
sshync.run_profile(path.expanduser('~/.config/sshyp/sshyp.sshync'))
|
|
||||||
|
|
||||||
|
|
||||||
def add_entry(): # adds a new entry
|
|
||||||
_shm_folder, _shm_entry = None, None # sets base-line values to avoid errors
|
|
||||||
if len(argument_list) < 4:
|
|
||||||
_entry_name = entry_name_fetch('name of new entry: ')
|
|
||||||
else:
|
|
||||||
_entry_name = entry_name_fetch(2)
|
|
||||||
if Path(f"{directory}{_entry_name}.gpg").is_file():
|
|
||||||
print(f"\n\u001b[38;5;9merror: entry ({_entry_name}) already exists\u001b[0m\n")
|
|
||||||
s_exit(1)
|
|
||||||
if argument_list[2] == 'note' or argument_list[2] == '-n':
|
|
||||||
_shm_folder, _shm_entry = shm_gen()
|
|
||||||
system(f"{editor} {tmp_dir}{_shm_folder}/{_shm_entry}-n")
|
|
||||||
_notes = open(f"{tmp_dir}{_shm_folder}/{_shm_entry}-n", 'r').read()
|
|
||||||
open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'w').writelines(optimized_edit(['', '', '', _notes], None, -1))
|
|
||||||
elif argument_list[2] == 'password' or argument_list[2] == '-p':
|
|
||||||
_username = str(input('username: '))
|
|
||||||
_password = str(input('password: '))
|
|
||||||
_url = str(input('url: '))
|
|
||||||
_add_note = input('add a note to this entry? (y/N) ')
|
|
||||||
_shm_folder, _shm_entry = shm_gen()
|
|
||||||
if _add_note.lower() == 'y':
|
|
||||||
system(f"{editor} {tmp_dir}{_shm_folder}/{_shm_entry}-n")
|
|
||||||
_notes = open(f"{tmp_dir}{_shm_folder}/{_shm_entry}-n", 'r').read()
|
|
||||||
else:
|
|
||||||
_notes = ''
|
|
||||||
open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'w')\
|
|
||||||
.writelines(optimized_edit([_password, _username, _url, _notes], None, -1))
|
|
||||||
print('\n\u001b[1mentry preview:\u001b[0m')
|
|
||||||
entry_reader(f"{tmp_dir}{_shm_folder}/{_shm_entry}")
|
|
||||||
encrypt(directory + _entry_name, _shm_folder, _shm_entry, gpg, gpg_id)
|
|
||||||
|
|
||||||
|
|
||||||
def add_folder(): # creates a new folder
|
|
||||||
if len(argument_list) < 4:
|
|
||||||
_entry_name = entry_name_fetch('name of new folder: ')
|
|
||||||
else:
|
|
||||||
_entry_name = entry_name_fetch(2)
|
|
||||||
Path(directory + _entry_name).mkdir(0o700)
|
|
||||||
system(f"ssh -i '{path.expanduser('~/.ssh/sshyp')}' -p {port} {username_ssh}@{ip} \"mkdir -p '{directory_ssh}"
|
|
||||||
f"{_entry_name}'\"")
|
|
||||||
|
|
||||||
|
|
||||||
def rename(): # renames an entry or folder
|
|
||||||
if argument == 'edit rename' or argument == 'edit relocate' or argument == 'edit -r':
|
|
||||||
_entry_name = entry_name_fetch('entry/folder to rename/relocate: ')
|
|
||||||
else:
|
|
||||||
_entry_name = entry_name_fetch(2)
|
|
||||||
if not Path(f"{directory}{_entry_name}.gpg").is_file() and not Path(f"{directory}{_entry_name}").is_dir():
|
|
||||||
print(f"\n\u001b[38;5;9merror: entry ({_entry_name}) does not exist\u001b[0m\n")
|
|
||||||
s_exit(1)
|
|
||||||
_new_name = entry_name_fetch('new name: ')
|
|
||||||
if Path(f"{directory}{_new_name}.gpg").is_file() or Path(f"{directory}{_new_name}").is_dir():
|
|
||||||
print(f"\n\u001b[38;5;9merror: ({_new_name}) already exists\u001b[0m\n")
|
|
||||||
s_exit(1)
|
|
||||||
if _entry_name.endswith('/'):
|
|
||||||
move(f"{directory}{_entry_name}", f"{directory}{_new_name}")
|
|
||||||
system(f"ssh -i '{path.expanduser('~/.ssh/sshyp')}' -p {port} {username_ssh}@{ip} \"mkdir -p '{directory_ssh}"
|
|
||||||
f"{_new_name}'\"")
|
|
||||||
else:
|
|
||||||
move(f"{directory}{_entry_name}.gpg", f"{directory}{_new_name}.gpg")
|
|
||||||
system(f"ssh -i '{path.expanduser('~/.ssh/sshyp')}' -p {port} {username_ssh}@{ip} \"cd /bin; python -c "
|
|
||||||
f"'import sshypRemote; sshypRemote.delete(\"'\"{_entry_name}\"'\")'\"")
|
|
||||||
|
|
||||||
|
|
||||||
def edit(): # edits the contents of an entry
|
|
||||||
_shm_folder, _shm_entry, _detail, _edit_line = None, None, None, None # sets values to avoid PEP8 warnings
|
|
||||||
if len(argument_list) < 4:
|
|
||||||
_entry_name = entry_name_fetch('entry to edit: ')
|
|
||||||
else:
|
|
||||||
_entry_name = entry_name_fetch(2)
|
|
||||||
if not Path(f"{directory}{_entry_name}.gpg").is_file():
|
|
||||||
print(f"\n\u001b[38;5;9merror: entry ({_entry_name}) does not exist\u001b[0m\n")
|
|
||||||
s_exit(1)
|
|
||||||
_shm_folder, _shm_entry = shm_gen()
|
|
||||||
decrypt(directory + _entry_name, _shm_folder, _shm_entry, gpg)
|
|
||||||
if argument_list[2] == 'username' or argument_list[2] == '-u':
|
|
||||||
_detail, _edit_line = str(input('username: ')), 1
|
|
||||||
elif argument_list[2] == 'password' or argument_list[2] == '-p':
|
|
||||||
_detail, _edit_line = str(input('password: ')), 0
|
|
||||||
elif argument_list[2] == 'url' or argument_list[2] == '-l':
|
|
||||||
_detail, _edit_line = str(input('url: ')), 2
|
|
||||||
if argument_list[2] == 'note' or argument_list[2] == '-n':
|
|
||||||
_edit_line = 2
|
|
||||||
_new_lines = optimized_edit(edit_note(_shm_folder, _shm_entry,
|
|
||||||
open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'r').readlines()), None, -1)
|
|
||||||
else:
|
|
||||||
_new_lines = optimized_edit(open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'r').readlines(), _detail, _edit_line)
|
|
||||||
open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'w').writelines(_new_lines)
|
|
||||||
remove(f"{directory}{_entry_name}.gpg")
|
|
||||||
print('\n\u001b[1mentry preview:\u001b[0m')
|
|
||||||
entry_reader(f"{tmp_dir}{_shm_folder}/{_shm_entry}")
|
|
||||||
encrypt(directory + _entry_name, _shm_folder, _shm_entry, gpg, gpg_id)
|
|
||||||
|
|
||||||
|
|
||||||
def gen(): # generates a password for a new or an existing entry
|
|
||||||
_username, _url, _notes = None, None, None # sets base-line values to avoid errors
|
|
||||||
if argument == 'gen update' or argument == 'gen -u' or argument == 'gen':
|
|
||||||
_entry_name = entry_name_fetch('name of entry: ')
|
|
||||||
elif argument_list[2] == 'update' or argument_list[2] == '-u':
|
|
||||||
_entry_name = entry_name_fetch(2)
|
|
||||||
if not Path(f"{directory}{_entry_name}.gpg").is_file():
|
|
||||||
print(f"\n\u001b[38;5;9merror: entry ({_entry_name}) does not exist\u001b[0m\n")
|
|
||||||
s_exit(1)
|
|
||||||
else:
|
|
||||||
_entry_name = entry_name_fetch(1)
|
|
||||||
if len(argument_list) == 2 or (not argument_list[2] == 'update' and not argument_list[2] == '-u'):
|
|
||||||
if Path(f"{directory}{_entry_name}.gpg").is_file():
|
|
||||||
print(f"\n\u001b[38;5;9merror: entry ({_entry_name}) already exists\u001b[0m\n")
|
|
||||||
s_exit(1)
|
|
||||||
_username = str(input('username: '))
|
|
||||||
_password = pass_gen()
|
|
||||||
_url = str(input('url: '))
|
|
||||||
_add_note = input('add a note to this entry? (y/N) ')
|
|
||||||
_shm_folder, _shm_entry = shm_gen()
|
|
||||||
if _add_note.lower() == 'y':
|
|
||||||
system(f"{editor} {tmp_dir}{_shm_folder}/{_shm_entry}-n")
|
|
||||||
_notes = open(f"{tmp_dir}{_shm_folder}/{_shm_entry}-n", 'r').read()
|
|
||||||
else:
|
|
||||||
_notes = ''
|
|
||||||
open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'w')\
|
|
||||||
.writelines(optimized_edit([_password, _username, _url, _notes], None, -1))
|
|
||||||
else:
|
|
||||||
_shm_folder, _shm_entry = shm_gen()
|
|
||||||
decrypt(directory + _entry_name, _shm_folder, _shm_entry, gpg)
|
|
||||||
_new_lines = optimized_edit(open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'r').readlines(), pass_gen(), 0)
|
|
||||||
open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'w').writelines(_new_lines)
|
|
||||||
remove(f"{directory}{_entry_name}.gpg")
|
|
||||||
print('\n\u001b[1mentry preview:\u001b[0m')
|
|
||||||
entry_reader(f"{tmp_dir}{_shm_folder}/{_shm_entry}")
|
|
||||||
encrypt(directory + _entry_name, _shm_folder, _shm_entry, gpg, gpg_id)
|
|
||||||
|
|
||||||
|
|
||||||
def copy_data(): # copies a specified field of an entry to the clipboard
|
|
||||||
if len(argument_list) < 4:
|
|
||||||
_entry_name = entry_name_fetch('entry to copy: ')
|
|
||||||
else:
|
|
||||||
_entry_name = entry_name_fetch(2)
|
|
||||||
if not Path(f"{directory}{_entry_name}.gpg").is_file():
|
|
||||||
print(f"\n\u001b[38;5;9merror: entry ({_entry_name}) does not exist\u001b[0m\n")
|
|
||||||
s_exit(1)
|
|
||||||
_shm_folder, _shm_entry = shm_gen()
|
|
||||||
decrypt(directory + _entry_name, _shm_folder, _shm_entry, gpg)
|
|
||||||
_copy_line = open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'r').readlines()
|
|
||||||
if uname()[0] == 'Haiku': # Haiku clipboard detection
|
|
||||||
if argument_list[2] == 'username' or argument_list[2] == '-u':
|
|
||||||
system('clipboard -c ' + "'" + _copy_line[1].replace('\n', '').replace("'", "'\\''") + "'")
|
|
||||||
elif argument_list[2] == 'password' or argument_list[2] == '-p':
|
|
||||||
system('clipboard -c ' + "'" + _copy_line[0].replace('\n', '').replace("'", "'\\''") + "'")
|
|
||||||
elif argument_list[2] == 'url' or argument_list[2] == '-l':
|
|
||||||
system('clipboard -c ' + "'" + _copy_line[2].replace('\n', '').replace("'", "'\\''") + "'")
|
|
||||||
elif argument_list[2] == 'note' or argument_list[2] == '-n':
|
|
||||||
system('clipboard -c ' + "'" + _copy_line[3].replace('\n', '').replace("'", "'\\''") + "'")
|
|
||||||
Popen('sleep 30; clipboard -r', shell=True, close_fds=True)
|
|
||||||
elif Path("/data/data/com.termux").exists(): # Termux (Android) clipboard detection
|
|
||||||
if argument_list[2] == 'username' or argument_list[2] == '-u':
|
|
||||||
system('termux-clipboard-set ' + "'" + _copy_line[1].replace('\n', '').replace("'", "'\\''") + "'")
|
|
||||||
elif argument_list[2] == 'password' or argument_list[2] == '-p':
|
|
||||||
system('termux-clipboard-set ' + "'" + _copy_line[0].replace('\n', '').replace("'", "'\\''") + "'")
|
|
||||||
elif argument_list[2] == 'url' or argument_list[2] == '-l':
|
|
||||||
system('termux-clipboard-set ' + "'" + _copy_line[2].replace('\n', '').replace("'", "'\\''") + "'")
|
|
||||||
elif argument_list[2] == 'note' or argument_list[2] == '-n':
|
|
||||||
system('termux-clipboard-set ' + "'" + _copy_line[3].replace('\n', '').replace("'", "'\\''") + "'")
|
|
||||||
Popen("sleep 30; termux-clipboard-set ''", shell=True, close_fds=True)
|
|
||||||
elif environ.get('WAYLAND_DISPLAY') == 'wayland-0': # Wayland clipboard detection
|
|
||||||
if argument_list[2] == 'username' or argument_list[2] == '-u':
|
|
||||||
system('wl-copy ' + "'" + _copy_line[1].replace('\n', '').replace("'", "'\\''") + "'")
|
|
||||||
elif argument_list[2] == 'password' or argument_list[2] == '-p':
|
|
||||||
system('wl-copy ' + "'" + _copy_line[0].replace('\n', '').replace("'", "'\\''") + "'")
|
|
||||||
elif argument_list[2] == 'url' or argument_list[2] == '-l':
|
|
||||||
system('wl-copy ' + "'" + _copy_line[2].replace('\n', '').replace("'", "'\\''") + "'")
|
|
||||||
elif argument_list[2] == 'note' or argument_list[2] == '-n':
|
|
||||||
system('wl-copy ' + "'" + _copy_line[3].replace('\n', '').replace("'", "'\\''") + "'")
|
|
||||||
Popen('sleep 30; wl-copy -c', shell=True, close_fds=True)
|
|
||||||
else: # X11 clipboard detection
|
|
||||||
if argument_list[2] == 'username' or argument_list[2] == '-u':
|
|
||||||
system('echo -n ' + "'" + _copy_line[1].replace('\n', '').replace("'", "'\\''") + "'" + ' | xclip -sel c')
|
|
||||||
elif argument_list[2] == 'password' or argument_list[2] == '-p':
|
|
||||||
system('echo -n ' + "'" + _copy_line[0].replace('\n', '').replace("'", "'\\''") + "'" + ' | xclip -sel c')
|
|
||||||
elif argument_list[2] == 'url' or argument_list[2] == '-l':
|
|
||||||
system('echo -n ' + "'" + _copy_line[2].replace('\n', '').replace("'", "'\\''") + "'" + ' | xclip -sel c')
|
|
||||||
elif argument_list[2] == 'note' or argument_list[2] == '-n':
|
|
||||||
system('echo -n ' + "'" + _copy_line[3].replace('\n', '').replace("'", "'\\''") + "'" + ' | xclip -sel c')
|
|
||||||
Popen("sleep 30; echo -n '' | xclip -sel c", shell=True, close_fds=True)
|
|
||||||
rmtree(f"{tmp_dir}{_shm_folder}")
|
|
||||||
|
|
||||||
|
|
||||||
def remove_data(): # deletes an entry from the server and flags it for local deletion on sync
|
|
||||||
if argument == 'shear' or argument == '-rm':
|
|
||||||
_entry_name = entry_name_fetch('entry/folder to shear: ')
|
|
||||||
else:
|
|
||||||
_entry_name = entry_name_fetch(1)
|
|
||||||
decrypt(path.expanduser('~/.config/sshyp/lock.gpg'), 0, 0, gpg)
|
|
||||||
system(f"ssh -i '{path.expanduser('~/.ssh/sshyp')}' -p {port} {username_ssh}@{ip} \"cd /bin; python -c "
|
|
||||||
f"'import sshypRemote; sshypRemote.delete(\"'\"{_entry_name}\"'\")'\"")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
try:
|
|
||||||
silent_sync, ssh_error = 0, 0
|
|
||||||
# retrieve typed argument
|
|
||||||
argument_list = argv
|
|
||||||
argument = ' '.join(argument_list[1:])
|
|
||||||
if uname()[0] == 'Haiku': # set proper gpg command for OS
|
|
||||||
gpg = 'gpg --pinentry-mode loopback'
|
|
||||||
else:
|
|
||||||
gpg = 'gpg'
|
|
||||||
|
|
||||||
# import saved userdata
|
|
||||||
if argument != 'tweak':
|
|
||||||
device_type = ''
|
|
||||||
tmp_dir = path.expanduser('~/.config/sshyp/tmp/')
|
|
||||||
try:
|
|
||||||
device_type = open(path.expanduser('~/.config/sshyp/sshyp-device')).read().strip()
|
|
||||||
if device_type == 'c':
|
|
||||||
ssh_info = sshync.get_profile(path.expanduser('~/.config/sshyp/sshyp.sshync'))
|
|
||||||
username_ssh = ssh_info[0].replace('\n', '')
|
|
||||||
ip = ssh_info[1].replace('\n', '')
|
|
||||||
port = ssh_info[2].replace('\n', '')
|
|
||||||
directory = str(ssh_info[3].replace('\n', ''))
|
|
||||||
directory_ssh = str(ssh_info[4].replace('\n', ''))
|
|
||||||
client_device_name = listdir(path.expanduser('~/.config/sshyp/devices'))[0]
|
|
||||||
sshyp_data = open(path.expanduser('~/.config/sshyp/sshyp-data')).readlines()
|
|
||||||
gpg_id = sshyp_data[0].replace('\n', '')
|
|
||||||
editor = sshyp_data[1].replace('\n', '')
|
|
||||||
ssh_error = int(open(path.expanduser('~/.config/sshyp/ssh-error')).read().strip())
|
|
||||||
if ssh_error != 0:
|
|
||||||
ssh_error = copy_name_check(port, username_ssh, ip, client_device_name)
|
|
||||||
else:
|
|
||||||
print('running as server, option disabled')
|
|
||||||
s_exit(0)
|
|
||||||
except (FileNotFoundError, IndexError):
|
|
||||||
if device_type.lower() != 's':
|
|
||||||
print('\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
|
|
||||||
print("not all necessary configuration files are present - please run 'sshyp tweak'")
|
|
||||||
print('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n')
|
|
||||||
s_exit(1)
|
|
||||||
else:
|
|
||||||
tweak()
|
|
||||||
s_exit(0)
|
|
||||||
|
|
||||||
# run function based on arguments
|
|
||||||
if argument.startswith('/'):
|
|
||||||
read_shortcut()
|
|
||||||
elif argument == '':
|
|
||||||
no_arg()
|
|
||||||
elif argument == 'help' or argument == '--help' or argument == '-h' or argument == 'license' or argument \
|
|
||||||
== 'version' or argument == '-v':
|
|
||||||
print_info()
|
|
||||||
elif argument_list[1] == 'add':
|
|
||||||
if len(argument_list) == 2:
|
|
||||||
print_info()
|
|
||||||
elif argument_list[2] == 'note' or argument_list[2] == '-n' or argument_list[2] == 'password' or \
|
|
||||||
argument_list[2] == '-p':
|
|
||||||
add_entry()
|
|
||||||
elif argument_list[2] == 'folder' or argument_list[2] == '-f':
|
|
||||||
add_folder()
|
|
||||||
else:
|
|
||||||
print_info()
|
|
||||||
s_exit(0)
|
|
||||||
elif argument_list[1] == 'edit':
|
|
||||||
if len(argument_list) == 2:
|
|
||||||
print_info()
|
|
||||||
elif argument_list[2] == 'rename' or argument_list[2] == 'relocate' or argument_list[2] == '-r':
|
|
||||||
silent_sync = 1
|
|
||||||
rename()
|
|
||||||
elif argument_list[2] == 'username' or argument_list[2] == '-u' or argument_list[2] == 'password' or \
|
|
||||||
argument_list[2] == '-p' or argument_list[2] == 'url' or argument_list[2] == '-l' or \
|
|
||||||
argument_list[2] == 'note' or argument_list[2] == '-n':
|
|
||||||
edit()
|
|
||||||
else:
|
|
||||||
print_info()
|
|
||||||
s_exit(0)
|
|
||||||
elif argument_list[1] == 'gen':
|
|
||||||
gen()
|
|
||||||
elif argument_list[1] == 'copy':
|
|
||||||
if len(argument_list) == 2:
|
|
||||||
print_info()
|
|
||||||
elif argument_list[2] == 'username' or argument_list[2] == '-u' or argument_list[2] == 'password' or \
|
|
||||||
argument_list[2] == '-p' or argument_list[2] == 'url' or argument_list[2] == '-l' or \
|
|
||||||
argument_list[2] == 'note' or argument_list[2] == '-n':
|
|
||||||
try:
|
|
||||||
copy_data()
|
|
||||||
except IndexError:
|
|
||||||
print(f"\n\u001b[38;5;9merror: field does not exist in entry\u001b[0m\n")
|
|
||||||
s_exit(1)
|
|
||||||
else:
|
|
||||||
print_info()
|
|
||||||
elif argument_list[1] == 'shear' or argument_list[1] == '-rm':
|
|
||||||
remove_data()
|
|
||||||
elif argument_list[1] != 'sync' and argument_list[1] != '-s':
|
|
||||||
print(f"\n\u001b[38;5;9merror: invalid argument - run 'sshyp help' to list usable commands\u001b[0m\n")
|
|
||||||
s_exit(1)
|
|
||||||
|
|
||||||
# sync if any changes were made
|
|
||||||
if len(argument_list) > 1 and ssh_error == 0 and \
|
|
||||||
((argument_list[1] == 'sync' or argument_list[1] == '-s' or argument_list[1] == 'gen' or
|
|
||||||
argument_list[1] == 'shear' or argument_list[1] == '-rm') or ((argument_list[1] == 'add'
|
|
||||||
or argument_list[1] == 'edit')
|
|
||||||
and len(argument_list) > 2)):
|
|
||||||
sync()
|
|
||||||
|
|
||||||
s_exit(0)
|
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
print('\n')
|
|
||||||
s_exit(0)
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
# external modules
|
|
||||||
|
|
||||||
from os import listdir, remove, walk
|
|
||||||
from os.path import expanduser
|
|
||||||
from shutil import rmtree
|
|
||||||
|
|
||||||
|
|
||||||
# utility functions
|
|
||||||
|
|
||||||
def delete(_file_path): # deletes an entry or folder, should be run remotely via ssh
|
|
||||||
try:
|
|
||||||
if _file_path.endswith('/'):
|
|
||||||
rmtree(f"{expanduser('~/.local/share/sshyp/')}{_file_path}")
|
|
||||||
else:
|
|
||||||
remove(f"{expanduser('~/.local/share/sshyp/')}{_file_path}.gpg")
|
|
||||||
except FileNotFoundError:
|
|
||||||
print('location does not exist remotely')
|
|
||||||
for _device_name in listdir(expanduser('~/.config/sshyp/devices')):
|
|
||||||
open(f"{expanduser('~/.config/sshyp/deleted/')}{_file_path.replace('/', '@')}^&*{_device_name}", 'w')
|
|
||||||
|
|
||||||
|
|
||||||
def deletion_check(_client_device_name): # creates a list of entries and folders to be deleted from a local machine
|
|
||||||
open(expanduser('~/.config/sshyp/deletion_database'), 'w').write('')
|
|
||||||
for _file in listdir(expanduser('~/.config/sshyp/deleted')):
|
|
||||||
_file_path = _file.replace('@', '/').split('^&*')[0]
|
|
||||||
_device = _file.split('^&*')[1]
|
|
||||||
if _device == _client_device_name:
|
|
||||||
try:
|
|
||||||
remove(f"{expanduser('~/.config/sshyp/deleted/')}{_file}")
|
|
||||||
except FileNotFoundError:
|
|
||||||
pass
|
|
||||||
open(expanduser('~/.config/sshyp/deletion_database'), 'a').write(_file_path + '\n')
|
|
||||||
|
|
||||||
|
|
||||||
def folder_check(): # creates a list of folders to be compared with those of a local machine
|
|
||||||
open(expanduser('~/.config/sshyp/folder_database'), 'w').write('')
|
|
||||||
for _root, _directories, _files in walk(expanduser('~/.local/share/sshyp')):
|
|
||||||
for _dir in _directories:
|
|
||||||
open(expanduser('~/.config/sshyp/folder_database'), 'a')\
|
|
||||||
.write(f"{_root.replace(expanduser('~'), '')}/{_dir}\n")
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
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
|
||||||
@@ -0,0 +1,460 @@
|
|||||||
|
sshyp 2021.12.01.fr2.2
|
||||||
|
|
||||||
|
The Lighter Update - Patch 2
|
||||||
|
|
||||||
|
This release is a hotfix for 2021.12.01.fr2.1.
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
|
||||||
|
- a major fix for "/var/lib/sshync_database" needing to already exist server-side
|
||||||
|
- a major fix for syncing functionality (it should actually work now)
|
||||||
|
- silenced some sftp output
|
||||||
|
|
||||||
|
An fr2.3 patch is planned for this version. It will include the following:
|
||||||
|
|
||||||
|
- fixes for any new bugs that may be discovered in the new sshyp and sshync code
|
||||||
|
- the gen command in sshyp will be updated to use the newer notetaking system
|
||||||
|
- printing functions in sshyp will be combined into one function (arguments will determine section to print)
|
||||||
|
- file not found exceptions will be re-implemented
|
||||||
|
- when syncing, a check will be made to see if any folders are not on all devices and this will be corrected
|
||||||
|
|
||||||
|
Planned for next major update (The Fluffier Update):
|
||||||
|
|
||||||
|
- new visual features
|
||||||
|
^ this includes the new file list and thematic text art. sshyp will be
|
||||||
|
getting a retro computing theme.
|
||||||
|
- packages for more environments
|
||||||
|
^ "sshyp" will be officially packaged for Arch Linux, Alpine Linux, and Termux (Android).
|
||||||
|
Debian/Ubuntu and Fedora packaging will come later.
|
||||||
|
- imporove password generator to guarantee more secure results
|
||||||
|
|
||||||
|
Planned for next, next major update (The Shears Update Pt. 1):
|
||||||
|
|
||||||
|
- enforce permissions on the server-side
|
||||||
|
- allow for copying entire notes (not just first line)
|
||||||
|
- allow for multi-client deletion... somehow
|
||||||
|
- detatch sshync and make it a separate package
|
||||||
|
- manpage
|
||||||
|
|
||||||
|
Planned for next, next, next major update (The Shears Update Pt. 2):
|
||||||
|
|
||||||
|
- ability to pass entries as arguments for more functions
|
||||||
|
- auto-completion on tab (currently unsure of best way to make this work)
|
||||||
|
|
||||||
|
Backlog:
|
||||||
|
|
||||||
|
- package for more Linux distributions, such as Debian and Fedora
|
||||||
|
- create separate gui frontend targetting mobile and desktop Linux
|
||||||
|
|
||||||
|
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
||||||
|
|
||||||
|
sshyp 2021.12.01.fr2.1
|
||||||
|
|
||||||
|
The Lighter Update - Patch 1
|
||||||
|
|
||||||
|
This release is a hotfix for 2021.12.01.fr2.
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
|
||||||
|
- fixed a bug where "sshyp tweak" would not function on new installations
|
||||||
|
- moved the argument parser to the global process
|
||||||
|
- running "sshyp version" or "sshyp -v" no longer triggers syncing
|
||||||
|
- folders are now created natively with Python, rather than through system commands
|
||||||
|
|
||||||
|
An fr2.2 patch is planned for this version. It will include the following:
|
||||||
|
|
||||||
|
- fixes for any new bugs that may be discovered in the new sshyp and sshync code
|
||||||
|
- the gen command in sshyp will be updated to use the newer notetaking system
|
||||||
|
- printing functions in sshyp will be combined into one function (arguments will determine section to print)
|
||||||
|
- file not found exceptions will be re-implemented
|
||||||
|
|
||||||
|
Planned for next major update (The Fluffier Update):
|
||||||
|
|
||||||
|
- new visual features
|
||||||
|
^ this includes the new file list and thematic text art. sshyp will be
|
||||||
|
getting a retro computing theme.
|
||||||
|
- packages for more environments
|
||||||
|
^ "sshyp" will be officially packaged for Arch Linux, Alpine Linux, and Termux (Android).
|
||||||
|
Debian/Ubuntu and Fedora packaging will come later.
|
||||||
|
- imporove password generator to guarantee more secure results
|
||||||
|
|
||||||
|
Planned for next, next major update (The Shears Update Pt. 1):
|
||||||
|
|
||||||
|
- enforce permissions on the server-side
|
||||||
|
- allow for copying entire notes (not just first line)
|
||||||
|
- allow for multi-client deletion... somehow
|
||||||
|
- detatch sshync and make it a separate package
|
||||||
|
- manpage
|
||||||
|
|
||||||
|
Planned for next, next, next major update (The Shears Update Pt. 2):
|
||||||
|
|
||||||
|
- ability to pass entries as arguments for more functions
|
||||||
|
- auto-completion on tab (currently unsure of best way to make this work)
|
||||||
|
|
||||||
|
Backlog:
|
||||||
|
|
||||||
|
- package for more Linux distributions, such as Debian and Fedora
|
||||||
|
- create separate gui frontend targetting mobile and desktop Linux
|
||||||
|
|
||||||
|
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
||||||
|
|
||||||
|
sshyp 2021.12.01.fr2
|
||||||
|
|
||||||
|
The Lighter Update
|
||||||
|
|
||||||
|
This release features a major re-write of old "rpass" code.
|
||||||
|
sshyp is now more reliable and better documented.
|
||||||
|
|
||||||
|
New features:
|
||||||
|
|
||||||
|
- a full re-write of the old "rpass" code used in the last release
|
||||||
|
^ includes more reliable argument parsing and various optimizations
|
||||||
|
- a full re-write of "sshync", many issues fixed
|
||||||
|
^fixed many instances where sshync would refuse to upload/download
|
||||||
|
^fixed errors that were being thrown despite sshync working as intended
|
||||||
|
- new folders are now also created on the server
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
|
||||||
|
- nested folders actually work now
|
||||||
|
- multi-word entry and folder titles are fixed
|
||||||
|
|
||||||
|
An fr2.1 patch is planned for this version. It will include the following:
|
||||||
|
|
||||||
|
- fixes for any new bugs that may be discovered in the new sshyp and sshync code
|
||||||
|
- the gen command in sshyp will be updated to use the newer notetaking system
|
||||||
|
- printing functions in sshyp will be combined into one function (arguments will determine section to print)
|
||||||
|
- file not found exceptions will be re-implemented
|
||||||
|
- the argument parser will be moved to the global process (no need for it to be in a function)
|
||||||
|
- folders will be created natively in python
|
||||||
|
|
||||||
|
Planned for next major update (The Fluffier Update):
|
||||||
|
|
||||||
|
- new visual features
|
||||||
|
^ this includes the new file list and thematic text art. sshyp will be
|
||||||
|
getting a retro computing theme.
|
||||||
|
- packages for more environments
|
||||||
|
^ "sshyp" will be officially packaged for Arch Linux, Alpine Linux, and Termux (Android).
|
||||||
|
Debian/Ubuntu and Fedora packaging will come later.
|
||||||
|
- imporove password generator to guarantee more secure results
|
||||||
|
|
||||||
|
Planned for next, next major update (The Shears Update Pt. 1):
|
||||||
|
|
||||||
|
- allow for copying entire notes (not just first line)
|
||||||
|
- allow for multi-client deletion... somehow
|
||||||
|
- detatch sshync and make it a separate package
|
||||||
|
- manpage
|
||||||
|
|
||||||
|
Planned for next, next, next major update (The Shears Update Pt. 2):
|
||||||
|
|
||||||
|
- ability to pass entries as arguments for more functions
|
||||||
|
- auto-completion on tab (currently unsure of best way to make this work)
|
||||||
|
|
||||||
|
Backlog:
|
||||||
|
|
||||||
|
- package for more Linux distributions, such as Debian and Fedora
|
||||||
|
- create separate gui frontend targetting mobile and desktop Linux
|
||||||
|
|
||||||
|
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
||||||
|
|
||||||
|
sshyp 2021.11.05.fr1
|
||||||
|
|
||||||
|
First public release of sshyp.
|
||||||
|
|
||||||
|
This release fixes major bugs from rpass and rebrands the project.
|
||||||
|
|
||||||
|
New features:
|
||||||
|
|
||||||
|
- an entirely new syncing back-end called sshync (custom Python wrapper around sftp)
|
||||||
|
^ in thoery, all syncing-related data loss should now be fixed, however, this new
|
||||||
|
system has just been created and is still under testing. Maybe wait for a few patches
|
||||||
|
before trusting this system.
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
|
||||||
|
- branding has been updated to reflect new project name and goals
|
||||||
|
- custom directories, custom ssh keys, and offline only mode are removed (may be temporary)
|
||||||
|
^ the configuration wizard is being re-written in the next major update
|
||||||
|
|
||||||
|
Planned for next update (The Fluffy Update):
|
||||||
|
|
||||||
|
- modularized and optimized code
|
||||||
|
^ some older, bad code from "rpass" will be re-written.
|
||||||
|
- new visual features
|
||||||
|
^ this includes the new file list and thematic text art. sshyp will be
|
||||||
|
getting a retro computing theme.
|
||||||
|
- packages for more environments
|
||||||
|
^ "sshyp" will be officially packaged for Arch Linux, Alpine Linux, and Termux (Android).
|
||||||
|
Debian/Ubuntu and Fedora packaging will come later.
|
||||||
|
|
||||||
|
Planned for next update (The Sheep w/Shears Update):
|
||||||
|
|
||||||
|
- allow for copying entire notes (not just first line)
|
||||||
|
- allow for multi-client deletion... somehow
|
||||||
|
- ability to pass entries as arguments for more functions
|
||||||
|
- detatch sshync and make it a separate package
|
||||||
|
- manpage
|
||||||
|
|
||||||
|
Planned, no timeline:
|
||||||
|
|
||||||
|
- create separate gui frontend targetting mobile devices (Android + Linux phones)
|
||||||
|
^ the gui will also function on desktop linux
|
||||||
|
- package for more Linux distributions, such as Debian and Fedora)
|
||||||
|
- auto-completion on tab (currently unsure of best way to make this work)
|
||||||
|
|
||||||
|
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
||||||
|
|
||||||
|
rpass 2021.11.01.mr5.1
|
||||||
|
|
||||||
|
The Notetaker Update - Patch 1
|
||||||
|
|
||||||
|
This is the FINAL release of "rpass".
|
||||||
|
After this release, the project will be rebranded as "sshyp".
|
||||||
|
|
||||||
|
rpass's syncing system is fundamentally broken, hence the need for "sshyp"
|
||||||
|
and its new syncing system. I would highly advise against using this release.
|
||||||
|
|
||||||
|
New features:
|
||||||
|
|
||||||
|
- none
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
|
||||||
|
- fixed an issue with copying notes (currently only copies first line, will be changed in the future)
|
||||||
|
- rpass is validated to work with Python 3.10 (no changes were needed)
|
||||||
|
|
||||||
|
Planned for next update (sshyp fr1)
|
||||||
|
|
||||||
|
- full project rebrand ("sshyp")
|
||||||
|
- new syncing back-end
|
||||||
|
|
||||||
|
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
||||||
|
|
||||||
|
rpass 2021.10.07.mr5
|
||||||
|
|
||||||
|
The Notetaker Update - Fifth main release of rpass
|
||||||
|
|
||||||
|
This release overhauls the note-taking system and provides general improvements
|
||||||
|
to overall code quality, including optimizations, bug fixes, and security improvements.
|
||||||
|
|
||||||
|
New features:
|
||||||
|
|
||||||
|
- notes are now edited in nano and can be split across multiple lines
|
||||||
|
- password confirmation has been added for entry deletion
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
|
||||||
|
- fixed issues with copying passwords with certain special characters, e.g. '"()|
|
||||||
|
- auto-generated ssh key now uses ed25519, rather than rsa-3072
|
||||||
|
- entry format modified to include a blank line before the notes field (old format still mostly compatible)
|
||||||
|
- rpass is now much better at catching bad arguments
|
||||||
|
- some general code cleanup
|
||||||
|
|
||||||
|
Planned for next update (The Housekeeping Update Pt. 2):
|
||||||
|
|
||||||
|
- add new file list w/color and w/o extensions + proper displaying of nested folders
|
||||||
|
- ability to pass entries as arguments for more functions
|
||||||
|
- manpage
|
||||||
|
- add more comments to code
|
||||||
|
- validate for Python 3.10
|
||||||
|
|
||||||
|
Planned for next update (The cryptpass Update):
|
||||||
|
|
||||||
|
- initial implementation of a data store for other applications (libsecret alt.)
|
||||||
|
- modularize code in preparation for GUI frontends
|
||||||
|
|
||||||
|
Planned, no timeline:
|
||||||
|
|
||||||
|
- create separate gui frontend targetting mobile devices (first Linux phones, maybe Android at a later date - official iOS client not likely)
|
||||||
|
^ the gui will also function on desktop linux
|
||||||
|
- make platform agnostic (add Windows support and package for more Linux distributions, such as Alpine and Debian)
|
||||||
|
- auto-completion on tab (currently unsure of best way to make this work)
|
||||||
|
|
||||||
|
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
||||||
|
|
||||||
|
rpass 2021.09.15.mr4.2
|
||||||
|
|
||||||
|
The Housekeeping Update (Pt. 1) - Hotfix 2
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
|
||||||
|
- sync support is fixed
|
||||||
|
- files with spaces in their names can now be properly deleted from remote servers
|
||||||
|
|
||||||
|
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
||||||
|
|
||||||
|
rpass 2021.09.15.mr4.1
|
||||||
|
|
||||||
|
The Housekeeping Update (Pt. 1) - Hotfix
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
|
||||||
|
- the user is once again properly presented with a prompt to enter a gpg key when pressing enter through the config dialog
|
||||||
|
|
||||||
|
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
||||||
|
|
||||||
|
rpass-2021.09.15.mr4
|
||||||
|
|
||||||
|
The Housekeeping Update (Pt. 1) - Fourth main release of rpass
|
||||||
|
|
||||||
|
This release focuses primarily on polishing rpass and tying up loose ends before
|
||||||
|
making any major changes in the future.
|
||||||
|
|
||||||
|
New features:
|
||||||
|
|
||||||
|
- added clipboard support for copying entry information
|
||||||
|
- many common errors are now handled with proper exceptions
|
||||||
|
- offline support (sync no longer needs to be configured for rpass to function)
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
|
||||||
|
- fixed gpg decryption warnings
|
||||||
|
- added version info argument
|
||||||
|
- re-ordered config wizard to make more sense
|
||||||
|
- entry deletion can now be done client-side
|
||||||
|
- removed rsyncup/rsyncdown/username/ip/port/directoryssh files,
|
||||||
|
^ replaced with one file that holds usernamessh, ip, port, directoryssh, and devicetype
|
||||||
|
- files should no longer be left in /dev/shm when an error is encountered
|
||||||
|
- fixed issues relating to using a non-default entry directory
|
||||||
|
- lots of optimizations have been made to reduce the size of rpass
|
||||||
|
|
||||||
|
Planned for next update (The Housekeeping Update Pt. 2):
|
||||||
|
|
||||||
|
- a clean, colorful entry list w/o file extensions
|
||||||
|
- manpage
|
||||||
|
- multi-line notes
|
||||||
|
- commented the code
|
||||||
|
- ability to pass entries as arguments for more functions
|
||||||
|
- improve exceptions for unknown arguments
|
||||||
|
|
||||||
|
Planned, no timeline:
|
||||||
|
|
||||||
|
- possible libsecret integration
|
||||||
|
- create separate gui frontend targetting mobile devices (first Linux phones, maybe Android at a later date - official iOS client not likely)
|
||||||
|
^ the gui will also function on desktop linux
|
||||||
|
- make platform agnostic (add Windows support and package for more Linux distributions)
|
||||||
|
- auto-completion on tab (currently unsure of best way to make this work)
|
||||||
|
|
||||||
|
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
||||||
|
|
||||||
|
rpass 2021.09.07.pr3.1
|
||||||
|
|
||||||
|
The Sync and Foundations Update - Hotfix
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
|
||||||
|
- gpg configuration now prompts for expiration, rather than defaulting to two years
|
||||||
|
- rsync is now configured to not delete anything from any remote device when uploading
|
||||||
|
^ this behavior may be modified in the future - this is a quick fix to prevent data loss
|
||||||
|
|
||||||
|
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
||||||
|
|
||||||
|
rpass 2021.09.07.pr3
|
||||||
|
|
||||||
|
The Sync and Foundations Update - Third public release of rpass.
|
||||||
|
|
||||||
|
As of this version, rpass has its intended base set of features and is considered usable.
|
||||||
|
It will now primarily recieve bug fixes and polish updates to prepare it to go
|
||||||
|
cross-platform and to make it compatible with GUI frontends.
|
||||||
|
|
||||||
|
New features:
|
||||||
|
|
||||||
|
- rsync support!
|
||||||
|
- new entry format with URL field (for future use)
|
||||||
|
- the temporary directory (/dev/shm) for editing entries has been somewhat secured
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
|
||||||
|
- 'rpass edit relocate' is now properly bound
|
||||||
|
- dropped the use of 'echo'
|
||||||
|
- added 'rsync' and 'openssh' as dependencies
|
||||||
|
- fixed a bug with deleting entries from folders
|
||||||
|
- entry directory now has more secure permissions
|
||||||
|
^ these permissions are re-enforced every time rpass is used
|
||||||
|
- fixed some small typos and potential bugs, made some small optimizations
|
||||||
|
|
||||||
|
Planned for next update (The Housekeeping Update):
|
||||||
|
|
||||||
|
- move to github
|
||||||
|
- get rid of gpg decryption warnings
|
||||||
|
- proper error messages using 'except'
|
||||||
|
- add exceptions for unknown arguments
|
||||||
|
- cleanup file list by exporting to document and removing .gpg, adding color
|
||||||
|
- add a manpage
|
||||||
|
- allow notes to be multiple lines long (break lines after so many characters)
|
||||||
|
- validate for Python 3.10
|
||||||
|
- properly comment the code
|
||||||
|
|
||||||
|
Planned, no timeline:
|
||||||
|
|
||||||
|
- allow to function as alternative to gnome keyring (libsecret integration)
|
||||||
|
- create separate gui frontend targetting mobile devices (first linux phones, maybe android at a later date - official iOS client not likely)
|
||||||
|
^ the gui will also function on desktop linux
|
||||||
|
- make platform agnostic (add windows support and package for more Linux distributions)
|
||||||
|
- ability to pass entries as arguments for more functions
|
||||||
|
- auto-completion on tab (currently unsure of best way to make this work)
|
||||||
|
|
||||||
|
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
||||||
|
|
||||||
|
rpass 2021.09.02.pr2
|
||||||
|
|
||||||
|
The Folder Update - Second public release of rpass.
|
||||||
|
|
||||||
|
New features:
|
||||||
|
|
||||||
|
- entry and folder titles can now have spaces and be as long as you want
|
||||||
|
- entires and folders can now be deleted with 'rpass remove' or 'rpass -rm'
|
||||||
|
- entries now properly save to folders via 'rpass add' and should no longer throw errors
|
||||||
|
- entries saved in folders are now readable
|
||||||
|
- existing entries can now be moved to, from, and between folders
|
||||||
|
- previews are now shown after creating or editing an entry
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
|
||||||
|
- every pre-existing function of rpass has been updated to work with the new folder system
|
||||||
|
- dropped awk as a dependency
|
||||||
|
- removed 'argument' file in /var/lib/rpass
|
||||||
|
- reduced unneccessary characters in argument parsing to decrease compatibility issues
|
||||||
|
- ensured all user input is handled correctly as a string or an integer
|
||||||
|
- lots of miscellaneous optimizations
|
||||||
|
|
||||||
|
Expected for next update (The rsync Update):
|
||||||
|
|
||||||
|
- rsync (over ssh) support!
|
||||||
|
^ including any fixes, changes, or optimizations necessary to make that happen
|
||||||
|
- new file formatting with support for URLs
|
||||||
|
^all entries created in new format should be compatible with future versions of rpass
|
||||||
|
- secure /dev/shm with random text generator
|
||||||
|
|
||||||
|
Expected for the next (next) update (The Housekeeping Update)
|
||||||
|
|
||||||
|
- get rid of gpg decryption warnings
|
||||||
|
- proper error messages using 'except'
|
||||||
|
- add exceptions for unknown arguments
|
||||||
|
- cleanup file list by exporting to document and removing .gpg, adding color
|
||||||
|
|
||||||
|
Planned, no timeline:
|
||||||
|
|
||||||
|
- make platform agnostic (basically, add windows support)
|
||||||
|
- create separate gui frontend targetting mobile devices (first linux phones, android at a later date)
|
||||||
|
^ the gui will also function on desktop linux
|
||||||
|
- ability to pass entries as arguments for more functions
|
||||||
|
- auto-completion on tab
|
||||||
|
|
||||||
|
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
||||||
|
|
||||||
|
rpass 2021.09.01.pr1.1
|
||||||
|
|
||||||
|
First public release of rpass.
|
||||||
|
|
||||||
|
Early build, missing critical features (e.g. sync support and the ability to have spaces in filenames).
|
||||||
|
|
||||||
|
What is currently functional:
|
||||||
|
|
||||||
|
- guided configuration wizard
|
||||||
|
- generate passwords
|
||||||
|
- add existing passwords
|
||||||
|
- make notes
|
||||||
|
- sort notes and passwords into folders
|
||||||
|
- gpg encryption
|
||||||
|
- edit password/note entries
|
||||||
Executable → Regular
+104
-464
@@ -1,3 +1,106 @@
|
|||||||
|
sshyp v1.3.0
|
||||||
|
|
||||||
|
the serious shepherd update
|
||||||
|
|
||||||
|
this release ties up many of sshyp's loose ends where there was
|
||||||
|
room for major performance, compatibility, and security improvements
|
||||||
|
|
||||||
|
compatibility-breaking changes:
|
||||||
|
|
||||||
|
- due to a near full re-write of the syncing functionality, all clients and servers
|
||||||
|
must be updated to this release (v1.3.0 is not backwards compatible with any prior release)
|
||||||
|
- it is recommended to either delete the contents of ~/.config/sshyp/deleted (on the server-side)
|
||||||
|
or sync all of your clients before updating
|
||||||
|
^ old entries in this folder will throw errors with v1.3.0
|
||||||
|
|
||||||
|
user-facing features:
|
||||||
|
|
||||||
|
- none - all changes were under-the-hood - the user experience should be
|
||||||
|
exactly the same as v1.2.0 - just faster, less buggy, and more secure
|
||||||
|
|
||||||
|
major fixes/optimizations:
|
||||||
|
|
||||||
|
- a near full re-write of the syncing functionality
|
||||||
|
^ all syncing logic has been moved into sshync.py (from sshyp.py and sshypRemote.py)
|
||||||
|
^ in my setup, a dry, local "sshyp sync" went from 2.00+ seconds (v1.2.0) to 0.36 seconds (v1.3.0)
|
||||||
|
^ the performance improvements are even greater when syncing from outside your local network
|
||||||
|
- the following character sequences will no longer break the syncing logic: "@", "^&*", and "*&^"
|
||||||
|
^ ASCII separator characters 29-31 are now used, instead
|
||||||
|
- os.system has been replaced with subprocess.run in all cases, shell=True is no longer used with subprocess.run
|
||||||
|
^ this protects against shell escape attacks and potentially makes sshyp more compatible with some environments
|
||||||
|
- replaced shell commands with python built-in library functions where applicable
|
||||||
|
^ this brings speed and compatibility improvements
|
||||||
|
- sshyp should no longer incorrectly assume an X11 environment when Wayland is in use
|
||||||
|
^ this fixes clipboard support in some Wayland environments, such as Sway (Plasma/Gnome/Phosh were unaffected)
|
||||||
|
- sshyp now uses the default pinentry on Haiku thanks to haikuports/haikuports#7457
|
||||||
|
^ this brings the Haiku port in-line with the other sshyp packages in terms of security
|
||||||
|
- "python3" is now called over ssh, rather than "python"
|
||||||
|
^ some environments do not have a "python" symlink, or it links to "python2" - changing this increases compatibility
|
||||||
|
- fixed an issue from v1.2.0 where renaming threw an error if not in offline mode
|
||||||
|
|
||||||
|
other notable changes:
|
||||||
|
|
||||||
|
- quick-unlock password input is now hidden while the user is typing
|
||||||
|
^ user input is now invisible to prevent snooping
|
||||||
|
- lots of smaller optimizations not listed here
|
||||||
|
|
||||||
|
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
||||||
|
|
||||||
|
sshyp v1.2.0
|
||||||
|
|
||||||
|
the brisk bahh update
|
||||||
|
|
||||||
|
this release focuses on speeding up the sshyp user experience by adding
|
||||||
|
new features that reduce wasted time
|
||||||
|
|
||||||
|
compatibility-breaking changes:
|
||||||
|
|
||||||
|
- new configuration options (quick-unlock, offline mode) have been added and
|
||||||
|
the configuration files have been reorganized
|
||||||
|
^ simply running "sshyp tweak" and following the setup wizard will correct any compatibility
|
||||||
|
issues
|
||||||
|
- for quick-unlock security, device names have been replaced with more secure device ids
|
||||||
|
^ older device names are still compatible, but for security reasons it is recommended
|
||||||
|
to delete any pre-existing device names from the server and allow "sshyp tweak" to re-register
|
||||||
|
your devices
|
||||||
|
|
||||||
|
user-facing features:
|
||||||
|
|
||||||
|
- quick-unlock mode has been added
|
||||||
|
^ this allows you to use a shortened version of your password by verifying that your device
|
||||||
|
is whitelisted on your sshyp server - it's both faster and more secure than standard unlock,
|
||||||
|
but it requires an active connection to your sshyp server to authenticate (otherwise it will
|
||||||
|
fall back to standard unlock)
|
||||||
|
- full support for offline usage
|
||||||
|
^ though sshyp could be used without a server before, it now can be configured to not attempt
|
||||||
|
to find one ever - this saves time and hides sync failure error messages
|
||||||
|
- bash completions have been added
|
||||||
|
^ if you have bash-completion installed, you can now use the tab key in bash to auto-complete
|
||||||
|
sshyp arguments and entry names (client only, not added for server-specific arguments)
|
||||||
|
^ if you do not have bash-completion installed, you can source
|
||||||
|
/usr/share/bash-completion/completions/sshyp (Linux/BSD) or
|
||||||
|
/boot/system/data/bash-completion/completions/sshyp (Haiku) in your ~/.bashrc to
|
||||||
|
use this feature
|
||||||
|
|
||||||
|
fixes/optimizations:
|
||||||
|
|
||||||
|
- fixed entries with multi-word titles failing to decrypt
|
||||||
|
- password generation is now much faster and more resource efficient
|
||||||
|
- there is no longer a length limit on generated passwords
|
||||||
|
- improved visual consistency of help menus
|
||||||
|
- rarely used modules are now imported only when needed
|
||||||
|
- sshyp now uses one fewer configuration file
|
||||||
|
|
||||||
|
other notable changes:
|
||||||
|
|
||||||
|
- sshyp is now specifically licensed under the GPL-3.0-only (keyword: only)
|
||||||
|
- sshyp now has some possible arguments and its own help menu when running in server mode
|
||||||
|
- temporary files in /dev/shm are now generated with more complex names
|
||||||
|
- sshyp now installs in /usr/lib/sshyp (Linux/BSD) or /system/lib/sshyp (Haiku) instead of
|
||||||
|
/usr/bin or /bin (it is still symlinked to the old directories)
|
||||||
|
|
||||||
|
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
||||||
|
|
||||||
sshyp v1.1.2
|
sshyp v1.1.2
|
||||||
|
|
||||||
the sheecrets update - patch two
|
the sheecrets update - patch two
|
||||||
@@ -600,7 +703,7 @@ Changes:
|
|||||||
|
|
||||||
- sshyp now uses a symlink to the temporary storage directory, allowing for easy implementation of different temporary directories on different platforms
|
- sshyp now uses a symlink to the temporary storage directory, allowing for easy implementation of different temporary directories on different platforms
|
||||||
^ the first usage of this is enabling the use of temporary storage on Termux
|
^ the first usage of this is enabling the use of temporary storage on Termux
|
||||||
- sshync now properly auto-generates its config directory
|
- sshync now properly auto-generates its config directory
|
||||||
|
|
||||||
Additions planned for minor releases:
|
Additions planned for minor releases:
|
||||||
|
|
||||||
@@ -794,466 +897,3 @@ Backlog:
|
|||||||
|
|
||||||
- package for more Linux distributions, such as Debian and Fedora
|
- package for more Linux distributions, such as Debian and Fedora
|
||||||
- create separate gui frontend targetting mobile and desktop Linux
|
- create separate gui frontend targetting mobile and desktop Linux
|
||||||
|
|
||||||
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
|
||||||
|
|
||||||
sshyp 2021.12.01.fr2.2
|
|
||||||
|
|
||||||
The Lighter Update - Patch 2
|
|
||||||
|
|
||||||
This release is a hotfix for 2021.12.01.fr2.1.
|
|
||||||
|
|
||||||
Changes:
|
|
||||||
|
|
||||||
- a major fix for "/var/lib/sshync_database" needing to already exist server-side
|
|
||||||
- a major fix for syncing functionality (it should actually work now)
|
|
||||||
- silenced some sftp output
|
|
||||||
|
|
||||||
An fr2.3 patch is planned for this version. It will include the following:
|
|
||||||
|
|
||||||
- fixes for any new bugs that may be discovered in the new sshyp and sshync code
|
|
||||||
- the gen command in sshyp will be updated to use the newer notetaking system
|
|
||||||
- printing functions in sshyp will be combined into one function (arguments will determine section to print)
|
|
||||||
- file not found exceptions will be re-implemented
|
|
||||||
- when syncing, a check will be made to see if any folders are not on all devices and this will be corrected
|
|
||||||
|
|
||||||
Planned for next major update (The Fluffier Update):
|
|
||||||
|
|
||||||
- new visual features
|
|
||||||
^ this includes the new file list and thematic text art. sshyp will be
|
|
||||||
getting a retro computing theme.
|
|
||||||
- packages for more environments
|
|
||||||
^ "sshyp" will be officially packaged for Arch Linux, Alpine Linux, and Termux (Android).
|
|
||||||
Debian/Ubuntu and Fedora packaging will come later.
|
|
||||||
- imporove password generator to guarantee more secure results
|
|
||||||
|
|
||||||
Planned for next, next major update (The Shears Update Pt. 1):
|
|
||||||
|
|
||||||
- enforce permissions on the server-side
|
|
||||||
- allow for copying entire notes (not just first line)
|
|
||||||
- allow for multi-client deletion... somehow
|
|
||||||
- detatch sshync and make it a separate package
|
|
||||||
- manpage
|
|
||||||
|
|
||||||
Planned for next, next, next major update (The Shears Update Pt. 2):
|
|
||||||
|
|
||||||
- ability to pass entries as arguments for more functions
|
|
||||||
- auto-completion on tab (currently unsure of best way to make this work)
|
|
||||||
|
|
||||||
Backlog:
|
|
||||||
|
|
||||||
- package for more Linux distributions, such as Debian and Fedora
|
|
||||||
- create separate gui frontend targetting mobile and desktop Linux
|
|
||||||
|
|
||||||
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
|
||||||
|
|
||||||
sshyp 2021.12.01.fr2.1
|
|
||||||
|
|
||||||
The Lighter Update - Patch 1
|
|
||||||
|
|
||||||
This release is a hotfix for 2021.12.01.fr2.
|
|
||||||
|
|
||||||
Changes:
|
|
||||||
|
|
||||||
- fixed a bug where "sshyp tweak" would not function on new installations
|
|
||||||
- moved the argument parser to the global process
|
|
||||||
- running "sshyp version" or "sshyp -v" no longer triggers syncing
|
|
||||||
- folders are now created natively with Python, rather than through system commands
|
|
||||||
|
|
||||||
An fr2.2 patch is planned for this version. It will include the following:
|
|
||||||
|
|
||||||
- fixes for any new bugs that may be discovered in the new sshyp and sshync code
|
|
||||||
- the gen command in sshyp will be updated to use the newer notetaking system
|
|
||||||
- printing functions in sshyp will be combined into one function (arguments will determine section to print)
|
|
||||||
- file not found exceptions will be re-implemented
|
|
||||||
|
|
||||||
Planned for next major update (The Fluffier Update):
|
|
||||||
|
|
||||||
- new visual features
|
|
||||||
^ this includes the new file list and thematic text art. sshyp will be
|
|
||||||
getting a retro computing theme.
|
|
||||||
- packages for more environments
|
|
||||||
^ "sshyp" will be officially packaged for Arch Linux, Alpine Linux, and Termux (Android).
|
|
||||||
Debian/Ubuntu and Fedora packaging will come later.
|
|
||||||
- imporove password generator to guarantee more secure results
|
|
||||||
|
|
||||||
Planned for next, next major update (The Shears Update Pt. 1):
|
|
||||||
|
|
||||||
- enforce permissions on the server-side
|
|
||||||
- allow for copying entire notes (not just first line)
|
|
||||||
- allow for multi-client deletion... somehow
|
|
||||||
- detatch sshync and make it a separate package
|
|
||||||
- manpage
|
|
||||||
|
|
||||||
Planned for next, next, next major update (The Shears Update Pt. 2):
|
|
||||||
|
|
||||||
- ability to pass entries as arguments for more functions
|
|
||||||
- auto-completion on tab (currently unsure of best way to make this work)
|
|
||||||
|
|
||||||
Backlog:
|
|
||||||
|
|
||||||
- package for more Linux distributions, such as Debian and Fedora
|
|
||||||
- create separate gui frontend targetting mobile and desktop Linux
|
|
||||||
|
|
||||||
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
|
||||||
|
|
||||||
sshyp 2021.12.01.fr2
|
|
||||||
|
|
||||||
The Lighter Update
|
|
||||||
|
|
||||||
This release features a major re-write of old "rpass" code.
|
|
||||||
sshyp is now more reliable and better documented.
|
|
||||||
|
|
||||||
New features:
|
|
||||||
|
|
||||||
- a full re-write of the old "rpass" code used in the last release
|
|
||||||
^ includes more reliable argument parsing and various optimizations
|
|
||||||
- a full re-write of "sshync", many issues fixed
|
|
||||||
^fixed many instances where sshync would refuse to upload/download
|
|
||||||
^fixed errors that were being thrown despite sshync working as intended
|
|
||||||
- new folders are now also created on the server
|
|
||||||
|
|
||||||
Changes:
|
|
||||||
|
|
||||||
- nested folders actually work now
|
|
||||||
- multi-word entry and folder titles are fixed
|
|
||||||
|
|
||||||
An fr2.1 patch is planned for this version. It will include the following:
|
|
||||||
|
|
||||||
- fixes for any new bugs that may be discovered in the new sshyp and sshync code
|
|
||||||
- the gen command in sshyp will be updated to use the newer notetaking system
|
|
||||||
- printing functions in sshyp will be combined into one function (arguments will determine section to print)
|
|
||||||
- file not found exceptions will be re-implemented
|
|
||||||
- the argument parser will be moved to the global process (no need for it to be in a function)
|
|
||||||
- folders will be created natively in python
|
|
||||||
|
|
||||||
Planned for next major update (The Fluffier Update):
|
|
||||||
|
|
||||||
- new visual features
|
|
||||||
^ this includes the new file list and thematic text art. sshyp will be
|
|
||||||
getting a retro computing theme.
|
|
||||||
- packages for more environments
|
|
||||||
^ "sshyp" will be officially packaged for Arch Linux, Alpine Linux, and Termux (Android).
|
|
||||||
Debian/Ubuntu and Fedora packaging will come later.
|
|
||||||
- imporove password generator to guarantee more secure results
|
|
||||||
|
|
||||||
Planned for next, next major update (The Shears Update Pt. 1):
|
|
||||||
|
|
||||||
- allow for copying entire notes (not just first line)
|
|
||||||
- allow for multi-client deletion... somehow
|
|
||||||
- detatch sshync and make it a separate package
|
|
||||||
- manpage
|
|
||||||
|
|
||||||
Planned for next, next, next major update (The Shears Update Pt. 2):
|
|
||||||
|
|
||||||
- ability to pass entries as arguments for more functions
|
|
||||||
- auto-completion on tab (currently unsure of best way to make this work)
|
|
||||||
|
|
||||||
Backlog:
|
|
||||||
|
|
||||||
- package for more Linux distributions, such as Debian and Fedora
|
|
||||||
- create separate gui frontend targetting mobile and desktop Linux
|
|
||||||
|
|
||||||
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
|
||||||
|
|
||||||
sshyp 2021.11.05.fr1
|
|
||||||
|
|
||||||
First public release of sshyp.
|
|
||||||
|
|
||||||
This release fixes major bugs from rpass and rebrands the project.
|
|
||||||
|
|
||||||
New features:
|
|
||||||
|
|
||||||
- an entirely new syncing back-end called sshync (custom Python wrapper around sftp)
|
|
||||||
^ in thoery, all syncing-related data loss should now be fixed, however, this new
|
|
||||||
system has just been created and is still under testing. Maybe wait for a few patches
|
|
||||||
before trusting this system.
|
|
||||||
|
|
||||||
Changes:
|
|
||||||
|
|
||||||
- branding has been updated to reflect new project name and goals
|
|
||||||
- custom directories, custom ssh keys, and offline only mode are removed (may be temporary)
|
|
||||||
^ the configuration wizard is being re-written in the next major update
|
|
||||||
|
|
||||||
Planned for next update (The Fluffy Update):
|
|
||||||
|
|
||||||
- modularized and optimized code
|
|
||||||
^ some older, bad code from "rpass" will be re-written.
|
|
||||||
- new visual features
|
|
||||||
^ this includes the new file list and thematic text art. sshyp will be
|
|
||||||
getting a retro computing theme.
|
|
||||||
- packages for more environments
|
|
||||||
^ "sshyp" will be officially packaged for Arch Linux, Alpine Linux, and Termux (Android).
|
|
||||||
Debian/Ubuntu and Fedora packaging will come later.
|
|
||||||
|
|
||||||
Planned for next update (The Sheep w/Shears Update):
|
|
||||||
|
|
||||||
- allow for copying entire notes (not just first line)
|
|
||||||
- allow for multi-client deletion... somehow
|
|
||||||
- ability to pass entries as arguments for more functions
|
|
||||||
- detatch sshync and make it a separate package
|
|
||||||
- manpage
|
|
||||||
|
|
||||||
Planned, no timeline:
|
|
||||||
|
|
||||||
- create separate gui frontend targetting mobile devices (Android + Linux phones)
|
|
||||||
^ the gui will also function on desktop linux
|
|
||||||
- package for more Linux distributions, such as Debian and Fedora)
|
|
||||||
- auto-completion on tab (currently unsure of best way to make this work)
|
|
||||||
|
|
||||||
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
|
||||||
|
|
||||||
rpass 2021.11.01.mr5.1
|
|
||||||
|
|
||||||
The Notetaker Update - Patch 1
|
|
||||||
|
|
||||||
This is the FINAL release of "rpass".
|
|
||||||
After this release, the project will be rebranded as "sshyp".
|
|
||||||
|
|
||||||
rpass's syncing system is fundamentally broken, hence the need for "sshyp"
|
|
||||||
and its new syncing system. I would highly advise against using this release.
|
|
||||||
|
|
||||||
New features:
|
|
||||||
|
|
||||||
- none
|
|
||||||
|
|
||||||
Changes:
|
|
||||||
|
|
||||||
- fixed an issue with copying notes (currently only copies first line, will be changed in the future)
|
|
||||||
- rpass is validated to work with Python 3.10 (no changes were needed)
|
|
||||||
|
|
||||||
Planned for next update (sshyp fr1)
|
|
||||||
|
|
||||||
- full project rebrand ("sshyp")
|
|
||||||
- new syncing back-end
|
|
||||||
|
|
||||||
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
|
||||||
|
|
||||||
rpass 2021.10.07.mr5
|
|
||||||
|
|
||||||
The Notetaker Update - Fifth main release of rpass
|
|
||||||
|
|
||||||
This release overhauls the note-taking system and provides general improvements
|
|
||||||
to overall code quality, including optimizations, bug fixes, and security improvements.
|
|
||||||
|
|
||||||
New features:
|
|
||||||
|
|
||||||
- notes are now edited in nano and can be split across multiple lines
|
|
||||||
- password confirmation has been added for entry deletion
|
|
||||||
|
|
||||||
Changes:
|
|
||||||
|
|
||||||
- fixed issues with copying passwords with certain special characters, e.g. '"()|
|
|
||||||
- auto-generated ssh key now uses ed25519, rather than rsa-3072
|
|
||||||
- entry format modified to include a blank line before the notes field (old format still mostly compatible)
|
|
||||||
- rpass is now much better at catching bad arguments
|
|
||||||
- some general code cleanup
|
|
||||||
|
|
||||||
Planned for next update (The Housekeeping Update Pt. 2):
|
|
||||||
|
|
||||||
- add new file list w/color and w/o extensions + proper displaying of nested folders
|
|
||||||
- ability to pass entries as arguments for more functions
|
|
||||||
- manpage
|
|
||||||
- add more comments to code
|
|
||||||
- validate for Python 3.10
|
|
||||||
|
|
||||||
Planned for next update (The cryptpass Update):
|
|
||||||
|
|
||||||
- initial implementation of a data store for other applications (libsecret alt.)
|
|
||||||
- modularize code in preparation for GUI frontends
|
|
||||||
|
|
||||||
Planned, no timeline:
|
|
||||||
|
|
||||||
- create separate gui frontend targetting mobile devices (first Linux phones, maybe Android at a later date - official iOS client not likely)
|
|
||||||
^ the gui will also function on desktop linux
|
|
||||||
- make platform agnostic (add Windows support and package for more Linux distributions, such as Alpine and Debian)
|
|
||||||
- auto-completion on tab (currently unsure of best way to make this work)
|
|
||||||
|
|
||||||
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
|
||||||
|
|
||||||
rpass 2021.09.15.mr4.2
|
|
||||||
|
|
||||||
The Housekeeping Update (Pt. 1) - Hotfix 2
|
|
||||||
|
|
||||||
Changes:
|
|
||||||
|
|
||||||
- sync support is fixed
|
|
||||||
- files with spaces in their names can now be properly deleted from remote servers
|
|
||||||
|
|
||||||
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
|
||||||
|
|
||||||
rpass 2021.09.15.mr4.1
|
|
||||||
|
|
||||||
The Housekeeping Update (Pt. 1) - Hotfix
|
|
||||||
|
|
||||||
Changes:
|
|
||||||
|
|
||||||
- the user is once again properly presented with a prompt to enter a gpg key when pressing enter through the config dialog
|
|
||||||
|
|
||||||
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
|
||||||
|
|
||||||
rpass-2021.09.15.mr4
|
|
||||||
|
|
||||||
The Housekeeping Update (Pt. 1) - Fourth main release of rpass
|
|
||||||
|
|
||||||
This release focuses primarily on polishing rpass and tying up loose ends before
|
|
||||||
making any major changes in the future.
|
|
||||||
|
|
||||||
New features:
|
|
||||||
|
|
||||||
- added clipboard support for copying entry information
|
|
||||||
- many common errors are now handled with proper exceptions
|
|
||||||
- offline support (sync no longer needs to be configured for rpass to function)
|
|
||||||
|
|
||||||
Changes:
|
|
||||||
|
|
||||||
- fixed gpg decryption warnings
|
|
||||||
- added version info argument
|
|
||||||
- re-ordered config wizard to make more sense
|
|
||||||
- entry deletion can now be done client-side
|
|
||||||
- removed rsyncup/rsyncdown/username/ip/port/directoryssh files,
|
|
||||||
^ replaced with one file that holds usernamessh, ip, port, directoryssh, and devicetype
|
|
||||||
- files should no longer be left in /dev/shm when an error is encountered
|
|
||||||
- fixed issues relating to using a non-default entry directory
|
|
||||||
- lots of optimizations have been made to reduce the size of rpass
|
|
||||||
|
|
||||||
Planned for next update (The Housekeeping Update Pt. 2):
|
|
||||||
|
|
||||||
- a clean, colorful entry list w/o file extensions
|
|
||||||
- manpage
|
|
||||||
- multi-line notes
|
|
||||||
- commented the code
|
|
||||||
- ability to pass entries as arguments for more functions
|
|
||||||
- improve exceptions for unknown arguments
|
|
||||||
|
|
||||||
Planned, no timeline:
|
|
||||||
|
|
||||||
- possible libsecret integration
|
|
||||||
- create separate gui frontend targetting mobile devices (first Linux phones, maybe Android at a later date - official iOS client not likely)
|
|
||||||
^ the gui will also function on desktop linux
|
|
||||||
- make platform agnostic (add Windows support and package for more Linux distributions)
|
|
||||||
- auto-completion on tab (currently unsure of best way to make this work)
|
|
||||||
|
|
||||||
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
|
||||||
|
|
||||||
rpass 2021.09.07.pr3.1
|
|
||||||
|
|
||||||
The Sync and Foundations Update - Hotfix
|
|
||||||
|
|
||||||
Changes:
|
|
||||||
|
|
||||||
- gpg configuration now prompts for expiration, rather than defaulting to two years
|
|
||||||
- rsync is now configured to not delete anything from any remote device when uploading
|
|
||||||
^ this behavior may be modified in the future - this is a quick fix to prevent data loss
|
|
||||||
|
|
||||||
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
|
||||||
|
|
||||||
rpass 2021.09.07.pr3
|
|
||||||
|
|
||||||
The Sync and Foundations Update - Third public release of rpass.
|
|
||||||
|
|
||||||
As of this version, rpass has its intended base set of features and is considered usable.
|
|
||||||
It will now primarily recieve bug fixes and polish updates to prepare it to go
|
|
||||||
cross-platform and to make it compatible with GUI frontends.
|
|
||||||
|
|
||||||
New features:
|
|
||||||
|
|
||||||
- rsync support!
|
|
||||||
- new entry format with URL field (for future use)
|
|
||||||
- the temporary directory (/dev/shm) for editing entries has been somewhat secured
|
|
||||||
|
|
||||||
Changes:
|
|
||||||
|
|
||||||
- 'rpass edit relocate' is now properly bound
|
|
||||||
- dropped the use of 'echo'
|
|
||||||
- added 'rsync' and 'openssh' as dependencies
|
|
||||||
- fixed a bug with deleting entries from folders
|
|
||||||
- entry directory now has more secure permissions
|
|
||||||
^ these permissions are re-enforced every time rpass is used
|
|
||||||
- fixed some small typos and potential bugs, made some small optimizations
|
|
||||||
|
|
||||||
Planned for next update (The Housekeeping Update):
|
|
||||||
|
|
||||||
- move to github
|
|
||||||
- get rid of gpg decryption warnings
|
|
||||||
- proper error messages using 'except'
|
|
||||||
- add exceptions for unknown arguments
|
|
||||||
- cleanup file list by exporting to document and removing .gpg, adding color
|
|
||||||
- add a manpage
|
|
||||||
- allow notes to be multiple lines long (break lines after so many characters)
|
|
||||||
- validate for Python 3.10
|
|
||||||
- properly comment the code
|
|
||||||
|
|
||||||
Planned, no timeline:
|
|
||||||
|
|
||||||
- allow to function as alternative to gnome keyring (libsecret integration)
|
|
||||||
- create separate gui frontend targetting mobile devices (first linux phones, maybe android at a later date - official iOS client not likely)
|
|
||||||
^ the gui will also function on desktop linux
|
|
||||||
- make platform agnostic (add windows support and package for more Linux distributions)
|
|
||||||
- ability to pass entries as arguments for more functions
|
|
||||||
- auto-completion on tab (currently unsure of best way to make this work)
|
|
||||||
|
|
||||||
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
|
||||||
|
|
||||||
rpass 2021.09.02.pr2
|
|
||||||
|
|
||||||
The Folder Update - Second public release of rpass.
|
|
||||||
|
|
||||||
New features:
|
|
||||||
|
|
||||||
- entry and folder titles can now have spaces and be as long as you want
|
|
||||||
- entires and folders can now be deleted with 'rpass remove' or 'rpass -rm'
|
|
||||||
- entries now properly save to folders via 'rpass add' and should no longer throw errors
|
|
||||||
- entries saved in folders are now readable
|
|
||||||
- existing entries can now be moved to, from, and between folders
|
|
||||||
- previews are now shown after creating or editing an entry
|
|
||||||
|
|
||||||
Changes:
|
|
||||||
|
|
||||||
- every pre-existing function of rpass has been updated to work with the new folder system
|
|
||||||
- dropped awk as a dependency
|
|
||||||
- removed 'argument' file in /var/lib/rpass
|
|
||||||
- reduced unneccessary characters in argument parsing to decrease compatibility issues
|
|
||||||
- ensured all user input is handled correctly as a string or an integer
|
|
||||||
- lots of miscellaneous optimizations
|
|
||||||
|
|
||||||
Expected for next update (The rsync Update):
|
|
||||||
|
|
||||||
- rsync (over ssh) support!
|
|
||||||
^ including any fixes, changes, or optimizations necessary to make that happen
|
|
||||||
- new file formatting with support for URLs
|
|
||||||
^all entries created in new format should be compatible with future versions of rpass
|
|
||||||
- secure /dev/shm with random text generator
|
|
||||||
|
|
||||||
Expected for the next (next) update (The Housekeeping Update)
|
|
||||||
|
|
||||||
- get rid of gpg decryption warnings
|
|
||||||
- proper error messages using 'except'
|
|
||||||
- add exceptions for unknown arguments
|
|
||||||
- cleanup file list by exporting to document and removing .gpg, adding color
|
|
||||||
|
|
||||||
Planned, no timeline:
|
|
||||||
|
|
||||||
- make platform agnostic (basically, add windows support)
|
|
||||||
- create separate gui frontend targetting mobile devices (first linux phones, android at a later date)
|
|
||||||
^ the gui will also function on desktop linux
|
|
||||||
- ability to pass entries as arguments for more functions
|
|
||||||
- auto-completion on tab
|
|
||||||
|
|
||||||
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
|
||||||
|
|
||||||
rpass 2021.09.01.pr1.1
|
|
||||||
|
|
||||||
First public release of rpass.
|
|
||||||
|
|
||||||
Early build, missing critical features (e.g. sync support and the ability to have spaces in filenames).
|
|
||||||
|
|
||||||
What is currently functional:
|
|
||||||
|
|
||||||
- guided configuration wizard
|
|
||||||
- generate passwords
|
|
||||||
- add existing passwords
|
|
||||||
- make notes
|
|
||||||
- sort notes and passwords into folders
|
|
||||||
- gpg encryption
|
|
||||||
- edit password/note entries
|
|
||||||
@@ -0,0 +1,242 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
the argumentative agronomist update - patch two
|
||||||
|
|
||||||
|
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 release makes use of a new ports framework to make sshyp even smaller and faster than before
|
||||||
|
|
||||||
|
user-facing features:
|
||||||
|
|
||||||
|
- full WSL (Windows Subsystem for Linux) support
|
||||||
|
- partial MacOS support
|
||||||
|
^ fully functional; packaging support has not yet been added
|
||||||
|
- dedicated help screen for 'gen'
|
||||||
|
^ this was previously not included due to 'sshyp gen' being a valid command prior to v1.4.0
|
||||||
|
- removed interactive input for whitelist management
|
||||||
|
^ this is to be consistent with the removal of interactive input on clients in v1.4.0
|
||||||
|
|
||||||
|
fixes/optimizations:
|
||||||
|
|
||||||
|
- leading and following spaces are no longer stripped when printing entry contents
|
||||||
|
- properly display an error when trying to remove a device from the whitelist that is not whitelisted
|
||||||
|
- ports framework to remove unneeded code for each platform
|
||||||
|
^ BLANKS.py removes all blank lines
|
||||||
|
^ CLIPBOARD.py removes non-platform-critical clipboard code
|
||||||
|
^ COMMENTS.py removes all comments
|
||||||
|
^ RMSERVER.py removes server-only code for creating client-only packages
|
||||||
|
^ the Haiku package, which was already unsupported as a server, now only includes client code
|
||||||
|
^ SHEBANG.sh sets the shebang to what is required by the platform
|
||||||
|
^ TABS.sh converts all groups of spaces to tabs for space efficiency
|
||||||
|
^ UNAME.py removes all instances of platform detection and leaves only the platform-required code
|
||||||
|
^ THESE SCRIPTS ARE ONLY USED FOR PACKAGING AND DO NOT AFFECT THE SOURCE FILES IN THE GIT REPO
|
||||||
|
|
||||||
|
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
||||||
|
|
||||||
|
sshyp v1.4.1
|
||||||
|
|
||||||
|
the argumentative agronomist update - patch one
|
||||||
|
|
||||||
|
this release improves sshyp's shell completions by adding zsh support and significantly optimizing and improving Bash support
|
||||||
|
|
||||||
|
user-facing features:
|
||||||
|
|
||||||
|
- zsh completion support
|
||||||
|
^ ensure modern completions are enabled in your ~/.zshrc
|
||||||
|
|
||||||
|
fixes/optimizations:
|
||||||
|
|
||||||
|
- Bash completions are now much faster due to the use of recursive globbing
|
||||||
|
- Bash completions have been modified to be functionally similar to the new zsh completions
|
||||||
|
|
||||||
|
other notable changes:
|
||||||
|
|
||||||
|
- some official packages now use more space-efficient compression
|
||||||
|
|
||||||
|
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
||||||
|
|
||||||
|
sshyp v1.4.0
|
||||||
|
|
||||||
|
the argumentative agronomist update
|
||||||
|
|
||||||
|
this release overhauls sshyp's argument system and further streamlines
|
||||||
|
the experience with optimizations and the removal of legacy features
|
||||||
|
|
||||||
|
compatibility-breaking changes:
|
||||||
|
|
||||||
|
- there are no technical breaking changes, but the UX has changed significantly due to the new argument system (detailed below)
|
||||||
|
|
||||||
|
user-facing features:
|
||||||
|
|
||||||
|
- when typing an entry/folder name, it is now always the FIRST argument
|
||||||
|
^ e.g. instead of "sshyp copy -p /example/test", it would now be "sshyp /example/test copy -p"
|
||||||
|
^ this allows more easily editing the previous command to copy/edit a different field
|
||||||
|
- better extension integration
|
||||||
|
^ extensions can now ship with a config file that sshyp can use to call them from standard sshyp arguments
|
||||||
|
^ the first extension supporting this is sshyp-mfa
|
||||||
|
^ if sshyp-mfa v1.4.0.1+ is installed, "sshyp /example/test copy -m" will copy MFA keys to your clipboard
|
||||||
|
- entry/folder names now MUST be specified as arguments
|
||||||
|
^ there is no longer a separate input prompt if the entry/folder name is not provided as an argument
|
||||||
|
^ the input prompt was a legacy feature from before arguments could specify entrys/folders with shell completion
|
||||||
|
^ the entry list generated by entry_list_gen() is still present and can be viewed by running "sshyp" with no arguments
|
||||||
|
- better Bash completions
|
||||||
|
^ now correctly places backslashes to escape spaces in entry/folder names
|
||||||
|
^ slightly faster than the previous iteration
|
||||||
|
- IPv6 configuration support
|
||||||
|
|
||||||
|
fixes/optimizations:
|
||||||
|
|
||||||
|
- replaced "+=" with ".append" when extending lists to prevent creating an additional list
|
||||||
|
- optimized usage of "str.split()" and replaced it with "str.splitlines()" where applicable
|
||||||
|
- more concise "if... in ()" syntax replaces long "if" statement chains
|
||||||
|
- lists have been substituted with tuples where applicable
|
||||||
|
- entry_list_gen() has been re-written to be much smaller and faster
|
||||||
|
- os.path is now used in place of pathlib.Path in areas where it is faster
|
||||||
|
- the user's home directory is saved to a variable to prevent running expanduser() every time it is needed
|
||||||
|
|
||||||
|
other notable changes:
|
||||||
|
|
||||||
|
- removed some uncommonly used, redundant arguments
|
||||||
|
^ e.g. "-rm", "-s", "delete"
|
||||||
|
^ their functionality was NOT removed, just their redundant arguments, since "shear", "sync", and "del" are the accepted syntax
|
||||||
|
- errors containing entry/folder names reinforce correct syntax by adding leading/following slashes where necessary
|
||||||
|
- thanks to pull request #25, there are new error messages for the read shortcut
|
||||||
|
^ includes when no entry name is provided or the entry name only refers to a directory
|
||||||
|
- directories provided in user input are no longer denoted by a following slash
|
||||||
|
^ instead, os.path is used to determine if the user is referring to a file or directory
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# sshyp(1) completion
|
||||||
|
|
||||||
|
_path_gen() {
|
||||||
|
local sshyp_path="$HOME/.local/share/sshyp"
|
||||||
|
local glob_status=$(shopt -p globstar)
|
||||||
|
[ -z "${glob_status##*u*}" ] && shopt -s globstar # if recursive globbing is disabled, enable it
|
||||||
|
local full_paths=("$sshyp_path"/**/*.gpg)
|
||||||
|
$glob_status # set recursive globbing to user default
|
||||||
|
for scan_path in "${full_paths[@]}"; do
|
||||||
|
trimmed_paths+=("${scan_path:${#sshyp_path}:-4}")
|
||||||
|
done
|
||||||
|
if [ "${trimmed_paths[0]}" == '/**/*' ]; then trimmed_paths[0]=help; fi
|
||||||
|
} &&
|
||||||
|
|
||||||
|
_sshyp_completions() {
|
||||||
|
local cur=${COMP_WORDS[COMP_CWORD]}
|
||||||
|
local prev=${COMP_WORDS[COMP_CWORD-1]}
|
||||||
|
|
||||||
|
case $prev in
|
||||||
|
sshyp )
|
||||||
|
while read -r; do ITEM=${REPLY// /\\ }; COMPREPLY+=( "$ITEM" ); done < <( compgen -W "$(printf "'%s' " "${trimmed_paths[@]}")" -- "$cur" )
|
||||||
|
;;
|
||||||
|
/* )
|
||||||
|
while read -r; do COMPREPLY+=( "$REPLY" ); done < <( compgen -W "add gen edit copy shear" -- "$cur" )
|
||||||
|
;;
|
||||||
|
add )
|
||||||
|
while read -r; do COMPREPLY+=( "$REPLY" ); done < <( compgen -W "password note folder" -- "$cur" )
|
||||||
|
;;
|
||||||
|
copy )
|
||||||
|
while read -r; do COMPREPLY+=( "$REPLY" ); done < <( compgen -W "password username url note" -- "$cur" )
|
||||||
|
;;
|
||||||
|
edit )
|
||||||
|
while read -r; do COMPREPLY+=( "$REPLY" ); done < <( compgen -W "password username url note relocate" -- "$cur" )
|
||||||
|
;;
|
||||||
|
gen )
|
||||||
|
while read -r; do COMPREPLY+=( "$REPLY" ); done < <( compgen -W "update" -- "$cur" )
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
} &&
|
||||||
|
_path_gen && complete -F _sshyp_completions sshyp
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
#compdef sshyp
|
||||||
|
|
||||||
|
sshyp_path="$HOME/.local/share/sshyp"
|
||||||
|
full_paths=("$sshyp_path"/**/*.gpg)
|
||||||
|
trimmed_paths=()
|
||||||
|
for scan_path in "${full_paths[@]}"; do
|
||||||
|
trimmed_paths+=("${${scan_path#$sshyp_path}%????}")
|
||||||
|
done
|
||||||
|
[[ -z $trimmed_paths ]] && trimmed_paths=(help)
|
||||||
|
|
||||||
|
case ${words[-2]} in
|
||||||
|
sshyp )
|
||||||
|
compadd $trimmed_paths
|
||||||
|
;;
|
||||||
|
/* )
|
||||||
|
compadd {add,gen,edit,copy,shear}
|
||||||
|
;;
|
||||||
|
add )
|
||||||
|
compadd {password,note,folder}
|
||||||
|
;;
|
||||||
|
copy )
|
||||||
|
compadd {password,username,url,note}
|
||||||
|
;;
|
||||||
|
edit )
|
||||||
|
compadd {password,username,url,note,relocate}
|
||||||
|
;;
|
||||||
|
gen )
|
||||||
|
compadd update
|
||||||
|
;;
|
||||||
|
esac
|
||||||
Executable → Regular
+91
-39
@@ -1,91 +1,143 @@
|
|||||||
.TH sshyp 1 "27 August 2022" "v1.1.2" "sshyp man page"
|
.TH sshyp 1 "24 October 2023" "v1.5.1" "sshyp man page"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
sshyp \- A very simple self-hosted, synchronized password manager for UNIX(-like) systems. Alternative to (and compatible with) pass/password-store.
|
\fBsshyp\fR - Simple, self-hosted, synchronized password management for UNIX(-like) systems. Alternative to (and compatible with) pass/password-store.
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
sshyp [OPTION] [[FLAG]] [/<entry name>]
|
Client Usage: sshyp [/<entry name> [argument] [option]] | [argument]
|
||||||
|
|
||||||
|
Server Usage: sshyp <argument>
|
||||||
.SH DESCRIPTION
|
.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.
|
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
|
.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
|
sshyp tweak
|
||||||
|
|
||||||
Viewing the entry database:
|
Viewing the entry database:
|
||||||
sshyp
|
sshyp
|
||||||
|
|
||||||
Reading an existing entry saved as ~/.local/share/sshyp/development/github.gpg:
|
Reading an existing entry saved as '~/.local/share/sshyp/development/github.gpg':
|
||||||
sshyp /development/github
|
sshyp /development/github
|
||||||
|
|
||||||
Copying the password of an existing entry saved as ~/.local/share/sshyp/financial/bank.gpg:
|
Removing an existing folder saved as '~/.local/share/sshyp/social':
|
||||||
sshyp copy -p /financial/bank
|
sshyp /social shear
|
||||||
|
|
||||||
Editing the username of an existing entry saved as ~/.local/share/sshyp/game.gpg:
|
Reading the same entry without hiding the password:
|
||||||
sshyp edit -u game
|
sshyp /development/github --show
|
||||||
|
|
||||||
Making a new entry saved as ~/.local/share/sshyp/school/university.gpg using the built-in password generator:
|
Copying the password of an existing entry saved as '~/.local/share/sshyp/financial/bank.gpg':
|
||||||
sshyp gen /school/university
|
sshyp /financial/bank copy -p
|
||||||
|
|
||||||
Creating a note-only entry saved as ~/.local/share/sshyp/notes/testNote.gpg:
|
Editing the username of an existing entry saved as '~/.local/share/sshyp/game.gpg':
|
||||||
sshyp add -n /notes/testNote
|
sshyp /game edit -u
|
||||||
|
|
||||||
|
Making a new entry saved as '~/.local/share/sshyp/school/university.gpg' using the built-in password generator:
|
||||||
|
sshyp /school/university gen
|
||||||
|
|
||||||
|
Creating a note-only entry saved as '~/.local/share/sshyp/notes/test note.gpg':
|
||||||
|
sshyp /notes/test\ note add -n
|
||||||
|
|
||||||
Manually syncing entries with the server:
|
Manually syncing entries with the server:
|
||||||
sshyp sync
|
sshyp sync
|
||||||
|
.SH ARGUMENTS (CLIENT)
|
||||||
Removing an existing folder saved as ~/.local/share/sshyp/social:
|
help/-h bring up the help menu
|
||||||
sshyp shear social/
|
version/-v display sshyp version info
|
||||||
|
init set up sshyp
|
||||||
.SH OPTIONS
|
tweak change configuration options/manage extensions and updates
|
||||||
USAGE: sshyp [OPTION [FLAG] [<entry name>]] | [/<entry name>]
|
add add an entry
|
||||||
|
gen generate a new password
|
||||||
help/--help/-h bring up the help menu
|
edit edit an existing entry
|
||||||
version/-v display sshyp version info
|
copy copy details of an entry to your clipboard
|
||||||
tweak configure sshyp
|
shear delete an existing entry
|
||||||
add add an entry
|
sync manually sync the entry directory via sshync
|
||||||
gen generate a new password
|
.SH OPTIONS (CLIENT)
|
||||||
edit edit an existing entry
|
|
||||||
copy copy details of an entry to your clipboard
|
|
||||||
shear/-rm delete an existing entry
|
|
||||||
sync/-s manually sync the entry directory via sshync
|
|
||||||
.SH FLAGS
|
|
||||||
add:
|
add:
|
||||||
password/-p add a password entry
|
password/-p add a password entry
|
||||||
note/-n add a note entry
|
note/-n add a note entry
|
||||||
folder/-f add a new folder for entries
|
folder/-f add a new folder for entries
|
||||||
|
|
||||||
edit:
|
edit:
|
||||||
rename/relocate/-r rename or relocate an entry
|
rename/relocate/-r rename or relocate an entry
|
||||||
username/-u change the username of an entry
|
username/-u change the username of an entry
|
||||||
password/-p change the password of an entry
|
password/-p change the password of an entry
|
||||||
note/-n change the note attached to an entry
|
note/-n change the note attached to an entry
|
||||||
url/-l change the url attached to an entry
|
url/-l change the url attached to an entry
|
||||||
|
|
||||||
copy:
|
copy:
|
||||||
username/-u copy the username 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
|
password/-p copy the password of an entry to your clipboard
|
||||||
url/-l copy the URL 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
|
note/-n copy the note of an entry to your clipboard
|
||||||
|
|
||||||
gen:
|
gen:
|
||||||
update/-u generate a password for an existing entry
|
update/-u generate a password for an existing entry
|
||||||
.SH LIMITATIONS
|
.SH ARGUMENTS (SERVER)
|
||||||
The following characters/character sequences are not supported in entry/folder titles:
|
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
|
.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.
|
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.
|
||||||
|
|
||||||
The sshyp package includes modes for operating on both client and server devices, so only one package is necessary.
|
The sshyp package (on fully supported platforms) includes modes for operating on both client and server devices, so only one package is necessary.
|
||||||
|
|
||||||
Server setup:
|
Server setup:
|
||||||
Configure an OpenSSH server (if sshyp was installed from the Debian package, the ssh server was not installed as a dependency and needs to be installed manually)
|
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)
|
Allow remote access to the SSH server w/public key authentication (disabling password-only authentication recommended)
|
||||||
Install sshyp
|
Install sshyp
|
||||||
Run "sshyp tweak" and set the device type as server
|
Run "sshyp init" and set the device type as server
|
||||||
|
Optionally, configure a quick-unlock pin from the tweak menu
|
||||||
Done!
|
Done!
|
||||||
|
|
||||||
Client setup:
|
Client setup:
|
||||||
Install sshyp
|
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.)
|
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"
|
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!
|
Done!
|
||||||
|
|
||||||
Please note that the server setup intentionally does not allow the reading of entries (it does not allow adding a gpg decryption key). For security purposes, only clients can read entries.
|
Please note that the server setup intentionally does not allow the reading of entries (it does not allow adding a gpg decryption key). For security purposes, only clients can read entries.
|
||||||
|
.SH TROUBLESHOOTING
|
||||||
|
.B Clipboard not clearing:
|
||||||
|
|
||||||
|
The intended way for sshyp to interact with the system clipboard is for it to clear it 30 seconds after copying a field. Unfortunately, this does not work by default on all systems due to the prevalence of clipboard managers.
|
||||||
|
|
||||||
|
Clipboard managers save a history of what has been copied to the clipboard, which is already a big enough issue on its own for people who copy sensitive information to their clipboard. Some clipboard managers simply will not allow the clipboard to be empty and will replace its contents with the last copied item if you attempt to clear it. One such naughty clipboard manager is KDE Klipper, which comes packaged into KDE Plasma and is typically enabled by default on most distributions. Due to this behavior, KDE Klipper breaks sshyp's clipboard clearing functionality and should not be left enabled.
|
||||||
|
|
||||||
|
It is likely other popular clipboard managers exhibit this behavior as well (Klipper is just the one I first noticed it with). Clipboard managers should not be enabled by default in any environment or distribution due to their potential security implications.
|
||||||
|
|
||||||
|
.B Shell completions not working:
|
||||||
|
ZSH:
|
||||||
|
Make sure your ~/.zshrc contains the following:
|
||||||
|
autoload -Uz compinit && compinit
|
||||||
|
Restart your shell
|
||||||
|
|
||||||
|
Bash:
|
||||||
|
Install your distribution's 'bash-completion' package and restart your shell
|
||||||
|
OR
|
||||||
|
Source the completion script manually by adding the following to your ~/.bashrc (then restart your shell):
|
||||||
|
Linux:
|
||||||
|
source /usr/share/bash-completion/completions/sshyp
|
||||||
|
FreeBSD:
|
||||||
|
source /usr/local/share/bash-completion/completions/sshyp
|
||||||
|
Haiku:
|
||||||
|
source /system/data/bash-completion/completions/sshyp
|
||||||
|
|
||||||
|
.SH EXIT CODES
|
||||||
|
0 - no error
|
||||||
|
|
||||||
|
1 - configuration error
|
||||||
|
|
||||||
|
2 - data not found error
|
||||||
|
|
||||||
|
3 - data already exists error
|
||||||
|
|
||||||
|
4 - decryption/encryption error
|
||||||
|
|
||||||
|
5 - server connection error
|
||||||
|
|
||||||
|
6 - other error
|
||||||
.SH AUTHOR
|
.SH AUTHOR
|
||||||
Randall Winkhart (https://github.com/rwinkhart)
|
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
|
||||||
+184
@@ -0,0 +1,184 @@
|
|||||||
|
from os import listdir, remove, walk
|
||||||
|
from os.path import expanduser, isdir, isfile, getmtime, join
|
||||||
|
from subprocess import CalledProcessError, PIPE, run
|
||||||
|
from sys import exit as s_exit
|
||||||
|
home = expanduser('~')
|
||||||
|
|
||||||
|
|
||||||
|
# PORT START SSHYNC-REMOTE
|
||||||
|
# REMOTE
|
||||||
|
# prints all necessary remote data to stdout
|
||||||
|
def remote_list_gen(_client_device_name, _remote_dir):
|
||||||
|
# deletions
|
||||||
|
for _file in listdir(f"{home}/.config/sshyp/deleted"):
|
||||||
|
_file_path, _sep, _device = _file.partition('\x1f')
|
||||||
|
_file_path = _file_path.replace('\x1e', '/')
|
||||||
|
if _device == _client_device_name:
|
||||||
|
print(_file_path)
|
||||||
|
try:
|
||||||
|
remove(f"{home}/.config/sshyp/deleted/{_file}")
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
print('\x1d')
|
||||||
|
# folders
|
||||||
|
for _root, _directories, _files in walk(f"{home}/.local/share/sshyp"):
|
||||||
|
for _dir in _directories:
|
||||||
|
print(f"{_root.replace(home, '')}/{_dir}")
|
||||||
|
print('\x1d')
|
||||||
|
# titles and mod times
|
||||||
|
get_local_data(_remote_dir, 'server')
|
||||||
|
# PORT END SSHYNC-REMOTE
|
||||||
|
|
||||||
|
|
||||||
|
# HYBRID
|
||||||
|
# deletes a file or folder and/or marks it for deletion upon syncing
|
||||||
|
def delete(_file_path, _target_database, _silent):
|
||||||
|
from shutil import rmtree
|
||||||
|
_directory = f"{home}/.local/share/sshyp/"
|
||||||
|
try:
|
||||||
|
if isdir(_directory + _file_path):
|
||||||
|
rmtree(_directory + _file_path)
|
||||||
|
else:
|
||||||
|
remove(f"{_directory}{_file_path}.gpg")
|
||||||
|
except FileNotFoundError:
|
||||||
|
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')
|
||||||
|
|
||||||
|
|
||||||
|
# retrieves and returns titles and mod times from the local device
|
||||||
|
def get_local_data(_directory, _device):
|
||||||
|
_title_list, _mod_list = [], []
|
||||||
|
for _root, _directories, _files in walk(_directory):
|
||||||
|
for _filename in _files:
|
||||||
|
_title_list.append(join(_root.replace(_directory, '', 1), _filename))
|
||||||
|
_mod_list.append(int(getmtime(join(_root, _filename))))
|
||||||
|
if _device == 'server':
|
||||||
|
for _title in _title_list:
|
||||||
|
print(_title.rstrip())
|
||||||
|
for _time in _mod_list:
|
||||||
|
print(_time)
|
||||||
|
return _title_list, _mod_list
|
||||||
|
|
||||||
|
|
||||||
|
# LOCAL
|
||||||
|
# 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]}",
|
||||||
|
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
|
||||||
|
).stdout.split('\x1d')
|
||||||
|
except CalledProcessError:
|
||||||
|
print('\n\u001b[38;5;9merror: failed to connect to the remote server\u001b[0m\n')
|
||||||
|
_remote_data = ''
|
||||||
|
s_exit(5)
|
||||||
|
_deletion_database = _remote_data[0].strip().splitlines()
|
||||||
|
_folder_database = _remote_data[1].strip().splitlines()
|
||||||
|
_titles_mods = _remote_data[2].strip().splitlines()
|
||||||
|
return _deletion_database, _folder_database, _titles_mods[:len(_titles_mods)//2], \
|
||||||
|
_titles_mods[len(_titles_mods)//2:]
|
||||||
|
|
||||||
|
|
||||||
|
# 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', _silent)
|
||||||
|
if _synced:
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
# creates matches of remote folders on the local client
|
||||||
|
def folder_sync(_folder_database):
|
||||||
|
from pathlib import Path
|
||||||
|
for _folder in _folder_database:
|
||||||
|
if _folder != '' and not isdir(home + _folder):
|
||||||
|
print(f"\u001b[38;5;2m{_folder.replace('/.local/share/sshyp/', '')}/\u001b[0m does not exist locally, "
|
||||||
|
f"creating...")
|
||||||
|
Path(home + _folder).mkdir(mode=0o700, parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
# creates and returns two lists (of titles and mod times) generated by sorting information from two provided 2D lists
|
||||||
|
def sort_titles_mods(_list_1, _list_2):
|
||||||
|
_title_list_2_sorted, _mod_list_2_sorted = [], []
|
||||||
|
# title sorting
|
||||||
|
for _title in _list_1[0]:
|
||||||
|
if _title in _list_2[0]:
|
||||||
|
_title_list_2_sorted.append(_title)
|
||||||
|
for _title in _list_2[0]:
|
||||||
|
if _title not in _title_list_2_sorted:
|
||||||
|
_title_list_2_sorted.append(_title)
|
||||||
|
# mod time sorting
|
||||||
|
for _title in _title_list_2_sorted:
|
||||||
|
_mod_list_2_sorted.append(_list_2[1][_list_2[0].index(_title)])
|
||||||
|
return _title_list_2_sorted, _mod_list_2_sorted
|
||||||
|
|
||||||
|
|
||||||
|
# returns a list of data read from a sshync job profile
|
||||||
|
def get_profile(_profile_dir):
|
||||||
|
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')
|
||||||
|
s_exit(2)
|
||||||
|
_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
|
||||||
|
|
||||||
|
|
||||||
|
# runs a sshync job profile
|
||||||
|
def run_profile(_profile_dir, _silent):
|
||||||
|
# import profile data
|
||||||
|
_user_data = get_profile(_profile_dir)
|
||||||
|
# fetch remote lists
|
||||||
|
_deletion_database, _folder_database, _remote_titles, _remote_mods = remote_list_fetch(_user_data)
|
||||||
|
_remote_titles_mods = (_remote_titles, _remote_mods)
|
||||||
|
# sync deletions and folders
|
||||||
|
deletion_sync(_deletion_database, _silent)
|
||||||
|
folder_sync(_folder_database)
|
||||||
|
# sort titles and mods
|
||||||
|
_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, _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]}{_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]}{_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]}{_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]}{_title}"))
|
||||||
|
if _synced:
|
||||||
|
print()
|
||||||
Executable
+746
@@ -0,0 +1,746 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from configparser import ConfigParser, NoOptionError, NoSectionError
|
||||||
|
from os import chmod, environ, listdir, walk
|
||||||
|
from os.path import expanduser, isdir, isfile, realpath
|
||||||
|
from pathlib import Path
|
||||||
|
from shutil import move
|
||||||
|
from sshync import delete as offline_delete, run_profile
|
||||||
|
from subprocess import CalledProcessError, DEVNULL, PIPE, run
|
||||||
|
from sys import argv, exit as s_exit
|
||||||
|
# PORT START UNAME-IMPORT-SSHYP
|
||||||
|
from os import uname
|
||||||
|
# PORT END UNAME-IMPORT-SSHYP
|
||||||
|
home = expanduser('~')
|
||||||
|
|
||||||
|
|
||||||
|
# UTILITY FUNCTIONS
|
||||||
|
|
||||||
|
# generates and prints full entry list
|
||||||
|
def entry_list_gen(_directory=f"{home}/.local/share/sshyp/"):
|
||||||
|
from shutil import get_terminal_size
|
||||||
|
from textwrap import fill
|
||||||
|
_ran = False
|
||||||
|
print("\nfor a list of usable commands, run 'sshyp help'\n\n\u001b[38;5;0;48;5;15msshyp entries:\u001b[0m\n")
|
||||||
|
for _root, _dirs, _files in sorted(walk(_directory, topdown=True)):
|
||||||
|
_entry_list, _color_alternator = [], 1
|
||||||
|
if _ran:
|
||||||
|
print(f"\u001b[38;5;15;48;5;238m{_root.replace(f'{home}/.local/share/sshyp', '', 1)}/\u001b[0m")
|
||||||
|
for filename in sorted(_files):
|
||||||
|
if _color_alternator > 0:
|
||||||
|
_entry_list.append(filename[:-4])
|
||||||
|
else:
|
||||||
|
_entry_list.append(f"\u001b[38;5;8m{filename[:-4]}\u001b[0m")
|
||||||
|
_color_alternator = _color_alternator * -1
|
||||||
|
_real = len(' '.join(_entry_list)) - (5.5 * len(_entry_list))
|
||||||
|
if _real <= get_terminal_size()[0]:
|
||||||
|
_width = len(' '.join(_entry_list))
|
||||||
|
else:
|
||||||
|
_width = (len(' '.join(_entry_list)) / (_real / get_terminal_size()[0]) - 25)
|
||||||
|
if len(_entry_list) > 0:
|
||||||
|
print(fill(' '.join(_entry_list), width=_width) + '\n')
|
||||||
|
elif _ran:
|
||||||
|
print('\u001b[38;5;9m-empty directory-\u001b[0m\n')
|
||||||
|
_ran = True
|
||||||
|
|
||||||
|
|
||||||
|
# displays the contents of an entry in a readable format
|
||||||
|
def entry_reader(_decrypted_entry):
|
||||||
|
_notes_flag = 0
|
||||||
|
if pass_show:
|
||||||
|
_entry_password = f'\u001b[38;5;10m{_decrypted_entry[0]}\u001b[0m'
|
||||||
|
else:
|
||||||
|
_entry_password = f'\u001b[38;5;3mend command in "--show" or "-s" to view\u001b[0m'
|
||||||
|
print()
|
||||||
|
for _num in range(len(_decrypted_entry)):
|
||||||
|
try:
|
||||||
|
if _num == 0 and _decrypted_entry[1] != '':
|
||||||
|
print(f"\u001b[38;5;15;48;5;238musername:\u001b[0m\n{_decrypted_entry[1]}\n")
|
||||||
|
elif _num == 1 and _decrypted_entry[0] != '':
|
||||||
|
print(f"\u001b[38;5;15;48;5;238mpassword:\u001b[0m\n{_entry_password}\n")
|
||||||
|
elif _num == 2 and _decrypted_entry[2] != '':
|
||||||
|
print(f"\u001b[38;5;15;48;5;238murl:\u001b[0m\n{_decrypted_entry[_num]}\n")
|
||||||
|
elif _num >= 3 and _decrypted_entry[_num] != '' and _notes_flag != 1:
|
||||||
|
_notes_flag = 1
|
||||||
|
print('\u001b[38;5;15;48;5;238mnotes:\u001b[0m\n' + _decrypted_entry[_num])
|
||||||
|
elif _num >= 3 and _notes_flag == 1:
|
||||||
|
print(_decrypted_entry[_num])
|
||||||
|
if _notes_flag == 1:
|
||||||
|
try:
|
||||||
|
_line_test = _decrypted_entry[_num + 1]
|
||||||
|
except IndexError:
|
||||||
|
print()
|
||||||
|
except IndexError:
|
||||||
|
if _num == 0 and _decrypted_entry[0] != '':
|
||||||
|
print(f"\u001b[38;5;15;48;5;238mpassword:\u001b[0m\n{_entry_password}\n")
|
||||||
|
|
||||||
|
|
||||||
|
# generates and returns a random string based on input
|
||||||
|
def string_gen(_complexity, _length):
|
||||||
|
from random import SystemRandom
|
||||||
|
import string
|
||||||
|
if _complexity == 's':
|
||||||
|
_character_pool = string.ascii_letters + string.digits
|
||||||
|
elif _complexity == 'f':
|
||||||
|
_character_pool = string.digits + string.ascii_letters + string.punctuation.replace('/', '').replace('\\', '')\
|
||||||
|
.replace("'", '').replace('"', '').replace('`', '').replace('~', '')
|
||||||
|
else:
|
||||||
|
_character_pool = string.digits + string.ascii_letters + string.punctuation
|
||||||
|
_min_special, _special = round(.2 * _length), 0
|
||||||
|
while True:
|
||||||
|
_gen = ''.join(SystemRandom().choice(_character_pool) for _ in range(_length))
|
||||||
|
for _character in _gen:
|
||||||
|
if not _character.isalpha():
|
||||||
|
_special += 1
|
||||||
|
if _special >= _min_special:
|
||||||
|
break
|
||||||
|
return _gen
|
||||||
|
|
||||||
|
|
||||||
|
# prompts the user for necessary information to generate a password and passes it to string_gen
|
||||||
|
def pass_gen():
|
||||||
|
_length = 9
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
_length = int(input('\npassword length: '))
|
||||||
|
except ValueError:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
if _length < 1:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
_complexity = str(input('\npassword complexity - simple (for compatibility) or complex (for security)? (s/C) '))
|
||||||
|
if _complexity not in ('s', 'S'):
|
||||||
|
_complexity = 'c'
|
||||||
|
_gen = string_gen(_complexity.lower(), _length)
|
||||||
|
return _gen
|
||||||
|
|
||||||
|
|
||||||
|
# creates a temporary file to allow notes to be edited by standard editors
|
||||||
|
def edit_note(_note_lines, _exit_on_match=False):
|
||||||
|
from tempfile import NamedTemporaryFile
|
||||||
|
_joined_note_lines = '\n'.join(_note_lines)
|
||||||
|
with NamedTemporaryFile(mode='w+') as _tmp:
|
||||||
|
_tmp.write(_joined_note_lines)
|
||||||
|
_tmp.seek(0)
|
||||||
|
try:
|
||||||
|
run((editor, _tmp.name))
|
||||||
|
except FileNotFoundError:
|
||||||
|
print(f"\n\u001b[38;5;9merror: the configured text editor ({editor}) cannot be found on this system\n\n"
|
||||||
|
f"please either install the editor or re-configure the active editor using 'sshyp tweak'\u001b[0m\n")
|
||||||
|
_tmp.seek(0)
|
||||||
|
_new_note = _tmp.read().rstrip()
|
||||||
|
if _exit_on_match and _joined_note_lines == _new_note:
|
||||||
|
s_exit(0)
|
||||||
|
return _new_note
|
||||||
|
|
||||||
|
|
||||||
|
# encrypts an entry and cleans up the temporary files
|
||||||
|
def encrypt(_entry_data, _entry_dir, _gpg_id):
|
||||||
|
_bytes_data = '\n'.join(_entry_data).rstrip().encode()
|
||||||
|
_encrypted_data = run(('gpg', '-qr', str(_gpg_id), '-e'), input=_bytes_data, stdout=PIPE).stdout
|
||||||
|
open(_entry_dir + '.gpg', 'wb').write(_encrypted_data)
|
||||||
|
|
||||||
|
|
||||||
|
# decrypts an entry to a temporary directory
|
||||||
|
def decrypt(_entry_dir, _quick_verify=None, _quick_pass=None):
|
||||||
|
_contents = None
|
||||||
|
|
||||||
|
# check quick-unlock status, fetch passphrase
|
||||||
|
if _quick_verify:
|
||||||
|
_quick_pass = whitelist_verify(port, username_ssh, ip, client_device_id, identity)
|
||||||
|
else:
|
||||||
|
if _quick_pass is None:
|
||||||
|
_quick_pass = False
|
||||||
|
|
||||||
|
# set decryption method based on quick-unlock availability
|
||||||
|
if not isinstance(_quick_pass, bool):
|
||||||
|
_cmd = ['gpg', '--pinentry-mode', 'loopback', '--passphrase', _quick_pass, '-qd']
|
||||||
|
else:
|
||||||
|
_cmd = ['gpg', '-qd']
|
||||||
|
|
||||||
|
# set decryption target based on lock file availability
|
||||||
|
if _entry_dir is None:
|
||||||
|
_dec_target = [f"{home}/.config/sshyp/lock.gpg"]
|
||||||
|
else:
|
||||||
|
_dec_target = [f"{_entry_dir}.gpg"]
|
||||||
|
|
||||||
|
# run decryption command
|
||||||
|
try:
|
||||||
|
_contents = run(_cmd + _dec_target, stderr=DEVNULL, stdout=PIPE, text=True, check=True).stdout
|
||||||
|
except CalledProcessError:
|
||||||
|
if not isinstance(_quick_pass, bool):
|
||||||
|
print('\n\u001b[38;5;9merror: quick-unlock failed as a result of an incorrect passphrase, an unreachable '
|
||||||
|
'sshyp server, or an invalid configuration\n\nfalling back to standard unlock\u001b[0m\n')
|
||||||
|
try:
|
||||||
|
_contents = run(['gpg', '-qd'] + _dec_target, stderr=DEVNULL, stdout=PIPE, text=True, check=True).stdout
|
||||||
|
except CalledProcessError:
|
||||||
|
print('\n\u001b[38;5;9merror: could not decrypt - ensure the correct gpg key is present\u001b[0m\n')
|
||||||
|
s_exit(4)
|
||||||
|
else:
|
||||||
|
print('\n\u001b[38;5;9merror: could not decrypt - ensure the correct gpg key is present\u001b[0m\n')
|
||||||
|
s_exit(4)
|
||||||
|
return _contents.rstrip().split('\n')
|
||||||
|
|
||||||
|
|
||||||
|
# checks the user's whitelist status and fetches the full gpg key password if possible
|
||||||
|
def whitelist_verify(_port, _username_ssh, _ip, _client_device_id, _identity):
|
||||||
|
try:
|
||||||
|
run(('gpg', '--pinentry-mode', 'cancel', '-qd', '--output', '/dev/null',
|
||||||
|
f"{home}/.config/sshyp/lock.gpg"), stderr=DEVNULL, check=True)
|
||||||
|
return False
|
||||||
|
except CalledProcessError:
|
||||||
|
_i, _full_password = 0, ''
|
||||||
|
_server_whitelist = run(('ssh', '-i', _identity, '-p', _port, f"{_username_ssh}@{_ip}",
|
||||||
|
f'python3 -c \'from os import listdir; print(*listdir("/home/{_username_ssh}'
|
||||||
|
f'/.config/sshyp/whitelist"))\''), stdout=PIPE, text=True).stdout.rstrip().split()
|
||||||
|
for _device_id in _server_whitelist:
|
||||||
|
if _device_id == _client_device_id:
|
||||||
|
from getpass import getpass
|
||||||
|
_quick_unlock_password = getpass(prompt='\nquick-unlock pin: ')
|
||||||
|
_quick_unlock_password_excluded = \
|
||||||
|
run(('ssh', '-i', _identity, '-p', _port, f"{_username_ssh}@{_ip}",
|
||||||
|
f"gpg --pinentry-mode loopback --passphrase '{_quick_unlock_password}' "
|
||||||
|
f"-qd ~/.config/sshyp/excluded.gpg"), stdout=PIPE, text=True).stdout.rstrip()
|
||||||
|
while _i < len(_quick_unlock_password_excluded):
|
||||||
|
try:
|
||||||
|
_full_password += _quick_unlock_password_excluded[_i]
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
_full_password += _quick_unlock_password[_i]
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
_i += 1
|
||||||
|
break
|
||||||
|
return _full_password
|
||||||
|
|
||||||
|
|
||||||
|
# returns True if expected and reality align, otherwise error
|
||||||
|
def target_exists_check(_target_name, _expected_presence):
|
||||||
|
if isfile(f"{directory}{_target_name}.gpg") or isdir(f"{directory}{_target_name}"):
|
||||||
|
if _expected_presence:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print(f"\n\u001b[38;5;9merror: (/{_target_name}) already exists\u001b[0m\n")
|
||||||
|
s_exit(3)
|
||||||
|
else:
|
||||||
|
if not _expected_presence:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print(f"\n\u001b[38;5;9merror: (/{_target_name}) does not exist\u001b[0m\n")
|
||||||
|
s_exit(2)
|
||||||
|
|
||||||
|
|
||||||
|
# returns target type (entry == True, folder == False, null == None), optional errors
|
||||||
|
def target_type_check(_target_name, _expected_type=True, _error=False):
|
||||||
|
if isfile(f"{directory}{_target_name}.gpg"):
|
||||||
|
if _error and not _expected_type:
|
||||||
|
print(f"\n\u001b[38;5;9merror: (/{_target_name}) is an entry\u001b[0m\n")
|
||||||
|
s_exit(2)
|
||||||
|
return True
|
||||||
|
elif isdir(f"{directory}{_target_name}"):
|
||||||
|
if _error and _expected_type:
|
||||||
|
print(f"\n\u001b[38;5;9merror: (/{_target_name}) is a folder\u001b[0m\n")
|
||||||
|
s_exit(2)
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
print(f"\n\u001b[38;5;9merror: (/{_target_name}) does not exist\u001b[0m\n")
|
||||||
|
s_exit(2)
|
||||||
|
|
||||||
|
|
||||||
|
# ensures an edited entry is optimized for best compatibility
|
||||||
|
def line_edit(_lines, _edit_data, _edit_line):
|
||||||
|
# ensure enough lines are present for edited field
|
||||||
|
while len(_lines) < _edit_line + 1:
|
||||||
|
_lines.append('')
|
||||||
|
# write the edited field
|
||||||
|
_lines[_edit_line] = _edit_data.rstrip()
|
||||||
|
return _lines
|
||||||
|
|
||||||
|
|
||||||
|
# attempts to connect to the user's server via ssh to register the device for syncing
|
||||||
|
def copy_id_check(_port, _username_ssh, _ip, _client_device_id, _identity, _sshyp_data):
|
||||||
|
from stweak import write_config
|
||||||
|
if not _sshyp_data.has_section('CLIENT-ONLINE'):
|
||||||
|
_sshyp_data.add_section('CLIENT-ONLINE')
|
||||||
|
try:
|
||||||
|
run(('ssh', '-o', 'ConnectTimeout=3', '-i', _identity, '-p', _port, f"{_username_ssh}@{_ip}",
|
||||||
|
f'python3 -c \'from pathlib import Path; Path("/home/{_username_ssh}/.config/sshyp/devices/'
|
||||||
|
f'{_client_device_id}").touch(mode=0o400, exist_ok=True)\''), stderr=DEVNULL, check=True)
|
||||||
|
except CalledProcessError:
|
||||||
|
print(f'\n\u001b[38;5;9mwarning: ssh connection could not be made - ensure the public key ({_identity}) is '
|
||||||
|
'registered on the remote server and that the entered ip, port, and username are correct\n\nsyncing '
|
||||||
|
'functionality will be disabled until this is addressed\u001b[0m\n')
|
||||||
|
_sshyp_data.set('CLIENT-ONLINE', 'ssh_error', 'true')
|
||||||
|
write_config(_sshyp_data)
|
||||||
|
return True
|
||||||
|
_sshyp_data.set('CLIENT-ONLINE', 'ssh_error', 'false')
|
||||||
|
write_config(_sshyp_data)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# ARGUMENT-SPECIFIC FUNCTIONS
|
||||||
|
|
||||||
|
# prints help text based on argument
|
||||||
|
def print_info():
|
||||||
|
if arguments[0] in ('version', '-v'):
|
||||||
|
_blank = '\u001b[38;5;7;48;5;8m\\\u001b[38;5;15;48;5;15m' + 55*' ' + '\u001b[38;5;7;48;5;8m/\u001b[0m'
|
||||||
|
_border = '\u001b[38;5;7;48;5;8m' + 14*'<>' + '-' + 14*'<>' + '\u001b[0m\n'
|
||||||
|
print(f"""\nsshyp is a simple, self-hosted, sftp-synchronized\npassword manager for unix(-like) systems\n
|
||||||
|
{9*' '}..{15*' '}\u001b[38;5;12m♥♥ \u001b[38;5;9m♥♥\u001b[0m{15*' '}..
|
||||||
|
{8*' '}/()\\''.''.{7*' '}\u001b[38;5;12m♥♥♥\u001b[0m♥♥♥♥\u001b[0m{7*' '}.''.''/()\\{3*' '}_)
|
||||||
|
{5*' '}_.{3*' '}:{7*' '}*{7*' '}\u001b[38;5;9m♥♥♥♥♥\u001b[0m{7*' '}*{7*' '}:{3*' '}<[◎]|_|=
|
||||||
|
}}-}}-*]{4*' '}`..'..'{9*' '}\u001b[0m♥♥♥\u001b[0m{9*' '}`..'..'{6*' '}|
|
||||||
|
{4*' '}◎-◎{4*' '}//{3*' '}\\\\{10*' '}\u001b[38;5;9m♥\u001b[0m{10*' '}//{3*' '}\\\\{5*' '}/|\\""")
|
||||||
|
print(f"{_border}{_blank}\n\u001b[38;5;7;48;5;8m\\\u001b[38;5;15;48;5;15m{18*' '}\u001b[38;5;15;48;5;8msshyp "
|
||||||
|
f"version 1.5.1\u001b[38;5;15;48;5;15m{18*' '}\u001b[38;5;7;48;5;8m/\u001b[0m")
|
||||||
|
print(f"\u001b[38;5;7;48;5;8m\\\u001b[38;5;15;48;5;15m{14*' '}\u001b[38;5;15;48;5;8mthe fortified flock"
|
||||||
|
f" update\u001b[38;5;15;48;5;15m{15*' '}\u001b[38;5;7;48;5;8m/\u001b[0m\n{_blank}")
|
||||||
|
print(f"\u001b[38;5;7;48;5;8m\\\u001b[38;5;15;48;5;15m{9*' '}\u001b[38;5;15;48;5;8mcopyright 2021-2023 ",
|
||||||
|
f"randall winkhart\u001b[38;5;15;48;5;15m{9*' '}\u001b[38;5;7;48;5;8m/\u001b[0m")
|
||||||
|
print(f"{_blank}\n{_border}\nsee https://github.com/rwinkhart/sshyp for more information\n")
|
||||||
|
elif arguments[0] == 'license':
|
||||||
|
print('\nThis program is free software: you can redistribute it and/or modify it under the terms\nof version 3 '
|
||||||
|
'(only) of the GNU General Public License as published by the Free Software Foundation.\n\nThis program '
|
||||||
|
'is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\nwithout even the implied '
|
||||||
|
'warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\nSee the GNU General Public License for'
|
||||||
|
' more details.\n\nhttps://opensource.org/licenses/GPL-3.0\n')
|
||||||
|
elif arguments[0] == 'add' and device_type == 'client':
|
||||||
|
print(f"""\n\u001b[1musage:\u001b[0m sshyp /<entry name> add <option>\u001b[0m\n
|
||||||
|
\u001b[1moptions:\u001b[0m
|
||||||
|
add:
|
||||||
|
password/-p{12*' '}add a password entry
|
||||||
|
note/-n{16*' '}add a note entry
|
||||||
|
folder/-f{14*' '}add a new folder for entries\n""")
|
||||||
|
elif arguments[0] == 'edit' and device_type == 'client':
|
||||||
|
print(f"""\n\u001b[1musage:\u001b[0m sshyp /<entry name> edit <option>\u001b[0m\n
|
||||||
|
\u001b[1moptions:\u001b[0m
|
||||||
|
edit:
|
||||||
|
rename/relocate/-r{5*' '}rename or relocate an entry
|
||||||
|
username/-u{12*' '}change the username of an entry
|
||||||
|
password/-p{12*' '}change the password of an entry
|
||||||
|
url/-l{17*' '}change the url attached to an entry
|
||||||
|
note/-n{16*' '}change the note attached to an entry\n""")
|
||||||
|
elif arguments[0] == 'copy' and device_type == 'client':
|
||||||
|
print(f"""\n\u001b[1musage:\u001b[0m sshyp /<entry name> copy <option>\u001b[0m\n
|
||||||
|
\u001b[1moptions:\u001b[0m
|
||||||
|
copy:
|
||||||
|
username/-u{12*' '}copy the username of an entry to your clipboard
|
||||||
|
password/-p{12*' '}copy the password of an entry to your clipboard
|
||||||
|
url/-l{17*' '}copy the url of an entry to your clipboard
|
||||||
|
note/-n{16*' '}copy the note of an entry to your clipboard\n""")
|
||||||
|
elif arguments[0] == 'gen' and device_type == 'client':
|
||||||
|
print(f"""\n\u001b[1musage:\u001b[0m sshyp /<entry name> gen [option]\u001b[0m\n
|
||||||
|
\u001b[1moptions:\u001b[0m
|
||||||
|
gen:
|
||||||
|
update/-u{14*' '}generate a password for an existing entry\n""")
|
||||||
|
else:
|
||||||
|
print("\n\u001b[1msshyp ", "copyright (c) 2021-2023 ", """randall winkhart\u001b[0m
|
||||||
|
this is free software, and you are welcome to redistribute it under certain conditions;
|
||||||
|
this program comes with absolutely no warranty; type 'sshyp license' for details""")
|
||||||
|
if device_type == 'client':
|
||||||
|
print(f"""\n\u001b[1musage:\u001b[0m sshyp [/<entry name> [argument] [option]] | [argument]\n
|
||||||
|
\u001b[1marguments:\u001b[0m
|
||||||
|
help/-h{17*' '}bring up this menu
|
||||||
|
version/-v{14*' '}display sshyp version info
|
||||||
|
init{20*' '}set up sshyp
|
||||||
|
tweak{19*' '}change configuration options/manage extensions and updates
|
||||||
|
add{21*' '}add an entry
|
||||||
|
gen{21*' '}generate a new password
|
||||||
|
edit{20*' '}edit an existing entry
|
||||||
|
copy{20*' '}copy details of an entry to your clipboard
|
||||||
|
shear{19*' '}delete an existing entry
|
||||||
|
sync{20*' '}manually sync the entry directory via sshync
|
||||||
|
\n\u001b[1moptions:\u001b[0m
|
||||||
|
add:
|
||||||
|
password/-p{12*' '}add a password entry
|
||||||
|
note/-n{16*' '}add a note entry
|
||||||
|
folder/-f{14*' '}add a new folder for entries
|
||||||
|
edit:
|
||||||
|
rename/relocate/-r{5*' '}rename or relocate an entry
|
||||||
|
username/-u{12*' '}change the username of an entry
|
||||||
|
password/-p{12*' '}change the password of an entry
|
||||||
|
url/-l{17*' '}change the url attached to an entry
|
||||||
|
note/-n{16*' '}change the note attached to an entry
|
||||||
|
copy:
|
||||||
|
username/-u{12*' '}copy the username of an entry to your clipboard
|
||||||
|
password/-p{12*' '}copy the password of an entry to your clipboard
|
||||||
|
url/-l{17*' '}copy the url of an entry to your clipboard
|
||||||
|
note/-n{16*' '}copy the note of an entry to your clipboard
|
||||||
|
gen:
|
||||||
|
update/-u{14*' '}generate a password for an existing entry
|
||||||
|
\n\u001b[1mtip 1:\u001b[0m you can quickly read an entry with 'sshyp /<entry name>'
|
||||||
|
\u001b[1mtip 2:\u001b[0m type 'sshyp' to view a list of saved entries\n""")
|
||||||
|
# PORT START HELP-SERVER
|
||||||
|
else:
|
||||||
|
print(f"""\n\u001b[1musage:\u001b[0m sshyp <argument>\n
|
||||||
|
\u001b[1marguments:\u001b[0m
|
||||||
|
help/-h{17*' '}bring up this menu
|
||||||
|
version/-v{14*' '}display sshyp version info
|
||||||
|
init{20*' '}set up sshyp
|
||||||
|
tweak{19*' '}change configuration options/manage extensions and updates\n""")
|
||||||
|
# PORT END HELP-SERVER
|
||||||
|
|
||||||
|
|
||||||
|
# shortcut to quickly read an entry
|
||||||
|
def read_shortcut():
|
||||||
|
target_type_check(entry_name, True, True)
|
||||||
|
entry_reader(decrypt(directory + entry_name, _quick_verify=quick_unlock_enabled))
|
||||||
|
|
||||||
|
|
||||||
|
# calls sshync to sync changes to the user's server
|
||||||
|
def sync(_start_text=''):
|
||||||
|
print(f"{_start_text}syncing entries with the server device...\n")
|
||||||
|
# set permissions before uploading
|
||||||
|
for _root, _dirs, _files in walk(f"{home}/.local/share/sshyp"):
|
||||||
|
for _path in _root.splitlines():
|
||||||
|
chmod(_path, 0o700)
|
||||||
|
for _file in _files:
|
||||||
|
chmod(_root + '/' + _file, 0o600)
|
||||||
|
run_profile(f"{home}/.config/sshyp/sshyp.ini", silent_sync)
|
||||||
|
|
||||||
|
|
||||||
|
# adds a new entry
|
||||||
|
def add_entry():
|
||||||
|
# make sure the add target does not already exist
|
||||||
|
target_exists_check(entry_name, False)
|
||||||
|
|
||||||
|
# note entry
|
||||||
|
if arguments[2] in ('note', '-n'):
|
||||||
|
_password, _username, _url, _note = '', '', '', edit_note([])
|
||||||
|
else:
|
||||||
|
# password entry
|
||||||
|
from getpass import getpass
|
||||||
|
_username = str(input('\nusername: '))
|
||||||
|
_password = str(getpass(prompt='\npassword: '))
|
||||||
|
_url = str(input('\nurl: '))
|
||||||
|
if input('\nadd a note to this entry? (y/N) ').lower() == 'y':
|
||||||
|
_note = edit_note([])
|
||||||
|
else:
|
||||||
|
_note = ''
|
||||||
|
print('\n\u001b[1mentry preview:\u001b[0m')
|
||||||
|
entry_reader([_password, _username, _url, _note])
|
||||||
|
encrypt([_password, _username, _url, _note], directory + entry_name, gpg_id)
|
||||||
|
|
||||||
|
|
||||||
|
# creates a new folder
|
||||||
|
def add_folder():
|
||||||
|
Path(directory + entry_name).mkdir(mode=0o700, parents=True, exist_ok=True)
|
||||||
|
if not ssh_error:
|
||||||
|
run(('ssh', '-i', identity, '-p', port, f"{username_ssh}@{ip}",
|
||||||
|
f'python3 -c \'from pathlib import Path; Path("{directory_ssh}{entry_name}")'
|
||||||
|
f'.mkdir(mode=0o700, parents=True, exist_ok=True)\''))
|
||||||
|
|
||||||
|
|
||||||
|
# renames an entry or folder
|
||||||
|
def rename():
|
||||||
|
from shutil import copy
|
||||||
|
|
||||||
|
# check if the renaming target is an entry (file) or a folder
|
||||||
|
_file = target_type_check(entry_name)
|
||||||
|
|
||||||
|
# collect the new name for the target from user input
|
||||||
|
_new_name = str(input('new name: ')).strip('/')
|
||||||
|
|
||||||
|
# check if the new name already exists
|
||||||
|
target_exists_check(_new_name, False)
|
||||||
|
|
||||||
|
# if renaming a file
|
||||||
|
if _file:
|
||||||
|
if not ssh_error:
|
||||||
|
copy(f"{directory}{entry_name}.gpg", f"{directory}{_new_name}.gpg")
|
||||||
|
else:
|
||||||
|
move(f"{directory}{entry_name}.gpg", f"{directory}{_new_name}.gpg")
|
||||||
|
else:
|
||||||
|
|
||||||
|
# if renaming a folder
|
||||||
|
if not ssh_error:
|
||||||
|
Path(f"{directory}{_new_name}").mkdir(mode=0o700, parents=True, exist_ok=True)
|
||||||
|
run(('ssh', '-i', identity, '-p', port, f"{username_ssh}@{ip}",
|
||||||
|
f'python3 -c \'from pathlib import Path; Path("{directory_ssh}{entry_name}")'
|
||||||
|
f'.rename(Path("{directory_ssh}{_new_name}"))\''))
|
||||||
|
else:
|
||||||
|
move(f"{directory}{entry_name}", f"{directory}{_new_name}")
|
||||||
|
if not ssh_error:
|
||||||
|
run(('ssh', '-i', identity, '-p', port, f"{username_ssh}@{ip}",
|
||||||
|
f'cd /usr/lib/sshyp; python3 -c \'from sshync import delete; delete("{entry_name}", "remotely", True)\''))
|
||||||
|
|
||||||
|
|
||||||
|
# edits the contents of an entry
|
||||||
|
def edit():
|
||||||
|
# set to avoid PEP8 warnings
|
||||||
|
_detail, _edit_line = None, None
|
||||||
|
|
||||||
|
# ensure the edit target is an entry
|
||||||
|
target_type_check(entry_name, True, True)
|
||||||
|
|
||||||
|
if arguments[2] in ('username', '-u'):
|
||||||
|
_detail, _edit_line = str(input('\nusername: ')), 1
|
||||||
|
elif arguments[2] in ('password', '-p'):
|
||||||
|
from getpass import getpass
|
||||||
|
_detail, _edit_line = str(getpass(prompt='\npassword: ')), 0
|
||||||
|
elif arguments[2] in ('url', '-l'):
|
||||||
|
_detail, _edit_line = str(input('\nurl: ')), 2
|
||||||
|
if arguments[2] in ('note', '-n'):
|
||||||
|
_old_lines = decrypt(directory + entry_name, _quick_verify=quick_unlock_enabled)
|
||||||
|
_new_lines = _old_lines[0:3] + edit_note(_old_lines[3:], True).split('\n')
|
||||||
|
else:
|
||||||
|
_new_lines = line_edit(decrypt(directory + entry_name, _quick_verify=quick_unlock_enabled), _detail,
|
||||||
|
_edit_line)
|
||||||
|
print('\n\u001b[1mentry preview:\u001b[0m')
|
||||||
|
entry_reader(_new_lines)
|
||||||
|
encrypt(_new_lines, directory + entry_name, gpg_id)
|
||||||
|
|
||||||
|
|
||||||
|
# generates a password for a new or an existing entry
|
||||||
|
def gen():
|
||||||
|
# set to avoid PEP8 warnings
|
||||||
|
_username, _url, _notes = None, None, None
|
||||||
|
# gen update
|
||||||
|
if arg_count == 3 and arguments[2] in ('update', '-u'):
|
||||||
|
# ensure the gen update target is an entry
|
||||||
|
target_type_check(entry_name, True, True)
|
||||||
|
_new_lines = line_edit(decrypt(directory + entry_name, _quick_verify=quick_unlock_enabled), pass_gen(), 0)
|
||||||
|
# gen
|
||||||
|
else:
|
||||||
|
# make sure the gen target does not already exist
|
||||||
|
target_exists_check(entry_name, False)
|
||||||
|
_username = str(input('\nusername: '))
|
||||||
|
_password = pass_gen()
|
||||||
|
_url = str(input('\nurl: '))
|
||||||
|
if input('\nadd a note to this entry? (y/N) ').lower() == 'y':
|
||||||
|
_note = edit_note([])
|
||||||
|
else:
|
||||||
|
_note = ''
|
||||||
|
_new_lines = [_password, _username, _url, _note]
|
||||||
|
print('\n\u001b[1mentry preview:\u001b[0m')
|
||||||
|
entry_reader(_new_lines)
|
||||||
|
encrypt(_new_lines, directory + entry_name, gpg_id)
|
||||||
|
|
||||||
|
|
||||||
|
# copies a specified field of an entry to the clipboard
|
||||||
|
def copy_data():
|
||||||
|
from hashlib import sha512
|
||||||
|
from subprocess import Popen
|
||||||
|
# ensure the copy target is an entry
|
||||||
|
target_type_check(entry_name, True, True)
|
||||||
|
_index = 0
|
||||||
|
if arguments[2] in ('username', '-u'):
|
||||||
|
_index = 1
|
||||||
|
elif arguments[2] in ('password', '-p'):
|
||||||
|
_index = 0
|
||||||
|
elif arguments[2] in ('url', '-l'):
|
||||||
|
_index = 2
|
||||||
|
elif arguments[2] in ('note', '-n'):
|
||||||
|
_index = 3
|
||||||
|
_copy_subject = decrypt(directory + entry_name, _quick_verify=quick_unlock_enabled)[_index]
|
||||||
|
# ensure field is not blank
|
||||||
|
if _copy_subject == '':
|
||||||
|
raise IndexError
|
||||||
|
|
||||||
|
# store hashed _copy_subject for later comparison
|
||||||
|
_hash = sha512()
|
||||||
|
_hash.update(_copy_subject.encode('utf-8'))
|
||||||
|
|
||||||
|
# PORT START CLIPBOARD
|
||||||
|
# WSL clipboard detection
|
||||||
|
if 'WSL_DISTRO_NAME' in environ:
|
||||||
|
run(('powershell.exe', '-c', "Set-Clipboard '" + _copy_subject.replace("'", "''") + "'"))
|
||||||
|
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'wsl'), stdout=DEVNULL,
|
||||||
|
stderr=DEVNULL)
|
||||||
|
# Wayland clipboard detection
|
||||||
|
elif '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'))
|
||||||
|
# Haiku clipboard detection
|
||||||
|
elif uname()[0] == 'Haiku':
|
||||||
|
run(('clipboard', '-c', _copy_subject))
|
||||||
|
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'haiku'))
|
||||||
|
# MacOS clipboard detection
|
||||||
|
elif uname()[0] == 'Darwin':
|
||||||
|
run('pbcopy', stdin=Popen(('printf', '%b', _copy_subject.replace('\\', '\\\\')), stdout=PIPE).stdout)
|
||||||
|
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'mac'))
|
||||||
|
# Termux (Android) clipboard detection
|
||||||
|
elif isdir("/data/data/com.termux"):
|
||||||
|
run(('termux-clipboard-set', _copy_subject))
|
||||||
|
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'termux'))
|
||||||
|
# X11 clipboard detection
|
||||||
|
elif 'DISPLAY' in environ:
|
||||||
|
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'))
|
||||||
|
else:
|
||||||
|
print('\n\u001b[38;5;9merror: clipboard tool could not be determined\n\nnote that the clipboard does not '
|
||||||
|
'function in a raw tty\u001b[0m\n')
|
||||||
|
# PORT END CLIPBOARD
|
||||||
|
|
||||||
|
|
||||||
|
# deletes an entry from the server and flags it for local deletion on sync
|
||||||
|
def remove_data():
|
||||||
|
decrypt(None, _quick_verify=quick_unlock_enabled)
|
||||||
|
if not ssh_error:
|
||||||
|
run(('ssh', '-i', identity, '-p', port, f"{username_ssh}@{ip}",
|
||||||
|
f'cd /usr/lib/sshyp; python3 -c \'from sshync import delete; delete("{entry_name}", "remotely", True)\''))
|
||||||
|
else:
|
||||||
|
offline_delete(entry_name, 'locally', silent_sync)
|
||||||
|
|
||||||
|
|
||||||
|
# checks extension config files for matches to argument, runs extensions
|
||||||
|
def extension_runner():
|
||||||
|
_output_com, _extension_dir = None, realpath(__file__).rsplit('/', 1)[0] + '/extensions/'
|
||||||
|
if isdir(_extension_dir):
|
||||||
|
for _extension in listdir(_extension_dir):
|
||||||
|
_extension_config = ConfigParser(interpolation=None)
|
||||||
|
_extension_config.read(_extension_dir + _extension)
|
||||||
|
_input_com = _extension_config.get('config', 'input').split()
|
||||||
|
if _input_com == arguments[arg_start:]:
|
||||||
|
_output_com = _extension_config.get('config', 'output').split()
|
||||||
|
if arg_start == 1:
|
||||||
|
_output_com.append(arguments[0])
|
||||||
|
if _output_com is not None:
|
||||||
|
run(_output_com)
|
||||||
|
else:
|
||||||
|
print_info()
|
||||||
|
else:
|
||||||
|
print_info()
|
||||||
|
s_exit()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
# set default states
|
||||||
|
ssh_error, success_flag, sync_flag, silent_sync, pass_show = False, False, False, False, False
|
||||||
|
|
||||||
|
# set to avoid PEP8 warnings
|
||||||
|
arg_start, device_type, offline_mode_enabled = None, None, None
|
||||||
|
|
||||||
|
# retrieve typed argument
|
||||||
|
arguments = argv[1:]
|
||||||
|
arg_count = len(arguments)
|
||||||
|
|
||||||
|
# check if an entry name is correctly supplied
|
||||||
|
if arg_count < 1 or (arg_count > 0 and arguments[0] != 'init'):
|
||||||
|
if arg_count > 0 and arguments[0].startswith('/'):
|
||||||
|
arg_start = 1
|
||||||
|
entry_name = arguments[0].strip('/')
|
||||||
|
# determine whether to show passwords in entry previews
|
||||||
|
if arg_count > 1 and arguments[arg_count-1] in ('--show', '-s'):
|
||||||
|
arguments.pop()
|
||||||
|
arg_count -= 1
|
||||||
|
pass_show = True
|
||||||
|
else:
|
||||||
|
arg_start = 0
|
||||||
|
|
||||||
|
# import saved userdata
|
||||||
|
try:
|
||||||
|
sshyp_data = ConfigParser(interpolation=None)
|
||||||
|
sshyp_data.read(f"{home}/.config/sshyp/sshyp.ini")
|
||||||
|
device_type = sshyp_data.get('GENERAL', 'device_type')
|
||||||
|
if device_type == 'client':
|
||||||
|
directory = f"{home}/.local/share/sshyp/"
|
||||||
|
gpg_id = sshyp_data.get('CLIENT-GENERAL', 'gpg_id')
|
||||||
|
editor = sshyp_data.get('CLIENT-GENERAL', 'text_editor')
|
||||||
|
offline_mode_enabled = sshyp_data.getboolean('CLIENT-GENERAL', 'offline_mode_enabled')
|
||||||
|
if offline_mode_enabled:
|
||||||
|
ssh_error = True
|
||||||
|
quick_unlock_enabled = False
|
||||||
|
else:
|
||||||
|
quick_unlock_enabled = sshyp_data.getboolean('CLIENT-ONLINE', 'quick_unlock_enabled')
|
||||||
|
username_ssh = sshyp_data.get('SSHYNC', 'user')
|
||||||
|
ip = sshyp_data.get('SSHYNC', 'ip')
|
||||||
|
port = sshyp_data.get('SSHYNC', 'port')
|
||||||
|
directory_ssh = sshyp_data.get('SSHYNC', 'remote_dir')
|
||||||
|
identity = sshyp_data.get('SSHYNC', 'identity_file')
|
||||||
|
client_device_id = listdir(f"{home}/.config/sshyp/devices")[0]
|
||||||
|
ssh_error = sshyp_data.getboolean('CLIENT-ONLINE', 'ssh_error')
|
||||||
|
if ssh_error:
|
||||||
|
ssh_error = copy_id_check(port, username_ssh, ip, client_device_id, identity, sshyp_data)
|
||||||
|
except (FileNotFoundError, NoSectionError, NoOptionError):
|
||||||
|
print(f"\n{73*'!'}")
|
||||||
|
print("not all necessary configurations have been made - please run 'sshyp init'")
|
||||||
|
print(f"{73*'!'}\n")
|
||||||
|
s_exit(1)
|
||||||
|
else:
|
||||||
|
from stweak import wrapped_entry
|
||||||
|
wrapped_entry(False)
|
||||||
|
s_exit()
|
||||||
|
|
||||||
|
# run function based on arguments
|
||||||
|
if device_type == 'client':
|
||||||
|
if arg_count < 1:
|
||||||
|
entry_list_gen()
|
||||||
|
|
||||||
|
elif arg_count == 3 and arg_start == 1:
|
||||||
|
if arguments[1] == 'copy':
|
||||||
|
if arguments[2] in ('username', '-u', 'password', '-p', 'url', '-l', 'note', '-n'):
|
||||||
|
try:
|
||||||
|
success_flag = 1
|
||||||
|
copy_data()
|
||||||
|
except IndexError:
|
||||||
|
print('\n\u001b[38;5;9merror: field does not exist in entry\u001b[0m\n')
|
||||||
|
s_exit(2)
|
||||||
|
elif arguments[1] == 'add':
|
||||||
|
if arguments[2] in ('note', '-n', 'password', '-p'):
|
||||||
|
success_flag, sync_flag = True, True
|
||||||
|
add_entry()
|
||||||
|
elif arguments[2] in ('folder', '-f'):
|
||||||
|
success_flag = 1
|
||||||
|
add_folder()
|
||||||
|
elif arguments[1] == 'edit':
|
||||||
|
if arguments[2] in ('rename', 'relocate', '-r'):
|
||||||
|
success_flag, sync_flag, silent_sync = True, True, True
|
||||||
|
rename()
|
||||||
|
elif arguments[2] in ('username', '-u', 'password', '-p', 'url', '-l', 'note', '-n'):
|
||||||
|
success_flag, sync_flag = True, True
|
||||||
|
edit()
|
||||||
|
elif arguments[1] == 'gen' and arguments[2] in ('update', '-u'):
|
||||||
|
success_flag, sync_flag = True, True
|
||||||
|
gen()
|
||||||
|
|
||||||
|
elif arg_count == 2 and arg_start == 1:
|
||||||
|
if arguments[1] == 'gen':
|
||||||
|
success_flag, sync_flag = True, True
|
||||||
|
gen()
|
||||||
|
elif arguments[1] == 'shear':
|
||||||
|
success_flag = True
|
||||||
|
remove_data()
|
||||||
|
|
||||||
|
elif arg_count == 1:
|
||||||
|
if arg_start == 1:
|
||||||
|
success_flag = True
|
||||||
|
read_shortcut()
|
||||||
|
elif arguments[0] == 'tweak':
|
||||||
|
success_flag = True
|
||||||
|
from stweak import wrapped_entry
|
||||||
|
wrapped_entry(device_type)
|
||||||
|
|
||||||
|
if arg_count > 0 and success_flag == 0 and arguments[0] != 'sync':
|
||||||
|
if arguments[0] not in ('help', '-h', 'version', '-v', 'license'):
|
||||||
|
extension_runner()
|
||||||
|
else:
|
||||||
|
print_info()
|
||||||
|
elif not ssh_error:
|
||||||
|
if sync_flag:
|
||||||
|
sync()
|
||||||
|
elif (arg_count > 0 and arguments[0] == 'sync') or (arg_count > 1 and arguments[1] == 'shear'):
|
||||||
|
sync('\n')
|
||||||
|
elif arg_count > 0 and arguments[0] == 'sync' and offline_mode_enabled:
|
||||||
|
print("\n\u001b[38;5;9mwarning: sshyp is currently configured in offline mode - ssh synchronization is "
|
||||||
|
"disabled\u001b[0m\n")
|
||||||
|
|
||||||
|
# PORT START ARGS-SERVER
|
||||||
|
# server arguments
|
||||||
|
else:
|
||||||
|
if arg_count == 1 and arguments[0] == 'tweak':
|
||||||
|
success_flag = True
|
||||||
|
from stweak import wrapped_entry
|
||||||
|
wrapped_entry(device_type)
|
||||||
|
else:
|
||||||
|
if arg_count < 1:
|
||||||
|
arguments.append('help')
|
||||||
|
print_info()
|
||||||
|
# PORT END ARGS-SERVER
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print('\n')
|
||||||
+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
|
||||||
+417
-203
@@ -1,183 +1,360 @@
|
|||||||
#!/bin/bash
|
#!/bin/sh
|
||||||
|
|
||||||
# This script packages sshyp (from source) for various UNIX(-like) environments.
|
version=$(head -n1 extra/changelog-archive/2023 | cut -c8-)
|
||||||
# Dependencies (Arch Linux): dpkg (packaging for Debian/Termux), freebsd-pkg (packaging for FreeBSD)
|
if [ -z "$2" ]; then
|
||||||
# Dependencies (Fedora) (can only package for self): rpmdevtools
|
revision=1
|
||||||
# Dependencies (Alpine): xz (not part of a basic installation, needed for generic package creation and unpacking for abuild), alpine-sdk (required to build the .apk file from the generated APKBUILD), bash
|
else
|
||||||
# NOTE It is recommended to instead use the latest officially packaged and tagged release.
|
revision="$2"
|
||||||
|
|
||||||
echo -e '\nOptions (please enter the number only):'
|
|
||||||
echo -e '\nPackage Formats:\n\n1. Haiku\n2. Debian&Ubuntu Linux\n3. Fedora Linux\n4. FreeBSD\n5. Termux\n6. Generic (used for PKGBUILD/APKBUILD)'
|
|
||||||
echo -e '\nBuild Scripts:\n\n7. Arch Linux (PKGBUILD)\n8. Alpine Linux (APKBUILD)'
|
|
||||||
echo -e '\nOther:\n\n9. All (generates all distribution packages (excluding Haiku, Fedora, and Alpine, as these must be packaged on their respective distributions) and build scripts)\n'
|
|
||||||
read -n 1 -r -p "Distribution: " distro
|
|
||||||
|
|
||||||
echo -e '\n\nThe value entered in this field will only affect the version reported to the package manager. The latest source is used regardless.\n'
|
|
||||||
read -r -p "Version number: " version
|
|
||||||
|
|
||||||
echo -e '\nThe value entered in this field will only affect the revision number for build scripts.\n'
|
|
||||||
read -r -p "Revision number: " revision
|
|
||||||
|
|
||||||
if [ "$distro" == "7" ] || [ "$distro" == "8" ] || [ "$distro" == "9" ]; then
|
|
||||||
source='https://github.com/rwinkhart/sshyp/releases/download/v$pkgver/sshyp-$pkgver.tar.xz'
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
mkdir -p packages
|
mkdir -p port-jobs/working
|
||||||
|
|
||||||
if [ "$distro" == "1" ]; then
|
_create_generic_linux() {
|
||||||
echo -e '\nPackaging for Haiku...\n'
|
printf '\npackaging as generic (Linux)...\n'
|
||||||
mkdir -p packages/haikutemp/documentation/{man/man1,packages/sshyp}
|
mkdir -p output/linuxtemp/usr/bin \
|
||||||
echo "name sshyp
|
output/linuxtemp/usr/lib/sshyp/extensions \
|
||||||
version "$version"-"$revision"
|
output/linuxtemp/usr/share/man/man1 \
|
||||||
architecture any
|
output/linuxtemp/usr/share/bash-completion/completions \
|
||||||
summary \"A light-weight, self-hosted, synchronized password manager\"
|
output/linuxtemp/usr/share/zsh/functions/Completion/Unix
|
||||||
description \"sshyp is the only password-store compatible CLI password manager available for Haiku - it is also available on Linux/Android (via Termux) so that you can sync your entries across all of your devices.\"
|
# START PORT
|
||||||
packager \"Randall Winkhart <idgr at tutanota dot com>\"
|
cp lib/* port-jobs/working/
|
||||||
vendor \"Randall Winkhart\"
|
cd port-jobs
|
||||||
|
./CLIPTOOL.py LINUX
|
||||||
|
./CLIPBOARD.py LINUX
|
||||||
|
./UNAME.py LINUX
|
||||||
|
./COMMENTS.py ALL
|
||||||
|
./BLANKS.py
|
||||||
|
./TABS.sh TABS
|
||||||
|
cd ..
|
||||||
|
mv port-jobs/working/* output/linuxtemp/usr/lib/sshyp/
|
||||||
|
# END PORT
|
||||||
|
ln -s /usr/lib/sshyp/sshyp.py output/linuxtemp/usr/bin/sshyp
|
||||||
|
cp -r share output/linuxtemp/usr/
|
||||||
|
cp extra/completion.bash output/linuxtemp/usr/share/bash-completion/completions/sshyp
|
||||||
|
cp extra/completion.zsh output/linuxtemp/usr/share/zsh/functions/Completion/Unix/_sshyp
|
||||||
|
cp extra/manpage output/linuxtemp/usr/share/man/man1/sshyp.1
|
||||||
|
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 | 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"/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'
|
||||||
|
local decomp_target='GENERIC-LINUX-sshyp-"$pkgver".tar.xz'
|
||||||
|
fi
|
||||||
|
printf "# Maintainer: Randall Winkhart <idgr at tutanota dot com>
|
||||||
|
pkgname=sshyp
|
||||||
|
pkgver="$version"
|
||||||
|
pkgrel="$revision"
|
||||||
|
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)
|
||||||
|
optdepends=(
|
||||||
|
'wl-clipboard: wayland clipboard support'
|
||||||
|
'xclip: x11 clipboard support'
|
||||||
|
'bash-completion: bash completion support'
|
||||||
|
)
|
||||||
|
source=(\""$source"\")
|
||||||
|
sha512sums=('"$sha512"')
|
||||||
|
|
||||||
|
package() {
|
||||||
|
tar -xf $decomp_target -C "\"\${pkgdir}\""
|
||||||
|
}
|
||||||
|
" > output/PKGBUILD
|
||||||
|
printf '\nPKGBUILD generated\n\n'
|
||||||
|
} &&
|
||||||
|
|
||||||
|
_create_apkbuild() {
|
||||||
|
printf '\ngenerating APKBUILD...\n'
|
||||||
|
if [ "$1" = 'Deb' ]; then
|
||||||
|
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"
|
||||||
|
mkdir -p "$pkgdir/usr/share/zsh/site-functions"
|
||||||
|
mv "$pkgdir/usr/share/zsh/functions/Completion/Unix/_sshyp" "$pkgdir/usr/share/zsh/site-functions/_sshyp"
|
||||||
|
rm -rf "$pkgdir/usr/share/zsh/functions"'
|
||||||
|
else
|
||||||
|
local source='https://github.com/rwinkhart/sshyp/releases/download/v"$pkgver"/GENERIC-LINUX-sshyp-"$pkgver".tar.xz'
|
||||||
|
local sumsname='GENERIC-LINUX-sshyp-"$pkgver".tar.xz'
|
||||||
|
local processing='mkdir -p "$pkgdir"
|
||||||
|
mkdir -p "$srcdir/usr/share/zsh/site-functions"
|
||||||
|
mv "$srcdir/usr/share/zsh/functions/Completion/Unix/_sshyp" "$srcdir/usr/share/zsh/site-functions/_sshyp"
|
||||||
|
rm -rf "$srcdir/usr/share/zsh/functions"
|
||||||
|
cp -r "$srcdir/usr/" "$pkgdir"'
|
||||||
|
fi
|
||||||
|
printf "# Maintainer: Randall Winkhart <idgr@tutanota.com>
|
||||||
|
pkgname=sshyp
|
||||||
|
pkgver="$version"
|
||||||
|
pkgrel="$((revision-1))"
|
||||||
|
pkgdesc='A light-weight, self-hosted, synchronized password manager'
|
||||||
|
options=!check
|
||||||
|
url='https://github.com/rwinkhart/sshyp'
|
||||||
|
arch='noarch'
|
||||||
|
license='GPL-3.0-only'
|
||||||
|
depends='python3 gnupg openssh'
|
||||||
|
source=\""$source"\"
|
||||||
|
|
||||||
|
package() {
|
||||||
|
$processing
|
||||||
|
}
|
||||||
|
|
||||||
|
sha512sums=\"
|
||||||
|
"$sha512" "$sumsname"
|
||||||
|
\"
|
||||||
|
" > output/APKBUILD
|
||||||
|
printf '\nAPKBUILD generated\n\n'
|
||||||
|
} &&
|
||||||
|
|
||||||
|
_create_hpkg() {
|
||||||
|
printf '\npackaging for Haiku...\n'
|
||||||
|
mkdir -p output/haikutemp/bin \
|
||||||
|
output/haikutemp/lib/sshyp/extensions \
|
||||||
|
output/haikutemp/documentation/packages/sshyp \
|
||||||
|
output/haikutemp/documentation/man/man1 \
|
||||||
|
output/haikutemp/data/bash-completion/completions \
|
||||||
|
output/haikutemp/data/zsh/site-functions
|
||||||
|
printf "name sshyp_client
|
||||||
|
version "$version"-"$revision"
|
||||||
|
architecture any
|
||||||
|
summary \"A light-weight, self-hosted, synchronized password manager (client only)\"
|
||||||
|
description \"The fully-featured client for the sshyp password manager ported for Haiku. This is just a client: server hosting functionality is only available on Linux/BSD.\"
|
||||||
|
packager \"Randall Winkhart <idgr at tutanota dot com>\"
|
||||||
|
vendor \"Randall Winkhart\"
|
||||||
licenses {
|
licenses {
|
||||||
\"GNU GPL v3\"
|
\"GNU GPL v3\"
|
||||||
}
|
}
|
||||||
copyrights {
|
copyrights {
|
||||||
\"2021-2022 Randall Winkhart\"
|
\"2021-2023 Randall Winkhart\"
|
||||||
}
|
}
|
||||||
provides {
|
provides {
|
||||||
sshyp = "$version"
|
sshyp_client = "$version"
|
||||||
cmd:sshyp
|
cmd:sshyp
|
||||||
}
|
}
|
||||||
requires {
|
requires {
|
||||||
gnupg
|
gnupg
|
||||||
openssh
|
openssh
|
||||||
python3
|
python3.11
|
||||||
}
|
}
|
||||||
urls {
|
urls {
|
||||||
\"https://github.com/rwinkhart/sshyp\"
|
\"https://github.com/rwinkhart/sshyp\"
|
||||||
}
|
}
|
||||||
" > packages/haikutemp/.PackageInfo
|
" > output/haikutemp/.PackageInfo
|
||||||
cp -r bin packages/haikutemp/
|
# START PORT
|
||||||
sed -i '1 s/.*/#!\/bin\/env\ python3/' packages/haikutemp/bin/sshync.py
|
cp lib/* port-jobs/working/
|
||||||
sed -i '1 s/.*/#!\/bin\/env\ python3/' packages/haikutemp/bin/sshyp.py
|
cd port-jobs
|
||||||
sed -i '1 s/.*/#!\/bin\/env\ python3/' packages/haikutemp/bin/sshypRemote.py
|
./SHEBANG.sh
|
||||||
ln -s /bin/sshyp.py packages/haikutemp/bin/sshyp
|
./RMSERVER.py
|
||||||
cp -r share/doc/sshyp/ packages/haikutemp/documentation/packages/
|
./RMEXTMAN.py
|
||||||
cp -r share/licenses/sshyp/ packages/haikutemp/documentation/packages/
|
./CLIPTOOL.py
|
||||||
cp extra/manpage packages/haikutemp/documentation/man/man1/sshyp.1
|
./CLIPBOARD.py HAIKU
|
||||||
gzip packages/haikutemp/documentation/man/man1/sshyp.1
|
./UNAME.py TMP
|
||||||
cd packages/haikutemp
|
./COMMENTS.py ALL
|
||||||
package create -b sshyp-"$version"-"$revision"_all.hpkg
|
./BLANKS.py
|
||||||
package add sshyp-"$version"-"$revision"_all.hpkg bin documentation
|
./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/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
|
||||||
cd ../..
|
cd ../..
|
||||||
mv packages/haikutemp/sshyp-"$version"-"$revision"_all.hpkg packages/
|
mv output/haikutemp/HAIKU-sshyp_client-"$version"-"$revision"_all.hpkg output/
|
||||||
rm -rf packages/haikutemp
|
rm -rf output/haikutemp
|
||||||
echo -e "\nHaiku packaging complete.\n"
|
printf '\nHaiku packaging complete\n\n'
|
||||||
fi
|
} &&
|
||||||
|
|
||||||
if [ "$distro" == "2" ] || [ "$distro" == "9" ]; then
|
_create_deb() {
|
||||||
echo -e '\nPackaging for Debian...\n'
|
printf "\npackaging for $1...\n"
|
||||||
mkdir -p packages/debiantemp/sshyp_"$version"-"$revision"_all/{DEBIAN,usr/share/man/man1}
|
mkdir -p output/debiantemp/sshyp_"$version"-"$revision"_all/DEBIAN \
|
||||||
echo "Package: sshyp
|
output/debiantemp/sshyp_"$version"-"$revision"_all/usr/lib/sshyp/extensions \
|
||||||
|
output/debiantemp/sshyp_"$version"-"$revision"_all/usr/bin \
|
||||||
|
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
|
||||||
|
# START PORT
|
||||||
|
cp lib/* port-jobs/working/
|
||||||
|
cd port-jobs
|
||||||
|
if [ "$1" = 'Debian' ]; then
|
||||||
|
./CLIPTOOL.py LINUX
|
||||||
|
./CLIPBOARD.py LINUX
|
||||||
|
special=DEBIAN
|
||||||
|
else
|
||||||
|
./CLIPTOOL.py
|
||||||
|
./CLIPBOARD.py WSL
|
||||||
|
special=WSL-ONLY-DEBIAN
|
||||||
|
fi
|
||||||
|
./UNAME.py LINUX
|
||||||
|
./COMMENTS.py ALL
|
||||||
|
./BLANKS.py
|
||||||
|
./TABS.sh TABS
|
||||||
|
cd ..
|
||||||
|
mv port-jobs/working/* output/debiantemp/sshyp_"$version"-"$revision"_all/usr/lib/sshyp/
|
||||||
|
# END PORT
|
||||||
|
ln -s /usr/lib/sshyp/sshyp.py output/debiantemp/sshyp_"$version"-"$revision"_all/usr/bin/sshyp
|
||||||
|
cp -r share output/debiantemp/sshyp_"$version"-"$revision"_all/usr/
|
||||||
|
cp extra/completion.bash output/debiantemp/sshyp_"$version"-"$revision"_all/usr/share/bash-completion/completions/sshyp
|
||||||
|
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
|
Version: $version
|
||||||
Section: utils
|
Section: utils
|
||||||
Architecture: all
|
Architecture: all
|
||||||
Maintainer: Randall Winkhart <idgr at tutanota dot com>
|
Maintainer: Randall Winkhart <idgr at tutanota dot com>
|
||||||
Description: A light-weight, self-hosted, synchronized password manager
|
Description: A light-weight, self-hosted, synchronized password manager
|
||||||
Depends: python3, gnupg, openssh-client, xclip, wl-clipboard
|
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Installed-Size: 185
|
Installed-Size: $size
|
||||||
" > packages/debiantemp/sshyp_"$version"-"$revision"_all/DEBIAN/control
|
Depends: python3, gnupg, openssh-client
|
||||||
cp -r bin packages/debiantemp/sshyp_"$version"-"$revision"_all/usr/
|
" > output/debiantemp/sshyp_"$version"-"$revision"_all/DEBIAN/control
|
||||||
ln -s /usr/bin/sshyp.py packages/debiantemp/sshyp_"$version"-"$revision"_all/usr/bin/sshyp
|
if [ "$1" = 'Debian' ]; then
|
||||||
cp -r share packages/debiantemp/sshyp_"$version"-"$revision"_all/usr/
|
printf "Suggests: wl-clipboard, xclip, bash-completion, openssh-server
|
||||||
cp extra/manpage packages/debiantemp/sshyp_"$version"-"$revision"_all/usr/share/man/man1/sshyp.1
|
" >> output/debiantemp/sshyp_"$version"-"$revision"_all/DEBIAN/control
|
||||||
gzip packages/debiantemp/sshyp_"$version"-"$revision"_all/usr/share/man/man1/sshyp.1
|
else
|
||||||
dpkg-deb --build --root-owner-group packages/debiantemp/sshyp_"$version"-"$revision"_all/
|
printf "Suggests: bash-completion, openssh-server
|
||||||
mv packages/debiantemp/sshyp_"$version"-"$revision"_all.deb packages/
|
" >> output/debiantemp/sshyp_"$version"-"$revision"_all/DEBIAN/control
|
||||||
rm -rf packages/debiantemp
|
fi
|
||||||
echo -e "\nDebian packaging complete.\n"
|
dpkg-deb --build --root-owner-group -z6 -Sextreme -Zxz output/debiantemp/sshyp_"$version"-"$revision"_all/
|
||||||
fi
|
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 | cut -d' ' -f1)"
|
||||||
|
printf "\n$1 packaging complete\n\n"
|
||||||
|
} &&
|
||||||
|
|
||||||
if [ "$distro" == "5" ] || [ "$distro" == "9" ]; then
|
_create_termux() {
|
||||||
echo -e '\nPackaging for Termux...\n'
|
printf '\npackaging for Termux...\n'
|
||||||
mkdir -p packages/termuxtemp/sshyp_"$version"-"$revision"_all_termux/{data/data/com.termux/files/usr/share/man/man1,DEBIAN}
|
mkdir -p output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/DEBIAN \
|
||||||
echo "Package: sshyp
|
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
|
Version: $version
|
||||||
Section: utils
|
Section: utils
|
||||||
Architecture: all
|
Architecture: all
|
||||||
Maintainer: Randall Winkhart <idgr at tutanota dot com>
|
Maintainer: Randall Winkhart <idgr at tutanota dot com>
|
||||||
Description: A light-weight, self-hosted, synchronized password manager
|
Description: A light-weight, self-hosted, synchronized password manager
|
||||||
Depends: python, gnupg, openssh, termux-api, termux-am
|
Depends: python, gnupg, openssh, termux-api, termux-am
|
||||||
|
Suggests: bash-completion
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Installed-Size: 185
|
Installed-Size: $size
|
||||||
" > packages/termuxtemp/sshyp_"$version"-"$revision"_all_termux/DEBIAN/control
|
" > output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/DEBIAN/control
|
||||||
cp -r bin packages/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/
|
dpkg-deb --build --root-owner-group -z6 -Sextreme -Zxz output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux/
|
||||||
ln -s /data/data/com.termux/files/usr/bin/sshyp.py packages/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/bin/sshyp
|
mv output/termuxtemp/TERMUX-sshyp-client_"$version"-"$revision"_all_termux.deb output/
|
||||||
cp -r share packages/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/
|
rm -rf output/termuxtemp
|
||||||
cp extra/manpage packages/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/man/man1/sshyp.1
|
printf '\nTermux packaging complete\n\n'
|
||||||
gzip packages/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/man/man1/sshyp.1
|
} &&
|
||||||
dpkg-deb --build --root-owner-group packages/termuxtemp/sshyp_"$version"-"$revision"_all_termux/
|
|
||||||
mv packages/termuxtemp/sshyp_"$version"-"$revision"_all_termux.deb packages/
|
|
||||||
rm -rf packages/termuxtemp
|
|
||||||
echo -e "\nTermux packaging complete.\n"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$distro" == "3" ] || [ "$distro" == "4" ] || [ "$distro" == "6" ] || [ "$distro" == "7" ] || [ "$distro" == "8" ] || [ "$distro" == "9" ]; then
|
_create_rpm() {
|
||||||
echo -e '\nPackaging as generic...\n'
|
printf '\npackaging for Fedora...\n'
|
||||||
mkdir -p packages/generictemp/usr/share/man/man1
|
|
||||||
cp -r bin packages/generictemp/usr/
|
|
||||||
ln -s /usr/bin/sshyp.py packages/generictemp/usr/bin/sshyp
|
|
||||||
cp -r share packages/generictemp/usr/
|
|
||||||
cp extra/manpage packages/generictemp/usr/share/man/man1/sshyp.1
|
|
||||||
gzip packages/generictemp/usr/share/man/man1/sshyp.1
|
|
||||||
tar -C packages/generictemp -cvJf packages/sshyp-"$version".tar.xz usr/
|
|
||||||
rm -rf packages/generictemp
|
|
||||||
sha512="$(sha512sum packages/sshyp-"$version".tar.xz | awk '{print $1;}')"
|
|
||||||
echo -e "\nsha512 sum:\n$sha512"
|
|
||||||
echo -e "\nGeneric packaging complete.\n"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$distro" == "3" ]; then
|
|
||||||
echo -e '\nPackaging for Fedora...\n'
|
|
||||||
rm -rf ~/rpmbuild
|
rm -rf ~/rpmbuild
|
||||||
rpmdev-setuptree
|
rpmdev-setuptree
|
||||||
cp packages/sshyp-"$version".tar.xz ~/rpmbuild/SOURCES
|
mkdir -p output/fedoratemp/usr/bin \
|
||||||
echo "Name: sshyp
|
output/fedoratemp/usr/lib/sshyp/extensions \
|
||||||
|
output/fedoratemp/usr/share/man/man1 \
|
||||||
|
output/fedoratemp/usr/share/bash-completion/completions \
|
||||||
|
output/fedoratemp/usr/share/zsh/site-functions
|
||||||
|
printf "Name: sshyp
|
||||||
Version: "$version"
|
Version: "$version"
|
||||||
Release: "$revision"
|
Release: "$revision"
|
||||||
Summary: A light-weight, self-hosted, synchronized password manager
|
Summary: A light-weight, self-hosted, synchronized password manager
|
||||||
BuildArch: noarch
|
BuildArch: noarch
|
||||||
|
License: GPL-3.0-only
|
||||||
License: GPLv3
|
|
||||||
URL: https://github.com/rwinkhart/sshyp
|
URL: https://github.com/rwinkhart/sshyp
|
||||||
Source0: sshyp-"$version".tar.xz
|
Source0: GENERIC-FEDORA-sshyp-"$version".tar.xz
|
||||||
|
Requires: python gnupg openssh-clients
|
||||||
Requires: python gnupg openssh wl-clipboard
|
Recommends: wl-clipboard xclip bash-completion openssh-server
|
||||||
|
%%description
|
||||||
%description
|
|
||||||
sshyp is a password-store compatible CLI password manager available for UNIX(-like) systems - its primary goal is to make syncing passwords and notes across devices as easy as possible via CLI.
|
sshyp is a password-store compatible CLI password manager available for UNIX(-like) systems - its primary goal is to make syncing passwords and notes across devices as easy as possible via CLI.
|
||||||
|
%%install
|
||||||
%install
|
tar xf %%{_sourcedir}/GENERIC-FEDORA-sshyp-"$version".tar.xz -C %%{_sourcedir}
|
||||||
tar xf %{_sourcedir}/sshyp-"$version".tar.xz -C %{_sourcedir}
|
cp -r %%{_sourcedir}/usr %%{buildroot}
|
||||||
cp -r %{_sourcedir}/usr %{buildroot}
|
%%files
|
||||||
|
|
||||||
%files
|
|
||||||
/usr/bin/sshyp
|
/usr/bin/sshyp
|
||||||
/usr/bin/sshyp.py
|
/usr/lib/sshyp/sshyp.py
|
||||||
/usr/bin/sshync.py
|
/usr/lib/sshyp/sshync.py
|
||||||
/usr/bin/sshypRemote.py
|
/usr/lib/sshyp/stweak.py
|
||||||
%license /usr/share/licenses/sshyp/license
|
/usr/lib/sshyp/extensions/
|
||||||
%doc /usr/share/doc/sshyp/changelog
|
/usr/share/bash-completion/completions/sshyp
|
||||||
%doc /usr/share/man/man1/sshyp.1.gz
|
/usr/share/zsh/site-functions/_sshyp
|
||||||
|
%%license /usr/share/licenses/sshyp/license
|
||||||
|
%%doc
|
||||||
|
/usr/share/man/man1/sshyp.1.gz
|
||||||
" > ~/rpmbuild/SPECS/sshyp.spec
|
" > ~/rpmbuild/SPECS/sshyp.spec
|
||||||
rpmbuild -bb ~/rpmbuild/SPECS/sshyp.spec
|
# START PORT
|
||||||
mv ~/rpmbuild/RPMS/noarch/* packages/
|
cp lib/* port-jobs/working/
|
||||||
rm -rf ~/rpmbuild
|
cd port-jobs
|
||||||
echo -e "\nFedora packaging complete.\n"
|
./CLIPTOOL.py LINUX
|
||||||
fi
|
./CLIPBOARD.py LINUX
|
||||||
|
./UNAME.py LINUX
|
||||||
|
./COMMENTS.py ALL
|
||||||
|
./BLANKS.py
|
||||||
|
./TABS.sh TABS
|
||||||
|
cd ..
|
||||||
|
mv port-jobs/working/* output/fedoratemp/usr/lib/sshyp/
|
||||||
|
# END PORT
|
||||||
|
ln -s /usr/lib/sshyp/sshyp.py output/fedoratemp/usr/bin/sshyp
|
||||||
|
cp -r share output/fedoratemp/usr/
|
||||||
|
cp extra/completion.bash output/fedoratemp/usr/share/bash-completion/completions/sshyp
|
||||||
|
cp extra/completion.zsh output/fedoratemp/usr/share/zsh/site-functions/_sshyp
|
||||||
|
cp extra/manpage output/fedoratemp/usr/share/man/man1/sshyp.1
|
||||||
|
gzip output/fedoratemp/usr/share/man/man1/sshyp.1
|
||||||
|
XZ_OPT=-e6 tar -C output/fedoratemp -cvJf output/GENERIC-FEDORA-sshyp-"$version".tar.xz usr/
|
||||||
|
rm -rf output/fedoratemp
|
||||||
|
cp output/GENERIC-FEDORA-sshyp-"$version".tar.xz ~/rpmbuild/SOURCES
|
||||||
|
rpmbuild -bb ~/rpmbuild/SPECS/sshyp.spec
|
||||||
|
mv ~/rpmbuild/RPMS/noarch/sshyp-"$version"-"$revision".noarch.rpm output/FEDORA-sshyp-"$version"-"$revision".noarch.rpm
|
||||||
|
rm -rf ~/rpmbuild
|
||||||
|
printf '\nFedora packaging complete\n\n'
|
||||||
|
} &&
|
||||||
|
|
||||||
if [ "$distro" == "4" ] || [ "$distro" == "9" ]; then
|
_create_freebsd_pkg() {
|
||||||
echo -e '\nPackaging for FreeBSD...\n'
|
printf '\npackaging for FreeBSD...\n'
|
||||||
mkdir -p packages/FreeBSDtemp/bin
|
mkdir -p output/freebsdtemp/usr/lib/sshyp/extensions \
|
||||||
tar xf packages/sshyp-"$version".tar.xz -C packages/FreeBSDtemp
|
output/freebsdtemp/usr/bin \
|
||||||
echo "name: sshyp
|
output/freebsdtemp/usr/share/man/man1 \
|
||||||
|
output/freebsdtemp/usr/local/share/bash-completion/completions \
|
||||||
|
output/freebsdtemp/usr/local/share/zsh/site-functions
|
||||||
|
printf "name: sshyp
|
||||||
version: \""$version"\"
|
version: \""$version"\"
|
||||||
abi = \"FreeBSD:13:*\";
|
abi = \"FreeBSD:13:*\";
|
||||||
arch = \"freebsd:13:*\";
|
arch = \"freebsd:13:*\";
|
||||||
@@ -188,76 +365,113 @@ maintainer: <idgr at tutanota dot com>
|
|||||||
www: https://github.com/rwinkhart/sshyp
|
www: https://github.com/rwinkhart/sshyp
|
||||||
prefix: /
|
prefix: /
|
||||||
\"deps\" : {
|
\"deps\" : {
|
||||||
\"python\" : {
|
\"python3\" : {
|
||||||
\"origin\" : \"lang/python\"
|
\"origin\" : \"lang/python3\"
|
||||||
},
|
},
|
||||||
\"gnupg\" : {
|
\"gnupg\" : {
|
||||||
\"origin\" : \"security/gnupg\"
|
\"origin\" : \"security/gnupg\"
|
||||||
},
|
},
|
||||||
\"xclip\" : {
|
|
||||||
\"origin\" : \"x11/xclip\"
|
|
||||||
},
|
|
||||||
\"wl-clipboard\" : {
|
|
||||||
\"origin\" : \"x11/wl-clipboard\"
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
" > packages/FreeBSDtemp/+MANIFEST
|
" > output/freebsdtemp/+MANIFEST
|
||||||
echo "/usr/bin/sshync.py
|
printf "/usr/bin/sshyp
|
||||||
/usr/bin/sshyp
|
/usr/lib/sshyp/sshyp.py
|
||||||
/usr/bin/sshyp.py
|
/usr/lib/sshyp/sshync.py
|
||||||
/usr/bin/sshypRemote.py
|
/usr/lib/sshyp/stweak.py
|
||||||
/usr/share/doc/sshyp/changelog
|
/usr/local/share/bash-completion/completions/sshyp
|
||||||
|
/usr/local/share/zsh/site-functions/_sshyp
|
||||||
/usr/share/licenses/sshyp/license
|
/usr/share/licenses/sshyp/license
|
||||||
/usr/share/man/man1/sshyp.1.gz
|
/usr/share/man/man1/sshyp.1.gz
|
||||||
" > packages/FreeBSDtemp/plist
|
@dir /usr/lib/sshyp/extensions/
|
||||||
pkg create -m packages/FreeBSDtemp/ -r packages/FreeBSDtemp/ -p packages/FreeBSDtemp/plist -o packages/
|
" > output/freebsdtemp/plist
|
||||||
rm -rf packages/FreeBSDtemp
|
# START PORT
|
||||||
echo -e "\nFreeBSD packaging complete.\n"
|
cp lib/* port-jobs/working/
|
||||||
fi
|
cd port-jobs
|
||||||
|
./CLIPTOOL.py LINUX
|
||||||
|
./CLIPBOARD.py BSD
|
||||||
|
./UNAME.py TMP
|
||||||
|
./COMMENTS.py ALL
|
||||||
|
./BLANKS.py
|
||||||
|
./TABS.sh TABS
|
||||||
|
cd ..
|
||||||
|
mv port-jobs/working/* output/freebsdtemp/usr/lib/sshyp/
|
||||||
|
# END PORT
|
||||||
|
ln -s /usr/lib/sshyp/sshyp.py output/freebsdtemp/usr/bin/sshyp
|
||||||
|
cp -r share output/freebsdtemp/usr/
|
||||||
|
cp extra/completion.bash output/freebsdtemp/usr/local/share/bash-completion/completions/sshyp
|
||||||
|
cp extra/completion.zsh output/freebsdtemp/usr/local/share/zsh/site-functions/_sshyp
|
||||||
|
cp extra/manpage output/freebsdtemp/usr/share/man/man1/sshyp.1
|
||||||
|
gzip output/freebsdtemp/usr/share/man/man1/sshyp.1
|
||||||
|
pkg create -m output/freebsdtemp/ -r output/freebsdtemp/ -p output/freebsdtemp/plist -o output/
|
||||||
|
mv output/sshyp-"$version".pkg output/FREEBSD-sshyp-"$version"-"$revision".pkg
|
||||||
|
rm -rf output/freebsdtemp
|
||||||
|
printf '\nFreeBSD packaging complete\n\n'
|
||||||
|
} &&
|
||||||
|
|
||||||
if [ "$distro" == "7" ] || [ "$distro" == "9" ]; then
|
case "$1" in
|
||||||
echo -e '\nGenerating PKGBUILD...'
|
pkgbuild)
|
||||||
echo "# Maintainer: Randall Winkhart <idgr at tutanota dot com>
|
_create_generic_linux
|
||||||
pkgname=sshyp
|
_create_pkgbuild Generic
|
||||||
pkgver="$version"
|
;;
|
||||||
pkgrel="$revision"
|
pkgbuild-deb)
|
||||||
pkgdesc='A light-weight, self-hosted, synchronized password manager'
|
_create_deb Debian
|
||||||
url='https://github.com/rwinkhart/sshyp'
|
_create_pkgbuild Deb
|
||||||
arch=('any')
|
;;
|
||||||
license=('GPL3')
|
apkbuild)
|
||||||
depends=(python gnupg openssh xclip wl-clipboard)
|
_create_generic_linux
|
||||||
source=(\""$source"\")
|
_create_apkbuild Generic
|
||||||
sha512sums=('"$sha512"')
|
;;
|
||||||
|
apkbuild-deb)
|
||||||
package() {
|
_create_deb Debian
|
||||||
tar xf sshyp-"\"\$pkgver\"".tar.xz -C "\"\${pkgdir}\""
|
_create_apkbuild Deb
|
||||||
}
|
;;
|
||||||
" > packages/PKGBUILD
|
haiku)
|
||||||
echo -e "\nPKGBUILD generated.\n"
|
_create_hpkg
|
||||||
fi
|
;;
|
||||||
|
debian)
|
||||||
if [ "$distro" == "8" ] || [ "$distro" == "9" ]; then
|
_create_deb Debian
|
||||||
echo -e '\nGenerating APKBUILD...'
|
;;
|
||||||
echo "# Maintainer: Randall Winkhart <idgr@tutanota.com>
|
wsl)
|
||||||
pkgname=sshyp
|
_create_deb WSL
|
||||||
pkgver="$version"
|
;;
|
||||||
pkgrel="$((revision-1))"
|
termux)
|
||||||
pkgdesc='A light-weight, self-hosted, synchronized password manager'
|
_create_termux
|
||||||
options=!check
|
;;
|
||||||
url='https://github.com/rwinkhart/sshyp'
|
fedora)
|
||||||
arch='noarch'
|
_create_rpm
|
||||||
license='GPL-3.0-or-later'
|
;;
|
||||||
depends='python3 gnupg openssh xclip wl-clipboard'
|
freebsd)
|
||||||
source=\""$source"\"
|
_create_freebsd_pkg
|
||||||
|
;;
|
||||||
package() {
|
buildable-arch)
|
||||||
mkdir -p "\"\$pkgdir\""
|
_create_generic_linux
|
||||||
cp -r "\"\$srcdir/usr/"\" "\"\$pkgdir\""
|
_create_pkgbuild Generic
|
||||||
}
|
_create_apkbuild Generic
|
||||||
|
case "$(pacman -Q dpkg)" in
|
||||||
sha512sums=\"
|
dpkg*)
|
||||||
"$sha512' 'sshyp-\"\$pkgver\".tar.xz"
|
_create_termux
|
||||||
\"
|
_create_deb WSL
|
||||||
" > packages/APKBUILD
|
_create_deb Debian
|
||||||
echo -e "\nAPKBUILD generated.\n"
|
;;
|
||||||
fi
|
esac
|
||||||
|
case "$(pacman -Q freebsd-pkg)" in
|
||||||
|
freebsd-pkg*)
|
||||||
|
_create_freebsd_pkg
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
buildable-arch-deb)
|
||||||
|
_create_termux
|
||||||
|
_create_deb WSL
|
||||||
|
_create_deb Debian
|
||||||
|
_create_pkgbuild Deb
|
||||||
|
_create_apkbuild Deb
|
||||||
|
case "$(pacman -Q freebsd-pkg)" in
|
||||||
|
freebsd-pkg*)
|
||||||
|
_create_freebsd_pkg
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
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
|
||||||
|
|||||||
Executable
+16
@@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from os import listdir
|
||||||
|
|
||||||
|
# loop through all Python files in the working directory
|
||||||
|
for filename in listdir('working'):
|
||||||
|
if filename.endswith('.py'):
|
||||||
|
filepath = 'working/' + filename
|
||||||
|
# read input file
|
||||||
|
with open(filepath, 'r') as file:
|
||||||
|
new_file = []
|
||||||
|
for line in file:
|
||||||
|
# remove blank lines
|
||||||
|
if line != '\n':
|
||||||
|
new_file.append(line)
|
||||||
|
# write the updated file contents
|
||||||
|
open(filepath, 'w').writelines(new_file)
|
||||||
Executable
+62
@@ -0,0 +1,62 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import re
|
||||||
|
from sys import argv, exit as s_exit
|
||||||
|
|
||||||
|
# read arguments
|
||||||
|
arguments, clip_replacement, clear_replacement = argv[1:], None, None
|
||||||
|
|
||||||
|
# define replacement text depending on arguments
|
||||||
|
if len(arguments) > 0:
|
||||||
|
if arguments[0] == 'WSL':
|
||||||
|
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':
|
||||||
|
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':
|
||||||
|
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':
|
||||||
|
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', '%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()
|
||||||
|
|
||||||
|
targets = (('CLIPBOARD', 'sshyp.py', '', '', clip_replacement),
|
||||||
|
('CLIPCLEAR', 'clipclear.py', '\n', '', clear_replacement))
|
||||||
|
|
||||||
|
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
+29
@@ -0,0 +1,29 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from os import listdir
|
||||||
|
from re import match as rematch
|
||||||
|
from sys import argv
|
||||||
|
|
||||||
|
# read arguments
|
||||||
|
arguments = argv[1:]
|
||||||
|
|
||||||
|
# loop through all Python files in the working directory
|
||||||
|
for filename in listdir('working'):
|
||||||
|
if filename.endswith('.py'):
|
||||||
|
filepath = 'working/' + filename
|
||||||
|
# read input file
|
||||||
|
with open(filepath, 'r') as file:
|
||||||
|
new_file = []
|
||||||
|
first_line = True
|
||||||
|
for line in file:
|
||||||
|
# determine whether to remove all or only PORT comments
|
||||||
|
if first_line:
|
||||||
|
new_file.append(line)
|
||||||
|
first_line = False
|
||||||
|
else:
|
||||||
|
if len(arguments) > 0 and arguments[0] == 'ALL':
|
||||||
|
if not rematch(r'^\s*#', line):
|
||||||
|
new_file.append(line)
|
||||||
|
elif not rematch(r'^\s*# PORT', line):
|
||||||
|
new_file.append(line)
|
||||||
|
# write the updated file contents
|
||||||
|
open(filepath, 'w').writelines(new_file)
|
||||||
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)
|
||||||
Executable
+18
@@ -0,0 +1,18 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import re
|
||||||
|
|
||||||
|
devtype_replacement = """_install_type = curses_radio(('client (ssh-synchronized)', 'client (offline)'),
|
||||||
|
'device + sync type configuration')"""
|
||||||
|
|
||||||
|
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', ''))
|
||||||
|
|
||||||
|
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
+6
@@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
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
|
||||||
Executable
+18
@@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
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
|
||||||
|
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
|
||||||
|
if [ "$replace" != 'n' ] && [ "$replace" != 'N' ]; then
|
||||||
|
expand -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 "$1" > "$1".new
|
||||||
|
chmod --reference="$1" "$1".new
|
||||||
|
mv "$1".new "$1"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
Executable
+13
@@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import re
|
||||||
|
|
||||||
|
targets = (('UNAME-IMPORT-SSHYP', 'sshyp.py', ''), ('UNAME-IMPORT-STWEAK', 'stweak.py', ''))
|
||||||
|
|
||||||
|
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,23 +0,0 @@
|
|||||||
sshyp v1.1.2
|
|
||||||
|
|
||||||
the sheecrets update - patch two
|
|
||||||
|
|
||||||
this is a general polish/bugfix release
|
|
||||||
|
|
||||||
user-facing features:
|
|
||||||
|
|
||||||
- packaging support for Alpine Linux/postmarketOS
|
|
||||||
- the rename() function is no longer unnecessarily verbose
|
|
||||||
- handled exception for when the user attempts to copy an entry field that does not exist
|
|
||||||
|
|
||||||
fixes/optimizations:
|
|
||||||
|
|
||||||
- replaced shebang with "#!/usr/bin/env python3" for improved compatibility with various systems
|
|
||||||
^ does not affect Haiku packaging
|
|
||||||
- fixed the generic package not actually being compressed
|
|
||||||
- reduced lines of code used for cross-device entry deletion
|
|
||||||
|
|
||||||
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
|
|
||||||
|
|
||||||
The full history of changes to "sshyp" can be found on the following page:
|
|
||||||
https://raw.githubusercontent.com/rwinkhart/sshyp/main/extra/changelog-total
|
|
||||||
Executable → Regular
+4
-5
@@ -1,10 +1,9 @@
|
|||||||
sshyp is a FOSS password manager that uses an sftp-based syncing back-end.
|
sshyp is a FOSS password manager that uses an sftp-based syncing back-end.
|
||||||
Copyright (C) 2022 Randall Winkhart idgr@tutanota.com
|
Copyright (C) 2021-2023 Randall Winkhart idgr@tutanota.com
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of version 3 (only) of the GNU General Public License
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
as published by the Free Software Foundation.
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
This program is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
@@ -12,4 +11,4 @@
|
|||||||
GNU General Public License for more details.
|
GNU General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|||||||
Reference in New Issue
Block a user