tunfinished refactoring, now using zuper - coffin - secure lan file storage on a device
 (HTM) 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
 (HTM) 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/\&/\&amp;/g
       +s/>/\&gt;/g
       +s/</\&lt;/g
       +s/"/\&quot;/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"
       +
       +