00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "vevent.h"
00025
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
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:
00105 AddValue(attr,"FREQ=DAILY");
00106 break;
00107
00108 case Calendar::MonthByDate:
00109
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:
00119
00120 AddValue(attr, "FREQ=MONTHLY");
00121 if( cal.DayOfWeek <= 6 ) {
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:
00129
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:
00144
00145
00146 AddValue(attr, "FREQ=YEARLY");
00147 if( cal.DayOfWeek <= 6 ) {
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:
00159
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
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
00196
00197
00198
00199
00200
00201
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
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
00269 } while(1);
00270
00271
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
00282 }
00283 } else {
00284
00285 if(args.find(string("COUNT"))==args.end()) {
00286 cal.Perpetual=TRUE;
00287 } else {
00288
00289
00290
00291
00292
00293
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
00302
00303
00304
00305
00306
00307 if(args.find(string("FREQ"))==args.end()) {
00308
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
00318 if(args.find(string("BYDAY"))!=args.end()) {
00319 std::vector<std::string> v=Tokenize(args["BYDAY"]);
00320
00321 for(unsigned int idx=0;idx<v.size();idx++) {
00322 cal.WeekDays|=pmap[v[idx]];
00323 }
00324 } else {
00325
00326
00327 }
00328 if(count) {
00329
00330
00331
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
00345 }
00346 }
00347 if(count) {
00348
00349
00350
00351 struct tm datestruct;
00352 localtime_r(&starttime,&datestruct);
00353
00354
00355
00356
00357
00358
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
00363
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
00389 }
00390 }
00391 } else {
00392
00393
00394
00395
00396
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
00405 struct tm datestruct;
00406 localtime_r(&starttime,&datestruct);
00407 datestruct.tm_year += count;
00408 cal.RecurringEndTime=mktime(&datestruct);
00409 }
00410 }
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421 }
00422
00423
00424
00425 const std::string& vCalendar::ToVCal(const Barry::Calendar &cal)
00426 {
00427
00428 std::ostringstream oss;
00429 cal.Dump(oss);
00430
00431
00432
00433 Clear();
00434 SetFormat( b_vformat_new() );
00435 if( !Format() )
00436 throw ConvertError("resource error allocating vformat");
00437
00438
00439 m_BarryCal = cal;
00440
00441
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
00454 if( cal.AllDayEvent && start.find('T') != string::npos ) {
00455
00456 start = start.substr(0, start.find('T'));
00457
00458
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
00467
00468
00469 AddAttr(NewAttr("BEGIN", "VALARM"));
00470 AddAttr(NewAttr("ACTION", "AUDIO"));
00471
00472
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
00487 m_gCalData = b_vformat_to_string(Format(), VFORMAT_EVENT_20);
00488 m_vCalData = m_gCalData;
00489
00490
00491 return m_vCalData;
00492 }
00493
00494
00495
00496 const Barry::Calendar& vCalendar::ToBarry(const char *vcal, uint32_t RecordId)
00497 {
00498 using namespace std;
00499
00500
00501
00502
00503
00504 if( HasMultipleVEvents() )
00505 throw ConvertError("vCalendar data contains more than one VEVENT block, unsupported");
00506
00507
00508 Clear();
00509
00510
00511 m_vCalData = vcal;
00512
00513
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
00520 string end = GetAttr("DTEND", "/vevent");
00521
00522 string subject = GetAttr("SUMMARY", "/vevent");
00523
00524 if( subject.size() == 0 ) {
00525 subject = "<blank subject>";
00526
00527 }
00528 vAttr trigger_obj = GetAttrObj("TRIGGER", 0, "/valarm");
00529
00530 string location = GetAttr("LOCATION", "/vevent");
00531
00532
00533 string notes = GetAttr("DESCRIPTION", "/vevent");
00534
00535
00536 vAttr rrule = GetAttrObj("RRULE",0,"/vevent");
00537
00538
00539
00540
00541
00542
00543
00544
00545
00546
00547
00548
00549
00550
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
00561
00562
00563
00564
00565
00566
00567
00568 rec.EndTime = rec.StartTime + 24 * 60 * 60;
00569 }
00570 else {
00571 rec.EndTime = m_vtc.vtime2unix(end.c_str());
00572 }
00573
00574
00575
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
00592
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
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
00606 string related = trigger_obj.GetParam("RELATED");
00607
00608
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
00621 }
00622
00623 std::ostringstream oss;
00624 m_BarryCal.Dump(oss);
00625
00626 return m_BarryCal;
00627 }
00628
00629
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 }}
00650