SNS suceeded by VNS

This commit is contained in:
Jon-William Lewis
2017-08-24 16:10:42 -05:00
parent f63bc4c908
commit e4e4718128
3 changed files with 242 additions and 292 deletions

View File

@@ -1,24 +1,25 @@
Simple Note System
Vim Note System
==================
## About
The Simple Note System is a shell script partially inspired by [pass], in that
The Vim Note System is a shell script partially inspired by [pass], in that
it stores notes as normal, plaintext, files in normal folders. It uses the
environment-specified editor, and can be configured to use GPG encryption.
vim-gnupg plugin, and asymmetric GPG encryption.
SNS was originally conceived one morning during an update to a popular note-taking
app. The thought occurred that a note system need not reinvent the wheel with
its own GUI editor and proprietary file format, but instead could use the tools
already provided by the operating system.
VNS, originally SNS, was originally conceived one morning during an update
to a popular note-taking app. The thought occurred that a note system need
not reinvent the wheel with its own GUI editor and proprietary file format,
but instead could use the tools already provided by the operating system.
As it developed, OpenSSL encryption was dropped in favor of GPG, and the script
was almost entirely rewritten as SNSv2, and rewritten again as SNS v2a.
was almost entirely rewritten as SNSv2, rewritten again as SNS v2a, and forked
as VNS 1.0.
simple note system
vim note system
==================
usage: sns [-cedlp] <notebook/section/name>
sns [-hi]
usage: vns [-cedlp] <notebook/section/name>
vns [-hi]
-c | --create : Create note
-d | --delete : Delete note
-e | --edit : Open note for editing
@@ -28,51 +29,38 @@ was almost entirely rewritten as SNSv2, and rewritten again as SNS v2a.
-p | --print : Print note to console
## Installing
### Notice:
If you are upgrading to SNS v2a from an earlier version, please move
$HOME/.config/sns to $HOME/.local/sns
To migrate from SNS, simply move
$HOME/.local/sns to $HOME/.local/vns,
then append ".gpg" to each note name, for example
`find "$HOME/.local/vns" -type f -exec mv {} {}.gpg \;`
To install, place `sns.sh` in your path,
To install, place `vns` in your path,
To uninstall, remove the files you copied. Notes will still exist in $HOME/.local/sns
To uninstall, remove the files you copied. Notes will still exist in $HOME/.local/vns
Once installed, SNS will require you to run `sns -i [gpgkey]`, to indicate you would like
Once installed, VNS will require you to run `sns -i [gpgkey]`, to indicate you would like
to create its note store and write its default configuration.
By default, SNS will set itself up in `~/.local/sns`
**A word about encryption:**
When editing an encrypted note, SNS will decrypt
the note to a temporary file inside its store. The temporary file will have a
random name, but a predictable location. It can only be read by the user and
root, and is deleted after editing, but can be recovered by forensic utilities.
SNS's encryption is mainly useful when the store is being _transferred over a
network_.
## Vim Encryption
As of SNS v2b, SNS will look for a file named .vim in its store. If found, SNS will start in Vim mode, calling Vim directly instead of the environment's $EDITOR, and using Vim's native functionality for in-place encryption.
At this time, SNS does not provide a way to convert between store formats. Additionally, SNS v2b does not support printing Vim-encrypted notes to stdout; you will need to open the note for editing and copy/paste out of Vim for the time being.
Finally, when using Vim mode, treat -c and -e separately.
Encryption is handled by the vim-gnupg plugin. By default, the plugin creates temporary files on disk to
edit GPG-encrypted files. Setting `let g:GPGUsePipes=1` in .vimrc will bypass the creation of these files,
but the plugin author warns doing so may break certain CLI-based GPG agents.
## Tips and Tricks
* To list all notes in all notebooks, use `sns -l .`
## Credits
The code here is my own, however much of SNS v2a's design and behavior was
The code here is my own, however much of VNS's design and behavior was
influenced by [pass].
## License
Simple Note System is licensed under the terms of the GNU General Public License
Vim Note System is licensed under the terms of the GNU General Public License
Version 2, as detailed in `LICENSE`.
## To Do
* Switch back to calling $EDITOR instead of vim
* implement in-place encryption (vim only)
## Bugs and Feature Requests
If something seems off, or just doesn't work, please open an issue and I'll look
into it.

256
sns.sh
View File

