Sharing, Syncing and editing iCal over WebDAV

(Sep 11 2010)Update: I am still seeing some traffic to this page in my logs. I thought I should mention that the method described below no longer works. A better alternative now would be to use the CalDAV protocol. You might want to investigate running Apple’s Open Source implementation. I did that for the longest time before I finally just switched to Gmail, which supports CalDAV and syncs with iCal both ways like a dream. Problem solved.

iCal. Such a nice calendering application that is, and a pioneer in the vastly used ics calendar format. Is it the ultimate calendar ever? Well, almost.

What drove me insane was that, while you can publish your calendars to a WebDAV server for others to see and subscribe to, you cannot edit the calendars that are published — unless you have a .mac account. While I sincerly hope this will be in Leopard, I’m not holding my breath.

You see, I run Linux on my desktop at home, and Mac OS X on my laptop, for work. I just wanted to find a simple way to share my calendar between the two machines, and to be able to edit them on any machine.

And it just so happens I just have the solution.

Configuring the WebDAV server

Now, vast amounts of documentation can be found on the subject of setting up the WebDAV server. I’m not going to insult those who already worked on the problem by needlessly rewriting the instructions here. However, I will give you pointers.

So you could go ahead and setup your WebDAV server on *nix or OS X using the two first links, or you could just get free WebDAV hosting using the two others. Any will do.

For the sake of completeness, I will add my own WebDAV configuration below, on Gentoo Linux with Apache 2. I just installed mod_dav on apache2, and added the following configuration to /etc/apache/modules.d/10_mod_dav.conf:


<IfModule mod_dav.c>
  DAVLockDB /var/lock/mod_dav/Dav_Lock
  Alias /ical /var/dav/davroot
  DavMinTimeout 600
        <Directory /var/dav/davroot>
                Dav On
                Options +Indexes
                AllowOverride None
                AuthType Digest
                AuthName "ical-webdav"
                AuthDigestFile /var/dav/htpasswd.digest
                Require valid-user
                Order allow,deny
                Allow from All
        </Directory>
</IfModule>

Of course, I just then added a user using htdigest. You could use “AuthType Basic” if you don’t use mod_digest.

I tested the WebDAV functionality using the OS X “Connect to server” Function. Just enter the path to your webdav location.

connect_to_webdav

I was prompted for authentication. Upon submitting my credentials, the drive was mounted on the desktop.

connected_webdav

You should test to see if you can write files to the WebDAV folder. Once that’s done, we can move on the beef of this article.

Setting up iCal

You should take care of iCal first. First thing first, go ahead and share your calendar via iCal, as usual. Enter your WebDAV server information.

ical_sharing

Once all is published and well, according to the little icon next to your calendar (ical_sharing_widget), you have successfully published your calendar in a perfect read only fashion.

Making iCal Sync from the server

This is the sweet part, the core of this article. Now, we want iCal to also download the changes from the server. For this purpose, you must find out the Path of your corestorage.ics file for that calendar. Go have a look in ~/Libary/Application Support/iCal/Sources/, right now. (That being the “Library” folder from your Home Directory).

ical_files

You should see some folder(s) with ugly names, one per Calendar you have. There’s no easy step to this, but you have to find out which is which. You can judge either by creation dates, or by opening the .ics file in your favorite text editor to see if you recognize some of the contents. Of course, if you only have one calendar, things are simplified, you will just have one folder, and one file.

In any case, write down the absolute path to the folder containing the ics file, it’s important. For instance, in my case, it turns out to be:

/Users/supernaut/Library/Application Support/iCal/Sources/2FBCA21F-80E0-44FD-B47A-ED34AE652010.calendar

ical_sync.sh

I have then written two scripts that take care of downloading, comparing and synchronizing your calendars from the WebDAV server. You will need the following utilities installed, most are available from Fink.

  • wget
  • md5sum

Download and place the following script somewhere on your system. I created a folder named “bin” in my home directory and placed it there.

Next, edit the script and change the variables to match your situation, eg:


ICALPATH="/you/core/ics/folder"
WEBDAV="http://www.yourserver.com/path/to/dav/folder/calendar.ics"
LOGIN="yourlogin"
PASSWORD="yourpassword"

WGET="/sw/bin/wget"
MD5SUM="/sw/bin/md5sum"

Remember! The value for $ICALPATH is actually the one you wrote down earlier. Don’t worry if you mess it up, the script will warn you.

