Compare commits

..
47 Commits
Author SHA1 Message Date
RandyTheSilly 018d0c511d Updated README.md for release v1.1.2
Former-commit-id: 4eb7a866fa03d5990a300120974def01ab4babec [formerly 0ce5b1c1f6]
Former-commit-id: b0e3e46e63b15184ad7d600ccc5e33e98c185081
2022-08-27 14:27:35 -04:00
RandyTheSilly 55b94fc4cc Removed option for using local source from packaging script, as it is mostly unnecessary and/or broken
Former-commit-id: 86a32b1b3e217708078d5d99f8e30571169697a6 [formerly 35068270af]
Former-commit-id: db7e4d8eedcda2d94a5cec75e4ef9e354eec0687
2022-08-27 14:08:58 -04:00
RandyTheSilly 3f9c4e2c8d Automatically calculate revision number for Alpine packaging
Former-commit-id: 2de24f3dc4b9e5f5e27265da1271ea96be0387f5 [formerly cd5c9150a5]
Former-commit-id: 2c613cdc811dc127968a46758c4f238562bc64f9
2022-08-27 13:53:09 -04:00
RandyTheSilly 166ac50f42 Updated documentation for release v1.1.2
Former-commit-id: e212291e40989a1c50cbccf27060dbc7744da668 [formerly f97c9ad4f9]
Former-commit-id: 150048cc1ab40ee49e83295cedcdc9c3b1c49a8c
2022-08-27 13:12:58 -04:00
RandyTheSilly df1c6482a8 Optimized cross-device entry deletion code
Former-commit-id: 2a1dcda6460c06103846b41fbf2d83bf78c05527 [formerly f1aca39fd1]
Former-commit-id: 268b5564ff2f4cc157ebe039e9c46826a4bdb878
2022-08-27 12:46:54 -04:00
RandyTheSilly 38c6ef3d1b rename() now uses entry_name_fetch()
Former-commit-id: c3a3e7b95eb85225dd0bfbbbb15ac81d78adf7e2 [formerly 1f0b23c25f]
Former-commit-id: d2fe8fce784a01a9f5c127ad1eb3d05550624425
2022-08-27 12:11:59 -04:00
RandyTheSilly 7bea6093b8 Properly handle exception for attempting to copy a field that does not exist
Former-commit-id: 57c07b5ad482413febc9b6ee4e27c3f3a5f4fe11 [formerly 7355092cfe]
Former-commit-id: dc1d96e23a8a558c4b880b997a17e3a8d7cfcadd
2022-08-26 18:04:01 -04:00
RandyTheSilly f9ca6f8e94 Fixed generic package not being compressed
Former-commit-id: 600f05d063f52cfc34b58abef6acb799ce35f651 [formerly 56a99d09ef]
Former-commit-id: 90d9f29f91c3e44cf0e2ef75a8bbbd48ad3aa52c
2022-08-14 19:16:04 -04:00
RandyTheSilly 75d8eb7b7d Added Alpine packaging support, fixed packaging woes regarding a bad shebang
Former-commit-id: 2a440affcd09a28554c4ebe316408889047dc052 [formerly a83297c52b]
Former-commit-id: 41d6aebd702925cfbd4089f974b09250f3f6d8b3
2022-08-14 19:13:50 -04:00
RandyTheSilly 39316d04a2 Updated documentation for release v1.1.1
Former-commit-id: defed7054804d7bf36e93c302282039d335d6561 [formerly 558512ba52]
Former-commit-id: f6cf7d3e5633652439a46c51f9d5cfad007f2236
2022-07-21 22:40:29 -04:00
RandyTheSilly 3e9a4c0403 Fixed gpg auto-generation adding extra spaces to gpg-gen file
Former-commit-id: 0a273095a3a13d2997655c19ff62a674b135f530 [formerly 83a408ca0b]
Former-commit-id: eeee558508a68dcb9e243eea4e049704448c9f3e
2022-07-21 19:34:25 -04:00
RandyTheSilly 3bc07b48ef Fixed encrypt() using global tmp_dir
Former-commit-id: 2e993a03f745ae5b4a4f58acc6695e0660bc278c [formerly b6d3f7c5d1]
Former-commit-id: 9306abe18fe741cdd36280798f4bcffc66a77dd7
2022-07-21 01:37:53 -04:00
RandyTheSilly 5234e8c048 Improved encrypt() usability by extensions
Former-commit-id: 1dad668c15e2e1629c0233c80b88763ed5535c04 [formerly 0c975eab8f]
Former-commit-id: 3bddba6995f6aa92de8ea00500cb64b08d7e17db
2022-07-21 01:29:59 -04:00
RandyTheSilly b7c3535955 Exposed encrypt() function to be used by extensions
Former-commit-id: a055f6359a1bd2b81191ef1ef7b5dfb9432f75b2 [formerly 033bbd59de]
Former-commit-id: 451bafbfaaec27086c8c6cb50cb918ca94cf5497
2022-07-20 16:23:43 -04:00
RandyTheSilly 1f52c45c43 Bump version to v1.1.1
Former-commit-id: b2aad92f56b7931daf7ccab2a44793b29bec61f3 [formerly 5aeaee4c8f]
Former-commit-id: 2c651547721c89147dff385e0ad25d2d44694870
2022-07-17 07:29:56 -04:00
RandyTheSilly 7723baa69e Convert add_entry() and gen() to use optimized_edit()
Former-commit-id: 09f1256a4eed4bc042af7149f15ebb5b1197cbe8 [formerly 2d40db5467]
Former-commit-id: f7aa3b0cd2a761283ed11646b6c8fbb6f8130501
2022-07-17 07:25:12 -04:00
RandyTheSilly b680cc96d6 Significant overhaul to entry format optimization
Former-commit-id: 032103385be1ec017f97192829d591acd7a8c902 [formerly 8f318446bf]
Former-commit-id: 7ae69f3bafa268df1cf114d443d367ff45618f4a
2022-07-17 05:37:54 -04:00
RandyTheSilly f750fe1e4d Fixed compatibility check not adding the correct number of lines under certain circumstances
Former-commit-id: 93a4ff7bd25e38ae0a2b40d147afa51b08d73e04 [formerly a4368d60eb]
Former-commit-id: 58c89fcb9728bcfb430aaaf0df817f696688f1cd
2022-07-16 19:32:57 -04:00
RandyTheSilly b96db3ad92 Updated date in manpage for release v1.1.0
Former-commit-id: f9b74560f437e3dcc3d731909599957c460e8e8b [formerly 393ef08a21]
Former-commit-id: ddd1f4834f044d5e3da5016218138690627bcbfe
2022-07-14 03:21:43 -04:00
RandyTheSilly 224fc1fc6d Minor visual consistency tweak for Haiku gpg key generation
Former-commit-id: 1afd8df260d65f210e90cc93270971bf2c2fcb46 [formerly c5c110d0b4]
Former-commit-id: a24c571e55d987595250ebcfc558924cec675c62
2022-07-14 02:53:39 -04:00
RandyTheSilly 0bd2e45dba Fixed Termux packaging
Former-commit-id: 6d38a02d6211412bc99615704f311f820bdcef27 [formerly 4b02fd02b8]
Former-commit-id: 92f48521175fb89015cdb6dc9d99fb9485650c1f
2022-07-13 07:44:29 -04:00
RandyTheSilly 6410843911 Updated changelog for v1.1.0
Former-commit-id: 7ebe77ae88f470586d291eb4054cbdfa6f365950 [formerly de28d19969]
Former-commit-id: 7d27d41aef11546e74dc7073c8b260566addb0d7
2022-07-13 04:48:11 -04:00
RandyTheSilly 3dda6d8428 Update README.md
Former-commit-id: 5b20287fab20945ddc8841249503c321d6cbf999 [formerly beeb426ac8]
Former-commit-id: 90746e5b5225435bf8fd7ab9c2659fa5547a0c14
2022-07-11 22:30:48 -04:00
RandyTheSilly 4f31af5eff Clean up for automatically created gpg generation file
Former-commit-id: eba9c2f414fd59d655e2d3b992b2a8982745e01f [formerly e2b8e31f50]
Former-commit-id: 1166007ce70d0e1d61f2e2d8c3fab78970a6407b
2022-07-11 22:19:31 -04:00
RandyTheSilly bfb830c5e5 Fixed Haiku gpg key generation
Former-commit-id: 1a441cea301049608232742f4fddcc6338adf0a7 [formerly a673404fae]
Former-commit-id: 5ad1eef49307617c0f629f0a660d977f1784d380
2022-07-11 19:06:58 -04:00
RandyTheSilly 790121e7ea Greatly improved 'sshyp tweak' readability
Former-commit-id: 1f0f3ad2ca92e321c4819a14e9db1a961ff0816d [formerly d6bc4a2301]
Former-commit-id: 331e6e9b3d78f4aab1e4c958d526466a469ac515
2022-07-11 04:16:33 -04:00
rwinkhart 4168b800f8 Reduced code used for argument parsing
Former-commit-id: fb6b5f10b1077e2bba9c6bf43c23a59a455491eb [formerly faea36f6cd]
Former-commit-id: 5292e931128c413557c0e9609c009d7430949cf7
2022-07-10 05:41:49 -04:00
rwinkhart 801d515697 Restored banner image
Former-commit-id: 77219101a9c71848dce9d96e7dd17d9440d03b6c [formerly 398999b23f]
Former-commit-id: 77a032369f2f492f8227fa927da3a1b3d88ad7d0
2022-07-10 00:16:50 -04:00
rwinkhart 379970d53b Moved conversion scripts to sshyp-labs repo
Former-commit-id: b2ce1378324b6b19b71a54f02e4ac34a94d17728 [formerly 77873d061b]
Former-commit-id: ab23a45ed7b1d11597823081b2f0132b4b01ec2f
2022-07-10 00:15:56 -04:00
rwinkhart 4abb3ed718 Fixed entries readout not skipping lines between fields under certain conditions
Former-commit-id: 952acd940a754745d1798070113ea6c4d9d84408 [formerly f6875b15f0]
Former-commit-id: 496bdf8eb8abb6aa6c55e355e2b0457002793811
2022-07-09 23:35:08 -04:00
rwinkhart 6690fd15c9 Updated sshyp-refresh to use new entry directory
Former-commit-id: 9dedbb71ef7cf4f45511780d62a4c51d75d782a9 [formerly 8c520d972e]
Former-commit-id: 900cc5e3e7c0508a2629cfcb002faebb7c1d3753
2022-07-09 18:17:24 -04:00
rwinkhart 1582e73a98 Removed unused import
Former-commit-id: a7209c33c11d1eb36b7b27a1184b783f23253ff4 [formerly 01d59ce79a]
Former-commit-id: 8a9c2cb3017b966c138ebe9ae4cf8a1ea252ddca
2022-07-08 06:17:06 -04:00
rwinkhart dfe1703e62 gpg key generation and input is now automated
Former-commit-id: 97072166a69bc920cf39a87aa05f03ffc783d3d0 [formerly 9e49aa0bcb]
Former-commit-id: 9037e0b2a5f41f5db6616fc730ba21a8aec4dbe1
2022-07-08 06:14:13 -04:00
rwinkhart 7a76130b26 Updated banner artwork
Former-commit-id: c51116e0cdb7866b2c0bb969a23b57c4cb2cb7ff [formerly a2a473454f]
Former-commit-id: b5636d731c0398a17ca972d15325cced3c4df36a
2022-07-08 02:24:55 -04:00
rwinkhart 98dcbf57c6 Updated directory specified in the manpage
Former-commit-id: 934137a00c709169d9c316aa6a670eb6ef832f8f [formerly 2639cea365]
Former-commit-id: 98831f08192eb3deeb596594a9a24cbdb1c8dd1f
2022-07-08 00:46:23 -04:00
rwinkhart a5946d558a Added comments to every function
Former-commit-id: 6b8f15be379d51af5ced2449b1db7749d528dcf4 [formerly 87ae61ae30]
Former-commit-id: f5919588095deb509e899cff4e024dd76affbbce
2022-07-08 00:39:48 -04:00
rwinkhart 30fb5ed041 Updated compatibility with the upcoming sshyp-mfa extension
Former-commit-id: 3d900bfcce18345a9595deca8951b3a91bdfa85b [formerly bf22987544]
Former-commit-id: 34cbff97d0e671e71acd2baa95a0d129e1bafe79
2022-06-30 22:11:19 -04:00
rwinkhart fb1f5b92c3 Disabled all arguments on server except for 'tweak'
Former-commit-id: b35e7fca86b9fc7937dfb6119a8ba154ebade8fe [formerly c7fa3e3d09]
Former-commit-id: 7cab5ad48bd0a41d630fd5f943aa2e32c50ad434
2022-06-30 21:56:08 -04:00
rwinkhart 61b2186b0a In compliance with the XDG base directory spec, entries are now stored in ~/.local/share/sshyp/
Former-commit-id: fbb98f890b417395345a7a71ad158e48fa36a7ef [formerly 0c1f34bc30]
Former-commit-id: 91bd7170c0c56c093cf2126ce089be0e8255d6ce
2022-06-30 21:22:20 -04:00
rwinkhart 2113b93481 Allow the user to choose preferred editor, drop nano as dependency
Former-commit-id: d84048c212beca87419aac3577cf32c01d4f518f [formerly 9891fe4a81]
Former-commit-id: 2204730779caa4adad44a03f1fcd0bb76f25a1cd
2022-06-30 20:27:59 -04:00
RandyTheSilly 2030d90114 Fixed license name for Fedora package
Former-commit-id: 4caad0c333bdb66c3945692368ba26c702060426 [formerly 50972fcdf8]
Former-commit-id: 2645a1286c5d23f05f22e9dde0ead1edde6e27d5
2022-06-21 23:39:07 -04:00
RandyTheSilly 75071601e6 Update packaging for sshyp.py rename
Former-commit-id: 1f52fb7fb48c7e1152314d065602139f62b5ea2e [formerly 37fe8e37e7]
Former-commit-id: 8f17c38475e647b511bc7858c3859d3c65e29882
2022-06-21 23:37:45 -04:00
RandyTheSilly 9a0a261d55 Removed unused code in entry_name_fetch()
Former-commit-id: 047a66dc53042dda37f6825862c2fc14fb3a17cc [formerly 1f2543fe3f]
Former-commit-id: 4bb6fca31988c8ef74a4e4a730f1d25e9b21481d
2022-06-21 23:18:54 -04:00
RandyTheSilly 4ca2d89464 Updated read_shortcut() to use argument_list[] notation
Former-commit-id: 01a62c169c089618c8015f94f9d63ddbfb295d37 [formerly c0f3296a75]
Former-commit-id: 4bf2ff467f12d9f1e7c5f7fd0187d441718808a7
2022-06-21 23:12:03 -04:00
RandyTheSilly f90ec5c900 Optimized import statement for random
Former-commit-id: e18122f2bc1df6525c890d42ae02d90062e5bd6e [formerly c2f43e563a]
Former-commit-id: a993e29ce0410dd63a4171d10e3dc842c9297be5
2022-06-21 23:03:00 -04:00
RandyTheSilly fa0a4fe0b8 Removed unnecessary arguments in usage of decrypt()
Former-commit-id: cd966935bf552d5b5222c59e1887544b79e8a6aa [formerly db0142d202]
Former-commit-id: c3347ea5164901f8377414b097af426b1569b26d
2022-06-21 22:58:39 -04:00
RandyTheSilly c85a9d2ccd Compatibility with upcoming sshyp-mfa standalone extension
Former-commit-id: adf34e9d38e3ce5a7d96738a97593fca777aa244 [formerly f184864e90]
Former-commit-id: ff632e3bd21132c838ba4602d99ab4106d387971
2022-06-21 22:44:10 -04:00
12 changed files with 412 additions and 411 deletions
Regular → Executable
View File
Regular → Executable
+8 -6
View File
@@ -1,4 +1,4 @@
![sshyp](https://github.com/rwinkhart/sshyp/blob/main/extra/sshyp-linux-banner.png) ![sshyp](https://github.com/rwinkhart/sshyp-labs/blob/main/extra/artwork/sshyp-banner.png)
[![CodeQL](https://github.com/rwinkhart/sshyp/actions/workflows/codeql-analysis.yml/badge.svg?branch=main)](https://github.com/rwinkhart/sshyp/actions/workflows/codeql-analysis.yml) [![CodeQL](https://github.com/rwinkhart/sshyp/actions/workflows/codeql-analysis.yml/badge.svg?branch=main)](https://github.com/rwinkhart/sshyp/actions/workflows/codeql-analysis.yml)
@@ -25,12 +25,12 @@ 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
- utilize [extensions](https://github.com/rwinkhart/sshyp-labs) to interact with your entries is additional ways (such as generating TOTP keys)
- 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 (an Android terminal emulator)!
What sshyp will likely do: What sshyp will likely do:
- (planned v1.1.0) 2FA/MFA management (beneficial for reducing phone usage)
- (planned v1.2.0) everything it already does, but also in a GUI for Linux/Haiku - (planned v1.2.0) everything it already does, but also in a GUI for Linux/Haiku
What sshyp definitely won't do: What sshyp definitely won't do:
@@ -40,10 +40,12 @@ What sshyp definitely won't do:
# Installation # Installation
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, 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, Arch Linux, Debian/Ubuntu Linux, Fedora Linux, and Termux. These can be downloaded from the releases page.
Requests for additional distribution/OS support can be filed as issues. Requests for additional distribution/OS support can be filed as issues.
Extensions are available in the [sshyp-labs](https://github.com/rwinkhart/sshyp-labs) repository.
# 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. Since sshyp is written entirely in Python, it doesn't need to be compiled. It does, however, need to be packaged for installation.
@@ -61,7 +63,7 @@ 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 attatched to the release tags were already packaged using this script.
Currently, the script can create packages for Haiku, FreeBSD, 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 generic.
# 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), be sure to run:
@@ -89,10 +91,10 @@ man sshyp
# Roadmap # Roadmap
Short-term Goals: Short-term Goals:
- 2FA/MFA management - create minimal GUI app (Linux x86_64, Linux aarch64)
- create minimal GUI apps (Linux x86_64, Linux aarch64, Haiku x86_64)
- various optimizations/bug fixes - various optimizations/bug fixes
Long-term Goals: Long-term Goals:
- a fun surprise
- seize the thrones, shear the humans - seize the thrones, shear the humans
+7 -6
View File
@@ -1,4 +1,4 @@
#!/bin/python3 #!/usr/bin/env python3
# external modules # external modules
@@ -10,7 +10,7 @@ from sys import exit as s_exit
# utility functions # utility functions
def get_titles_mods(_directory, _destination, _user_data): def get_titles_mods(_directory, _destination, _user_data): # fetches and returns lists of titles and their mod times
_title_list, _mod_list = [], [] _title_list, _mod_list = [], []
Path(path.expanduser('~/.config/sshync')).mkdir(0o700, parents=True, exist_ok=True) # create config directory Path(path.expanduser('~/.config/sshync')).mkdir(0o700, parents=True, exist_ok=True) # create config directory
# local fetching # local fetching
@@ -37,7 +37,8 @@ def get_titles_mods(_directory, _destination, _user_data):
return _title_list, _mod_list return _title_list, _mod_list
def sort_titles_mods(_list_1, _list_2): 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_list_2_sorted, _mod_list_2_sorted = [], []
# title sorting # title sorting
for _title in _list_1[0]: for _title in _list_1[0]:
@@ -52,12 +53,12 @@ def sort_titles_mods(_list_1, _list_2):
return _title_list_2_sorted, _mod_list_2_sorted return _title_list_2_sorted, _mod_list_2_sorted
def make_profile(_profile_dir, _local_dir, _remote_dir, _identity, _ip, _port, _user): 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' + open(_profile_dir, 'w').write(_user + '\n' + _ip + '\n' + _port + '\n' + _local_dir + '\n' + _remote_dir + '\n' +
_identity + '\n') _identity + '\n')
def get_profile(_profile_dir): def get_profile(_profile_dir): # returns a list of data read from a sshync job profile
try: try:
_profile_data = open(_profile_dir).readlines() _profile_data = open(_profile_dir).readlines()
except (FileNotFoundError, IndexError): except (FileNotFoundError, IndexError):
@@ -74,7 +75,7 @@ def get_profile(_profile_dir):
return _user, _ip, _port, _local_dir, _remote_dir, _identity return _user, _ip, _port, _local_dir, _remote_dir, _identity
def run_profile(_profile_dir): def run_profile(_profile_dir): # runs a sshync job profile
_user_data = get_profile(_profile_dir) _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 _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_l = sort_titles_mods(_remote_titles_mods_saver, get_titles_mods(_user_data[3], 'l', _user_data))
+223 -197
View File
@@ -1,10 +1,10 @@
#!/bin/python3 #!/usr/bin/env python3
# external modules # external modules
from os import environ, listdir, path, remove, system, uname, walk from os import environ, listdir, path, remove, system, uname, walk
from pathlib import Path from pathlib import Path
import random from random import randint, SystemRandom
from shutil import get_terminal_size, move, rmtree from shutil import get_terminal_size, move, rmtree
import sshync import sshync
import string import string
@@ -13,13 +13,13 @@ from sys import argv, exit as s_exit
from textwrap import fill from textwrap import fill
# utility functions # BELOW - utility functions
def entry_list_gen(): # generates and prints a list of all folders and entries 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') print('\n\u001b[38;5;0;48;5;15msshyp entries:\u001b[0m\n')
_entry_list, _color_alternator = [], 1 _entry_list, _color_alternator = [], 1
for _entry in sorted(listdir(path.expanduser('~/.password-pasture'))): for _entry in sorted(listdir(_directory)):
if Path(path.expanduser(f"~/.password-pasture/{_entry}")).is_file(): if Path(f"{_directory}{_entry}").is_file():
if _color_alternator == 1: if _color_alternator == 1:
_entry_list.append(f"{_entry.replace('.gpg', '')}") _entry_list.append(f"{_entry.replace('.gpg', '')}")
_color_alternator = 2 _color_alternator = 2
@@ -35,12 +35,12 @@ def entry_list_gen(): # generates and prints a list of all folders and entries
print(fill(' '.join(_entry_list), width=_width) + '\n') print(fill(' '.join(_entry_list), width=_width) + '\n')
except ValueError: except ValueError:
pass pass
for _root, _directories, _files in walk(path.expanduser('~/.password-pasture')): for _root, _directories, _files in walk(_directory):
for _dir in sorted(_directories): for _dir in sorted(_directories):
_inner_dir = f"{_root.replace(path.expanduser('~/.password-pasture'), '')}/{_dir}" _inner_dir = f"{_root.replace(_directory, '')}/{_dir}"
print(f"\u001b[38;5;15;48;5;238m{_inner_dir}/\u001b[0m") print(f"\u001b[38;5;15;48;5;238m{_inner_dir}/\u001b[0m")
_entry_list, _color_alternator = [], 1 _entry_list, _color_alternator = [], 1
for _s_root, _s_directories, _s_files in walk(path.expanduser(f"~/.password-pasture{_inner_dir}")): for _s_root, _s_directories, _s_files in walk(f"{_directory[:-1]}{_inner_dir}"):
for _entry in sorted(_s_files): for _entry in sorted(_s_files):
if _color_alternator == 1: if _color_alternator == 1:
_entry_list.append(f"{_entry.replace('.gpg', '')}") _entry_list.append(f"{_entry.replace('.gpg', '')}")
@@ -59,20 +59,20 @@ def entry_list_gen(): # generates and prints a list of all folders and entries
print('\u001b[38;5;9m-empty directory-\u001b[0m\n') print('\u001b[38;5;9m-empty directory-\u001b[0m\n')
def entry_reader(_decrypted_entry): 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 _entry_lines, _notes_flag = open(_decrypted_entry, 'r').readlines(), 0
print() print()
for _num in range(len(_entry_lines)): for _num in range(len(_entry_lines)):
try: try:
if _num == 0 and _entry_lines[1] != '\n': if _num == 0 and _entry_lines[1] != '\n':
print(f"\u001b[38;5;15;48;5;238musername:\u001b[0m\n{_entry_lines[1]}") 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': elif _num == 1 and _entry_lines[0] != '\n':
print(f"\u001b[38;5;15;48;5;238mpassword:\u001b[0m\n{_entry_lines[0]}") 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': elif _num == 2 and _entry_lines[2] != '\n':
print(f"\u001b[38;5;15;48;5;238murl:\u001b[0m\n{_entry_lines[_num]}") 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: elif _num >= 3 and _entry_lines[_num] != '\n' and _notes_flag != 1:
_notes_flag = 1 _notes_flag = 1
print('\u001b[38;5;15;48;5;238mnotes:\u001b[0m\n' + _entry_lines[_num].replace('\n', '')) print(f"\u001b[38;5;15;48;5;238mnotes:\u001b[0m\n{_entry_lines[_num].strip()}")
elif _num >= 3 and _notes_flag == 1: elif _num >= 3 and _notes_flag == 1:
print(_entry_lines[_num].replace('\n', '')) print(_entry_lines[_num].replace('\n', ''))
if _notes_flag == 1: if _notes_flag == 1:
@@ -82,21 +82,13 @@ def entry_reader(_decrypted_entry):
print() print()
except IndexError: except IndexError:
if _num == 0: if _num == 0:
print(f"\u001b[38;5;15;48;5;238mpassword:\u001b[0m\n{_entry_lines[0]}") print(f"\u001b[38;5;15;48;5;238mpassword:\u001b[0m\n{_entry_lines[0]}\n")
def replace_line(file_name, line_num, text): # replaces text in a given line with different text def entry_name_fetch(_entry_name_location): # fetches and returns entry name from user input or from provided argument
_lines = open(file_name, 'r').readlines()
_lines[line_num] = text
open(file_name, 'w').writelines(_lines)
def entry_name_fetch(_entry_name_location):
if type(_entry_name_location) == str: if type(_entry_name_location) == str:
entry_list_gen() entry_list_gen()
_entry_name = str(input(_entry_name_location)) _entry_name = str(input(_entry_name_location))
elif _entry_name_location == 0:
_entry_name = argument
else: else:
_entry_name_split = argument.split(' ') _entry_name_split = argument.split(' ')
del _entry_name_split[:_entry_name_location] del _entry_name_split[:_entry_name_location]
@@ -107,22 +99,22 @@ def entry_name_fetch(_entry_name_location):
return _entry_name return _entry_name
def shm_gen(): # generates a random temporary folder for security and returns random names for the folder and temp file def shm_gen(_tmp_dir=path.expanduser('~/.config/sshyp/tmp/')): # creates a temporary directory for entry editing
_shm_folder_gen = ''.join(random.SystemRandom().choice(string.ascii_letters + string.digits) _shm_folder_gen = ''.join(SystemRandom().choice(string.ascii_letters + string.digits)
for _ in range(random.randint(10, 30))) for _ in range(randint(10, 30)))
_shm_entry_gen = ''.join(random.SystemRandom().choice(string.ascii_letters + string.digits) _shm_entry_gen = ''.join(SystemRandom().choice(string.ascii_letters + string.digits)
for _ in range(random.randint(10, 30))) for _ in range(randint(10, 30)))
Path(tmp_dir + _shm_folder_gen).mkdir(0o700) Path(_tmp_dir + _shm_folder_gen).mkdir(0o700)
return _shm_folder_gen, _shm_entry_gen return _shm_folder_gen, _shm_entry_gen
def pass_gen(): def pass_gen(): # generates and returns a random password based on user-specified options
def _pass_gen_function(__complexity, __length): def _pass_gen_function(__complexity, __length):
if __complexity.lower() == 's': if __complexity.lower() == 's':
__character_pool = string.ascii_letters + string.digits __character_pool = string.ascii_letters + string.digits
else: else:
__character_pool = string.ascii_letters + string.digits + string.punctuation __character_pool = string.ascii_letters + string.digits + string.punctuation
__gen = ''.join(random.SystemRandom().choice(__character_pool) for _ in range(__length)) __gen = ''.join(SystemRandom().choice(__character_pool) for _ in range(__length))
__min_special, __special = round(.2 * __length), 0 __min_special, __special = round(.2 * __length), 0
for __character in __gen: for __character in __gen:
if not __character.isalpha(): if not __character.isalpha():
@@ -144,17 +136,19 @@ def pass_gen():
return _gen return _gen
def encrypt(_shm_folder, _shm_entry, _location): def encrypt(_entry_dir, _shm_folder, _shm_entry, _gpg_com, _gpg_id, _tmp_dir=path.expanduser('~/.config/sshyp/tmp/')):
system(f"{gpg} -qr {str(gpg_id)} -e '{tmp_dir}{_shm_folder}/{_shm_entry}'") # encrypts an entry and cleans up the temporary files
move(f"{tmp_dir}{_shm_folder}/{_shm_entry}.gpg", f"{directory}{_location}.gpg") system(f"{_gpg_com} -qr {str(_gpg_id)} -e '{_tmp_dir}{_shm_folder}/{_shm_entry}'")
rmtree(f"{tmp_dir}{_shm_folder}") 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): 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: if _shm_folder == 0 and _shm_entry == 0:
_command = f"{gpg} -qd --output /dev/null {path.expanduser('~/.config/sshyp/lock.gpg')}" _command = f"{_gpg_command} -qd --output /dev/null {path.expanduser('~/.config/sshyp/lock.gpg')}"
else: else:
_command = f"{gpg} -qd --output {tmp_dir}{_shm_folder}/{_shm_entry} {_entry_dir}.gpg" _command = f"{_gpg_command} -qd --output {_tmp_dir}{_shm_folder}/{_shm_entry} {_entry_dir}.gpg"
try: try:
run(_command, shell=True, stderr=PIPE, check=True, close_fds=True) run(_command, shell=True, stderr=PIPE, check=True, close_fds=True)
except CalledProcessError: except CalledProcessError:
@@ -162,16 +156,38 @@ def decrypt(_entry_dir, _shm_folder, _shm_entry):
s_exit(1) s_exit(1)
def edit_note(_shm_folder, _shm_entry): def optimized_edit(_lines, _edit_data, _edit_line): # ensures an edited entry is optimized for best compatibility
lines = open(f"{tmp_dir}{_shm_folder}/{_shm_entry}").readlines() while len(_lines) < _edit_line + 1:
open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'w').writelines(lines[0:3]) _lines += ['\n']
open(f"{tmp_dir}{_shm_folder}/{_shm_entry}-n", 'w').writelines(lines[3:]) if _edit_data is not None:
system(f"nano {tmp_dir}{_shm_folder}/{_shm_entry}-n") _lines[_edit_line] = _edit_data.strip('\n').rstrip() + '\n'
edit_notes = open(f"{tmp_dir}{_shm_folder}/{_shm_entry}-n").read() for _num in range(len(_lines)):
open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'a').write(edit_notes) 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): 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} " \ _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}'\"" f"\"touch '/home/{_username_ssh}/.config/sshyp/devices/{_client_device_name}'\""
try: try:
@@ -185,12 +201,13 @@ def copy_name_check(_port, _username_ssh, _ip, _client_device_name):
open(path.expanduser('~/.config/sshyp/ssh-error'), 'w').write('0') open(path.expanduser('~/.config/sshyp/ssh-error'), 'w').write('0')
return 0 return 0
# argument-specific functions # BELOW - argument-specific functions
def tweak(): # runs configuration wizard def tweak(): # runs configuration wizard
_divider = f"\n{'=' * (get_terminal_size()[0] - int((.5 * get_terminal_size()[0])))}\n\n"
# storage/config directory creation # storage/config directory creation
Path(path.expanduser('~/.password-pasture')).mkdir(0o700, parents=True, exist_ok=True) 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) Path(path.expanduser('~/.config/sshyp/devices')).mkdir(0o700, parents=True, exist_ok=True)
if not Path(f"{path.expanduser('~/.config/sshyp/tmp')}").exists(): if not Path(f"{path.expanduser('~/.config/sshyp/tmp')}").exists():
if uname()[0] == 'Haiku' or uname()[0] == 'FreeBSD': if uname()[0] == 'Haiku' or uname()[0] == 'FreeBSD':
@@ -204,7 +221,8 @@ def tweak(): # runs configuration wizard
if _device_type.lower() == 's': if _device_type.lower() == 's':
open(path.expanduser('~/.config/sshyp/sshyp-device'), 'w').write(_device_type) open(path.expanduser('~/.config/sshyp/sshyp-device'), 'w').write(_device_type)
Path(path.expanduser('~/.config/sshyp/deleted')).mkdir(0o700, parents=True, exist_ok=True) Path(path.expanduser('~/.config/sshyp/deleted')).mkdir(0o700, parents=True, exist_ok=True)
print('\nmake sure the ssh service is running and properly configured\n\nconfiguration complete\n') 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) s_exit(0)
else: else:
# device type configuration # device type configuration
@@ -212,57 +230,75 @@ def tweak(): # runs configuration wizard
open(path.expanduser('~/.config/sshyp/sshyp-device'), 'w').write(_device_type) open(path.expanduser('~/.config/sshyp/sshyp-device'), 'w').write(_device_type)
# gpg configuration # gpg configuration
_gpg_id = input('\nsshyp requires the use of a unique gpg key - do you already have one that you are ' _gpg_id = input(f"{_divider}sshyp requires the use of a unique gpg key - use an (e)xisting key or (g)enerate "
'willing to use? (y/N) ') f"a new one? (E/g) ")
if _gpg_id.lower() != 'y': if _gpg_id.lower() != 'g':
print('\na unique gpg key has been generated for you.') system(f"{gpg} -k")
system(f"{gpg} --full-generate-key") _gpg_id = str(input('gpg key id: '))
_gpg_id = str(input('\nplease input the id of your gpg key: ')) else:
open(path.expanduser('~/.config/sshyp/sshyp-gpg'), 'w').write(_gpg_id + '\n') 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 # 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') open(path.expanduser('~/.config/sshyp/lock'), 'w')
system(f"{gpg} -qr {str(_gpg_id)} -e {path.expanduser('~/.config/sshyp/lock')}") system(f"{gpg} -qr {str(_gpg_id)} -e {path.expanduser('~/.config/sshyp/lock')}")
remove(f"{path.expanduser('~/.config/sshyp')}/lock") remove(path.expanduser('~/.config/sshyp/lock'))
# ssh key configuration # ssh key configuration
_ssh_gen = (input('\nmake sure the ssh service on the remote server is running and properly configured' _ssh_gen = (input(f"{_divider}make sure the ssh service on the remote server is running and properly "
'\n\nsync support requires a unique ssh key - would you like to have this automatically ' f"configured\n\nsync support requires a unique ssh key - would you like to have this "
'generated? (Y/n) ')) f"automatically generated? (Y/n) "))
if _ssh_gen.lower() != 'n': if _ssh_gen.lower() != 'n':
if uname()[0] == 'Haiku': if uname()[0] == 'Haiku':
Path(f"{path.expanduser('~')}/.ssh").mkdir(0o700, exist_ok=True) Path(f"{path.expanduser('~')}/.ssh").mkdir(0o700, exist_ok=True)
system('ssh-keygen -t ed25519 -f ~/.ssh/sshyp') system('ssh-keygen -t ed25519 -f ~/.ssh/sshyp')
else: else:
print(f"\nensure that the key file you are using is located at {path.expanduser('~/.ssh/sshyp')}") 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 # ssh ip+port configuration
_ip_port = str(input('\nexample input: 10.10.10.10:9999\n\nip and ssh port of the remote server: ')) _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(':') _ip, _sep, _port = _ip_port.partition(':')
# ssh user configuration # ssh user configuration
_username_ssh = str(input('\nusername of the remote server: ')) _username_ssh = str(input('\nusername of the remote server: '))
print(f"\nentry directory: /home/{_username_ssh}/.password-pasture/")
# 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 profile generation
sshync.make_profile(path.expanduser('~/.config/sshyp/sshyp.sshync'), sshync.make_profile(path.expanduser('~/.config/sshyp/sshyp.sshync'),
path.expanduser('~/.password-pasture/'), f"/home/{_username_ssh}/.password-pasture/", path.expanduser('~/.local/share/sshyp/'), f"/home/{_username_ssh}/.local/share/sshyp/",
path.expanduser('~/.ssh/sshyp'), _ip, _port, _username_ssh) path.expanduser('~/.ssh/sshyp'), _ip, _port, _username_ssh)
# device name configuration # device name configuration
for _name in listdir(path.expanduser('~/.config/sshyp/devices')): # remove existing device name for _name in listdir(path.expanduser('~/.config/sshyp/devices')): # remove existing device name
remove(f"{path.expanduser('~/.config/sshyp/devices/')}{_name}") remove(f"{path.expanduser('~/.config/sshyp/devices/')}{_name}")
print('\n\u001b[4;1mimportant:\u001b[0m This name \u001b[4;1mmust\u001b[0m be unique amongst your client ' print(f"{_divider}\u001b[4;1mimportant:\u001b[0m this name \u001b[4;1mmust\u001b[0m be unique amongst your "
'devices\n\nthis is used to keep track of which devices have up-to-date databases.\n') 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: ')) _client_device_name = str(input('device name: '))
open(f"{path.expanduser('~/.config/sshyp/devices/')}{_client_device_name}", 'w') open(f"{path.expanduser('~/.config/sshyp/devices/')}{_client_device_name}", 'w')
copy_name_check(_port, _username_ssh, _ip, _client_device_name) copy_name_check(_port, _username_ssh, _ip, _client_device_name)
print('\nconfiguration complete\n') print(f"{_divider}configuration complete\n")
def print_info(): # prints help text based on argument def print_info(): # prints help text based on argument
if argument_list[0] == 'help' or argument_list[0] == '--help' or argument_list[0] == '-h': 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('\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 " 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") "comes with absolutely no warranty;\ntype `sshyp license' for details")
@@ -296,7 +332,7 @@ def print_info(): # prints help text based on argument
print('gen:') print('gen:')
print(' update/-u generate a password for an existing entry\n') 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>'") print("\u001b[1mtip:\u001b[0m you can quickly read an entry with 'sshyp /<entry name>'")
elif argument_list[0] == 'version' or argument_list[0] == '-v': 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/' 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') '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" print(" .. \u001b[38;5;9m♥♥ ♥♥\u001b[0m ..\n .''.''/()\\ \u001b[38;5;13m"
@@ -310,29 +346,29 @@ def print_info(): # prints help text based on argument
'randall winkhart\u001b[38;5;15;48;5;15m \u001b[38;5;7;48;5;8m/\u001b[0m') '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 ' print('\u001b[38;5;7;48;5;8m/\u001b[38;5;15;48;5;15m '
'\u001b[38;5;7;48;5;8m/\u001b[0m') '\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.0.1' 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') '\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 polished trotters ' 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') '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 ' print('\u001b[38;5;7;48;5;8m/\u001b[38;5;15;48;5;15m '
'\u001b[38;5;7;48;5;8m/\u001b[0m') '\u001b[38;5;7;48;5;8m/\u001b[0m')
print('\u001b[38;5;7;48;5;8m<><><><><><><><><><><><><><><><><><><><><><><><><><><><>\u001b[0m\n') print('\u001b[38;5;7;48;5;8m<><><><><><><><><><><><><><><><><><><><><><><><><><><><>\u001b[0m\n')
print('see https://github.com/rwinkhart/sshyp for more information\n') print('see https://github.com/rwinkhart/sshyp for more information\n')
elif argument_list[0] == 'license': 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 ' 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,' '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 ' '\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' '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.' ' PARTICULAR PURPOSE.\nSee the GNU General Public License for more details.'
'\n\nhttps://opensource.org/licenses/GPL-3.0\n') '\n\nhttps://opensource.org/licenses/GPL-3.0\n')
elif argument_list[0] == 'add': elif argument_list[1] == 'add':
print('\n\u001b[1musage:\u001b[0m sshyp add [flag [<entry name>]]\u001b[0m\n') print('\n\u001b[1musage:\u001b[0m sshyp add [flag [<entry name>]]\u001b[0m\n')
print('\u001b[1mflags:\u001b[0m') print('\u001b[1mflags:\u001b[0m')
print('add:') print('add:')
print(' password/-p add a password entry') print(' password/-p add a password entry')
print(' note/-n add a note entry') print(' note/-n add a note entry')
print(' folder/-f add a new folder for entries\n') print(' folder/-f add a new folder for entries\n')
elif argument_list[0] == 'edit': elif argument_list[1] == 'edit':
print('\n\u001b[1musage:\u001b[0m sshyp edit [flag [<entry name>]]\u001b[0m\n') print('\n\u001b[1musage:\u001b[0m sshyp edit [flag [<entry name>]]\u001b[0m\n')
print('\u001b[1mflags:\u001b[0m') print('\u001b[1mflags:\u001b[0m')
print('edit:') print('edit:')
@@ -341,7 +377,7 @@ def print_info(): # prints help text based on argument
print(' password/-p change the password of an entry') print(' password/-p change the password of an entry')
print(' url/-l change the url attached to an entry') print(' url/-l change the url attached to an entry')
print(' note/-n change the note attached to an entry\n') print(' note/-n change the note attached to an entry\n')
elif argument_list[0] == 'copy': elif argument_list[1] == 'copy':
print('\n\u001b[1musage:\u001b[0m sshyp copy [flag [<entry name>]]\u001b[0m\n') print('\n\u001b[1musage:\u001b[0m sshyp copy [flag [<entry name>]]\u001b[0m\n')
print('\u001b[1mflags:\u001b[0m') print('\u001b[1mflags:\u001b[0m')
print('copy:') print('copy:')
@@ -358,7 +394,7 @@ def no_arg(): # displays a list of entries and gives an option to select one fo
print(f"\n\u001b[38;5;9merror: entry ({_entry_name}) does not exist\u001b[0m\n") print(f"\n\u001b[38;5;9merror: entry ({_entry_name}) does not exist\u001b[0m\n")
s_exit(1) s_exit(1)
_shm_folder, _shm_entry = shm_gen() _shm_folder, _shm_entry = shm_gen()
decrypt(directory + _entry_name, _shm_folder, _shm_entry) decrypt(directory + _entry_name, _shm_folder, _shm_entry, gpg)
entry_reader(f"{tmp_dir}{_shm_folder}/{_shm_entry}") entry_reader(f"{tmp_dir}{_shm_folder}/{_shm_entry}")
rmtree(f"{tmp_dir}{_shm_folder}") rmtree(f"{tmp_dir}{_shm_folder}")
@@ -368,7 +404,7 @@ def read_shortcut(): # shortcut to quickly read an entry
print(f"\n\u001b[38;5;9merror: entry ({argument.replace('/', '', 1)}) does not exist\u001b[0m\n") print(f"\n\u001b[38;5;9merror: entry ({argument.replace('/', '', 1)}) does not exist\u001b[0m\n")
s_exit(1) s_exit(1)
_shm_folder, _shm_entry = shm_gen() _shm_folder, _shm_entry = shm_gen()
decrypt(directory + argument.replace('/', '', 1), _shm_folder, _shm_entry) decrypt(directory + argument.replace('/', '', 1), _shm_folder, _shm_entry, gpg)
entry_reader(f"{tmp_dir}{_shm_folder}/{_shm_entry}") entry_reader(f"{tmp_dir}{_shm_folder}/{_shm_entry}")
rmtree(f"{tmp_dir}{_shm_folder}") rmtree(f"{tmp_dir}{_shm_folder}")
@@ -387,22 +423,16 @@ def sync(): # calls sshync to sync changes to the user's server
_deletion_database = None _deletion_database = None
s_exit(1) s_exit(1)
for _file in _deletion_database: for _file in _deletion_database:
if _file[:-1].endswith('/'): try:
try: if silent_sync != 1:
if silent_sync != 1: print(f"\u001b[38;5;208m{_file[:-1]}\u001b[0m has been sheared, removing...")
print(f"\u001b[38;5;208m{_file[:-1]}\u001b[0m has been sheared, removing...") if _file[:-1].endswith('/'):
rmtree(f"{path.expanduser('~/.password-pasture/')}{_file[:-1]}") rmtree(f"{directory}{_file[:-1]}")
except FileNotFoundError: else:
if silent_sync != 1: remove(f"{directory}{_file[:-1]}.gpg")
print('folder does not exist locally.') except FileNotFoundError:
else: if silent_sync != 1:
try: print('location does not exist locally')
if silent_sync != 1:
print(f"\u001b[38;5;208m{_file[:-1]}\u001b[0m has been sheared, removing...")
remove(f"{path.expanduser('~/.password-pasture/')}{_file[:-1]}.gpg")
except (FileNotFoundError, IsADirectoryError):
if silent_sync != 1:
print('file does not exist locally.')
# check for new folders # check for new folders
system(f"ssh -i '{path.expanduser('~/.ssh/sshyp')}' -p {port} {username_ssh}@{ip} \"cd /bin; python -c " system(f"ssh -i '{path.expanduser('~/.ssh/sshyp')}' -p {port} {username_ssh}@{ip} \"cd /bin; python -c "
f"'import sshypRemote; sshypRemote.folder_check()'\"") f"'import sshypRemote; sshypRemote.folder_check()'\"")
@@ -418,7 +448,7 @@ def sync(): # calls sshync to sync changes to the user's server
if Path(f"{path.expanduser('~')}{_folder[:-1]}").is_dir(): if Path(f"{path.expanduser('~')}{_folder[:-1]}").is_dir():
pass pass
else: else:
print(f"\u001b[38;5;2m{_folder.replace('/.password-pasture/', '')[:-1]}/\u001b[0m does not exist locally, " print(f"\u001b[38;5;2m{_folder.replace('/.local/share/sshyp/', '')[:-1]}/\u001b[0m does not exist locally, "
f"creating...") f"creating...")
Path(f"{path.expanduser('~')}{_folder[:-1]}").mkdir(0o700, parents=True, exist_ok=True) Path(f"{path.expanduser('~')}{_folder[:-1]}").mkdir(0o700, parents=True, exist_ok=True)
# set permissions before uploading # set permissions before uploading
@@ -429,38 +459,38 @@ def sync(): # calls sshync to sync changes to the user's server
def add_entry(): # adds a new entry def add_entry(): # adds a new entry
_shm_folder, _shm_entry = None, None # sets base-line values to avoid errors _shm_folder, _shm_entry = None, None # sets base-line values to avoid errors
if len(argument_list) < 3: if len(argument_list) < 4:
_entry_name = entry_name_fetch('name of new entry: ') _entry_name = entry_name_fetch('name of new entry: ')
else: else:
_entry_name = entry_name_fetch(2) _entry_name = entry_name_fetch(2)
if Path(f"{directory}{_entry_name}.gpg").is_file(): if Path(f"{directory}{_entry_name}.gpg").is_file():
print(f"\n\u001b[38;5;9merror: entry ({_entry_name}) already exists\u001b[0m\n") print(f"\n\u001b[38;5;9merror: entry ({_entry_name}) already exists\u001b[0m\n")
s_exit(1) s_exit(1)
if argument_list[1] == 'note' or argument_list[1] == '-n': if argument_list[2] == 'note' or argument_list[2] == '-n':
_shm_folder, _shm_entry = shm_gen() _shm_folder, _shm_entry = shm_gen()
system(f"nano {tmp_dir}{_shm_folder}/{_shm_entry}-n") system(f"{editor} {tmp_dir}{_shm_folder}/{_shm_entry}-n")
_notes = open(f"{tmp_dir}{_shm_folder}/{_shm_entry}-n", 'r').read() _notes = open(f"{tmp_dir}{_shm_folder}/{_shm_entry}-n", 'r').read()
open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'w').writelines('\n\n\n' + _notes) open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'w').writelines(optimized_edit(['', '', '', _notes], None, -1))
elif argument_list[1] == 'password' or argument_list[1] == '-p': elif argument_list[2] == 'password' or argument_list[2] == '-p':
_username = str(input('username: ')) _username = str(input('username: '))
_password = str(input('password: ')) _password = str(input('password: '))
_url = str(input('url: ')) _url = str(input('url: '))
_add_note = input('add a note to this entry? (y/N) ') _add_note = input('add a note to this entry? (y/N) ')
_shm_folder, _shm_entry = shm_gen() _shm_folder, _shm_entry = shm_gen()
if _add_note.lower() == 'y': if _add_note.lower() == 'y':
system(f"nano {tmp_dir}{_shm_folder}/{_shm_entry}-n") system(f"{editor} {tmp_dir}{_shm_folder}/{_shm_entry}-n")
_notes = open(f"{tmp_dir}{_shm_folder}/{_shm_entry}-n", 'r').read() _notes = open(f"{tmp_dir}{_shm_folder}/{_shm_entry}-n", 'r').read()
else: else:
_notes = '' _notes = ''
open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'w').writelines(_password + '\n' + _username + '\n' + _url + open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'w')\
'\n' + _notes) .writelines(optimized_edit([_password, _username, _url, _notes], None, -1))
print('\n\u001b[1mentry preview:\u001b[0m') print('\n\u001b[1mentry preview:\u001b[0m')
entry_reader(f"{tmp_dir}{_shm_folder}/{_shm_entry}") entry_reader(f"{tmp_dir}{_shm_folder}/{_shm_entry}")
encrypt(_shm_folder, _shm_entry, _entry_name) encrypt(directory + _entry_name, _shm_folder, _shm_entry, gpg, gpg_id)
def add_folder(): # creates a new folder def add_folder(): # creates a new folder
if len(argument_list) < 3: if len(argument_list) < 4:
_entry_name = entry_name_fetch('name of new folder: ') _entry_name = entry_name_fetch('name of new folder: ')
else: else:
_entry_name = entry_name_fetch(2) _entry_name = entry_name_fetch(2)
@@ -477,8 +507,7 @@ def rename(): # renames an entry or folder
if not Path(f"{directory}{_entry_name}.gpg").is_file() and not Path(f"{directory}{_entry_name}").is_dir(): 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") print(f"\n\u001b[38;5;9merror: entry ({_entry_name}) does not exist\u001b[0m\n")
s_exit(1) s_exit(1)
_new_name = str(input('new name: ')) _new_name = entry_name_fetch('new name: ')
print(f"{directory}{_new_name}")
if Path(f"{directory}{_new_name}.gpg").is_file() or Path(f"{directory}{_new_name}").is_dir(): 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") print(f"\n\u001b[38;5;9merror: ({_new_name}) already exists\u001b[0m\n")
s_exit(1) s_exit(1)
@@ -493,8 +522,8 @@ def rename(): # renames an entry or folder
def edit(): # edits the contents of an entry def edit(): # edits the contents of an entry
_shm_folder, _shm_entry = None, None # sets base-line values to avoid errors _shm_folder, _shm_entry, _detail, _edit_line = None, None, None, None # sets values to avoid PEP8 warnings
if len(argument_list) < 3: if len(argument_list) < 4:
_entry_name = entry_name_fetch('entry to edit: ') _entry_name = entry_name_fetch('entry to edit: ')
else: else:
_entry_name = entry_name_fetch(2) _entry_name = entry_name_fetch(2)
@@ -502,44 +531,38 @@ def edit(): # edits the contents of an entry
print(f"\n\u001b[38;5;9merror: entry ({_entry_name}) does not exist\u001b[0m\n") print(f"\n\u001b[38;5;9merror: entry ({_entry_name}) does not exist\u001b[0m\n")
s_exit(1) s_exit(1)
_shm_folder, _shm_entry = shm_gen() _shm_folder, _shm_entry = shm_gen()
decrypt(directory + _entry_name, _shm_folder, _shm_entry) decrypt(directory + _entry_name, _shm_folder, _shm_entry, gpg)
if argument_list[2] == 'username' or argument_list[2] == '-u':
# compatibility check for GNU pass entries (ensuring they have at least four lines) _detail, _edit_line = str(input('username: ')), 1
_lines = open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'r').readlines() elif argument_list[2] == 'password' or argument_list[2] == '-p':
if len(_lines) < 3: _detail, _edit_line = str(input('password: ')), 0
while len(_lines) < 3: elif argument_list[2] == 'url' or argument_list[2] == '-l':
_lines.append('\n') _detail, _edit_line = str(input('url: ')), 2
open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'w').writelines(_lines) if argument_list[2] == 'note' or argument_list[2] == '-n':
_edit_line = 2
if argument_list[1] == 'username' or argument_list[1] == '-u': _new_lines = optimized_edit(edit_note(_shm_folder, _shm_entry,
_detail = str(input('new username: ')) open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'r').readlines()), None, -1)
replace_line(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 1, _detail + '\n') else:
elif argument_list[1] == 'password' or argument_list[1] == '-p': _new_lines = optimized_edit(open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'r').readlines(), _detail, _edit_line)
_detail = str(input('new password: ')) open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'w').writelines(_new_lines)
replace_line(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 0, _detail + '\n')
elif argument_list[1] == 'url' or argument_list[1] == '-l':
_detail = str(input('new url: '))
replace_line(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 2, _detail + '\n')
elif argument_list[1] == 'note' or argument_list[1] == '-n':
edit_note(_shm_folder, _shm_entry)
remove(f"{directory}{_entry_name}.gpg") remove(f"{directory}{_entry_name}.gpg")
print('\n\u001b[1mentry preview:\u001b[0m') print('\n\u001b[1mentry preview:\u001b[0m')
entry_reader(f"{tmp_dir}{_shm_folder}/{_shm_entry}") entry_reader(f"{tmp_dir}{_shm_folder}/{_shm_entry}")
encrypt(_shm_folder, _shm_entry, _entry_name) encrypt(directory + _entry_name, _shm_folder, _shm_entry, gpg, gpg_id)
def gen(): # generates a password for a new or an existing entry 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 _username, _url, _notes = None, None, None # sets base-line values to avoid errors
if argument == 'gen update' or argument == 'gen -u' or argument == 'gen': if argument == 'gen update' or argument == 'gen -u' or argument == 'gen':
_entry_name = entry_name_fetch('name of entry: ') _entry_name = entry_name_fetch('name of entry: ')
elif argument_list[1] == 'update' or argument_list[1] == '-u': elif argument_list[2] == 'update' or argument_list[2] == '-u':
_entry_name = entry_name_fetch(2) _entry_name = entry_name_fetch(2)
if not Path(f"{directory}{_entry_name}.gpg").is_file(): 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") print(f"\n\u001b[38;5;9merror: entry ({_entry_name}) does not exist\u001b[0m\n")
s_exit(1) s_exit(1)
else: else:
_entry_name = entry_name_fetch(1) _entry_name = entry_name_fetch(1)
if len(argument_list) == 1 or (not argument_list[1] == 'update' and not argument_list[1] == '-u'): 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(): if Path(f"{directory}{_entry_name}.gpg").is_file():
print(f"\n\u001b[38;5;9merror: entry ({_entry_name}) already exists\u001b[0m\n") print(f"\n\u001b[38;5;9merror: entry ({_entry_name}) already exists\u001b[0m\n")
s_exit(1) s_exit(1)
@@ -549,28 +572,25 @@ def gen(): # generates a password for a new or an existing entry
_add_note = input('add a note to this entry? (y/N) ') _add_note = input('add a note to this entry? (y/N) ')
_shm_folder, _shm_entry = shm_gen() _shm_folder, _shm_entry = shm_gen()
if _add_note.lower() == 'y': if _add_note.lower() == 'y':
system(f"nano {tmp_dir}{_shm_folder}/{_shm_entry}-n") system(f"{editor} {tmp_dir}{_shm_folder}/{_shm_entry}-n")
_notes = open(f"{tmp_dir}{_shm_folder}/{_shm_entry}-n", 'r').read() _notes = open(f"{tmp_dir}{_shm_folder}/{_shm_entry}-n", 'r').read()
else: else:
_notes = '' _notes = ''
open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'w').writelines(_password + '\n' + _username + '\n' + _url + '\n' open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'w')\
+ _notes) .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(_shm_folder, _shm_entry, _entry_name)
else: else:
_password = pass_gen()
_shm_folder, _shm_entry = shm_gen() _shm_folder, _shm_entry = shm_gen()
decrypt(directory + _entry_name, _shm_folder, _shm_entry) decrypt(directory + _entry_name, _shm_folder, _shm_entry, gpg)
replace_line(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 0, _password + '\n') _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") remove(f"{directory}{_entry_name}.gpg")
print('\n\u001b[1mentry preview:\u001b[0m') print('\n\u001b[1mentry preview:\u001b[0m')
entry_reader(f"{tmp_dir}{_shm_folder}/{_shm_entry}") entry_reader(f"{tmp_dir}{_shm_folder}/{_shm_entry}")
encrypt(_shm_folder, _shm_entry, _entry_name) encrypt(directory + _entry_name, _shm_folder, _shm_entry, gpg, gpg_id)
def copy_data(): # copies a specified field of an entry to the clipboard def copy_data(): # copies a specified field of an entry to the clipboard
if len(argument_list) < 3: if len(argument_list) < 4:
_entry_name = entry_name_fetch('entry to copy: ') _entry_name = entry_name_fetch('entry to copy: ')
else: else:
_entry_name = entry_name_fetch(2) _entry_name = entry_name_fetch(2)
@@ -578,46 +598,46 @@ def copy_data(): # copies a specified field of an entry to the clipboard
print(f"\n\u001b[38;5;9merror: entry ({_entry_name}) does not exist\u001b[0m\n") print(f"\n\u001b[38;5;9merror: entry ({_entry_name}) does not exist\u001b[0m\n")
s_exit(1) s_exit(1)
_shm_folder, _shm_entry = shm_gen() _shm_folder, _shm_entry = shm_gen()
decrypt(directory + _entry_name, _shm_folder, _shm_entry) decrypt(directory + _entry_name, _shm_folder, _shm_entry, gpg)
_copy_line = open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'r').readlines() _copy_line = open(f"{tmp_dir}{_shm_folder}/{_shm_entry}", 'r').readlines()
if uname()[0] == 'Haiku': # Haiku clipboard detection if uname()[0] == 'Haiku': # Haiku clipboard detection
if argument_list[1] == 'username' or argument_list[1] == '-u': if argument_list[2] == 'username' or argument_list[2] == '-u':
system('clipboard -c ' + "'" + _copy_line[1].replace('\n', '').replace("'", "'\\''") + "'") system('clipboard -c ' + "'" + _copy_line[1].replace('\n', '').replace("'", "'\\''") + "'")
elif argument_list[1] == 'password' or argument_list[1] == '-p': elif argument_list[2] == 'password' or argument_list[2] == '-p':
system('clipboard -c ' + "'" + _copy_line[0].replace('\n', '').replace("'", "'\\''") + "'") system('clipboard -c ' + "'" + _copy_line[0].replace('\n', '').replace("'", "'\\''") + "'")
elif argument_list[1] == 'url' or argument_list[1] == '-l': elif argument_list[2] == 'url' or argument_list[2] == '-l':
system('clipboard -c ' + "'" + _copy_line[2].replace('\n', '').replace("'", "'\\''") + "'") system('clipboard -c ' + "'" + _copy_line[2].replace('\n', '').replace("'", "'\\''") + "'")
elif argument_list[1] == 'note' or argument_list[1] == '-n': elif argument_list[2] == 'note' or argument_list[2] == '-n':
system('clipboard -c ' + "'" + _copy_line[3].replace('\n', '').replace("'", "'\\''") + "'") system('clipboard -c ' + "'" + _copy_line[3].replace('\n', '').replace("'", "'\\''") + "'")
Popen('sleep 30; clipboard -r', shell=True, close_fds=True) Popen('sleep 30; clipboard -r', shell=True, close_fds=True)
elif Path("/data/data/com.termux").exists(): # Termux (Android) clipboard detection elif Path("/data/data/com.termux").exists(): # Termux (Android) clipboard detection
if argument_list[1] == 'username' or argument_list[1] == '-u': if argument_list[2] == 'username' or argument_list[2] == '-u':
system('termux-clipboard-set ' + "'" + _copy_line[1].replace('\n', '').replace("'", "'\\''") + "'") system('termux-clipboard-set ' + "'" + _copy_line[1].replace('\n', '').replace("'", "'\\''") + "'")
elif argument_list[1] == 'password' or argument_list[1] == '-p': elif argument_list[2] == 'password' or argument_list[2] == '-p':
system('termux-clipboard-set ' + "'" + _copy_line[0].replace('\n', '').replace("'", "'\\''") + "'") system('termux-clipboard-set ' + "'" + _copy_line[0].replace('\n', '').replace("'", "'\\''") + "'")
elif argument_list[1] == 'url' or argument_list[1] == '-l': elif argument_list[2] == 'url' or argument_list[2] == '-l':
system('termux-clipboard-set ' + "'" + _copy_line[2].replace('\n', '').replace("'", "'\\''") + "'") system('termux-clipboard-set ' + "'" + _copy_line[2].replace('\n', '').replace("'", "'\\''") + "'")
elif argument_list[1] == 'note' or argument_list[1] == '-n': elif argument_list[2] == 'note' or argument_list[2] == '-n':
system('termux-clipboard-set ' + "'" + _copy_line[3].replace('\n', '').replace("'", "'\\''") + "'") system('termux-clipboard-set ' + "'" + _copy_line[3].replace('\n', '').replace("'", "'\\''") + "'")
Popen("sleep 30; termux-clipboard-set ''", shell=True, close_fds=True) Popen("sleep 30; termux-clipboard-set ''", shell=True, close_fds=True)
elif environ.get('WAYLAND_DISPLAY') == 'wayland-0': # Wayland clipboard detection elif environ.get('WAYLAND_DISPLAY') == 'wayland-0': # Wayland clipboard detection
if argument_list[1] == 'username' or argument_list[1] == '-u': if argument_list[2] == 'username' or argument_list[2] == '-u':
system('wl-copy ' + "'" + _copy_line[1].replace('\n', '').replace("'", "'\\''") + "'") system('wl-copy ' + "'" + _copy_line[1].replace('\n', '').replace("'", "'\\''") + "'")
elif argument_list[1] == 'password' or argument_list[1] == '-p': elif argument_list[2] == 'password' or argument_list[2] == '-p':
system('wl-copy ' + "'" + _copy_line[0].replace('\n', '').replace("'", "'\\''") + "'") system('wl-copy ' + "'" + _copy_line[0].replace('\n', '').replace("'", "'\\''") + "'")
elif argument_list[1] == 'url' or argument_list[1] == '-l': elif argument_list[2] == 'url' or argument_list[2] == '-l':
system('wl-copy ' + "'" + _copy_line[2].replace('\n', '').replace("'", "'\\''") + "'") system('wl-copy ' + "'" + _copy_line[2].replace('\n', '').replace("'", "'\\''") + "'")
elif argument_list[1] == 'note' or argument_list[1] == '-n': elif argument_list[2] == 'note' or argument_list[2] == '-n':
system('wl-copy ' + "'" + _copy_line[3].replace('\n', '').replace("'", "'\\''") + "'") system('wl-copy ' + "'" + _copy_line[3].replace('\n', '').replace("'", "'\\''") + "'")
Popen('sleep 30; wl-copy -c', shell=True, close_fds=True) Popen('sleep 30; wl-copy -c', shell=True, close_fds=True)
else: # X11 clipboard detection else: # X11 clipboard detection
if argument_list[1] == 'username' or argument_list[1] == '-u': if argument_list[2] == 'username' or argument_list[2] == '-u':
system('echo -n ' + "'" + _copy_line[1].replace('\n', '').replace("'", "'\\''") + "'" + ' | xclip -sel c') system('echo -n ' + "'" + _copy_line[1].replace('\n', '').replace("'", "'\\''") + "'" + ' | xclip -sel c')
elif argument_list[1] == 'password' or argument_list[1] == '-p': elif argument_list[2] == 'password' or argument_list[2] == '-p':
system('echo -n ' + "'" + _copy_line[0].replace('\n', '').replace("'", "'\\''") + "'" + ' | xclip -sel c') system('echo -n ' + "'" + _copy_line[0].replace('\n', '').replace("'", "'\\''") + "'" + ' | xclip -sel c')
elif argument_list[1] == 'url' or argument_list[1] == '-l': elif argument_list[2] == 'url' or argument_list[2] == '-l':
system('echo -n ' + "'" + _copy_line[2].replace('\n', '').replace("'", "'\\''") + "'" + ' | xclip -sel c') system('echo -n ' + "'" + _copy_line[2].replace('\n', '').replace("'", "'\\''") + "'" + ' | xclip -sel c')
elif argument_list[1] == 'note' or argument_list[1] == '-n': elif argument_list[2] == 'note' or argument_list[2] == '-n':
system('echo -n ' + "'" + _copy_line[3].replace('\n', '').replace("'", "'\\''") + "'" + ' | xclip -sel c') 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) Popen("sleep 30; echo -n '' | xclip -sel c", shell=True, close_fds=True)
rmtree(f"{tmp_dir}{_shm_folder}") rmtree(f"{tmp_dir}{_shm_folder}")
@@ -628,7 +648,7 @@ def remove_data(): # deletes an entry from the server and flags it for local de
_entry_name = entry_name_fetch('entry/folder to shear: ') _entry_name = entry_name_fetch('entry/folder to shear: ')
else: else:
_entry_name = entry_name_fetch(1) _entry_name = entry_name_fetch(1)
decrypt(path.expanduser('~/.config/sshyp/lock.gpg'), 0, 0) 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 " system(f"ssh -i '{path.expanduser('~/.ssh/sshyp')}' -p {port} {username_ssh}@{ip} \"cd /bin; python -c "
f"'import sshypRemote; sshypRemote.delete(\"'\"{_entry_name}\"'\")'\"") f"'import sshypRemote; sshypRemote.delete(\"'\"{_entry_name}\"'\")'\"")
@@ -637,11 +657,8 @@ if __name__ == "__main__":
try: try:
silent_sync, ssh_error = 0, 0 silent_sync, ssh_error = 0, 0
# retrieve typed argument # retrieve typed argument
argument_list, argument = argv, '' argument_list = argv
del argument_list[0] argument = ' '.join(argument_list[1:])
for _argument in argument_list:
argument += _argument + ' '
argument = argument[:-1]
if uname()[0] == 'Haiku': # set proper gpg command for OS if uname()[0] == 'Haiku': # set proper gpg command for OS
gpg = 'gpg --pinentry-mode loopback' gpg = 'gpg --pinentry-mode loopback'
else: else:
@@ -659,12 +676,17 @@ if __name__ == "__main__":
ip = ssh_info[1].replace('\n', '') ip = ssh_info[1].replace('\n', '')
port = ssh_info[2].replace('\n', '') port = ssh_info[2].replace('\n', '')
directory = str(ssh_info[3].replace('\n', '')) directory = str(ssh_info[3].replace('\n', ''))
directory_ssh = '/home/' + username_ssh + '/.password-pasture/' directory_ssh = str(ssh_info[4].replace('\n', ''))
client_device_name = listdir(path.expanduser('~/.config/sshyp/devices'))[0] client_device_name = listdir(path.expanduser('~/.config/sshyp/devices'))[0]
gpg_id = open(path.expanduser('~/.config/sshyp/sshyp-gpg')).read().strip() 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()) ssh_error = int(open(path.expanduser('~/.config/sshyp/ssh-error')).read().strip())
if ssh_error != 0: if ssh_error != 0:
ssh_error = copy_name_check(port, username_ssh, ip, client_device_name) 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): except (FileNotFoundError, IndexError):
if device_type.lower() != 's': if device_type.lower() != 's':
print('\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!') print('\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
@@ -683,53 +705,57 @@ if __name__ == "__main__":
elif argument == 'help' or argument == '--help' or argument == '-h' or argument == 'license' or argument \ elif argument == 'help' or argument == '--help' or argument == '-h' or argument == 'license' or argument \
== 'version' or argument == '-v': == 'version' or argument == '-v':
print_info() print_info()
elif argument_list[0] == 'add': elif argument_list[1] == 'add':
if len(argument_list) == 1: if len(argument_list) == 2:
print_info() print_info()
elif argument_list[1] == 'note' or argument_list[1] == '-n' or argument_list[1] == 'password' or \ elif argument_list[2] == 'note' or argument_list[2] == '-n' or argument_list[2] == 'password' or \
argument_list[1] == '-p': argument_list[2] == '-p':
add_entry() add_entry()
elif argument_list[1] == 'folder' or argument_list[1] == '-f': elif argument_list[2] == 'folder' or argument_list[2] == '-f':
add_folder() add_folder()
else: else:
print_info() print_info()
s_exit(0) s_exit(0)
elif argument_list[0] == 'edit': elif argument_list[1] == 'edit':
if len(argument_list) == 1: if len(argument_list) == 2:
print_info() print_info()
elif argument_list[1] == 'rename' or argument_list[1] == 'relocate' or argument_list[1] == '-r': elif argument_list[2] == 'rename' or argument_list[2] == 'relocate' or argument_list[2] == '-r':
silent_sync = 1 silent_sync = 1
rename() rename()
elif argument_list[1] == 'username' or argument_list[1] == '-u' or argument_list[1] == 'password' or \ elif argument_list[2] == 'username' or argument_list[2] == '-u' or argument_list[2] == 'password' or \
argument_list[1] == '-p' or argument_list[1] == 'url' or argument_list[1] == '-l' or \ argument_list[2] == '-p' or argument_list[2] == 'url' or argument_list[2] == '-l' or \
argument_list[1] == 'note' or argument_list[1] == '-n': argument_list[2] == 'note' or argument_list[2] == '-n':
edit() edit()
else: else:
print_info() print_info()
s_exit(0) s_exit(0)
elif argument_list[0] == 'gen': elif argument_list[1] == 'gen':
gen() gen()
elif argument_list[0] == 'copy': elif argument_list[1] == 'copy':
if len(argument_list) == 1: if len(argument_list) == 2:
print_info() print_info()
elif argument_list[1] == 'username' or argument_list[1] == '-u' or argument_list[1] == 'password' or \ elif argument_list[2] == 'username' or argument_list[2] == '-u' or argument_list[2] == 'password' or \
argument_list[1] == '-p' or argument_list[1] == 'url' or argument_list[1] == '-l' or \ argument_list[2] == '-p' or argument_list[2] == 'url' or argument_list[2] == '-l' or \
argument_list[1] == 'note' or argument_list[1] == '-n': argument_list[2] == 'note' or argument_list[2] == '-n':
copy_data() 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: else:
print_info() print_info()
elif argument_list[0] == 'shear' or argument_list[0] == '-rm': elif argument_list[1] == 'shear' or argument_list[1] == '-rm':
remove_data() remove_data()
elif argument_list[0] != 'sync' and argument_list[0] != '-s': 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") print(f"\n\u001b[38;5;9merror: invalid argument - run 'sshyp help' to list usable commands\u001b[0m\n")
s_exit(1) s_exit(1)
# sync if any changes were made # sync if any changes were made
if len(argument_list) > 0 and ssh_error == 0 and \ if len(argument_list) > 1 and ssh_error == 0 and \
((argument_list[0] == 'sync' or argument_list[0] == '-s' or argument_list[0] == 'gen' or ((argument_list[1] == 'sync' or argument_list[1] == '-s' or argument_list[1] == 'gen' or
argument_list[0] == 'shear' or argument_list[0] == '-rm') or ((argument_list[0] == 'add' argument_list[1] == 'shear' or argument_list[1] == '-rm') or ((argument_list[1] == 'add'
or argument_list[0] == 'edit') or argument_list[1] == 'edit')
and len(argument_list) > 1)): and len(argument_list) > 2)):
sync() sync()
s_exit(0) s_exit(0)
+12 -15
View File
@@ -1,4 +1,4 @@
#!/bin/python3 #!/usr/bin/env python3
# external modules # external modules
@@ -9,22 +9,19 @@ from shutil import rmtree
# utility functions # utility functions
def delete(_file_path): def delete(_file_path): # deletes an entry or folder, should be run remotely via ssh
if _file_path.endswith('/'): try:
try: if _file_path.endswith('/'):
rmtree(f"{expanduser('~/.password-pasture/')}{_file_path}") rmtree(f"{expanduser('~/.local/share/sshyp/')}{_file_path}")
except FileNotFoundError: else:
print('folder does not exist remotely') remove(f"{expanduser('~/.local/share/sshyp/')}{_file_path}.gpg")
else: except FileNotFoundError:
try: print('location does not exist remotely')
remove(f"{expanduser('~/.password-pasture/')}{_file_path}.gpg")
except FileNotFoundError:
print('file does not exist remotely')
for _device_name in listdir(expanduser('~/.config/sshyp/devices')): for _device_name in listdir(expanduser('~/.config/sshyp/devices')):
open(f"{expanduser('~/.config/sshyp/deleted/')}{_file_path.replace('/', '@')}^&*{_device_name}", 'w') open(f"{expanduser('~/.config/sshyp/deleted/')}{_file_path.replace('/', '@')}^&*{_device_name}", 'w')
def deletion_check(_client_device_name): 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('') open(expanduser('~/.config/sshyp/deletion_database'), 'w').write('')
for _file in listdir(expanduser('~/.config/sshyp/deleted')): for _file in listdir(expanduser('~/.config/sshyp/deleted')):
_file_path = _file.replace('@', '/').split('^&*')[0] _file_path = _file.replace('@', '/').split('^&*')[0]
@@ -37,9 +34,9 @@ def deletion_check(_client_device_name):
open(expanduser('~/.config/sshyp/deletion_database'), 'a').write(_file_path + '\n') open(expanduser('~/.config/sshyp/deletion_database'), 'a').write(_file_path + '\n')
def folder_check(): 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('') open(expanduser('~/.config/sshyp/folder_database'), 'w').write('')
for _root, _directories, _files in walk(expanduser('~/.password-pasture')): for _root, _directories, _files in walk(expanduser('~/.local/share/sshyp')):
for _dir in _directories: for _dir in _directories:
open(expanduser('~/.config/sshyp/folder_database'), 'a')\ open(expanduser('~/.config/sshyp/folder_database'), 'a')\
.write(f"{_root.replace(expanduser('~'), '')}/{_dir}\n") .write(f"{_root.replace(expanduser('~'), '')}/{_dir}\n")
+84
View File
@@ -1,3 +1,87 @@
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
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
sshyp v1.1.1
the sheecrets update - patch one
this is a polish/bug fix release that makes tweaks to the entry format (without breaking compatibility)
compatibility-breaking changes:
- none; the entry format tweaks are backward compatible with sshyp v1.0.0+
^ the new format is slightly more efficient in terms of storage space
^ if you would like to upgrade to it, run this script (after updating to v1.1.1):
https://raw.githubusercontent.com/rwinkhart/sshyp-labs/main/extra/conversion-scripts/sshyp-refresh-1.1.1%2B.py
user-facing features:
- entries (when editing or adding) are now run through the new optimized_edit() function to strip trailing new lines
and ensure the format of the entries is fully compatible with all of sshyp's functions
fixes:
- when adding a note to an entry, there is no longer a chance the first note line will be added as a url
if the entry is using an older format/pass format
- the gpg auto-generation file temporarily created by sshyp no longer includes extra spaces
^ seems to serve no functional purpose - fixed for the sake of polish
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
sshyp v1.1.0
the sheecrets update
the main focus of this release was adding extension support (particularly for sshyp-mfa)
and cleaning up the configuration experience
compatibility-breaking changes:
- the entry directory has been moved from ~/.password-pasture to ~/.local/share/sshyp
as a means of complying with the XDG base directory spec
^ this requires the user to move their entry directory to the new location manually
^ if this is not done, sshyp will not be able to find entries made prior to v1.1.0
- due to a new configuration option (preferred text editor), 'sshyp tweak' must be run after updating
user-facing features:
- extension support
^ some of sshyp's critical functions can now be imported into other programs to extend
upon sshyp's functionality
^ the first usecase of this is sshyp-mfa, which can be found at https://github.com/rwinkhart/sshyp-labs
- a new configuration experience
^ the 'sshyp tweak' menu has been made much more readable, and now the automatically generated
gpg key has its id automatically input, making the process easier for the user
- customizable text editor
^ the text editor used for editing notes can now be set to any editor preferred by the user
^ this means that nano was dropped as a dependency
fixes:
- to avoid exceptions on server devices, all arguments (apart from 'tweak') have been disabled on servers
- fixed entry readout not printing blank lines between fields under certain conditions
- various optimizations
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
sshyp v1.0.1 sshyp v1.0.1
The Polished Trotters Update - Patch 1 The Polished Trotters Update - Patch 1
Regular → Executable
+7 -7
View File
@@ -1,4 +1,4 @@
.TH sshyp 1 "20 June 2022" "v1.0.0" "sshyp man page" .TH sshyp 1 "27 August 2022" "v1.1.2" "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. sshyp \- A very simple self-hosted, synchronized password manager for UNIX(-like) systems. Alternative to (and compatible with) pass/password-store.
.SH SYNOPSIS .SH SYNOPSIS
@@ -12,25 +12,25 @@ Setting up sshyp for the first time or altering its configuration (also recommen
Viewing the entry database: Viewing the entry database:
sshyp sshyp
Reading an existing entry saved as ~/.password-pasture/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 ~/.password-pasture/financial/bank.gpg: Copying the password of an existing entry saved as ~/.local/share/sshyp/financial/bank.gpg:
sshyp copy -p /financial/bank sshyp copy -p /financial/bank
Editing the username of an existing entry saved as ~/.password-pasture/game.gpg: Editing the username of an existing entry saved as ~/.local/share/sshyp/game.gpg:
sshyp edit -u game sshyp edit -u game
Making a new entry saved as ~/.password-pasture/school/university.gpg using the built-in password generator: Making a new entry saved as ~/.local/share/sshyp/school/university.gpg using the built-in password generator:
sshyp gen /school/university sshyp gen /school/university
Creating a note-only entry saved as ~/.password-pasture/notes/testNote.gpg: Creating a note-only entry saved as ~/.local/share/sshyp/notes/testNote.gpg:
sshyp add -n /notes/testNote sshyp add -n /notes/testNote
Manually syncing entries with the server: Manually syncing entries with the server:
sshyp sync sshyp sync
Removing an existing folder saved as ~/.password-pasture/social: Removing an existing folder saved as ~/.local/share/sshyp/social:
sshyp shear social/ sshyp shear social/
.SH OPTIONS .SH OPTIONS
-48
View File
@@ -1,48 +0,0 @@
#!/bin/python3
from os import path, remove, system, walk
from pathlib import Path
from shutil import move, rmtree
if __name__ == "__main__":
gpg_id = input('\nWhat is the ID of the GPG key you would like to use for re-encryption?\n\nID: ')
pasture = path.expanduser('~/.password-pasture')
if Path(f"{pasture}.new").exists():
rmtree(f"{pasture}.new")
if Path(f"{pasture}.old").exists():
rmtree(f"{pasture}.old")
if path.isdir(pasture):
for dirPath, dirNames, filenames in walk(pasture):
for filename in sorted(filenames):
Path(dirPath.replace(pasture, pasture + '.new')).mkdir(0o700, parents=True, exist_ok=True)
system(f"gpg -d '{dirPath}/{filename}' "
f"> '{dirPath.replace(pasture, pasture + '.new')}/{filename[:-4]}'")
_old_contents, _new_contents = \
open(f"{dirPath.replace(pasture, pasture + '.new')}/{filename[:-4]}", 'r').readlines(), ''
for _num in range(len(_old_contents)):
if _num == 0:
try:
_new_contents += _old_contents[1]
except IndexError:
_new_contents += '\n'
elif _num == 1:
_new_contents += _old_contents[0]
elif _num == 3:
if _old_contents[_num] == ' ' or _old_contents[_num] == '\n':
pass
else:
_new_contents += _old_contents[_num]
else:
_new_contents += _old_contents[_num]
for _num in reversed(range(len(_new_contents))):
if _new_contents[_num] == '\n' or _new_contents[_num] == '':
_new_contents = _new_contents[:-1]
else:
break
open(f"{dirPath.replace(pasture, pasture + '.new')}/{filename[:-4]}", 'w').writelines(_new_contents)
system(f"gpg -qr {str(gpg_id)} -e '{dirPath.replace(pasture, pasture + '.new')}/{filename[:-4]}'")
remove(f"{dirPath.replace(pasture, pasture + '.new')}/{filename[:-4]}")
else:
print(f"\nError: no entry folder ({pasture}) found\n")
move(pasture, f"{pasture}.old")
move(f"{pasture}.new", pasture)
@@ -1 +0,0 @@
af6236472791614ed41bf13318a2d1c41228855c
-41
View File
@@ -1,41 +0,0 @@
#!/bin/python3
from os import path, remove, system, walk
from pathlib import Path
from shutil import move, rmtree
if __name__ == "__main__":
gpg_id = input('\nWhat is the ID of the GPG key you would like to use for re-encryption?\n\nID: ')
pasture = path.expanduser('~/.password-pasture')
if Path(f"{pasture}.new").exists():
rmtree(f"{pasture}.new")
if Path(f"{pasture}.old").exists():
rmtree(f"{pasture}.old")
if path.isdir(pasture):
for dirPath, dirNames, filenames in walk(pasture):
for filename in sorted(filenames):
Path(dirPath.replace(pasture, pasture + '.new')).mkdir(0o700, parents=True, exist_ok=True)
system(f"gpg -d '{dirPath}/{filename}' "
f"> '{dirPath.replace(pasture, pasture + '.new')}/{filename[:-4]}'")
_old_contents, _new_contents = \
open(f"{dirPath.replace(pasture, pasture + '.new')}/{filename[:-4]}", 'r').readlines(), ''
for _num in range(len(_old_contents)):
if _num == 3:
if _old_contents[_num] == ' ' or _old_contents[_num] == '\n':
pass
else:
_new_contents += _old_contents[_num]
else:
_new_contents += _old_contents[_num]
for _num in reversed(range(len(_new_contents))):
if _new_contents[_num] == '\n' or _new_contents[_num] == '':
_new_contents = _new_contents[:-1]
else:
break
open(f"{dirPath.replace(pasture, pasture + '.new')}/{filename[:-4]}", 'w').writelines(_new_contents)
system(f"gpg -qr {str(gpg_id)} -e '{dirPath.replace(pasture, pasture + '.new')}/{filename[:-4]}'")
remove(f"{dirPath.replace(pasture, pasture + '.new')}/{filename[:-4]}")
else:
print(f"\nError: no entry folder ({pasture}) found\n")
move(pasture, f"{pasture}.old")
move(f"{pasture}.new", pasture)
+59 -42
View File
@@ -3,12 +3,13 @@
# This script packages sshyp (from source) for various UNIX(-like) environments. # This script packages sshyp (from source) for various UNIX(-like) environments.
# Dependencies (Arch Linux): dpkg (packaging for Debian/Termux), freebsd-pkg (packaging for FreeBSD) # Dependencies (Arch Linux): dpkg (packaging for Debian/Termux), freebsd-pkg (packaging for FreeBSD)
# Dependencies (Fedora) (can only package for self): rpmdevtools # Dependencies (Fedora) (can only package for self): rpmdevtools
# 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
# NOTE It is recommended to instead use the latest officially packaged and tagged release. # NOTE It is recommended to instead use the latest officially packaged and tagged release.
echo -e '\nOptions (please enter the number only):' 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 '\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)' echo -e '\nBuild Scripts:\n\n7. Arch Linux (PKGBUILD)\n8. Alpine Linux (APKBUILD)'
echo -e '\nOther:\n\n8. All (generates all distribution packages (excluding Haiku and Fedora, as these must be packaged on their respective distributions) and build scripts)\n' 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 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' 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'
@@ -17,16 +18,8 @@ read -r -p "Version number: " version
echo -e '\nThe value entered in this field will only affect the revision number for build scripts.\n' echo -e '\nThe value entered in this field will only affect the revision number for build scripts.\n'
read -r -p "Revision number: " revision read -r -p "Revision number: " revision
if [ "$distro" == "7" ] || [ "$distro" == "8" ]; then if [ "$distro" == "7" ] || [ "$distro" == "8" ] || [ "$distro" == "9" ]; then
echo -e '\nOptions (please enter the number only):' source='https://github.com/rwinkhart/sshyp/releases/download/v$pkgver/sshyp-$pkgver.tar.xz'
echo -e '\n1. GitHub Release Tag\n2. Local\n'
read -r -p "Source (for build scripts): " source
if [ "$source" == "1" ]; then
source='https://github.com/rwinkhart/sshyp/releases/download/v$pkgver/sshyp-$pkgver.tar.xz'
else
source=local://sshyp-"$version".tar.xz
fi
fi fi
mkdir -p packages mkdir -p packages
@@ -55,13 +48,16 @@ requires {
gnupg gnupg
openssh openssh
python3 python3
nano
} }
urls { urls {
\"https://github.com/rwinkhart/sshyp\" \"https://github.com/rwinkhart/sshyp\"
} }
" > packages/haikutemp/.PackageInfo " > packages/haikutemp/.PackageInfo
cp -r bin packages/haikutemp/ cp -r bin packages/haikutemp/
sed -i '1 s/.*/#!\/bin\/env\ python3/' packages/haikutemp/bin/sshync.py
sed -i '1 s/.*/#!\/bin\/env\ python3/' packages/haikutemp/bin/sshyp.py
sed -i '1 s/.*/#!\/bin\/env\ python3/' packages/haikutemp/bin/sshypRemote.py
ln -s /bin/sshyp.py packages/haikutemp/bin/sshyp
cp -r share/doc/sshyp/ packages/haikutemp/documentation/packages/ cp -r share/doc/sshyp/ packages/haikutemp/documentation/packages/
cp -r share/licenses/sshyp/ packages/haikutemp/documentation/packages/ cp -r share/licenses/sshyp/ packages/haikutemp/documentation/packages/
cp extra/manpage packages/haikutemp/documentation/man/man1/sshyp.1 cp extra/manpage packages/haikutemp/documentation/man/man1/sshyp.1
@@ -75,7 +71,7 @@ urls {
echo -e "\nHaiku packaging complete.\n" echo -e "\nHaiku packaging complete.\n"
fi fi
if [ "$distro" == "2" ] || [ "$distro" == "8" ]; then if [ "$distro" == "2" ] || [ "$distro" == "9" ]; then
echo -e '\nPackaging for Debian...\n' echo -e '\nPackaging for Debian...\n'
mkdir -p packages/debiantemp/sshyp_"$version"-"$revision"_all/{DEBIAN,usr/share/man/man1} mkdir -p packages/debiantemp/sshyp_"$version"-"$revision"_all/{DEBIAN,usr/share/man/man1}
echo "Package: sshyp echo "Package: sshyp
@@ -84,11 +80,12 @@ 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, nano, xclip, wl-clipboard Depends: python3, gnupg, openssh-client, xclip, wl-clipboard
Priority: optional Priority: optional
Installed-Size: 185 Installed-Size: 185
" > packages/debiantemp/sshyp_"$version"-"$revision"_all/DEBIAN/control " > packages/debiantemp/sshyp_"$version"-"$revision"_all/DEBIAN/control
cp -r bin packages/debiantemp/sshyp_"$version"-"$revision"_all/usr/ cp -r bin packages/debiantemp/sshyp_"$version"-"$revision"_all/usr/
ln -s /usr/bin/sshyp.py packages/debiantemp/sshyp_"$version"-"$revision"_all/usr/bin/sshyp
cp -r share packages/debiantemp/sshyp_"$version"-"$revision"_all/usr/ cp -r share packages/debiantemp/sshyp_"$version"-"$revision"_all/usr/
cp extra/manpage packages/debiantemp/sshyp_"$version"-"$revision"_all/usr/share/man/man1/sshyp.1 cp extra/manpage packages/debiantemp/sshyp_"$version"-"$revision"_all/usr/share/man/man1/sshyp.1
gzip packages/debiantemp/sshyp_"$version"-"$revision"_all/usr/share/man/man1/sshyp.1 gzip packages/debiantemp/sshyp_"$version"-"$revision"_all/usr/share/man/man1/sshyp.1
@@ -98,21 +95,21 @@ Installed-Size: 185
echo -e "\nDebian packaging complete.\n" echo -e "\nDebian packaging complete.\n"
fi fi
if [ "$distro" == "5" ] || [ "$distro" == "8" ]; then if [ "$distro" == "5" ] || [ "$distro" == "9" ]; then
echo -e '\nPackaging for Termux...\n' echo -e '\nPackaging for Termux...\n'
mkdir -p packages/termuxtemp/sshyp_"$version"-"$revision"_all_termux/{data,DEBIAN} mkdir -p packages/termuxtemp/sshyp_"$version"-"$revision"_all_termux/{data/data/com.termux/files/usr/share/man/man1,DEBIAN}
mkdir -p packages/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/man/man1
echo "Package: sshyp echo "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: python, gnupg, openssh, nano, termux-api, termux-am Depends: python, gnupg, openssh, termux-api, termux-am
Priority: optional Priority: optional
Installed-Size: 185 Installed-Size: 185
" > packages/termuxtemp/sshyp_"$version"-"$revision"_all_termux/DEBIAN/control " > packages/termuxtemp/sshyp_"$version"-"$revision"_all_termux/DEBIAN/control
cp -r bin packages/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/ cp -r bin packages/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/
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
cp -r share packages/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/ cp -r share packages/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/
cp extra/manpage packages/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/man/man1/sshyp.1 cp extra/manpage packages/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/man/man1/sshyp.1
gzip packages/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/man/man1/sshyp.1 gzip packages/termuxtemp/sshyp_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/man/man1/sshyp.1
@@ -122,15 +119,16 @@ Installed-Size: 185
echo -e "\nTermux packaging complete.\n" echo -e "\nTermux packaging complete.\n"
fi fi
if [ "$distro" == "3" ] || [ "$distro" == "4" ] || [ "$distro" == "6" ] || [ "$distro" == "7" ] || [ "$distro" == "8" ]; then if [ "$distro" == "3" ] || [ "$distro" == "4" ] || [ "$distro" == "6" ] || [ "$distro" == "7" ] || [ "$distro" == "8" ] || [ "$distro" == "9" ]; then
echo -e '\nPackaging as generic...\n' echo -e '\nPackaging as generic...\n'
mkdir -p packages/archtemp/usr/share/man/man1 mkdir -p packages/generictemp/usr/share/man/man1
cp -r bin packages/archtemp/usr/ cp -r bin packages/generictemp/usr/
cp -r share packages/archtemp/usr/ ln -s /usr/bin/sshyp.py packages/generictemp/usr/bin/sshyp
cp extra/manpage packages/archtemp/usr/share/man/man1/sshyp.1 cp -r share packages/generictemp/usr/
gzip packages/archtemp/usr/share/man/man1/sshyp.1 cp extra/manpage packages/generictemp/usr/share/man/man1/sshyp.1
tar -C packages/archtemp -cvf packages/sshyp-"$version".tar.xz usr/ gzip packages/generictemp/usr/share/man/man1/sshyp.1
rm -rf packages/archtemp 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;}')" sha512="$(sha512sum packages/sshyp-"$version".tar.xz | awk '{print $1;}')"
echo -e "\nsha512 sum:\n$sha512" echo -e "\nsha512 sum:\n$sha512"
echo -e "\nGeneric packaging complete.\n" echo -e "\nGeneric packaging complete.\n"
@@ -147,11 +145,11 @@ 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: GPL3 License: GPLv3
URL: https://github.com/rwinkhart/sshyp URL: https://github.com/rwinkhart/sshyp
Source0: sshyp-"$version".tar.xz Source0: sshyp-"$version".tar.xz
Requires: python gnupg openssh nano wl-clipboard Requires: python gnupg openssh wl-clipboard
%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.
@@ -162,6 +160,7 @@ cp -r %{_sourcedir}/usr %{buildroot}
%files %files
/usr/bin/sshyp /usr/bin/sshyp
/usr/bin/sshyp.py
/usr/bin/sshync.py /usr/bin/sshync.py
/usr/bin/sshypRemote.py /usr/bin/sshypRemote.py
%license /usr/share/licenses/sshyp/license %license /usr/share/licenses/sshyp/license
@@ -174,11 +173,10 @@ rm -rf ~/rpmbuild
echo -e "\nFedora packaging complete.\n" echo -e "\nFedora packaging complete.\n"
fi fi
if [ "$distro" == "4" ] || [ "$distro" == "8" ]; then if [ "$distro" == "4" ] || [ "$distro" == "9" ]; then
echo -e '\nPackaging for FreeBSD...\n' echo -e '\nPackaging for FreeBSD...\n'
mkdir -p packages/FreeBSDtemp/bin mkdir -p packages/FreeBSDtemp/bin
tar xf packages/sshyp-"$version".tar.xz -C packages/FreeBSDtemp tar xf packages/sshyp-"$version".tar.xz -C packages/FreeBSDtemp
ln -s /usr/local/bin/python3 packages/FreeBSDtemp/bin/python3
echo "name: sshyp echo "name: sshyp
version: \""$version"\" version: \""$version"\"
abi = \"FreeBSD:13:*\"; abi = \"FreeBSD:13:*\";
@@ -196,9 +194,6 @@ prefix: /
\"gnupg\" : { \"gnupg\" : {
\"origin\" : \"security/gnupg\" \"origin\" : \"security/gnupg\"
}, },
\"nano\" : {
\"origin\" : \"editors/nano\"
},
\"xclip\" : { \"xclip\" : {
\"origin\" : \"x11/xclip\" \"origin\" : \"x11/xclip\"
}, },
@@ -207,9 +202,9 @@ prefix: /
}, },
}, },
" > packages/FreeBSDtemp/+MANIFEST " > packages/FreeBSDtemp/+MANIFEST
echo "/bin/python3 echo "/usr/bin/sshync.py
/usr/bin/sshync.py
/usr/bin/sshyp /usr/bin/sshyp
/usr/bin/sshyp.py
/usr/bin/sshypRemote.py /usr/bin/sshypRemote.py
/usr/share/doc/sshyp/changelog /usr/share/doc/sshyp/changelog
/usr/share/licenses/sshyp/license /usr/share/licenses/sshyp/license
@@ -220,10 +215,9 @@ rm -rf packages/FreeBSDtemp
echo -e "\nFreeBSD packaging complete.\n" echo -e "\nFreeBSD packaging complete.\n"
fi fi
if [ "$distro" == "7" ] || [ "$distro" == "8" ]; then if [ "$distro" == "7" ] || [ "$distro" == "9" ]; then
echo -e '\nGenerating PKGBUILD...' echo -e '\nGenerating PKGBUILD...'
echo "# Maintainer: Randall Winkhart <idgr at tutanota dot com> echo "# Maintainer: Randall Winkhart <idgr at tutanota dot com>
pkgname=sshyp pkgname=sshyp
pkgver="$version" pkgver="$version"
pkgrel="$revision" pkgrel="$revision"
@@ -231,16 +225,39 @@ pkgdesc='A light-weight, self-hosted, synchronized password manager'
url='https://github.com/rwinkhart/sshyp' url='https://github.com/rwinkhart/sshyp'
arch=('any') arch=('any')
license=('GPL3') license=('GPL3')
depends=(python gnupg openssh nano xclip wl-clipboard) depends=(python gnupg openssh xclip wl-clipboard)
source=(\""$source"\") source=(\""$source"\")
sha512sums=('"$sha512"') sha512sums=('"$sha512"')
package() { package() {
tar xf sshyp-"\"\$pkgver\"".tar.xz -C "\"\${pkgdir}\"" tar xf sshyp-"\"\$pkgver\"".tar.xz -C "\"\${pkgdir}\""
} }
" > packages/PKGBUILD " > packages/PKGBUILD
echo -e "\nPKGBUILD generated.\n" echo -e "\nPKGBUILD generated.\n"
fi fi
if [ "$distro" == "8" ] || [ "$distro" == "9" ]; then
echo -e '\nGenerating APKBUILD...'
echo "# 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-or-later'
depends='python3 gnupg openssh xclip wl-clipboard'
source=\""$source"\"
package() {
mkdir -p "\"\$pkgdir\""
cp -r "\"\$srcdir/usr/"\" "\"\$pkgdir\""
}
sha512sums=\"
"$sha512' 'sshyp-\"\$pkgver\".tar.xz"
\"
" > packages/APKBUILD
echo -e "\nAPKBUILD generated.\n"
fi
Regular → Executable
+12 -48
View File
@@ -1,59 +1,23 @@
sshyp v1.0.1 sshyp v1.1.2
The Polished Trotters Update - Patch 1 the sheecrets update - patch two
This is a hotfix for the Haiku decryption bug. this is a general polish/bugfix release
Changes: user-facing features:
- subprocess.Popen has been replaced with subprocess.run where applicable - packaging support for Alpine Linux/postmarketOS
- stdout is no longer captured with any subprocess.Popen or subprocess.run usages - the rename() function is no longer unnecessarily verbose
^ this fixes decryption on Haiku - handled exception for when the user attempts to copy an entry field that does not exist
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><> fixes/optimizations:
sshyp v1.0.0 - replaced shebang with "#!/usr/bin/env python3" for improved compatibility with various systems
^ does not affect Haiku packaging
The Polished Trotters Update - fixed the generic package not actually being compressed
- reduced lines of code used for cross-device entry deletion
This release brings 'sshyp' in line with the original goals of the project.
As such, it is being crowned "v1.0.0", and is considered a stable base for future expansions.
Features:
- sshyp has been modified to run on Haiku, an open-source re-implementation of BeOS
^ new package formats have been added for Haiku, FreeBSD, and Fedora Linux
- the entry format has been changed to support entries created in pass/password-store
^ this change requires existing entry libraries to be converted - see the sshyp v1.0.0 release page on GitHub for a conversion script
- majorly overhauled visual experience for sshyp
^ this includes a new entry list, a new entry readout, colored output, ascii art, and more!
- entries can now optionally be passed as arguments, directly
^ as opposed to the user being prompted for an entry name after running a command
Changes:
- corrected outdated licensing and copyright information
- 'if __name__=="__main__":' convention is now used
- help screens have been overhauled
- improved password generator to guarantee more secure results
- fixed errors when trying to sync with a multi-word entry on the server
- sshync now uses 'scp' (via the sftp protocol), rather than 'sftp'
- rename function now properly allows for renaming folders
- renaming an entry/folder will now properly delete the old entry/folder from the server
- entry previews at the time of entry creation/editing no longer require entering the gpg password more times than necessary
- entries will no longer be overwritten if attempting to create a new entry that uses an already existing name
- the clipboard now automatically clears after 30 seconds
- sshyp server no longer needs to create copies of its libraries in ~/
- added more and improved existing exceptions
- the lock file is now decrypted to /dev/null
- ...and lots of minor (under the hood) changes!
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><> <><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
The full history of changes to "sshyp" can be found on the following page: The full history of changes to "sshyp" can be found on the following page:
https://raw.githubusercontent.com/rwinkhart/sshyp/main/extra/changelog-total https://raw.githubusercontent.com/rwinkhart/sshyp/main/extra/changelog-total
Legend:
- = feature/change implemented and pushed upstream
^ = note regarding the above feature/change