Folks,

        Attached is a patch that provides reasonable (but not yet fully tested)
2-way syncing of recurrent events in the opensync-0.4x plugin. I based
this on the recurrence handling stuff I wrote for the SynCE project
although it is not a direct port.

It also fixes up a problem with some addresses not syncing and a
case-issue in Evo.

You can apply the patch to a checked-out git tree with

cat barry-recur.patch | patch -p1

If it is in the wrong format, let me know and I will resend.

Have fun.

        John.


diff --git a/opensync-plugin-0.4x/src/vcard.cc b/opensync-plugin-0.4x/src/vcard.cc
index ec34aa4..d5f6dcc 100644
--- a/opensync-plugin-0.4x/src/vcard.cc
+++ b/opensync-plugin-0.4x/src/vcard.cc
@@ -189,21 +189,21 @@ const std::string& vCard::ToVCard(const Barry::Contact &con)
 	}
 
 	if( con.WorkAddress.HasData() )
-		AddAddress("work", con.WorkAddress);
+		AddAddress("WORK", con.WorkAddress);
 	if( con.HomeAddress.HasData() )
-		AddAddress("home", con.HomeAddress);
+		AddAddress("HOME", con.HomeAddress);
 
 	// add all applicable phone numbers... there can be multiple
 	// TEL fields, even with the same TYPE value... therefore, the
 	// second TEL field with a TYPE=work, will be stored in WorkPhone2
-	AddPhoneCond("pref", con.Phone);
-	AddPhoneCond("fax", con.Fax);
-	AddPhoneCond("work", con.WorkPhone);
-	AddPhoneCond("work", con.WorkPhone2);
-	AddPhoneCond("home", con.HomePhone);
-	AddPhoneCond("home", con.HomePhone2);
-	AddPhoneCond("cell", con.MobilePhone);
-	AddPhoneCond("pager", con.Pager);
+	AddPhoneCond("PREF", con.Phone);
+	AddPhoneCond("FAX", con.Fax);
+	AddPhoneCond("WORK", con.WorkPhone);
+	AddPhoneCond("WORK", con.WorkPhone2);
+	AddPhoneCond("HOME", con.HomePhone);
+	AddPhoneCond("HOME", con.HomePhone2);
+	AddPhoneCond("CELL", con.MobilePhone);
+	AddPhoneCond("PAGER", con.Pager);
 	AddPhoneCond(con.OtherPhone);
 
 	// add all email addresses, marking first one as "pref"
@@ -213,10 +213,10 @@ const std::string& vCard::ToVCard(const Barry::Contact &con)
 		if( e.size() ) {
 			vAttrPtr email = NewAttr("EMAIL", e.c_str());
 			if( i == 0 ) {
-				AddParam(email, "TYPE", "internet,pref");
+				AddParam(email, "TYPE", "INTERNET,PREF");
 			}
 			else {
-				AddParam(email, "TYPE", "internet");
+				AddParam(email, "TYPE", "INTERNET");
 			}
 			AddAttr(email);
 		}
diff --git a/opensync-plugin-0.4x/src/vevent.cc b/opensync-plugin-0.4x/src/vevent.cc
index fffccc8..ae866db 100644
--- a/opensync-plugin-0.4x/src/vevent.cc
+++ b/opensync-plugin-0.4x/src/vevent.cc
@@ -30,6 +30,7 @@
 #include <glib.h>
 #include <strings.h>
 #include <sstream>
+#include <vector>
 
 
 //////////////////////////////////////////////////////////////////////////////
@@ -58,6 +59,54 @@ unsigned short vCalendar::GetWeekDayIndex(const char *dayname)
 	return 0;
 }
 
