2 /// \file event_converter.cc
3 /// Conversion routines for calendar events, to/from
4 /// OpenSync's XMLFormats
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"
26 #include <opensync/opensync-time.h>
30 //////////////////////////////////////////////////////////////////////////////
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 )
45 //////////////////////////////////////////////////////////////////////////////
46 // XmlField wrapper class
50 OSyncXMLField
*m_field
;
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
);
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
);
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
);
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
)
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
);
102 throw osync_error("osync_xmlfield_get_key_value returned NULL");
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
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 ) {
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
);
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
);
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
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
219 uint32_t RecordId
= 0;
220 cal
->SetIds(Barry::Calendar::GetDefaultRecType(), RecordId
);
222 // FIXME - until notification time is supported, we assume 15 min
224 cal
->NotificationTime
= cal
->StartTime
- 15 * 60;
230 //////////////////////////////////////////////////////////////////////////////
231 // Barry::Calendar -> XML conversion routines
233 void RecurToXml(fXMLFormatPtr
&xml
, const Barry::Calendar
&cal
)
235 using namespace Barry
;
241 XmlField
recur(xml
, "RecurrenceRule");
243 switch( cal
.RecurringType
)
245 case Calendar::Day
: // eg. every day
246 recur
.SetKeyValue("Frequency", "DAILY");
249 case Calendar::MonthByDate
: // eg. every month on the 12th
251 recur
.SetKeyValue("Frequency", "MONTHLY");
254 oss
<< cal
.DayOfMonth
;
255 recur
.SetKeyValue("ByMonthDay", oss
.str().c_str());
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
264 oss
<< cal
.WeekOfMonth
<< WeekDays
[cal
.DayOfWeek
];
265 recur
.SetKeyValue("ByDay", oss
.str().c_str());
269 case Calendar::YearByDate
: // eg. every year on March 5
270 // see: DayOfMonth and MonthOfYear
271 recur
.SetKeyValue("Frequency", "YEARLY");
274 oss
<< cal
.MonthOfYear
;
275 recur
.SetKeyValue("ByMonth", oss
.str().c_str());
279 oss
<< cal
.DayOfMonth
;
280 recur
.SetKeyValue("ByMonthDay", oss
.str().c_str());
284 case Calendar::YearByDay
: // eg. every year on 3rd Wed of Jan
285 // see: DayOfWeek, WeekOfMonth, and
287 recur
.SetKeyValue("Frequency", "YEARLY");
288 if( cal
.DayOfWeek
<= 6 ) { // DayOfWeek is unsigned
290 oss
<< cal
.WeekOfMonth
<< WeekDays
[cal
.DayOfWeek
];
291 recur
.SetKeyValue("ByDay", oss
.str().c_str());
294 oss
<< cal
.MonthOfYear
;
295 recur
.SetKeyValue("ByMonth", oss
.str().c_str());
299 case Calendar::Week
: // eg. every week on Mon and Fri
301 recur
.SetKeyValue("Frequency", "WEEKLY");
304 for( int i
= 0, bm
= 1, cnt
= 0; i
< 7; i
++, bm
<<= 1 ) {
305 if( cal
.WeekDays
& bm
) {
312 // recur.SetKeyValue("WKST", oss.str().c_str());
313 recur
.SetKeyValue("ByDay", oss
.str().c_str());
318 throw ConvertError("Unknown RecurringType in Barry Calendar object");
321 // add some common parameters
322 if( cal
.Interval
> 1 ) {
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());
335 // /// Recurring data
337 // /// Note: interval can be used on all of these recurring types to
338 // /// make it happen "every other time" or more, etc.
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
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
379 /// Converts a Barry::Calendar object into an OSyncXMLFormat object,
380 /// and returns the resulting, completed, XML object. On error,
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
) );
394 trace
.errorf("Cannot create new event XML format: %s",
395 osync_error_print(&error
));
396 osync_error_unref(&error
);
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
);