Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Overview

This utility is a fancy wrapper around `syncevolution` and `cron` and it creates or removes connections to remote carddav/caldav servers. This utility was created specifically to work on Ubuntu Touch but it may work in different environments.
This utility is a fancy wrapper around `syncevolution` and user-based `systemd timer` templates and it creates or removes connections to remote carddav/caldav servers. This utility was created specifically to work on Ubuntu Touch but it may work in different environments.

# Get the Code.

Expand All @@ -14,7 +14,7 @@ Configure server URLs, credentials, and naming preferences.
cp config-nextcloud-template.txt config-personal.txt
vim config-personal.txt

Executing `setup-dav-sync.sh --contacts config-personal.txt` or `setup-dav-sync.sh --calendar config-personal.txt` will read your configurations, connect to the specified carddav/caldav server, synchronize data and setup a cron job to keep this device in sync with the server.
Executing `setup-dav-sync.sh --contacts config-personal.txt` or `setup-dav-sync.sh --calendar config-personal.txt` will read your configurations, connect to the specified carddav/caldav server, synchronize data and setup a systemd timer to keep this device in sync with the server.

Executing `setup-dav-sync.sh --delete-contacts config-personal.txt` or `setup-dav-sync.sh --delete-calendar config-personal.txt` will read your configurations, remove them from `syncevolution` configurations and remove all data from this device only (the data on the server will not be affected).

Expand All @@ -24,10 +24,17 @@ Simple specify multiple configurations files and a single command will process t

setup-dav-sync.sh --contacts --calendar config-personal.txt holidays.txt team-awesome-schedule.txt

# Backwards compatibility

Users of `v0.1.1` or older of this tool (typically used with Ubuntu-Touch before the 20.04 release) might want to consider using the old script to remove the old configuration before reapplying it with the new script, as there has been some breaking changes. The old script relied on `cron` to do the regular sync-job. In Ubuntu-Touch Focal cron is no longer installed by default. Therefore this script shifts to using systemd timer units.

This new version of the script does not remove existing cron-jobs that may remain from previous installations. These must be cleared out manually, otherwise synchronization might start in parallel. The effects of this are unknown but potentially not desirable.

# Known Bugs

1. When a contact is delete from Ubuntu Touch using the native Contacts app `syncevolution` will relay this change to the carddav server as a 'modification' of the contact and it will not be deleted from the server and other clients synchronized with the server will still have this _old_ contact.
2. Even after a calendar has been removed it will still show up as an available calendar in the Ubuntu Touch native calendar app.
2. Even after a calendar has been removed it will still show up as an available calendar in the Ubuntu Touch native calendar app. This can sometimes be mitigated by rebooting the device and running the delete command again.
3. There currently is only one synchronization interval possible for all synchronizations. This could be mitigated by systemd unit overlays, but needs to be implemented.

# Recommendations for Nextcloud Users.

Expand Down
7 changes: 3 additions & 4 deletions configs-template-nextcloud.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ CONTACTS_VISUAL_NAMES[0]="$USERNAME - nextcloud" # can NOT includ

###################################################################################################################################

declare -g CRON_FREQUENCY='0,15,30,45 * * * *' # every 15 minutes
#declare -g CRON_FREQUENCY='0,30 * * * *' # every 30 minutes
#declare -g CRON_FREQUENCY='0 * * * *' # every hour
#declare -g CRON_FREQUENCY='@hourly' # non standard
declare -g SYNC_INTERVAL='15min' # every 15 minutes
#declare -g SYNC_INTERVAL='60min' # every hour
#declare -g SYNC_INTERVAL='2hr 30min' # every 2,5 hours
133 changes: 59 additions & 74 deletions setup-dav-sync.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,67 +32,6 @@ function generate_padding {
done
}

function cron_handler {
local action="$1" # either 'add' or 'delete'
local server_config_name="$2"
local cron_entry="$3"

local cron_tab="/var/spool/cron/crontabs/$USER"
local regex_search="^.*$server_config_name.*$"

sudo mount / -o remount,rw

case "$action" in
'add')
if sudo [ -f "$cron_tab" ]; then
local grep_result=$(sudo grep --only-matching "$regex_search" "$cron_tab")

# if a matching cron entry exists replace it
if [ ! -z "$grep_result" ]; then
echo "cron - updating entry for '$server_config_name'"
sudo sed --in-place --expression="s|$regex_search|$cron_entry|" "$cron_tab"
else
echo "cron - creating new entry for '$server_config_name'"
echo "$cron_entry" | sudo tee --append "$cron_tab" &>/dev/null
fi
else
echo "User's cron tab not found, creating it."
sudo touch "$cron_tab"
sudo chown $USER:clickpkg "$cron_tab" # non portable operation
sudo chmod 600 "$cron_tab"

echo "$cron_entry" | sudo tee --append "$cron_tab" &>/dev/null
fi
;;
'delete')
if sudo [ -f "$cron_tab" ]; then
local grep_result=$(sudo grep --only-matching "$regex_search" "$cron_tab")

