Under Six Weeks

I had intended doing the migration via SyncML but wasn't able to because the new SIM hasn't arrived yet. Instead I whipped up a small Perl script to get the data directly from the database and massaged that
into a text file that I imported into the Señora's Lotus Notes address book with a COL definition file, a bit like this one.

All that under six weeks. Twenty one minutes actually. ;-)

Silent Nexthaus Updates

Another silent update from Nexthaus: SyncJE 2.23 for BlackBerry. I've been using SyncJE for some time now, and I'm very pleased with it.

Even though they have an OTA installer, you have to install the Windows executable to find the change log. :-( I've done that for you:

Changes to Version 2.11
* Added option to set client timeout in settings
Version 2.20
* Added option to choose between WAP, MDS or BIS-B Internet connection method.
Version 2.21
* Fixed problem with local time and reminders
* Fixed wrong number of recurring events for week and month
* Added X-RIM-DCID field to contacts for PTT or DC support for iDen phones
Version 2.22
* Fixed registration problem
Version 2.23
* Fixed all day events problem with certain servers

If you look at their News page, the last update was in February of 2005! Why? What is the point? These guys make good software. Why don't they publicize it better? A few months ago, I even went to the trouble of writing them a message to this effect, but I was rewarded with silence.

SyncJE for BlackBerry now at 2.10

Nexthaus have silently updated their SyncJE for BlackBerry to version 2.10. The only news concerns the Windows based clients, so I have asked their support to detail the changes to the BlackBerry version. I'll update this if and when I get an answer.

The changes are:
Changes to Version 2.10
* Made adjustment to device info for problem with GooSync and time zone
* Will now automatically add ?/dr(-n,n) to Calendar folder name when date range filter is used.
* New Registration process
* Get Folders will run in a separate screen

Four-Way SyncML (Almost)

After solving the issues with the duplicates in my SyncML setup, I added another synchronization node to the picture ∗.

iSync

I've actually had that in there all the time, but I didn't want to complicate the issue by mentioning it. ;-)

The original setup hasn't really changed, but instead of altering contacts on Lotus Notes, I do so in AddressBook on the Mac.

Threeway Sync with Mac OS

It works, and I am now really happy. I've been hammering deletes, updates and inserts to all sides of this party, and I've yet to find a problem.

iSync log

∗ Until the next fubar happens with my setup. ;-)

Using iNotes' Person Documents

The advantage of relying on the "person" documents contained in the iNotes (Domino Web Access, DWA) mailfile instead of those in the personal addressbook on a Notes workstation, is that they travel with the mailfile, i.e. They replicate with mail and calendar documents.

I have stopped using the address book synchronization, as I recently explained. Instead, a small agent removes existing addresses in the PNAB and copies those from the mailfile.

Before continuing, you ought to create a backup of the personal address book (file, database, new copy)

Create a new Lotusscript agent as follows:

Create agent

And copy this script into it

