#!/bin/bash

# updates a hardlinked backup
# licensed under the GPL version 3
# Copyright Miek Gieben, 2007 - 2010
# rewritten for rdup-up and rdup-tr

echo2() {
    echo "** $(basename $PROGNAME): $@" >&2
}

version() {
    rdup -V
}

copy_and_link() {
    # hardlink a previous backup directory to a new directory
    # SYNOPSIS: copy_and_link N BACKUPDIR
    # N: look back N days
    # BACKUPDIR: top directory of the backups
    # By default a new directory BACKUPDIR/YYYYMM/DD wil be created

    # 3 return codes
    # 0: BACKUPDIR/YYYYMM/DD is created (now make a inc dump)
    # 1: BACKUPDIR/YYYYMM/DD is created (now make a full dump)
    # 2: an error occured

    LOOKBACK=$1; shift
    DATESTR='+%Y%m/%d'
    TODAY=$(date $DATESTR)
    TOPDIR="$1"

    if $dry; then exit 1; fi
    [ -z "$TOPDIR" ] && exit 2
    [ -d $TOPDIR/$TODAY ] && exit 0

    if ! mkdir -p $TOPDIR/$TODAY; then
	exit 2
    fi

    let i=1
    while [ $i -le $LOOKBACK ]; do
	    D=$(date $DATESTR --date "$i days ago")
	    if [ -d $TOPDIR/$D ]; then
		if ! cp -plr $TOPDIR/$D/* $TOPDIR/$TODAY; then
		    exit 2
		fi
		exit 0
	    fi
	    let i=i+1
    done
    exit 1
}

usage() {
        cat << HELP
$PROGNAME [+N] DIR [DIR ...] DEST

This is a wrapper around rdup, rdup-tr and rdup-up

DIR  - directories to back up
+N   - Look N days back for previous backups, defaults to 8
DEST - where to store the backup. This can be:
	ssh://user@host/directory (note: no colon after the hostname)
	ssh://host/directory
	file:///directory (note: 3 slashes)
	/directory
	directory

OPTIONS:
 -k KEYFILE encrypt all files: rdup -P "mcrypt -f KEYFILE"
 -g	    encrypt all files: rdup -P "gpg --default-recipient-self --encrypt"
 -z         compress all files: rdup -P gzip
 -E FILE    use FILE as an exclude list
 -f         force a full dump
 -v         echo the files processed to stderr and be more verbose
 -n	    dry run; show the actually rdup command and pass -n to rdup
 -a	    reset atime
 -x         pass -x to rdup
 -q         pass -q to rdup-up
 -s NUM	    pass -s NUM to rdup-up (strip NUM leading path components)
 -X FILE    encrypt all paths with AES and key in FILE
 -Y FILE    decrypt all paths with AES and key in FILE
 -h         this help
 -V         print version
HELP
}

PROGNAME=$0
NOW=$(date +%Y%m/%d)
DAYS=8
OPT_DRY=
ssh=
trans=
pathtrans=
atime=
enc=false
etc=~/.rdup
force=false
verbose=false
link=false
dry=false
mcrypt=false

while getopts "aE:k:vfgzxhVX:Y:s:Lnq" o; do
        case $o in
		a) atime=" -a " ;;
		E)
                if [ -z "$OPTARG" ]; then
                        echo2 "-E needs an argument"
                        exit 1
                fi
                E=" -E $OPTARG "
                ;;
		Y|X)
		# check for rdup-tr
		pathtrans="-$o $OPTARG";
		;;
                k)
                if [ -z "$OPTARG" ]; then
                        echo2 "-k needs an argument"
                        exit 1
                fi
                if [ ! -r "$OPTARG" ]; then
                        echo2 "Cannot read keyfile \`$OPTARG': failed"
                        exit 1
                fi
                trans="$trans -P 'mcrypt -q -f $OPTARG'"
		if $enc; then
			echo2 "Encryption already set"
			exit 1
		fi
		enc=true
		mcrypt=true
                ;;
                z) trans="$trans -P gzip"
		if $enc; then
			echo2 "Select compression first, then encryption"
			exit 1
		fi
                ;;
		g) trans="$trans -P 'gpg -e --default-recipient-self'"
		if $enc; then
			echo2 "Encryption already set"
			exit 1
		fi
		# if there a no key, this will fail
		if [ $(gpg --list-keys | wc -l) -eq "0" ]; then
			echo2 "No gpg keys found"
			exit 1
		fi
		enc=true
		;;
                f) force=true;;
                q) OPT_QUIET=" -q ";;
		s) STRIP="-s $OPTARG";;
                v) OPT=" $OPT -v "; verbose=true;;
		n) dry=true; OPT_DRY=" -n ";;
                x) x=" -x ";;
		L) link=true;;
                h) usage && exit;;
                V) version && exit;;
                \?) echo2 "Invalid option seen"; exit 1;;
        esac
done
shift $((OPTIND - 1))

if [ "${1:0:1}" = "+" ]; then
        DAYS=${1:1}
	if [ $DAYS -lt 1 ] || [ $DAYS -gt 99 ]; then
                echo2 "+N needs to be a number [1..99]"
                exit 1
        fi
        shift
else
        DAYS=8
fi

[ $# -lt 2 ] && usage && exit

if $mcrypt; then
    if ! which mcrypt 2>/dev/null 1>&2; then
	echo2 "Mcrypt not found, can not continue"
	exit 1
    fi
fi

i=1; last=$#; DIRS=
while [ $i -lt $last ]; do
	DIRS="$DIRS $1"
	shift
	((i=$i+1))
done
# rdup [options] source destination
#dest="ssh://elektron.atoom.net/directory"
#dest="ssh://elektron.atoom.net/directory/var/"
#dest="file:///var/backup"
#dest="/var/backup"
#dest="ssh://miekg@elektron.atoom.net/directory"

dest=$1
if [ ${dest:0:6} = "ssh://" ]; then
	rest=${dest/ssh:\/\//}
	u=${rest%%@*}

	if [ "$u" = "$rest" ]; then
            # no @ used, nullify $u
            u=
        fi

	rest=${rest/$u@/}
	h=$(echo $rest | cut -s -f1 -d/)
	BACKUPDIR=${rest/$h/}

	if [ -z "$u" ]; then
		ssh=" ssh -x $h"
	else
		ssh=" ssh -x $u@$h"
	fi
fi
if [ ${dest:0:7} = "file://" ]; then
	rest=${dest/file:\/\//}
	BACKUPDIR=$rest
fi
[ ${dest:0:1} = "/" ] && BACKUPDIR=$dest

# no hits above, assume relative filename
[ -z "$BACKUPDIR" ] && BACKUPDIR=$PWD/$dest

$link && copy_and_link $DAYS $BACKUPDIR

# change all / to _ to make a valid filename
STAMP=$etc/timestamp.${HOSTNAME}.${dest//\//_}
LIST=$etc/list.${HOSTNAME}.${dest//\//_}

[ ! -d $etc ] && mkdir $etc

# remote or not
if [ -z "$ssh" ]; then
        pipe="rdup-up$OPT $OPT_QUIET $OPT_DRY $STRIP -t $BACKUPDIR/$NOW"
else
        pipe="$ssh rdup-up$OPT $OPT_QUIET $OPT_DRY $STRIP -t $BACKUPDIR/$NOW"
fi
# path encryption
if [ -n "$pathtrans" ]; then
	pipe="rdup-tr $pathtrans | $pipe"
fi

cmd="rdup$E$x$atime -N $STAMP $trans $LIST $DIRS | $pipe"

if ! $force; then
	# path is set at the top
        if [ -z "$ssh" ]; then
		$PROGNAME $OPT_DRY -L +$DAYS /dev/null $BACKUPDIR
                purpose=$?
        else
		# You need to set your path so rdup-simple can be found
                $ssh "rdup-simple $OPT_DRY -L +$DAYS /dev/null $BACKUPDIR"
                purpose=$?
        fi
else
        purpose=1
fi
case $purpose in
        0)
	$verbose && echo "INCREMENTAL DUMP" ;;
        1)
        $verbose && echo "FULL DUMP"
        rm -f $LIST
        rm -f $STAMP ;;
        *)
        echo2 "Illegal return code from rdup-simple -L"
        exit 1 ;;
esac
# execute the backup command
if $dry; then
    echo "${cmd}"
fi
eval ${cmd}