# if a matching cron entry exists delete it
if [ ! -z "$grep_result" ]; then
echo "cron - deleting entry for '$server_config_name'"
sudo sed --in-place --expression="/$regex_search/d" "$cron_tab"
else
echo "cron - no entry found for '$server_config_name'"
fi
else
echo "User's cron tab not found, no action taken on cron tab."
fi
;;
*)
sudo mount / -o remount,ro
echo 'Internal error!' >&2
echo "in the '${FUNCNAME[0]}' function" >&2
exit 1
;;
esac

sudo service cron restart &>/dev/null

sudo mount / -o remount,ro

}

function manual_sync {
local action="$1" # either 'add' or 'delete'
local name="$2"
Expand Down Expand Up @@ -126,7 +65,6 @@ function manual_sync {
echo "$padding"
;;
*)
sudo mount / -o remount,ro
echo 'Internal error!' >&2
echo "in the '${FUNCNAME[0]}' function" >&2
exit 1
Expand All @@ -138,11 +76,11 @@ function setup_sync {
local server_config_name="$1"
local name="$2"

local sync="export DISPLAY=:0.0 && export DBUS_SESSION_BUS_ADDRESS=\$(ps -u $USER e | grep -Eo 'dbus-daemon.*address=unix:abstract=/tmp/dbus-[A-Za-z0-9]{10}' | tail -c35) && /usr/bin/syncevolution $server_config_name"
local cron_entry="$CRON_FREQUENCY $sync"
local sync="systemctl start utdavsync@${server_config_name}.service"
local action='add'

cron_handler "$action" "$server_config_name" "$cron_entry"
# Enable systemd user timer
systemctl --user enable --now "utdavsync@${server_config_name}.timer"

if [ ! -d $HOME/bin ]; then
mkdir $HOME/bin
Expand All @@ -151,14 +89,56 @@ function setup_sync {
manual_sync "$action" "$name" "$sync"
}

function setup_systemd_units {
# Default interval to 60min if not configured
# TODO: allow for individual intervals
local interval="${1:-60min}"

# On a fresh install, there is no systemd user unit folder
mkdir --parents "${HOME}/.config/systemd/user/"

# Create systemd user service template
# Indentation and bash here documents are not known to work well together,
# so this will look a little ugly.
cat << EOF > "${HOME}/.config/systemd/user/utdavsync@.service"
[Unit]
Description=CardDAV sync for %i
After=network.target

[Service]
Type=oneshot
WorkingDirectory=%h
ExecStart=/usr/bin/syncevolution %i

[Install]
WantedBy=default.target
EOF

# Create systemd user timer template
cat << EOF > "${HOME}/.config/systemd/user/utdavsync@.timer"
[Unit]
Description=CardDAV sync for %i

[Timer]
OnBootSec=3min
OnUnitInactiveSec=${interval}

[Install]
WantedBy=timers.target
EOF

# Make the new user units known to systemd
systemctl --user daemon-reload
}

function delete {
local type_action="$1"
local server_config_name="$2"
local name="$3"
local visual_name="$4"

local action='delete'
cron_handler "$action" "$server_config_name"
systemctl --user disable --now "utdavsync@${server_config_name}.timer"
manual_sync "$action" "$name"

case "$type_action" in
Expand Down Expand Up @@ -207,11 +187,6 @@ function contacts {

local url="${CARD_URL%%/}/${CARD_NAMES[$i]}"


# add cron entry and create manual sync script
setup_sync "$contacts_server_config_names" \
"$contacts_names"

#Create contact list
syncevolution --create-database backend=evolution-contacts \
database="$contacts_visual_names"
Expand Down Expand Up @@ -249,6 +224,10 @@ function contacts {
#Start first sync
syncevolution --sync refresh-from-remote \
"$contacts_server_config_names" "$contacts_names"

# add systemd user timer and create manual sync script
setup_sync "$contacts_server_config_names" \
"$contacts_names"
done
}

Expand All @@ -262,9 +241,6 @@ function calendar {

local url="${CAL_URL%%/}/${CAL_NAMES[$i]}"

# add cron entry and create manual sync script
setup_sync "$calendar_server_config_names" "$calendar_names"

#Create Calendar
syncevolution --create-database backend=evolution-calendar \
database="$calendar_visual_names"
Expand Down Expand Up @@ -301,6 +277,9 @@ function calendar {
#Start first sync
syncevolution --sync refresh-from-remote \
"$calendar_server_config_names" "$calendar_names"

# add systemd user timer and create manual sync script
setup_sync "$calendar_server_config_names" "$calendar_names"
done
}

Expand Down Expand Up @@ -400,6 +379,12 @@ for config_file in "$@"; do
continue
fi

# systemd units only need to be set up once, but we don't know the sync
# interval before we source a config file. This way the configured interval
# from the last config file wins.
# TODO: allow for individual intervals
setup_systemd_units "${SYNC_INTERVAL}"

if $calendar; then
calendar
fi
Expand Down