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.
23 #include "r_calendar.h"
24 #include "r_recur_base-int.h"
25 #include "record-internal.h"
27 #include "protostructs.h"
38 #include "ios_state.h"
40 #define __DEBUG_MODE__
44 using namespace Barry::Protocol
;
49 ///////////////////////////////////////////////////////////////////////////////
50 // Calendar class, static members
53 // Note! These functions currently only pass the same values through.
54 // In actuality, these are technically two different values:
55 // one on the raw protocol side, and the other part of the
56 // guaranteed Barry API. If the Blackberry ever changes the
57 // meanings for these codes, do the translation here.
60 Calendar::FreeBusyFlagType
Calendar::FreeBusyFlagProto2Rec(uint8_t f
)
62 return (FreeBusyFlagType
)f
;
65 uint8_t Calendar::FreeBusyFlagRec2Proto(FreeBusyFlagType f
)
70 Calendar::ClassFlagType
Calendar::ClassFlagProto2Rec(uint8_t f
)
72 return (ClassFlagType
)f
;
75 uint8_t Calendar::ClassFlagRec2Proto(ClassFlagType f
)
82 ///////////////////////////////////////////////////////////////////////////////
85 // calendar field codes
86 #define CALFC_APPT_TYPE_FLAG 0x01
87 #define CALFC_SUBJECT 0x02
88 #define CALFC_NOTES 0x03
89 #define CALFC_LOCATION 0x04
90 #define CALFC_NOTIFICATION_TIME 0x05
91 #define CALFC_START_TIME 0x06
92 #define CALFC_END_TIME 0x07
93 #define CALFC_ACCEPTED_BY 0x0b
94 #define CALFC_VERSION_DATA 0x10
95 #define CALFC_INVITED 0x15
96 #define CALFC_ORGANIZER 0x16
97 #define CALFC_NOTIFICATION_DATA 0x1a
98 #define CALFC_FREEBUSY_FLAG 0x1c
99 #define CALFC_TIMEZONE_CODE 0x1e // only seems to show up if recurring
100 #define CALFC_CLASS_FLAG 0x28 // private flag from outlook
101 #define CALFC_CALENDAR_ID 0x2b // Calendar using (new devices have several calendar)
102 #define CALFC_ALLDAYEVENT_FLAG 0xff
103 #define CALFC_END 0xffff
105 static FieldLink
<Calendar
> CalendarFieldLinks
[] = {
106 { CALFC_SUBJECT
, N_("Subject"), 0, 0, &Calendar::Subject
, 0, 0, 0, 0, true },
107 { CALFC_NOTES
, N_("Notes"), 0, 0, &Calendar::Notes
, 0, 0, 0, 0, true },
108 { CALFC_LOCATION
, N_("Location"), 0, 0, &Calendar::Location
, 0, 0, 0, 0, true },
109 { CALFC_NOTIFICATION_TIME
,N_("Notification Time"),0,0, 0, 0, &Calendar::NotificationTime
, 0, 0, false },
110 { CALFC_START_TIME
, N_("Start Time"), 0, 0, 0, 0, &Calendar::StartTime
, 0, 0, false },
111 { CALFC_END_TIME
, N_("End Time"), 0, 0, 0, 0, &Calendar::EndTime
, 0, 0, false },
112 { CALFC_ORGANIZER
, N_("Organizer"), 0, 0, 0, &Calendar::Organizer
, 0, 0, 0, true },
113 { CALFC_ACCEPTED_BY
,N_("Accepted By"),0, 0, 0, &Calendar::AcceptedBy
, 0, 0, 0, true },
114 { CALFC_INVITED
, N_("Invited"), 0, 0, 0, &Calendar::Invited
, 0, 0, 0, true },
115 { CALFC_END
, N_("End of List"),0, 0, 0, 0, 0, 0, 0, false }
123 Calendar::~Calendar()
127 const unsigned char* Calendar::ParseField(const unsigned char *begin
,
128 const unsigned char *end
,
129 const IConverter
*ic
)
131 const CommonField
*field
= (const CommonField
*) begin
;
133 // advance and check size
134 begin
+= COMMON_FIELD_HEADER_SIZE
+ btohs(field
->size
);
135 if( begin
> end
) // if begin==end, we are ok
138 if( !btohs(field
->size
) ) // if field has no size, something's up
141 // cycle through the type table
142 for( FieldLink
<Calendar
> *b
= CalendarFieldLinks
;
143 b
->type
!= CALFC_END
;
146 if( b
->type
== field
->type
) {
148 std::string
&s
= this->*(b
->strMember
);
149 s
= ParseFieldString(field
);
150 if( b
->iconvNeeded
&& ic
)
152 return begin
; // done!
154 else if( b
->timeMember
&& btohs(field
->size
) == 4 ) {
155 TimeT
&t
= this->*(b
->timeMember
);
156 dout("min1900: " << field
->u
.min1900
);
157 t
.Time
= min2time(field
->u
.min1900
);
160 else if( b
->addrMember
) {
162 // parse email address
163 // get dual addr+name string first
164 // Note: this is a different format than
165 // used in r_message*.cc
167 std::string
dual((const char*)field
->u
.raw
, btohs(field
->size
));
171 // assign first string, using null terminator
172 // letting std::string add it for us if it
174 a
.Email
= dual
.c_str();
176 // assign second string, using first size
178 a
.Name
= dual
.c_str() + a
.Email
.size() + 1;
180 // if the address is non-empty, add to list
182 // i18n convert if needed
183 if( b
->iconvNeeded
&& ic
) {
184 a
.Name
= ic
->FromBB(a
.Name
);
185 a
.Email
= ic
->FromBB(a
.Email
);
188 EmailAddressList
&al
= this->*(b
->addrMember
);
197 // handle special cases
198 switch( field
->type
)
200 case CALFC_APPT_TYPE_FLAG
:
201 switch( field
->u
.raw
[0] )
203 case 'a': // regular non-recurring appointment
207 case '*': // recurring appointment
212 throw Error(_("Calendar::ParseField: unknown appointment type"));
216 case CALFC_ALLDAYEVENT_FLAG
:
217 AllDayEvent
= field
->u
.raw
[0] == 1;
220 case CALFC_TIMEZONE_CODE
:
221 if( btohs(field
->size
) == 2 ) {
223 TimeZoneCode
= btohs(field
->u
.code
);
224 TimeZoneValid
= true;
227 throw Error(_("Calendar::ParseField: not enough data in time zone code field"));
231 case CALFC_FREEBUSY_FLAG
:
232 if( field
->u
.raw
[0] > CR_FREEBUSY_RANGE_HIGH
) {
233 throw Error(_("Calendar::ParseField: FreeBusyFlag out of range"));
235 FreeBusyFlag
= FreeBusyFlagProto2Rec(field
->u
.raw
[0]);
238 case CALFC_CALENDAR_ID
:
239 if( btohs(field
->size
) == 8 ) {
240 CalendarID
= btohll(field
->u
.uint64
);
243 throw Error(_("Calendar::ParseField: size data unknown in calendar field"));
247 case CALFC_CLASS_FLAG
:
248 if( field
->u
.raw
[0] > CR_CLASS_RANGE_HIGH
) {
249 throw Error(_("Calendar::ParseField: ClassFlag out of range" ));
251 ClassFlag
= ClassFlagProto2Rec(field
->u
.raw
[0]);
255 // base class handles recurring data
256 if( RecurBase::ParseField(field
->type
, field
->u
.raw
, btohs(field
->size
), ic
) )
259 // if still not handled, add to the Unknowns list
261 uf
.type
= field
->type
;
262 uf
.data
.assign((const char*)field
->u
.raw
, btohs(field
->size
));
263 Unknowns
.push_back(uf
);
265 // return new pointer for next field
269 void Calendar::ParseHeader(const Data
&data
, size_t &offset
)
271 // no header in Calendar records
274 void Calendar::ParseFields(const Data
&data
, size_t &offset
, const IConverter
*ic
)
276 const unsigned char *finish
= ParseCommonFields(*this,
277 data
.GetData() + offset
, data
.GetData() + data
.GetSize(), ic
);
278 offset
+= finish
- (data
.GetData() + offset
);
281 void Calendar::Validate() const
283 RecurBase::Validate();
286 void Calendar::BuildHeader(Data
&data
, size_t &offset
) const
288 // no header in Calendar records
294 /// Build fields part of record.
296 void Calendar::BuildFields(Data
&data
, size_t &offset
, const IConverter
*ic
) const
300 // output the type first
301 BuildField(data
, offset
, CALFC_APPT_TYPE_FLAG
, Recurring
? '*' : 'a');
303 // output all day event flag only if set
305 BuildField(data
, offset
, CALFC_ALLDAYEVENT_FLAG
, (char)1);
307 // cycle through the type table
308 for( const FieldLink
<Calendar
> *b
= CalendarFieldLinks
;
309 b
->type
!= CALFC_END
;
313 const std::string
&s
= this->*(b
->strMember
);
315 BuildField(data
, offset
, b
->type
, (b
->iconvNeeded
&& ic
) ? ic
->ToBB(s
) : s
);
317 else if( b
->timeMember
) {
318 TimeT t
= this->*(b
->timeMember
);
320 BuildField1900(data
, offset
, b
->type
, t
.Time
);
322 else if( b
->addrMember
) {
323 const EmailAddressList
&al
= this->*(b
->addrMember
);
324 EmailAddressList::const_iterator lb
= al
.begin(), le
= al
.end();
326 // add all entries in list
327 for( ; lb
!= le
; ++lb
) {
329 // skip empty entries
333 std::string Name
= lb
->Name
,
336 // do i18n conversion only if needed
337 if( b
->iconvNeeded
&& ic
) {
338 Name
= ic
->ToBB(Name
);
339 Email
= ic
->ToBB(Email
);
343 // Build an addr+name field, each string
345 // Note: this is a different format than
346 // what is used in r_message*.cc
348 std::string
field(lb
->Email
.c_str(), lb
->Email
.size() + 1);
349 field
.append(lb
->Name
.c_str(), lb
->Name
.size() + 1);
350 BuildField(data
, offset
, b
->type
, field
.data(), field
.size());
355 // handle special cases
357 CalendarRecurrenceDataField recur
;
358 BuildRecurrenceData(StartTime
.Time
, &recur
);
359 BuildField(data
, offset
, RecurBase::RecurringFieldType(),
360 &recur
, CALENDAR_RECURRENCE_DATA_FIELD_SIZE
);
364 BuildField(data
, offset
, CALFC_TIMEZONE_CODE
, TimeZoneCode
);
366 BuildField(data
, offset
, CALFC_FREEBUSY_FLAG
, FreeBusyFlagRec2Proto(FreeBusyFlag
));
367 BuildField(data
, offset
, CALFC_CLASS_FLAG
, ClassFlagRec2Proto(ClassFlag
));
369 // If CalendarID is defined and most of supported !
370 // (by default 0xffff ffff ffff ffff)
371 if( CalendarID
!= (uint64_t) -1 )
372 BuildField(data
, offset
, CALFC_CALENDAR_ID
, CalendarID
);
374 // and finally save unknowns
375 UnknownsType::const_iterator
376 ub
= Unknowns
.begin(), ue
= Unknowns
.end();
377 for( ; ub
!= ue
; ub
++ ) {
378 BuildField(data
, offset
, *ub
);
381 data
.ReleaseBuffer(offset
);
384 void Calendar::Clear()
386 // clear the base class too
390 RecType
= GetDefaultRecType();
397 NotificationTime
.clear();
407 CalendarID
= btohll((uint64_t) -1);
409 TimeZoneCode
= GetStaticTimeZoneCode(0, 0); // default to GMT
410 TimeZoneValid
= false;
415 const FieldHandle
<Calendar
>::ListT
& Calendar::GetFieldHandles()
417 static FieldHandle
<Calendar
>::ListT fhv
;
422 #undef CONTAINER_OBJECT_NAME
423 #define CONTAINER_OBJECT_NAME fhv
425 #undef RECORD_CLASS_NAME
426 #define RECORD_CLASS_NAME Calendar
428 #define ALL_COMMON_CALENDAR_FIELDS \
429 FHP(RecType, _("Record Type Code")); \
430 FHP(RecordId, _("Unique Record ID")); \
432 FHP(AllDayEvent, _("All Day Event")); \
433 FHD(Subject, _("Subject"), CALFC_SUBJECT, true); \
434 FHD(Notes, _("Notes"), CALFC_NOTES, true); \
435 FHD(Location, _("Location"), CALFC_LOCATION, true); \
436 FHD(NotificationTime, _("Notification Time (0 is off)"), \
437 CALFC_NOTIFICATION_TIME, false); \
438 FHD(StartTime, _("Start Time"), CALFC_START_TIME, false); \
439 FHD(EndTime, _("End Time"), CALFC_END_TIME, false); \
440 FHD(Organizer, _("Organizer"), CALFC_ORGANIZER, true); \
441 FHD(AcceptedBy, _("Accepted By"), CALFC_ACCEPTED_BY, true); \
442 FHD(Invited, _("Invited"), CALFC_INVITED, true); \
444 FHE(fbf, FreeBusyFlagType, FreeBusyFlag, _("Free or Busy Flag")); \
445 FHE_CONST(fbf, Free, _("Free")); \
446 FHE_CONST(fbf, Tentative, _("Tentative")); \
447 FHE_CONST(fbf, Busy, _("Busy")); \
448 FHE_CONST(fbf, OutOfOffice, _("Out of Office")); \
450 FHE(cf, ClassFlagType, ClassFlag, _("Event Class")); \
451 FHE_CONST(cf, Public, _("Public")); \
452 FHE_CONST(cf, Confidential, _("Confidential")); \
453 FHE_CONST(cf, Private, _("Private")); \
455 FHP(TimeZoneCode, _("Time Zone Code")); \
456 FHP(TimeZoneValid, _("Time Zone Validity")); \
458 FHP(Unknowns, _("Unknown Fields"));
460 ALL_COMMON_CALENDAR_FIELDS
462 // the fields unique to Calendar, or different in CalendarALL
463 FHD(CalendarID
, _("Calendar ID"), CALFC_CALENDAR_ID
, false);
465 // and finally, the RecurBase fields
466 RECUR_BASE_FIELD_HANDLES
471 std::string
Calendar::GetDescription() const
476 void Calendar::DumpSpecialFields(std::ostream
&os
) const
478 ios_format_state
state(os
);
480 static const char *ClassTypes
[] = {
485 static const char *FreeBusy
[] = {
492 os
<< _(" Calendar ID: ")
493 << "0x" << setbase(16) << CalendarID
<< "\n";
494 os
<< _(" All Day Event: ") << (AllDayEvent
? "yes" : "no") << "\n";
495 os
<< _(" Class: ") << ClassTypes
[ClassFlag
] << "\n";
496 os
<< _(" Free/Busy: ") << FreeBusy
[FreeBusyFlag
] << "\n";
498 os
<< _(" Time Zone: ") << gettext(GetStaticTimeZone(TimeZoneCode
)->Name
) << "\n";
501 void Calendar::Dump(std::ostream
&os
) const
503 ios_format_state
state(os
);
505 // FIXME - need a "check all data" function that make sure that all
506 // recurrence data is within range. Then call that before using
507 // the data, such as in Build and in Dump.
509 os
<< _("Calendar entry: ") << "0x" << setbase(16) << RecordId
510 << " (" << (unsigned int)RecType
<< ")\n";
511 DumpSpecialFields(os
);
513 // cycle through the type table
514 for( const FieldLink
<Calendar
> *b
= CalendarFieldLinks
;
515 b
->type
!= CALFC_END
;
519 const std::string
&s
= this->*(b
->strMember
);
521 os
<< " " << gettext(b
->name
) << ": " << s
<< "\n";
523 else if( b
->timeMember
) {
524 TimeT t
= this->*(b
->timeMember
);
526 os
<< " " << gettext(b
->name
) << ": " << t
<< "\n";
528 os
<< " " << gettext(b
->name
) << ": " << _("disabled") << "\n";
530 else if( b
->addrMember
) {
531 const EmailAddressList
&al
= this->*(b
->addrMember
);
532 EmailAddressList::const_iterator lb
= al
.begin(), le
= al
.end();
534 for( ; lb
!= le
; ++lb
) {
538 os
<< " " << gettext(b
->name
) << ": " << *lb
<< "\n";
543 // print recurrence data if available
546 // print any unknowns
550 bool Calendar::operator<(const Calendar
&other
) const
552 if( StartTime
< other
.StartTime
)
554 else if( other
.StartTime
< StartTime
)
557 // times are equal, so secondary sort based on Subject + Location
558 int cmp
= Subject
.compare(other
.Subject
);
560 cmp
= Location
.compare(other
.Location
);
565 ///////////////////////////////////////////////////////////////////////////////
566 // Calendar-All class
568 // calendar-all field codes
569 #define CALALLFC_CALENDAR_ID 0x02 // Calendar using (new devices have several calendar)
570 #define CALALLFC_MAIL_ACCOUNT 0x03
571 #define CALALLFC_UNIQUEID 0x05
572 #define CALALLFC_CAL_OBJECT 0x0a
573 #define CALALLFC_END 0xffff
575 void CalendarAll::Clear()
582 const FieldHandle
<CalendarAll
>::ListT
& CalendarAll::GetFieldHandles()
584 static FieldHandle
<CalendarAll
>::ListT fhv
;
589 #undef CONTAINER_OBJECT_NAME
590 #define CONTAINER_OBJECT_NAME fhv
592 #undef RECORD_CLASS_NAME
593 #define RECORD_CLASS_NAME CalendarAll
595 ALL_COMMON_CALENDAR_FIELDS
597 // Calendar:: field, but with a CalendarAll ID
598 FHD(CalendarID
, _("Calendar ID"), CALALLFC_CALENDAR_ID
, false);
600 // add the fields specific to CalendarAll
601 FHD(MailAccount
, _("Mail Account"), CALALLFC_MAIL_ACCOUNT
, true);
603 // and finally, the RecurBase fields
604 RECUR_BASE_FIELD_HANDLES
609 void CalendarAll::ParseHeader(const Data
&data
, size_t &offset
)
611 const unsigned char *b
= (const unsigned char*) (data
.GetData() + offset
);
612 const unsigned char *e
= (const unsigned char*) (data
.GetData() + data
.GetSize());
614 while( (b
+ COMMON_FIELD_HEADER_SIZE
) < e
) {
615 const CommonField
*field
= (const CommonField
*) b
;
617 // advance and check size
618 b
+= COMMON_FIELD_HEADER_SIZE
+ btohs(field
->size
);
619 if( b
> e
) // if begin==end, we are ok
622 if( !btohs(field
->size
) ) // if field has no size, something's up
625 // handle special cases
626 if( field
->type
== CALALLFC_CAL_OBJECT
)
628 b
-= btohs(field
->size
);
633 switch( field
->type
)
635 case CALALLFC_CALENDAR_ID
:
636 if( btohs(field
->size
) == 8 ) {
637 CalendarID
= btohll(field
->u
.uint64
);
640 throw Error(_("CalendarAll::ParseField: size data unknown in calendar field"));
644 case CALALLFC_MAIL_ACCOUNT
:
645 MailAccount
= ParseFieldString(field
);
648 case CALALLFC_UNIQUEID
:
649 if( btohs(field
->size
) == 4 ) {
650 RecordId
= btohl(field
->u
.uint32
);
653 throw Error(_("CalendarAll::ParseHeader: size data unknown in calendar field"));
658 // if still not handled, add to the Unknowns list
660 uf
.type
= field
->type
;
661 uf
.data
.assign((const char*)field
->u
.raw
, btohs(field
->size
));
662 Unknowns
.push_back(uf
);
665 offset
+= b
- (data
.GetData() + offset
);
668 void CalendarAll::DumpSpecialFields(std::ostream
&os
) const
670 ios_format_state
state(os
);
672 Calendar::DumpSpecialFields(os
);
673 os
<< _(" Mail Account: ") << MailAccount
<< "\n";