# vevent.cc

    00001 //
    00002 // \file        vevent.cc
    00003 //              Conversion routines for vevents (VCALENDAR, etc)
    00004 //
    00005
    00006 /*
    00007     Copyright (C) 2006-2011, Net Direct Inc. (http://www.netdirect.ca/)
    00008     Copyright (C) 2010, Nicolas VIVIEN
    00009     Copyright (C) 2009, Dr J A Gow <J.A.Gow@wellfrazzled.com>
    00010
    00011     This program is free software; you can redistribute it and/or modify
    00012     it under the terms of the GNU General Public License as published by
    00013     the Free Software Foundation; either version 2 of the License, or
    00014     (at your option) any later version.
    00015
    00016     This program is distributed in the hope that it will be useful,
    00017     but WITHOUT ANY WARRANTY; without even the implied warranty of
    00018     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    00019
    00020     See the GNU General Public License in the COPYING file at the
    00021     root directory of this project for more details.
    00022 */
    00023
    00024 #include "vevent.h"
    00025 //#include "trace.h"
    00026 #include <stdint.h>
    00027 #include <glib.h>
    00028 #include <strings.h>
    00029 #include <stdlib.h>
    00030 #include <sstream>
    00031 #include <string>
    00032
    00033 using namespace std;
    00034
    00035 namespace Barry { namespace Sync {
    00036
    00037 //////////////////////////////////////////////////////////////////////////////
    00038 // vCalendar
    00039
    00040 vCalendar::vCalendar(vTimeConverter &vtc)
    00041         : m_vtc(vtc)
    00042         , m_gCalData(0)
    00043 {
    00044 }
    00045
    00046 vCalendar::~vCalendar()
    00047 {
    00048         if( m_gCalData ) {
    00049                 g_free(m_gCalData);
    00050         }
    00051 }
    00052
    00053 const char *vCalendar::WeekDays[] = { "SU", "MO", "TU", "WE", "TH", "FR", "SA" };
    00054
    00055 unsigned short vCalendar::GetWeekDayIndex(const char *dayname)
    00056 {
    00057         for( int i = 0; i < 7; i++ ) {
    00058                 if( strcasecmp(dayname, WeekDays[i]) == 0 )
    00059                         return i;
    00060         }
    00061         return 0;
    00062 }
    00063
    00064 unsigned short vCalendar::GetMonthWeekNumFromBYDAY(const std::string& ByDay)
    00065 {
    00066         return atoi(ByDay.substr(0,ByDay.length()-2).c_str());
    00067 }
    00068
    00069 unsigned short vCalendar::GetWeekDayIndexFromBYDAY(const std::string& ByDay)
    00070 {
    00071         return GetWeekDayIndex(ByDay.substr(ByDay.length()-2).c_str());
    00072 }
    00073
    00074
    00075 bool vCalendar::HasMultipleVEvents() const
    00076 {
    00077         int count = 0;
    00078         b_VFormat *format = const_cast<b_VFormat*>(Format());
    00079         GList *attrs = format ? b_vformat_get_attributes(format) : 0;
    00080         for( ; attrs; attrs = attrs->next ) {
    00081                 b_VFormatAttribute *attr = (b_VFormatAttribute*) attrs->data;
    00082                 if( strcasecmp(b_vformat_attribute_get_name(attr), "BEGIN") == 0 &&
    00083                     strcasecmp(b_vformat_attribute_get_nth_value(attr, 0), "VEVENT") == 0 )
    00084                 {
    00085                         count++;
    00086                 }
    00087         }
    00088         return count > 1;
    00089 }
    00090
    00091 void vCalendar::RecurToVCal()
    00092 {
    00093         using namespace Barry;
    00094         using namespace std;
    00095         Barry::Calendar &cal = m_BarryCal;
    00096
    00097         if( !cal.Recurring )
    00098                 return;
    00099
    00100         vAttrPtr attr = NewAttr("RRULE");
    00101
    00102         switch( cal.RecurringType )
    00103         {
    00104         case Calendar::Day:             // eg. every day
    00105                 AddValue(attr,"FREQ=DAILY");
    00106                 break;
    00107
    00108         case Calendar::MonthByDate:     // eg. every month on the 12th
    00109                                         // see: DayOfMonth
    00110                 AddValue(attr,"FREQ=MONTHLY");
    00111                 {
    00112                         ostringstream oss;
    00113                         oss << "BYMONTHDAY=" << cal.DayOfMonth;
    00114                         AddValue(attr, oss.str().c_str());
    00115                 }
    00116                 break;
    00117
    00118         case Calendar::MonthByDay:      // eg. every month on 3rd Wed
    00119                                         // see: DayOfWeek and WeekOfMonth
    00120                 AddValue(attr, "FREQ=MONTHLY");
    00121                 if( cal.DayOfWeek <= 6 ) {      // DayOfWeek is unsigned
    00122                         ostringstream oss;
    00123                         oss << "BYDAY=" << cal.WeekOfMonth << WeekDays[cal.DayOfWeek];
    00124                         AddValue(attr, oss.str().c_str());
    00125                 }
    00126                 break;
    00127
    00128         case Calendar::YearByDate:      // eg. every year on March 5
    00129                                         // see: DayOfMonth and MonthOfYear
    00130                 AddValue(attr, "FREQ=YEARLY");
    00131                 {
    00132                         ostringstream oss;
    00133                         oss << "BYMONTH=" << cal.MonthOfYear;
    00134                         AddValue(attr, oss.str().c_str());
    00135                 }
    00136                 {
    00137                         ostringstream oss;
    00138                         oss << "BYMONTHDAY=" << cal.DayOfMonth;
    00139                         AddValue(attr, oss.str().c_str());
    00140                 }
    00141                 break;
    00142
    00143         case Calendar::YearByDay:       // eg. every year on 3rd Wed of Jan
    00144                                         // see: DayOfWeek, WeekOfMonth, and
    00145                                         //      MonthOfYear
    00146                 AddValue(attr, "FREQ=YEARLY");
    00147                 if( cal.DayOfWeek <= 6 ) {      // DayOfWeek is unsigned
    00148                         ostringstream oss;
    00149                         oss << "BYDAY=" << cal.WeekOfMonth << WeekDays[cal.DayOfWeek];
    00150                         AddValue(attr, oss.str().c_str());
    00151
    00152                         oss.str("");
    00153                         oss << "BYMONTH=" << cal.MonthOfYear;
    00154                         AddValue(attr, oss.str().c_str());
    00155                 }
    00156                 break;
    00157
    00158         case Calendar::Week:            // eg. every week on Mon and Fri
    00159                                         // see: WeekDays
    00160                 AddValue(attr, "FREQ=WEEKLY");
    00161                 {
    00162                         ostringstream oss;
    00163                         oss << "BYDAY=";
    00164                         for( int i = 0, bm = 1, cnt = 0; i < 7; i++, bm <<= 1 ) {
    00165                                 if( cal.WeekDays & bm ) {
    00166                                         if( cnt )
    00167                                                 oss << ",";
    00168                                         oss << WeekDays[i];
    00169                                         cnt++;
    00170                                 }
    00171                         }
    00172                         AddValue(attr, oss.str().c_str());
    00173                 }
    00174                 break;
    00175
    00176         default:
    00177                 throw ConvertError("Unknown RecurringType in Barry Calendar object");
    00178         }
    00179
    00180         // add some common parameters
    00181         if( cal.Interval > 1 ) {
    00182                 ostringstream oss;
    00183                 oss << "INTERVAL=" << cal.Interval;
    00184                 AddValue(attr, oss.str().c_str());
    00185         }
    00186         if( !cal.Perpetual ) {
    00187                 ostringstream oss;
    00188                 oss << "UNTIL=" << m_vtc.unix2vtime(&cal.RecurringEndTime);
    00189                 AddValue(attr, oss.str().c_str());
    00190         }
    00191
    00192         AddAttr(attr);
    00193
    00194 /*
    00195         bool AllDayEvent;
    00196
    00197         ///
    00198         /// Recurring data
    00199         ///
    00200         /// Note: interval can be used on all of these recurring types to
    00201         ///       make it happen "every other time" or more, etc.
    00202         ///
    00203
    00204         bool Recurring;
    00205         RecurringCodeType RecurringType;
    00206         unsigned short Interval;        // must be >= 1
    00207         time_t RecurringEndTime;        // only pertains if Recurring is true
    00208                                         // sets the date and time when
    00209                                         // recurrence of this appointment
    00210                                         // should no longer occur
    00211                                         // If a perpetual appointment, this
    00212                                         // is 0xFFFFFFFF in the low level data
    00213                                         // Instead, set the following flag.
    00214         bool Perpetual;                 // if true, this will always recur
    00215         unsigned short TimeZoneCode;    // the time zone originally used
    00216                                         // for the recurrence data...
    00217                                         // seems to have little use, but
    00218                                         // set to your current time zone
    00219                                         // as a good default
    00220
    00221         unsigned short                  // recurring details, depending on type
    00222                 DayOfWeek,              // 0-6
    00223                 WeekOfMonth,            // 1-5
    00224                 DayOfMonth,             // 1-31
    00225                 MonthOfYear;            // 1-12
    00226         unsigned char WeekDays;         // bitmask, bit 0 = sunday
    00227
    00228                 #define CAL_WD_SUN      0x01
    00229                 #define CAL_WD_MON      0x02
    00230                 #define CAL_WD_TUE      0x04
    00231                 #define CAL_WD_WED      0x08
    00232                 #define CAL_WD_THU      0x10
    00233                 #define CAL_WD_FRI      0x20
    00234                 #define CAL_WD_SAT      0x40
    00235
    00236 */
    00237
    00238 }
    00239
    00240 void vCalendar::RecurToBarryCal(vAttr& rrule, time_t starttime)
    00241 {
    00242         using namespace Barry;
    00243         using namespace std;
    00244         Barry::Calendar &cal = m_BarryCal;
    00245 //      Trace trace("vCalendar::RecurToBarryCal");
    00246         std::map<std::string,unsigned char> pmap;
    00247         pmap["SU"] = CAL_WD_SUN;
    00248         pmap["MO"] = CAL_WD_MON;
    00249         pmap["TU"] = CAL_WD_TUE;
    00250         pmap["WE"] = CAL_WD_WED;
    00251         pmap["TH"] = CAL_WD_THU;
    00252         pmap["FR"] = CAL_WD_FRI;
    00253         pmap["SA"] = CAL_WD_SAT;
    00254
    00255
    00256         int i=0;
    00257         unsigned int count=0;
    00258         string val;
    00259         std::map<std::string,std::string> args;
    00260         do {
    00261                 val=rrule.GetValue(i++);
    00262                 if(val.length()==0) {
    00263                         break;
    00264                 }
    00265                 string n=val.substr(0,val.find("="));
    00266                 string v=val.substr(val.find("=")+1);
    00267                 args[n]=v;
    00268 //              trace.logf("RecurToBarryCal: |%s|%s|",n.c_str(),v.c_str());
    00269         } while(1);
    00270
    00271         // now process the interval.
    00272         cal.Recurring=TRUE;
    00273
    00274         if(args.find(string("INTERVAL"))!=args.end()) {
    00275                 cal.Interval = atoi(args["INTERVAL"].c_str());
    00276         }
    00277         if(args.find(string("UNTIL"))!=args.end()) {
    00278                 cal.Perpetual = FALSE;
    00279                 cal.RecurringEndTime=m_vtc.vtime2unix(args["UNTIL"].c_str());
    00280                 if( cal.RecurringEndTime == (time_t)-1 ) {
    00281 //                      trace.logf("osync_time_vtime2unix() failed: UNTIL = %s, zoneoffset = %d", args["UNTIL"].c_str(), zoneoffset);
    00282                 }
    00283         } else {
    00284                 // if we do not also have COUNT, then we must be forerver
    00285                 if(args.find(string("COUNT"))==args.end()) {
    00286                         cal.Perpetual=TRUE;
    00287                 } else {
    00288                         // we do have COUNT. This means we won't have UNTIL.
    00289                         // So we need to process the RecurringEndTime from
    00290                         // the current start date. Set the count level to
    00291                         // something other than zero to indicate we need
    00292                         // to process it as the exact end date will
    00293                         // depend upon the frequency.
    00294                         count=atoi(args["COUNT"].c_str());
    00295                         if( count == 0 ) {
    00296                                 throw std::runtime_error("Invalid COUNT in recurring rule: " + args["COUNT"]);
    00297                         }
    00298                 }
    00299         }
    00300
    00301         // we need these if COUNT is true, or if we are a yearly job.
    00302
    00303         // TO-DO: we must process COUNT in terms of an end date if we have it.
    00304
    00305         // Now deal with the freq
    00306
    00307         if(args.find(string("FREQ"))==args.end()) {
    00308 //              trace.logf("RecurToBarryCal: No frequency specified!");
    00309                 return;
    00310         }
    00311
    00312         if(args["FREQ"]==string("DAILY")) {
    00313                 cal.RecurringType=Calendar::Day;
    00314
    00315         } else if(args["FREQ"]==string("WEEKLY")) {
    00316                 cal.RecurringType=Calendar::Week;
    00317                 // we must have a dayofweek entry
    00318                 if(args.find(string("BYDAY"))!=args.end()) {
    00319                         std::vector<std::string> v=Tokenize(args["BYDAY"]);
    00320                         // iterate along our vector and convert
    00321                         for(unsigned int idx=0;idx<v.size();idx++) {
    00322                                 cal.WeekDays|=pmap[v[idx]];
    00323                         }
    00324                 } else {
    00325                         // handle error here
    00326 //                      trace.logf("RecurToBarryCal: no BYDAY on weekly event");
    00327                 }
    00328                 if(count) {
    00329                         // need to process end date. This is easy
    00330                         // for weeks, as a number of weeks can be
    00331                         // reduced to seconds simply.
    00332                         cal.RecurringEndTime=starttime +((count-1)*60*60*24*7);
    00333                 }
    00334         } else if(args["FREQ"]=="MONTHLY") {
    00335                 if(args.find(string("BYMONTHDAY"))!=args.end()) {
    00336                         cal.RecurringType=Calendar::MonthByDate;
    00337                         cal.DayOfMonth=atoi(args["BYMONTHDAY"].c_str());
    00338                 } else {
    00339                         if(args.find(string("BYDAY"))!=args.end()) {
    00340                                 cal.RecurringType=Calendar::MonthByDay;
    00341                                 cal.WeekOfMonth=GetMonthWeekNumFromBYDAY(args["BYDAY"]);
    00342                                 cal.DayOfWeek=GetWeekDayIndexFromBYDAY(args["BYDAY"]);
    00343                         } else {
    00344 //                              trace.logf("RecurToBarryCal: No qualifier on MONTHLY freq");
    00345                         }
    00346                 }
    00347                 if(count) {
    00348                         // Nasty. We need to convert to struct tm,
    00349                         // do some modulo-12 addition then back
    00350                         // to time_t
    00351                         struct tm datestruct;
    00352                         localtime_r(&starttime,&datestruct);
    00353                         // now do some modulo-12 on the month and year
    00354                         // We could end up with an illegal date if
    00355                         // the day of month is >28 and the resulting
    00356                         // month falls on a February. We don't need
    00357                         // to worry about day of week as mktime()
    00358                         // clobbers it.
    00359                         datestruct.tm_year += (datestruct.tm_mon+count)/12;
    00360                         datestruct.tm_mon = (datestruct.tm_mon+count)%12;
    00361                         if(datestruct.tm_mday>28 && datestruct.tm_mon==1) {
    00362                                 // force it to 1st Mar
    00363                                 // TODO Potential bug on leap years
    00364                                 datestruct.tm_mon=2;
    00365                                 datestruct.tm_mday=1;
    00366                         }
    00367                         if(datestruct.tm_mday==31 && (datestruct.tm_mon==8 ||
    00368                                                       datestruct.tm_mon==3 ||
    00369                                                       datestruct.tm_mon==5 ||
    00370                                                                                   datestruct.tm_mon==10)) {
    00371                                 datestruct.tm_mon+=1;
    00372                                 datestruct.tm_mday=1;
    00373                         }
    00374                         cal.RecurringEndTime=mktime(&datestruct);
    00375                 }
    00376         } else if(args["FREQ"]=="YEARLY") {
    00377                 if(args.find(string("BYMONTH"))!=args.end()) {
    00378                         cal.MonthOfYear=atoi(args["BYMONTH"].c_str());
    00379                         if(args.find(string("BYMONTHDAY"))!=args.end()) {
    00380                                 cal.RecurringType=Calendar::YearByDate;
    00381                                 cal.DayOfMonth=atoi(args["BYMONTHDAY"].c_str());
    00382                         } else {
    00383                                 if(args.find(string("BYDAY"))!=args.end()) {
    00384                                         cal.RecurringType=Calendar::YearByDay;
    00385                                         cal.WeekOfMonth=GetMonthWeekNumFromBYDAY(args["BYDAY"]);
    00386                                         cal.DayOfWeek=GetWeekDayIndexFromBYDAY(args["BYDAY"]);
    00387                                 } else {
    00388 //                                      trace.logf("RecurToBarryCal: No qualifier on YEARLY freq");
    00389                                 }
    00390                         }
    00391                 } else {
    00392                         // otherwise use the start date and translate
    00393                         // to a BYMONTHDAY.
    00394                         // cal.StartTime has already been processed
    00395                         // when we get here we need month of year,
    00396                         // and day of month.
    00397                         struct tm datestruct;
    00398                         localtime_r(&starttime,&datestruct);
    00399                         cal.RecurringType=Calendar::YearByDate;
    00400                         cal.MonthOfYear=datestruct.tm_mon;
    00401                         cal.DayOfMonth=datestruct.tm_mday;
    00402                 }
    00403                 if(count) {
    00404                         // convert to struct tm, then simply add to the year.
    00405                         struct tm datestruct;
    00406                         localtime_r(&starttime,&datestruct);
    00407                         datestruct.tm_year += count;
    00408                         cal.RecurringEndTime=mktime(&datestruct);
    00409                 }
    00410         }
    00411
    00412 //      unsigned char WeekDays;         // bitmask, bit 0 = sunday
    00413 //
    00414 //              #define CAL_WD_SUN      0x01
    00415 //              #define CAL_WD_MON      0x02
    00416 //              #define CAL_WD_TUE      0x04
    00417 //              #define CAL_WD_WED      0x08
    00418 //              #define CAL_WD_THU      0x10
    00419 //              #define CAL_WD_FRI      0x20
    00420 //              #define CAL_WD_SAT      0x40
    00421 }
    00422
    00423 // Main conversion routine for converting from Barry::Calendar to
    00424 // a vCalendar string of data.
    00425 const std::string& vCalendar::ToVCal(const Barry::Calendar &cal)
    00426 {
    00427 //      Trace trace("vCalendar::ToVCal");
    00428         std::ostringstream oss;
    00429         cal.Dump(oss);
    00430 //      trace.logf("ToVCal, initial Barry record: %s", oss.str().c_str());
    00431
    00432         // start fresh
    00433         Clear();
    00434         SetFormat( b_vformat_new() );
    00435         if( !Format() )
    00436                 throw ConvertError("resource error allocating vformat");
    00437
    00438         // store the Barry object we're working with
    00439         m_BarryCal = cal;
    00440
    00441         // begin building vCalendar data
    00442         AddAttr(NewAttr("PRODID", "-//OpenSync//NONSGML Barry Calendar Record//EN"));
    00443         AddAttr(NewAttr("BEGIN", "VEVENT"));
    00444         AddAttr(NewAttr("SEQUENCE", "0"));
    00445         AddAttr(NewAttr("SUMMARY", cal.Subject.c_str()));
    00446         AddAttr(NewAttr("DESCRIPTION", cal.Notes.c_str()));
    00447         AddAttr(NewAttr("LOCATION", cal.Location.c_str()));
    00448
    00449         string start(m_vtc.unix2vtime(&cal.StartTime));
    00450         string end(m_vtc.unix2vtime(&cal.EndTime));
    00451         string notify(m_vtc.unix2vtime(&cal.NotificationTime));
    00452
    00453         // if an all day event, only print the date parts of the string
    00454         if( cal.AllDayEvent && start.find('T') != string::npos ) {
    00455                 // truncate start date
    00456                 start = start.substr(0, start.find('T'));
    00457
    00458                 // create end date 1 day in future
    00459                 time_t end_t = cal.StartTime + 24 * 60 * 60;
    00460                 end = m_vtc.unix2vtime(&end_t);
    00461                 end = end.substr(0, end.find('T'));
    00462         }
    00463
    00464         AddAttr(NewAttr("DTSTART", start.c_str()));
    00465         AddAttr(NewAttr("DTEND", end.c_str()));
    00466         // FIXME - add a truly globally unique "UID" string?
    00467
    00468
    00469         AddAttr(NewAttr("BEGIN", "VALARM"));
    00470         AddAttr(NewAttr("ACTION", "AUDIO"));
    00471
    00472         // notify must be UTC, when specified in DATE-TIME
    00473         vAttrPtr trigger = NewAttr("TRIGGER", notify.c_str());
    00474         AddParam(trigger, "VALUE", "DATE-TIME");
    00475         AddAttr(trigger);
    00476
    00477         AddAttr(NewAttr("END", "VALARM"));
    00478
    00479
    00480         if( cal.Recurring ) {
    00481                 RecurToVCal();
    00482         }
    00483
    00484         AddAttr(NewAttr("END", "VEVENT"));
    00485
    00486         // generate the raw VCALENDAR data
    00487         m_gCalData = b_vformat_to_string(Format(), VFORMAT_EVENT_20);
    00488         m_vCalData = m_gCalData;
    00489
    00490 //      trace.logf("ToVCal, resulting vcal data: %s", m_vCalData.c_str());
    00491         return m_vCalData;
    00492 }
    00493
    00494 // Main conversion routine for converting from vCalendar data string
    00495 // to a Barry::Calendar object.
    00496 const Barry::Calendar& vCalendar::ToBarry(const char *vcal, uint32_t RecordId)
    00497 {
    00498         using namespace std;
    00499
    00500 //      Trace trace("vCalendar::ToBarry");
    00501 //      trace.logf("ToBarry, working on vcal data: %s", vcal);
    00502
    00503         // we only handle vCalendar data with one vevent block
    00504         if( HasMultipleVEvents() )
    00505                 throw ConvertError("vCalendar data contains more than one VEVENT block, unsupported");
    00506
    00507         // start fresh
    00508         Clear();
    00509
    00510         // store the vCalendar raw data
    00511         m_vCalData = vcal;
    00512
    00513         // create format parser structures
    00514         SetFormat( b_vformat_new_from_string(vcal) );
    00515         if( !Format() )
    00516                 throw ConvertError("resource error allocating vformat");
    00517
    00518         string start = GetAttr("DTSTART", "/vevent");
    00519 //      trace.logf("DTSTART attr retrieved: %s", start.c_str());
    00520         string end = GetAttr("DTEND", "/vevent");
    00521 //      trace.logf("DTEND attr retrieved: %s", end.c_str());
    00522         string subject = GetAttr("SUMMARY", "/vevent");
    00523 //      trace.logf("SUMMARY attr retrieved: %s", subject.c_str());
    00524         if( subject.size() == 0 ) {
    00525                 subject = "<blank subject>";
    00526 //              trace.logf("ERROR: bad data, blank SUMMARY: %s", vcal);
    00527         }
    00528         vAttr trigger_obj = GetAttrObj("TRIGGER", 0, "/valarm");
    00529
    00530         string location = GetAttr("LOCATION", "/vevent");
    00531 //      trace.logf("LOCATION attr retrieved: %s", location.c_str());
    00532
    00533         string notes = GetAttr("DESCRIPTION", "/vevent");
    00534 //      trace.logf("DESCRIPTION attr retrieved: %s", notes.c_str());
    00535
    00536         vAttr rrule = GetAttrObj("RRULE",0,"/vevent");
    00537
    00538
    00539         //
    00540         // Now, run checks and convert into Barry object
    00541         //
    00542
    00543
    00544         // FIXME - we are assuming that any non-UTC timestamps
    00545         // in the vcalendar record will be in the current timezone...
    00546         // This is wrong!  fix this later.
    00547         //
    00548         // Also, we currently ignore any time zone
    00549         // parameters that might be in the vcalendar format... this
    00550         // must be fixed.
    00551         //
    00552         Barry::Calendar &rec = m_BarryCal;
    00553         rec.SetIds(Barry::Calendar::GetDefaultRecType(), RecordId);
    00554
    00555         if( !start.size() )
    00556                 throw ConvertError("Blank DTSTART");
    00557         rec.StartTime = m_vtc.vtime2unix(start.c_str());
    00558
    00559         if( !end.size() ) {
    00560                 // DTEND is actually optional!  According to the
    00561                 // RFC, a DTSTART with no DTEND should be treated
    00562                 // like a "special day" like an anniversary, which occupies
    00563                 // no time.
    00564                 //
    00565                 // Since the Blackberry doesn't really map well to this
    00566                 // case, we'll set the end time to 1 day past start.
    00567                 //
    00568                 rec.EndTime = rec.StartTime + 24 * 60 * 60;
    00569         }
    00570         else {
    00571                 rec.EndTime = m_vtc.vtime2unix(end.c_str());
    00572         }
    00573
    00574         // check for "all day event" which is specified by a DTSTART
    00575         // and a DTEND with no times, and one day apart
    00576         if( start.find('T') == string::npos && end.size() &&
    00577             end.find('T') == string::npos &&
    00578             (rec.EndTime - rec.StartTime) == 24 * 60 * 60 )
    00579         {
    00580                 rec.AllDayEvent = true;
    00581         }
    00582
    00583         rec.Subject = subject;
    00584         rec.Location = location;
    00585         rec.Notes = notes;
    00586
    00587         if(rrule.Get()) {
    00588                 RecurToBarryCal(rrule, rec.StartTime);
    00589         }
    00590
    00591         // convert trigger time into notification time
    00592         // assume no notification, by default
    00593         rec.NotificationTime = 0;
    00594         if( trigger_obj.Get() ) {
    00595                 string trigger_type = trigger_obj.GetParam("VALUE");
    00596                 string trigger = trigger_obj.GetValue();
    00597
    00598                 if( trigger.size() == 0 ) {
    00599 //                      trace.logf("ERROR: no TRIGGER found in calendar entry, assuming notification time as 15 minutes before start.");
    00600                 }
    00601                 else if( trigger_type == "DATE-TIME" ) {
    00602                         rec.NotificationTime = m_vtc.vtime2unix(trigger.c_str());
    00603                 }
    00604                 else if( trigger_type == "DURATION" || trigger_type.size() == 0 ) {
    00605                         // default is DURATION (RFC 4.8.6.3)
    00606                         string related = trigger_obj.GetParam("RELATED");
    00607
    00608                         // default to relative to start time
    00609                         time_t *relative = &rec.StartTime;
    00610                         if( related == "END" )
    00611                                 relative = &rec.EndTime;
    00612
    00613                         rec.NotificationTime = *relative + m_vtc.alarmduration2sec(trigger.c_str());
    00614                 }
    00615                 else {
    00616                         throw ConvertError("Unknown TRIGGER VALUE");
    00617                 }
    00618         }
    00619         else {
    00620 //              trace.logf("ERROR: no TRIGGER found in calendar entry, assuming notification time as 15 minutes before start.");
    00621         }
    00622
    00623         std::ostringstream oss;
    00624         m_BarryCal.Dump(oss);
    00625 //      trace.logf("ToBarry, resulting Barry record: %s", oss.str().c_str());
    00626         return m_BarryCal;
    00627 }
    00628
    00629 // Transfers ownership of m_gCalData to the caller.
    00630 char* vCalendar::ExtractVCal()
    00631 {
    00632         char *ret = m_gCalData;
    00633         m_gCalData = 0;
    00634         return ret;
    00635 }
    00636
    00637 void vCalendar::Clear()
    00638 {
    00639         vBase::Clear();
    00640         m_vCalData.clear();
    00641         m_BarryCal.Clear();
    00642
    00643         if( m_gCalData ) {
    00644                 g_free(m_gCalData);
    00645                 m_gCalData = 0;
    00646         }
    00647 }
    00648
    00649 }} // namespace Barry::Sync
    00650