Once that’s done, run it in terminal. It should tell you if ANYTHING goes wrong and back off. I made it extra careful, it would suck to wreck your calendar because the server is down or answers strange things.

This will indeed download and update your calendar on your Mac from the WebDAV server. But how will you make this happen automagically? Well, I have taken care of that too.

Working the Applescript magic

Turns out I have written a second script, this time, an applescript one. You can download the source over there.

Open it or paste it in Script Editor. You should then change the line

do shell script "bash /Users/supernaut/bin/ical_sync.sh"

to match the location of the ical_sync.sh script and then hit “compile”.

Once that’s done, just save it somewhere, but make sure you save it:

  • As an Application
  • Make sure the startup screen is unchecked

script_save

Once that’s done, simply replace the iCal icon in your dock by this script. You can add a pretty icon to it to make it more bearable.

dock screenshot

Everytime you click on iCal then, it will sync up the calendar from the server, and tell if you if anything goes wrong. It will then proceed to launch the real iCal for you.

The AppleScript will show a dialog box if, say, the network is down, or something went wrong. For example:

ical crapped out screenshot

You can then take action. As a precaution, in the event that it wasn’t what you wanted to do, the script will ask you if you still want to launch iCal.

ical notice

I made it that way because, sometimes, you might want to look at your calendar without being connected to the internet. Makes sense, right?

Now, onwards to other Calendaring software.

Configuring Other Calendars

Nearly any other Calendaring software will not only fetch the lastest ics file from a webdav server, but will also write it back on change, or upon instruction to do so. This means, no problems whatsoever.

I use Korganizer in conjunction with Kontact on my KDE based desktop. Evolution and Sunbird could most likely do this as well.

I just added a calendar for a web source, and configured it with my WebDAV information:

kontact_korganizer_webdav

Please excuse the craptacular quality of the screenshot. It was taken while forwarding X11 to my Mac over an SSH tunnel, and as such, fonts are screwy.

Then, it just worked out of the box. No problem whatsoever.

In conclusion

This is somewhat of a cheap and dirty hack, but it works fairly well. I’ve been using it for three weeks and it’s a dream. I sincerely hope that this has been somewhat useful to at least someone :)

Drop me a comment if you liked it, or if you have problems. Feel free to ask, really.

You might also enjoy:

About Alexandre Gauthier

A freelance network guy, sometimes programmer and overall tinkerer. Said to be a decent writer, in both english and en français. Wears fancy pants with torn t-shirts on sundays. Enjoys writing long, vitriolic diatribes and short stories. Lives inside a unix shell, favorite text editor is vim.
This entry was posted in Computers, English, Tutorials/How-To, Web Servers and tagged , , , , , , . Bookmark the permalink.

