#!/bin/bash -
#===============================================================================
#
#          FILE: ibackup
#
#         USAGE: ibackup <Command>
#
#   DESCRIPTION: This is a frontend for increment backup tool rdiff-backup.
#
#       OPTIONS: All options: run this script without any command will show
#                
#  REQUIREMENTS: 1. A ibackup.conf file
#                2. A correct ibackup.list
#                2. rdiff-backup 2.x installed (API 201)
#
#         NOTES: 1. ALL CONFIG INSIDE ibackup.conf, DO NOT EDIT THIS FILE.
#                2. You can put the ibackup script to a path in $PATH
#                   but you should put the ibackup.conf together with the script.
#               
#
#          BUGS:  ---
#        AUTHOR: rickz (Rick Zhang), xlrickz@gmail.com
#       COMPANY: X-LEGEND Entertainment Corp.
#       CREATED: Fri Jul  8 02:33:56 EDT 2011
#      REVISION: 1.0
#
#          TODO: 
#
#===============================================================================

#set -o nounset                              # Treat unset variables as an error
set -m                                       # Enable job control

#############################################################################
#                                                                           #
#                           DO NOT EDIT THIS FILE                           #
#                          EDIT ibackup.conf ONLY                           #
#                                                                           #
#############################################################################
[ -r $(dirname $0)/ibackup.conf ] && source $(dirname $0)/ibackup.conf || exec echo "Error: Read $(dirname $0)/ibackup.conf failed."

# What to do?
ACTION="$1"
ARG2="$2";ARG3="$3";ARG4="$4";ARG5="$5";ARG6="$6";ARG7="$7";ARG8="$8";ARG9="$9"

# Log on begin
touch "$IBACKUP_LOGFILE"
TTY="$(who -m|awk '{print $2}')"
MYIP="$(who -m|sed 's/.*(\(.*\))/\1/g')"
LOGIN_USER=$(who -m|awk '{print $1}')
DATE_NOW="$(date +%Y/%m/%dT%H:%M:%S)"
[ "$ACTION" == "" ] && DO="NOTHING-TO-DO" || DO="$(head -c 12 <<< "$ACTION                 ")"
echo -e "$DATE_NOW\tBegin\t$DO\tAns:$ANSWER\tLogin:$LOGIN_USER\tUser:$USER\t$TTY\t$MYIP\tARG:$*" >> "$IBACKUP_LOGFILE"

# Detect the ACTION right or not
grep -h "^[A-Za-z0-9]* *(" $0|awk '{print $1}'|grep -q "^$ACTION$" || ACTION="show_help"
[ "$ACTION" == "" ] && ACTION="show_help"

# Common options (rdiff-backup 2.x, API 201)
# --api-version 201 必須是 global，必須在 action verb 前面
# --new 在 API 201 已不需要（rdiff-backup 會自動判斷是否首次備份目錄）
COMMON_OPTIONS="--api-version 201 --verbosity 5 --no-ssh-compression"

export PATH="/usr/bin:$PATH"

which rdiff-backup > /dev/null 2>&1 || exec echo "Error: Cannot find rdiff-backup."

#############################
# Built-in script functions #
#############################
show_cmd () {
  for CMD_TYPE in $(grep "^[A-Za-z_-]\+ () { # " $0|awk -F" # " '{print $2}'|awk -F" -> " '{print $1}'|sort -r|uniq) ; do
	echo "$CMD_TYPE:" | colorize yellow black
	for CMD in $(grep -h "^[A-Za-z_-]\+ () { #" $0|grep " # $CMD_TYPE -> "|awk '{print $1}'|sort|uniq) ; do 
		DESCRIPTION="$(grep "^$CMD () { # " $0|awk -F" # " '{print $2}'|awk -F" -> " '{print $2}')"
		echo -n "$CMD" | colorize cyan black
		echo " -> $DESCRIPTION"
	done
	echo
  done
}

show_help () {
	echo $"Usage: $0 <Command>

Commands:
$(show_cmd)


list [Target]
backup <Target> [Comment]
restore <Target> <Restore to> [number of increment]
compare <Target> [Directory to compare with]
verify <Backuped directory>
listchange <Target> [number of increment]
"
}

# Colorize
function colorize() {
# Colorize stream editor by Rickz <iamrickz@gmail.com>
#                                       21:50, 2007/10/03.
FG="$1" ; BG="$2" ; EF1="$3" ; EF2="$4" ; EF3="$5"
echo "$3 $4 $5"|grep "1" 2>&1 > /dev/null && FG="39"
OLDFG="$FG";case "$FG" in
        black)  FG="30";;
        red)    FG="31";;
        green)  FG="32";;
        yellow) FG="33";;
        blue)   FG="34";;
        magenta)FG="35";;
        purple) FG="35";;
        cyan)   FG="36";;
        white)  FG="37";;
        default)FG="39";;