Sub Initialize
        ' Reload PNAB from mailfile people, destroying target documents
        Dim s As New NotesSession
        Dim maildb As NotesDatabase
        Dim ws As New NotesUIWorkspace
        Dim askme As Variant
       
        Dim doc As NotesDocument
        Dim user As String
        Dim msg As String
        Dim ndocs As Integer
        Dim n As Integer
       
        user = s.UserName
        Set maildb = s.CurrentDatabase
       
        Dim CalendarProfile As NotesDocument
        Set CalendarProfile = maildb.GetProfileDocument("CalendarProfile")
        If user <> CalendarProfile.owner(0) Then
                Messagebox "This is not your mailfile. Will not do restore!", 16, "PNAB"
                Exit Sub
        End If
       
        Set maildb = s.CurrentDatabase
       
        Dim pnab As New NotesDatabase( "", "names.nsf" )
        If Not pnab.IsOpen Then
                Messagebox( "Can't open PNAB (names.nsf): Stop" )
                Exit Sub
        End If
       
        Dim collection As NotesDocumentCollection
        Dim selection As String
        Dim formula As String
       
        ' Find and remove all Person & Group documents in current names.nsf
       
        formula = "Form = ""Person"" | Form = ""Group"" "
        Set collection = pnab.Search( formula, Nothing, 0 )
        If collection.Count > 0 Then
                msg = "The current Personal Address Book has " & _
                collection.Count & " documents which need to be deleted. Is it OK to delete these?"
                askme = ws.Prompt(PROMPT_YESNO, "PNAB: Delete old documents?", msg)
                If askme = 0 Then
                        Messagebox "Nothing deleted. Nothing restored. Exit", 0, "PNAB"
                        Exit Sub
                End If
               
                For n = 1 To collection.Count
                        Set doc = collection.GetNthDocument(n)
                        Call doc.Remove(True)
                Next
        End If
       
        ' Find all Person and Group documents in the Mailfile.
        ' copy these documents from mailfile to PNAB
       
        formula = "Form = ""Person"" | Form = ""Group"" "
       
        Set collection = maildb.Search( formula, Nothing, 0 )
       
        ndocs = 0
        For n = 1 To collection.Count
                Set doc = collection.GetNthDocument(n)
                doc.FUPPSdn = user
               
                ' doc.NameDisplaypref = "1"   ' Firstname Lastname
                doc.NameDisplayPref = "2"   ' Lastname, Firstname
               
                ' Contacts which are created by a Web browser have no
                ' Type in the document. I'm going to set it here...
                ' The same occurs in Groups, but I cannot set them here because
                ' WebGroups are created with email address only and NotesGroups
                ' with the FullName of the Contact
                If doc.Form(0) = "Person" And doc.Type(0) = "" Then
                        doc.Type = "Person"
                End If
                If doc.Form(0) = "Group"  Then
                        If Not doc.HasItem("Type")  Then
                                Dim f1 As NotesItem
                                Set f1 = doc.AppendItemValue( "Type" , "Group")
                        End If
                        If Not doc.HasItem("GroupType") Then
                                Dim f2 As NotesItem
                                Set f2 = doc.AppendItemValue( "GroupType" , "1")
                        End If
                End If
                Call doc.Save(True,True)
                Call doc.CopyToDatabase(pnab)
                ndocs = ndocs + 1
        Next
        msg = ndocs & " restored to personal address book from mailfile"
        Messagebox msg, 0, "PNAB"
End Sub
 

Download this code: lotus/pnab-reload.lss

Depending on whether you prefer names sorted by first or last names, adjust the line which sets the preference. I want names sorted by surname, which is how it is set. If you prefer sorting by first or given name, set NameDisplayPref to the value 1.

Notice that I have removed the Synchronize Address Book agent from my mail file in so as to not use it by mistake.

Actions menu

Choose Recreate PNAB People from the Actions menu to launch the agent. If you already have people in your personal address book on the workstation (names.nsf), the agent will request permission to delete those.

Start the agent

If you answer with No, the agent terminates and doesn't otherwise alter anything. Answering yes to the question lets the agent remove all person (not connection or other documents) from your names.nsf, and it then copies all person and group documents from your mail file into your personal address book, indicating how many where thus created

Agent result

This function can be used on each of your workstations without endangering the addresses in the person documents of the mail file. Do note that any changes to the names.nsf will of course be clobberred at the next run.

If you want read/write access to the address book in your mailfile, use iNotes to access it.

Debugging Dreaded Distributed Duplicates

In the course of the last two weeks I've had trouble with my synchronization setup, but I think I've found the cause of the problem!

I quickly purchased the Nexthaus SyncJE client program for the BlackBerry because I considered that to be the best way to get my BlackBerry device synchronized with my syncml server, which it was is.

Thusly, I had a three way sync going: the Nokia N70 with the SyncML server, the BlackBerry with it too, and by virtue of the BlackBerry being connected to a BlackBery Enterprise Server with a Lotus Domino backend, the device's address book was being synchronized with my Lotus Domino mail file. The addresses being in the Lotus mail file, as soon as they were replicated via NRPC I could simply synchronize those with my Lotus Notes address book.

Three-Way SyncML

What else could I want? That was the perfect setup.

Hah!

After a bit of there and back, dozens of duplicate contacts suddenly appeared on the two devices, and I couldn't for the life of me determine what it was.

I then started investigating the SyncML server's log files, and at first glance noticed, that whenever the Nexthaus client performed a sync, the log was full of unknown property: UID messages. Being the clever chappie I am, I determined that must be the reason why Nexthaus' SyncJE couldn't associate its vCards with the server's database. That seemed quite logical to me.

Things got even worse, when I set up SSL on the server. I messed about with the server's URL on the mobile phone, and got not only duplicates, but bunches of them; eighty or so each time I synced.

I stopped using SyncJE for BlackBerry and set about cleaning up all my duplicate contacts, resetting the phone, etc. etc. etc.; you get the picture.

