tunfinished refactoring, now using zuper - coffin - secure lan file storage on a device
HTML git clone git://parazyd.org/coffin.git
DIR Log
DIR Files
DIR Refs
DIR Submodules
DIR README
DIR LICENSE
---
DIR commit b7fbb10958f1c7aab9f940476f93963a9210d579
DIR parent 4bb95404c004caea78e233a8fb4b8ac0b5ba64bb
HTML Author: parazyd <parazyd@dyne.org>
Date: Mon, 21 Mar 2016 23:45:10 +0100
unfinished refactoring, now using zuper
Diffstat:
A .gitignore | 1 +
M README.md | 4 ++--
D bin/mourner | 23 -----------------------
D bin/sacrist | 219 -------------------------------
D gmakehook | 539 -------------------------------
A helpers/gmakehook | 539 +++++++++++++++++++++++++++++++
A src/mourner | 23 +++++++++++++++++++++++
A src/sacrist | 63 +++++++++++++++++++++++++++++++
A src/zlibs/features | 45 +++++++++++++++++++++++++++++++
A src/zlibs/hooks | 80 +++++++++++++++++++++++++++++++
A src/zlibs/keyfiles | 19 +++++++++++++++++++
A src/zlibs/mounts | 31 +++++++++++++++++++++++++++++++
A src/zlibs/ttab | 54 +++++++++++++++++++++++++++++++
A src/zlibs/zuper | 729 +++++++++++++++++++++++++++++++
A src/zlibs/zuper.init | 35 +++++++++++++++++++++++++++++++
15 files changed, 1621 insertions(+), 783 deletions(-)
---
DIR diff --git a/.gitignore b/.gitignore
t@@ -0,0 +1 @@
+NOTES.md
DIR diff --git a/README.md b/README.md
t@@ -14,9 +14,9 @@ files are once again unreadable.
* `gmakehook` can be used to create tombox hooks in a more user-friendly
manner. It is a GUI (zenity) helper script.
-## Installation
-
## Usage
+## Installation
+
## Troubleshooting
DIR diff --git a/bin/mourner b/bin/mourner
t@@ -1,23 +0,0 @@
-#!/bin/bash
-#
-# mourner - inotify script to watch /dev for new keys
-#
-# ~ parazyd
-
-pattern='sd[a-z][1-9]$'
-coproc inotifywait --monitor --event create,delete --format '%e %w%f' /dev
-
-while read -r -u "${COPROC[0]}" event file; do
- if [[ $file =~ $pattern ]]; then
- case $event in
- CREATE)
- echo "Created $file..."; sleep 1
- `pwd`/sacrist $file $event
- ;;
- DELETE)
- echo "Removed $file..."; sleep 1
- `pwd`/sacrist $file $event
- ;;
- esac
- fi
-done
DIR diff --git a/bin/sacrist b/bin/sacrist
t@@ -1,219 +0,0 @@
-#!/bin/zsh
-#
-# sacrist - script called by mourner, for our graveyard administration
-#
-# ~ parazyd
-
-device=$1
-happenz=$2
-keyuuid=$(blkid $device | awk -F\" '{print $2}')
-
-typeset -H keypass
-typeset -H keyuuid
-typeset -H undertaker
-typeset -H graveyard
-typeset -H tombs
-typeset -H tombpasswd
-
-# Vars
-graveyard="/home/graveyard" # Our graveyard, with all the tombs
-tombs="$graveyard/tombs" # Info about opened tombs, holds keyuuid, keyhash and tombid
-tmptombs="$graveyard/tmptombs" # Temp tempfile, for updating $tombs
-keymount="/media/tombkey" # Directory where keys get mounted
-coffindot="$keymount/.coffin" # .coffin directory on the usb key
-ttab="$coffindot/ttab" # Our ttab
-hooks="$coffindot/hook"
-tomb="/usr/local/bin/tomb"
-tombpasswd="$graveyard/passwd"
-
-# Debugs
-print "Arg1: $1"
-print "Arg2: $2"
-print "Device path is: $device"
-print "Device UUID is: $keyuuid"
-
-# {{{ msg
-_msg() {
- if [[ $1 == "error" ]]; then
- print "\e[1;31m[E] \e[0;31m$2 \e[0m"
- elif [[ $1 == "warn" ]]; then
- print "\e[1;33m[W] \e[0;33m$2 \e[0m"
- elif [[ $1 == "info" ]]; then
- print "\e[1;34m[i] \e[0;34m$2 \e[0m"
- fi
-}
-# }}}
-
-_umountkey() { umount $keymount; rmdir $keymount }
-_mountkey() { mkdir -p $keymount; mount $device $keymount }
-
-_hashkey() { cat $coffindot/$tombid.key | sha512sum | awk -F" " '{print $1}' }
-
-_comparekey() {
- keyhash=$(_hashkey)
- if [[ ( $(cat $tombs | grep $keyhash | grep $keyuuid) ) ]]; then
- happenz=close
- else
- happenz=open
- fi
-}
-
-_hooks() {
- for entry in $(cat $hooks); do
- let hook=$hook+1
- _msg info "Found hook $hook..."
-
- # Check what's hook supposed to do
- if [[ ${entry[(ws@:@)1]} == "create" ]]; then
- _create_new_tomb
- elif [[ ${entry[(ws@:@)1]} == "delete" ]]; then
- #DELETE TOMB
- elif [[ ${entry[(ws@:@)1]} == "backup" ]]; then
- # do backup
- else
- _msg error "No valid hook syntax on hook $hook"
- fi
- rm $hooks && _msg info "Removed $hooks"
- done
-}
-
-# {{{ ttabmagic
-_ttabmagic() {
- # Loop entire ttab and do stuff for tombs that want to be opened
- _msg info "Doing ttab magic..."
- line=0
-
- for entry in $(cat $ttab); do
- let line=$line+1
- _msg info "Found line $line..."
-
- if [[ ${entry[(ws@:@)3]} == "true" ]]; then
- _msg info "Working on tomb from line $line..."
- undertaker=${entry[(ws@:@)1]}
- print "Username: $undertaker"
- tombid=${entry[(ws@:@)2]}
- print "Tomb name: $tombid.tomb"
- _comparekey
-
- if [[ $happenz == "close" ]]; then
- _msg warn "Comparekey true"
- sudo -u $undertaker $tomb slam $tombid
- cp $tombs $tmptombs
- grep -v $keyhash $tmptombs > $tombs && chmod 600 $tombs && _msg info "Updated $tombs"
- rm $tmptombs
- continue
- fi
-
- _msg warn "Comparekey false"
- keypass=$(cat $tombpasswd | grep $keyhash | awk -F: '{print $2}')
- print "Tomb passphrase: $keypass"
- sudo -u $undertaker $tomb open $graveyard/$tombid.tomb -k $coffindot/$tombid.key \
- --unsafe --tomb-pwd "$keypass"
-
- chmod g+rw /media/$tombid
- if [[ -d "/media/$tombid" ]]; then
- print "$undertaker:$keyhash:$keyuuid" >> $tombs && chmod 600 $tombs && _msg info "Added info to $tombs"
- else
- _msg warn "Nothing added to $tombs"
- fi
- fi
-
- done
- umount $keymount && rmdir $keymount && _msg info "Unmounted and deleted $keymount"
-}
-# }}}
-
-# {{{ _create_new_tomb
-_create_new_tomb() {
- # TODO: options for webdav, sshfs, ipfs
- # TODO: recognize custom post/bind hooks and implement them inside the new tomb
- # TODO: delete/backup/foo hooks
-
- _msg info "Creating new tomb!"
- undertaker=${entry[(ws@:@)2]}
- tombid=${entry[(ws@:@)3]}
- tombsize=${entry[(ws@:@)4]}
- keypass=$(pwgen 30 -1 1)
-
- if ! [[ ( $(id $undertaker) ) ]]; then
- _msg warn "No user called $undertaker found. Creating..."
- useradd -G tombox -m -s /bin/bash $undertaker
- _msg info "Created user $undertaker"
- else
- _msg warn "User $undertaker exists. Continuing..."
- fi
-
- if [[ $entry =~ webdav && -f $coffindot/webdav.conf ]]; then
- _msg info "Found WebDAV data. Setting up..."
- if [[ -f $coffindot/davinfo ]]; then
- cat $coffindot/davinfo >> /etc/apache2/davpasswd
- rm $coffindot/davinfo
- gpasswd -a www-data $undertaker
- _msg info "Wrote to davpasswd"
- fi
- sed -i -e :a -e '$d;N;2,3ba' -e 'P;D' /etc/apache2/sites-available/tomboxdav.conf
- cat $coffindot/webdav.conf >> /etc/apache2/sites-available/tomboxdav.conf
- _msg info "Wrote to tomboxdav.conf"
- rm $coffindot/webdav.conf
- /etc/init.d/apache2 restart
- _msg info "Done setting up WebDAV!"
- else
- _msg info "No WebDAV data found. Continuing..."
- fi
-
- if [[ $entry =~ sshfs && -f $coffindot/sshpubkey ]]; then
- _msg info "Found SSH info. Setting up..."
- mkdir -p /home/$undertaker/.ssh
- cat $coffindot/sshpubkey >> /home/$undertaker/.ssh/authorized_keys
- chown -R $undertaker:$undertaker /home/$undertaker/.ssh
- _msg info "Wrote to authorized_keys"
- rm $coffindot/sshpubkey
- _msg info "Done setting up SSH."
- else
- _msg info "No SSH data found. Continuing..."
- fi
-
- sudo -u $undertaker $tomb dig -s $tombsize $graveyard/$tombid.tomb
- sudo -u $undertaker $tomb forge $graveyard/$tombid.key --unsafe --tomb-pwd "$keypass"
- sudo -u $undertaker $tomb lock $graveyard/$tombid.tomb -k $graveyard/$tombid.key \
- --unsafe --tomb-pwd "$keypass"
-
- mv $graveyard/$tombid.key $coffindot/ && chown $undertaker:$undertaker $coffindot/$tombid.key && \
- _msg info "Moved and chowned keyfile"
- print "${undertaker}:${tombid}:true" >> $ttab
- keyhash=$(_hashkey)
- print "${keyhash}:${keypass}" >> $tombpasswd
- _msg info "Wrote to $ttab and $tombpasswd"
- # rm $createme && _msg info "Removed $createme"
-}
-# }}}
-
-_endgame() {
- # Mr. Proper
-}
-
-
-# {{{ MAIN
-if [[ $happenz == "CREATE" ]]; then
- _mountkey
- if [[ -d "$coffindot" ]]; then
- _msg info "$coffindot found..."
-
- if [[ -f "$hooks" ]]; then
- _hooks
- else
- _msg warn "No hooks detected"
- fi
-
- if ! [[ -f "$ttab" ]]; then
- _msg error "No ttab!"
- umount $keymount && rmdir $keymount && _msg info "Unmounted and removed $keymount"
- else
- _ttabmagic
- fi
- else
- _msg error "No valid .coffin directory! Exiting..."
- _umountkey && _msg info "Unmounted and removed $keymount"
- fi
-fi
-# }}}
DIR diff --git a/gmakehook b/gmakehook
t@@ -1,539 +0,0 @@
-#!/usr/bin/env zsh
-#
-# Zenity helper script to make tombox hooks more user-friendly
-#
-# ~ parazyd
-
-typeset -H UNDERTAKER
-typeset -H TOMBSIZE
-typeset -H TOMBNAME
-typeset -H sudoassword
-
-keymount="/media/tombkey"
-coffindot="$keymount/.coffin"
-hooks="$coffindot/hook"
-
-# {{{ icon
-MONMORT="/tmp/monmort.png"
-ICONB64="iVBORw0KGgoAAAANSUhEUgAAACAAAAAgEAQAAACJ4248AAAAAmJLR0T//xSrMc0AAAAJcEhZcwAA
-AEgAAABIAEbJaz4AAAAJdnBBZwAAACAAAAAgAIf6nJ0AAADrSURBVFjD5ZZBEsMgCEU/TO/l2XMx
-04VjQ1K1CDYswkwWJnH+E/EL4RP7jluDCACoim/bvfIpFQiKEJcQHCN9xEtLCgDMQM7f33sZrPNG
-/05loCXujfAtCAVgNgLwIuycjQAra8G9Fm823ADabPRA1QDelfZAVUZktWrNvL8ew5YTnsStx3Am
-AyOInJVbYF1prZuU+tsR1g9UMDqGuo5oFWhtSEQNEGmeVrqv73Tj0pIZirANMYqRhyw5Bb9MauSW
-SwA8l9OzG5LnAsiiDQGQRRvaEwInK54J390hndAIYIeQ4k6AAjE/h06ab0SjP08MA1xDAAAAJXRF
-WHRkYXRlOmNyZWF0ZQAyMDExLTAxLTEyVDA5OjM0OjI0KzAxOjAwo//d7wAAACV0RVh0ZGF0ZTpt
-b2RpZnkAMjAxMS0wMS0xMlQwOTozNDoyNCswMTowMNKiZVMAAAAASUVORK5CYII="
-print "$ICONB64" | base64 --decode > $MONMORT
-# }}}
-
-# {{{ sudo functions
-function _sudo {
- sudoassword=$(ask_password "Insert sudo password for user $USER")
- print "$sudoassword\n" | sudo -S -v
- _sudowrong
-}
-function _sudowrong {
- if [[ $? == 1 ]]; then
- sudoassword=$(ask_password "Wrong password. Insert sudo password for user $USER")
- print "$sudoassword\n" | sudo -S -v
- _sudowrong
- fi
-}
-# }}}
-
-# {{{ Some pinentry code shamelessly stolen from tomb
-# Ask user for a password
-# Wraps around the pinentry command, from the GnuPG project, as it
-# provides better security and conveniently use the right toolkit.
-ask_password() {
- local description="$1"
- local title="${2:-Enter tomb password.}"
- local output
- local password
- local gtkrc
- local theme
-
- # Distributions have broken wrappers for pinentry: they do
- # implement fallback, but they disrupt the output somehow. We are
- # better off relying on less intermediaries, so we implement our
- # own fallback mechanisms. Pinentry supported: curses, gtk-2, qt4
- # and x11.
-
- # make sure LANG is set, default to C
- LANG=${LANG:-C}
-
-
- if [[ "$DISPLAY" = "" ]]; then
-
- if _is_found "pinentry-curses"; then
- output=`cat <<EOF | pinentry-curses
-OPTION ttyname=$TTY
-OPTION lc-ctype=$LANG
-SETTITLE $title
-SETDESC $description
-SETPROMPT Password:
-GETPIN
-EOF`
- fi
-
- else # a DISPLAY is found to be active
-
- # customized gtk2 dialog with a skull (if extras are installed)
- if _is_found "pinentry-gtk-2"; then
-
- gtkrc=""
- theme=/share/themes/tomb/gtk-2.0-key/gtkrc
- for i in /usr/local /usr; do
- [[ -r $i/$theme ]] && {
- gtkrc="$i/$theme"
- break
- }
- done
- [[ "$gtkrc" = "" ]] || {
- gtkrc_old="$GTK2_RC_FILES"
- export GTK2_RC_FILES="$gtkrc"
- }
- output=`cat <<EOF | pinentry-gtk-2
-OPTION ttyname=$TTY
-OPTION lc-ctype=$LANG
-SETTITLE $title
-SETDESC $description
-SETPROMPT Password:
-GETPIN
-EOF`
- [[ "$gtkrc" = "" ]] || export GTK2_RC_FILES="$gtkrc_old"
-
- # TODO QT4 customization of dialog
- elif _is_found "pinentry-qt4"; then
-
- # TODO X11 customization of dialog
- elif _is_found "pinentry-x11"; then
-
- output=`cat <<EOF | pinentry-x11
-OPTION ttyname=$TTY
-OPTION lc-ctype=$LANG
-SETTITLE $title
-SETDESC $description
-SETPROMPT Password:
-GETPIN
-EOF`
-
- else
-
- if _is_found "pinentry-curses"; then
-
- output=`cat <<EOF | pinentry-curses
-OPTION ttyname=$TTY
-OPTION lc-ctype=$LANG
-SETTITLE $title
-SETDESC $description
-SETPROMPT Password:
-GETPIN
-EOF`
- else
- fi
-
- fi
-
- fi # end of DISPLAY block
-
- # parse the pinentry output
- for i in ${(f)output}; do
- [[ "$i" =~ "^ERR.*" ]] && {
- print "canceled"
- return 1 }
-
- # here the password is found
- [[ "$i" =~ "^D .*" ]] && password="${i##D }"
- done
-
- [[ "$password" = "" ]] && {
- print "empty"
- return 1 }
-
- print "$password"
- return 0
-}
-
-_is_found() {
- # returns 0 if binary is found in path
- [[ "$1" = "" ]] && return 1
- command -v "$1" 1>/dev/null 2>/dev/null
- return $?
-}
-# }}}
-
-# {{{ _clean - Clean function, removes sensitive stuff from memory
-function _clean {
- TOMBSIZE="$rr"; unset TOMBSIZE
- TOMBNAME="$rr"; unset TOMBNAME
- UNDERTAKER="$rr"; unset UNDERTAKER
- sudoassword="$rr"; unset sudoassword
-}
-# }}}
-
-# {{{ zenity dialogs
-function _fsel {
- zenity \
- --window-icon="$MONMORT" \
- --file-selection \
- --title="$1"
-}
-function _zenques {
- zenity \
- --window-icon="$MONMORT" \
- --question \
- --text="$1"
-}
-function _zenwarn {
- zenity \
- --window-icon="$MONMORT" \
- --warning \
- --title="$1" \
- --text="$2"
-}
-function _zeninfo {
- zenity \
- --window-icon="$MONMORT" \
- --info \
- --title="$1" \
- --text="$2"
-}
-function _zenerr {
- zenity \
- --window-icon="$MONMORT" \
- --error \
- --title="$1" \
- --text="$2"
-}
-function _zenentry {
- zenity \
- --window-icon="$MONMORT" \
- --entry \
- --title="$1" \
- --text="$2" \
- --entry-text="$3"
-}
-# }}}
-
-function _umountkey { sudo umount $keymount && sudo rmdir $keymount }
-function _mountkey {
- sudo umount $USBKEY
- if [[ $? = 1 ]]; then
- _zenerr "Error" "Your key is mounted somewhere and I've run into issues. Please unmount it and re-run."
- _clean && exit
- fi
- if [[ -d $keymount ]]; then
- _zenwarn "Warning" "Something is already mounted on $keymount. Please check and re-run."
- _clean && exit
- fi
-
- sudo mkdir -p $keymount
- sudo mount $USBKEY $keymount
-
- if ! [[ -d $coffindot ]]; then
- sudo mkdir $coffindot
- fi
-}
-
-# {{{ _writedavinfo
-function _writedavinfo {
- if [[ -n $davpass ]]; then
- davinfo=$(echo -n "$UNDERTAKER:WebDAV:" \
- && echo -n "$UNDERTAKER:WebDAV:$davpass" \
- | md5sum \
- | awk '{print $1}')
- print $davinfo | sudo tee $coffindot/davinfo
- fi
-
- cat <<EOF | sudo tee $coffindot/webdav.conf
-
- alias /${TOMBNAME} /media/${TOMBNAME}
- <Directory "/media/${TOMBNAME}">
- Dav On
- AllowOverride none
- Options Indexes FollowSymlinks
- AuthType Digest
- AuthName WebDAV
- AuthUserFile /etc/apache2/davpasswd
- Require user ${UNDERTAKER}
- </Directory>
-
- </VirtualHost>
-</IfModule>
-EOF
-}
-# }}}
-
-function _main {
- _clean
- cmnd=$(zenity \
- --window-icon="$MONMORT" \
- --title="tombox hook helper" \
- --list \
- --hide-header \
- --text="tombox hook helper\nChoose what hook you want to create:" \
- --separator=" # " \
- --column=Function \
- --column=Description \
- "create" "a new tomb on the tombox" \
- "delete" "a tomb from the tombox" \
- "backup" "a tomb from the tombox" \
- "foobar" "on the tombox")
- eval "_$cmnd"
-}
-
-# {{{ _create
-function _create {
- vars=$(zenity \
- --window-icon="$MONMORT" \
- --title="Create a new tomb" \
- --forms \
- --text="Enter the info for your new tomb" \
- --separator=":" \
- --add-entry="Tombox username" \
- --add-entry="Tomb name" \
- --add-entry="Tomb's size in MiB")
- if [[ $? = 1 ]]; then
- exec _main
- else
- UNDERTAKER=${vars[(ws@:@)1]}
- re='^[A-Za-z0-9]+$'
- if ! [[ $UNDERTAKER =~ $re ]]; then
- _zenwarn "Warning" "Invalid characters in username!"
- exec _create
- fi
- TOMBNAME=${vars[(ws@:@)2]}
- if ! [[ $TOMBNAME =~ $re ]]; then
- _zenwarn "Warning" "Invalid characters in tomb name!"
- exec _create
- fi
- TOMBSIZE=${vars[(ws@:@)3]}
- re='^[0-9]+$'
- if ! [[ $TOMBSIZE =~ $re ]]; then
- _zenwarn "Warning" "Invalid characters in tomb size!"
- exec _create
- fi
- fi
-
- feats=$(zenity \
- --window-icon="$MONMORT" \
- --title="Choose features" \
- --text="Choose features you wish to include with your tomb" \
- --list \
- --checklist \
- --separator=":" \
- --column=Yes/No \
- --column=Feature \
- "FALSE" "webdav" \
- "FALSE" "sshfs" \
- "FALSE" "ipfs")
- if [[ $? = 1 ]]; then; exec _main; fi
-
- if [[ $feats =~ "webdav" ]]; then
- _zenques "Do you already have a WebDAV login?"
- if [[ $? = 1 ]]; then
- davpass=$(_zenentry "WebDAV Password" \
- "Insert the password you want to use for WebDAV login:" \
- "my very strong password")
- if [[ $? = 1 ]]; then; davpass=""; fi
- fi
- fi
-
- if [[ $feats =~ "sshfs" ]]; then
- _zenques "Do you already have an SSH key setup for your user?"
- if [[ $? = 1 ]]; then
- sshpubkey=$(_fsel "Select your SSH pubkey you want to use.")
- else
- sshpubkey=""
- fi
- fi
-
- TOMBHOOKS=$(_zenques "Choose 'Yes' if you want to edit your tomb's bind-hooks and post-hooks")
- if [[ $? = 0 ]]; then
- bindhook=$(zenity \
- --window-icon="$MONMORT" \
- --title="bind-hooks" \
- --checkbox="Accept these bind-hooks" \
- --text-info \
- --editable)
- if [[ $? = 1 ]]; then; exec _main; fi
-
- posthook=$(zenity \
- --window-icon="$MONMORT" \
- --title="post-hooks" \
- --checkbox="Accept these post-hooks" \
- --text-info \
- --editable)
- if [[ $? = 1 ]]; then; exec _main; fi
- fi
-
- _zeninfo "gmakehook" "Plug in your USB key and click OK."
- lsblk -npl | awk -F" " '{print $1 " " $4}' | grep '^/dev/sd.. ' > ./.devs
-
- USBKEY=$(zenity \
- --window-icon="$MONMORT" \
- --title="Choose USB key" \
- --list \
- --text="Choose your USB key to use" \
- --separator=" " \
- --column="Device path" \
- --column="Device size" \
- $(cat ./.devs))
- if [[ $? = 1 ]]; then; exec _main; fi
-
- _sudo
- _mountkey
- if [[ -f $hooks ]]; then
- _zenques "Warning! There is already an existing hook. Do you want to overwrite?"
- if [[ $? = 1 ]]; then
- _zeninfo "gmakehook" "Postponing..." && _umountkey && exec _main
- else
- print "create:${UNDERTAKER}:${TOMBNAME}:${TOMBSIZE}:${feats}" | sudo tee $hooks
- if [[ $feats =~ "webdav" ]]; then; _writedavinfo; fi
- if [[ -n $sshpubkey ]]; then; sudo cp $sshpubkey ./sshpubkey ; fi
- if [[ -n $bindhook ]]; then
- print "$bindhook" | sudo tee $coffindot/bindhooks
- fi
- if [[ -n $posthook ]]; then
- print "$posthook" | sudo tee $coffindot/posthooks
- fi
- _umountkey && _zeninfo "Success" "$hooks written successfully!\nPlug the USB key in a tombox to activate it."
- exec _main
- fi
- else
- print "create:${UNDERTAKER}:${TOMBNAME}:${TOMBSIZE}:${feats}" | sudo tee $hooks
- if [[ $feats =~ "webdav" ]]; then; _writedavinfo; fi
- if [[ -n $sshpubkey ]]; then; sudo cp $sshpubkey $coffindot/sshpubkey ; fi
- if [[ -n $bindhook ]]; then
- print "$bindhook" | sudo tee $coffindot/bindhooks
- fi
- if [[ -n $posthook ]]; then
- print "$posthook" | sudo tee $coffindot/posthooks
- fi
- _umountkey && _zeninfo "Success" "$hooks written successfully!\nPlug the USB key in the tombox to activate it."
- exec _main
- fi
-}
-# }}}
-
-# {{{ _delete
-function _delete {
- vars=$(zenity \
- --window-icon="$MONMORT" \
- --title="Delete an existing tomb" \
- --forms \
- --text="Enter the info of your tomb" \
- --separator=":" \
- --add-entry="Tombox username" \
- --add-entry="Tomb name")
- if [[ $? = 1 ]]; then
- exec _main
- else
- UNDERTAKER=${vars[(ws@:@)1]}
- re='^[A-Za-z0-9]+$'
- if ! [[ $UNDERTAKER =~ $re ]]; then
- _zenwarn "Warning" "Invalid characters in username!"
- exec _delete
- fi
- TOMBNAME=${vars[(ws@:@)2]}
- if ! [[ $TOMBNAME =~ $re ]]; then
- _zenwarn "Warning" "Invalid characters in tomb name!"
- exec _delete
- fi
- fi
-
- _zeninfo "gmakehook" "Plug in your USB key and click OK."
- lsblk -npl | awk -F" " '{print $1 " " $4}' | grep '^/dev/sd.. ' > ./.devs
-
- USBKEY=$(zenity \
- --window-icon="$MONMORT" \
- --title="Choose USB key" \
- --list \
- --text="Choose your USB key to use" \
- --separator=" " \
- --column="Device path" \
- --column="Device size" \
- $(cat ./.devs))
- if [[ $? = 1 ]]; then; exec _main; fi
-
- _sudo
- _mountkey
- if [[ -f $hooks ]]; then
- _zenques "Warning! There is already an existing hook. Do you want to overwrite?"
- if [[ $? = 1 ]]; then
- _zeninfo "gmakehook" "Postponing..." && _umountkey && exec _main
- else
- print "delete:${UNDERTAKER}:${TOMBNAME}" | sudo tee $hooks
- _umountkey && _zeninfo "Success" "$hooks written successfully!\nPlug the USB key in a tombox to activate it."
- exec _main
- fi
- else
- print "delete:${UNDERTAKER}:${TOMBNAME}" | sudo tee $hooks
- _umountkey && _zeninfo "Success" "$hooks written successfully!\nPlug the USB key in the tombox to activate it."
- exec _main
- fi
-}
-# }}}
-
-# {{{ _backup
-function _backup {
- vars=$(zenity \
- --window-icon="$MONMORT" \
- --title="Backup an existing tomb" \
- --forms \
- --text="Enter the info of your tomb" \
- --separator=":" \
- --add-entry="Tombox username" \
- --add-entry="Tomb name")
- if [[ $? = 1 ]]; then
- exec _main
- else
- UNDERTAKER=${vars[(ws@:@)1]}
- re='^[A-Za-z0-9]+$'
- if ! [[ $UNDERTAKER =~ $re ]]; then
- _zenwarn "Warning" "Invalid characters in username!"
- exec _delete
- fi
- TOMBNAME=${vars[(ws@:@)2]}
- if ! [[ $TOMBNAME =~ $re ]]; then
- _zenwarn "Warning" "Invalid characters in tomb name!"
- exec _delete
- fi
- fi
-
- _zeninfo "gmakehook" "Plug in your USB key and click OK."
- lsblk -npl | awk -F" " '{print $1 " " $4}' | grep '^/dev/sd.. ' > ./.devs
-
- USBKEY=$(zenity \
- --window-icon="$MONMORT" \
- --title="Choose USB key" \
- --list \
- --text="Choose your USB key to use" \
- --separator=" " \
- --column="Device path" \
- --column="Device size" \
- $(cat ./.devs))
- if [[ $? = 1 ]]; then; exec _main; fi
-
- _sudo
- _mountkey
- if [[ -f $hooks ]]; then
- _zenques "Warning! There is already an existing hook. Do you want to overwrite?"
- if [[ $? = 1 ]]; then
- _zeninfo "gmakehook" "Postponing..." && _umountkey && exec _main
- else
- print "backup:${UNDERTAKER}:${TOMBNAME}" | sudo tee $hooks
- _umountkey && _zeninfo "Success" "$hooks written successfully!\nPlug the USB key in a tombox to activate it."
- exec _main
- fi
- else
- print "backup:${UNDERTAKER}:${TOMBNAME}" | sudo tee $hooks
- _umountkey && _zeninfo "Success" "$hooks written successfully!\nPlug the USB key in the tombox to activate it."
- exec _main
- fi
-}
-# }}}
-
-function _ { rm ./.devs }
-exec _main
DIR diff --git a/helpers/gmakehook b/helpers/gmakehook
t@@ -0,0 +1,539 @@
+#!/usr/bin/env zsh
+#
+# Zenity helper script to make tombox hooks more user-friendly
+#
+# ~ parazyd
+
+typeset -H UNDERTAKER
+typeset -H TOMBSIZE
+typeset -H TOMBNAME
+typeset -H sudoassword
+
+keymount="/media/tombkey"
+coffindot="$keymount/.coffin"
+hooks="$coffindot/hook"
+
+# {{{ icon
+MONMORT="/tmp/monmort.png"
+ICONB64="iVBORw0KGgoAAAANSUhEUgAAACAAAAAgEAQAAACJ4248AAAAAmJLR0T//xSrMc0AAAAJcEhZcwAA
+AEgAAABIAEbJaz4AAAAJdnBBZwAAACAAAAAgAIf6nJ0AAADrSURBVFjD5ZZBEsMgCEU/TO/l2XMx
+04VjQ1K1CDYswkwWJnH+E/EL4RP7jluDCACoim/bvfIpFQiKEJcQHCN9xEtLCgDMQM7f33sZrPNG
+/05loCXujfAtCAVgNgLwIuycjQAra8G9Fm823ADabPRA1QDelfZAVUZktWrNvL8ew5YTnsStx3Am
+AyOInJVbYF1prZuU+tsR1g9UMDqGuo5oFWhtSEQNEGmeVrqv73Tj0pIZirANMYqRhyw5Bb9MauSW
+SwA8l9OzG5LnAsiiDQGQRRvaEwInK54J390hndAIYIeQ4k6AAjE/h06ab0SjP08MA1xDAAAAJXRF
+WHRkYXRlOmNyZWF0ZQAyMDExLTAxLTEyVDA5OjM0OjI0KzAxOjAwo//d7wAAACV0RVh0ZGF0ZTpt
+b2RpZnkAMjAxMS0wMS0xMlQwOTozNDoyNCswMTowMNKiZVMAAAAASUVORK5CYII="
+print "$ICONB64" | base64 --decode > $MONMORT
+# }}}
+
+# {{{ sudo functions
+function _sudo {
+ sudoassword=$(ask_password "Insert sudo password for user $USER")
+ print "$sudoassword\n" | sudo -S -v
+ _sudowrong
+}
+function _sudowrong {
+ if [[ $? == 1 ]]; then
+ sudoassword=$(ask_password "Wrong password. Insert sudo password for user $USER")
+ print "$sudoassword\n" | sudo -S -v
+ _sudowrong
+ fi
+}
+# }}}
+
+# {{{ Some pinentry code shamelessly stolen from tomb
+# Ask user for a password
+# Wraps around the pinentry command, from the GnuPG project, as it
+# provides better security and conveniently use the right toolkit.
+ask_password() {
+ local description="$1"
+ local title="${2:-Enter tomb password.}"
+ local output
+ local password
+ local gtkrc
+ local theme
+
+ # Distributions have broken wrappers for pinentry: they do
+ # implement fallback, but they disrupt the output somehow. We are
+ # better off relying on less intermediaries, so we implement our
+ # own fallback mechanisms. Pinentry supported: curses, gtk-2, qt4
+ # and x11.
+
+ # make sure LANG is set, default to C
+ LANG=${LANG:-C}
+
+
+ if [[ "$DISPLAY" = "" ]]; then
+
+ if _is_found "pinentry-curses"; then
+ output=`cat <<EOF | pinentry-curses
+OPTION ttyname=$TTY
+OPTION lc-ctype=$LANG
+SETTITLE $title
+SETDESC $description
+SETPROMPT Password:
+GETPIN
+EOF`
+ fi
+
+ else # a DISPLAY is found to be active
+
+ # customized gtk2 dialog with a skull (if extras are installed)
+ if _is_found "pinentry-gtk-2"; then
+
+ gtkrc=""
+ theme=/share/themes/tomb/gtk-2.0-key/gtkrc
+ for i in /usr/local /usr; do
+ [[ -r $i/$theme ]] && {
+ gtkrc="$i/$theme"
+ break
+ }
+ done
+ [[ "$gtkrc" = "" ]] || {
+ gtkrc_old="$GTK2_RC_FILES"
+ export GTK2_RC_FILES="$gtkrc"
+ }
+ output=`cat <<EOF | pinentry-gtk-2
+OPTION ttyname=$TTY
+OPTION lc-ctype=$LANG
+SETTITLE $title
+SETDESC $description
+SETPROMPT Password:
+GETPIN
+EOF`
+ [[ "$gtkrc" = "" ]] || export GTK2_RC_FILES="$gtkrc_old"
+
+ # TODO QT4 customization of dialog
+ elif _is_found "pinentry-qt4"; then
+
+ # TODO X11 customization of dialog
+ elif _is_found "pinentry-x11"; then
+
+ output=`cat <<EOF | pinentry-x11
+OPTION ttyname=$TTY
+OPTION lc-ctype=$LANG
+SETTITLE $title
+SETDESC $description
+SETPROMPT Password:
+GETPIN
+EOF`
+
+ else
+
+ if _is_found "pinentry-curses"; then
+
+ output=`cat <<EOF | pinentry-curses
+OPTION ttyname=$TTY
+OPTION lc-ctype=$LANG
+SETTITLE $title
+SETDESC $description
+SETPROMPT Password:
+GETPIN
+EOF`
+ else
+ fi
+
+ fi
+
+ fi # end of DISPLAY block
+
+ # parse the pinentry output
+ for i in ${(f)output}; do
+ [[ "$i" =~ "^ERR.*" ]] && {
+ print "canceled"
+ return 1 }
+
+ # here the password is found
+ [[ "$i" =~ "^D .*" ]] && password="${i##D }"
+ done
+
+ [[ "$password" = "" ]] && {
+ print "empty"
+ return 1 }
+
+ print "$password"
+ return 0
+}
+
+_is_found() {
+ # returns 0 if binary is found in path
+ [[ "$1" = "" ]] && return 1
+ command -v "$1" 1>/dev/null 2>/dev/null
+ return $?
+}
+# }}}
+
+# {{{ _clean - Clean function, removes sensitive stuff from memory
+function _clean {
+ TOMBSIZE="$rr"; unset TOMBSIZE
+ TOMBNAME="$rr"; unset TOMBNAME
+ UNDERTAKER="$rr"; unset UNDERTAKER
+ sudoassword="$rr"; unset sudoassword
+}
+# }}}
+
+# {{{ zenity dialogs
+function _fsel {
+ zenity \
+ --window-icon="$MONMORT" \
+ --file-selection \
+ --title="$1"
+}
+function _zenques {
+ zenity \
+ --window-icon="$MONMORT" \
+ --question \
+ --text="$1"
+}
+function _zenwarn {
+ zenity \
+ --window-icon="$MONMORT" \
+ --warning \
+ --title="$1" \
+ --text="$2"
+}
+function _zeninfo {
+ zenity \
+ --window-icon="$MONMORT" \
+ --info \
+ --title="$1" \
+ --text="$2"
+}
+function _zenerr {
+ zenity \
+ --window-icon="$MONMORT" \
+ --error \
+ --title="$1" \
+ --text="$2"
+}
+function _zenentry {
+ zenity \
+ --window-icon="$MONMORT" \
+ --entry \
+ --title="$1" \
+ --text="$2" \
+ --entry-text="$3"
+}
+# }}}
+
+function _umountkey { sudo umount $keymount && sudo rmdir $keymount }
+function _mountkey {
+ sudo umount $USBKEY
+ [[ $? = 0 ]] || {
+ _zenerr "Error" "Your key is mounted somewhere and I've run into issues. Please unmount it and re-run."
+ _clean && exit }
+
+ if [[ -d $keymount ]]; then
+ _zenwarn "Warning" "Something is already mounted on $keymount. Please check and re-run."
+ _clean && exit
+ fi
+
+ sudo mkdir -p $keymount
+ sudo mount $USBKEY $keymount
+
+ if ! [[ -d $coffindot ]]; then
+ sudo mkdir $coffindot
+ fi
+}
+
+# {{{ _writedavinfo
+function _writedavinfo {
+ if [[ -n $davpass ]]; then
+ davinfo=$(echo -n "$UNDERTAKER:WebDAV:" \
+ && echo -n "$UNDERTAKER:WebDAV:$davpass" \
+ | md5sum \
+ | awk '{print $1}')
+ print $davinfo | sudo tee $coffindot/davinfo
+ fi
+
+ cat <<EOF | sudo tee $coffindot/webdav.conf
+
+ alias /${TOMBNAME} /media/${TOMBNAME}
+ <Directory "/media/${TOMBNAME}">
+ Dav On
+ AllowOverride none
+ Options Indexes FollowSymlinks
+ AuthType Digest
+ AuthName WebDAV
+ AuthUserFile /etc/apache2/davpasswd
+ Require user ${UNDERTAKER}
+ </Directory>
+
+ </VirtualHost>
+</IfModule>
+EOF
+}
+# }}}
+
+function _main {
+ _clean
+ cmnd=$(zenity \
+ --window-icon="$MONMORT" \
+ --title="tombox hook helper" \
+ --list \
+ --hide-header \
+ --text="tombox hook helper\nChoose what hook you want to create:" \
+ --separator=" # " \
+ --column=Function \
+ --column=Description \
+ "create" "a new tomb on the tombox" \
+ "delete" "a tomb from the tombox" \
+ "backup" "a tomb from the tombox" \
+ "foobar" "on the tombox")
+ eval "_$cmnd"
+}
+
+# {{{ _create
+function _create {
+ vars=$(zenity \
+ --window-icon="$MONMORT" \
+ --title="Create a new tomb" \
+ --forms \
+ --text="Enter the info for your new tomb" \
+ --separator=":" \
+ --add-entry="Tombox username" \
+ --add-entry="Tomb name" \
+ --add-entry="Tomb's size in MiB")
+ if [[ $? = 1 ]]; then
+ exec _main
+ else
+ UNDERTAKER=${vars[(ws@:@)1]}
+ re='^[A-Za-z0-9]+$'
+ if ! [[ $UNDERTAKER =~ $re ]]; then
+ _zenwarn "Warning" "Invalid characters in username!"
+ exec _create
+ fi
+ TOMBNAME=${vars[(ws@:@)2]}
+ if ! [[ $TOMBNAME =~ $re ]]; then
+ _zenwarn "Warning" "Invalid characters in tomb name!"
+ exec _create
+ fi
+ TOMBSIZE=${vars[(ws@:@)3]}
+ re='^[0-9]+$'
+ if ! [[ $TOMBSIZE =~ $re ]]; then
+ _zenwarn "Warning" "Invalid characters in tomb size!"
+ exec _create
+ fi
+ fi
+
+ feats=$(zenity \
+ --window-icon="$MONMORT" \
+ --title="Choose features" \
+ --text="Choose features you wish to include with your tomb" \
+ --list \
+ --checklist \
+ --separator=":" \
+ --column=Yes/No \
+ --column=Feature \
+ "FALSE" "webdav" \
+ "FALSE" "sshfs" \
+ "FALSE" "ipfs")
+ if [[ $? = 1 ]]; then; exec _main; fi
+
+ if [[ $feats =~ "webdav" ]]; then
+ _zenques "Do you already have a WebDAV login?"
+ if [[ $? = 1 ]]; then
+ davpass=$(_zenentry "WebDAV Password" \
+ "Insert the password you want to use for WebDAV login:" \
+ "my very strong password")
+ if [[ $? = 1 ]]; then; davpass=""; fi
+ fi
+ fi
+
+ if [[ $feats =~ "sshfs" ]]; then
+ _zenques "Do you already have an SSH key setup for your user?"
+ if [[ $? = 1 ]]; then
+ sshpubkey=$(_fsel "Select your SSH pubkey you want to use.")
+ else
+ sshpubkey=""
+ fi
+ fi
+
+ TOMBHOOKS=$(_zenques "Choose 'Yes' if you want to edit your tomb's bind-hooks and post-hooks")
+ if [[ $? = 0 ]]; then
+ bindhook=$(zenity \
+ --window-icon="$MONMORT" \
+ --title="bind-hooks" \
+ --checkbox="Accept these bind-hooks" \
+ --text-info \
+ --editable)
+ if [[ $? = 1 ]]; then; exec _main; fi
+
+ posthook=$(zenity \
+ --window-icon="$MONMORT" \
+ --title="post-hooks" \
+ --checkbox="Accept these post-hooks" \
+ --text-info \
+ --editable)
+ if [[ $? = 1 ]]; then; exec _main; fi
+ fi
+
+ _zeninfo "gmakehook" "Plug in your USB key and click OK."
+ lsblk -npl | awk -F" " '{print $1 " " $4}' | grep '^/dev/sd.. ' > ./.devs
+
+ USBKEY=$(zenity \
+ --window-icon="$MONMORT" \
+ --title="Choose USB key" \
+ --list \
+ --text="Choose your USB key to use" \
+ --separator=" " \
+ --column="Device path" \
+ --column="Device size" \
+ $(cat ./.devs))
+ if [[ $? = 1 ]]; then; exec _main; fi
+
+ _sudo
+ _mountkey
+ if [[ -f $hooks ]]; then
+ _zenques "Warning! There is already an existing hook. Do you want to overwrite?"
+ if [[ $? = 1 ]]; then
+ _zeninfo "gmakehook" "Postponing..." && _umountkey && exec _main
+ else
+ print "create:${UNDERTAKER}:${TOMBNAME}:${TOMBSIZE}:${feats}" | sudo tee $hooks
+ if [[ $feats =~ "webdav" ]]; then; _writedavinfo; fi
+ if [[ -n $sshpubkey ]]; then; sudo cp $sshpubkey ./sshpubkey ; fi
+ if [[ -n $bindhook ]]; then
+ print "$bindhook" | sudo tee $coffindot/bindhooks
+ fi
+ if [[ -n $posthook ]]; then
+ print "$posthook" | sudo tee $coffindot/posthooks
+ fi
+ _umountkey && _zeninfo "Success" "$hooks written successfully!\nPlug the USB key in a tombox to activate it."
+ exec _main
+ fi
+ else
+ print "create:${UNDERTAKER}:${TOMBNAME}:${TOMBSIZE}:${feats}" | sudo tee $hooks
+ if [[ $feats =~ "webdav" ]]; then; _writedavinfo; fi
+ if [[ -n $sshpubkey ]]; then; sudo cp $sshpubkey $coffindot/sshpubkey ; fi
+ if [[ -n $bindhook ]]; then
+ print "$bindhook" | sudo tee $coffindot/bindhooks
+ fi
+ if [[ -n $posthook ]]; then
+ print "$posthook" | sudo tee $coffindot/posthooks
+ fi
+ _umountkey && _zeninfo "Success" "$hooks written successfully!\nPlug the USB key in the tombox to activate it."
+ exec _main
+ fi
+}
+# }}}
+
+# {{{ _delete
+function _delete {
+ vars=$(zenity \
+ --window-icon="$MONMORT" \
+ --title="Delete an existing tomb" \
+ --forms \
+ --text="Enter the info of your tomb" \
+ --separator=":" \
+ --add-entry="Tombox username" \
+ --add-entry="Tomb name")
+ if [[ $? = 1 ]]; then
+ exec _main
+ else
+ UNDERTAKER=${vars[(ws@:@)1]}
+ re='^[A-Za-z0-9]+$'
+ if ! [[ $UNDERTAKER =~ $re ]]; then
+ _zenwarn "Warning" "Invalid characters in username!"
+ exec _delete
+ fi
+ TOMBNAME=${vars[(ws@:@)2]}
+ if ! [[ $TOMBNAME =~ $re ]]; then
+ _zenwarn "Warning" "Invalid characters in tomb name!"
+ exec _delete
+ fi
+ fi
+
+ _zeninfo "gmakehook" "Plug in your USB key and click OK."
+ lsblk -npl | awk -F" " '{print $1 " " $4}' | grep '^/dev/sd.. ' > ./.devs
+
+ USBKEY=$(zenity \
+ --window-icon="$MONMORT" \
+ --title="Choose USB key" \
+ --list \
+ --text="Choose your USB key to use" \
+ --separator=" " \
+ --column="Device path" \
+ --column="Device size" \
+ $(cat ./.devs))
+ if [[ $? = 1 ]]; then; exec _main; fi
+
+ _sudo
+ _mountkey
+ if [[ -f $hooks ]]; then
+ _zenques "Warning! There is already an existing hook. Do you want to overwrite?"
+ if [[ $? = 1 ]]; then
+ _zeninfo "gmakehook" "Postponing..." && _umountkey && exec _main
+ else
+ print "delete:${UNDERTAKER}:${TOMBNAME}" | sudo tee $hooks
+ _umountkey && _zeninfo "Success" "$hooks written successfully!\nPlug the USB key in a tombox to activate it."
+ exec _main
+ fi
+ else
+ print "delete:${UNDERTAKER}:${TOMBNAME}" | sudo tee $hooks
+ _umountkey && _zeninfo "Success" "$hooks written successfully!\nPlug the USB key in the tombox to activate it."
+ exec _main
+ fi
+}
+# }}}
+
+# {{{ _backup
+function _backup {
+ vars=$(zenity \
+ --window-icon="$MONMORT" \
+ --title="Backup an existing tomb" \
+ --forms \
+ --text="Enter the info of your tomb" \
+ --separator=":" \
+ --add-entry="Tombox username" \
+ --add-entry="Tomb name")
+ if [[ $? = 1 ]]; then
+ exec _main
+ else
+ UNDERTAKER=${vars[(ws@:@)1]}
+ re='^[A-Za-z0-9]+$'
+ if ! [[ $UNDERTAKER =~ $re ]]; then
+ _zenwarn "Warning" "Invalid characters in username!"
+ exec _delete
+ fi
+ TOMBNAME=${vars[(ws@:@)2]}
+ if ! [[ $TOMBNAME =~ $re ]]; then
+ _zenwarn "Warning" "Invalid characters in tomb name!"
+ exec _delete
+ fi
+ fi
+
+ _zeninfo "gmakehook" "Plug in your USB key and click OK."
+ lsblk -npl | awk -F" " '{print $1 " " $4}' | grep '^/dev/sd.. ' > ./.devs
+
+ USBKEY=$(zenity \
+ --window-icon="$MONMORT" \
+ --title="Choose USB key" \
+ --list \
+ --text="Choose your USB key to use" \
+ --separator=" " \
+ --column="Device path" \
+ --column="Device size" \
+ $(cat ./.devs))
+ if [[ $? = 1 ]]; then; exec _main; fi
+
+ _sudo
+ _mountkey
+ if [[ -f $hooks ]]; then
+ _zenques "Warning! There is already an existing hook. Do you want to overwrite?"
+ if [[ $? = 1 ]]; then
+ _zeninfo "gmakehook" "Postponing..." && _umountkey && exec _main
+ else
+ print "backup:${UNDERTAKER}:${TOMBNAME}" | sudo tee $hooks
+ _umountkey && _zeninfo "Success" "$hooks written successfully!\nPlug the USB key in a tombox to activate it."
+ exec _main
+ fi
+ else
+ print "backup:${UNDERTAKER}:${TOMBNAME}" | sudo tee $hooks
+ _umountkey && _zeninfo "Success" "$hooks written successfully!\nPlug the USB key in the tombox to activate it."
+ exec _main
+ fi
+}
+# }}}
+
+function _ { rm ./.devs }
+exec _main
DIR diff --git a/src/mourner b/src/mourner
t@@ -0,0 +1,23 @@
+#!/usr/bin/env bash
+#
+# mourner - inotify script to watch /dev for new keys
+#
+# ~ parazyd
+
+pattern='sd[a-z][1-9]$'
+coproc inotifywait --monitor --event create,delete --format '%e %w%f' /dev
+
+while read -r -u "${COPROC[0]}" event file; do
+ if [[ $file =~ $pattern ]]; then
+ case $event in
+ CREATE)
+ echo "Created $file..."; sleep 1
+ `pwd`/sacrist $file $event
+ ;;
+ DELETE)
+ echo "Removed $file..."; sleep 1
+ `pwd`/sacrist $file $event
+ ;;
+ esac
+ fi
+done
DIR diff --git a/src/sacrist b/src/sacrist
t@@ -0,0 +1,63 @@
+#!/usr/bin/env zsh
+#
+# sacrist - script called by mourner, for our graveyard administration
+#
+# ~ parazyd
+
+zkv=1
+helpers=1
+
+R=$(pwd)
+source $R/zlibs/zuper
+
+vars+=(device keyuuid)
+
+source $R/zlibs/features
+source $R/zlibs/hooks
+source $R/zlibs/keyfiles
+source $R/zlibs/mounts
+source $R/zlibs/ttab
+
+source $R/zlibs/zuper.init
+
+device=$1
+happenz=$2
+keyuuid=$(lsblk -no uuid $device)
+
+typeset -H keypass
+typeset -H keyuuid
+typeset -H undertaker
+typeset -H graveyard
+typeset -H tombs
+typeset -H tombpasswd
+
+# Vars
+graveyard="/home/graveyard" # Our graveyard, with all the tombs
+tombs="$graveyard/tombs" # Info about opened tombs, holds keyuuid, keyhash and tombid
+tmptombs="$graveyard/tmptombs" # Temp tempfile, for updating $tombs
+keymount="/media/tombkey" # Directory where keys get mounted
+coffindot="$keymount/.coffin" # .coffin directory on the usb key
+ttab="$coffindot/ttab" # Our ttab
+hooks="$coffindot/hook"
+tomb="/usr/local/bin/tomb"
+tombpasswd="$graveyard/passwd"
+
+[[ $happenz == "CREATE" ]] && {
+ mount-key
+
+ if [[ -d "$coffindot" ]]; then
+ notice "Found .coffin"
+
+ [[ -f "$hooks" ]] && check-hooks
+ [[ -f "$ttab" ]] && ttab-magic
+
+ else
+ _msg warning "No .coffin directory"
+ fi
+
+ umount-key
+}
+
+[[ $happenz == "DELETE" ]] && {
+ # Some kind of endgame
+}
DIR diff --git a/src/zlibs/features b/src/zlibs/features
t@@ -0,0 +1,45 @@
+#!/usr/bin/env zsh
+
+check-webdav-hook() {
+ fn check-webdav-hook
+
+ davconf="/etc/apache2/sites-available/tomboxdav.conf"
+
+ if [[ $entry =~ webdav && -f $coffindot/webdav.conf ]]; then
+ notice "Found WebDAV data. Setting up..."
+
+ [[ -f $coffindot/davinfo ]] && {
+ cat $coffindot/davinfo >> /etc/apache2/davpasswd
+ [[ $? = 0 ]] && {
+ rm $coffindot/davinfo
+ gpasswd -a www-data $undertaker
+ notice "Added new WebDAV user"
+ }
+ }
+ sed -i -e :a -e '$d;N;2,3ba' -e 'P;D' $davconf
+ cat $coffindot/webdav.conf >> $davconf
+ notice "Wrote to $davconf"
+ /etc/init.d/apache2 restart
+ notice "Done setting up WebDAV"
+ else
+ notice "No WebDAV data found"
+ fi
+}
+
+check-sshfs-hook() {
+ fn check-sshfs-hook
+
+ if [[ $entry =~ sshfs && -f $coffindot/sshpubkey ]]; then
+ notice "Found SSH data. Setting up..."
+
+ mkdir -p /home/$undertaker/.ssh
+ cat $coffindot/sshpubkey >> /home/$undertaker/.ssh/authorized_keys
+ chown -R $undertaker:$undertaker /home/$undertaker/.ssh
+ chmod 700 /home/$undertaker/.ssh && chmod 600 /home/$undertaker/.ssh/authorized_keys
+
+ [[ $? = 0 ]] && notice "Wrote to authorized_keys" \
+ && notice "Done setting up SSH"
+ else
+ notice "No SSH data found"
+ fi
+}
DIR diff --git a/src/zlibs/hooks b/src/zlibs/hooks
t@@ -0,0 +1,80 @@
+#!/usr/bin/env zsh
+
+check-hooks() {
+ # TODO: fragmented keys, delete, backup, moar
+ fn check-hooks
+
+ for entry in $(cat $hooks); do
+ let hook=$hook+1
+ notice "Found hook $hook..."
+
+ # Check what's hook supposed to do
+ if [[ ${entry[(ws@:@)1]} == "create" ]]; then
+ create-new-tomb
+ elif [[ ${entry[(ws@:@)1]} == "delete" ]]; then
+ delete-tomb
+ elif [[ ${entry[(ws@:@)1]} == "backup" ]]; then
+ backup-tomb
+ else
+ _msg failure "No valid hook syntax on hook $hook"
+ print $entry >> $hooks.fail
+ notice "Wrote failed hook to $hooks.fail"
+ fi
+ done
+}
+
+create-new-tomb() {
+ fn create-new-tomb
+
+ # TODO: recognize custom post/bind hooks and implement them in the
+ # new tomb
+
+ notice "Creating new tomb"
+
+ undertaker=${entry[(ws@:@)2]}
+ tombid=${entry[(ws@:@)3]}
+ tombsize=${entry[(ws@:@)4]}
+ keypass=$(pwgen 30 -1 1)
+
+ if ! [[ ( $(id $undertaker) ) ]]; then
+ _msg warning "No user called $undertaker found. Creating..."
+ useradd -G tombox -m -s /bin/nologin $undertaker
+ notice "Created user $undertaker"
+ else
+ notice "Found user $undertaker"
+ fi
+
+ # Check for features
+ check-webdav-hook
+ check-sshfs-hook
+
+ notice "Digging your tomb..."
+
+ sudo -u $undertaker $tomb dig -s $tombsize $graveyard/$tombid.tomb || \
+ (_msg failure "Digging went downhill. Cleaning and exiting" && \
+ clean-failed-hook)
+
+ sudo -u $undertaker $tomb forge $graveyard/$tombid.key \
+ --unsafe \
+ --tomb-pwd "$keypass" || \
+ (_msg failure "Forging key went downhill. Cleaning and exiting" && \
+ clean-failed-hook)
+
+ sudo -u $undertaker $tomb lock $graveyard/$tombid.tomb \
+ -k $graveyard/$tombid.key \
+ --unsafe \
+ --tomb-pwd "$keypass" || \
+ (_msg failure "Locking tomb went downhill. Cleaning and exiting" && \
+ clean-failed-hook)
+
+ notice "Moving your keyfile to your USB key..."
+ mv $graveyard/$tombid.key $coffindot/ && \
+ chown $undertaker:$undertaker $coffindot/$tombid.key && \
+ notice "Moved and chowned keyfile"
+
+ print "${undertaker}:${tombid}:true" >> $ttab
+
+ keyhash=$(hash-key)
+ print "${keyhash}:${keypass}" >> $tombpasswd
+ notice "Wrote to ttab and tombpasswd"
+}
DIR diff --git a/src/zlibs/keyfiles b/src/zlibs/keyfiles
t@@ -0,0 +1,19 @@
+#!/usr/bin/env zsh
+
+hash-key() {
+ fn hash-key
+
+ ${$(sha256sum $coffindot/$tombid.key)[(ws: :)1]}
+}
+
+compare-key() {
+ fn compare-key
+
+ keyhash=$(hash-key)
+
+ if [[ ( $(grep $keyhash $tombs | grep $keyuuid) ) ]]; then
+ return 0
+ else
+ return 1
+ fi
+}
DIR diff --git a/src/zlibs/mounts b/src/zlibs/mounts
t@@ -0,0 +1,31 @@
+#!/usr/bin/env zsh
+
+mount-key() {
+ fn mount-key
+
+ if [[ -d $keymount ]]; then
+ _msg failure "$keymount already exists."
+ return 1
+ else
+ notice "Creating $keymount"
+ mkdir -p $keymount
+ notice "Mounting..."
+ mount $device $keymount
+ return 0
+ fi
+}
+
+umount-key() {
+ fn umount-key
+
+ if [[ -d $keymount ]]; then
+ notice "Unmounting $keymount"
+ umount $keymount \
+ && rmdir $keymount
+ notice "Success umounting"
+ return 0
+ else
+ notice "No $keymount found"
+ return 0
+ fi
+}
DIR diff --git a/src/zlibs/ttab b/src/zlibs/ttab
t@@ -0,0 +1,54 @@
+#!/usr/bin/env zsh
+
+ttab-magic() {
+ fn ttab-magic
+
+ notice "Doing ttab magic..."
+
+ line=0
+
+ for entry in $(cat $ttab); do
+ let line=$line+1
+ notice "Found line $line..."
+
+ [[ ${entry[(ws@:@)3]} == "true" ]] && {
+ notice "Working on tomb from line $line"
+
+ undertaker=${entry[(ws@:@)1]}
+ notice "Username: $undertaker"
+ tombid=${entry[(ws@:@)2]}
+ notice "Tombname: $tombid"
+
+ compare-key
+
+ [[ $? = 0 ]] && {
+ notice "compare-key -> true"
+ sudo -u $undertaker $tomb slam $tombid
+
+ cp $tombs $tmptombs
+ grep -v $keyhash $tmptombs > $tombs && \
+ chmod 600 $tombs && \
+ notice "Updated $tombs"$tombs
+ rm $tmptombs
+ continue
+ }
+
+ notice "compare-key -> false"
+
+ keypass=${$(grep $keyhash $tombpasswd)[(ws@:@)2]}
+
+ sudo -u $undertaker $tomb open $graveyard/$tombid.tomb \
+ -k $coffindot/$tombid.key \
+ --unsafe \
+ --tomb-pwd "$keypass"
+
+ chmod g+rw /media/$tombid
+
+ [[ -d "/media/$tombid" ]] && {
+ print "${undertaker}:${keyhash}:${keyuuid}" >> $tombs && \
+ chmod 600 $tombs && \
+ notice "Added info to $tombs"
+ }
+ }
+ done
+}
DIR diff --git a/src/zlibs/zuper b/src/zlibs/zuper
t@@ -0,0 +1,729 @@
+#!/usr/bin/env zsh
+## -*- origami-fold-style: triple-braces -*-
+#
+# Zuper - Zsh Ultimate Programmer's Extensions Refurbished
+#
+# Copyright (C) 2015 Dyne.org Foundation
+#
+# Zuper is designed, written and maintained by Denis Roio <jaromil@dyne.org>
+#
+# This source code is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This source code is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# Please refer to the GNU Public License for more details.
+#
+# You should have received a copy of the GNU Public License along with
+# this source code; if not, write to:
+# Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+##########################
+typeset -aU vars
+typeset -aU arrs
+typeset -aU maps
+
+vars=(DEBUG QUIET LOG)
+arrs=(req freq)
+
+vars+=(zuper_version)
+zuper_version=0.2
+
+# {{{ Messaging
+
+# Messaging function with pretty coloring
+autoload colors
+colors
+
+vars+=(last_act last_func last_notice)
+
+function _msg() {
+ local msg="$2"
+ command -v gettext 1>/dev/null 2>/dev/null && msg="$(gettext -s "$2")"
+ for i in $(seq 3 ${#});
+ do
+ msg=${(S)msg//::$(($i - 2))*::/$*[$i]}
+ done
+
+ local command="print -P"
+ local progname="$fg[magenta]${PROGRAM##*/}$reset_color"
+ local message="$fg_bold[normal]$fg_no_bold[normal]$msg$reset_color"
+ local -i returncode
+
+ case "$1" in
+ inline)
+ command+=" -n"; pchars=" > "; pcolor="yellow"
+ ;;
+ message)
+ last_act="$msg"
+ pchars=" . "; pcolor="white"; message="$fg_no_bold[$pcolor]$msg$reset_color"
+ ;;
+ verbose)
+ last_func="$msg"
+ pchars="[D]"; pcolor="blue"
+ ;;
+ success)
+ last_notice="$msg"
+ pchars="(*)"; pcolor="green"; message="$fg_no_bold[$pcolor]$msg$reset_color"
+ ;;
+ warning)
+ pchars="[W]"; pcolor="yellow"; message="$fg_no_bold[$pcolor]$msg$reset_color"
+ ;;
+ failure)
+ pchars="[E]"; pcolor="red"; message="$fg_no_bold[$pcolor]$msg$reset_color"
+ returncode=1
+ ;;
+ print)
+ progname=""
+ ;;
+ *)
+ pchars="[F]"; pcolor="red"
+ message="Developer oops! Usage: _msg MESSAGE_TYPE \"MESSAGE_CONTENT\""
+ returncode=127
+ zerr
+ ;;
+ esac
+ ${=command} "${progname} $fg_bold[$pcolor]$pchars$reset_color ${message}$color[reset_color]" >&2
+
+ # write the log if its configured
+ [[ "$LOG" = "" ]] || {
+ touch $LOG || return $?
+ ${=command} "${progname} $fg_bold[$pcolor]$pchars$reset_color ${message}$color[reset_color]" >> $LOG
+ }
+
+ return $returncode
+}
+
+function _message say act() {
+ local notice="message"
+ [[ "$1" = "-n" ]] && shift && notice="inline"
+ [[ $QUIET = 1 ]] || _msg "$notice" $@
+ return 0
+}
+
+function _verbose xxx func() {
+ [[ $DEBUG = 1 ]] && _msg verbose $@
+ return 0
+}
+
+function _success yes notice() {
+ [[ $QUIET = 1 ]] || _msg success $@
+ return 0
+}
+
+function _warning no warn warning() {
+ [[ $QUIET = 1 ]] || _msg warning $@
+ return 0
+}
+
+function _failure fatal die error() {
+ # typeset -i exitcode=${exitv:-1}
+ [[ $QUIET = 1 ]] || _msg failure $@
+ return 1
+}
+
+function _print() {
+ [[ $QUIET = 1 ]] || _msg print $@
+ return 0
+}
+
+# }}} Messaging
+
+# {{{ Debugging
+
+fn() {
+ fun="$@"
+ req=()
+ freq=()
+ func "$fun"
+}
+
+zerr() {
+ error "error in: ${fun:-$last_notice}"
+ [[ "$last_func" = "" ]] || warn "called in: $last_func"
+ [[ "$last_act" = "" ]] || warn "called in: $last_act"
+ [[ "$last_notice" = "" ]] || warn "called in: $last_notice"
+ # [[ "$fun" = "" ]] || warn "called in: $fun"
+ TRAPEXIT() {
+ error "error reported, operation aborted."
+ }
+ return 1
+}
+
+
+ckreq reqck() {
+ err=0
+ for v in $req; do
+ [[ "${(P)v}" = "" ]] && {
+ warn "${fun[(ws: :)1]}(): required setting is blank: $v"
+ err=1
+ }
+ done
+
+ [[ $err = 1 ]] && return $err
+
+ for f in $freq; do
+ # exists and has size greater than zero
+ [[ -s $f ]] || {
+ warn "required file empty: $f"
+ err=1
+ }
+ done
+ [[ $err == 1 ]] && zerr
+ return $err
+}
+
+zdump() {
+ fn zdump
+ [[ ${#vars} -gt 0 ]] && {
+ print "Global variables:"
+ for _v in $vars; do
+ print " $_v = \t ${(P)_v}"
+ done
+ }
+ [[ ${#arrs} -gt 0 ]] && {
+ print "Global arrays:"
+ for _a in $arrs; do
+ print " $_a \t ( ${(P)_a} )"
+ done
+ }
+ [[ ${#maps} -gt 0 ]] && {
+ print "Global maps:"
+ for _m in $maps; do
+ print " $_m [key] \t ( ${(Pk)_m} )"
+ print " $_m [val] \t ( ${(Pv)_m} )"
+ done
+ }
+}
+
+# handy wrappers for throw/catch execution of blocks where we need the
+# program to exit on any error (non-zero) returned by any function
+throw() { function TRAPZERR() { zerr; return 1 } }
+catch() { function TRAPZERR() { } }
+
+##########################
+# Endgame handling
+
+arrs+=(destruens)
+destruens=()
+
+# Trap functions for the endgame event
+TRAPINT() { endgame INT; return $? }
+# TRAPEXIT() { endgame EXIT; return $? }
+TRAPHUP() { endgame HUP; return $? }
+TRAPQUIT() { endgame QUIT; return $? }
+TRAPABRT() { endgame ABORT; return $? }
+TRAPKILL() { endgame KILL; return $? }
+# TRAPPIPE() { endgame PIPE; return $? }
+TRAPTERM() { endgame TERM; return $? }
+TRAPSTOP() { endgame STOP; return $? }
+# TRAPZERR() { func "function returns non-zero." }
+
+
+endgame() {
+ fn "endgame $*"
+
+ # execute all no matter what
+ TRAPZERR() { }
+
+ # process registered destructors
+ for d in $destruens; do
+ fn "destructor: $d"
+ $d
+ done
+ return 0
+}
+
+# Register endgame() to be called at exit.
+# unlike TRAPEXIT, the zshexit() hook is not called when functions exit.
+zshexit() { endgame EXIT; return $? }
+
+# }}} Debugging
+
+# {{{ Tempfiles
+
+##########################
+# Temp file handling
+
+vars+=(ztmpfile)
+# ztmp() fills in $ztmpfile global. Caller must copy that variable as
+# it will be overwritten at every call.
+ztmp() {
+ fn ztmp
+
+ ztmpfile=`mktemp`
+ tmpfiles+=($ztmpfile)
+}
+
+# All tempfiles are freed in endgame()
+_ztmp_destructor() {
+ fn _ztmp_destructor
+
+ for f in $tmpfiles; do
+ rm -f "$f"
+ done
+ tmpfiles=()
+}
+
+arrs+=(tmpfiles)
+destruens+=(_ztmp_destructor)
+
+# }}} Tempfiles
+
+# {{{ Strings
+
+# tokenizer, works only with one char length delimiters
+# saves everything in global array tok=()
+arrs+=(tok)
+strtok() {
+ fn "strtok $*"
+ _string="$1"
+ _delim="$2"
+ req=(_string _delim)
+ ckreq || return $?
+
+ tok=()
+ f=0
+ c=0
+ for c in {1..${#_string}}; do
+ if [[ "${_string[(e)$c]}" == "$_delim" ]]; then
+ # check if not empty
+ t=${_string[(e)$(($f + 1)),$(($c - 1))]}
+ [[ "$t" == "" ]] || tok+=($t)
+ # save last found
+ f=$c
+ fi
+ done
+ # add last token
+ t=${_string[(e)$(($f + 1)),$c]}
+ [[ "$t" == "" ]] || tok+=($t)
+}
+
+# TODO: move in here some helpers
+
+# }}} Strings
+
+# {{{ Key/Value filesave
+
+# optional: define zkv=1 on source
+
+[[ "$zkv" = "" ]] || {
+
+ ##########################
+ # Key/Value file storage using ZSh associative maps
+
+ zmodload zsh/system
+
+ # load a map from a file
+ # map must be already instantiated with typeset -A by called
+ # name of map is defined inside the file
+ function zkv.load() {
+ fn "zkv-load $*"
+
+ file=$1
+ [[ "$file" = "" ]] && {
+ error "zkv-open() missing argument: file-path"
+ zerr
+ return 1 }
+ [[ -r "$file" ]] || {
+ error "zkv-open() file not found $file"
+ zerr
+ return 1 }
+ [[ -s "$file" ]] || {
+ error "zkv-open() file is empty"
+ zerr
+ return 1 }
+
+ source $file
+ }
+
+ # save a map in a file
+ # $1 = name of the map associative array
+ # $2 = full path to the file
+ function zkv.save() {
+ fn "zkv.save $*"
+
+ _map=$1
+ _path=$2
+ [[ "$_path" = "" ]] && {
+ error "zkv.save() missing argument: map-name path-to-file"
+ zerr
+ return 1
+ }
+ [[ -r $_path ]] && {
+ func "zkv.close() overwriting $_path"
+ func "backup turd left behind: ${_path}~"
+ mv $_path $_path~
+ }
+ touch $_path
+
+ # wondering about http://www.zsh.org/mla/users/2015/msg00286.html
+ # meanwhile solved using a double array, wasting a full map memcpy
+ _karr=(${(Pk)_map})
+ _varr=(${(Pv)_map})
+ _num="${#_karr}"
+ for c in {1..$_num}; do
+ # can also be cat here, however for speed we use builtins
+ # switch to cat if compatibility is an issue
+ sysread -o 1 <<EOF >> $_path
+$_map+=("${_karr[$c]}" "${(v)_varr[$c]}")
+EOF
+ done
+ func "$_num key/values stored in $_path"
+ }
+
+}
+
+# }}} Key/Value filesave
+
+# {{{ Get/Set REST API
+
+# optional: define restful=1 on source
+
+[[ "$restful" = "" ]] || {
+
+ ########
+ # Restful API client
+ # there is a clear zsh optimization here in get/set kv
+ # using zsh/tcp instead of spawning curl
+ # and perhaps querying with one call using ?recursive
+
+ zmodload zsh/net/tcp
+
+
+ function restful.put() {
+ fn "restful.put $*"
+
+ # $1 = hostname
+ # $2 = port
+ # $3 = path
+ # value from stdin |
+
+ # to check if the http service is running is up to the caller
+
+ _host=${1} # ip address
+ _port=${2}
+ _path=${3}
+ sysread _v
+
+ req=(_host)
+ ckreq || return $?
+
+ if ztcp $_host $_port; then
+
+ # TODO: work out various parsers, this one works with consul.io
+
+ _fd=$REPLY
+ # func "tcp open on fd $fd"
+ cat <<EOF >& $_fd
+PUT ${_path} HTTP/1.1
+User-Agent: Zuper/$zuper_version
+Host: ${_host}:${_port}
+Accept: */*
+Content-Length: ${#_v}
+Content-Type: application/x-www-form-urlencoded
+
+EOF
+
+ print -n "$_v" >& $_fd
+
+ sysread -i $_fd _res
+
+ # close connection
+ ztcp -c $_fd
+
+ [[ "$_res" =~ "true" ]] || {
+ warn "failed PUT on restful key/value"
+ warn "host: ${_host}"
+ warn "port: ${_port}"
+ warn "path: ${_path}"
+ warn "value: $_v"
+ print - "$_res"
+ zerr
+ return 1
+ }
+
+ else
+ error "cannot connect to restful service: $_host:$_port"
+ zerr
+ return 1
+ fi
+
+ return 0
+
+ }
+
+ function restful.get() {
+ fn "restful.get $*"
+
+ _host=${1}
+ _port=${2}
+ _path=${3}
+
+ req=(_host _port)
+ ckreq || return $?
+
+ ztcp $_host $_port || {
+ zerr
+ return 1
+ }
+
+ _fd=$REPLY
+
+ # TODO: work out various parsers, this one works with consul.io
+
+ cat <<EOF >& $_fd
+GET ${_path} HTTP/1.1
+User-Agent: Zuper/$zuper_version
+Host: $_host:$_port
+Accept: */*
+
+EOF
+ sysread -i $_fd -o 1 | awk -F: '
+/"Value":/ { gsub(/"|}]/,"",$7) ; print $7 }' | base64 -d
+
+ # close connection
+ ztcp -c $_fd
+
+ return 0
+
+ }
+
+}
+
+# }}} Get/Set REST API
+
+# {{{ Helpers
+[[ "$helpers" = "" ]] || {
+
+ function helper.isfound isfound() {
+ command -v $1 1>/dev/null 2>/dev/null
+ return $?
+ }
+
+ # remote leading and trailing spaces in a string taken from stdin
+ function helper.trim trim() {
+ sed -e 's/^[[:space:]]*//g ; s/[[:space:]]*\$//g'
+ }
+
+ zmodload zsh/mapfile
+ # faster substitute for cat
+ function helper.printfile printfile() {
+ print ${mapfile[$1]}
+ }
+
+ # extract all emails found in a text from stdin
+ # outputs them one per line
+ function helper.extract-emails extract_emails() {
+ awk '{ for (i=1;i<=NF;i++)
+ if ( $i ~ /[[:alnum:]]@[[:alnum:]]/ ) {
+ gsub(/<|>|,/ , "" , $i); print $i } }'
+ }
+
+
+ zmodload zsh/regex
+ # takes a string as argument, returns success if is an email
+ function helper.isemail isemail() {
+ [[ "$1" -regex-match "\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}\b" ]] && return 0
+
+ return 1
+ }
+
+ # takes a numeric argument and prints out a human readable size
+ function helper.human-size human_size() {
+ [[ $1 -gt 0 ]] || {
+ error "human_size() called with invalid argument"
+ return 1
+ }
+
+ # we use the binary operation for speed
+ # shift right 10 is divide by 1024
+
+ # gigabytes
+ [[ $1 -gt 1073741824 ]] && {
+ print -n "$(( $1 >> 30 )) GB"
+ return 0
+ }
+
+ # megabytes
+ [[ $1 -gt 1048576 ]] && {
+ print -n "$(( $1 >> 20 )) MB"
+ return 0
+ }
+ # kilobytes
+ [[ $1 -gt 1024 ]] && {
+ print -n "$(( $1 >> 10 )) KB"
+ return 0
+ }
+ # bytes
+ print -n "$1 Bytes"
+ return 0
+ }
+
+
+ # strips out all html/xml tags (everything between < >)
+ function helper.html-strip xml_strip html_strip() { sed 's/<[^>]\+>//g' }
+
+ # changes stdin string special chars to be shown in html
+ function helper.escape-html escape_html() {
+ sed -e '
+s/\&/\&/g
+s/>/\>/g
+s/</\</g
+s/"/\"/g
+'
+ }
+
+ # escapes special chars in urls
+ function helper.decode-url decode_url urldecode() {
+ sed -e '
+s/%25/%/gi
+s/%20/ /gi
+s/%09/ /gi
+s/%21/!/gi
+s/%22/"/gi
+s/%23/#/gi
+s/%24/\$/gi
+s/%26/\&/gi
+s/%27/'\''/gi
+s/%28/(/gi
+s/%29/)/gi
+s/%2a/\*/gi
+s/%2b/+/gi
+s/%2c/,/gi
+s/%2d/-/gi
+s/%2e/\./gi
+s/%2f/\//gi
+s/%3a/:/gi
+s/%3b/;/gi
+s/%3d/=/gi
+s/%3e//gi
+s/%3f/?/gi
+s/%40/@/gi
+s/%5b/\[/gi
+s/%5c/\\/gi
+s/%5d/\]/gi
+s/%5e/\^/gi
+s/%5f/_/gi
+s/%60/`/gi
+s/%7b/{/gi
+s/%7c/|/gi
+s/%7d/}/gi
+s/%7e/~/gi
+s/%09/ /gi
+'
+ }
+
+ function helper.encode-url encode_url urlencode() {
+ sed -e '
+s/%/%25/g
+s/ /%20/g
+s/ /%09/g
+s/!/%21/g
+s/"/%22/g
+s/#/%23/g
+s/\$/%24/g
+s/\&/%26/g
+s/'\''/%27/g
+s/(/%28/g
+s/)/%29/g
+s/\*/%2a/g
+s/+/%2b/g
+s/,/%2c/g
+s/-/%2d/g
+s/\./%2e/g
+s/\//%2f/g
+s/:/%3a/g
+s/;/%3b/g
+s//%3e/g
+s/?/%3f/g
+s/@/%40/g
+s/\[/%5b/g
+s/\\/%5c/g
+s/\]/%5d/g
+s/\^/%5e/g
+s/_/%5f/g
+s/`/%60/g
+s/{/%7b/g
+s/|/%7c/g
+s/}/%7d/g
+s/~/%7e/g
+s/ /%09/g
+'
+ }
+
+}
+# }}} Helpers
+
+# {{{ Config
+
+# This is not a full config parser, but its a mechanism to read single
+# sections of configuration files that are separated using various
+# syntax methods. The only method supported is now org-mode whose
+# sections start with #+ . It fills in the global array
+# $config_section which can be read out to a file or interpreted in
+# memory, whatever syntax it may contain.
+
+vars+=(config_section_type)
+arrs+=(config_section)
+config_section_type=org-mode
+
+config.section.type() {
+ fn config.section.type
+ _type=$1
+ req=(_type)
+ ckreq || return $?
+
+ case $_type in
+ org-mode)
+ config_section_type=org-mode
+ ;;
+ *)
+ error "Unknown config type:$_type"
+ return 1
+ ;;
+ esac
+
+ act "$_type config section parser initialized"
+ return 0
+
+}
+
+# fills in contents of section in array config_section
+config.section.read() {
+ fn config.section.read
+ _file=$1
+ _section=$2
+ req=(_file _section)
+ freq=($_file)
+ ckreq || return $?
+
+ case $config_section_type in
+ org-mode)
+ _contents=`awk '
+BEGIN { found=0 }
+/^#\+ '"$_section"'/ { found=1; next }
+/^#\+/ { if(found==1) exit 0 }
+/^$/ { next }
+{ if(found==1) print $0 }
+' $_file`
+
+ ;;
+ *)
+ error "Unknown config type:$_type"
+ ;;
+ esac
+
+ config_section=()
+ for c in ${(f)_contents}; do
+ config_section+=("$c")
+ done
+ return 0
+
+}
+
+# }}} Config
DIR diff --git a/src/zlibs/zuper.init b/src/zlibs/zuper.init
t@@ -0,0 +1,35 @@
+##########################
+# Zuper Init
+
+# initialize globals only after sourcing everything
+# since zlibs may contain more variable declarations
+for _v in $vars; do
+ typeset -h $_v
+done
+for _a in $arrs; do
+ typeset -aU $_a
+done
+for _m in $maps; do
+ typeset -A $_m
+done
+
+# reset defaults
+DEBUG=${DEBUG:-0}
+QUIET=${QUIET:-0}
+LOG=${LOG:-""}
+req=()
+freq=()
+last_act=()
+last_func=()
+last_notice=()
+tmpfiles=()
+config_section=()
+config_section_type=${config_section_type:-org-mode}
+
+
+func "Zuper $zuper_version initialized"
+func "${#vars} global variables registered"
+func "${#arrs} global arrays registered"
+func "${#maps} global maps registered"
+
+