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 178c5e4636f5d2168423f2d51115a615609e4952
parent 74b03fbe5279a5bc7fac35e5d357dbbb203258e5
Author: Jaromil <jaromil@dyne.org>
Date:   Tue,  6 Jan 2015 12:51:31 +0100

backup function now uses notmuch search. also cleaned up merge and using safer refile() to move files

Diffstat:
Msrc/jaro | 1-
Msrc/zlibs/addressbook | 4++--
Msrc/zlibs/helpers | 28++++++++++++++++++++++++++++
Msrc/zlibs/maildirs | 79+++++++++++++++++++++++++++++++++----------------------------------------------
Msrc/zlibs/search | 116+++++++++++++++++++++++--------------------------------------------------------
5 files changed, 96 insertions(+), 132 deletions(-)

diff --git a/src/jaro b/src/jaro @@ -699,7 +699,6 @@ main() index) CLEANEXIT=0; nm_index - nm compact ;; search) CLEANEXIT=0; search ${PARAM} ;; diff --git a/src/zlibs/addressbook b/src/zlibs/addressbook @@ -70,7 +70,7 @@ insert_address() { } remove_address() { - warning "remove_address() TODO in abook branch" + error "remove_address() TODO in abook branch" return 0 } @@ -213,7 +213,7 @@ learn() { } forget() { - warning "forget() TODO in abook branch" + error "forget() TODO in abook branch" return 0 # func "forget sender from mail in stdin" diff --git a/src/zlibs/helpers b/src/zlibs/helpers @@ -73,6 +73,34 @@ s/"/\&quot;/g ' } +# move an email file from a maildir to another +# keeping cur/new/tmp positioning +refile() { + src="$1" + dst="$2" + + [[ "$2" = "" ]] && { + error "refile() needs 2 args: source file and destination maildir" + return 1 + } + + [[ -r "$1" ]] || { + error "refile origin file not existing: $1" + return 1 + } + + # weak destination check, called should use maildircheck anyway + [[ -d "$2"/new ]] || { + error "refile destination not a maildir: $2" + return 1 + } + + srcarr=( ${=src//\// } ) + srcnum=${#srcarr} + pos=${srcarr[$(( $srcnum - 1 ))]} + func "mv $src ${dst}/${pos}/" + [[ $DRYRUN = 0 ]] && mv $src ${dst}/${pos}/ +} autostart() { diff --git a/src/zlibs/maildirs b/src/zlibs/maildirs @@ -147,63 +147,50 @@ rmdupes() { } merge() { - src=${1} - dst=${2} + _src=${1} + _dst=${2} - { test "$src" = "$dst" } && { - error "Cannot merge same directory in itself: $src = $dst" - return 1 } - - { maildircheck "$src" } || { - error "Source is not a maildir: $src" - return 1 } + [[ "$_src" = "$_dst" ]] && { + error "Cannot merge same directory in itself: $_src = $_dst" + return 1 } - { maildircheck "$dst" } || { - error "Destination is not a maildir: $dst" - return 1 } + maildircheck "$_src" + [[ $? = 0 ]] || { + error "Source is not a maildir: $_src" + return 1 + } + + maildircheck "$_dst" + [[ $? = 0 ]] || { + error "Destination is not a maildir: $_dst" + return 1 + } # merge does not uses deliver() because # the new-flag and read-flags must be kept intact. # it will abort on any single error on moving each file. - notice "Merging maildir ${src} into ${dst}" - c=0 - fr=`${=find} ${src}/cur -type f` + notice "Merging maildir ${_src} into ${_dst}" + fr=`${=find} ${_src} -type f` - # TODO: preserve timestamps using cp hardlinks - # cp -p -r -l source/date target/ - # rm -rf source/data - for i in ${(f)fr}; do - mv "$i" "${dst}/cur/"; c=$(($c + 1)) - { test $? = 0 } || { - error "error moving file: $i" - error "merge aborted." - return 1 } - done - fr=`${=find} ${src}/new -type f` - for i in ${(f)fr}; do - mv "$i" "${dst}/new/"; c=$(($c + 1)) - { test $? = 0 } || { - error "error moving file: $i" - error "merge aborted." - return 1 } - done - fr=`${=find} ${src}/tmp -type f` + c=0 for i in ${(f)fr}; do - mv "$i" "${dst}/tmp/"; c=$(($c + 1)) - { test $? = 0 } || { - error "error moving file: $i" - error "merge aborted." - return 1 } + refile "$i" "$_dst" + [[ $? = 0 ]] || { + # bail out on every single error + error "Error refiling emails to destination maildir" + error "Operation aborted, $c files moved." + return 1 + } + c=$(($c + 1)) done notice "$c mails succesfully moved" - act "Removing source directory ${src}" - ${=rm} -r "${src}" - - act "Purging duplicates in destination" - rmdupes "${dst}" - - act "Done. All mails merged into ${dst}" + [[ $DRYRUN = 0 ]] && { + act "Removing source directory ${_src}" + ${=rm} -r "${_src}" + } + + act "Done. All mails merged into ${_dst}" } diff --git a/src/zlibs/search b/src/zlibs/search @@ -235,98 +235,48 @@ search() { backup() { id=$datestamp.$RANDOM - mairixrc=$TMPDIR/backup.rc.$id - mairixdb=$TMPDIR/backup.db.$id - typeset -al expr - typeset -al fold - - src=""; dst="" - basedir=$MAILDIRS - # check if the name of a maildir is among params - # we need at least 2 maildirs, the second is the destination - for p in ${PARAM}; do - c=$(( $c + 1 )) - - if [ $c = ${#PARAM} ]; then - # last one is always the destination - func "destination is ${p}" - fold+=(${p}) - - elif [ -r "${p}" ]; then - - { maildircheck ${p} } && { - func "param ${p} is a maildir" - fold+=(${p}) - { test ${#fold} = 1 } && { - # base path is the dir of the first folder - pushd `dirname ${p}` - basedir=`pwd` - popd } - } - - elif [ -r "${MAILDIRS}/${p}" ]; then - - { maildircheck ${MAILDIRS}/${p} } && { - func "param ${p} is a jaro maildir" - fold+=(${MAILDIRS}/${p}) - } - - else # not a folder, add it to expressions array - func "param ${p} is an expression" - expr+=(${p}) - fi - done - - { test ${#fold} -lt 2 } && { - error "Not enough folders specified for backup: minimum is 2" - act "When specifying more than 2, the last one is the destination" - return 1 + c=0 + + dst=${PARAM[1]} + shift 1 PARAM + expr=${PARAM} + + notice "Backup of mails matched by search expression" + act "expression: $expr" + act "destination folder: $dst" + [[ $DRYRUN = 1 ]] && act "Dryrun mode enabled" + + [[ $DRYRUN = 0 ]] && { + maildircheck "$dst" + [[ $? = 0 ]] || { + error "First argument of backup command must be an existing maildir" + return 1 + } } - dst=${fold[${#fold}]} - { test -r "$dst" } || { maildirmake "${dst}" } - - { test ${#expr} = 0 } && { + [[ "$expr" = "" ]] && { error "No expression set for backup, please indicate what you want to backup" act "For example: d:10y-2y (all mails older than 1 year up to 10 years ago)" act "Or a simple search string, all expressions can be verified using search." return 1 } - # forge the folder string for mairix conf - folders="" - for f in ${=fold}; do - { test $f = $dst } || { - folders="$folders`basename $f`:" } - done - - notice "Backup of all mails in '$folders' matching expression '$expr'" + read_account + nm_setup - act "Indexing folders" - cat <<EOF > ${mairixrc} -base=$basedir -database=$mairixdb -maildir=${folders} -mformat=maildir -EOF - mairix -F -f ${mairixrc} 2> /dev/null - tempdst="bck.`basename $dst`" - notice "Backup of $dst" - rm -f ${mairixrc}; cat <<EOF > ${mairixrc} -base=$basedir -database=$mairixdb -maildir=${folders} -mfolder=$tempdst -mformat=maildir -EOF - mairix -F -f ${mairixrc} -t -M ${expr} - { test -r $basedir/$tempdst } && { - merge $basedir/$tempdst $dst - notice "Destination folder size is `du -hs $dst | awk '{print $1}'`" + _res=`nm_search ${=expr}` + + [[ $DRYRUN = 0 ]] && { + for i in ${(f)_res}; do + refile $i $dst + [[ $? = 0 ]] || { + # bail out on every single error + error "Error refiling emails to destination maildir" + error "Operation aborted" + return 1 + } + done } + - ${=rm} ${mairixrc} - ${=rm} ${mairixdb} - - act "Backup completed to destination: $dst" }