#!/bin/sh

# File: refdb-backup

# Author: David Nebauer

# History: 2004-11-28 Created

# Purpose: Backups references, notes and styles


# VARIABLES
references_backup="references"  # base of references backup file name
notes_backup="xnotes.xml"  # notes backup file name
styles_directory="styles"  # name of styles directory (intra-archive)
logfile="log_backup"  # name of log file
archive="refdb_backup_`date '+%Y%m%d_%H%M'`.tar.gz"  # archive name
start_dir=`pwd`  # directory in which utility is run
backup_dir=${start_dir}  # overridden by '-d' option
format="ris"  # overridden by '-f' option
refdba="refdba"  # refdba command
refdbc="refdbc"  # refdbc command
parameters="[-u USER] [-w PWD] [-f FORMAT] [-d DIR] [-s]"  # cmd-line args
feedback="true"  # whether user receives screen feedback


# PROCEDURES

# Give user feedback
#   params: 1 - message
#           2 - indent (number of indents|tabs)
#           3 - copy to log? ( 0 = true, 1 = false )
#           4 - terminate with newline? ( 0 = true, 1 = false )
#   return: nil
feedback () {
	# decide on use of newline flag
	if [ $4 -ne 0 ] ; then use_newline="-n" ; else use_newline="" ; fi
	# add indents as required
	msg=$1
	loop=1
	while [ 1 ] ; do
		[ ${loop} -gt $2 ] && break
		[ ${feedback} ] && echo -n "  "
		loop=$(( ${loop} + 1 ))
	done
	# screen feedback
	[ ${feedback} ] && echo ${use_newline} ${msg}
	# log feedback
	[ $3 -eq 0 ] && echo ${use_newline} ${msg} >> ${logfile}
}	
# Report success of operation
#   params: 1 - message
#           2 - copy to log? ("log" = yes, "" = no )
#   return: nil
report () {
	if [ $1 -eq 0 ] ; then
		[ "$2" = "log" ] && echo "OK." >> ${logfile}
		feedback " OK." 0 1 0
	else
		[ "$2" = "log" ] && echo "Failed." >> ${logfile}
		feedback " Failed." 0 1 0
	fi
}
# Report failure and abort
#   params: 1 - message
#           2 - success flag ( "succeeded" = yes | "*" = no )
#   return: nil
endScript () {
	# attempt to change back to initial directory
	cd ${start_dir} || \
		feedback "Error: Unable to cd to start dir '${start_dir}'." 0 1 0
	# attempt to delete temporary files
	err_msg="Error: Unable to delete temporary directory '${tempdir}'."
	[ -d ${tempdir} ] && rm -fr ${tempdir} || feedback ${err_msg} 0 1 0
	# provide feedback regarding success of backup
	feedback " " 0 1 0 ; feedback "$1" 0 1 0 
	feedback " " 0 1 0 ; feedback "refdb backup $2." 0 1 0
	feedback "`basename $0` is finished." 0 1 0
	# provide exit status to shell
	if [ "$2" = "succeeded" ] ; then exit 0 ; else exit 1 ; fi
}
# Show usage
#   params: nil
#   return: nil
displayUsage () {
	echo "`basename $0`: Backs up RefDB data."
	echo
	echo "Backs up references, notes and styles from RefDB."
	echo 
	echo "Usage: `basename $0` ${parameters}"
	echo "       `basename $0` [ -h ]"
	echo
	echo "Options: -u USER   = username for RefDB clients ('refdba'|'refdbc')"
	echo "         -w PWD    = password for RefDB clients ('refdba'|'refdbc')"
	echo "         -f FORMAT = references storage format ('ris'|'risx')"
	echo "                     default is 'ris'"
	echo "         -d DIR    = directory to place backup file"
	echo "                     default is current directory"
	echo "         -s        = silent, i.e., no screen feedback"
	echo "Note: RefDB clients can be configured for automatic access."
	echo "      If this is so, username and password are not required."
	echo
	echo "The backup archive is a gzipped tarfile (for more information"
	echo "try 'man tar' and 'man gzip').  The archive filename is"
	echo "'refdb_backup_YYYYMMDD_HHMM.tar.gz'."
}


# MAIN

# Read the command line options
while getopts ":hf:d:u:w:s" opt ; do
	case ${opt} in
		h  ) displayUsage && exit 0;;
		f  ) format=${OPTARG};;
		d  ) backup_dir=${OPTARG};;
		u  ) refdba="${refdba} -u \"${OPTARG}\""
		     refdbc="${refdbc} -u \"${OPTARG}\"";;
		w  ) refdba="${refdba} -w \"${OPTARG}\""
		     refdbc="${refdbc} -w \"${OPTARG}\"";;
		s  ) feedback="";;
		\? ) echo "Error: Invalid flag '${OPTARG}' detected"
		     echo "Usage: `basename $0` ${parameters}"
		     echo "Try '`basename $0` -h' for help"
			 printf "\a"
			 exit 1;;
		\: ) echo "Error: No argument supplied for flag '${OPTARG}'"
		     echo "Usage: `basename $0` ${parameters}"
		     echo "Try '`basename $0` -h' for help"
			 printf "\a"
			 exit 1;;
	esac