esac;[ "$OLDFG" == "$FG" ] && FG="39"
OLDBG="$BG";case "$BG" in
        black)  BG="40";;
        red)    BG="41";;
        green)  BG="42";;
        yellow) BG="43";;
        blue)   BG="44";;
        magenta)BG="45";;
        purple) BG="45";;
        cyan)   BG="46";;
        white)  BG="47";;
        default)BG="49";;
esac;[ "$OLDBG" == "$BG" ] && BG="49"
[ "$EF3" != "" ] && exec sed "s/\(.*\)/\x1b\x5b$BG\x3b$FG\x3b\x31\x6d\x1b\x5b$EF1\x6d\x1b\x5b$EF2\x6d\x1b\x5b$EF3\x6d\1\x1b\x5b\x30\x6d/g" -
[ "$EF2" != "" ] && exec sed "s/\(.*\)/\x1b\x5b$BG\x3b$FG\x3b\x31\x6d\x1b\x5b$EF1\x6d\x1b\x5b$EF2\x6d\1\x1b\x5b\x30\x6d/g" -
[ "$EF1" != "" ] && exec sed "s/\(.*\)/\x1b\x5b$BG\x3b$FG\x3b\x31\x6d\x1b\x5b$EF1\x6d\1\x1b\x5b\x30\x6d/g" -
[ "$BG" != "" ] && exec sed "s/\(.*\)/\x1b\x5b$BG\x3b$FG\x3b\x31\x6d\1\x1b\x5b\x30\x6d/g" -
[ "$FG" != "" ] && exec sed "s/\(.*\)/\x1b\x5b0\x3b$FG\x3b\x31\x6d\1\x1b\x5b\x30\x6d/g" -
}
yesno2 () {
        GET_ANSWER=0
        OLD_DESCRIPTION="$DESCRIPTION"
        CMD="$1"
        [ "$2" == "" ] \
        && DESCRIPTION="$(grep "^$1.*" $0|awk -F"# " '{print $2}'|tr -d '\n'|colorize red)" \
        || DESCRIPTION="$(colorize red <<< "$2")"
        until [ "$GET_ANSWER" == "1" ] ; do
                read -n 1 -p "Are you sure you want to $DESCRIPTION? " ANSWER
                if echo $ANSWER | grep -qi y ; then
                        echo
                        echo "Answer is yes, now $DESCRIPTION ..."
                        GET_ANSWER=1
                        $CMD && echo "$DESCRIPTION is done." || echo "$DESCRIPTION is done(return: $?)."
                else
                        if echo $ANSWER | grep -qi n ; then
                                echo
                                echo "Answer is no, won't $DESCRIPTION."
                                GET_ANSWER=1
                        else
                                echo
                                echo "Accept only Y/N."
                        fi
                fi

        done
        DESCRIPTION="$OLD_DESCRIPTION"
}

restore_data () { 
   if [ "$ARG4" != "" ] ; then
	#rdiff-backup $COMMON_OPTIONS -r "$DATE_TO_RESTORE" "$IBACKUP_DIR/$ARG2" "$ARG3"
	rdiff-backup $COMMON_OPTIONS restore --at "$DATE_TO_RESTORE" "$IBACKUP_DIR/$ARG2" "$ARG3"
   else
	#rdiff-backup $COMMON_OPTIONS -r 0B "$IBACKUP_DIR/$ARG2" "$ARG3"
	rdiff-backup $COMMON_OPTIONS restore --at 0B "$IBACKUP_DIR/$ARG2" "$ARG3"
   fi
}

