lib: improved error message reporting in vevent.cc
[barry.git] / src / vevent.cc
blobaefbcad11f0b291d001d3dd9f0f523300f498aef
1 ///
2 /// \file vevent.cc
3 /// Conversion routines for vevents (VCALENDAR, etc)
4 ///
6 /*
7 Copyright (C) 2006-2012, Net Direct Inc. (http://www.netdirect.ca/)
8 Copyright (C) 2010, Nicolas VIVIEN
9 Copyright (C) 2009, Dr J A Gow <J.A.Gow@wellfrazzled.com>
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20 See the GNU General Public License in the COPYING file at the
21 root directory of this project for more details.
24 #include "vevent.h"
25 //#include "trace.h"
26 #include "log.h"
27 #include <stdint.h>
28 #include <glib.h>
29 #include <strings.h>
30 #include <stdlib.h>
31 #include <sstream>
32 #include <string>
34 using namespace std;
36 namespace Barry { namespace Sync {
38 //////////////////////////////////////////////////////////////////////////////
39 // vCalendar
41 vCalendar::vCalendar(vTimeConverter &vtc)
42 : m_vtc(vtc)
43 , m_gCalData(0)
47 vCalendar::~vCalendar()
49 if( m_gCalData ) {
50 g_free(m_gCalData);
54 const char *vCalendar::WeekDays[] = { "SU", "MO", "TU", "WE", "TH", "FR", "SA" };
56 uint16_t vCalendar::GetWeekDayIndex(const char *dayname)
58 for( int i = 0; i < 7; i++ ) {
59 if( strcasecmp(dayname, WeekDays[i]) == 0 )
60 return i;
62 return 0;
65 void vCalendar::CheckUnsupportedArg(const ArgMapType &args,
66 const std::string &name)
68 if( args.find(name) != args.end() ) {
69 barrylog("ERROR: recurrence rule contains " << name << ", unsupported by Barry. MIME conversion will be incorrect.");
70 barryverbose("Record data so far:\n" << m_BarryCal);
74 uint16_t vCalendar::GetMonthWeekNumFromBYDAY(const std::string& ByDay)
76 return atoi(ByDay.substr(0,ByDay.length()-2).c_str());
79 uint16_t vCalendar::GetWeekDayIndexFromBYDAY(const std::string& ByDay)
81 return GetWeekDayIndex(ByDay.substr(ByDay.length()-2).c_str());
85 bool vCalendar::HasMultipleVEvents() const
87 int count = 0;
88 b_VFormat *format = const_cast<b_VFormat*>(Format());
89 GList *attrs = format ? b_vformat_get_attributes(format) : 0;
90 for( ; attrs; attrs = attrs->next ) {
91 b_VFormatAttribute *attr = (b_VFormatAttribute*) attrs->data;
92 if( strcasecmp(b_vformat_attribute_get_name(attr), "BEGIN") == 0 &&
93 strcasecmp(b_vformat_attribute_get_nth_value(attr, 0), "VEVENT") == 0 )
95 count++;
98 return count > 1;
101 void vCalendar::RecurToVCal()
103 using namespace Barry;
104 using namespace std;
105 Barry::Calendar &cal = m_BarryCal;
107 if( !cal.Recurring )
108 return;
110 vAttrPtr attr = NewAttr("RRULE");
112 switch( cal.RecurringType )
114 case Calendar::Day: // eg. every day
115 AddValue(attr,"FREQ=DAILY");
116 break;
118 case Calendar::MonthByDate: // eg. every month on the 12th
119 // see: DayOfMonth
120 AddValue(attr,"FREQ=MONTHLY");
122 ostringstream oss;
123 oss << "BYMONTHDAY=" << cal.DayOfMonth;
124 AddValue(attr, oss.str().c_str());
126 break;
128 case Calendar::MonthByDay: // eg. every month on 3rd Wed
129 // see: DayOfWeek and WeekOfMonth
130 AddValue(attr, "FREQ=MONTHLY");
131 if( cal.DayOfWeek <= 6 ) { // DayOfWeek is unsigned
132 ostringstream oss;
133 oss << "BYDAY=" << cal.WeekOfMonth << WeekDays[cal.DayOfWeek];
134 AddValue(attr, oss.str().c_str());
136 break;
138 case Calendar::YearByDate: // eg. every year on March 5
139 // see: DayOfMonth and MonthOfYear
140 AddValue(attr, "FREQ=YEARLY");
142 ostringstream oss;
143 oss << "BYMONTH=" << cal.MonthOfYear;
144 AddValue(attr, oss.str().c_str());
147 ostringstream oss;
148 oss << "BYMONTHDAY=" << cal.DayOfMonth;
149 AddValue(attr, oss.str().c_str());
151 break;
153 case Calendar::YearByDay: // eg. every year on 3rd Wed of Jan
154 // see: DayOfWeek, WeekOfMonth, and
155 // MonthOfYear
156 AddValue(attr, "FREQ=YEARLY");
157 if( cal.DayOfWeek <= 6 ) { // DayOfWeek is unsigned
158 ostringstream oss;
159 oss << "BYDAY=" << cal.WeekOfMonth << WeekDays[cal.DayOfWeek];
160 AddValue(attr, oss.str().c_str());
162 oss.str("");
163 oss << "BYMONTH=" << cal.MonthOfYear;
164 AddValue(attr, oss.str().c_str());
166 break;
168 case Calendar::Week: // eg. every week on Mon and Fri
169 // see: WeekDays
170 AddValue(attr, "FREQ=WEEKLY");
172 ostringstream oss;
173 oss << "BYDAY=";
174 for( int i = 0, bm = 1, cnt = 0; i < 7; i++, bm <<= 1 ) {
175 if( cal.WeekDays & bm ) {
176 if( cnt )
177 oss << ",";
178 oss << WeekDays[i];
179 cnt++;
182 AddValue(attr, oss.str().c_str());
184 break;
186 default:
187 throw ConvertError("Unknown RecurringType in Barry Calendar object");
190 // add some common parameters
191 if( cal.Interval > 1 ) {
192 ostringstream oss;
193 oss << "INTERVAL=" << cal.Interval;
194 AddValue(attr, oss.str().c_str());
196 if( !cal.Perpetual ) {
197 ostringstream oss;
198 oss << "UNTIL=" << m_vtc.unix2vtime(&cal.RecurringEndTime.Time);
199 AddValue(attr, oss.str().c_str());
202 AddAttr(attr);
205 bool AllDayEvent;
208 /// Recurring data
210 /// Note: interval can be used on all of these recurring types to
211 /// make it happen "every other time" or more, etc.
214 bool Recurring;
215 RecurringCodeType RecurringType;
216 uint16_t Interval; // must be >= 1
217 time_t RecurringEndTime; // only pertains if Recurring is true
218 // sets the date and time when
219 // recurrence of this appointment
220 // should no longer occur
221 // If a perpetual appointment, this
222 // is 0xFFFFFFFF in the low level data
223 // Instead, set the following flag.
224 bool Perpetual; // if true, this will always recur
225 uint16_t TimeZoneCode; // the time zone originally used
226 // for the recurrence data...
227 // seems to have little use, but
228 // set to your current time zone
229 // as a good default
231 uint16_t // recurring details, depending on type
232 DayOfWeek, // 0-6
233 WeekOfMonth, // 1-5
234 DayOfMonth, // 1-31
235 MonthOfYear; // 1-12
236 unsigned char WeekDays; // bitmask, bit 0 = sunday
238 #define CAL_WD_SUN 0x01
239 #define CAL_WD_MON 0x02
240 #define CAL_WD_TUE 0x04
241 #define CAL_WD_WED 0x08
242 #define CAL_WD_THU 0x10
243 #define CAL_WD_FRI 0x20
244 #define CAL_WD_SAT 0x40
250 void vCalendar::RecurToBarryCal(vAttr& rrule, time_t starttime)
252 using namespace Barry;
253 using namespace std;
254 Barry::Calendar &cal = m_BarryCal;
255 // Trace trace("vCalendar::RecurToBarryCal");
256 std::map<std::string,unsigned char> pmap;
257 pmap["SU"] = CAL_WD_SUN;
258 pmap["MO"] = CAL_WD_MON;
259 pmap["TU"] = CAL_WD_TUE;
260 pmap["WE"] = CAL_WD_WED;
261 pmap["TH"] = CAL_WD_THU;
262 pmap["FR"] = CAL_WD_FRI;
263 pmap["SA"] = CAL_WD_SAT;
266 int i=0;
267 unsigned int count=0;
268 string val;
269 ArgMapType args;
270 do {
271 val=rrule.GetValue(i++);
272 if(val.length()==0) {
273 break;
275 string n=val.substr(0,val.find("="));
276 string v=val.substr(val.find("=")+1);
277 args[n]=v;
278 // trace.logf("RecurToBarryCal: |%s|%s|",n.c_str(),v.c_str());
279 } while(1);
281 // now process the interval.
282 cal.Recurring=TRUE;
284 if(args.find(string("INTERVAL"))!=args.end()) {
285 int interval = atoi(args["INTERVAL"].c_str());
286 if( interval < 1 ) {
287 // force to at least 1, for math below
288 interval = 1;
290 cal.Interval = interval;
292 else {
293 // default to 1, for the math below.
294 // RecurBase::Clear() does this for us as well, but
295 // best to be safe
296 cal.Interval = 1;
298 if(args.find(string("UNTIL"))!=args.end()) {
299 cal.Perpetual = FALSE;
300 cal.RecurringEndTime.Time = m_vtc.vtime2unix(args["UNTIL"].c_str());
301 if( cal.RecurringEndTime.Time == (time_t)-1 ) {
302 // trace.logf("osync_time_vtime2unix() failed: UNTIL = %s, zoneoffset = %d", args["UNTIL"].c_str(), zoneoffset);
304 } else {
305 // if we do not also have COUNT, then we must be forerver
306 if(args.find(string("COUNT"))==args.end()) {
307 cal.Perpetual=TRUE;
308 } else {
309 // we do have COUNT. This means we won't have UNTIL.
310 // So we need to process the RecurringEndTime from
311 // the current start date. Set the count level to
312 // something other than zero to indicate we need
313 // to process it as the exact end date will
314 // depend upon the frequency.
315 count=atoi(args["COUNT"].c_str());
316 if( count == 0 ) {
317 throw std::runtime_error("Invalid COUNT in recurring rule: " + args["COUNT"]);
322 // we need these if COUNT is true, or if we are a yearly job.
324 // TO-DO: we must process COUNT in terms of an end date if we have it.
326 // warn the user about unsupported arguments
327 CheckUnsupportedArg(args, "BYSETPOS");// FIXME - theorectically supportable
328 CheckUnsupportedArg(args, "BYYEARDAY");
329 CheckUnsupportedArg(args, "BYWEEKNO");
330 CheckUnsupportedArg(args, "WKST");
331 CheckUnsupportedArg(args, "BYSECOND");
332 CheckUnsupportedArg(args, "BYMINUTE");
333 CheckUnsupportedArg(args, "BYHOUR");
335 // Now deal with the freq
337 if(args.find(string("FREQ"))==args.end()) {
338 // trace.logf("RecurToBarryCal: No frequency specified!");
339 return;
342 if(args["FREQ"]==string("DAILY")) {
343 cal.RecurringType=Calendar::Day;
345 if(count) {
346 // add count-1*interval days to find the end time:
347 // i.e. if starting on 2012/01/01 and going
348 // for 3 days, then the last day will be
349 // 2012/01/03.
351 // For intervals, the count is every interval days,
352 // so interval of 2 means 2012/01/01, 2012/01/03, etc.
353 // and the calculation still works.
354 cal.RecurringEndTime.Time =
355 starttime + (count-1) * cal.Interval * 24*60*60;
357 } else if(args["FREQ"]==string("WEEKLY")) {
358 cal.RecurringType=Calendar::Week;
359 // we must have a dayofweek entry
360 if(args.find(string("BYDAY"))!=args.end()) {
361 std::vector<std::string> v=Tokenize(args["BYDAY"]);
362 // iterate along our vector and convert
363 for(unsigned int idx=0;idx<v.size();idx++) {
364 cal.WeekDays|=pmap[v[idx]];
366 } else {
367 // we must have at least one day selected, and if no
368 // BYDAY is selected, use the start time's day
369 struct tm datestruct;
370 localtime_r(&starttime,&datestruct);
371 cal.WeekDays = pmap[WeekDays[datestruct.tm_wday]];
373 barrylog("Warning: WEEKLY VEVENT without a day selected. Assuming day of start time.");
374 barryverbose("Record data so far:\n" << cal);
377 if(count) {
378 // need to process end date. This is easy
379 // for weeks, as a number of weeks can be
380 // reduced to seconds simply.
381 cal.RecurringEndTime.Time =
382 starttime + (count-1) * cal.Interval * 60*60*24*7;
384 } else if(args["FREQ"]=="MONTHLY") {
385 if(args.find(string("BYMONTHDAY"))!=args.end()) {
386 cal.RecurringType=Calendar::MonthByDate;
387 cal.DayOfMonth=atoi(args["BYMONTHDAY"].c_str());
388 } else {
389 if(args.find(string("BYDAY"))!=args.end()) {
390 cal.RecurringType=Calendar::MonthByDay;
391 cal.WeekOfMonth=GetMonthWeekNumFromBYDAY(args["BYDAY"]);
392 cal.DayOfWeek=GetWeekDayIndexFromBYDAY(args["BYDAY"]);
393 } else {
394 // must have a recurring type, so assume
395 // that monthly means every day on the day
396 // of month specified by starttime
397 struct tm datestruct;
398 localtime_r(&starttime,&datestruct);
400 cal.RecurringType = Calendar::MonthByDate;
401 cal.DayOfMonth = datestruct.tm_mday;
402 barrylog("Warning: MONTHLY VEVENT without a day type specified (no BYMONTHDAY nor BYDAY). Assuming BYMONTHDAY, using day of start time.");
403 barryverbose("Record data so far:\n" << cal);
406 if(count) {
407 // Nasty. We need to convert to struct tm,
408 // do some modulo-12 addition then back
409 // to time_t
410 struct tm datestruct;
411 localtime_r(&starttime,&datestruct);
412 // now do some modulo-12 on the month and year
413 // We could end up with an illegal date if
414 // the day of month is >28 and the resulting
415 // month falls on a February. We don't need
416 // to worry about day of week as mktime()
417 // clobbers it.
418 int add = (count-1) * cal.Interval;
419 datestruct.tm_year += (datestruct.tm_mon+add)/12;
420 datestruct.tm_mon = (datestruct.tm_mon+add)%12;
421 if(datestruct.tm_mday>28 && datestruct.tm_mon==1) {
422 // force it to 1st Mar
423 // TODO Potential bug on leap years
424 datestruct.tm_mon=2;
425 datestruct.tm_mday=1;
427 if(datestruct.tm_mday==31 && (datestruct.tm_mon==8 ||
428 datestruct.tm_mon==3 ||
429 datestruct.tm_mon==5 ||
430 datestruct.tm_mon==10)) {
431 datestruct.tm_mon+=1;
432 datestruct.tm_mday=1;
434 // Just in case we're crossing DST boundaries,
435 // add an hour, to make sure we reach the ending
436 // month, in the case of intervals
437 datestruct.tm_hour++;
438 cal.RecurringEndTime.Time = mktime(&datestruct);
440 } else if(args["FREQ"]=="YEARLY") {
441 bool need_assumption = true;
442 if(args.find(string("BYMONTH"))!=args.end()) {
443 cal.MonthOfYear=atoi(args["BYMONTH"].c_str());
444 if(args.find(string("BYMONTHDAY"))!=args.end()) {
445 cal.RecurringType=Calendar::YearByDate;
446 cal.DayOfMonth=atoi(args["BYMONTHDAY"].c_str());
447 need_assumption = false;
448 } else {
449 if(args.find(string("BYDAY"))!=args.end()) {
450 cal.RecurringType=Calendar::YearByDay;
451 cal.WeekOfMonth=GetMonthWeekNumFromBYDAY(args["BYDAY"]);
452 cal.DayOfWeek=GetWeekDayIndexFromBYDAY(args["BYDAY"]);
453 need_assumption = false;
454 } else {
455 // fall through to assumption below...
460 if( need_assumption ) {
461 // otherwise use the start date and translate
462 // to a BYMONTHDAY.
463 // cal.StartTime has already been processed
464 // when we get here we need month of year,
465 // and day of month.
466 struct tm datestruct;
467 localtime_r(&starttime,&datestruct);
468 cal.RecurringType=Calendar::YearByDate;
469 cal.MonthOfYear=datestruct.tm_mon;
470 cal.DayOfMonth=datestruct.tm_mday;
471 barrylog("Warning: YEARLY VEVENT without a day type specified (no BYMONTHDAY nor BYDAY). Assuming BYMONTHDAY, using day and month of start time.");
472 barryverbose("Record data so far:\n" << cal);
474 if(count) {
475 // convert to struct tm, then simply add to the year.
477 // Note: intervals do work in the device firmware,
478 // but not all of the devices allow you to edit it
479 // with their GUI... hmmm... oh well, allow it
480 // anyway, and do the multiplication below.
481 struct tm datestruct;
482 localtime_r(&starttime,&datestruct);
483 datestruct.tm_year += (count-1) * cal.Interval;
484 cal.RecurringEndTime.Time = mktime(&datestruct);
488 // unsigned char WeekDays; // bitmask, bit 0 = sunday
490 // #define CAL_WD_SUN 0x01
491 // #define CAL_WD_MON 0x02
492 // #define CAL_WD_TUE 0x04
493 // #define CAL_WD_WED 0x08
494 // #define CAL_WD_THU 0x10
495 // #define CAL_WD_FRI 0x20
496 // #define CAL_WD_SAT 0x40
499 // Main conversion routine for converting from Barry::Calendar to
500 // a vCalendar string of data.
501 const std::string& vCalendar::ToVCal(const Barry::Calendar &cal)
503 // Trace trace("vCalendar::ToVCal");
504 std::ostringstream oss;
505 cal.Dump(oss);
506 // trace.logf("ToVCal, initial Barry record: %s", oss.str().c_str());
508 // start fresh
509 Clear();
510 SetFormat( b_vformat_new() );
511 if( !Format() )
512 throw ConvertError("resource error allocating vformat");
514 // store the Barry object we're working with
515 m_BarryCal = cal;
517 // RFC section 4.8.7.2 requires DTSTAMP in all VEVENT, VTODO,
518 // VJOURNAL, and VFREEBUSY calendar components, and it must be
519 // in UTC. DTSTAMP holds the timestamp of when the iCal object itself
520 // was created, not when the object was created in the device or app.
521 // So, find out what time it is "now".
522 time_t now = time(NULL);
524 // begin building vCalendar data
525 AddAttr(NewAttr("PRODID", "-//OpenSync//NONSGML Barry Calendar Record//EN"));
526 AddAttr(NewAttr("BEGIN", "VEVENT"));
527 AddAttr(NewAttr("DTSTAMP", m_vtc.unix2vtime(&now).c_str())); // see note above
528 AddAttr(NewAttr("SEQUENCE", "0"));
529 AddAttr(NewAttr("SUMMARY", cal.Subject.c_str()));
530 AddAttr(NewAttr("DESCRIPTION", cal.Notes.c_str()));
531 AddAttr(NewAttr("LOCATION", cal.Location.c_str()));
533 string start(m_vtc.unix2vtime(&cal.StartTime.Time));
534 string end(m_vtc.unix2vtime(&cal.EndTime.Time));
535 string notify(m_vtc.unix2vtime(&cal.NotificationTime.Time));
537 // if an all day event, only print the date parts of the string
538 if( cal.AllDayEvent && start.find('T') != string::npos ) {
539 // truncate start date
540 start = start.substr(0, start.find('T'));
542 // create end date 1 day in future
543 time_t end_t = cal.StartTime.Time + 24 * 60 * 60;
544 end = m_vtc.unix2vtime(&end_t);
545 end = end.substr(0, end.find('T'));
548 AddAttr(NewAttr("DTSTART", start.c_str()));
549 AddAttr(NewAttr("DTEND", end.c_str()));
550 // FIXME - add a truly globally unique "UID" string?
553 AddAttr(NewAttr("BEGIN", "VALARM"));
554 AddAttr(NewAttr("ACTION", "AUDIO"));
556 // notify must be UTC, when specified in DATE-TIME
557 vAttrPtr trigger = NewAttr("TRIGGER", notify.c_str());
558 AddParam(trigger, "VALUE", "DATE-TIME");
559 AddAttr(trigger);
561 AddAttr(NewAttr("END", "VALARM"));
564 if( cal.Recurring ) {
565 RecurToVCal();
568 AddAttr(NewAttr("END", "VEVENT"));
570 // generate the raw VCALENDAR data
571 m_gCalData = b_vformat_to_string(Format(), VFORMAT_EVENT_20);
572 m_vCalData = m_gCalData;
574 // trace.logf("ToVCal, resulting vcal data: %s", m_vCalData.c_str());
575 return m_vCalData;
578 // Main conversion routine for converting from vCalendar data string
579 // to a Barry::Calendar object.
580 const Barry::Calendar& vCalendar::ToBarry(const char *vcal, uint32_t RecordId)
582 using namespace std;
584 // Trace trace("vCalendar::ToBarry");
585 // trace.logf("ToBarry, working on vcal data: %s", vcal);
587 // we only handle vCalendar data with one vevent block
588 if( HasMultipleVEvents() )
589 throw ConvertError("vCalendar data contains more than one VEVENT block, unsupported");
591 // start fresh
592 Clear();
594 // store the vCalendar raw data
595 m_vCalData = vcal;
597 // create format parser structures
598 SetFormat( b_vformat_new_from_string(vcal) );
599 if( !Format() )
600 throw ConvertError("resource error allocating vformat");
602 string start = GetAttr("DTSTART", "/vevent");
603 // trace.logf("DTSTART attr retrieved: %s", start.c_str());
604 string end = GetAttr("DTEND", "/vevent");
605 // trace.logf("DTEND attr retrieved: %s", end.c_str());
606 string subject = GetAttr("SUMMARY", "/vevent");
607 // trace.logf("SUMMARY attr retrieved: %s", subject.c_str());
608 if( subject.size() == 0 ) {
609 subject = "<blank subject>";
610 // trace.logf("ERROR: bad data, blank SUMMARY: %s", vcal);
612 vAttr trigger_obj = GetAttrObj("TRIGGER", 0, "/valarm");
614 string location = GetAttr("LOCATION", "/vevent");
615 // trace.logf("LOCATION attr retrieved: %s", location.c_str());
617 string notes = GetAttr("DESCRIPTION", "/vevent");
618 // trace.logf("DESCRIPTION attr retrieved: %s", notes.c_str());
620 vAttr rrule = GetAttrObj("RRULE",0,"/vevent");
624 // Now, run checks and convert into Barry object
628 // FIXME - we are assuming that any non-UTC timestamps
629 // in the vcalendar record will be in the current timezone...
630 // This is wrong! fix this later.
632 // Also, we currently ignore any time zone
633 // parameters that might be in the vcalendar format... this
634 // must be fixed.
636 Barry::Calendar &rec = m_BarryCal;
637 rec.SetIds(Barry::Calendar::GetDefaultRecType(), RecordId);
639 if( !start.size() )
640 throw ConvertError("Blank DTSTART");
641 rec.StartTime.Time = m_vtc.vtime2unix(start.c_str());
643 if( !end.size() ) {
644 // DTEND is actually optional! According to the
645 // RFC, a DTSTART with no DTEND should be treated
646 // like a "special day" like an anniversary, which occupies
647 // no time.
649 // Since the Blackberry doesn't really map well to this
650 // case, we'll set the end time to 1 day past start.
652 rec.EndTime.Time = rec.StartTime.Time + 24 * 60 * 60;
654 else {
655 rec.EndTime.Time = m_vtc.vtime2unix(end.c_str());
658 // check for "all day event" which is specified by a DTSTART
659 // and a DTEND with no times, and one day apart
660 if( start.find('T') == string::npos && end.size() &&
661 end.find('T') == string::npos &&
662 (rec.EndTime.Time - rec.StartTime.Time) == 24 * 60 * 60 )
664 rec.AllDayEvent = true;
667 rec.Subject = subject;
668 rec.Location = location;
669 rec.Notes = notes;
671 if(rrule.Get()) {
672 RecurToBarryCal(rrule, rec.StartTime.Time);
675 // convert trigger time into notification time
676 // assume no notification, by default
677 rec.NotificationTime.Time = 0;
678 if( trigger_obj.Get() ) {
679 string trigger_type = trigger_obj.GetParam("VALUE");
680 string trigger = trigger_obj.GetValue();
682 if( trigger.size() == 0 ) {
683 // trace.logf("ERROR: no TRIGGER found in calendar entry, assuming notification time as 15 minutes before start.");
685 else if( trigger_type == "DATE-TIME" ) {
686 rec.NotificationTime.Time = m_vtc.vtime2unix(trigger.c_str());
688 else if( trigger_type == "DURATION" || trigger_type.size() == 0 ) {
689 // default is DURATION (RFC 4.8.6.3)
690 string related = trigger_obj.GetParam("RELATED");
692 // default to relative to start time
693 time_t *relative = &rec.StartTime.Time;
694 if( related == "END" )
695 relative = &rec.EndTime.Time;
697 rec.NotificationTime.Time = *relative + m_vtc.alarmduration2sec(trigger.c_str());
699 else {
700 throw ConvertError("Unknown TRIGGER VALUE");
703 else {
704 // trace.logf("ERROR: no TRIGGER found in calendar entry, assuming notification time as 15 minutes before start.");
707 std::ostringstream oss;
708 m_BarryCal.Dump(oss);
709 // trace.logf("ToBarry, resulting Barry record: %s", oss.str().c_str());
710 return m_BarryCal;
713 // Transfers ownership of m_gCalData to the caller.
714 char* vCalendar::ExtractVCal()
716 char *ret = m_gCalData;
717 m_gCalData = 0;
718 return ret;
721 void vCalendar::Clear()
723 vBase::Clear();
724 m_vCalData.clear();
725 m_BarryCal.Clear();
727 if( m_gCalData ) {
728 g_free(m_gCalData);
729 m_gCalData = 0;
733 }} // namespace Barry::Sync