done
shift $(( ${OPTIND} - 1 ))  # not really needed, but...

# Check for valid arguments
# - format ('ris'|'risx')
case ${format} in
	ris  ) extension="ris";;
	risx ) extension="xml";;
	*    ) endScript \
			"Error: '${format}' is an invalid reference storage format." \
			"failed";;
esac
# - directory must be writable
	# remove terminal slash '/'
[ `echo ${backup_dir} | grep "\/$"` ] && backup_dir=${backup_dir%%/}
	# convert relative to absolute dirpath
[ `echo ${backup_dir} | grep -v "^\/"` ] && \
	backup_dir="${start_dir}/${backup_dir}"
	# now can test
if [ ! -d ${backup_dir} -o ! -w ${backup_dir} ] ; then
	endScript "Error: '${backup_dir}' is not a writable directory." "failed"
fi

# Test for RefDB access
${refdba} -C listdb >/dev/null 2>&1 || \
	endScript "Error: Unable to access RefDB using client 'refdba'." "failed"
${refdbc} -C listdb >/dev/null 2>&1 || \
	endScript "Error: Unable to access RefDB using client 'refdbc'." "failed"

# Will use temporary directory for working
# - ensure deletion on exit and change to working directory
tempdir_base="`basename $0`.XXXXXX"
tempdir=`mktemp -dq ${tempdir_base}` \
	|| endScript "Error: Unable to create temporary directory." "failed"
trap "rm -fr ${tempdir}" 0 1 2 3 5 15  # delete on exit
cd ${tempdir} || \
	endScript "Error: Unable to cd to temporary directory '${tempdir}'." \
	"failed"

# Informational message
feedback "`basename $0` is running..." 0 1 0 ; feedback " " 0 1 0

# Record date/time of backup
echo "RefDB backup performed by '`basename $0`'." > ${logfile}
echo "Performed: `date '+%Y-%m-%d, %H:%M:%S'`." >> ${logfile}
feedback "Storage format: ${format}." 0 0 0
feedback " " 0 1 0
echo "--------------------------------------------------------" >> ${logfile}

# Cycle through databases in turn and backup references and notes
for db in `${refdba} -C listdb | cut -d ' ' -f 1` ; do
	feedback "Processing database: ${db}" 0 0 0
	dirname="db_${db}"
	feedback "Creating subdirectory '${dirname}' ..." 1 0 1
	mkdir ${dirname} >/dev/null 2>&1
	report $? log
	# dump references
	reffile="${dirname}/${references_backup}.${extension}"
	feedback "Backing up references ..." 1 0 1
	${refdbc} -C getref -d ${db} -t ${format} -o ${reffile} ":ID:>0" \
			>/dev/null 2>&1
	report $? log
	# dump notes
	notefile="${dirname}/${notes_backup}"
	feedback "Backing up notes ..." 1 0 1
	${refdbc} -C getnote -d ${db} -t xnote -o ${notefile} ":NID:>0" \
			>/dev/null 2>&1
	report $? log
done

# Cycle through styles in turn and back them up
feedback "Processing styles" 0 0 0
feedback "Creating styles directory '${styles_directory}' ..." 1 0 1
mkdir ${styles_directory} >/dev/null 2>&1
report $? log
for style in `${refdba} -C liststyle` ; do
	feedback "Backing up style: ${style} ..." 1 0 1
	# remove terminal period from file name if present
	if [ `echo ${style} | grep "\.$" | wc -l` -eq 1 ] ; then
		stylefile=${style%*.}
	else
		stylefile=${style}
	fi
	stylefile="${styles_directory}/${stylefile}.xml"
	${refdba} -C getstyle -o ${stylefile} ${style} >/dev/null 2>&1
	report $? log
done

# Now done
echo "--------------------------------------------------------" >> ${logfile}
feedback "Written log file: '${logfile}' (located in archive)." 0 1 0

# Create archive
feedback "Create archive" 0 1 0
feedback "Writing archive ..." 1 1 1
tar -czf ${archive} *
exit_status=$? && report ${exit_status}
[ ${exit_status} -ne 0 ] \
	&& endScript "Error: Unable to write archive." "failed"

# Move archive to backup directory
feedback "Copying archive to target directory..." 1 1 1
mv -f ${archive} ${backup_dir} >/dev/null 2>&1
exit_status=$? && report ${exit_status}
[ ${exit_status} -ne 0 ] \
	&& endScript "Error: Unable to move archive to backup directory." \
	"failed"

# Finished
[ "${backup_dir}" != "${start_dir}" ] && archive="${backup_dir}/${archive}"
feedback " " 0 1 0 ; feedback "Backup archive: '${archive}'." 0 1 0
endScript "Backup finished." "succeeded"