I've since logged an email call to both the support at Synthesis and at Nexthaus, and both parties answered astonishingly quickly (both within a couple of hours), for which they have my appreciation. It turns out, that actually neither was doing anything wrong, at least according to the two companies.

Yeah, right. So? What is the problem?

I spent half a day on the weekend synchronizing the shit out of the two devices, keeping logs and making notes of all changes. Have a look at a the initial list of the directories in which I'm keeping the stuff. ;-)

01-n70-okay-slowsync-to-bb/
02-resync-from-bb/
03-bb-added-1-updated-pam-telephone/
04-sync-n70/
05-bb-change-pam/
06-both/
07-apple-sync-n70/
08-n70-change-to-HTTPS/
08-n70-change-to-HTTPS.1/
09-fromBB/
09-fromBB.1/
10-change-url-on-bb/
11-remove-dup-hanna-on-n70/
...

Believe me, there is smoke coming out of my ears, after keeping notes on all of this.

real "notes"

It turns out, both Synthesis and Nexthaus are right: there isn't a problem. Everything works as it is supposed to, with the exception of a single duplicate calendar entry, that was created along the way. It happened during the slow sync in 08-n70-change-to-HTTPS; as far as I can tell due to an alarm setting on the phone (ALARM_MSG = z:systemSystemSoundsalarm.wav). I deleted the duplicate and thats it.

So?

The problem is here. For some reason which I don't in the slightest feel like debugging, the Lotus Notes synchronize address book action is updating documents in the (iNotes-)mail file even if I haven't changed anything in the local address book (PAB). When they are sent back to the mail file and finally find their way back to the BlackBerry device, there is something in them that causes the Nexthaus SyncJE client to create duplicates of the contacts. A cursory glance at the code indicates that the problem might be in the iN_CopyDocToDB function in the Notes design template. It would appear that documents are not simply being modified (i.e.: the name has changed, we change the name) but actually deleted and re-created, which might of course influence SyncJE into believing it is a new document. This is simply a suspicion, as I don't know exactly how SyncJE records "seen" entries.

As I say, I currently cannot be bothered to find the exact cause of the problem because I can live with the situation. I won't use this functionality any longer. I will limit myself to using the Person documents which are in the mail-file proper, only copying them to the personal address book (copy, paste) when necessary.

Now my iCal feed from SyncML database is reliable.

Utilizing SyncML Data to Feed iCal

After installing Synthesis SyncML Server and using it for a bit, I decided to look into its innards to see if I could use some of the data. You may recall I'm using this server in a three-way combination, and this adds another piece to the mosaic. I don't use Microsoft Outlook but Lotus Notes. My calendar entries and tasks are replicated onto my BlackBerry, and from there, they are synchronized via SyncML to my server. It is from here, that I wanted to attempt to utilize the calendar and task entries in order to feed other programs, notably Apple's iCal.

I am using the "PRO" version of Synthesis AG's product which stores all data received via SyncML into a backend database via ODBC which, in my case, lands in a MySQL datbase on the server. One of the great advantages of this setup is that I have access to my data.

