[Synckolab] Calendar sync nearly finished for Kolab XML format

Robert Schetterer robert at schetterer.org
Sat Oct 14 10:32:46 PDT 2006


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Andreas Gungl schrieb:
> Hello all,
> 
> find attached a patch against Synckolab 0.4.26 which makes the calendar sync 
> fully functional for me.
> Limitations exist regarding recurring events, yearly recurrence still 
> doesn't work. Everything else should work fine now.
> 
> Please feel free to test the changes and report problems back to me.
> @Nico, can you please prepare a new version with the changes included?
> 
> Best Regards,
> Andreas
> 
> --
> Diese Nachricht wurde auf Viren und andere gefährliche Inhalte untersucht
> und ist - aktuelle Virenscanner vorausgesetzt - sauber.
> 
> 
> 
> 
> ------------------------------------------------------------------------
> 
> diff -U 3 -b -d -r -N -- synckolab/calTools.js my-synckolab/calTools.js
> --- synckolab/calTools.js	2006-09-14 09:45:07.000000000 +0200
> +++ my-synckolab/calTools.js	2006-10-12 15:14:08.000000000 +0200
> @@ -32,6 +32,7 @@
>  /* ----- general functions to access calendar and events ----- */
>  
>  var activeCalendarManager;
> +var debugKolabXML = false;
>  
>  /**
>   * New and updated calendar functions (lightning 0.1)
> @@ -108,29 +109,9 @@
>      // to switch back to something faster if needed.
>      var aString = cnv_event2xml( event, true);
>      hashValue = hex_sha1(aString);
> +    if (debugKolabXML)
> +        consoleService.logStringMessage("Parsed event in XML:\n" + aString + "\nHash Value: " + hashValue);
>      return hashValue;
> -
> -    /* This is pretty annoying to go all over the attributes
> -       as we can simply use the existing event to xml conversion
> -       to build a unique string containing the values of all
> -       relevant attributes.
> -    var aString = event.id + ":" +
> -        event.startDate.icalString() + ":" +
> -        event.endDate.icalString() + ":" +
> -        event.title + ":" +
> -        event.getProperty("description") + ":" +
> -        event.getProperty("location") + ":" +
> -        event.getProperty("categories") + ":" +
> -        event.getProperty("class") + ":" +
> -        //event.getProperty("last-modified") +
> -        event.alarmOffset /*+ ":" +
> -        event.privateEvent + ":" +
> -        event.recurUnits + ":" +
> -        event.recurInterval + ":" +
> -        event.recurCount + ":" +
> -        event.recurEnd.getTime() + ":" +
> -        event.recurCount + ":" +
> -        event.recurForever*/
>  }
>  
>  
> @@ -223,8 +204,8 @@
>   */
>  function xml2Event (xml, event)
>  {
> -// XXX take me out ASAP
> -consoleService.logStringMessage("Parsing an XML event:\n" + xml);
> +    if (debugKolabXML)
> +        consoleService.logStringMessage("Parsing an XML event:\n====================\n" + xml);
>  	// TODO improve recurrence settings
>  	//      not working ATM:
>  	//          - yearly recurrence
> @@ -638,8 +619,8 @@
>  		} // end if
>  		cur = cur.nextSibling;
>  	} // end while
> -// XXX take me out ASAP
> -consoleService.logStringMessage("Parsed event in ICAL:\n" + event.icalString);
> +	if (debugKolabXML)
> +	   consoleService.logStringMessage("Parsed event in ICAL:\n=====================\n" + event.icalString);
>  	return true;
>  }
>  
> @@ -695,8 +676,11 @@
>          xml += " <sensitivity>public</sensitivity>\n";
>      if (event.getProperty("LOCATION"))
>          xml += " <location>" + event.getProperty("LOCATION") +"</location>\n";
> -    if (event.alarmOffset && event.alarmOffset != 0)
> -        xml += " <alarm>" + event.alarmOffset + "</alarm>\n";
> +    if (event.alarmOffset && event.alarmOffset.inSeconds != 0)
> +    {
> +        minutes = Math.floor(Math.abs(event.alarmOffset.inSeconds)/60);
> +        xml += " <alarm>" + minutes + "</alarm>\n";
> +    }
>      if (event.getProperty("CATEGORIES"))
>          xml += " <categories>" + event.getProperty("CATEGORIES") + "</categories>\n";
>  
> @@ -803,7 +787,7 @@
>          {
>              mail = attendee.id.replace(/MAILTO:/, '');
>              // FIXME the check for the array size == 1 is a hack to work around a Lightning bug
> -            if (attendee.isOrganizer || (attendees.length == 1))
> +            if (attendee.isOrganizer || (attendee.role == "CHAIR") || (attendees.length == 1))
>              {
>                  xml += " <organizer>\n";
>                  xml += "  <display-name>" + attendee.commonName + "</display-name>\n";
> @@ -811,7 +795,7 @@
>                  xml += " </organizer>\n";
>                  hasOrganizer = true;
>                  // FIXME indicator for workaround
> -                if (!attendee.isOrganizer) consoleService.logStringMessage("Organizer status expected!!!"); 
> +                if (!attendee.isOrganizer) consoleService.logStringMessage("Synckolab: Organizer status expected!!!"); 
>              }
>              else
>              {
> @@ -833,7 +817,7 @@
>                  }
>                  xml += " <attendee>\n";
>                  xml += "  <display-name>" + attendee.commonName + "</display-name>\n";
> -                xml += "  <smtp-address>" + attendee.id + "</smtp-address>\n";
> +                xml += "  <smtp-address>" + mail + "</smtp-address>\n";
>                  xml += "  <status>" + status + "</status>\n";
>                  xml += "  <request-response>" + (attendee.rsvp ? "true" : "false") + "</request-response>\n";
>                  switch (attendee.role)
> @@ -880,7 +864,7 @@
>      xml += " <revision>0</revision>\n";
>      xml += "</event>\n"
>  
> -// XXX take me out ASAP
> +    if (debugKolabXML)
>  	consoleService.logStringMessage("Event in ICAL:\n=============\n" + event.icalString + "\n" 
>  		+ "Created XML event structure:\n=============================\n" + xml);
>  	return xml;
> @@ -890,14 +874,14 @@
>  /**
>   * convert an ICAL event into a Kolab 2 XML format message
>   *
> + * event: an event instance
>   * @return a message in Kolab 2 format
>   */
>  function event2kolabXmlMsg (event)
>  {
> -    var xml = "";
> -    xml = event2xml(event);
> -    var my_msg = genMailHeader(event.id, "", "application/x-vnd.kolab.event", true)
> -	           + encodeQuoted(encode_utf8(xml))
> +    var xml = event2xml(event);
> +    var my_msg = generateMail(event.id, "", "application/x-vnd.kolab.event", 
> +                              true, encode_utf8(xml));
>  	return my_msg;
>  }
>  
> diff -U 3 -b -d -r -N -- synckolab/calendar.js my-synckolab/calendar.js
> --- synckolab/calendar.js	2006-09-14 09:45:07.000000000 +0200
> +++ my-synckolab/calendar.js	2006-10-12 14:29:12.000000000 +0200
> @@ -30,6 +30,8 @@
>   *
>   ***** END LICENSE BLOCK ***** */
>  
> +var debugCalendarSync = false;
> +
>  /*
>   * A kinda "provider" class for syncing the calendar. 
>   * The functions are called by the main synckolab loop to 
> @@ -94,7 +96,6 @@
>  	    	}
>  		}		
>  		
> -		
>      	this.folderMessageUids = new Array(); // the checked uids - for better sync
>      	
>      	// get the sync db
> @@ -106,6 +107,7 @@
>  		// get ALL the items from calendar - when done call nextfunc
>  		this.gEvents.nextFunc = nextFunc;
>  		this.gEvents.sync = sync;
> +		this.gEvents.events = new Array();
>  		// gCalendar might be invalid if no calendar is selected in the settings
>  		if (this.gCalendar) {
>  		  this.gCalendar.getItems(this.gCalendar.ITEM_FILTER_TYPE_EVENT, 0, null, null, this.gEvents);
> @@ -127,7 +129,7 @@
>  		events: new Array(),
>  		sync: '',
>  		onOperationComplete: function(aCalendar, aStatus, aOperator, aId, aDetail) {		
> -// FIXME			    consoleService.logStringMessage("operation: status="+aStatus + " Op=" + aOperator + " Detail=" + aDetail);
> +			    //consoleService.logStringMessage("operation: status="+aStatus + " Op=" + aOperator + " Detail=" + aDetail);
>  			},
>  		onGetResult: function(aCalendar, aStatus, aItemType, aDetail, aCount, aItems) {
>                  consoleService.logStringMessage("got results: " + aCount + " items");
> @@ -193,7 +195,8 @@
>  		if (foundEvent == null)
>  		{
>  		    // a new event
> -		    consoleService.logStringMessage("a new event:" + newEvent.id);
> +		    if (debugCalendarSync)
> +		      consoleService.logStringMessage("a new event, locally unknown:" + newEvent.id);
>  			var cEntry = getDbEntry (newEvent.id, this.db);
>  			if (cEntry == -1)
>  			{
> @@ -202,15 +205,15 @@
>  				curEntry.push(genCalSha1(newEvent));
>  				this.db.push(curEntry);
>  				this.curItemInListStatus.setAttribute("label", "add local");
> -				
> +				if (debugCalendarSync)
> +				    consoleService.logStringMessage("added locally:" + newEvent.id);
>  			}
>  			else
>  			{
> -				// normally now this should be deleted, since it was in the db already
> -				// but since calendar isnt finished yet we will comment this out
>  				this.curItemInListStatus.setAttribute("label", "server delete");
> +				if (debugCalendarSync)
> +				    consoleService.logStringMessage("server delete - DELETEME returned:" + newEvent.id);
>  				return "DELETEME";
> -				// return null;
>  			}
>  
>  			// add the new event
> @@ -219,24 +222,29 @@
>  		else
>  		{
>  		    // event exists in local calendar
> +		    if (debugCalendarSync)
> +		      consoleService.logStringMessage("event exists locally:" + newEvent.id);
>  			var cdb = getDbEntry (newEvent.id, this.db);
>  			var lastSyncEntry = cdb!=-1?this.db[cdb][1]:null;
>  			var newSyncEntry = genCalSha1 (newEvent);
>  			var curSyncEntry = genCalSha1 (foundEvent);
>  
> +            // where did changes happen?
>  			if (lastSyncEntry != null && lastSyncEntry != curSyncEntry && lastSyncEntry != newSyncEntry)
>  			{
>  			    // changed locally and on server side
> +			    if (debugCalendarSync)
> +			        consoleService.logStringMessage("changed both on server and client:" + newEvent.id);
>  				if (window.confirm("Changes were made on the server and local. Click ok to use the server version.\nClient Event: " + 
>  					foundEvent.title + "<"+ foundEvent.id + ">\nServer Event: " + newEvent.title + "<"+ newEvent.id + ">"))
>  				{
> +				    // take event from server
> +				    if (debugCalendarSync)
> +				        consoleService.logStringMessage("take event from server:" + newEvent.id);
>  					var newdb = new Array();
>  					newdb.push(newEvent.id);
>  					newdb.push(newSyncEntry);
> -					if (lastSyncEntry != null)
> -					{
> -						this.db[cdb][0] = ""; // mark for delete
> -					}
> +					if (lastSyncEntry != null) this.db[cdb][0] = ""; // mark for delete
>  					this.db.push(newdb);
>  	
>  					for (var i = 0; i < this.gEvents.events.length; i++)
> @@ -245,24 +253,21 @@
>  						{
>  							// modify the item
>  							this.gCalendar.modifyItem(newEvent, foundEvent, this.gEvents);
> -							this.gEvents.events[i] = newEvent;
> -							
>  							//update list item
>  							this.curItemInListStatus.setAttribute("label", "update local");
> -							
>  							return null;
>  						}
>  					}
>  				}
>  				else
>  				{
> +					// put local change to server
> +					if (debugCalendarSync)
> +					   consoleService.logStringMessage("put event on server:" + newEvent.id);
>  					var newdb = new Array();
>  					newdb.push(foundEvent.id);
>  					newdb.push(curSyncEntry);
> -					if (lastSyncEntry != null)
> -					{
> -						this.db[cdb][0] = ""; // mark for delete
> -					}
> +					if (lastSyncEntry != null) this.db[cdb][0] = ""; // mark for delete
>  					this.db.push(newdb);
>  	
>                      var msg = null;
> @@ -276,28 +281,27 @@
>                          msg += encodeQuoted(encode_utf8(foundEvent.getIcalString()));
>                          msg += "\n\n";
>  					}
> -
>  						// update list item
>  						this.curItemInListStatus.setAttribute("label", "update server");
> -					
>  					// remember this message for update
>  					return msg;
>  				}
>  			}
>  			else
> -
> +			{
> +			    if (debugCalendarSync)
> +			         consoleService.logStringMessage("changed only on one side (if at all):" + newEvent.id);
>  			// we got that already, see which is newer and update the message or the event
> +				// the sync database might be out-of-date, so we handle a non-existent entry as well
>  			if (lastSyncEntry == null || (lastSyncEntry == curSyncEntry && lastSyncEntry != newSyncEntry))
>  			{
> -			    consoleService.logStringMessage("server changed: " + foundEvent.id);
> -			    
> +					// event has been changed on the server
> +					if (debugCalendarSync)
> +					   consoleService.logStringMessage("event changed on server:" + newEvent.id);
>  				var newdb = new Array();
>  				newdb.push(newEvent.id);
>  				newdb.push(newSyncEntry);
> -				if (lastSyncEntry != null)
> -				{
> -					this.db[cdb][0] = ""; // mark for delete
> -				}
> +					if (lastSyncEntry != null) this.db[cdb][0] = ""; // mark for delete
>  				this.db.push(newdb);
>  
>  				for (var i = 0; i < this.gEvents.events.length; i++)
> @@ -306,19 +310,19 @@
>  					{
>  						// modify the item
>  						this.gCalendar.modifyItem(newEvent, foundEvent, this.gEvents);
> -						this.gEvents.events[i] = newEvent;
> -
>  						// update list item
>  						this.curItemInListStatus.setAttribute("label", "update local");
> -						 
>  						return null;
>  					}
>  				}
>  			}
>  			else
> +				{
>  			if (lastSyncEntry != curSyncEntry && lastSyncEntry == newSyncEntry)
>  			{
> -			    consoleService.logStringMessage("client changed: " + foundEvent.id);
> +						// event has been changed on the client
> +						if (debugCalendarSync)
> +						    consoleService.logStringMessage("event changed on client:" + newEvent.id);
>  				var newdb = new Array();
>  				newdb.push(foundEvent.id);
>  				newdb.push(curSyncEntry);
> @@ -353,9 +357,12 @@
>  				// remember this message for update
>  				return msg;
>  			}
> -
> +				}
> +				if (debugCalendarSync)
> +				    consoleService.logStringMessage("no change for event:" + newEvent.id);
>  			this.curItemInListStatus.setAttribute("label", "no change");
>  		}
> +		}
>  		return null;
>  	},
>  	
> @@ -384,16 +391,18 @@
>  		{
>  			var cur = this.gEvents.events[this.gCurEvent++];
>  			var msg = null;
> -			var writeCur = false;
> +			var writeCur = true;
>  		    
> -			writeCur = true;
> +            if (debugCalendarSync)
> +                consoleService.logStringMessage("nextUpdate for event:" + cur.id);
>  			// check if we have this uid in the messages
>  			var i;
>  			for (i = 0; i < this.folderMessageUids.length; i++)
>  			{
>  				if (cur.id == this.folderMessageUids[i])
>  				{
> -					consoleService.logStringMessage("we got this event: " + cur.id);
> +				    if (debugCalendarSync)
> +				        consoleService.logStringMessage("event is know from IMAP lookup:" + cur.id);
>  					writeCur = false;
>  					break;
>  				}
> @@ -404,10 +413,15 @@
>  			// it has been deleted on the server and we dont know about it yet
>  			if (writeCur)
>  			{
> +			    if (debugCalendarSync)
> +			        consoleService.logStringMessage("nextUpdate decided to write event:" + cur.id);
>  				var curSyncEntry = genCalSha1 (cur);
>  				var cdb = getDbEntry (cur.id, this.db);
>  				if (cdb != -1)
>  				{
> +				    // we have it in our database
> +				    if (debugCalendarSync)
> +				        consoleService.logStringMessage("nextUpdate found -1, better don't write event:" + cur.id);
>  					writeCur = false;
>  					this.db[cdb][0] = ""; // mark for delete
>  					this.gCalendar.deleteItem(cur, this.gEvents);
> @@ -428,9 +442,11 @@
>  					
>  					this.itemList.appendChild(this.curItemInList);
>  				}
> -				// ok its NOT in our internal db... add it
>  				else
>  				{
> +				    // ok its NOT in our internal db, add it
> +				    if (debugCalendarSync)
> +				        consoleService.logStringMessage("nextUpdate puts event into db (1):" + cur.id);
>  					var newdb = new Array();
>  					newdb.push(cur.id);
>  					newdb.push(curSyncEntry);
> @@ -458,6 +474,8 @@
>  		
>  			if (writeCur)
>  			{
> +			    if (debugCalendarSync)
> +			        consoleService.logStringMessage("nextUpdate really writes event:" + cur.id);
>  				// and write the message
>                  var msg = null;
>                  if (this.format == "Xml")
> @@ -479,12 +497,13 @@
>  				}
>  		    	consoleService.logStringMessage("New event:\n" + msg);
>  				// add the new event into the db
> +				if (debugCalendarSync)
> +				    consoleService.logStringMessage("nextUpdate puts event into db (2):" + cur.id);
>  				var curSyncEntry = genCalSha1 (cur);
>  				var newdb = new Array();
>  				newdb.push(cur.id);
>  				newdb.push(curSyncEntry);
>  				this.db.push(newdb);
> -
>  			}
>  		}	
>  		else
> diff -U 3 -b -d -r -N -- synckolab/tools.js my-synckolab/tools.js
> --- synckolab/tools.js	2006-09-14 09:45:07.000000000 +0200
> +++ my-synckolab/tools.js	2006-10-12 10:01:31.000000000 +0200
> @@ -855,6 +855,10 @@
>  }
>  
>  /**
> + * DEPRECATED: use generateMail() below, as this method is broken.
> + * Multi-part messages need the boundary also at its end, which 
> + * is impossible with this method!
> + *
>   * cid: the id of the card/event
>   * adsubject: optional additional subject (iCal or vCard)
>   * mime: the mime type (application/x-vnd.kolab.contact, application/x-vnd.kolab.event, application/x-vnd.kolab.task, application/x-vnd.kolab.journal, text/x-vcard, text/calendar)
> @@ -884,7 +888,7 @@
>  	
>  	msg += sdate;
>  	if (!part)
> -		msg += 'Content-Type: '+mime+';charset="utf-8"\n';
> +		msg += 'Content-Type: '+mime+';\n  charset="utf-8"\n';
>  	else
>  		msg += 'Content-Type: Multipart/Mixed;boundary="Boundary-00='+bound+'"\n';
>  	msg += 'Content-Transfer-Encoding: quoted-printable\n';
> @@ -894,19 +898,80 @@
>  	msg += "\n"
>  	if (part)
>  	{
> +		msg += '--Boundary-00='+bound+'\n';
> +		msg += 'Content-Type: Text/Plain;\n  charset="us-ascii"\n';
> +		msg += 'Content-Transfer-Encoding: 7bit\n\n';
> +		msg += 'This is a Kolab Groupware object.\n';
> +		msg += 'To view this object you will need an email client that can understand the Kolab Groupware format.\n';
> +		msg += 'For a list of such email clients please visit\n';
> +		msg += 'http://www.kolab.org/kolab2-clients.html\n';
> +		msg += '--Boundary-00='+bound+'\n'
> +		msg += 'Content-Type: '+mime+';\n  name="kolab.xml"\n';
> +		msg += 'Content-Transfer-Encoding: quoted-printable\n'
> +		msg += 'Content-Disposition: attachment;\n  filename="kolab.xml"\n\n';
> +	}
>  
> +	return msg;
> +}
> +
> +/**
> + * Create a message to be stored on the Kolab server
> + *
> + * cid: the id of the card/event
> + * adsubject: optional additional subject (iCal or vCard)
> + * mime: the mime type (application/x-vnd.kolab.contact, application/x-vnd.kolab.event, application/x-vnd.kolab.task, application/x-vnd.kolab.journal, text/x-vcard, text/calendar)
> + * part: true if this is a multipart message
> + * content: the content for the message
> + */
> +function generateMail (cid, adsubject, mime, part, content)
> +{
> +	var msg = "";
> +	var bound = get_randomVcardId();
> +	var cdate = new Date();
> +	var sTime = (cdate.getHours()<10?"0":"") + cdate.getHours() + ":" + (cdate.getMinutes()<10?"0":"") + cdate.getMinutes() + ":" +
> +		(cdate.getSeconds()<10?"0":"") + cdate.getSeconds();		
> +	var sdate = "Date: " + getDayString(cdate.getDay()) + ", " + cdate.getDate() + " " +
> +           getMonthString (cdate.getMonth()) + " " + cdate.getFullYear() + " " + sTime
> +          + " " + ((cdate.getTimezoneOffset() < 0)?"+":"-") +
> +          (Math.abs(cdate.getTimezoneOffset()/60)<10?"0":"") + Math.abs(cdate.getTimezoneOffset()/60) +"00\n"; 
> +
> +	msg += "From: synckolab at no.tld\n";
> +	msg += "Reply-To: \n";
> +	msg += "Bcc: \n";
> +	msg += "To: synckolab at no.tld\n";
> +	
> +	msg += "Subject: "; 
> +	if (!part)
> +		msg += adsubject+" ";
> +	msg += cid + "\n";
> +	
> +	msg += sdate;
> +	if (!part)
> +		msg += 'Content-Type: '+mime+';\n  charset="utf-8"\n';
> +	else
> +		msg += 'Content-Type: Multipart/Mixed;boundary="Boundary-00='+bound+'"\n';
> +	msg += 'Content-Transfer-Encoding: quoted-printable\n';
> +	msg += "User-Agent: SyncKolab\n";
> +	if (part)
> +		msg += "X-Kolab-Type: "+mime+"\n";
> +	msg += "\n"
> +	if (part)
> +	{
>  		msg += '--Boundary-00='+bound+'\n';
> -		msg += 'Content-Type: Text/Plain;charset="us-ascii"\n';
> +		msg += 'Content-Type: Text/Plain;\n  charset="us-ascii"\n';
>  		msg += 'Content-Transfer-Encoding: 7bit\n\n';
>  		msg += 'This is a Kolab Groupware object.\n';
>  		msg += 'To view this object you will need an email client that can understand the Kolab Groupware format.\n';
>  		msg += 'For a list of such email clients please visit\n';
>  		msg += 'http://www.kolab.org/kolab2-clients.html\n';
>  		msg += '--Boundary-00='+bound+'\n'
> -		msg += 'Content-Type: '+mime+';name="kolab.xml"\n';
> +		msg += 'Content-Type: '+mime+';\n  name="kolab.xml"\n';
>  		msg += 'Content-Transfer-Encoding: quoted-printable\n'
> -		msg += 'Content-Disposition: attachment;filename="kolab.xml"\n\n';
> +		msg += 'Content-Disposition: attachment;\n  filename="kolab.xml"\n\n';
>  	}
> +	msg += content + '\n';
> +	if (part)
> +		msg += '--Boundary-00='+bound+'\n';
>  	
>  	return msg;
>  }
> \ No newline at end of file
> 
> 
> ------------------------------------------------------------------------
> 
> _______________________________________________
> Synckolab mailing list
> Synckolab at mozdev.org
> http://mozdev.org/mailman/listinfo/synckolab
Hi,
will this patch included to the next release?
i am afraid to use patch on a windows system *g
- --
Mit freundlichen Gruessen
Best Regards
Robert Schetterer

robert_at_schetterer_dot_org
Munich / Bavaria / Germany
https://www.schetterer.org
https://www.schetterer.com/public-gpg-robert-schetterer.key
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.3 (MingW32)

iD8DBQFFMR8+NxddAhXBw7QRAv0iAJ4jk7ZtwPwFLabBoWQXes8sfXqzNQCdFgfn
s8Expma6HH0shBhqGYAIIRI=
=lTUB
-----END PGP SIGNATURE-----

--
Diese Nachricht wurde auf Viren und andere gefährliche Inhalte untersucht
und ist - aktuelle Virenscanner vorausgesetzt - sauber.



More information about the Synckolab mailing list