2 /// \file r_calendar.cc
3 /// Blackberry database record parser class for calendar records.
7 Copyright (C) 2005-2008, Net Direct Inc. (http://www.netdirect.ca/)
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU General Public License in the COPYING file at the
19 root directory of this project for more details.
22 #include "r_calendar.h"
23 #include "record-internal.h"
25 #include "protostructs.h"
35 #define __DEBUG_MODE__
39 using namespace Barry::Protocol
;
44 ///////////////////////////////////////////////////////////////////////////////
47 // calendar field codes
48 #define CALFC_APPT_TYPE_FLAG 0x01
49 #define CALFC_SUBJECT 0x02
50 #define CALFC_NOTES 0x03
51 #define CALFC_LOCATION 0x04
52 #define CALFC_NOTIFICATION_TIME 0x05
53 #define CALFC_START_TIME 0x06
54 #define CALFC_END_TIME 0x07
55 #define CALFC_RECURRENCE_DATA 0x0c
56 #define CALFC_VERSION_DATA 0x10
57 #define CALFC_NOTIFICATION_DATA 0x1a
58 #define CALFC_FREEBUSY_FLAG 0x1c
59 #define CALFC_TIMEZONE_CODE 0x1e // only seems to show up if recurring
60 #define CALFC_CLASS_FLAG 0x28 // private flag from outlook
61 #define CALFC_ALLDAYEVENT_FLAG 0xff
62 #define CALFC_END 0xffff
64 FieldLink
<Calendar
> CalendarFieldLinks
[] = {
65 { CALFC_SUBJECT
, "Subject", 0, 0, &Calendar::Subject
, 0, 0 },
66 { CALFC_NOTES
, "Notes", 0, 0, &Calendar::Notes
, 0, 0 },
67 { CALFC_LOCATION
, "Location", 0, 0, &Calendar::Location
, 0, 0 },
68 { CALFC_NOTIFICATION_TIME
,"Notification Time",0,0, 0, 0, &Calendar::NotificationTime
},
69 { CALFC_START_TIME
, "Start Time", 0, 0, 0, 0, &Calendar::StartTime
},
70 { CALFC_END_TIME
, "End Time", 0, 0, 0, 0, &Calendar::EndTime
},
71 { CALFC_END
, "End of List",0, 0, 0, 0, 0 }
83 const unsigned char* Calendar::ParseField(const unsigned char *begin
,
84 const unsigned char *end
)
86 const CommonField
*field
= (const CommonField
*) begin
;
88 // advance and check size
89 begin
+= COMMON_FIELD_HEADER_SIZE
+ btohs(field
->size
);
90 if( begin
> end
) // if begin==end, we are ok
93 if( !btohs(field
->size
) ) // if field has no size, something's up
96 // cycle through the type table
97 for( FieldLink
<Calendar
> *b
= CalendarFieldLinks
;
101 if( b
->type
== field
->type
) {
103 std::string
&s
= this->*(b
->strMember
);
104 s
= ParseFieldString(field
);
105 return begin
; // done!
107 else if( b
->timeMember
&& btohs(field
->size
) == 4 ) {
108 time_t &t
= this->*(b
->timeMember
);
109 dout("min1900: " << field
->u
.min1900
);
110 t
= min2time(field
->u
.min1900
);
116 // handle special cases
117 switch( field
->type
)
119 case CALFC_APPT_TYPE_FLAG
:
120 switch( field
->u
.raw
[0] )
122 case 'a': // regular non-recurring appointment
126 case '*': // recurring appointment
131 throw Error("Calendar::ParseField: unknown appointment type");
135 case CALFC_ALLDAYEVENT_FLAG
:
136 AllDayEvent
= field
->u
.raw
[0] == 1;
139 case CALFC_RECURRENCE_DATA
:
140 if( btohs(field
->size
) >= CALENDAR_RECURRENCE_DATA_FIELD_SIZE
) {
142 ParseRecurrenceData(&field
->u
.raw
[0]);
146 throw Error("Calendar::ParseField: not enough data in recurrence data field");
150 case CALFC_TIMEZONE_CODE
:
151 if( btohs(field
->size
) == 2 ) {
153 TimeZoneCode
= btohs(field
->u
.code
);
156 throw Error("Calendar::ParseField: not enough data in time zone code field");
160 case CALFC_FREEBUSY_FLAG
:
161 FreeBusyFlag
= (FreeBusyFlagType
)field
->u
.raw
[0];
162 if( FreeBusyFlag
> OutOfOffice
) {
163 throw Error("Calendar::ParseField: FreeBusyFlag out of range" );
167 case CALFC_CLASS_FLAG
:
168 ClassFlag
= (ClassFlagType
)field
->u
.raw
[0];
169 if( ClassFlag
> Private
) {
170 throw Error("Calendar::ParseField: ClassFlag out of range" );
175 // if still not handled, add to the Unknowns list
177 uf
.type
= field
->type
;
178 uf
.data
.assign((const char*)field
->u
.raw
, btohs(field
->size
));
179 Unknowns
.push_back(uf
);
181 // return new pointer for next field
185 // this function assumes the size has already been checked
186 void Calendar::ParseRecurrenceData(const void *data
)
188 const CalendarRecurrenceDataField
*rec
=
189 (const CalendarRecurrenceDataField
*) data
;
191 Interval
= btohs(rec
->interval
);
193 Interval
= 1; // must always be >= 1
195 if( rec
->endTime
== 0xffffffff ) {
199 RecurringEndTime
= min2time(rec
->endTime
);
210 case CRDF_TYPE_MONTH_BY_DATE
:
211 RecurringType
= MonthByDate
;
212 DayOfMonth
= rec
->u
.month_by_date
.monthDay
;
215 case CRDF_TYPE_MONTH_BY_DAY
:
216 RecurringType
= MonthByDay
;
217 DayOfWeek
= rec
->u
.month_by_day
.weekDay
;
218 WeekOfMonth
= rec
->u
.month_by_day
.week
;
221 case CRDF_TYPE_YEAR_BY_DATE
:
222 RecurringType
= YearByDate
;
223 DayOfMonth
= rec
->u
.year_by_date
.monthDay
;
224 MonthOfYear
= rec
->u
.year_by_date
.month
;
227 case CRDF_TYPE_YEAR_BY_DAY
:
228 RecurringType
= YearByDay
;
229 DayOfWeek
= rec
->u
.year_by_day
.weekDay
;
230 WeekOfMonth
= rec
->u
.year_by_day
.week
;
231 MonthOfYear
= rec
->u
.year_by_day
.month
;
235 RecurringType
= Week
;
237 // Note: this simple copy is only possible since
238 // the CAL_WD_* constants are the same as CRDF_WD_* constants.
239 // If this ever changes, this code will need to change.
240 WeekDays
= rec
->u
.week
.days
;
244 eout("Unknown recurrence data type: " << rec
->type
);
245 throw Error("Unknown recurrence data type");
249 // this function assumes there is CALENDAR_RECURRENCE_DATA_FIELD_SIZE bytes
251 void Calendar::BuildRecurrenceData(void *data
) const
254 throw Error("Calendar::BuildRecurrenceData: Attempting to build recurrence data on non-recurring record.");
256 CalendarRecurrenceDataField
*rec
= (CalendarRecurrenceDataField
*) data
;
259 memset(data
, 0, CALENDAR_RECURRENCE_DATA_FIELD_SIZE
);
261 rec
->interval
= htobs(Interval
);
262 rec
->startTime
= time2min(StartTime
);
264 rec
->endTime
= 0xffffffff;
266 rec
->endTime
= time2min(RecurringEndTime
);
268 switch( RecurringType
)
271 rec
->type
= CRDF_TYPE_DAY
;
276 rec
->type
= CRDF_TYPE_MONTH_BY_DATE
;
277 rec
->u
.month_by_date
.monthDay
= DayOfMonth
;
281 rec
->type
= CRDF_TYPE_MONTH_BY_DAY
;
282 rec
->u
.month_by_day
.weekDay
= DayOfWeek
;
283 rec
->u
.month_by_day
.week
= WeekOfMonth
;
287 rec
->type
= CRDF_TYPE_YEAR_BY_DATE
;
288 rec
->u
.year_by_date
.monthDay
= DayOfMonth
;
289 rec
->u
.year_by_date
.month
= MonthOfYear
;
293 rec
->type
= CRDF_TYPE_YEAR_BY_DAY
;
294 rec
->u
.year_by_day
.weekDay
= DayOfWeek
;
295 rec
->u
.year_by_day
.week
= WeekOfMonth
;
296 rec
->u
.year_by_day
.month
= MonthOfYear
;
300 rec
->type
= CRDF_TYPE_WEEK
;
302 // Note: this simple copy is only possible since
303 // the CAL_WD_* constants are the same as CRDF_WD_* constants.
304 // If this ever changes, this code will need to change.
305 rec
->u
.week
.days
= WeekDays
;
309 eout("Calendar::BuildRecurrenceData: "
310 "Unknown recurrence data type: " << rec
->type
);
311 throw Error("Calendar::BuildRecurrenceData: Unknown recurrence data type");
315 void Calendar::ParseHeader(const Data
&data
, size_t &offset
)
317 // no header in Calendar records
320 void Calendar::ParseFields(const Data
&data
, size_t &offset
)
322 const unsigned char *finish
= ParseCommonFields(*this,
323 data
.GetData() + offset
, data
.GetData() + data
.GetSize());
324 offset
+= finish
- (data
.GetData() + offset
);
327 void Calendar::BuildHeader(Data
&data
, size_t &offset
) const
329 // no header in Calendar records
335 /// Build fields part of record.
337 void Calendar::BuildFields(Data
&data
, size_t &offset
) const
341 // output the type first
342 BuildField(data
, offset
, CALFC_APPT_TYPE_FLAG
, Recurring
? '*' : 'a');
344 // output all day event flag only if set
346 BuildField(data
, offset
, CALFC_ALLDAYEVENT_FLAG
, (char)1);
348 // cycle through the type table
349 for( const FieldLink
<Calendar
> *b
= CalendarFieldLinks
;
350 b
->type
!= CALFC_END
;
354 const std::string
&s
= this->*(b
->strMember
);
356 BuildField(data
, offset
, b
->type
, s
);
358 else if( b
->timeMember
) {
359 time_t t
= this->*(b
->timeMember
);
361 BuildField1900(data
, offset
, b
->type
, t
);
365 // handle special cases
368 CalendarRecurrenceDataField recur
;
369 BuildRecurrenceData(&recur
);
370 BuildField(data
, offset
, CALFC_RECURRENCE_DATA
,
371 &recur
, CALENDAR_RECURRENCE_DATA_FIELD_SIZE
);
375 BuildField(data
, offset
, CALFC_TIMEZONE_CODE
, TimeZoneCode
);
377 BuildField(data
, offset
, CALFC_FREEBUSY_FLAG
, (char)FreeBusyFlag
);
378 BuildField(data
, offset
, CALFC_CLASS_FLAG
, (char)ClassFlag
);
380 // and finally save unknowns
381 UnknownsType::const_iterator
382 ub
= Unknowns
.begin(), ue
= Unknowns
.end();
383 for( ; ub
!= ue
; ub
++ ) {
384 BuildField(data
, offset
, *ub
);
387 data
.ReleaseBuffer(offset
);
390 void Calendar::Clear()
392 RecType
= Calendar::GetDefaultRecType();
398 NotificationTime
= StartTime
= EndTime
= 0;
404 RecurringType
= Calendar::Week
;
406 RecurringEndTime
= 0;
408 TimeZoneCode
= GetTimeZoneCode(0, 0); // default to GMT
409 TimeZoneValid
= false;
410 DayOfWeek
= WeekOfMonth
= DayOfMonth
= MonthOfYear
= 0;
416 void Calendar::Dump(std::ostream
&os
) const
418 static const char *DayNames
[] = { "Sun", "Mon", "Tue", "Wed",
419 "Thu", "Fri", "Sat" };
420 static const char *MonthNames
[] = { "Jan", "Feb", "Mar", "Apr",
421 "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
422 static const char *ClassTypes
[] = { "Public", "Confidential", "Private" };
423 static const char *FreeBusy
[] = { "Free", "Tentative", "Busy", "Out of Office" };
425 // FIXME - need a "check all data" function that make sure that all
426 // recurrence data is within range. Then call that before using
427 // the data, such as in Build and in Dump.
429 os
<< "Calendar entry: 0x" << setbase(16) << RecordId
430 << " (" << (unsigned int)RecType
<< ")\n";
431 os
<< " All Day Event: " << (AllDayEvent
? "yes" : "no") << "\n";
432 os
<< " Class: " << ClassTypes
[ClassFlag
] << "\n";
433 os
<< " Free/Busy: " << FreeBusy
[FreeBusyFlag
] << "\n";
435 os
<< " Time Zone: " << GetTimeZone(TimeZoneCode
)->Name
<< "\n";
437 // cycle through the type table
438 for( const FieldLink
<Calendar
> *b
= CalendarFieldLinks
;
439 b
->type
!= CALFC_END
;
443 const std::string
&s
= this->*(b
->strMember
);
445 os
<< " " << b
->name
<< ": " << s
<< "\n";
447 else if( b
->timeMember
) {
448 time_t t
= this->*(b
->timeMember
);
450 os
<< " " << b
->name
<< ": " << ctime(&t
);
452 os
<< " " << b
->name
<< ": disabled\n";
456 // print recurrence data if available
457 os
<< " Recurring: " << (Recurring
? "yes" : "no") << "\n";
459 switch( RecurringType
)
462 os
<< " Every day.\n";
466 os
<< " Every month on the "
468 << (DayOfMonth
== 1 ? "st" : "")
469 << (DayOfMonth
== 2 ? "nd" : "")
470 << (DayOfMonth
== 3 ? "rd" : "")
471 << (DayOfMonth
> 3 ? "th" : "")
476 os
<< " Every month on the "
477 << DayNames
[DayOfWeek
]
484 os
<< " Every year on "
485 << MonthNames
[MonthOfYear
-1]
486 << " " << DayOfMonth
<< "\n";
490 os
<< " Every year in " << MonthNames
[MonthOfYear
-1]
492 << DayNames
[DayOfWeek
]
493 << " of week " << WeekOfMonth
<< "\n";
497 os
<< " Every week on: ";
498 if( WeekDays
& CAL_WD_SUN
) os
<< "Sun ";
499 if( WeekDays
& CAL_WD_MON
) os
<< "Mon ";
500 if( WeekDays
& CAL_WD_TUE
) os
<< "Tue ";
501 if( WeekDays
& CAL_WD_WED
) os
<< "Wed ";
502 if( WeekDays
& CAL_WD_THU
) os
<< "Thu ";
503 if( WeekDays
& CAL_WD_FRI
) os
<< "Fri ";
504 if( WeekDays
& CAL_WD_SAT
) os
<< "Sat ";
509 os
<< " Unknown recurrence type\n";
513 os
<< " Interval: " << Interval
<< "\n";
516 os
<< " Ends: never\n";
519 << ctime(&RecurringEndTime
);
522 // print any unknowns