It occurred to me to have a closer look at the tasks and events tables, which are respectively named SYNC_TASKS and SYNC_EVENTS (it is a bit unfortunate that in the default configuration, the table names are capitalized; I usually reserve caps for reserved words in SQL statements, but I'm forced to capitalize the table names here, or the examples will not work). Although I describe tasks in a bit more details, the schema for the calendar (events) and for contacts is also easy to determine.

Let me first have a look at a task I created on my BlackBerry, and which I then synchronized to the server with the Nexthaus SyncJE for BlackBerry SyncML client. I have filled out all of the fields available on the BlackBerry.

One task

When pushing this task down to the SyncML server, it receives the task in iCalendar format (RFC 2445).

BEGIN:VCALENDAR
VERSION:1.0
PRODID:Blackberry
BEGIN:VTODO
DESCRIPTION:Test iCal integration
DUE;VALUE=DATE-TIME:20070121T160000Z
PRIORITY:5
UID:1806671339
SUMMARY:Todos in Synthesis SyncML
STATUS:NEEDS ACTION
END:VTODO
END:VCALENDAR

I note that not all of the fields I entered on the BlackBerry are contained in the vCard; the categories are missing, and possibly more important, so is the time zone of the entry. Nexthaus have documented this, and it is probably due to my version 4.0 of BlackBerry OS. Their readme clearly says:

There are some limitations in syncing certain fields depending on BlackBerry OS Version. Limitations prior to BlackBerry OS version 4.1: Contacts, will only sync one address either home or work; Calendar, All-day events flag will not be set; Tasks, Time zone setting in task is ignored, the timezone set in device settings will be used instead.

and their documentation also lists further limitations:


There are some fields in the Blackberry PIM that can either not be accessed or is outside the scope of the standard formats used. Here is a list of some fields that are not supported in the vCard, vCal standard or not accessible for sync.

Contacts (vCard): Birthday, PIN, Categories, Web Page and User fields 1-4 in Contacts are not supported.

Calendar (vCal): Recurring events: Monthly by Position and Yearly by Date not supported. Sensitivity (Private/Public) not supported.

Tasks (vCal): Categories not supported.

Upon receiving the vCard, the SyncML server converts that card into SQL statements and inserts or updates the task in the database. For the event we are looking at, the resulting row in the database is

mysql> SELECT * FROM SYNC_TASKS WHERE summary LIKE '%Synthe%';
*************************** 1. row ***************************
   TASKS_KEY: 60
   FOLDERKEY: 6
     SYNCVIS: 10
  CATEGORIES:
       CLASS:
    MODIFIED: 2007-01-16 23:36:49
     CREATED: NULL
     SUMMARY: Todos in Synthesis SyncML
 DESCRIPTION: Test iCal integration
    LOCATION:
    TIMEZONE: NULL
         DUE: 2007-01-21 17:00:00
    PRIORITY:
      STATUS: NEEDS ACTION
    ATTENDEE:
  ALARM_TIME: NULL
ALARM_SNOOZE:
ALARM_REPEAT:
   ALARM_MSG:
1 row in set (0.00 sec)

What can I do with this information? Well I can further process it. In fact I'm going to create iCalendar files on the fly for import into Apple's iCal or Mozilla Sunbird. Here is a couple of screen shots of the result.

iCal from SyncML data

with Mozilla Sunbird

I was originally inspired by the article Using PHP to Make Basic vCalendar/iCalendar Events; it seemed so easy, that I took up the task of creating something similar. The program itself is quite simple and supports both the calendar with recurring events as well as tasks or todos. I haven't tested all possible combinations of recurring events, but it works for me. You are bound to have to change the timezone data.

<?php
        # fupps-sycal.php (C)2007 by Jan-Piet Mens (jpmens at gmail.com)
        # This program produces iCal subscriptions to the calendar
        # and tasks databases on a Synthesis SyncML Server installation.
        #
        # Use at your own risk. Timezone tested only at my location;
        # Your Mileage Will Vary!
        #
        # Documentation is at
        # http://blog.fupps.com/2007/01/18/utilizing-syncml-data-to-feed-ical/
        #
        # Bugs:
        #       1. Only one folder per user. Synthesis SyncML server
        #          supports multiple folders per user, but I don't
        #       2. Error-handling needs improvement!
        #       3. Timezone handling is a mess if it works at all.
        #

        # $Header: /home/jpm/src/syncml/fupps-sycal/RCS/fupps-sycal.php,v 1.5 2007/02/05 12:22:29 jpm Exp jpm $
        #
        # $Id: fupps-sycal.php,v 1.5 2007/02/05 12:22:29 jpm Exp jpm $
        #
        # $Log: fupps-sycal.php,v $
        # Revision 1.5  2007/02/05 12:22:29  jpm
        # Completed tasks get completion date = modification date if due date not set.
        #
        # Revision 1.4  2007/02/05 12:03:13  jpm
        # Small fix for events spanning more than one day: show date only
        #
        # Revision 1.3  2007/01/18 17:17:47  jpm
        # Added UTF-8 output to silence Mozilla Sunbird 0.3
        # Small cleanups.
        #
        # Revision 1.2  2007/01/14 18:42:34  jpm
        # *** empty log message ***
        #
        # Revision 1.1  2007/01/14 11:18:24  jpm
        # Initial revision
        #
        #


        $dbhost = 'localhost';
        $dbdatabase = 'syncml';
        $dbuser = 'somebody';
        $dbpass = 'totallysecret';

        mysql_connect($dbhost, $dbuser, $dbpass);
        mysql_select_db($dbdatabase);

        $username = (isset($_SERVER['PHP_AUTH_USER'])) ? $_SERVER['PHP_AUTH_USER'] : "";
        $password = (isset($_SERVER['PHP_AUTH_PW'])) ? $_SERVER['PHP_AUTH_PW'] : "";

        # Sanitize input
        $username = preg_replace('/[\\W]/', '//', $username);
        $password = preg_replace('/[\\W]/', '//', $password);

        if (strlen($username) < 1 || strlen($password) < 1) {
                header('WWW-Authenticate: Basic realm="My Realm"');
                header('HTTP/1.0 401 Unauthorized');
                echo 'Unauthorized';
                exit;
        } else {
                $sql = "SELECT COUNT(*) AS nr FROM SYNC_USERS WHERE userid = '$username' AND passwd = '$password'";

                $res = mysql_query($sql);
                $data = mysql_fetch_object($res);

                if ($data->nr != 1) {
                        header('WWW-Authenticate: Basic realm="My Realm"');
                        header('HTTP/1.0 401 Unauthorized');
                        echo 'Forget it! Unauthorized';
                        exit;
                }

                $sql = "SELECT folder_key
                        FROM SYNC_USERS u,
                             SYNC_FOLDERS f,
                             SYNC_PERM p
                        WHERE u.user_key = p.userkey
                        AND   p.folderkey = f.folder_key
                        AND   u.userid = '$username'"
;

                $res = mysql_query($sql);
                $data = mysql_fetch_object($res);

                $folderkey = $data->folder_key;
        }

        $filename = "$username.ics";
        header("Content-Type: text/x-vCalendar; charset=UTF-8");
        header("Content-Disposition: inline; filename=$filename");

        print <<<ENDHEAD
BEGIN:VCALENDAR
VERSION:2.0
METHOD:PUBLISH
X-WR-CALNAME;VALUE=TEXT:$username Calendar
X-WR-TIMEZONE;VALUE=TEXT:Europe/Berlin
PRODID:-//Jan-Piet Mens//fupps-sycal 1.0//EN

ENDHEAD;

        timezone();

        ###################################################
        # Events

        $sql = "SELECT events_key as id,
                DATE_FORMAT(dtstart, '%Y%m%dT%H%i%s') AS dtstart,
                DATE_FORMAT(dtend, '%Y%m%dT%H%i%s') AS dtend,
                summary,
                description,
                location,
                rr_freq, rr_interval, DATE_FORMAT(rr_end, '%Y%m%dT%H%i%s') AS rr_end
                FROM SYNC_EVENTS
                WHERE folderkey = $folderkey "
;

        $res = mysql_query($sql);

        $tz = ';TZID=Europe/Berlin';
        $year_now = date('Y', time());

        while ($data = mysql_fetch_object($res)) {
                $id          = 'Fupps-SyCal' . $data->id;
                $dtstart        = $data->dtstart;
                $dtend    = $data->dtend;
                $subj      = preg_replace('/\\s+/', ' ', $data->summary);
                $description    = $data->description;
                $description    = str_replace("\\r", '\\n', $description);
                $description    = str_replace("\\n", '', $description);
                $location       = $data->location;
                $rr_freq        = $data->rr_freq;
                $rr_interval    = $data->rr_interval;
                $rr_end  = $data->rr_end;
               
                $subj = utf8_encode($subj);
                $description = utf8_encode($description);
                $location = utf8_encode($location);

                print "BEGIN:VEVENT\\n";
                print "SUMMARY:$subj\\n";
                if (strlen($description)) {
                        print "DESCRIPTION:$description\\n";
                }
                if (strlen($location)) {
                        print "LOCATION:$location\\n";
                }
               
                if (strcmp(substr($dtstart, 9, 4), "0000") == 0) {
                        # event spans multiple days
                        print "DTSTART;VALUE=DATE:" . substr($dtstart, 0, 8) . "\\n";
                        if ($dtend) {
                                print "DTEND;VALUE=DATE:" . substr($dtend, 0, 8) . "\\n";
                        } else {
                                print "DTEND;VALUE=DATE:" . substr($dtstart, 0, 8) . "\\n";
                        }
                } else {
                        print "DTSTART:$dtstart\\n";
                        if ($dtend) {
                                print "DTEND:$dtend\\n";
                        } else {
                                print "DTEND:$dtstart\\n";
                        }
                }

                if ($rr_freq) {
                        $freq = 'UNKNOWN';
                        switch ($rr_freq) {
                                case 'YM': $freq = 'YEARLY'; break;
                                case 'WW': $freq = 'WEEKLY'; break;
                                case 'D'$freq = 'DAILY'; break;
                        }
                        print "RRULE:FREQ=${freq};INTERVAL=${rr_interval};UNTIL=${rr_end}\\n";
                }
                print "UID:$id\\n";
                print "END:VEVENT\\n";
        }

        ###################################################
        # Tasks (Todos)

        $sql = "SELECT tasks_key as id,
                DATE_FORMAT(due, '%Y%m%dT%H%i%s') AS due,
                DATE_FORMAT(modified, '%Y%m%dT%H%i%s') as modif,
                summary,
                description,
                status,
                priority
                FROM SYNC_TASKS WHERE folderkey = $folderkey"
;

        $res = mysql_query($sql);

        $seq = 0;
        while ($data = mysql_fetch_object($res)) {
                $id          = 'Fupps-SyCal-Todo-' . $data->id;
                $due        = $data->due;
                $priority       = $data->priority;
                $modif    = $data->modif;
                $subj      = preg_replace('/\\s+/', ' ', $data->summary);
                $description    = $data->description;
                $description    = str_replace("\\r", '\\n', $description);
                $description    = str_replace("\\n", '', $description);
                $status  = $data->status;
               
                $priority = ($priority == 'HIGH') ? 1 : 9;

                $seq++;

                $subj = utf8_encode($subj);
                $description = utf8_encode($description);

                print "BEGIN:VTODO\\n";
                print "DTSTART;TZID=Europe/Berlin:20070116T000000\\n";
                print "SUMMARY:$subj\\n";
                print "DESCRIPTION:$description\\n";
                print "UID:FPS-TODO-$id\\n";
                print "SEQUENCE:$seq\\n";
                print "DTSTAMP:20070116T214010Z\\n";
                print "PRIORITY:$priority\\n";
                if ($due) {
                        print "DUE:$due\\n";
                }
                switch ($status) {
                        case 'COMPLETED';
                                $due = (strlen($due) < 8) ? $modif : $due;
                                print "STATUS:COMPLETED\\n";
                                print "COMPLETED:$due\\n";       # fixme
                                break;
                }
                print "END:VTODO\\n";


}
print "END:VCALENDAR\\n";
exit;

function timezone() {
        print <<<TZEND
BEGIN:VTIMEZONE
TZID:Europe/Berlin
LAST-MODIFIED:20050910T165907Z
BEGIN:DAYLIGHT
DTSTART:20050327T010000
TZOFFSETTO:+0200
TZOFFSETFROM:+0000
TZNAME:CEST
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20051030T030000
TZOFFSETTO:+0100
TZOFFSETFROM:+0200
TZNAME:CET
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:20060326T030000
TZOFFSETTO:+0200
TZOFFSETFROM:+0100
TZNAME:CEST
END:DAYLIGHT
END:VTIMEZONE

TZEND;
}

?>
 

Download this code: syncml/cal/fupps-sycal.php

How do I use this program? I have it running under an Apache web server in its own directory. Since iCal complains when the subscription file is not called .ics, the program itself is named thus, but I have to tell Apache how to handle such files.

$ cat .htaccess
AddType application/x-httpd-php .ics

Order Deny,Allow
Deny from all
Allow from 127.0.0.1
Allow from 192.168

This forces Apache to use PHP when processing files ending in .ics. You might like to, as I have, protect the resource from prying eyes with Apache's Access Control directives.

The program itself uses Synthesis' SYNC_USER table to perform authentication, although that only works if I use the clear-text password in the database table (the default configuration file for Synthesis SyncML server defines true).

In iCal, use the Calendar menu to Subscribe to a new calendar and enter the URL to your installation in the Subscribe to field. After clicking on Subscribe, iCal will attempt to retrieve the calendar from our PHP program, but it will be requested to authenticate. Use your SyncML username and password (the same values to use in your mobile phone) to access your calendar. At this point you can chose a new name for the subscription and request that iCal refreshes your calendar periodically.

Note that calendars subscribed to thusly are read-only.

This might or might no work with Microsoft Outlook; I haven't tested it. If you do succeed, drop me a note.

Quote Of The Day

From the SyncML Discussion Forum:


Hai All
Anyone Know How To Install Syncml Client
If No Then
Tell Me How To Develop

Not bad; if he can't install it, he'll develop a new one. ;-)