jaromail

a commandline tool to easily and privately handle your e-mail
git clone git://parazyd.org/jaromail.git
Log | Files | Refs | Submodules | README

commit 0fd1d61b56cb36a90f4946cbd190385b1df3c441
parent a641f8b9e8b6e4ae64d7accfc97612cb0fcc2669
Author: Jaromil <jaromil@dyne.org>
Date:   Tue, 24 Feb 2015 18:11:59 +0100

started port to Cygwin (using babun)

Diffstat:
MMakefile | 4++--
Abuild/auto | 21+++++++++++++++++++++
Abuild/build-win.sh | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abuild/install-win.sh | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/jaro | 27+++++++++++++++++++++------
Msrc/zlibs/keyring | 65++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
6 files changed, 269 insertions(+), 17 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,8 +1,8 @@ all: - @cd build && ./build-gnu.sh && cd - + ./build/auto build install: - @build/install-gnu.sh + ./build/auto install clean: rm -f src/*.o diff --git a/build/auto b/build/auto @@ -0,0 +1,21 @@ +#!/usr/bin/env zsh + +os=`uname -o` +action=$1 + +[[ "$action" = "" ]] && { + print "usage: auto [ build | install ]" + return 1 +} + +case $os in + Cygwin) + print "Windows $action" + ./build/${action}-win.sh + ;; + GNU/Linux) + print "GNU/Linux $action" + ./build/${action}-gnu.sh + ;; +esac + diff --git a/build/build-win.sh b/build/build-win.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env zsh + +distro=cygwin + +builddir=`pwd` +# cc="${builddir}/cc-static.zsh" +cc="gcc -O3" + +pushd .. + +target=all +{ test -z $1 } || { target="$1" } +# no other distro supported atm + +mkdir -p build/win + +{ test "$target" = "deps" } || { + test "$target" = "all" } && { + # only babun's pact supported for now + deps=(fetchmail msmtp mutt pinentry-curses pinentry-w32) + deps+=(sqlite3 abook) + deps+=(make) + + print "Building on Cygwin" + print "Checking software to install" + for d in $deps; do + pact install $d + done + + print "All dependencies installed" +} + +{ test "$target" = "fetchaddr" } || { + test "$target" = "all" } && { + pushd src + print -n "Compiling the address parser (RFC2047) ... " + ${=cc} -c helpers.c + ${=cc} -c rfc2047.c + ${=cc} -c rfc822.c; + ${=cc} -c -DHAVE_ICONV fetchaddr.c; + ${=cc} -o fetchaddr fetchaddr.o helpers.o rfc2047.o rfc822.o + popd + cp src/fetchaddr build/win/ + print OK +} + +{ test "$target" = "parsedate" } || { + test "$target" = "all" } && { + print -n "Compiling the date parsers (RFC822) ... " + pushd src + ${=cc} -o parsedate parsedate.c + popd + cp src/parsedate build/win/ + print OK +} + +{ test "$target" = "dotlock" } || { + test "$target" = "all" } && { + print -n "Compiling the file dotlock... " + pushd src + ${=cc} -c dotlock.c -I . -DDL_STANDALONE + ${=cc} -o dotlock dotlock.o + popd + cp src/dotlock build/win/ + print OK +} + +{ test "$target" = "gpgewrap" } || { + test "$target" = "all" } && { + print -n "Compiling the GnuPG wrapper... " + pushd src + ${=cc} -c gpgewrap.c -I . + ${=cc} -o gpgewrap gpgewrap.o + popd + cp src/gpgewrap build/win/ + print OK +} + +# build mixmaster only if specified +{ test "$target" = "mixmaster" } && { + print "Compiling Mixmaster (anonymous remailer)" + pushd src/mixmaster-3.0/Src + mixmaster_sources=(main menustats mix rem rem1 rem2 chain chain1 chain2 nym) + mixmaster_sources+=(pgp pgpdb pgpdata pgpget pgpcreat pool mail rfc822 mime keymgt) + mixmaster_sources+=(compress stats crypto random rndseed util buffers maildir parsedate.tab) + bison parsedate.y + for s in ${=mixmaster_sources}; do ${=cc} -c ${s}.c; done + ${=cc} -o mixmaster *.o -lssl + popd + cp src/mixmaster-3.0/Src/mixmaster build/win +} + + +print +print "Done building JaroMail!" +print "Now run 'make install' as root to install jaromail in /usr/local" +print "use PREFIX=/home/private/jaromail to avoid system-wide installation." +print + +popd diff --git a/build/install-win.sh b/build/install-win.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env zsh + +PREFIX=${PREFIX:-$HOME/Mail} + +# TODO: separate libexec from share +JARO_LIBEXEC=$HOME/Mail/system +JARO_SHARE=$HOME/Mail +mkdir -p $HOME/Mail +touch $JARO_SHARE || { + print "Error: cannot write to $JARO_SHARE" + print "Run as root or set PREFIX to another location" + return 1 +} + +# cleanup previous installs +rm -rf "$JARO_SHARE" +mkdir -p "$JARO_SHARE" + +{ test $? = 0 } || { + print "No permissions to install system-wide."; return 1 } + +srcdir=. + +{ test -r ./src/fetchaddr } || { + print "Error: first build, then install."; return 1 } + +mkdir -p $JARO_SHARE/{.mutt,.stats} +cp -ra $srcdir/doc/* $JARO_SHARE/ +cp -ra $srcdir/src/mutt/* $JARO_SHARE/.mutt/ +cp -ra $srcdir/src/stats/* $JARO_SHARE/.stats/ + +# copy the executables +mkdir -p $JARO_LIBEXEC/{bin,zlibs} +cp $srcdir/src/jaro $JARO_LIBEXEC/bin +cp -ra $srcdir/build/win/* $JARO_LIBEXEC/bin +cp -ra $srcdir/src/zlibs/* $JARO_LIBEXEC/zlibs/ +cp -ra $srcdir/doc/jaromail-manual.pdf $JARO_LIBEXEC/ + +for l in `ls $JARO_LIBEXEC/zlibs/ | grep '.zwc$'`; do + rm -f $l +done + +for l in `ls $JARO_LIBEXEC/zlibs/ | grep -v '.zwc$'`; do + zcompile $JARO_LIBEXEC/zlibs/$l +done + +# fix pinentry w32 path +[[ -r /usr/bin/pinentry ]] || { + ln -s /usr/bin/pinentry-w32 /usr/bin/pinentry +} + +mkdir -p /usr/local/bin +rm -f /usr/local/bin/jaro +cat <<EOF > /usr/local/bin/jaro +#!/usr/bin/env zsh +export JAROWORKDIR=${JARO_LIBEXEC} +export JAROMAILDIR=${PREFIX} +${JARO_LIBEXEC}/bin/jaro \$* +EOF +chmod +x /usr/local/bin/jaro + +source ${JARO_LIBEXEC}/bin/jaro source + +jaro init + +notice "Jaro Mail succesfully installed in: $PREFIX" +act "Executable path: /usr/local/bin/jaro" + +return 0 diff --git a/src/jaro b/src/jaro @@ -112,7 +112,7 @@ typeset -aH JAROTMPFILES # Keep track of temporary files _tmp_create() { [[ -d "$TMPPREFIX" ]] || { # we create the tempdir with the sticky bit on - sudo mkdir -m 1777 "$TMPPREFIX" + mkdir -m 1777 "$TMPPREFIX" [[ $? == 0 ]] || _failure "Fatal error creating the temporary directory: ::1 temp dir::" "$TMPPREFIX" } @@ -170,13 +170,16 @@ if [[ ${@} == *-D* ]]; then DEBUG=1; fi # what operating system are we in? use os_detect() # simplifying modes of operation: GNU or MAC -case $(uname) in - Linux) OS=GNU +case $(uname -o) in + GNU/Linux) OS=GNU notice "Jaro Mail v$VERSION running on GNU/Linux" ;; Darwin) OS=MAC notice "Jaro Mail v$VERSION running on Mac/OSX" ;; + Cygwin) OS=WIN + notice "Jaro Mail v$VERSION runing on MS/Win" ;; + *) OS=GNU # default error "Running on an unknown operating system, assuming GNU" ;; esac @@ -211,7 +214,7 @@ else # use GNU/Linux default fi # env override -{ test "$JAROWORKDIR" = "" } || { WORKDIR="${JAROWORKDIR}" } +[[ "$JAROWORKDIR" = "" ]] || { WORKDIR="${JAROWORKDIR}" } # default addressbook ADDRESSBOOK="$MAILDIRS/whitelist.abook" @@ -245,9 +248,12 @@ if [ -d $WORKDIR/zlibs ]; then source ${z} done fi + act "full set of auxiliary functions loaded" elif [[ $1 = source ]]; then + act "limited set of auxiliary functions loaded" + else error "No ZLibs found in $WORKDIR/zlibs" error "This installation of Jaro Mail is broken." @@ -307,6 +313,9 @@ esac hostname=$(hostname) # gather the current hostname + +[[ "$1" = "source" ]] || { # skip checks if just sourcing + # make sure we have a directory for account configurations { test -d "$ACCOUNTS" } || { ${=mkdir} "$ACCOUNTS" } @@ -335,6 +344,8 @@ MUTTDIR="$MAILDIRS/.mutt" cp "$WORKDIR/Mutt.txt" "$MAILDIRS/Mutt.txt" notice "Default Mutt configuration template created" } +} # if not sourcing + _mutt() { for i; do _fa+=" $i "; done func "exec: mutt -F $MUTTDIR/rc ${=muttflags} ${_fa}" @@ -399,9 +410,11 @@ check_bin() { func "Notmuch binary: `command -v notmuch`" func "Alot binary: `command -v alot`" - pidof gnome-keyring-daemon > /dev/null && { + ps ax | grep '[g]nome-keyring-daemon' > /dev/null + [[ $? = 0 ]] && { act "using gnome-keyring to store secrets" - GNOMEKEY=1 } + GNOMEKEY=1 + } return 0 } @@ -712,8 +725,10 @@ main() { update_filters update_mutt update_sieve + command -v notmuch > /dev/null && { nm_setup nm new 2>&1 | grep -v '^Note: Ignoring' +} ;; help) usage ;; diff --git a/src/zlibs/keyring b/src/zlibs/keyring @@ -90,15 +90,15 @@ ask_password() { fi return 0 elif [ -r "$KEYRING" ]; then - func "Looking for password in local keyring for $email ($account)" + func "looking for password in local keyring for $email ($account)" func "new pass hash for: $login:$host" _hash=`print "$login:$host" | shasum | awk '{print $1}'` lookup="`lookup_secret ${_hash}`" { test "$lookup" = "" } || { - act "Saved password found for $email ($transport on $host)" - notice "Type the password to unlock this keyring entry:" - password="`print - $lookup | base64 -d | gpg -d --cipher-algo AES256 --openpgp --no-options`" - { test "$?" = 0 } || { error "Incorrect password to unlock local keyring entry, operation aborted."; return 1 } + act "saved password found for $email ($transport on $host)" + notice "type the password to unlock this keyring entry:" + password="`print - $lookup | base64 -d | gpg -d --cipher-algo aes256 --openpgp --no-options`" + { test "$?" = 0 } || { error "incorrect password to unlock local keyring entry, operation aborted."; return 1 } return 0 } fi @@ -111,8 +111,26 @@ ask_password() { return 0 ;; *) - error "Unknown system, can't figure out how to handle passwords" - return 1 + func "looking for password in local keyring for $email ($account)" + func "new pass hash for: $login:$host" + _hash=`print "$login:$host" | shasum | awk '{print $1}'` + lookup="`lookup_secret ${_hash}`" + { test "$lookup" = "" } || { + act "saved password found for $email ($transport on $host)" + notice "type the password to unlock this keyring entry:" + password="`print - $lookup | base64 -d | gpg -d --cipher-algo aes256 --openpgp --no-options`" + { test "$?" = 0 } || { error "incorrect password to unlock local keyring entry, operation aborted."; return 1 } + return 0 + } + #################### + # USE PINENTRY ALONE + new_password + { test $? != 0 } && { + error "Password input aborted." + return 1 } + return 0 + ;; + esac } @@ -226,7 +244,36 @@ EOF ;; *) - error "Unknown system, can't figure out how to handle passwords" - return 1 + + + { test -r "$KEYRING" } || { create_keyring "$KEYRING" } + + for h in "$imap" "$smtp"; do + # calculate the hash for this entry + _hash=`print "$login:$host" | shasum | awk '{print $1}'` + # check if the entry is already present + func "new pass hash for: $login:$host" + lookup="`lookup_secret ${_hash} rowid`" + notice "Select the password to lock this keyring entry:" + _password="`print - $password | gpg -c --cipher-algo AES256 --openpgp --no-options | base64`" + + if [ "$lookup" = "" ]; then # new entry + cat <<EOF | ${SQL} -batch "$KEYRING" +INSERT INTO secrets (hash, password) +VALUES ("${_hash}", "${_password}"); +EOF + act "saved new password in local keyring" + else # update entry + cat <<EOF | ${SQL} -batch "$KEYRING" +UPDATE secrets SET password="${_password}" WHERE hash LIKE "${_hash}"; +EOF + act "updated local keyring with new password" + fi + done + + + + + ;; esac }