2 /// \file r_calendar.cc
3 /// Blackberry database record parser class for calendar records.
7 Copyright (C) 2005-2009, 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"
37 #define __DEBUG_MODE__
41 using namespace Barry::Protocol
;
46 ///////////////////////////////////////////////////////////////////////////////
49 // calendar field codes
50 #define CALFC_APPT_TYPE_FLAG 0x01
51 #define CALFC_SUBJECT 0x02
52 #define CALFC_NOTES 0x03
53 #define CALFC_LOCATION 0x04
54 #define CALFC_NOTIFICATION_TIME 0x05
55 #define CALFC_START_TIME 0x06
56 #define CALFC_END_TIME 0x07
57 #define CALFC_RECURRENCE_DATA 0x0c
58 #define CALFC_VERSION_DATA 0x10
59 #define CALFC_NOTIFICATION_DATA 0x1a
60 #define CALFC_FREEBUSY_FLAG 0x1c
61 #define CALFC_TIMEZONE_CODE 0x1e // only seems to show up if recurring
62 #define CALFC_CLASS_FLAG 0x28 // private flag from outlook
63 #define CALFC_ALLDAYEVENT_FLAG 0xff
64 #define CALFC_END 0xffff
66 static FieldLink
<Calendar
> CalendarFieldLinks
[] = {
67 { CALFC_SUBJECT
, "Subject", 0, 0, &Calendar::Subject
, 0, 0, 0, 0, true },
68 { CALFC_NOTES
, "Notes", 0, 0, &Calendar::Notes
, 0, 0, 0, 0, true },
69 { CALFC_LOCATION
, "Location", 0, 0, &Calendar::Location
, 0, 0, 0, 0, true },
70 { CALFC_NOTIFICATION_TIME
,"Notification Time",0,0, 0, 0, &Calendar::NotificationTime
, 0, 0, false },
71 { CALFC_START_TIME
, "Start Time", 0, 0, 0, 0, &Calendar::StartTime
, 0, 0, false },
72 { CALFC_END_TIME
, "End Time", 0, 0, 0, 0, &Calendar::EndTime
, 0, 0, false },
73 { CALFC_END
, "End of List",0, 0, 0, 0, 0, 0, 0, false }
85 const unsigned char* Calendar::ParseField(const unsigned char *begin
,
86 const unsigned char *end
,
89 const CommonField
*field
= (const CommonField
*) begin
;
91 // advance and check size
92 begin
+= COMMON_FIELD_HEADER_SIZE
+ btohs(field
->size
);
93 if( begin
> end
) // if begin==end, we are ok
96 if( !btohs(field
->size
) ) // if field has no size, something's up
99 // cycle through the type table
100 for( FieldLink
<Calendar
> *b
= CalendarFieldLinks
;
101 b
->type
!= CALFC_END
;
104 if( b
->type
== field
->type
) {
106 std::string
&s
= this->*(b
->strMember
);
107 s
= ParseFieldString(field
);
108 if( b
->iconvNeeded
&& ic
)
110 return begin
; // done!
112 else if( b
->timeMember
&& btohs(field
->size
) == 4 ) {
113 time_t &t
= this->*(b
->timeMember
);
114 dout("min1900: " << field
->u
.min1900
);
115 t
= min2time(field
->u
.min1900
);
121 // handle special cases
122 switch( field
->type
)
124 case CALFC_APPT_TYPE_FLAG
:
125 switch( field
->u
.raw
[0] )
127 case 'a': // regular non-recurring appointment
131 case '*': // recurring appointment
136 throw Error("Calendar::ParseField: unknown appointment type");
140 case CALFC_ALLDAYEVENT_FLAG
:
141 AllDayEvent
= field
->u
.raw
[0] == 1;
144 case CALFC_RECURRENCE_DATA
:
145 if( btohs(field
->size
) >= CALENDAR_RECURRENCE_DATA_FIELD_SIZE
) {
147 ParseRecurrenceData(&field
->u
.raw
[0]);
151 throw Error("Calendar::ParseField: not enough data in recurrence data field");
155 case CALFC_TIMEZONE_CODE
:
156 if( btohs(field
->size
) == 2 ) {
158 TimeZoneCode
= btohs(field
->u
.code
);
161 throw Error("Calendar::ParseField: not enough data in time zone code field");
165 case CALFC_FREEBUSY_FLAG
:
166 FreeBusyFlag
= (FreeBusyFlagType
)field
->u
.raw
[0];
167 if( FreeBusyFlag
> OutOfOffice
) {
168 throw Error("Calendar::ParseField: FreeBusyFlag out of range" );
172 case CALFC_CLASS_FLAG
:
173 ClassFlag
= (ClassFlagType
)field
->u
.raw
[0];
174 if( ClassFlag
> Private
) {
175 throw Error("Calendar::ParseField: ClassFlag out of range" );
180 // if still not handled, add to the Unknowns list
182 uf
.type
= field
->type
;
183 uf
.data
.assign((const char*)field
->u
.raw
, btohs(field
->size
));
184 Unknowns
.push_back(uf
);
186 // return new pointer for next field
190 // this function assumes the size has already been checked
191 void Calendar::ParseRecurrenceData(const void *data
)
193 const CalendarRecurrenceDataField
*rec
=
194 (const CalendarRecurrenceDataField
*) data
;
196 Interval
= btohs(rec
->interval
);
198 Interval
= 1; // must always be >= 1
200 if( rec
->endTime
== 0xffffffff ) {
204 RecurringEndTime
= min2time(rec
->endTime
);
215 case CRDF_TYPE_MONTH_BY_DATE
:
216 RecurringType
= MonthByDate
;
217 DayOfMonth
= rec
->u
.month_by_date
.monthDay
;
220 case CRDF_TYPE_MONTH_BY_DAY
:
221 RecurringType
= MonthByDay
;
222 DayOfWeek
= rec
->u
.month_by_day
.weekDay
;
223 WeekOfMonth
= rec
->u
.month_by_day
.week
;
226 case CRDF_TYPE_YEAR_BY_DATE
:
227 RecurringType
= YearByDate
;
228 DayOfMonth
= rec
->u
.year_by_date
.monthDay
;
229 MonthOfYear
= rec
->u
.year_by_date
.month
;
232 case CRDF_TYPE_YEAR_BY_DAY
:
233 RecurringType
= YearByDay
;
234 DayOfWeek
= rec
->u
.year_by_day
.weekDay
;
235 WeekOfMonth
= rec
->u
.year_by_day
.week
;
236 MonthOfYear
= rec
->u
.year_by_day
.month
;
240 RecurringType
= Week
;
242 // Note: this simple copy is only possible since
243 // the CAL_WD_* constants are the same as CRDF_WD_* constants.
244 // If this ever changes, this code will need to change.
245 WeekDays
= rec
->u
.week
.days
;
249 eout("Unknown recurrence data type: " << rec
->type
);
250 throw Error("Unknown recurrence data type");
254 // this function assumes there is CALENDAR_RECURRENCE_DATA_FIELD_SIZE bytes
256 void Calendar::BuildRecurrenceData(void *data
) const
259 throw Error("Calendar::BuildRecurrenceData: Attempting to build recurrence data on non-recurring record.");
261 CalendarRecurrenceDataField
*rec
= (CalendarRecurrenceDataField
*) data
;
264 memset(data
, 0, CALENDAR_RECURRENCE_DATA_FIELD_SIZE
);
266 rec
->interval
= htobs(Interval
);
267 rec
->startTime
= time2min(StartTime
);
269 rec
->endTime
= 0xffffffff;
271 rec
->endTime
= time2min(RecurringEndTime
);
273 switch( RecurringType
)
276 rec
->type
= CRDF_TYPE_DAY
;
281 rec
->type
= CRDF_TYPE_MONTH_BY_DATE
;
282 rec
->u
.month_by_date
.monthDay
= DayOfMonth
;
286 rec
->type
= CRDF_TYPE_MONTH_BY_DAY
;
287 rec
->u
.month_by_day
.weekDay
= DayOfWeek
;
288 rec
->u
.month_by_day
.week
= WeekOfMonth
;
292 rec
->type
= CRDF_TYPE_YEAR_BY_DATE
;
293 rec
->u
.year_by_date
.monthDay
= DayOfMonth
;
294 rec
->u
.year_by_date
.month
= MonthOfYear
;
298 rec
->type
= CRDF_TYPE_YEAR_BY_DAY
;
299 rec
->u
.year_by_day
.weekDay
= DayOfWeek
;
300 rec
->u
.year_by_day
.week
= WeekOfMonth
;
301 rec
->u
.year_by_day
.month
= MonthOfYear
;
305 rec
->type
= CRDF_TYPE_WEEK
;
307 // Note: this simple copy is only possible since
308 // the CAL_WD_* constants are the same as CRDF_WD_* constants.
309 // If this ever changes, this code will need to change.
310 rec
->u
.week
.days
= WeekDays
;
314 eout("Calendar::BuildRecurrenceData: "
315 "Unknown recurrence data type: " << rec
->type
);
316 throw Error("Calendar::BuildRecurrenceData: Unknown recurrence data type");
320 void Calendar::ParseHeader(const Data
&data
, size_t &offset
)
322 // no header in Calendar records
325 void Calendar::ParseFields(const Data
&data
, size_t &offset
, const IConverter
*ic
)
327 const unsigned char *finish
= ParseCommonFields(*this,
328 data
.GetData() + offset
, data
.GetData() + data
.GetSize(), ic
);
329 offset
+= finish
- (data
.GetData() + offset
);
332 void Calendar::BuildHeader(Data
&data
, size_t &offset
) const
334 // no header in Calendar records
340 /// Build fields part of record.
342 void Calendar::BuildFields(Data
&data
, size_t &offset
, const IConverter
*ic
) const
346 // output the type first
347 BuildField(data
, offset
, CALFC_APPT_TYPE_FLAG
, Recurring
? '*' : 'a');
349 // output all day event flag only if set
351 BuildField(data
, offset
, CALFC_ALLDAYEVENT_FLAG
, (char)1);
353 // cycle through the type table
354 for( const FieldLink
<Calendar
> *b
= CalendarFieldLinks
;
355 b
->type
!= CALFC_END
;
359 const std::string
&s
= this->*(b
->strMember
);
361 BuildField(data
, offset
, b
->type
, (b
->iconvNeeded
&& ic
) ? ic
->ToBB(s
) : s
);
363 else if( b
->timeMember
) {
364 time_t t
= this->*(b
->timeMember
);
366 BuildField1900(data
, offset
, b
->type
, t
);
370 // handle special cases
373 CalendarRecurrenceDataField recur
;
374 BuildRecurrenceData(&recur
);
375 BuildField(data
, offset
, CALFC_RECURRENCE_DATA
,
376 &recur
, CALENDAR_RECURRENCE_DATA_FIELD_SIZE
);
380 BuildField(data
, offset
, CALFC_TIMEZONE_CODE
, TimeZoneCode
);
382 BuildField(data
, offset
, CALFC_FREEBUSY_FLAG
, (char)FreeBusyFlag
);
383 BuildField(data
, offset
, CALFC_CLASS_FLAG
, (char)ClassFlag
);
385 // and finally save unknowns
386 UnknownsType::const_iterator
387 ub
= Unknowns
.begin(), ue
= Unknowns
.end();
388 for( ; ub
!= ue
; ub
++ ) {
389 BuildField(data
, offset
, *ub
);
392 data
.ReleaseBuffer(offset
);
395 void Calendar::Clear()
397 RecType
= Calendar::GetDefaultRecType();
403 NotificationTime
= StartTime
= EndTime
= 0;
409 RecurringType
= Calendar::Week
;
411 RecurringEndTime
= 0;
413 TimeZoneCode
= GetTimeZoneCode(0, 0); // default to GMT
414 TimeZoneValid
= false;
415 DayOfWeek
= WeekOfMonth
= DayOfMonth
= MonthOfYear
= 0;
421 void Calendar::Dump(std::ostream
&os
) const
423 static const char *DayNames
[] = { "Sun", "Mon", "Tue", "Wed",
424 "Thu", "Fri", "Sat" };
425 static const char *MonthNames
[] = { "Jan", "Feb", "Mar", "Apr",
426 "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
427 static const char *ClassTypes
[] = { "Public", "Confidential", "Private" };
428 static const char *FreeBusy
[] = { "Free", "Tentative", "Busy", "Out of Office" };
430 // FIXME - need a "check all data" function that make sure that all
431 // recurrence data is within range. Then call that before using
432 // the data, such as in Build and in Dump.
434 os
<< "Calendar entry: 0x" << setbase(16) << RecordId
435 << " (" << (unsigned int)RecType
<< ")\n";
436 os
<< " All Day Event: " << (AllDayEvent
? "yes" : "no") << "\n";
437 os
<< " Class: " << ClassTypes
[ClassFlag
] << "\n";
438 os
<< " Free/Busy: " << FreeBusy
[FreeBusyFlag
] << "\n";
440 os
<< " Time Zone: " << GetTimeZone(TimeZoneCode
)->Name
<< "\n";
442 // cycle through the type table
443 for( const FieldLink
<Calendar
> *b
= CalendarFieldLinks
;
444 b
->type
!= CALFC_END
;
448 const std::string
&s
= this->*(b
->strMember
);
450 os
<< " " << b
->name
<< ": " << s
<< "\n";
452 else if( b
->timeMember
) {
453 time_t t
= this->*(b
->timeMember
);
455 os
<< " " << b
->name
<< ": " << ctime(&t
);
457 os
<< " " << b
->name
<< ": disabled\n";
461 // print recurrence data if available
462 os
<< " Recurring: " << (Recurring
? "yes" : "no") << "\n";
464 switch( RecurringType
)
467 os
<< " Every day.\n";
471 os
<< " Every month on the "
473 << (DayOfMonth
== 1 ? "st" : "")
474 << (DayOfMonth
== 2 ? "nd" : "")
475 << (DayOfMonth
== 3 ? "rd" : "")
476 << (DayOfMonth
> 3 ? "th" : "")
481 os
<< " Every month on the "
482 << DayNames
[DayOfWeek
]
489 os
<< " Every year on "
490 << MonthNames
[MonthOfYear
-1]
491 << " " << DayOfMonth
<< "\n";
495 os
<< " Every year in " << MonthNames
[MonthOfYear
-1]
497 << DayNames
[DayOfWeek
]
498 << " of week " << WeekOfMonth
<< "\n";
502 os
<< " Every week on: ";
503 if( WeekDays
& CAL_WD_SUN
) os
<< "Sun ";
504 if( WeekDays
& CAL_WD_MON
) os
<< "Mon ";
505 if( WeekDays
& CAL_WD_TUE
) os
<< "Tue ";
506 if( WeekDays
& CAL_WD_WED
) os
<< "Wed ";
507 if( WeekDays
& CAL_WD_THU
) os
<< "Thu ";
508 if( WeekDays
& CAL_WD_FRI
) os
<< "Fri ";
509 if( WeekDays
& CAL_WD_SAT
) os
<< "Sat ";
514 os
<< " Unknown recurrence type\n";
518 os
<< " Interval: " << Interval
<< "\n";
521 os
<< " Ends: never\n";
524 << ctime(&RecurringEndTime
);
527 // print any unknowns