#####################
# Command functions #
#####################
list () { # Query -> List target or backups
   if [ "$ARG2" == "" ] ; then
	echo -e "Target              Data Source"
	for TARGET in $(awk '{print $1}' $IBACKUP_LIST);do
		SOURCE_DIR="$(grep "^$TARGET[[:blank:]]" $IBACKUP_LIST|awk '{print $2}')"
		echo -n "$TARGET                             "|head -c 20
		echo "$SOURCE_DIR"
	done
   else
	[ -d "$IBACKUP_DIR/$ARG2/rdiff-backup-data" ] || exec echo "Error: $ARG2 is not a ibackup directory."
	BASE_SIZE="$(cat $(ls -tr $IBACKUP_DIR/$ARG2/rdiff-backup-data/session_statistics.*| head -2 | tail -1 )|grep "MirrorFileSize" | sed 's/.*(\(.*\))/\1/g')"
	NUM=1

	echo -e "No    \tBackup Date        \tChanged Files\tChanged Size\tRestore Size\tComment"
	for STATISTICS_FILE in $(ls -t $IBACKUP_DIR/$ARG2/rdiff-backup-data/session_statistics.*);do
        	INCREMENT_SIZE="$(grep "TotalDestinationSizeChange" $STATISTICS_FILE | sed 's/.*(\(.*\))/\1/g')"
	        INCREMENT_FILES="$(grep "IncrementFiles"   $STATISTICS_FILE | awk '{print $NF}')"
		RESTORE_SIZE="$(grep "SourceFileSize" $STATISTICS_FILE | sed 's/.*(\(.*\))/\1/g')"
	        BACKUP_DATE="$(date -d @$(grep "StartTime" $STATISTICS_FILE | awk '{print $2}') +%Y-%m-%dT%H:%M:%S)"
		BACKUP_COMMENT="$(grep "Comment" $STATISTICS_FILE | sed 's/^Comment //g')"
		if [ "$INCREMENT_FILES" != "0" ] ; then
		        grep -q "byte" <<< "$INCREMENT_SIZE" \
			&& echo -e "$NUM\t$BACKUP_DATE\t$INCREMENT_FILES\t\t$INCREMENT_SIZE\t$RESTORE_SIZE\t\t$BACKUP_COMMENT" \
			|| echo -e "$NUM\t$BACKUP_DATE\t$INCREMENT_FILES\t\t$INCREMENT_SIZE\t\t$RESTORE_SIZE\t\t$BACKUP_COMMENT"
		        NUM=$((NUM+1))
		fi
	done
	echo -e "0\tBase Mirror        \t0\t\t0 byte\t\t$BASE_SIZE"
	echo "Total backup size: $(du -sh $IBACKUP_DIR/$ARG2|awk '{print $1}')"
   fi
}

backup () { # Backup/Restore -> Backup
   [ "$ARG2" == "" ] && exec $0 list
   grep -q "^$ARG2[[:blank:]]" $IBACKUP_LIST || exec echo "Error: Target $ARG2 not in the list."
   grep -q "^$ARG2[[:blank:]]\+.\+$" $IBACKUP_LIST || exec echo "Error: The source path of target $ARG2 is wrong."
   [ "$ARG3" == "" ] || BACKUP_COMMENT="$ARG3"
   TARGET="$(grep "^$ARG2[[:blank:]]\+.\+$" $IBACKUP_LIST | awk '{print $1}')"
   SOURCE_DIRECTORY="$(grep "^$ARG2[[:blank:]]\+.\+$" $IBACKUP_LIST | awk '{print $2}')"
   if ! grep -q "::" <<< "$SOURCE_DIRECTORY" ; then
	[ -d "$SOURCE_DIRECTORY" ] || exec echo "Error: Source directory $SOURCE_DIRECTORY does not exist."
   fi

   echo "Calculating changed files ... " | colorize green
   [ -f $IBACKUP_DIR/$ARG2.exclude ] \
   && cat $IBACKUP_DIR/$ARG2.exclude | sed 's/^/\*\*\//g' | rdiff-backup $COMMON_OPTIONS backup --exclude-globbing-filelist - $SOURCE_DIRECTORY $IBACKUP_DIR/$ARG2|grep changed \
   || rdiff-backup $COMMON_OPTIONS backup $SOURCE_DIRECTORY $IBACKUP_DIR/$ARG2|grep changed
   #echo "cat $IBACKUP_DIR/$ARG2.exclude | sed 's/^/\*\*\//g' | rdiff-backup $COMMON_OPTIONS backup --exclude-globbing-filelist - $SOURCE_DIRECTORY $IBACKUP_DIR/$ARG2|grep changed"
   #echo "rdiff-backup $COMMON_OPTIONS backup $SOURCE_DIRECTORY $IBACKUP_DIR/$ARG2|grep changed"
   echo
   echo "Comment $BACKUP_COMMENT" >> $(ls -tr $IBACKUP_DIR/$ARG2/rdiff-backup-data/session_statistics.*|tail -1)
   cat $(ls -tr $IBACKUP_DIR/$ARG2/rdiff-backup-data/session_statistics.*|tail -1)|egrep "ElapsedTime|TotalDestinationSizeChange|IncrementFiles|SourceFileSize|Comment"
   echo "TotalBackupSize $(du -sh $IBACKUP_DIR/$ARG2|awk '{print $1}')"
}

