2 /// \file r_calendar.cc
3 /// Blackberry database record parser class for calendar records.
7 Copyright (C) 2005-2007, 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
.assign((const char *)field
->u
.raw
, btohs(field
->size
)-1);
105 return begin
; // done!
107 else if( b
->timeMember
&& btohs(field
->size
) == 4 ) {
108 time_t &t
= this->*(b
->timeMember
);
109 t
= min2time(field
->u
.min1900
);
115 // handle special cases
116 switch( field
->type
)
118 case CALFC_APPT_TYPE_FLAG
:
119 switch( field
->u
.raw
[0] )
121 case 'a': // regular non-recurring appointment
125 case '*': // recurring appointment
130 throw Error("Calendar::ParseField: unknown appointment type");
134 case CALFC_ALLDAYEVENT_FLAG
:
135 AllDayEvent
= field
->u
.raw
[0] == 1;
138 case CALFC_RECURRENCE_DATA
:
139 if( btohs(field
->size
) >= CALENDAR_RECURRENCE_DATA_FIELD_SIZE
) {
141 ParseRecurrenceData(&field
->u
.raw
[0]);
145 throw Error("Calendar::ParseField: not enough data in recurrence data field");
149 case CALFC_TIMEZONE_CODE
:
150 if( btohs(field
->size
) == 2 ) {
152 TimeZoneCode
= btohs(field
->u
.code
);
155 throw Error("Calendar::ParseField: not enough data in time zone code field");
159 case CALFC_FREEBUSY_FLAG
:
160 FreeBusyFlag
= (FreeBusyFlagType
)field
->u
.raw
[0];
161 if( FreeBusyFlag
> OutOfOffice
) {
162 throw Error("Calendar::ParseField: FreeBusyFlag out of range" );
166 case CALFC_CLASS_FLAG
:
167 ClassFlag
= (ClassFlagType
)field
->u
.raw
[0];
168 if( ClassFlag
> Private
) {
169 throw Error("Calendar::ParseField: ClassFlag out of range" );
174 // if still not handled, add to the Unknowns list
176 uf
.type
= field
->type
;
177 uf
.data
.assign((const char*)field
->u
.raw
, btohs(field
->size
));
178 Unknowns
.push_back(uf
);
180 // return new pointer for next field
184 // this function assumes the size has already been checked
185 void Calendar::ParseRecurrenceData(const void *data
)
187 const CalendarRecurrenceDataField
*rec
=
188 (const CalendarRecurrenceDataField
*) data
;
190 Interval
= btohs(rec
->interval
);
192 Interval
= 1; // must always be >= 1
194 if( rec
->endTime
== 0xffffffff ) {
198 RecurringEndTime
= min2time(rec
->endTime
);
209 case CRDF_TYPE_MONTH_BY_DATE
:
210 RecurringType
= MonthByDate
;
211 DayOfMonth
= rec
->u
.month_by_date
.monthDay
;
214 case CRDF_TYPE_MONTH_BY_DAY
:
215 RecurringType
= MonthByDay
;
216 DayOfWeek
= rec
->u
.month_by_day
.weekDay
;
217 WeekOfMonth
= rec
->u
.month_by_day
.week
;
220 case CRDF_TYPE_YEAR_BY_DATE
:
221 RecurringType
= YearByDate
;
222 DayOfMonth
= rec
->u
.year_by_date
.monthDay
;
223 MonthOfYear
= rec
->u
.year_by_date
.month
;
226 case CRDF_TYPE_YEAR_BY_DAY
:
227 RecurringType
= YearByDay
;
228 DayOfWeek
= rec
->u
.year_by_day
.weekDay
;
229 WeekOfMonth
= rec
->u
.year_by_day
.week
;
230 MonthOfYear
= rec
->u
.year_by_day
.month
;
234 RecurringType
= Week
;
236 // Note: this simple copy is only possible since
237 // the CAL_WD_* constants are the same as CRDF_WD_* constants.
238 // If this ever changes, this code will need to change.
239 WeekDays
= rec
->u
.week
.days
;
243 eout("Unknown recurrence data type: " << rec
->type
);
244 throw Error("Unknown recurrence data type");
248 // this function assumes there is CALENDAR_RECURRENCE_DATA_FIELD_SIZE bytes
250 void Calendar::BuildRecurrenceData(void *data
)
253 throw Error("Calendar::BuildRecurrenceData: Attempting to build recurrence data on non-recurring record.");
255 CalendarRecurrenceDataField
*rec
= (CalendarRecurrenceDataField
*) data
;
258 memset(data
, 0, CALENDAR_RECURRENCE_DATA_FIELD_SIZE
);
260 rec
->interval
= htobs(Interval
);
261 rec
->startTime
= time2min(StartTime
);
263 rec
->endTime
= 0xffffffff;
265 rec
->endTime
= time2min(RecurringEndTime
);
267 switch( RecurringType
)
270 rec
->type
= CRDF_TYPE_DAY
;
275 rec
->type
= CRDF_TYPE_MONTH_BY_DATE
;
276 rec
->u
.month_by_date
.monthDay
= DayOfMonth
;
280 rec
->type
= CRDF_TYPE_MONTH_BY_DAY
;
281 rec
->u
.month_by_day
.weekDay
= DayOfWeek
;
282 rec
->u
.month_by_day
.week
= WeekOfMonth
;
286 rec
->type
= CRDF_TYPE_YEAR_BY_DATE
;
287 rec
->u
.year_by_date
.monthDay
= DayOfMonth
;
288 rec
->u
.year_by_date
.month
= MonthOfYear
;
292 rec
->type
= CRDF_TYPE_YEAR_BY_DAY
;
293 rec
->u
.year_by_day
.weekDay
= DayOfWeek
;
294 rec
->u
.year_by_day
.week
= WeekOfMonth
;
295 rec
->u
.year_by_day
.month
= MonthOfYear
;
299 rec
->type
= CRDF_TYPE_WEEK
;
301 // Note: this simple copy is only possible since
302 // the CAL_WD_* constants are the same as CRDF_WD_* constants.
303 // If this ever changes, this code will need to change.
304 rec
->u
.week
.days
= WeekDays
;
308 eout("Calendar::BuildRecurrenceData: "
309 "Unknown recurrence data type: " << rec
->type
);
310 throw Error("Calendar::BuildRecurrenceData: Unknown recurrence data type");
314 void Calendar::ParseHeader(const Data
&data
, size_t &offset
)
316 // no header in Calendar records
319 void Calendar::ParseFields(const Data
&data
, size_t &offset
)
321 const unsigned char *finish
= ParseCommonFields(*this,
322 data
.GetData() + offset
, data
.GetData() + data
.GetSize());
323 offset
+= finish
- (data
.GetData() + offset
);
326 void Calendar::BuildHeader(Data
&data
, size_t &offset
) const
328 // no header in Calendar records
334 /// Build fields part of record.
336 void Calendar::BuildFields(Data
&data
, size_t &offset
) const
340 // output the type first
341 BuildField(data
, offset
, CALFC_APPT_TYPE_FLAG
, Recurring
? '*' : 'a');
343 // output all day event flag only if set
345 BuildField(data
, offset
, CALFC_ALLDAYEVENT_FLAG
, (char)1);
347 // cycle through the type table
348 for( const FieldLink
<Calendar
> *b
= CalendarFieldLinks
;
349 b
->type
!= CALFC_END
;
353 const std::string
&s
= this->*(b
->strMember
);
355 BuildField(data
, offset
, b
->type
, s
);
357 else if( b
->timeMember
) {
358 time_t t
= this->*(b
->timeMember
);
360 BuildField1900(data
, offset
, b
->type
, t
);
364 // and finally save unknowns
365 UnknownsType::const_iterator
366 ub
= Unknowns
.begin(), ue
= Unknowns
.end();
367 for( ; ub
!= ue
; ub
++ ) {
368 BuildField(data
, offset
, ub
->type
, ub
->data
);
371 data
.ReleaseBuffer(offset
);
374 void Calendar::Clear()
376 RecType
= Calendar::GetDefaultRecType();
382 NotificationTime
= StartTime
= EndTime
= 0;
385 RecurringType
= Calendar::Week
;
387 RecurringEndTime
= 0;
389 TimeZoneCode
= GetTimeZoneCode(0, 0); // default to GMT
390 DayOfWeek
= WeekOfMonth
= DayOfMonth
= MonthOfYear
= 0;
396 void Calendar::Dump(std::ostream
&os
) const
398 static const char *DayNames
[] = { "Sun", "Mon", "Tue", "Wed",
399 "Thu", "Fri", "Sat" };
400 static const char *MonthNames
[] = { "Jan", "Feb", "Mar", "Apr",
401 "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
402 static const char *ClassTypes
[] = { "Public", "Confidential", "Private" };
403 static const char *FreeBusy
[] = { "Free", "Tentative", "Busy", "Out of Office" };
405 // FIXME - need a "check all data" function that make sure that all
406 // recurrence data is within range. Then call that before using
407 // the data, such as in Build and in Dump.
409 os
<< "Calendar entry: 0x" << setbase(16) << RecordId
410 << " (" << (unsigned int)RecType
<< ")\n";
411 os
<< " All Day Event: " << (AllDayEvent
? "yes" : "no") << "\n";
412 os
<< " Class: " << ClassTypes
[ClassFlag
] << "\n";
413 os
<< " Free/Busy: " << FreeBusy
[FreeBusyFlag
] << "\n";
415 // cycle through the type table
416 for( const FieldLink
<Calendar
> *b
= CalendarFieldLinks
;
417 b
->type
!= CALFC_END
;
421 const std::string
&s
= this->*(b
->strMember
);
423 os
<< " " << b
->name
<< ": " << s
<< "\n";
425 else if( b
->timeMember
) {
426 time_t t
= this->*(b
->timeMember
);
428 os
<< " " << b
->name
<< ": " << ctime(&t
);
432 // print recurrence data if available
433 os
<< " Recurring: " << (Recurring
? "yes" : "no") << "\n";
435 switch( RecurringType
)
438 os
<< " Every day.\n";
442 os
<< " Every month on the "
444 << (DayOfMonth
== 1 ? "st" : "")
445 << (DayOfMonth
== 2 ? "nd" : "")
446 << (DayOfMonth
== 3 ? "rd" : "")
447 << (DayOfMonth
> 3 ? "th" : "")
452 os
<< " Every month on the "
453 << DayNames
[DayOfWeek
]
460 os
<< " Every year on "
461 << MonthNames
[MonthOfYear
-1]
462 << " " << DayOfMonth
<< "\n";
466 os
<< " Every year in " << MonthNames
[MonthOfYear
-1]
468 << DayNames
[DayOfWeek
]
469 << " of week " << WeekOfMonth
<< "\n";
473 os
<< " Every week on: ";
474 if( WeekDays
& CAL_WD_SUN
) os
<< "Sun ";
475 if( WeekDays
& CAL_WD_MON
) os
<< "Mon ";
476 if( WeekDays
& CAL_WD_TUE
) os
<< "Tue ";
477 if( WeekDays
& CAL_WD_WED
) os
<< "Wed ";
478 if( WeekDays
& CAL_WD_THU
) os
<< "Thu ";
479 if( WeekDays
& CAL_WD_FRI
) os
<< "Fri ";
480 if( WeekDays
& CAL_WD_SAT
) os
<< "Sat ";
485 os
<< " Unknown recurrence type\n";
489 os
<< " Interval: " << Interval
<< "\n";
492 os
<< " Ends: never\n";
495 << ctime(&RecurringEndTime
);
498 // print any unknowns