18 Responses to Sharing, Syncing and editing iCal over WebDAV

  1. The shell scripting is a very clever idea and pretty well written, too. However, a much cleaner way of automating the process would be to use a Cron job that ran every 5 minutes or so.

  2. mr_daemon says:

    I thought of the cronjob idea at first, but then again, I was afraid of what it would do if say, network was down, and if not, the shell script actually downloads the file everytime it is ran for comparison. And downloading that file every five minutes didn’t feel really polite. So I decided to run it on startup.

    (Also, what happens if the file is updated while iCal is running?)

  3. Hmm…perhaps every 5 minutes is a little on the heavy side; I run my own WebDAV server, though, so I’m less considerate of my own equipment. Every 30 minutes or every hour wouldn’t be out of the question, though

    To only run when there’s a connection, call a shell script with this at the beginning:


    /sbin/ping -c 1 [your_server] 2>&1 >/dev/null
    if [ $? -ne 0 ]
    then
    exit
    fi

    $? is the exit code of the last command (ping, in this case). Ping returns 0 if everything went well. If it can’t connect, it’s going to return a non-zero value.

    File updates while iCal is running are an issue, though. I experimented with manually editing one of the iCalendar files, and iCal completely ignored it. Maybe some sort of Applescript to reload or something?

  4. gaber says:

    It’s possible to add checking if network is up to above script, and if it’s up then syncs.

    if [ ping -c 1 -t apple.com > /dev/null; echo $? -eq 0 ]
    then
    bash /Users/USERNAME/bin/ical_sync.sh
    fi

    And put it into crond.

  5. Less than perfect, but you could just use Applescript to quit and restart iCal. That causes it to reload all the calendars. The following is the best I’ve been able to come up with (n.b.: I have no Applescripting experience):


    tell application "iCal" to quit
    delay 1
    tell application "iCal" to launch
    tell application "Finder" to set visible of every process whose name is "iCal" to false

    Notes:
    If the delay isn’t there, I think the script tries to relaunch iCal before it’s finished quitting, so it doesn’t do anything.
    I haven’t found any way to actually hide iCal on launch, the best I could find was hiding it immediately after it loads.

  6. mr_daemon says:

    It has never occured to me to check if the server was up before, I was more preoccupied with handling the occurence that it was down…

    Technically, I believe wget can be set to send an HTTP HEAD request and exit with 1 on failure. I could have used that. I could also have used the wget –timestamping option to decide weither or not to download the file, but I thought that since it was a “once per run” affair, md5 was safer. I don’t trust timestamps.

    And also, if the server is down, using this method, it will just exit with 1, and if you use the crappy AppleScript I cooked up, it should just tell you “This didn’t work. Do you want iCal anyways?”, which is all good.

    I’m not entirely convinced by restarting iCal automatically. What if I’m editing something and it restarts now? What if if there is a dialog box?

    If you have the LWP::Simple Perl module installed, this could also be used:

    perl -e 'use LWP::Simple;if(LWP::Simple::head("http://www.server.com/dav/file.ics")){exit 0;}else{exit 1;}'

    Ping is a bad idea in my case because my Cisco PIX would respond to ICMP regardless of if the web server behind was down or not.

    But wget timestamping is something I will look into when I get home.

    Kontact and Korganizer on the other hand are fine because I configured the source to automatically reload every 1 minute, and write changes only when they are commited….

  7. Preston says:

    Here is some Applescript code you might like to integrate that looks for published calendars.

    This script takes the approach of looking at all the calendars you publish, and will download changes (made by another) before opening up iCal. This way, you can use iCal’s publish on change feature.

    If two iCals are both publishing at the same time, they will stomp each other, but for the every few days edit, this doesn’t result in collisions. Anyway, you might be able to make use of the plist navigation code to avoid having the determine the full path to calendar source, and use the AS choose item dialog instead.


    property icalSupportFolder : (path to home folder as string) & "Library:Application Support:iCal:"
    property nodesPlist : icalSupportFolder & "nodes.plist"
    property sources : {}
    property passwds : {}

    on getValue(akey, keys, values)
    if keys does not contain akey then return false
    repeat with i from 1 to count of keys
    if item i of keys = akey then return item i of values
    end repeat
    return false
    end getValue

    on getPW(source)
    log “PW routine”
    return my getValue(source, sources, passwds)
    end getPW

    –iCal should be closed to refresh
    tell application “Finder”
    if name of every process contains “iCal” then
    tell application “iCal” to quit
    end if
    end tell

    tell application “System Events”
    –set nodesPlist to POSIX path of nodesPlist
    set calendarList to property list items of property list item “List” of contents of property list file nodesPlist
    set publishedCalendarList to {}
    –set publishedCalendarNames to {}
    repeat with i from 1 to count of calendarList
    set pubData to property list items of property list item “Publishers” of item i of calendarList
    if pubData ≠ {} then
    –set end of publishedCalendarList to i
    set end of publishedCalendarList to ((i as string) & ” : ” & value of property list item “PublishName” of item 1 of pubData)
    end if
    end repeat

    set chosenCalendars to choose from list publishedCalendarList with prompt "Choose calendars to refresh (hold CMD key for multiple)" with title "Published Calendars" with multiple selections allowed
    repeat with aChosenCalendar in chosenCalendars
        set calIndex to characters 1 thru 2 of aChosenCalendar as string as number
        set sourceItem to value of ¬
            property list item 1 of ¬
            property list item "Sources" of ¬
            property list item 1 of ¬
            property list item "Publishers" of (item calIndex of calendarList)
        set sourceURL to value of ¬
            property list item "UploadURL" of ¬
            property list item 1 of ¬
            property list item "Publishers" of (item calIndex of calendarList)
    
        set sourceFile to icalSupportFolder & "Sources:" & sourceItem & ".calendar:corestorage.ics" as alias
    
        (* --- URL Access scripting does not store pw in keychain
        tell application "URL Access Scripting"
            download sourceURL to sourceFile replacing yes with authentication
        end tell
        *)
    
        --using curl for download and storing pw
        if sourceURL contains "@" then --authentication steps
            set username to characters 8 thru ((offset of "@" in sourceURL) - 1) of sourceURL as string
    
            if sources contains sourceItem then
                set passwd to my getPW(sourceItem)
            else
                display dialog "Please enter your password" with title "Web Authentication" default answer "" buttons {"OK, Always", "OK, Once"} default button 1 with hidden answer
                set theResult to result
                set passwd to text returned of theResult
                if button returned of theResult = "OK, Always" then
                    set end of sources to sourceItem
                    set end of passwds to passwd
    
                end if
            end if
            set sourceFile to POSIX path of sourceFile
            set sourceFile to quoted form of sourceFile
            set userpass to username & ":" & passwd
            set sourceURL to "http://" & characters ((offset of "@" in sourceURL) + 1) thru -1 of sourceURL as string
            set shellcommand to "curl -u " & userpass & " " & sourceURL & " -o " & sourceFile
            do shell script shellcommand
        else
            --no auth needed
        end if
    
    end repeat
    tell application "iCal"
        reload calendars
        activate
    end tell
    

    end tell

  8. atom probe says:

    Subversion has a webdav plugin for apache. Keeping your icals in version control is a good way not to lose anything.

  9. You may want to look at Conditional Get HTTP requests; if the file on the server hasn’t changed then you don’t need to download it, which is more efficient than downloading it and md5ing it.

  10. donquichote says:

    Thanks for this script!

    I had to change one line. Instead of

    $WGET -c –http-user=$LOGIN –http-password=$PASSWORD $WEBDAV -O $NEWCAL

    I needed

    $WGET -c –http-user=$LOGIN –http-passwd=$PASSWORD $WEBDAV -O $NEWCAL

  11. Pingback: iCal Kalender abonnieren und editieren - Apfeltalk

  12. Pingback: daniel roehe» » online arbeiten

  13. Thanks to your script, we’re now able to share the calendar of our work group — great! :-) Here a few short improvements:

    Both in the shell script and in the do shell script instruction in the AppleScript, you can abbreviate your home directory using ~

    Instead of command; if [ $? -eq 0 ]; then …, you can write if command; then

    Furthermore, I do not understand why you use wget -c (continue download), as the script only tries to download the calendar file once.

  14. Maximus says:

    I would like to see a continuation of the topic

  15. Hi all,

    I know the thread is quite old but the solution seems to work for me. Almost that is.
    I made some changes because I have multiple calendars to sync and md5′s output differs from md5sum’s.

    When I change an event on one mac the online .ics file gets updated. I close iCal, call the script from the 2nd mac. The output:
    ----------------------------

    /Users/******/Library/Application Support/iCal/Sources/******-******-******-******-******.calendar
    Downloading calendar from WebDAV server…

    /opt/local/bin/wget -c –http-user=****** –http-password=****** http://cal.******./***.ics -O caldownload.ics
    –2009-03-27 11:21:28– http://cal.******./****.ics
    Auflösen des Hostnamen »cal.******.*«…. **.*..*
    Verbindungsaufbau zu cal.******.*|**.*..*|:80… verbunden.
    HTTP Anforderung gesendet, warte auf Antwort… 200 OK
    Länge: 142779 (139K) [text/calendar]
    In »cal
    download.ics« speichern.

    100%[===========================================================================>] 142.779 259K/s in 0,5s

    2009-03-27 11:21:28 (259 KB/s) – »cal_download.ics« gespeichert [142779/142779]

    ******.ics
    Checking for ICS format…
    END:VCALENDAR
    Is 1398d4586067870fb39db9cc1dd7be1c = 035d54e17dbb254fbc18a5f5f94d7bb5?
    Updating local ical…
    Done! Phew, glad this is over.

    The folder ~/Library/Application Support/iCal/Sources/******-******-******-******-******.calendar now contains an updated version of corestorage.ics and the backup corestorage.ics.old.
    When I open corestorage.ics in my favorite editor (TextMate) I can find the event change I made on the other system. When I launch iCal the event is not updated.

    In the folder ~/Library/Calendars I found more calendar data.

    Could it be that Apple changed the location of the main iCal data storage? Am I updating in the wrong place?

    Thanks for any hints on this one.

  16. Alexandre says:

    I noticed that these folders are no longer used in a long time. Actually, since 2007 in my case (leopard?). They are now stored in ~/Library/Calendars

  17. Alexandre says:

    And oh, besides that, every event is now an .ics on its own. Doesn’t work.

  18. Pingback: iCal Publishing?? - MacTalk Forums

Leave a Reply

Your email address will not be published. Required fields are marked *

*


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">