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"
36 #define __DEBUG_MODE__
40 using namespace Barry::Protocol
;
45 ///////////////////////////////////////////////////////////////////////////////
48 // calendar field codes
49 #define CALFC_APPT_TYPE_FLAG 0x01
50 #define CALFC_SUBJECT 0x02
51 #define CALFC_NOTES 0x03
52 #define CALFC_LOCATION 0x04
53 #define CALFC_NOTIFICATION_TIME 0x05
54 #define CALFC_START_TIME 0x06
55 #define CALFC_END_TIME 0x07
56 #define CALFC_RECURRENCE_DATA 0x0c
57 #define CALFC_VERSION_DATA 0x10
58 #define CALFC_NOTIFICATION_DATA 0x1a
59 #define CALFC_FREEBUSY_FLAG 0x1c
60 #define CALFC_TIMEZONE_CODE 0x1e // only seems to show up if recurring
61 #define CALFC_CLASS_FLAG 0x28 // private flag from outlook
62 #define CALFC_ALLDAYEVENT_FLAG 0xff
63 #define CALFC_END 0xffff
65 FieldLink
<Calendar
> CalendarFieldLinks
[] = {
66 { CALFC_SUBJECT
, "Subject", 0, 0, &Calendar::Subject
, 0, 0 },
67 { CALFC_NOTES
, "Notes", 0, 0, &Calendar::Notes
, 0, 0 },
68 { CALFC_LOCATION
, "Location", 0, 0, &Calendar::Location
, 0, 0 },
69 { CALFC_NOTIFICATION_TIME
,"Notification Time",0,0, 0, 0, &Calendar::NotificationTime
},
70 { CALFC_START_TIME
, "Start Time", 0, 0, 0, 0, &Calendar::StartTime
},
71 { CALFC_END_TIME
, "End Time", 0, 0, 0, 0, &Calendar::EndTime
},
72 { CALFC_END
, "End of List",0, 0, 0, 0, 0 }
84 const unsigned char* Calendar::ParseField(const unsigned char *begin
,
85 const unsigned char *end
)
87 const CommonField
*field
= (const CommonField
*) begin
;
89 // advance and check size
90 begin
+= COMMON_FIELD_HEADER_SIZE
+ btohs(field
->size
);
91 if( begin
> end
) // if begin==end, we are ok
94 if( !btohs(field
->size
) ) // if field has no size, something's up
97 // cycle through the type table
98 for( FieldLink
<Calendar
> *b
= CalendarFieldLinks
;
102 if( b
->type
== field
->type
) {
104 std::string
&s
= this->*(b
->strMember
);
105 s
= ParseFieldString(field
);
106 return begin
; // done!
108 else if( b
->timeMember
&& btohs(field
->size
) == 4 ) {
109 time_t &t
= this->*(b
->timeMember
);
110 dout("min1900: " << field
->u
.min1900
);
111 t
= min2time(field
->u
.min1900
);
117 // handle special cases
118 switch( field
->type
)
120 case CALFC_APPT_TYPE_FLAG
:
121 switch( field
->u
.raw
[0] )
123 case 'a': // regular non-recurring appointment
127 case '*': // recurring appointment
132 throw Error("Calendar::ParseField: unknown appointment type");
136 case CALFC_ALLDAYEVENT_FLAG
:
137 AllDayEvent
= field
->u
.raw
[0] == 1;
140 case CALFC_RECURRENCE_DATA
:
141 if( btohs(field
->size
) >= CALENDAR_RECURRENCE_DATA_FIELD_SIZE
) {
143 ParseRecurrenceData(&field
->u
.raw
[0]);
147 throw Error("Calendar::ParseField: not enough data in recurrence data field");
151 case CALFC_TIMEZONE_CODE
:
152 if( btohs(field
->size
) == 2 ) {
154 TimeZoneCode
= btohs(field
->u
.code
);
157 throw Error("Calendar::ParseField: not enough data in time zone code field");
161 case CALFC_FREEBUSY_FLAG
:
162 FreeBusyFlag
= (FreeBusyFlagType
)field
->u
.raw
[0];
163 if( FreeBusyFlag
> OutOfOffice
) {
164 throw Error("Calendar::ParseField: FreeBusyFlag out of range" );
168 case CALFC_CLASS_FLAG
:
169 ClassFlag
= (ClassFlagType
)field
->u
.raw
[0];
170 if( ClassFlag
> Private
) {
171 throw Error("Calendar::ParseField: ClassFlag out of range" );
176 // if still not handled, add to the Unknowns list
178 uf
.type
= field
->type
;
179 uf
.data
.assign((const char*)field
->u
.raw
, btohs(field
->size
));
180 Unknowns
.push_back(uf
);
182 // return new pointer for next field
186 // this function assumes the size has already been checked
187 void Calendar::ParseRecurrenceData(const void *data
)
189 const CalendarRecurrenceDataField
*rec
=
190 (const CalendarRecurrenceDataField
*) data
;
192 Interval
= btohs(rec
->interval
);
194 Interval
= 1; // must always be >= 1
196 if( rec
->endTime
== 0xffffffff ) {
200 RecurringEndTime
= min2time(rec
->endTime
);
211 case CRDF_TYPE_MONTH_BY_DATE
:
212 RecurringType
= MonthByDate
;
213 DayOfMonth
= rec
->u
.month_by_date
.monthDay
;
216 case CRDF_TYPE_MONTH_BY_DAY
:
217 RecurringType
= MonthByDay
;
218 DayOfWeek
= rec
->u
.month_by_day
.weekDay
;
219 WeekOfMonth
= rec
->u
.month_by_day
.week
;
222 case CRDF_TYPE_YEAR_BY_DATE
:
223 RecurringType
= YearByDate
;
224 DayOfMonth
= rec
->u
.year_by_date
.monthDay
;
225 MonthOfYear
= rec
->u
.year_by_date
.month
;
228 case CRDF_TYPE_YEAR_BY_DAY
:
229 RecurringType
= YearByDay
;
230 DayOfWeek
= rec
->u
.year_by_day
.weekDay
;
231 WeekOfMonth
= rec
->u
.year_by_day
.week
;
232 MonthOfYear
= rec
->u
.year_by_day
.month
;
236 RecurringType
= Week
;
238 // Note: this simple copy is only possible since
239 // the CAL_WD_* constants are the same as CRDF_WD_* constants.
240 // If this ever changes, this code will need to change.
241 WeekDays
= rec
->u
.week
.days
;
245 eout("Unknown recurrence data type: " << rec
->type
);
246 throw Error("Unknown recurrence data type");
250 // this function assumes there is CALENDAR_RECURRENCE_DATA_FIELD_SIZE bytes
252 void Calendar::BuildRecurrenceData(void *data
) const
255 throw Error("Calendar::BuildRecurrenceData: Attempting to build recurrence data on non-recurring record.");
257 CalendarRecurrenceDataField
*rec
= (CalendarRecurrenceDataField
*) data
;
260 memset(data
, 0, CALENDAR_RECURRENCE_DATA_FIELD_SIZE
);
262 rec
->interval
= htobs(Interval
);
263 rec
->startTime
= time2min(StartTime
);
265 rec
->endTime
= 0xffffffff;
267 rec
->endTime
= time2min(RecurringEndTime
);
269 switch( RecurringType
)
272 rec
->type
= CRDF_TYPE_DAY
;
277 rec
->type
= CRDF_TYPE_MONTH_BY_DATE
;
278 rec
->u
.month_by_date
.monthDay
= DayOfMonth
;
282 rec
->type
= CRDF_TYPE_MONTH_BY_DAY
;
283 rec
->u
.month_by_day
.weekDay
= DayOfWeek
;
284 rec
->u
.month_by_day
.week
= WeekOfMonth
;
288 rec
->type
= CRDF_TYPE_YEAR_BY_DATE
;
289 rec
->u
.year_by_date
.monthDay
= DayOfMonth
;
290 rec
->u
.year_by_date
.month
= MonthOfYear
;
294 rec
->type
= CRDF_TYPE_YEAR_BY_DAY
;
295 rec
->u
.year_by_day
.weekDay
= DayOfWeek
;
296 rec
->u
.year_by_day
.week
= WeekOfMonth
;
297 rec
->u
.year_by_day
.month
= MonthOfYear
;
301 rec
->type
= CRDF_TYPE_WEEK
;
303 // Note: this simple copy is only possible since
304 // the CAL_WD_* constants are the same as CRDF_WD_* constants.
305 // If this ever changes, this code will need to change.
306 rec
->u
.week
.days
= WeekDays
;
310 eout("Calendar::BuildRecurrenceData: "
311 "Unknown recurrence data type: " << rec
->type
);
312 throw Error("Calendar::BuildRecurrenceData: Unknown recurrence data type");
316 void Calendar::ParseHeader(const Data
&data
, size_t &offset
)
318 // no header in Calendar records
321 void Calendar::ParseFields(const Data
&data
, size_t &offset
)
323 const unsigned char *finish
= ParseCommonFields(*this,
324 data
.GetData() + offset
, data
.GetData() + data
.GetSize());
325 offset
+= finish
- (data
.GetData() + offset
);
328 void Calendar::BuildHeader(Data
&data
, size_t &offset
) const
330 // no header in Calendar records
336 /// Build fields part of record.
338 void Calendar::BuildFields(Data
&data
, size_t &offset
) const
342 // output the type first
343 BuildField(data
, offset
, CALFC_APPT_TYPE_FLAG
, Recurring
? '*' : 'a');
345 // output all day event flag only if set
347 BuildField(data
, offset
, CALFC_ALLDAYEVENT_FLAG
, (char)1);
349 // cycle through the type table
350 for( const FieldLink
<Calendar
> *b
= CalendarFieldLinks
;
351 b
->type
!= CALFC_END
;
355 const std::string
&s
= this->*(b
->strMember
);
357 BuildField(data
, offset
, b
->type
, s
);
359 else if( b
->timeMember
) {
360 time_t t
= this->*(b
->timeMember
);
362 BuildField1900(data
, offset
, b
->type
, t
);
366 // handle special cases
369 CalendarRecurrenceDataField recur
;
370 BuildRecurrenceData(&recur
);
371 BuildField(data
, offset
, CALFC_RECURRENCE_DATA
,
372 &recur
, CALENDAR_RECURRENCE_DATA_FIELD_SIZE
);
376 BuildField(data
, offset
, CALFC_TIMEZONE_CODE
, TimeZoneCode
);
378 BuildField(data
, offset
, CALFC_FREEBUSY_FLAG
, (char)FreeBusyFlag
);
379 BuildField(data
, offset
, CALFC_CLASS_FLAG
, (char)ClassFlag
);
381 // and finally save unknowns
382 UnknownsType::const_iterator
383 ub
= Unknowns
.begin(), ue
= Unknowns
.end();
384 for( ; ub
!= ue
; ub
++ ) {
385 BuildField(data
, offset
, *ub
);
388 data
.ReleaseBuffer(offset
);
391 void Calendar::Clear()
393 RecType
= Calendar::GetDefaultRecType();
399 NotificationTime
= StartTime
= EndTime
= 0;
405 RecurringType
= Calendar::Week
;
407 RecurringEndTime
= 0;
409 TimeZoneCode
= GetTimeZoneCode(0, 0); // default to GMT
410 TimeZoneValid
= false;
411 DayOfWeek
= WeekOfMonth
= DayOfMonth
= MonthOfYear
= 0;
417 void Calendar::Dump(std::ostream
&os
) const
419 static const char *DayNames
[] = { "Sun", "Mon", "Tue", "Wed",
420 "Thu", "Fri", "Sat" };
421 static const char *MonthNames
[] = { "Jan", "Feb", "Mar", "Apr",
422 "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
423 static const char *ClassTypes
[] = { "Public", "Confidential", "Private" };
424 static const char *FreeBusy
[] = { "Free", "Tentative", "Busy", "Out of Office" };
426 // FIXME - need a "check all data" function that make sure that all
427 // recurrence data is within range. Then call that before using
428 // the data, such as in Build and in Dump.
430 os
<< "Calendar entry: 0x" << setbase(16) << RecordId
431 << " (" << (unsigned int)RecType
<< ")\n";
432 os
<< " All Day Event: " << (AllDayEvent
? "yes" : "no") << "\n";
433 os
<< " Class: " << ClassTypes
[ClassFlag
] << "\n";
434 os
<< " Free/Busy: " << FreeBusy
[FreeBusyFlag
] << "\n";
436 os
<< " Time Zone: " << GetTimeZone(TimeZoneCode
)->Name
<< "\n";
438 // cycle through the type table
439 for( const FieldLink
<Calendar
> *b
= CalendarFieldLinks
;
440 b
->type
!= CALFC_END
;
444 const std::string
&s
= this->*(b
->strMember
);
446 os
<< " " << b
->name
<< ": " << s
<< "\n";
448 else if( b
->timeMember
) {
449 time_t t
= this->*(b
->timeMember
);
451 os
<< " " << b
->name
<< ": " << ctime(&t
);
453 os
<< " " << b
->name
<< ": disabled\n";
457 // print recurrence data if available
458 os
<< " Recurring: " << (Recurring
? "yes" : "no") << "\n";
460 switch( RecurringType
)
463 os
<< " Every day.\n";
467 os
<< " Every month on the "
469 << (DayOfMonth
== 1 ? "st" : "")
470 << (DayOfMonth
== 2 ? "nd" : "")
471 << (DayOfMonth
== 3 ? "rd" : "")
472 << (DayOfMonth
> 3 ? "th" : "")
477 os
<< " Every month on the "
478 << DayNames
[DayOfWeek
]
485 os
<< " Every year on "
486 << MonthNames
[MonthOfYear
-1]
487 << " " << DayOfMonth
<< "\n";
491 os
<< " Every year in " << MonthNames
[MonthOfYear
-1]
493 << DayNames
[DayOfWeek
]
494 << " of week " << WeekOfMonth
<< "\n";
498 os
<< " Every week on: ";
499 if( WeekDays
& CAL_WD_SUN
) os
<< "Sun ";
500 if( WeekDays
& CAL_WD_MON
) os
<< "Mon ";
501 if( WeekDays
& CAL_WD_TUE
) os
<< "Tue ";
502 if( WeekDays
& CAL_WD_WED
) os
<< "Wed ";
503 if( WeekDays
& CAL_WD_THU
) os
<< "Thu ";
504 if( WeekDays
& CAL_WD_FRI
) os
<< "Fri ";
505 if( WeekDays
& CAL_WD_SAT
) os
<< "Sat ";
510 os
<< " Unknown recurrence type\n";
514 os
<< " Interval: " << Interval
<< "\n";
517 os
<< " Ends: never\n";
520 << ctime(&RecurringEndTime
);
523 // print any unknowns