@@ -1,256 +0,0 @@
#!/bin/bash
XL_PRODUCT="Simple Note System"
XL_VER="v2b"
# Environment Constants
readonly SNS_STORE="$HOME/.local/sns"
readonly SNS_KEYFILE="$SNS_STORE/.pubkey"
readonly IFS=$'\n\t'
set -euo pipefail
readonly SNS_RED_COLOR='\033[1;31m'
readonly SNS_YELLOW_COLOR='\033[1;33m'
readonly SNS_RESET_COLOR='\033[0m'
# Erroneous Exit Constants
# Invocation Errors
readonly SNS_ERR_NO_STORE=5 # The sns store needs to be initialized
readonly SNS_ERR_NO_OPTS=10 # No mode argument was specified
readonly SNS_ERR_NS_ARGS=11 # The specified mode requires an argument
readonly SNS_ERR_NO_EDTR=15 # No editor specified in environment
# Dependency Errors
readonly SNS_ERR_DEPS=20 # Base error - add the following codes
readonly SNS_ERR_NO_gpg2=1 # GPG is not installed
readonly SNS_ERR_NO_tree=2 # Tree is not installed
readonly SNS_ERR_NO_git=3 # Git is not installed
# Global Variables
typeset -i SNS_SID="$RANDOM"
typeset -a SNS_ACTION=("")
typeset -a MISSING_DEPS
typeset -i SNS_EXIT=0
declare SNS_PUBKEY=
# Function Definitions
# sns_printError Prints an error message
# sns_NoteHeader Prints standard note header to stdout
# sns_checkDeps Checks the system for required dependencies
# sns_checkStore Checks if $SNS_STORE exists
# sns_sanityCheck Wrapper for sns_checkDeps and sns_checkStore
# ----
# sns_initStore Initializes the SNS Store ( -i)
# sns_printHelp Prints help page ( -h)
# sns_list Lists all notes in `tree` format ( *)
# sns_create Creates note; calls sns_edit() ( -c)
# sns_edit Decrypts note to /tmp, calls editor, re-encrypts note ( -e)
# sns_print Prints note to stdout ( -p)
# sns_rm Deletes note from store ( -d)
# sns_gitPassthrough Passes through all instructions to git (git)
function sns_printError(){
printf "$SNS_RED_COLOR!$SNS_RESET_COLOR - %s\n" "$@"
}
function sns_NoteHeader(){
printf "%s\n%s\n" "Title:" "Date:"
}
function sns_checkDeps(){
local SNS_RETURN="true";
for DEP in "${SNS_DEPS[@]}"; do
if test ! -e "$(which "$DEP" 2>/dev/null)"; then
MISSING_DEPS+=("$DEP")
SNS_RETURN="false";
fi
done
"$SNS_RETURN";
}
function sns_checkStore(){
if [ -d "$SNS_STORE" ]; then true; else false; fi
}
function sns_sanityCheck {
set +u
if [ "$SNS_EDITOR" != "vim" ] && [ -z "$EDITOR" ]; then
echo "$SNS_EDITOR"
sns_printError "No editor specified in environment."
SNS_EXIT="$SNS_ERR_NO_EDTR"
return
fi
set -u
if ! sns_checkDeps; then
SNS_EXIT="$SNS_ERR_DEPS"
for DEP in "${MISSING_DEPS[@]}"; do
local SNS_DEP_EC="\$ERR_NO_$DEP"
sns_printError "Dependency %s not in path." "$DEP";
SNS_EXIT+=${!SNS_DEP_EC}
done
fi
if [ -r "$SNS_STORE/.pubkey" ]; then
SNS_PUBKEY="$(cat "$SNS_STORE/.pubkey")"
elif ! sns_checkStore; then
if [ "$(echo "${SNS_ACTION[@]}" | awk '{print $1;}')" != "sns_initStore" ]; then
sns_printError "The sns store does not exist."
printf " - %s\n" "Please run \`sns -i [gpg-key]\` to initialize sns."
SNS_EXIT="$SNS_ERR_NO_STORE"
fi
fi
}
#----
function sns_initStore {
mkdir -p "$SNS_STORE"
echo "$@" | awk '{print $1}' > "$SNS_KEYFILE"
}
function sns_printHelp(){
printf "%s" "usage: sns [-cedlp] <notebook/section/name>"
printf "\n%s%s%s" "usage: sns " "git" " ..."
printf "\n%s" " sns [-hi]"
printf "\n%s" " -c | --create : Create note"
printf "\n%s" " -C | --config : Edit Config"
printf "\n%s" " -d | --delete : Delete note"
printf "\n%s" " -e | --edit : Open note for editing"
printf "\n%s" " -h | --help : Display this message"
printf "\n%s" " -i | --init : Write default config and initalize SNS store"
printf "\n%s" " -l | --list : List all notes in NOTEBOOK"
printf "\n%s" " -p | --print : Print note to console"
printf "\n\n"
}
function sns_list(){
# Change directories to fix tree header
cd "$SNS_STORE" || exit; cd .. || exit;
# Print the tree
tree -C --noreport --prune "$(echo "$@" | awk '{print $1}')"
}
function sns_rm(){
rm -f "$SNS_STORE/$(echo "$@" | awk '{print $1}')"
}
function sns_gitPassthrough(){
cd "$SNS_STORE" || exit;
git "$@";
}
# ----
function sns_argParse(){
ARGS=($@)
#echo "${#ARGS}"
SNS_ACTION=()
while getopts ":i:hc:e:p:d:" OPT; do
# If an option requiring an argument was passed without an argument,
# inform the user and set exit code to "$SNS_ERR_NS_ARGS"
case "${ARGS[!OPTIND]}" in
-i|-c|-e|-p|-d)
if [ "${#ARGS}" -lt 2 ]; then
sns_printError "A required argument was not given."
SNS_EXIT="$SNS_ERR_NS_ARGS"
return
fi
esac
case "$OPT" in
i)
SNS_ACTION=("sns_initStore" "$OPTARG");;
h)
SNS_ACTION=("sns_printHelp");;
c)
SNS_ACTION=("sns_create $OPTARG");;
e)
SNS_ACTION=("sns_edit $OPTARG");;
p)
SNS_ACTION=("sns_print $OPTARG");;
d)
SNS_ACTION=("sns_rm $OPTARG");;
esac
done
if [ "${#SNS_ACTION[@]}" -eq 0 ]; then
if [ "$(echo "$@" | awk '{print $1}')" == "git" ]; then
SNS_ACTION=($@)
else
if [ -d "$SNS_STORE/$*" ]; then
SNS_ACTION=("sns_list" "sns/$@")
else
SNS_ACTION=("sns_list" "sns")
fi
fi
fi
}
# Entry Point
printf "%s\n%s\n" "$XL_PRODUCT" "$XL_VER"
printf "\n"
#Determine Run Mode
if [ -r "$SNS_STORE/.vim" ]; then
# Vim mode
readonly SNS_EDITOR="vim"
readonly SNS_DEPS=("vim" "tree" "git")
function sns_create(){
if [ -r "$SNS_STORE/$(echo "$@" | awk '{print $1}')" ]; then
sns_printError "Note already exists."
return
fi
vim -x "$SNS_STORE/$(echo "$@" | awk '{print $1}')"
}
function sns_edit(){
vim "$SNS_STORE/$(echo "$@" | awk '{print $1}')"
}
function sns_print(){
sns_printError "Printing of Vim-encrypted notes is not supported at this time."
}
else
readonly SNS_EDITOR="$EDITOR"
readonly SNS_DEPS=("gpg2" "tree" "git")
function sns_create(){
# Make sure the note doesn't already exist
if [ -r "$SNS_STORE/$(echo "$@" | awk '{print $1}')" ]; then
sns_printError "Note already exists."
return
fi
# Print the standard header to a temporary note
sns_NoteHeader > /tmp/"$SNS_SID"
# Edit the new note
sns_edit "$(echo "$@" | awk '{print $1}')"
}
function sns_edit(){
# Make the function more readable
local readonly SNS_NOTE="$SNS_STORE/$(echo "$@" | awk '{print $1}')"
# Test if edit was called from create
if [ ! -r /tmp/"$SNS_SID" ]; then gpg2 -d -o /tmp/"$SNS_SID" "$SNS_NOTE"; fi
# Edit the note
"$EDITOR" /tmp/"$SNS_SID"
# Make sure the notebook/section exists
if [ ! -d "$(dirname "$SNS_NOTE")" ]; then mkdir -p "$(dirname "$SNS_NOTE")"; fi
# If the note previously existed, make a backup.
if [ -r "$SNS_NOTE" ]; then mv "$SNS_NOTE" "$SNS_NOTE.bk"; fi
# Re-encrypt it to the store
gpg2 -r "$SNS_PUBKEY" -o "$SNS_NOTE" -e /tmp/"$SNS_SID"
# If all went well, remove the backup
if [ -r "$SNS_NOTE" ]; then rm "$SNS_NOTE.bk"; fi
}
function sns_print(){
gpg2 -d "$SNS_STORE/$(echo "$@" | awk '{print $1}')"
}
fi
sns_argParse "$@"
sns_sanityCheck
if [ "$SNS_EXIT" -eq 0 ]; then
eval "${SNS_ACTION[@]}"
fi
printf "\n"
exit "$SNS_EXIT"

