#!/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 "** $PROGNAME: $@" >&2
}

version() {
    echo "rdup 1.1.3"
}

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 return 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 -Pmcrypt,-fKEYFILE
 -g	    encrypt all files: rdup -Pgpg,--default-recipient-self,--encrypt
 -z         compress all files: rdup -Pgzip
 -E FILE    use FILE as an exclude list
 -L	    link previous backup directory (note: this is used internally)
 -f         force a full dump
 -v         echo the files processed to stderr and be more verbose
 -n	    dry run; show the actually rdup commands, without executing them
 -a	    reset atime
 -x         pass -x to rdup
 -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
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:Ln" 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 -Pmcrypt,-q,-f,$OPTARG"
		if $enc; then
			echo2 "Encryption already set"
			exit 1
		fi
		enc=true
		mcrypt=true
                ;;
                z) trans="$trans -Pgzip"
		if $enc; then
			echo2 "Select compression first, then encryption"
			exit 1
		fi
                ;;
		g) trans="$trans -Pgpg,-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;;
		s) STRIP="-s $OPTARG";;
                v) OPT=" $OPT -v "; verbose=true;;
		n) dry=true;;
                x) x=" -x ";;
		L) link=true;;
                h) usage && exit;;
                V) version && exit;;
                \?) echo2 "Invalid option: $OPTARG"; 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%%@*}
	rest=${rest/$u@/}
	h=`echo $rest | cut -s -f1 -d/`
	BACKUPDIR=${rest/$h/}

	if [[ -z $u ]]; then
		ssh=" ssh -c blowfish -x $h"
	else
		ssh=" ssh -c blowfish -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 $STRIP -t $BACKUPDIR/$NOW"
else
        pipe="$ssh rdup-up$OPT $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 -L +$DAYS /dev/null $BACKUPDIR
#		rdup-ln -l $DAYS $BACKUPDIR
                purpose=$?
        else
		# You need to set your path
                $ssh "rdup-simple -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}"
else
    eval ${cmd}
fi