restore () { # Backup/Restore -> Restore
   [ "$ARG2" == "" ] && exec $0 list
   grep -q "^$ARG2[[:blank:]]" $IBACKUP_LIST || exec echo "Error: Target $ARG2 not in the list."
   grep -q "^$ARG2[[:blank:]]\+.\+$" $IBACKUP_LIST || exec echo "Error: The source path of target $ARG2 is wrong."
   TARGET="$(grep "^$ARG2[[:blank:]]\+.\+$" $IBACKUP_LIST | awk '{print $1}')"
   SOURCE_DIRECTORY="$(grep "^$ARG2[[:blank:]]\+.\+$" $IBACKUP_LIST | awk '{print $2}')"
   [ -d "$ARG3" ] && exec echo "Error: Restore directory already exist, will not overwrite."
   [ "$ARG3" == "" ] && exec echo "Error: No restore target directory specified."

   if [ "$ARG4" == "" ] ; then
	yesno2 restore_data "restore data from $ARG2(Newest backup) to $ARG3"
   else
	if grep -q "^[0-9]\+$" <<< "$ARG4" ; then
		if [ "$ARG4" != "0" ] ; then
			DATE_TO_RESTORE="$($0 list $TARGET | head -n $(($ARG4+1)) | tail -1 | awk '{print $2}')"
			[ "$DATE_TO_RESTORE" == "" ] && exec echo "Error: Increment not found."
			$0 list $TARGET | head -n $(($ARG4+1)) | tail -1 |egrep "^$ARG4[[:blank:]]|^No[[:blank:]]"
		else
			DATE_TO_RESTORE="$(ls -tr $IBACKUP_DIR/$ARG2/rdiff-backup-data/session_statistics.*|head -1|awk -F. '{print $2}')"
		fi
		yesno2 restore_data "restore data from $ARG2($DATE_TO_RESTORE) to $ARG3 with increment above"
	else
		DATE_TO_RESTORE="$($0 list $TARGET | head -n $(($ARG4+1)) | tail -1 | awk '{print $2}')"
		[ "$DATE_TO_RESTORE" == "" ] && exec echo "Error: Increment not found."
		$0 list $TARGET | head -n $(($ARG4+1)) | tail -1 |egrep "." || exec echo "Error: Wrong date format."
		yesno2 restore_data "restore data from $ARG2($DATE_TO_RESTORE) to $ARG3 with increment above"
	fi
   fi
}

compare () { # DataCheck -> Compare between the source and the backuped files
   [ "$ARG2" == "" ] && exec $0 list
   grep -q "^$ARG2[[:blank:]]" $IBACKUP_LIST || exec echo "Error: Target $ARG2 not in the list."
   grep -q "^$ARG2[[:blank:]]\+.\+$" $IBACKUP_LIST || exec echo "Error: The source path of target $ARG2 is wrong."
   TARGET="$(grep "^$ARG2[[:blank:]]\+.\+$" $IBACKUP_LIST | awk '{print $1}')"
   SOURCE_DIRECTORY="$(grep "^$ARG2[[:blank:]]\+.\+$" $IBACKUP_LIST | awk '{print $2}')"

   [ -f $IBACKUP_DIR/$ARG2.exclude ] \
   && cat $IBACKUP_DIR/$ARG2.exclude | sed 's/^/\*\*\//g' | rdiff-backup $COMMON_OPTIONS compare --exclude-globbing-filelist - $SOURCE_DIRECTORY $IBACKUP_DIR/$ARG2|grep changed \
   || rdiff-backup $COMMON_OPTIONS compare $SOURCE_DIRECTORY $IBACKUP_DIR/$ARG2 2>&1 |grep changed
}

