enable easy switching between 0.22 and svn opensync sources
[barry.git] / opensync-plugin / src / event_converter.cc
blob5dcb2134b903102724a5bbdcb30e5691d21fb5a1
1 ///
2 /// \file event_converter.cc
3 /// Conversion routines for calendar events, to/from
4 /// OpenSync's XMLFormats
5 ///
7 /*
8 Copyright (C) 2006-2007, Net Direct Inc. (http://www.netdirect.ca/)
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19 See the GNU General Public License in the COPYING file at the
20 root directory of this project for more details.
23 #include "event_converter.h"
24 #include "trace.h"
25 #include "error.h"
26 #include <opensync/opensync-time.h>
27 #include <sstream>
30 //////////////////////////////////////////////////////////////////////////////
31 // Week day helpers
33 const char *WeekDays[] = { "SU", "MO", "TU", "WE", "TH", "FR", "SA" };
35 unsigned short GetWeekDayIndex(const char *dayname)
37 for( int i = 0; i < 7; i++ ) {
38 if( strcasecmp(dayname, WeekDays[i]) == 0 )
39 return i;
41 return 0;
45 //////////////////////////////////////////////////////////////////////////////
46 // XmlField wrapper class
48 class XmlField
50 OSyncXMLField *m_field;
52 public:
53 /// Create new empty field, ready to add values.
54 XmlField(fXMLFormatPtr &format, const char *fieldname)
56 OSyncError *error = NULL;
57 m_field = osync_xmlfield_new(format.Get(), fieldname, &error);
58 if( !m_field )
59 throw osync_error(error);
62 /// Create new field, and add a "Content" key immediately with specified value
63 XmlField(fXMLFormatPtr &format, const char *fieldname, const char *content)
65 OSyncError *error = NULL;
66 m_field = osync_xmlfield_new(format.Get(), fieldname, &error);
67 if( !m_field )
68 throw osync_error(error);
69 osync_xmlfield_set_key_value(m_field, "Content", content);
72 /// Same as const char* version, except with std::string&.
73 XmlField(fXMLFormatPtr &format, const char *fieldname, const std::string &content)
75 OSyncError *error = NULL;
76 m_field = osync_xmlfield_new(format.Get(), fieldname, &error);
77 if( !m_field )
78 throw osync_error(error);
79 osync_xmlfield_set_key_value(m_field, "Content", content.c_str());
82 /// Create XmlField object based on existing OSyncXMLField pointer
83 explicit XmlField(OSyncXMLField *field)
84 : m_field(field)
88 void SetKeyValue(const char *key, const char *value)
90 osync_xmlfield_set_key_value(m_field, key, value);
93 void SetAttr(const char *name, const char *value)
95 osync_xmlfield_set_attr(m_field, name, value);
98 std::string GetKeyValue(const char *key)
100 const char *content = osync_xmlfield_get_key_value(m_field, key);
101 if( !content )
102 throw osync_error("osync_xmlfield_get_key_value returned NULL");
103 return content;
106 std::string GetKeyValue() // Defaults to "Content"
108 return GetKeyValue("Content");
113 //////////////////////////////////////////////////////////////////////////////
114 // XML -> Barry::Calendar conversion routines
116 void RecurToBarryCal(OSyncXMLFormat *event,
117 OSyncXMLField *field,
118 std::auto_ptr<Barry::Calendar> &cal)
120 // FIXME - needs to be implemented
122 // GetWeekDayIndex()
127 // XmlToCalendar
129 /// Converts an OSyncXMLFormat object into a Barry::Calendar object.
130 /// On error, the auto_ptr<> will contain a null pointer.
132 /// \exception osync_error for OpenSync related errors,
133 /// and ConvertError for conversion errors
135 std::auto_ptr<Barry::Calendar> XmlToCalendar(OSyncXMLFormat *event)
137 Trace trace("XmlToCalendar");
139 std::auto_ptr<Barry::Calendar> cal(new Barry::Calendar);
141 // this function only handles calendar events
142 if( strcmp("event", osync_xmlformat_get_objtype(event)) != 0 ) {
143 cal.reset();
144 throw ConvertError("OSyncXMLFormat is not an event.");
147 OSyncXMLField *field = osync_xmlformat_get_first_field(event);
148 for( ; field; field = osync_xmlfield_get_next(field) ) {
150 std::string name = osync_xmlfield_get_name(field);
151 XmlField xmlobj(field);
153 // log everything
154 trace.log(field);
156 if( name == "DateStarted" ) {
157 cal->StartTime = osync_time_xml2unix(event, field);
158 if( cal->StartTime == -1 ) {
159 trace.log("Unable to convert DateStarted to time_t");
162 else if( name == "DateEnd" ) {
163 cal->EndTime = osync_time_xml2unix(event, field);
164 if( cal->EndTime == -1 ) {
165 trace.log("Unable to convert DateEnd to time_t");
169 else if( name == "Alarm" ||
170 name == "AlarmAudio" ||
171 name == "AlarmDisplay" ||
172 name == "AlarmEmail" ||
173 name == "AlarmProcedure" ) {
175 XmlField alarm(xml, "Alarm");
176 alarm.SetKeyValue("AlarmAction", "AUDIO");
177 // notify must be UTC, when specified in DATE-TIME
178 alarm.SetKeyValue("AlarmTrigger", tz_notify.Get());
179 alarm.SetAttr("Value", "DATE-TIME");
183 else if( name == "Summary" ) {
184 cal->Subject = xmlobj.GetKeyValue();
185 if( cal->Subject.size() == 0 ) {
186 cal->Subject = "<blank subject>";
187 trace.log("ERROR: bad data, blank SUMMARY");
190 else if( name == "Description" ) {
191 cal->Notes = xmlobj.GetKeyValue();
193 else if( name == "Location" ) {
194 cal->Location = xmlobj.GetKeyValue();
196 else if( name == "RecurrenceRule" ) {
197 RecurToBarryCal(event, field, cal);
202 // do some checking
203 if( cal->StartTime == -1 || cal->EndTime == -1 ) {
204 // FIXME - DTEND is actually optional! According to the
205 // RFC, a DTSTART with no DTEND should be treated
206 // like a "special day" like an anniversary, which occupies
207 // no time.
208 throw ConvertError("Blank DateStarted or DateEnd");
211 // FIXME - we are assuming that any non-UTC timestamps
212 // in the vcalendar record will be in the current timezone...
213 // This is wrong! fix this later.
215 // Also, we current ignore any time zone
216 // parameters that might be in the vcalendar format... this
217 // must be fixed.
219 uint32_t RecordId = 0;
220 cal->SetIds(Barry::Calendar::GetDefaultRecType(), RecordId);
222 // FIXME - until notification time is supported, we assume 15 min
223 // in advance
224 cal->NotificationTime = cal->StartTime - 15 * 60;
226 return cal;
230 //////////////////////////////////////////////////////////////////////////////
231 // Barry::Calendar -> XML conversion routines
233 void RecurToXml(fXMLFormatPtr &xml, const Barry::Calendar &cal)
235 using namespace Barry;
236 using namespace std;
238 if( !cal.Recurring )
239 return;
241 XmlField recur(xml, "RecurrenceRule");
243 switch( cal.RecurringType )
245 case Calendar::Day: // eg. every day
246 recur.SetKeyValue("Frequency", "DAILY");
247 break;
249 case Calendar::MonthByDate: // eg. every month on the 12th
250 // see: DayOfMonth
251 recur.SetKeyValue("Frequency", "MONTHLY");
253 ostringstream oss;
254 oss << cal.DayOfMonth;
255 recur.SetKeyValue("ByMonthDay", oss.str().c_str());
257 break;
259 case Calendar::MonthByDay: // eg. every month on 3rd Wed
260 // see: DayOfWeek and WeekOfMonth
261 recur.SetKeyValue("Frequency", "MONTHLY");
262 if( cal.DayOfWeek <= 6 ) { // DayOfWeek is unsigned
263 ostringstream oss;
264 oss << cal.WeekOfMonth << WeekDays[cal.DayOfWeek];
265 recur.SetKeyValue("ByDay", oss.str().c_str());
267 break;
269 case Calendar::YearByDate: // eg. every year on March 5
270 // see: DayOfMonth and MonthOfYear
271 recur.SetKeyValue("Frequency", "YEARLY");
273 ostringstream oss;
274 oss << cal.MonthOfYear;
275 recur.SetKeyValue("ByMonth", oss.str().c_str());
278 ostringstream oss;
279 oss << cal.DayOfMonth;
280 recur.SetKeyValue("ByMonthDay", oss.str().c_str());
282 break;
284 case Calendar::YearByDay: // eg. every year on 3rd Wed of Jan
285 // see: DayOfWeek, WeekOfMonth, and
286 // MonthOfYear
287 recur.SetKeyValue("Frequency", "YEARLY");
288 if( cal.DayOfWeek <= 6 ) { // DayOfWeek is unsigned
289 ostringstream oss;
290 oss << cal.WeekOfMonth << WeekDays[cal.DayOfWeek];
291 recur.SetKeyValue("ByDay", oss.str().c_str());
293 oss.str("");
294 oss << cal.MonthOfYear;
295 recur.SetKeyValue("ByMonth", oss.str().c_str());
297 break;
299 case Calendar::Week: // eg. every week on Mon and Fri
300 // see: WeekDays
301 recur.SetKeyValue("Frequency", "WEEKLY");
303 ostringstream oss;
304 for( int i = 0, bm = 1, cnt = 0; i < 7; i++, bm <<= 1 ) {
305 if( cal.WeekDays & bm ) {
306 if( cnt )
307 oss << ",";
308 oss << WeekDays[i];
309 cnt++;
312 // recur.SetKeyValue("WKST", oss.str().c_str());
313 recur.SetKeyValue("ByDay", oss.str().c_str());
315 break;
317 default:
318 throw ConvertError("Unknown RecurringType in Barry Calendar object");
321 // add some common parameters
322 if( cal.Interval > 1 ) {
323 ostringstream oss;
324 oss << cal.Interval;
325 recur.SetKeyValue("Interval", oss.str().c_str());
327 if( !cal.Perpetual ) {
328 gStringPtr rend(osync_time_unix2vtime(&cal.RecurringEndTime));
329 recur.SetKeyValue("Until", rend.Get());
332 // bool AllDayEvent;
334 // ///
335 // /// Recurring data
336 // ///
337 // /// Note: interval can be used on all of these recurring types to
338 // /// make it happen "every other time" or more, etc.
339 // ///
341 // bool Recurring;
342 // RecurringCodeType RecurringType;
343 // unsigned short Interval; // must be >= 1
344 // time_t RecurringEndTime; // only pertains if Recurring is true
345 // // sets the date and time when
346 // // recurrence of this appointment
347 // // should no longer occur
348 // // If a perpetual appointment, this
349 // // is 0xFFFFFFFF in the low level data
350 // // Instead, set the following flag.
351 // bool Perpetual; // if true, this will always recur
352 // unsigned short TimeZoneCode; // the time zone originally used
353 // // for the recurrence data...
354 // // seems to have little use, but
355 // // set to your current time zone
356 // // as a good default
358 // unsigned short // recurring details, depending on type
359 // DayOfWeek, // 0-6
360 // WeekOfMonth, // 1-5
361 // DayOfMonth, // 1-31
362 // MonthOfYear; // 1-12
363 // unsigned char WeekDays; // bitmask, bit 0 = sunday
365 // #define CAL_WD_SUN 0x01
366 // #define CAL_WD_MON 0x02
367 // #define CAL_WD_TUE 0x04
368 // #define CAL_WD_WED 0x08
369 // #define CAL_WD_THU 0x10
370 // #define CAL_WD_FRI 0x20
371 // #define CAL_WD_SAT 0x40
377 // CalendarToXml
379 /// Converts a Barry::Calendar object into an OSyncXMLFormat object,
380 /// and returns the resulting, completed, XML object. On error,
381 /// returns 0.
383 /// \exception osync_error for OpenSync related errors,
384 /// and ConvertError for conversion errors
386 fXMLFormatPtr CalendarToXml(const Barry::Calendar &cal)
388 Trace trace("CalendarToXml");
390 OSyncError *error = NULL;
392 fXMLFormatPtr xml( osync_xmlformat_new("event", &error) );
393 if( !xml.Get() ) {
394 trace.errorf("Cannot create new event XML format: %s",
395 osync_error_print(&error));
396 osync_error_unref(&error);
397 return xml;
400 // begin building XML data
401 XmlField sum(xml, "Summary", cal.Subject);
402 XmlField dsc(xml, "Description", cal.Notes);
403 XmlField loc(xml, "Location", cal.Location);
405 gStringPtr tz_start(osync_time_unix2vtime(&cal.StartTime));
406 gStringPtr tz_end(osync_time_unix2vtime(&cal.EndTime));
408 XmlField str(xml, "DateStarted", tz_start.Get());
409 XmlField end(xml, "DateEnd", tz_end.Get());
410 // FIXME - add a truly globally unique "UID" string?
412 gStringPtr tz_notify(osync_time_unix2vtime(&cal.NotificationTime));
414 XmlField alarm(xml, "Alarm");
415 alarm.SetKeyValue("AlarmAction", "AUDIO");
416 // notify must be UTC, when specified in DATE-TIME
417 alarm.SetKeyValue("AlarmTrigger", tz_notify.Get());
418 alarm.SetAttr("Value", "DATE-TIME");
421 if( cal.Recurring ) {
422 RecurToXml(xml, cal);
425 return xml;