+std::vector<std::string> vCalendar::GetDOWList(const std::string& str)
+{
+	std::vector<std::string> tokens;
+	std::string::size_type delimPos = 0, tokenPos = 0, pos = 0;
+
+	if(str.length()<1) {
+		return tokens;
+	}
+	
+	while(1) {
+		delimPos = str.find_first_of(',', pos);
+		tokenPos = str.find_first_not_of(',', pos);
+
+		if(std::string::npos != delimPos) {
+			if(std::string::npos != tokenPos) {
+				if(tokenPos<delimPos) {
+					tokens.push_back(str.substr(pos,delimPos-pos));
+				} else {
+					tokens.push_back("");
+				}
+			} else {
+				tokens.push_back("");
+			}
+			pos = delimPos+1;
+		} else {
+			if(std::string::npos != tokenPos){
+				tokens.push_back(str.substr(pos));
+			} else {
+				tokens.push_back("");
+			}
+			break;
+		}
+	}
+	return tokens;
+}
+
+
+unsigned short vCalendar::GetMonthWeekNumFromBYDAY(const std::string& ByDay)
+{
+	return atoi(ByDay.substr(0,ByDay.length()-2).c_str());
+}
+
+unsigned short vCalendar::GetWeekDayIndexFromBYDAY(const std::string& ByDay)
+{
+	return GetWeekDayIndex(ByDay.substr(ByDay.length()-2).c_str());
+}
+
+
 bool vCalendar::HasMultipleVEvents() const
 {
 	int count = 0;
@@ -88,64 +137,68 @@ void vCalendar::RecurToVCal()
 	switch( cal.RecurringType )
 	{
 	case Calendar::Day:		// eg. every day
-		AddParam(attr, "FREQ", "DAILY");
+		//AddParam(attr, "FREQ", "DAILY");
+		AddValue(attr,"FREQ=DAILY");
 		break;
 
 	case Calendar::MonthByDate:	// eg. every month on the 12th
 					// see: DayOfMonth
-		AddParam(attr, "FREQ", "MONTHLY");
+		//AddParam(attr, "FREQ", "MONTHLY");
+		AddValue(attr,"FREQ=MONTHLY");
 		{
 			ostringstream oss;
-			oss << cal.DayOfMonth;
-			AddParam(attr, "BYMONTHDAY", oss.str().c_str());
+			oss << "BYMONTHDAY=" << cal.DayOfMonth;
+			AddValue(attr, oss.str().c_str());
 		}
 		break;
 
 	case Calendar::MonthByDay:	// eg. every month on 3rd Wed
 					// see: DayOfWeek and WeekOfMonth
-		AddParam(attr, "FREQ", "MONTHLY");
+		AddValue(attr, "FREQ=MONTHLY");
 		if( cal.DayOfWeek <= 6 ) {	// DayOfWeek is unsigned
 			ostringstream oss;
-			oss << cal.WeekOfMonth << WeekDays[cal.DayOfWeek];
-			AddParam(attr, "BYDAY", oss.str().c_str());
+			oss << "BYDAY=" << cal.WeekOfMonth << WeekDays[cal.DayOfWeek];
+			AddValue(attr, oss.str().c_str());
 		}
 		break;
 
 	case Calendar::YearByDate:	// eg. every year on March 5
 					// see: DayOfMonth and MonthOfYear
-		AddParam(attr, "FREQ", "YEARLY");
+		AddValue(attr, "FREQ=YEARLY");
 		{
 			ostringstream oss;
-			oss << cal.MonthOfYear;
-			AddParam(attr, "BYMONTH", oss.str().c_str());
+			oss << "BYMONTH=" << cal.MonthOfYear;
+			AddValue(attr, oss.str().c_str());
+
 		}
 		{
 			ostringstream oss;
-			oss << cal.DayOfMonth;
-			AddParam(attr, "BYMONTHDAY", oss.str().c_str());
+			oss << "BYMONTHDAY=" << cal.DayOfMonth;
+			AddValue(attr, oss.str().c_str());
 		}
 		break;
 
 	case Calendar::YearByDay:	// eg. every year on 3rd Wed of Jan
 					// see: DayOfWeek, WeekOfMonth, and
 					//      MonthOfYear
-		AddParam(attr, "FREQ", "YEARLY");
+		AddValue(attr, "FREQ=YEARLY");
 		if( cal.DayOfWeek <= 6 ) {	// DayOfWeek is unsigned
 			ostringstream oss;
-			oss << cal.WeekOfMonth << WeekDays[cal.DayOfWeek];
-			AddParam(attr, "BYDAY", oss.str().c_str());
+			oss << "BYDAY=" << cal.WeekOfMonth << WeekDays[cal.DayOfWeek];
+			AddValue(attr, oss.str().c_str());
 
 			oss.str("");
-			oss << cal.MonthOfYear;
-			AddParam(attr, "BYMONTH", oss.str().c_str());
+			oss << "BYMONTH=" << cal.MonthOfYear;
+			AddValue(attr, oss.str().c_str());
 		}
 		break;
 
 	case Calendar::Week:		// eg. every week on Mon and Fri
 					// see: WeekDays
-		AddParam(attr, "FREQ", "WEEKLY");
+		AddValue(attr, "FREQ=WEEKLY");
 		{
 			ostringstream oss;
+			oss << "BYDAY=";
 			for( int i = 0, bm = 1, cnt = 0; i < 7; i++, bm <<= 1 ) {
 				if( cal.WeekDays & bm ) {
 					if( cnt )
@@ -154,7 +207,7 @@ void vCalendar::RecurToVCal()
 					cnt++;
 				}
 			}
-			AddParam(attr, "BYDAY", oss.str().c_str());
+			AddValue(attr, oss.str().c_str());
 		}
 		break;
 
@@ -165,12 +218,14 @@ void vCalendar::RecurToVCal()
 	// add some common parameters
 	if( cal.Interval > 1 ) {
 		ostringstream oss;
-		oss << cal.Interval;
-		AddParam(attr, "INTERVAL", oss.str().c_str());
+		oss << "INTERVAL=" << cal.Interval;
+		AddValue(attr, oss.str().c_str());
 	}
 	if( !cal.Perpetual ) {
 		gStringPtr rend(osync_time_unix2vtime(&cal.RecurringEndTime));
-		AddParam(attr, "UNTIL", rend.Get());
+		ostringstream oss;
+		oss << "UNTIL=" << rend.Get();
+		AddValue(attr, oss.str().c_str());
 	}
 
 	AddAttr(attr);
@@ -221,11 +276,183 @@ void vCalendar::RecurToVCal()
 
 }
 
-void vCalendar::RecurToBarryCal()
+void vCalendar::RecurToBarryCal(vAttr& rrule)
 {
-	// FIXME - needs to be implemented
+	using namespace Barry;
+	using namespace std;
+	Barry::Calendar &cal = m_BarryCal;
+	Trace trace("vCalendar::RecurToBarryCal");
+	std::map<std::string,unsigned char> pmap;
+	pmap["SU"] = CAL_WD_SUN;
+	pmap["MO"] = CAL_WD_MON;
+	pmap["TU"] = CAL_WD_TUE;
+	pmap["WE"] = CAL_WD_WED;
+	pmap["TH"] = CAL_WD_THU;
+	pmap["FR"] = CAL_WD_FRI;
+	pmap["SA"] = CAL_WD_SAT;
+
+
+	int i=0;
+	unsigned int count=0;
+	string val;
+	std::map<std::string,std::string> args;
+	do {
+		val=rrule.GetValue(i++);
+		if(val.length()==0) {
+			break;
+		}
+		string n=val.substr(0,val.find("="));
+		string v=val.substr(val.find("=")+1);
+		args[n]=v;
+		trace.logf("RecurToBarryCal: |%s|%s|",n.c_str(),v.c_str());
+	} while(1);
+	
+	// now process the inferval.
+	
+	time_t now = time(NULL);
+	int zoneoffset = osync_time_timezone_diff(localtime(&now));
+
+	cal.Recurring=TRUE;
+	
+	if(args.find(string("INTERVAL"))!=args.end()) {
+		cal.Interval = atoi(args["INTERVAL"].c_str());
+	}
+	if(args.find(string("UNTIL"))!=args.end()) {
+		cal.Perpetual = FALSE;
+		cal.RecurringEndTime=osync_time_vtime2unix(args["UNTIL"].c_str(), zoneoffset);
+	} else {
+		// if we do not also have COUNT, then we must be forerver
+		if(args.find(string("COUNT"))==args.end()) {
+			cal.Perpetual=TRUE;
+		} else {
+			// we do have COUNT. This means we won't have UNTIL. So we need to
+			// process the RecurringEndTime from the current start date. Set the count level to
+			// something other than zero to indicate we need to process it as the exact end date will
+			// depend upon the frequency.
+			count=atoi(args["COUNT"].c_str());
+		}
+	}
+	
+	// we need these if COUNT is true, or if we are a yearly job.
+	
+	time_t time = cal.StartTime;
+	
+	// TO-DO: we must process COUNT in terms of an end date if we have it.
+
+	// Now deal with the freq
+
+	if(args.find(string("FREQ"))!=args.end()) {
+		if(args["FREQ"]==string("DAILY")) {
+			cal.RecurringType=Calendar::Day;
+		} else {
+			if(args["FREQ"]==string("WEEKLY")) {
+				cal.RecurringType=Calendar::Week;
+				// we must have a dayofweek entry
+				if(args.find(string("BYDAY"))!=args.end()) {
+					std::vector<std::string> v=GetDOWList(args["BYDAY"]);
+					// iterate along our vector and convert
+					for(unsigned int idx=0;idx<v.size();idx++) {
+						cal.WeekDays|=pmap[v[idx]];
+					}
+				} else {
+					// handle error here
+					trace.logf("RecurToBarryCal: no BYDAY on weekly event");
+				}
+				if(count) {
+					// need to process end date. This is easy for weeks,
+					// as a number of weeks can be reduced to seconds simply.
+					cal.RecurringEndTime=time +((count-1)*60*60*24*7);
+				}
+			} else {
+				if(args["FREQ"]=="MONTHLY") {
+					if(args.find(string("BYMONTHDAY"))!=args.end()) {
+						cal.RecurringType=Calendar::MonthByDate;
+						cal.DayOfMonth=atoi(args["BYMONTHDAY"].c_str());
+					} else {
+						if(args.find(string("BYDAY"))!=args.end()) {
+							cal.RecurringType=Calendar::MonthByDay;
+							cal.WeekOfMonth=GetMonthWeekNumFromBYDAY(args["BYDAY"]);
+							cal.DayOfWeek=GetWeekDayIndexFromBYDAY(args["BYDAY"]);
+						} else {
+							trace.logf("RecurToBarryCal: No qualifier on MONTHLY freq");
+						}
+					}
+					if(count) {
+						// Nasty. We need to convert to struct tm, do some modulo-12 addition
+						// then back to time_t
+						struct tm datestruct;
+						gmtime_r(&time,&datestruct);
+						// now do some modulo-12 on the month and year 
+						// We could end up with an illegal date if the day of month is >28 and
+						// the resulting month falls on a February. We don't need to worry about
+						// day of week as mktime() clobbers it.
+						datestruct.tm_year += (datestruct.tm_mon+count)/12;
+						datestruct.tm_mon = (datestruct.tm_mon+count)%12;
+						if(datestruct.tm_mday>28 && datestruct.tm_mon==1) {
+							// force it to 1st Mar
+							// TODO Potential bug on leap years
+							datestruct.tm_mon=2;
+							datestruct.tm_mday=1;
+						}
+						if(datestruct.tm_mday==31 && (datestruct.tm_mon==8 ||
+						                              datestruct.tm_mon==3 ||
+						                              datestruct.tm_mon==5 ||
+													  datestruct.tm_mon==10)) {
+							datestruct.tm_mon+=1;
+							datestruct.tm_mday=1;
+						}
+						cal.RecurringEndTime=mktime(&datestruct);
+					}
+				} else {
+					if(args["FREQ"]=="YEARLY") {
+						if(args.find(string("BYMONTH"))!=args.end()) {
+							cal.MonthOfYear=atoi(args["BYMONTH"].c_str());
+							if(args.find(string("BYMONTHDAY"))!=args.end()) {
+								cal.RecurringType=Calendar::YearByDate;
+								cal.DayOfMonth=atoi(args["BYMONTHDAY"].c_str());
+							} else {
+								if(args.find(string("BYDAY"))!=args.end()) {
+									cal.RecurringType=Calendar::YearByDay;
+									cal.WeekOfMonth=GetMonthWeekNumFromBYDAY(args["BYDAY"]);
+									cal.DayOfWeek=GetWeekDayIndexFromBYDAY(args["BYDAY"]);
+								} else {
+									trace.logf("RecurToBarryCal: No qualifier on YEARLY freq");
+								}
+							}
+						} else {
+							// otherwise use the start date and translate to a BYMONTHDAY.
+							// cal.StartTime has already been processed when we get here
+							// we need month of year, and day of month.
+							struct tm datestruct;
+							gmtime_r(&time,&datestruct);
+							cal.RecurringType=Calendar::YearByDate;
+							cal.MonthOfYear=datestruct.tm_mon;
+							cal.DayOfMonth=datestruct.tm_mday;
+						}
+					}
+					if(count) {
+						// convert to struct tm, then simply add to the year.
+						struct tm datestruct;
+						gmtime_r(&time,&datestruct);
+						datestruct.tm_year += count;
+						cal.RecurringEndTime=mktime(&datestruct);
+					}
+				}
+			}
+		}
+	} else {
+		trace.logf("RecurToBarryCal: No frequency specified!");
+	}
 
-	// GetWeekDayIndex()
+//	unsigned char WeekDays;		// bitmask, bit 0 = sunday
+//
+//		#define CAL_WD_SUN	0x01
+//		#define CAL_WD_MON	0x02
+//		#define CAL_WD_TUE	0x04
+//		#define CAL_WD_WED	0x08
+//		#define CAL_WD_THU	0x10
+//		#define CAL_WD_FRI	0x20
+//		#define CAL_WD_SAT	0x40
 }
 
 // Main conversion routine for converting from Barry::Calendar to
@@ -330,6 +557,8 @@ const Barry::Calendar& vCalendar::ToBarry(const char *vcal, uint32_t RecordId)
 	string notes = GetAttr("DESCRIPTION", "/vevent");
 	trace.logf("DESCRIPTION attr retrieved: %s", notes.c_str());
 
+	vAttr rrule = GetAttrObj("RRULE",0,"/vevent");
+
 
 	//
 	// Now, run checks and convert into Barry object
@@ -373,6 +602,10 @@ const Barry::Calendar& vCalendar::ToBarry(const char *vcal, uint32_t RecordId)
 	rec.Location = location;
 	rec.Notes = notes;
 
+	if(rrule.Get()) {
+		RecurToBarryCal(rrule);
+	}
+
 	// convert trigger time into notification time
 	// assume no notification, by default
 	rec.NotificationTime = 0;
diff --git a/opensync-plugin-0.4x/src/vevent.h b/opensync-plugin-0.4x/src/vevent.h
index 80c2427..3a02c71 100644
--- a/opensync-plugin-0.4x/src/vevent.h
+++ b/opensync-plugin-0.4x/src/vevent.h
@@ -47,11 +47,16 @@ class vCalendar : public vBase
 	Barry::Calendar m_BarryCal;
 
 	static const char *WeekDays[7];
+	
+	std::vector<std::string> GetDOWList(const std::string& str);
+	unsigned short GetMonthWeekNumFromBYDAY(const std::string& ByDay);
+	unsigned short GetWeekDayIndexFromBYDAY(const std::string& ByDay);
 
 protected:
 	void RecurToVCal();
-	void RecurToBarryCal();
-
+	//void RecurToBarryCal();
+	void RecurToBarryCal(vAttr& rrule);
+	
 	static unsigned short GetWeekDayIndex(const char *dayname);
 	bool HasMultipleVEvents() const;
 
diff --git a/opensync-plugin-0.4x/src/vformat.c b/opensync-plugin-0.4x/src/vformat.c
index a0f676d..ab5ea5c 100644
--- a/opensync-plugin-0.4x/src/vformat.c
+++ b/opensync-plugin-0.4x/src/vformat.c
@@ -1013,6 +1013,7 @@ char *b_vformat_to_string (b_VFormat *evc, b_VFormatType type)
 		}
 		attr_str = g_string_append (attr_str, attr->name);
 		/* handle the parameters */
+		
 		for (p = attr->params; p; p = p->next) {
 			b_VFormatParam *param = p->data;
 			/* 5.8.2:
------------------------------------------------------------------------------
The NEW KODAK i700 Series Scanners deliver under ANY circumstances! Your
production scanning environment may not be a perfect world - but thanks to
Kodak, there's a perfect scanner to get the job done! With the NEW KODAK i700
Series Scanner you'll get full speed at 300 dpi even with all image 
processing features enabled. http://p.sf.net/sfu/kodak-com
_______________________________________________
Barry-devel mailing list
Barry-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/barry-devel

Reply via email to