218
vns.sh Executable file
View File

@@ -0,0 +1,218 @@
#!/bin/bash
XL_PRODUCT="Vim Note System"
XL_VER="v2b"
# Environment Constants
readonly VNS_STORE="$HOME/.local/vns"
readonly VNS_KEYFILE="$VNS_STORE/.pubkey"
readonly IFS=$'\n\t'
set -euo pipefail
readonly VNS_RED_COLOR='\033[1;31m'
readonly VNS_YELLOW_COLOR='\033[1;33m'
readonly VNS_RESET_COLOR='\033[0m'
# Erroneous Exit Constants
# Invocation Errors
readonly VNS_ERR_NO_STORE=5 # The vns store needs to be initialized
readonly VNS_ERR_NO_OPTS=10 # No mode argument was specified
readonly VNS_ERR_NS_ARGS=11 # The specified mode requires an argument
readonly VNS_ERR_NO_EDTR=15 # No editor specified in environment
# Dependency Errors
readonly VNS_ERR_DEPS=20 # Base error - add the following codes
readonly VNS_ERR_NO_gpg2=1 # GPG is not installed
readonly VNS_ERR_NO_tree=2 # Tree is not installed
readonly VNS_ERR_NO_git=3 # Git is not installed
# Global Variables
typeset -i VNS_SID="$RANDOM"
typeset -a VNS_ACTION=("")
typeset -a MISSING_DEPS
typeset -i VNS_EXIT=0
declare VNS_PUBKEY=
# Function Definitions
# vns_printError Prints an error message
# vns_NoteHeader Prints standard note header to stdout
# vns_checkDeps Checks the system for required dependencies
# vns_checkStore Checks if $VNS_STORE exists
# vns_sanityCheck Wrapper for vns_checkDeps and vns_checkStore
# ----
# vns_initStore Initializes the VNS Store ( -i)
# vns_printHelp Prints help page ( -h)
# vns_list Lists all notes in `tree` format ( *)
# vns_create Creates note; calls vns_edit() ( -c)
# vns_edit Decrypts note to /tmp, calls editor, re-encrypts note ( -e)
# vns_print Prints note to stdout ( -p)
# vns_rm Deletes note from store ( -d)
# vns_gitPassthrough Passes through all instructions to git (git)
function vns_printError(){
printf "$VNS_RED_COLOR!$VNS_RESET_COLOR - %s\n" "$@"
}
function vns_NoteHeader(){
printf "%s\n%s\n" "Title:" "Date:"
}
function vns_checkDeps(){
local VNS_RETURN="true";
for DEP in "${VNS_DEPS[@]}"; do
if test ! -e "$(which "$DEP" 2>/dev/null)"; then
MISSING_DEPS+=("$DEP")
VNS_RETURN="false";
fi
done
"$VNS_RETURN";
}
function vns_checkStore(){
if [ -d "$VNS_STORE" ]; then true; else false; fi
}
function vns_sanityCheck {
set +u
if [ "$VNS_EDITOR" != "vim" ] && [ -z "$EDITOR" ]; then
echo "$VNS_EDITOR"
vns_printError "No editor specified in environment."
VNS_EXIT="$VNS_ERR_NO_EDTR"
return
fi
set -u
if ! vns_checkDeps; then
VNS_EXIT="$VNS_ERR_DEPS"
for DEP in "${MISSING_DEPS[@]}"; do
local VNS_DEP_EC="\$ERR_NO_$DEP"
vns_printError "Dependency %s not in path." "$DEP";
VNS_EXIT+=${!VNS_DEP_EC}
done
fi
if [ -r "$VNS_STORE/.pubkey" ]; then
VNS_PUBKEY="$(cat "$VNS_STORE/.pubkey")"
elif ! vns_checkStore; then
if [ "$(echo "${VNS_ACTION[@]}" | awk '{print $1;}')" != "vns_initStore" ]; then
vns_printError "The vns store does not exist."
printf " - %s\n" "Please run \`vns -i [gpg-key]\` to initialize vns."
VNS_EXIT="$VNS_ERR_NO_STORE"
fi
fi
}
#----
function vns_initStore {
mkdir -p "$VNS_STORE"
echo "$@" | awk '{print $1}' > "$VNS_KEYFILE"
}
function vns_printHelp(){
printf "%s" "usage: vns [-cedlp] <notebook/section/name>"
printf "\n%s%s%s" "usage: vns " "git" " ..."
printf "\n%s" " vns [-hi]"
printf "\n%s" " -c | --create : Create note"
printf "\n%s" " -C | --config : Edit Config"
printf "\n%s" " -d | --delete : Delete note"
printf "\n%s" " -e | --edit : Open note for editing"
printf "\n%s" " -h | --help : Display this message"
printf "\n%s" " -i | --init : Write default config and initalize VNS store"
printf "\n%s" " -l | --list : List all notes in NOTEBOOK"
printf "\n%s" " -p | --print : Print note to console"
printf "\n\n"
}
function vns_list(){
# Change directories to fix tree header
cd "$VNS_STORE" || exit; cd .. || exit;
# Print the tree
tree -C --noreport --prune "$(echo "$@" | awk '{print $1}')"
}
function vns_rm(){
rm -f "$VNS_STORE/$(echo "$@" | awk '{print $1}')"
}
function vns_gitPassthrough(){
cd "$VNS_STORE" || exit;
git "$@";
}
# ----
function vns_argParse(){
ARGS=($@)
#echo "${#ARGS}"
VNS_ACTION=()
while getopts ":i:hc:e:p:d:" OPT; do
# If an option requiring an argument was passed without an argument,
# inform the user and set exit code to "$VNS_ERR_NS_ARGS"
case "${ARGS[!OPTIND]}" in
-i|-c|-e|-p|-d)
if [ "${#ARGS}" -lt 2 ]; then
vns_printError "A required argument was not given."
VNS_EXIT="$VNS_ERR_NS_ARGS"
return
fi
esac
case "$OPT" in
i)
VNS_ACTION=("vns_initStore" "$OPTARG");;
h)
VNS_ACTION=("vns_printHelp");;
c)
VNS_ACTION=("vns_create $OPTARG");;
e)
VNS_ACTION=("vns_edit $OPTARG");;
p)
VNS_ACTION=("vns_print $OPTARG");;
d)
VNS_ACTION=("vns_rm $OPTARG");;
esac
done
if [ "${#VNS_ACTION[@]}" -eq 0 ]; then
if [ "$(echo "$@" | awk '{print $1}')" == "git" ]; then
VNS_ACTION=($@)
else
if [ -d "$VNS_STORE/$*" ]; then
VNS_ACTION=("vns_list" "vns/$@")
else
VNS_ACTION=("vns_list" "vns")
fi
fi
fi
}
# Entry Point
printf "%s\n%s\n" "$XL_PRODUCT" "$XL_VER"
printf "\n"
readonly VNS_EDITOR="vim"
readonly VNS_DEPS=("vim" "gpg2" "tree" "git")
function vns_create(){
VNS_NOTE="$VNS_STORE/$(echo "$@" | awk '{print $1}').gpg"
# Make sure the note doesn't already exist
if [ -r "$VNS_NOTE" ]; then
vns_printError "Note already exists."
else
if [ ! -d "$(dirname $VNS_NOTE)" ]; then
mkdir -p "$(dirname $VNS_NOTE)"
fi
vns_NoteHeader | gpg2 -e -r "$VNS_PUBKEY" -o "$VNS_NOTE"
vns_edit "$@"
fi
}
function vns_edit(){
# Make the function more readable
local readonly VNS_NOTE="$VNS_STORE/$(echo "$@" | awk '{print $1}').gpg"
# Edit the note
vim "$VNS_NOTE"
}
function vns_print(){
gpg2 -d "$VNS_STORE/$(echo "$@" | awk '{print $1}')"
}
vns_argParse "$@"
vns_sanityCheck
if [ "$VNS_EXIT" -eq 0 ]; then
eval "${VNS_ACTION[@]}"
fi
printf "\n"
exit "$VNS_EXIT"