verify () { # DataCheck -> Verify backups
   [ "$ARG2" == "" ] && exec $0 list
   grep -q "^$ARG2[[:blank:]]" $IBACKUP_LIST \
   && rdiff-backup $COMMON_OPTIONS verify "$IBACKUP_DIR/$ARG2" \
   || rdiff-backup $COMMON_OPTIONS verify "$ARG2"

   [ "$?" == "0" ] \
   && echo "Verify OK" | colorize green \
   || echo "WARNING: Verify FAILED." | colorize red
}

#removeolder () { # Remove -> Remove older increment backups
#   [ "$ARG2" == "" ] && exec $0 list
#   grep -q "^$ARG2[[:blank:]]" $IBACKUP_LIST || exec echo "Error: Target $ARG2 not in the list."
#   grep -q "^$ARG2[[:blank:]]\+.\+$" $IBACKUP_LIST || exec echo "Error: The source path of target $ARG2 is wrong."
#   TARGET="$(grep "^$ARG2[[:blank:]]\+.\+$" $IBACKUP_LIST | awk '{print $1}')"
#   [ "$ARG3" == "" ] && exec $0 list $TARGET
#
#   if grep -q "^[0-9]\+$" <<< "$ARG3" ; then
#	[ "$ARG3" == "0" ] && exec echo "Error: Could not remove the base backup."
#	REMOVE_OLDER_THAN="$($0 list $TARGET|egrep "^$ARG3[[:blank:]]"|awk '{print $2}')"
#	rdiff-backup $COMMON_OPTIONS --force remove increments --older-than $REMOVE_OLDER_THAN $IBACKUP_DIR/$ARG2 | grep "Deleting increment file"
#   else
#	rdiff-backup $COMMON_OPTIONS --force remove increments --older-than $ARG3 $IBACKUP_DIR/$ARG2
#   fi
#}

listchange () { # Query -> List changes on each backup
   [ "$ARG2" == "" ] && exec $0 list
   grep -q "^$ARG2[[:blank:]]" $IBACKUP_LIST || exec echo "Error: Target $ARG2 not in the list."
   grep -q "^$ARG2[[:blank:]]\+.\+$" $IBACKUP_LIST || exec echo "Error: The source path of target $ARG2 is wrong."
   TARGET="$(grep "^$ARG2[[:blank:]]\+.\+$" $IBACKUP_LIST | awk '{print $1}')"

   if [ "$ARG3" == "" ] ; then
	zcat $(ls -t $IBACKUP_DIR/$ARG2/rdiff-backup-data/file_statistics.*|head -1)|awk '$2=="1" {print $1}'
	cat $(ls -t $IBACKUP_DIR/$ARG2/rdiff-backup-data/session_statistics.*|head -1)|egrep "TotalDestinationSizeChange|IncrementFiles|StartTime|EndTime|Comment"
   else
	[ "$ARG3" == "0" ] && exec echo "Error: No file changed on base."
	DATE_TO_LIST_CHANGE="$($0 list $TARGET|egrep "^$ARG3[[:blank:]]"|awk '{print $2}')"
	[ "$DATE_TO_LIST_CHANGE" == "" ] && exec echo "Error: Increment not found."
	zcat $IBACKUP_DIR/$ARG2/rdiff-backup-data/file_statistics.$DATE_TO_LIST_CHANGE*|awk '$2=="1" {print $1}'
	cat $IBACKUP_DIR/$ARG2/rdiff-backup-data/session_statistics.$DATE_TO_LIST_CHANGE*|egrep "TotalDestinationSizeChange|IncrementFiles|StartTime|EndTime|Comment"
   fi
}


demofunc () { ## DEMO -> demo function, doing nothing
   echo "demo, doing nothing"
}



###################################
# Start run the Command from here #
###################################
if [ "$ACTION" == "show_help" ] ; then
	show_help
else
	$ACTION
fi

# Log the time on the end
DATE_NOW="$(date +%Y/%m/%dT%H:%M:%S)"
echo -e "$DATE_NOW\t=End=\t$DO\tAns:$ANSWER\tLogin:$LOGIN_USER\tUser:$USER\t$TTY\t$MYIP\tARG:$*" >> "$IBACKUP_LOGFILE"


