Running your own owncloud is a great thing, having all your data on your own servers. However since all the data are now only on your servers you also have to take care to run a regular backup  especially for calendar and contact information that users store in your owncloud.I also have my "owncloud" and so I also came across the backup problem since I use calendars ans contacts stored in my owncloud version 9.

Several scripts I found were not capable of backing up owncloud 9 data and thats why I started to write my own script for home use owncloud servers. It should:

  • be able to backup calendars and contacts for each of the existing users. Which eg calendar of which contact has to backed up  should be configurable in the script
  • the backup should be in form of exports for each calendar and each addressbook with contacts not a backup of the mysql database (or tables of it) that contains all these data. By exporting data into .ics files and .vcf files its easy to reimport the data in case of data loss.
  • be able to run this script by cron without user intervention
  • offer a way to create a configurable count of backups so one can easily access one of the older backups. Thus this script stores each backup in a separate folder with the todays date. This folder then contains a second level folder for each user in which the backup files for this user are written
  • since authentication (user/password) against ownclod server is needed to make a backup, the script  should only need one user/password to backup all wanted data for all users. This is important in my eyes since the password used for authentification has to be stored in cleartext in the script. So one cleartext password of a special backup user is better than <number of users to backup>  passwords that would have to be stores else.

As a result of the requirements from above, all users for which either calendars or addressbooks should be backed up, have to share each calendar, each addressbook read only to the owncloud backup user which by default is named "backup" (see AUTHUSER Shellvariable).

The script is designed for small home owncloud installations, since the way the backup is done via one single backup user to which all owncloud users have to share their calendars and addressbooks for backup is certainly not well suited for an installation with hundreds of users.

Configuration

Configuration is quite simple. You have to set the Shell-Variables in the header of the script by which it is configured. Take a look at the script first (see below) then you can better understand the following explanations.

  • USER contains the owncloud users for which backups should be performed. 
  • AUTHUSER is the owncloud user which is used for authentification and perfoming the backup. You need to create this regular user in your owncloud and enter its password (base64-encoded; see script) into the variable AUTHPWENC in the script. The base64 encoding is just a precaution so that the cleartext-password is not readable at a glance when someone is looking at the script.
  • BACKUPROOT is the filesystem path on the host on which you run the script where backups are stored
  • OCSERVER is the hostname of the server
  • GENERATIONS is the number of backups to keep. If there are more backups than this number, the oldest backups are deleted until maximal GENERATIONS backups are left over.
  • CALENDAR[] and CONTACT[] are to bash arrays where you can easily configure which calendar/addressbook should be backuped for which user.

The calendar names and addressbook names you write into CALENDAR[], CONTACT[] are not the names you see in the web interface of owncloud. Instead you have to look at the URL of the calendar or addressbook by clicking on the chain-symbol in ownclouds web interface for calendars/addressbooks you want to back up. At the very end of the URL you will see the name owncloud uses for this addressebook. Usually this name is very similar just a lower case version of the name the user sees.

The script code

The script is a simple bash script. So just copy it into a file, make this file executable and then set the configuration shell variables in the scripts head.

#!/bin/bash
#
# Script that does a backup of owncloud 9 calendars and contacts for
# given users. In order to keep only one cleartext password
# in this script not one for each user, an owncloud backup user is used
# for authentification. You have to create this user (AUTHUSER) in owncloud
# and all users wishing a backup of their data have to share their calendars
# and contacts (read only) to this user.
#
# R. Krienke, This email address is being protected from spambots. You need JavaScript enabled to view it.
# Version 0.1, 2016-04-18

# owncloud users for which contacs/calendars should be backupped
USER="john jeff cathy"
#

# owncloud user used for authentication against owncloud server
# !!! All users above have to share their cals, contacts to this user (read only) !!!
AUTHUSER="backup"
# The password of the backup user in base64 encoding for a little obfuscation

# Create new password by: echo "MyPassword"|base64
AUTHPWENC="TXlQYXNzd29yZAo="
AUTHPW=`echo $AUTHPWENC|base64 --decode`
#
# Where to store backupdata

BACKUPROOT="/import/backup/owncloud/calcard"
#
# hostname or ip of owncloud server

OCSERVER="owncloud"
#
# Each backup is stored in one subdirectory in $BACKUPROOT with the todays date

