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 ///////////////////////////////////////////////////////////////////////////////
47 // Calendar class, static members
50 // Note! These functions currently only pass the same values through.
51 // In actuality, these are technically two different values:
52 // one on the raw protocol side, and the other part of the
53 // guaranteed Barry API. If the Blackberry ever changes the
54 // meanings for these codes, do the translation here.
57 Calendar::FreeBusyFlagType
Calendar::FreeBusyFlagProto2Rec(uint8_t f
)
59 return (FreeBusyFlagType
)f
;
62 uint8_t Calendar::FreeBusyFlagRec2Proto(FreeBusyFlagType f
)
67 Calendar::ClassFlagType
Calendar::ClassFlagProto2Rec(uint8_t f
)
69 return (ClassFlagType
)f
;
72 uint8_t Calendar::ClassFlagRec2Proto(ClassFlagType f
)
79 ///////////////////////////////////////////////////////////////////////////////
82 // calendar field codes
83 #define CALFC_APPT_TYPE_FLAG 0x01
84 #define CALFC_SUBJECT 0x02
85 #define CALFC_NOTES 0x03
86 #define CALFC_LOCATION 0x04
87 #define CALFC_NOTIFICATION_TIME 0x05
88 #define CALFC_START_TIME 0x06
89 #define CALFC_END_TIME 0x07
90 #define CALFC_ACCEPTED_BY 0x0b
91 #define CALFC_VERSION_DATA 0x10
92 #define CALFC_INVITED 0x15
93 #define CALFC_ORGANIZER 0x16
94 #define CALFC_NOTIFICATION_DATA 0x1a
95 #define CALFC_FREEBUSY_FLAG 0x1c
96 #define CALFC_TIMEZONE_CODE 0x1e // only seems to show up if recurring
97 #define CALFC_CLASS_FLAG 0x28 // private flag from outlook
98 #define CALFC_ALLDAYEVENT_FLAG 0xff
99 #define CALFC_END 0xffff
101 static FieldLink
<Calendar
> CalendarFieldLinks
[] = {
102 { CALFC_SUBJECT
, "Subject", 0, 0, &Calendar::Subject
, 0, 0, 0, 0, true },
103 { CALFC_NOTES
, "Notes", 0, 0, &Calendar::Notes
, 0, 0, 0, 0, true },
104 { CALFC_LOCATION
, "Location", 0, 0, &Calendar::Location
, 0, 0, 0, 0, true },
105 { CALFC_NOTIFICATION_TIME
,"Notification Time",0,0, 0, 0, &Calendar::NotificationTime
, 0, 0, false },
106 { CALFC_START_TIME
, "Start Time", 0, 0, 0, 0, &Calendar::StartTime
, 0, 0, false },
107 { CALFC_END_TIME
, "End Time", 0, 0, 0, 0, &Calendar::EndTime
, 0, 0, false },
108 { CALFC_ORGANIZER
, "Organizer", 0, 0, 0, &Calendar::Organizer
, 0, 0, 0, true },
109 { CALFC_ACCEPTED_BY
,"Accepted By",0, 0, 0, &Calendar::AcceptedBy
, 0, 0, 0, true },
110 { CALFC_INVITED
, "Invited", 0, 0, 0, &Calendar::Invited
, 0, 0, 0, true },
111 { CALFC_END
, "End of List",0, 0, 0, 0, 0, 0, 0, false }
119 Calendar::~Calendar()
123 const unsigned char* Calendar::ParseField(const unsigned char *begin
,
124 const unsigned char *end
,
125 const IConverter
*ic
)
127 const CommonField
*field
= (const CommonField
*) begin
;
129 // advance and check size
130 begin
+= COMMON_FIELD_HEADER_SIZE
+ btohs(field
->size
);
131 if( begin
> end
) // if begin==end, we are ok
134 if( !btohs(field
->size
) ) // if field has no size, something's up
137 // cycle through the type table
138 for( FieldLink
<Calendar
> *b
= CalendarFieldLinks
;
139 b
->type
!= CALFC_END
;
142 if( b
->type
== field
->type
) {
144 std::string
&s
= this->*(b
->strMember
);
145 s
= ParseFieldString(field
);
146 if( b
->iconvNeeded
&& ic
)
148 return begin
; // done!
150 else if( b
->timeMember
&& btohs(field
->size
) == 4 ) {
151 time_t &t
= this->*(b
->timeMember
);
152 dout("min1900: " << field
->u
.min1900
);
153 t
= min2time(field
->u
.min1900
);
156 else if( b
->addrMember
) {
158 // parse email address
159 // get dual addr+name string first
160 // Note: this is a different format than
161 // used in r_message*.cc
163 std::string
dual((const char*)field
->u
.raw
, btohs(field
->size
));
167 // assign first string, using null terminator
168 // letting std::string add it for us if it
170 a
.Email
= dual
.c_str();
172 // assign second string, using first size
174 a
.Name
= dual
.c_str() + a
.Email
.size() + 1;
176 // if the address is non-empty, add to list
178 // i18n convert if needed
179 if( b
->iconvNeeded
&& ic
) {
180 a
.Name
= ic
->FromBB(a
.Name
);
181 a
.Email
= ic
->FromBB(a
.Email
);
184 EmailAddressList
&al
= this->*(b
->addrMember
);
193 // handle special cases
194 switch( field
->type
)
196 case CALFC_APPT_TYPE_FLAG
:
197 switch( field
->u
.raw
[0] )
199 case 'a': // regular non-recurring appointment
203 case '*': // recurring appointment
208 throw Error("Calendar::ParseField: unknown appointment type");
212 case CALFC_ALLDAYEVENT_FLAG
:
213 AllDayEvent
= field
->u
.raw
[0] == 1;
216 case CALFC_TIMEZONE_CODE
:
217 if( btohs(field
->size
) == 2 ) {
219 TimeZoneCode
= btohs(field
->u
.code
);
220 TimeZoneValid
= true;
223 throw Error("Calendar::ParseField: not enough data in time zone code field");
227 case CALFC_FREEBUSY_FLAG
:
228 if( field
->u
.raw
[0] > CR_FREEBUSY_RANGE_HIGH
) {
229 throw Error("Calendar::ParseField: FreeBusyFlag out of range" );
231 FreeBusyFlag
= FreeBusyFlagProto2Rec(field
->u
.raw
[0]);
234 case CALFC_CLASS_FLAG
:
235 if( field
->u
.raw
[0] > CR_CLASS_RANGE_HIGH
) {
236 throw Error("Calendar::ParseField: ClassFlag out of range" );
238 ClassFlag
= ClassFlagProto2Rec(field
->u
.raw
[0]);
242 // base class handles recurring data
243 if( RecurBase::ParseField(field
->type
, field
->u
.raw
, btohs(field
->size
), ic
) )
246 // if still not handled, add to the Unknowns list
248 uf
.type
= field
->type
;
249 uf
.data
.assign((const char*)field
->u
.raw
, btohs(field
->size
));
250 Unknowns
.push_back(uf
);
252 // return new pointer for next field
256 void Calendar::ParseHeader(const Data
&data
, size_t &offset
)
258 // no header in Calendar records
261 void Calendar::ParseFields(const Data
&data
, size_t &offset
, const IConverter
*ic
)
263 const unsigned char *finish
= ParseCommonFields(*this,
264 data
.GetData() + offset
, data
.GetData() + data
.GetSize(), ic
);
265 offset
+= finish
- (data
.GetData() + offset
);
268 void Calendar::BuildHeader(Data
&data
, size_t &offset
) const
270 // no header in Calendar records
276 /// Build fields part of record.
278 void Calendar::BuildFields(Data
&data
, size_t &offset
, const IConverter
*ic
) const
282 // output the type first
283 BuildField(data
, offset
, CALFC_APPT_TYPE_FLAG
, Recurring
? '*' : 'a');
285 // output all day event flag only if set
287 BuildField(data
, offset
, CALFC_ALLDAYEVENT_FLAG
, (char)1);
289 // cycle through the type table
290 for( const FieldLink
<Calendar
> *b
= CalendarFieldLinks
;
291 b
->type
!= CALFC_END
;
295 const std::string
&s
= this->*(b
->strMember
);
297 BuildField(data
, offset
, b
->type
, (b
->iconvNeeded
&& ic
) ? ic
->ToBB(s
) : s
);
299 else if( b
->timeMember
) {
300 time_t t
= this->*(b
->timeMember
);
302 BuildField1900(data
, offset
, b
->type
, t
);
304 else if( b
->addrMember
) {
305 const EmailAddressList
&al
= this->*(b
->addrMember
);
306 EmailAddressList::const_iterator lb
= al
.begin(), le
= al
.end();
308 // add all entries in list
309 for( ; lb
!= le
; ++lb
) {
311 // skip empty entries
315 std::string Name
= lb
->Name
,
318 // do i18n conversion only if needed
319 if( b
->iconvNeeded
&& ic
) {
320 Name
= ic
->ToBB(Name
);
321 Email
= ic
->ToBB(Email
);
325 // Build an addr+name field, each string
327 // Note: this is a different format than
328 // what is used in r_message*.cc
330 std::string
field(lb
->Email
.c_str(), lb
->Email
.size() + 1);
331 field
.append(lb
->Name
.c_str(), lb
->Name
.size() + 1);
332 BuildField(data
, offset
, b
->type
, field
.data(), field
.size());
337 // handle special cases
339 CalendarRecurrenceDataField recur
;
340 BuildRecurrenceData(StartTime
, &recur
);
341 BuildField(data
, offset
, RecurBase::RecurringFieldType(),
342 &recur
, CALENDAR_RECURRENCE_DATA_FIELD_SIZE
);
346 BuildField(data
, offset
, CALFC_TIMEZONE_CODE
, TimeZoneCode
);
348 BuildField(data
, offset
, CALFC_FREEBUSY_FLAG
, FreeBusyFlagRec2Proto(FreeBusyFlag
));
349 BuildField(data
, offset
, CALFC_CLASS_FLAG
, ClassFlagRec2Proto(ClassFlag
));
351 // and finally save unknowns
352 UnknownsType::const_iterator
353 ub
= Unknowns
.begin(), ue
= Unknowns
.end();
354 for( ; ub
!= ue
; ub
++ ) {
355 BuildField(data
, offset
, *ub
);
358 data
.ReleaseBuffer(offset
);
361 void Calendar::Clear()
365 RecType
= Calendar::GetDefaultRecType();
371 NotificationTime
= StartTime
= EndTime
= 0;
376 TimeZoneCode
= GetTimeZoneCode(0, 0); // default to GMT
377 TimeZoneValid
= false;
382 void Calendar::Dump(std::ostream
&os
) const
384 static const char *ClassTypes
[] = { "Public", "Confidential", "Private" };
385 static const char *FreeBusy
[] = { "Free", "Tentative", "Busy", "Out of Office" };
387 // FIXME - need a "check all data" function that make sure that all
388 // recurrence data is within range. Then call that before using
389 // the data, such as in Build and in Dump.
391 os
<< "Calendar entry: 0x" << setbase(16) << RecordId
392 << " (" << (unsigned int)RecType
<< ")\n";
393 os
<< " All Day Event: " << (AllDayEvent
? "yes" : "no") << "\n";
394 os
<< " Class: " << ClassTypes
[ClassFlag
] << "\n";
395 os
<< " Free/Busy: " << FreeBusy
[FreeBusyFlag
] << "\n";
397 os
<< " Time Zone: " << GetTimeZone(TimeZoneCode
)->Name
<< "\n";
399 // cycle through the type table
400 for( const FieldLink
<Calendar
> *b
= CalendarFieldLinks
;
401 b
->type
!= CALFC_END
;
405 const std::string
&s
= this->*(b
->strMember
);
407 os
<< " " << b
->name
<< ": " << s
<< "\n";
409 else if( b
->timeMember
) {
410 time_t t
= this->*(b
->timeMember
);
412 os
<< " " << b
->name
<< ": " << ctime(&t
);
414 os
<< " " << b
->name
<< ": disabled\n";
416 else if( b
->addrMember
) {
417 const EmailAddressList
&al
= this->*(b
->addrMember
);
418 EmailAddressList::const_iterator lb
= al
.begin(), le
= al
.end();
420 for( ; lb
!= le
; ++lb
) {
424 os
<< " " << b
->name
<< ": " << *lb
<< "\n";
429 // print recurrence data if available
432 // print any unknowns