2 /// \file r_calendar.cc
3 /// Blackberry database record parser class for calendar records.
7 Copyright (C) 2005-2012, 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 #include "ios_state.h"
38 #define __DEBUG_MODE__
42 using namespace Barry::Protocol
;
47 ///////////////////////////////////////////////////////////////////////////////
48 // Calendar class, static members
51 // Note! These functions currently only pass the same values through.
52 // In actuality, these are technically two different values:
53 // one on the raw protocol side, and the other part of the
54 // guaranteed Barry API. If the Blackberry ever changes the
55 // meanings for these codes, do the translation here.
58 Calendar::FreeBusyFlagType
Calendar::FreeBusyFlagProto2Rec(uint8_t f
)
60 return (FreeBusyFlagType
)f
;
63 uint8_t Calendar::FreeBusyFlagRec2Proto(FreeBusyFlagType f
)
68 Calendar::ClassFlagType
Calendar::ClassFlagProto2Rec(uint8_t f
)
70 return (ClassFlagType
)f
;
73 uint8_t Calendar::ClassFlagRec2Proto(ClassFlagType f
)
80 ///////////////////////////////////////////////////////////////////////////////
83 // calendar field codes
84 #define CALFC_APPT_TYPE_FLAG 0x01
85 #define CALFC_SUBJECT 0x02
86 #define CALFC_NOTES 0x03
87 #define CALFC_LOCATION 0x04
88 #define CALFC_NOTIFICATION_TIME 0x05
89 #define CALFC_START_TIME 0x06
90 #define CALFC_END_TIME 0x07
91 #define CALFC_ACCEPTED_BY 0x0b
92 #define CALFC_VERSION_DATA 0x10
93 #define CALFC_INVITED 0x15
94 #define CALFC_ORGANIZER 0x16
95 #define CALFC_NOTIFICATION_DATA 0x1a
96 #define CALFC_FREEBUSY_FLAG 0x1c
97 #define CALFC_TIMEZONE_CODE 0x1e // only seems to show up if recurring
98 #define CALFC_CLASS_FLAG 0x28 // private flag from outlook
99 #define CALFC_CALENDAR_ID 0x2b // Calendar using (new devices have several calendar)
100 #define CALFC_ALLDAYEVENT_FLAG 0xff
101 #define CALFC_END 0xffff
103 static FieldLink
<Calendar
> CalendarFieldLinks
[] = {
104 { CALFC_SUBJECT
, "Subject", 0, 0, &Calendar::Subject
, 0, 0, 0, 0, true },
105 { CALFC_NOTES
, "Notes", 0, 0, &Calendar::Notes
, 0, 0, 0, 0, true },
106 { CALFC_LOCATION
, "Location", 0, 0, &Calendar::Location
, 0, 0, 0, 0, true },
107 { CALFC_NOTIFICATION_TIME
,"Notification Time",0,0, 0, 0, &Calendar::NotificationTime
, 0, 0, false },
108 { CALFC_START_TIME
, "Start Time", 0, 0, 0, 0, &Calendar::StartTime
, 0, 0, false },
109 { CALFC_END_TIME
, "End Time", 0, 0, 0, 0, &Calendar::EndTime
, 0, 0, false },
110 { CALFC_ORGANIZER
, "Organizer", 0, 0, 0, &Calendar::Organizer
, 0, 0, 0, true },
111 { CALFC_ACCEPTED_BY
,"Accepted By",0, 0, 0, &Calendar::AcceptedBy
, 0, 0, 0, true },
112 { CALFC_INVITED
, "Invited", 0, 0, 0, &Calendar::Invited
, 0, 0, 0, true },
113 { CALFC_END
, "End of List",0, 0, 0, 0, 0, 0, 0, false }
121 Calendar::~Calendar()
125 const unsigned char* Calendar::ParseField(const unsigned char *begin
,
126 const unsigned char *end
,
127 const IConverter
*ic
)
129 const CommonField
*field
= (const CommonField
*) begin
;
131 // advance and check size
132 begin
+= COMMON_FIELD_HEADER_SIZE
+ btohs(field
->size
);
133 if( begin
> end
) // if begin==end, we are ok
136 if( !btohs(field
->size
) ) // if field has no size, something's up
139 // cycle through the type table
140 for( FieldLink
<Calendar
> *b
= CalendarFieldLinks
;
141 b
->type
!= CALFC_END
;
144 if( b
->type
== field
->type
) {
146 std::string
&s
= this->*(b
->strMember
);
147 s
= ParseFieldString(field
);
148 if( b
->iconvNeeded
&& ic
)
150 return begin
; // done!
152 else if( b
->timeMember
&& btohs(field
->size
) == 4 ) {
153 time_t &t
= this->*(b
->timeMember
);
154 dout("min1900: " << field
->u
.min1900
);
155 t
= min2time(field
->u
.min1900
);
158 else if( b
->addrMember
) {
160 // parse email address
161 // get dual addr+name string first
162 // Note: this is a different format than
163 // used in r_message*.cc
165 std::string
dual((const char*)field
->u
.raw
, btohs(field
->size
));
169 // assign first string, using null terminator
170 // letting std::string add it for us if it
172 a
.Email
= dual
.c_str();
174 // assign second string, using first size
176 a
.Name
= dual
.c_str() + a
.Email
.size() + 1;
178 // if the address is non-empty, add to list
180 // i18n convert if needed
181 if( b
->iconvNeeded
&& ic
) {
182 a
.Name
= ic
->FromBB(a
.Name
);
183 a
.Email
= ic
->FromBB(a
.Email
);
186 EmailAddressList
&al
= this->*(b
->addrMember
);
195 // handle special cases
196 switch( field
->type
)
198 case CALFC_APPT_TYPE_FLAG
:
199 switch( field
->u
.raw
[0] )
201 case 'a': // regular non-recurring appointment
205 case '*': // recurring appointment
210 throw Error("Calendar::ParseField: unknown appointment type");
214 case CALFC_ALLDAYEVENT_FLAG
:
215 AllDayEvent
= field
->u
.raw
[0] == 1;
218 case CALFC_TIMEZONE_CODE
:
219 if( btohs(field
->size
) == 2 ) {
221 TimeZoneCode
= btohs(field
->u
.code
);
222 TimeZoneValid
= true;
225 throw Error("Calendar::ParseField: not enough data in time zone code field");
229 case CALFC_FREEBUSY_FLAG
:
230 if( field
->u
.raw
[0] > CR_FREEBUSY_RANGE_HIGH
) {
231 throw Error("Calendar::ParseField: FreeBusyFlag out of range" );
233 FreeBusyFlag
= FreeBusyFlagProto2Rec(field
->u
.raw
[0]);
236 case CALFC_CALENDAR_ID
:
237 if( btohs(field
->size
) == 8 ) {
238 CalendarID
= btohll(field
->u
.uint64
);
241 throw Error("Calendar::ParseField: size data unknown in calendar field");
245 case CALFC_CLASS_FLAG
:
246 if( field
->u
.raw
[0] > CR_CLASS_RANGE_HIGH
) {
247 throw Error("Calendar::ParseField: ClassFlag out of range" );
249 ClassFlag
= ClassFlagProto2Rec(field
->u
.raw
[0]);
253 // base class handles recurring data
254 if( RecurBase::ParseField(field
->type
, field
->u
.raw
, btohs(field
->size
), ic
) )
257 // if still not handled, add to the Unknowns list
259 uf
.type
= field
->type
;
260 uf
.data
.assign((const char*)field
->u
.raw
, btohs(field
->size
));
261 Unknowns
.push_back(uf
);
263 // return new pointer for next field
267 void Calendar::ParseHeader(const Data
&data
, size_t &offset
)
269 // no header in Calendar records
272 void Calendar::ParseFields(const Data
&data
, size_t &offset
, const IConverter
*ic
)
274 const unsigned char *finish
= ParseCommonFields(*this,
275 data
.GetData() + offset
, data
.GetData() + data
.GetSize(), ic
);
276 offset
+= finish
- (data
.GetData() + offset
);
279 void Calendar::BuildHeader(Data
&data
, size_t &offset
) const
281 // no header in Calendar records
287 /// Build fields part of record.
289 void Calendar::BuildFields(Data
&data
, size_t &offset
, const IConverter
*ic
) const
293 // output the type first
294 BuildField(data
, offset
, CALFC_APPT_TYPE_FLAG
, Recurring
? '*' : 'a');
296 // output all day event flag only if set
298 BuildField(data
, offset
, CALFC_ALLDAYEVENT_FLAG
, (char)1);
300 // cycle through the type table
301 for( const FieldLink
<Calendar
> *b
= CalendarFieldLinks
;
302 b
->type
!= CALFC_END
;
306 const std::string
&s
= this->*(b
->strMember
);
308 BuildField(data
, offset
, b
->type
, (b
->iconvNeeded
&& ic
) ? ic
->ToBB(s
) : s
);
310 else if( b
->timeMember
) {
311 time_t t
= this->*(b
->timeMember
);
313 BuildField1900(data
, offset
, b
->type
, t
);
315 else if( b
->addrMember
) {
316 const EmailAddressList
&al
= this->*(b
->addrMember
);
317 EmailAddressList::const_iterator lb
= al
.begin(), le
= al
.end();
319 // add all entries in list
320 for( ; lb
!= le
; ++lb
) {
322 // skip empty entries
326 std::string Name
= lb
->Name
,
329 // do i18n conversion only if needed
330 if( b
->iconvNeeded
&& ic
) {
331 Name
= ic
->ToBB(Name
);
332 Email
= ic
->ToBB(Email
);
336 // Build an addr+name field, each string
338 // Note: this is a different format than
339 // what is used in r_message*.cc
341 std::string
field(lb
->Email
.c_str(), lb
->Email
.size() + 1);
342 field
.append(lb
->Name
.c_str(), lb
->Name
.size() + 1);
343 BuildField(data
, offset
, b
->type
, field
.data(), field
.size());
348 // handle special cases
350 CalendarRecurrenceDataField recur
;
351 BuildRecurrenceData(StartTime
, &recur
);
352 BuildField(data
, offset
, RecurBase::RecurringFieldType(),
353 &recur
, CALENDAR_RECURRENCE_DATA_FIELD_SIZE
);
357 BuildField(data
, offset
, CALFC_TIMEZONE_CODE
, TimeZoneCode
);
359 BuildField(data
, offset
, CALFC_FREEBUSY_FLAG
, FreeBusyFlagRec2Proto(FreeBusyFlag
));
360 BuildField(data
, offset
, CALFC_CLASS_FLAG
, ClassFlagRec2Proto(ClassFlag
));
362 // If CalendarID is defined and most of supported !
363 // (by default 0xffff ffff ffff ffff)
364 if( CalendarID
!= (uint64_t) -1 )
365 BuildField(data
, offset
, CALFC_CALENDAR_ID
, CalendarID
);
367 // and finally save unknowns
368 UnknownsType::const_iterator
369 ub
= Unknowns
.begin(), ue
= Unknowns
.end();
370 for( ; ub
!= ue
; ub
++ ) {
371 BuildField(data
, offset
, *ub
);
374 data
.ReleaseBuffer(offset
);
377 void Calendar::Clear()
379 // clear the base class too
383 RecType
= GetDefaultRecType();
390 NotificationTime
= StartTime
= EndTime
= 0;
398 CalendarID
= btohll((uint64_t) -1);
400 TimeZoneCode
= GetTimeZoneCode(0, 0); // default to GMT
401 TimeZoneValid
= false;
406 const std::vector
<FieldHandle
<Calendar
> >& Calendar::GetFieldHandles()
408 static std::vector
<FieldHandle
<Calendar
> > fhv
;
413 #undef CONTAINER_OBJECT_NAME
414 #define CONTAINER_OBJECT_NAME fhv
416 #undef RECORD_CLASS_NAME
417 #define RECORD_CLASS_NAME Calendar
419 #define ALL_COMMON_CALENDAR_FIELDS \
420 FHP(RecType, "Record Type Code"); \
421 FHP(RecordId, "Unique Record ID"); \
423 FHP(AllDayEvent, "All Day Event"); \
424 FHD(Subject, "Subject", CALFC_SUBJECT, true); \
425 FHD(Notes, "Notes", CALFC_NOTES, true); \
426 FHD(Location, "Location", CALFC_LOCATION, true); \
427 FHD(NotificationTime, "Notification Time (0 is off)", \
428 CALFC_NOTIFICATION_TIME, false); \
429 FHD(StartTime, "Start Time", CALFC_START_TIME, false); \
430 FHD(EndTime, "End Time", CALFC_END_TIME, false); \
431 FHD(Organizer, "Organizer", CALFC_ORGANIZER, true); \
432 FHD(AcceptedBy, "Accepted By", CALFC_ACCEPTED_BY, true); \
433 FHD(Invited, "Invited", CALFC_INVITED, true); \
435 FHE(fbf, FreeBusyFlagType, FreeBusyFlag, "Free or Busy Flag"); \
436 FHE_CONST(fbf, Free, "Free"); \
437 FHE_CONST(fbf, Tentative, "Tentative"); \
438 FHE_CONST(fbf, Busy, "Busy"); \
439 FHE_CONST(fbf, OutOfOffice, "Out of Office"); \
441 FHE(cf, ClassFlagType, ClassFlag, "Event Class"); \
442 FHE_CONST(cf, Public, "Public"); \
443 FHE_CONST(cf, Confidential, "Confidential"); \
444 FHE_CONST(cf, Private, "Private"); \
446 FHP(TimeZoneCode, "Time Zone Code"); \
447 FHP(TimeZoneValid, "Time Zone Validity"); \
449 FHP(Unknowns, "Unknown Fields");
451 ALL_COMMON_CALENDAR_FIELDS
453 // the fields unique to Calendar, or different in CalendarALL
454 FHD(CalendarID
, "Calendar ID", CALFC_CALENDAR_ID
, false);
459 std::string
Calendar::GetDescription() const
464 void Calendar::DumpSpecialFields(std::ostream
&os
) const
466 ios_format_state
state(os
);
468 static const char *ClassTypes
[] = { "Public", "Confidential", "Private" };
469 static const char *FreeBusy
[] = { "Free", "Tentative", "Busy", "Out of Office" };
471 os
<< " Calendar ID: 0x" << setbase(16) << CalendarID
<< "\n";
472 os
<< " All Day Event: " << (AllDayEvent
? "yes" : "no") << "\n";
473 os
<< " Class: " << ClassTypes
[ClassFlag
] << "\n";
474 os
<< " Free/Busy: " << FreeBusy
[FreeBusyFlag
] << "\n";
476 os
<< " Time Zone: " << GetTimeZone(TimeZoneCode
)->Name
<< "\n";
479 void Calendar::Dump(std::ostream
&os
) const
481 ios_format_state
state(os
);
483 // FIXME - need a "check all data" function that make sure that all
484 // recurrence data is within range. Then call that before using
485 // the data, such as in Build and in Dump.
487 os
<< "Calendar entry: 0x" << setbase(16) << RecordId
488 << " (" << (unsigned int)RecType
<< ")\n";
489 DumpSpecialFields(os
);
491 // cycle through the type table
492 for( const FieldLink
<Calendar
> *b
= CalendarFieldLinks
;
493 b
->type
!= CALFC_END
;
497 const std::string
&s
= this->*(b
->strMember
);
499 os
<< " " << b
->name
<< ": " << s
<< "\n";
501 else if( b
->timeMember
) {
502 time_t t
= this->*(b
->timeMember
);
504 os
<< " " << b
->name
<< ": " << ctime(&t
);
506 os
<< " " << b
->name
<< ": disabled\n";
508 else if( b
->addrMember
) {
509 const EmailAddressList
&al
= this->*(b
->addrMember
);
510 EmailAddressList::const_iterator lb
= al
.begin(), le
= al
.end();
512 for( ; lb
!= le
; ++lb
) {
516 os
<< " " << b
->name
<< ": " << *lb
<< "\n";
521 // print recurrence data if available
524 // print any unknowns
528 bool Calendar::operator<(const Calendar
&other
) const
530 if( StartTime
< other
.StartTime
)
532 else if( StartTime
> other
.StartTime
)
535 int cmp
= Subject
.compare(other
.Subject
);
537 cmp
= Location
.compare(other
.Location
);
542 ///////////////////////////////////////////////////////////////////////////////
543 // Calendar-All class
545 // calendar-all field codes
546 #define CALALLFC_CALENDAR_ID 0x02 // Calendar using (new devices have several calendar)
547 #define CALALLFC_MAIL_ACCOUNT 0x03
548 #define CALALLFC_UNIQUEID 0x05
549 #define CALALLFC_CAL_OBJECT 0x0a
550 #define CALALLFC_END 0xffff
552 void CalendarAll::Clear()
559 const std::vector
<FieldHandle
<CalendarAll
> >& CalendarAll::GetFieldHandles()
561 static std::vector
<FieldHandle
<CalendarAll
> > fhv
;
566 #undef CONTAINER_OBJECT_NAME
567 #define CONTAINER_OBJECT_NAME fhv
569 #undef RECORD_CLASS_NAME
570 #define RECORD_CLASS_NAME CalendarAll
572 ALL_COMMON_CALENDAR_FIELDS
574 // Calendar:: field, but with a CalendarAll ID
575 FHD(CalendarID
, "Calendar ID", CALALLFC_CALENDAR_ID
, false);
577 // add the fields specific to CalendarAll
578 FHD(MailAccount
, "Mail Account", CALALLFC_MAIL_ACCOUNT
, true);
583 void CalendarAll::ParseHeader(const Data
&data
, size_t &offset
)
585 const unsigned char *b
= (const unsigned char*) (data
.GetData() + offset
);
586 const unsigned char *e
= (const unsigned char*) (data
.GetData() + data
.GetSize());
588 while( (b
+ COMMON_FIELD_HEADER_SIZE
) < e
) {
589 const CommonField
*field
= (const CommonField
*) b
;
591 // advance and check size
592 b
+= COMMON_FIELD_HEADER_SIZE
+ btohs(field
->size
);
593 if( b
> e
) // if begin==end, we are ok
596 if( !btohs(field
->size
) ) // if field has no size, something's up
599 // handle special cases
600 if( field
->type
== CALALLFC_CAL_OBJECT
)
602 b
-= btohs(field
->size
);
607 switch( field
->type
)
609 case CALALLFC_CALENDAR_ID
:
610 if( btohs(field
->size
) == 8 ) {
611 CalendarID
= btohll(field
->u
.uint64
);
614 throw Error("CalendarAll::ParseField: size data unknown in calendar field");
618 case CALALLFC_MAIL_ACCOUNT
:
619 MailAccount
= ParseFieldString(field
);
622 case CALALLFC_UNIQUEID
:
623 if( btohs(field
->size
) == 4 ) {
624 RecordId
= btohl(field
->u
.uint32
);
627 throw Error("CalendarAll::ParseHeader: size data unknown in calendar field");
632 // if still not handled, add to the Unknowns list
634 uf
.type
= field
->type
;
635 uf
.data
.assign((const char*)field
->u
.raw
, btohs(field
->size
));
636 Unknowns
.push_back(uf
);
639 offset
+= b
- (data
.GetData() + offset
);
642 void CalendarAll::DumpSpecialFields(std::ostream
&os
) const
644 ios_format_state
state(os
);
646 Calendar::DumpSpecialFields(os
);
647 os
<< " Mail Account: " << MailAccount
<< "\n";