# The latest $GENERATIONS directories are kept, older ones are deleted after each run
# of this script.
GENERATIONS=30

# For each user to backup tell the script which calendars of this user should be backupped
# The calendar names are not the ones you see in ownclouds webpage but those you
# see when looking at the cal name at the end of the URL for this calendar shown in ownlouds
# webpage for this calendar. Seperate multiple calendar names by space.
declare -A CALENDAR
CALENDAR[john]="persönlich geburtstage"
CALENDAR[jeff]="persoenlich geburtstage"
CALENDAR[cathy]="persoenlich"

# Addr books, same like above
declare -A CONTACT
CONTACT[jeff]="kontakte"
CONTACT[cathy]="kontakte"

### configuration ends here ######################################################

echo "$0 starting caldav/carddav backup ...."

umask 0077
trap "rm -f $HOME/.wgetrc" HUP KILL TERM ABRT STOP
cat <<EOF >$HOME/.wgetrc
http-password = $AUTHPW
http-user = $AUTHUSER
EOF
chmod 600 $HOME/.wgetrc


#
# get one calendar for one specific user
#
getcal () {
    local CALNAME="$1"
    local USER="$2"
    local BUSER="$3"
    local SERVER=$4
    local ROOT="$5"

    BACKUPFILE="$CALNAME.ics"
    TODAY=`date "+%Y-%m-%d"`
    BPATH="$ROOT/$TODAY/$USER"
    if [ -e "$BPATH/$CALNAME" ]; then
    rm -rf "$BPATH/$CALNAME"
    fi
    mkdir -p $BPATH 2>/dev/null

    wget  -q --no-check-certificate --auth-no-challenge --no-clobber \
    --http-user=$BUSER -O $BPATH/${BACKUPFILE} \
    "https://$SERVER/owncloud/remote.php/dav/calendars/$USER/${CALNAME}?export"

    STATUS=$?
    echo "Status wget: $?"


    if [ -e "$BPATH/$CALNAME" ]; then
        chmod 600 "$BPATH/$CALNAME"
    fi    
}

#
# get one addressbook for one specific user and addressbook
#
getcard() {
    local ADDRBOOKNAME="$1"
    local USER="$2"
    local BUSER="$3"
    local SERVER="$4"
    local ROOT="$5"

    BACKUPFILE="${ADDRBOOKNAME}.vcf"
    TODAY=`date "+%Y-%m-%d"`
    BPATH="$ROOT/$TODAY/$USER"
    if [ -e "$BPATH/$BACKUPFILE" ]; then
    rm -rf "$BPATH/$BACKUPFILE"
    fi
    mkdir -p $BPATH 2>/dev/null

    wget -q --no-check-certificate --auth-no-challenge --no-clobber \
         --http-user=backup --http-password=$AUTHPW   \
         -O "$BPATH/$BACKUPFILE"   \
         "$SERVER/owncloud/remote.php/carddav/addressbooks/$USER/${ADDRBOOKNAME}?export" \
    STATUS=$?
    echo "Status wget: $?"

    if [ -e "$BPATH/$BACKUPFILE" ]; then
        chmod 600 "$BPATH/$BACKUPFILE"
    fi    
}


#
# Backup all calendars
#
for i in $USER; do
   CALLIST=${CALENDAR[$i]}
   echo "$CALLIST"
   for j in $CALLIST; do
    echo "Getting cal  $i, $j"
        getcal "$j" "$i" "$AUTHUSER" "$OCSERVER" "$BACKUPROOT"
   done
done   

#
# Backup all addressbooks
#
for i in $USER; do
   CARDLIST=${CONTACT[$i]}
   echo "$CARDLIST"
   for j in $CARDLIST; do
    echo "Getting card  $i, $j"
        getcard  "$j" "$i" "$AUTHUSER" "$OCSERVER" "$BACKUPROOT"
   done
done   

#
# cleanup
#
echo "Cleaning up old backups ..."
cd $BACKUPROOT
if [ $? != 0 ]; then
  echo "$0: Cannot chdir into $BACKUPROOT. Stop"
  exit 1
fi

NUM="+$GENERATIONS"
for i in `ls -1|sort -r|tail -n ${NUM}`; do
    echo "Deleting: $BACKUPROOT/$i"
    rm -rf "$i"
done

kill -TERM  $$