build: Adding support for choice of USB library to configure
[barry.git] / src / r_calendar.cc
blob664ea7bae17bfc9ea9f6a593a337a45b30bb969c
1 ///
2 /// \file r_calendar.cc
3 /// Blackberry database record parser class for calendar records.
4 ///
6 /*
7 Copyright (C) 2005-2011, 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"
24 #include "protocol.h"
25 #include "protostructs.h"
26 #include "data.h"
27 #include "time.h"
28 #include "error.h"
29 #include "endian.h"
30 #include "iconv.h"
31 #include <ostream>
32 #include <iomanip>
33 #include <time.h>
34 #include <string.h>
35 #include <stdexcept>
36 #include "ios_state.h"
38 #define __DEBUG_MODE__
39 #include "debug.h"
41 using namespace std;
42 using namespace Barry::Protocol;
44 namespace Barry {
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)
65 return f;
68 Calendar::ClassFlagType Calendar::ClassFlagProto2Rec(uint8_t f)
70 return (ClassFlagType)f;
73 uint8_t Calendar::ClassFlagRec2Proto(ClassFlagType f)
75 return f;
80 ///////////////////////////////////////////////////////////////////////////////
81 // Calendar class
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 }
116 Calendar::Calendar()
118 Clear();
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
134 return begin;
136 if( !btohs(field->size) ) // if field has no size, something's up
137 return begin;
139 // cycle through the type table
140 for( FieldLink<Calendar> *b = CalendarFieldLinks;
141 b->type != CALFC_END;
142 b++ )
144 if( b->type == field->type ) {
145 if( b->strMember ) {
146 std::string &s = this->*(b->strMember);
147 s = ParseFieldString(field);
148 if( b->iconvNeeded && ic )
149 s = ic->FromBB(s);
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);
156 return begin;
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));
167 EmailAddress a;
169 // assign first string, using null terminator
170 // letting std::string add it for us if it
171 // doesn't exist
172 a.Email = dual.c_str();
174 // assign second string, using first size
175 // as starting point
176 a.Name = dual.c_str() + a.Email.size() + 1;
178 // if the address is non-empty, add to list
179 if( a.size() ) {
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);
187 al.push_back(a);
190 return begin;
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
202 Recurring = false;
203 return begin;
205 case '*': // recurring appointment
206 Recurring = true;
207 return begin;
209 default:
210 throw Error("Calendar::ParseField: unknown appointment type");
212 break;
214 case CALFC_ALLDAYEVENT_FLAG:
215 AllDayEvent = field->u.raw[0] == 1;
216 return begin;
218 case CALFC_TIMEZONE_CODE:
219 if( btohs(field->size) == 2 ) {
220 // good data
221 TimeZoneCode = btohs(field->u.code);
222 TimeZoneValid = true;
224 else {
225 throw Error("Calendar::ParseField: not enough data in time zone code field");
227 return begin;
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]);
234 return begin;
236 case CALFC_CALENDAR_ID:
237 if( btohs(field->size) == 8 ) {
238 CalendarID = btohll(field->u.uint64);
240 else {
241 throw Error("Calendar::ParseField: size data unknown in calendar field");
243 return begin;
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]);
250 return begin;
253 // base class handles recurring data
254 if( RecurBase::ParseField(field->type, field->u.raw, btohs(field->size), ic) )
255 return begin;
257 // if still not handled, add to the Unknowns list
258 UnknownField uf;
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
264 return begin;
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
285 // Build
287 /// Build fields part of record.
289 void Calendar::BuildFields(Data &data, size_t &offset, const IConverter *ic) const
291 data.Zap();
293 // output the type first
294 BuildField(data, offset, CALFC_APPT_TYPE_FLAG, Recurring ? '*' : 'a');
296 // output all day event flag only if set
297 if( AllDayEvent )
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;
303 b++ )
305 if( b->strMember ) {
306 const std::string &s = this->*(b->strMember);
307 if( s.size() )
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);
312 if( t > 0 )
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
323 if( !lb->size() )
324 continue;
326 std::string Name = lb->Name,
327 Email = lb->Email;
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
337 // null terminated.
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
349 if( Recurring ) {
350 CalendarRecurrenceDataField recur;
351 BuildRecurrenceData(StartTime, &recur);
352 BuildField(data, offset, RecurBase::RecurringFieldType(),
353 &recur, CALENDAR_RECURRENCE_DATA_FIELD_SIZE);
356 if( TimeZoneValid )
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
380 RecurBase::Clear();
382 // clear our fields
383 RecType = GetDefaultRecType();
384 RecordId = 0;
386 AllDayEvent = false;
387 Subject.clear();
388 Notes.clear();
389 Location.clear();
390 NotificationTime = StartTime = EndTime = 0;
391 Organizer.clear();
392 AcceptedBy.clear();
393 Invited.clear();
395 FreeBusyFlag = Free;
396 ClassFlag = Public;
398 CalendarID = btohll((uint64_t) -1);
400 TimeZoneCode = GetTimeZoneCode(0, 0); // default to GMT
401 TimeZoneValid = false;
403 Unknowns.clear();
406 std::string Calendar::GetDescription() const
408 return Subject;
411 void Calendar::DumpSpecialFields(std::ostream &os) const
413 ios_format_state state(os);
415 static const char *ClassTypes[] = { "Public", "Confidential", "Private" };
416 static const char *FreeBusy[] = { "Free", "Tentative", "Busy", "Out of Office" };
418 os << " Calendar ID: 0x" << setbase(16) << CalendarID << "\n";
419 os << " All Day Event: " << (AllDayEvent ? "yes" : "no") << "\n";
420 os << " Class: " << ClassTypes[ClassFlag] << "\n";
421 os << " Free/Busy: " << FreeBusy[FreeBusyFlag] << "\n";
422 if( TimeZoneValid )
423 os << " Time Zone: " << GetTimeZone(TimeZoneCode)->Name << "\n";
426 void Calendar::Dump(std::ostream &os) const
428 ios_format_state state(os);
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 DumpSpecialFields(os);
438 // cycle through the type table
439 for( const FieldLink<Calendar> *b = CalendarFieldLinks;
440 b->type != CALFC_END;
441 b++ )
443 if( b->strMember ) {
444 const std::string &s = this->*(b->strMember);
445 if( s.size() )
446 os << " " << b->name << ": " << s << "\n";
448 else if( b->timeMember ) {
449 time_t t = this->*(b->timeMember);
450 if( t > 0 )
451 os << " " << b->name << ": " << ctime(&t);
452 else
453 os << " " << b->name << ": disabled\n";
455 else if( b->addrMember ) {
456 const EmailAddressList &al = this->*(b->addrMember);
457 EmailAddressList::const_iterator lb = al.begin(), le = al.end();
459 for( ; lb != le; ++lb ) {
460 if( !lb->size() )
461 continue;
463 os << " " << b->name << ": " << *lb << "\n";
468 // print recurrence data if available
469 RecurBase::Dump(os);
471 // print any unknowns
472 os << Unknowns;
475 bool Calendar::operator<(const Calendar &other) const
477 if( StartTime < other.StartTime )
478 return true;
479 else if( StartTime > other.StartTime )
480 return false;
482 int cmp = Subject.compare(other.Subject);
483 if( cmp == 0 )
484 cmp = Location.compare(other.Location);
485 return cmp < 0;
489 ///////////////////////////////////////////////////////////////////////////////
490 // Calendar-All class
492 // calendar-all field codes
493 #define CALALLFC_CALENDAR_ID 0x02 // Calendar using (new devices have several calendar)
494 #define CALALLFC_MAIL_ACCOUNT 0x03
495 #define CALALLFC_UNIQUEID 0x05
496 #define CALALLFC_CAL_OBJECT 0x0a
497 #define CALALLFC_END 0xffff
499 void CalendarAll::Clear()
501 Calendar::Clear();
503 MailAccount.clear();
506 void CalendarAll::ParseHeader(const Data &data, size_t &offset)
508 const unsigned char *b = (const unsigned char*) (data.GetData() + offset);
509 const unsigned char *e = (const unsigned char*) (data.GetData() + data.GetSize());
511 while( (b + COMMON_FIELD_HEADER_SIZE) < e ) {
512 const CommonField *field = (const CommonField *) b;
514 // advance and check size
515 b += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
516 if( b > e ) // if begin==end, we are ok
517 continue;
519 if( !btohs(field->size) ) // if field has no size, something's up
520 continue;
522 // handle special cases
523 if( field->type == CALALLFC_CAL_OBJECT )
525 b -= btohs(field->size);
526 // end of header
527 break;
530 switch( field->type )
532 case CALALLFC_CALENDAR_ID:
533 if( btohs(field->size) == 8 ) {
534 CalendarID = btohll(field->u.uint64);
536 else {
537 throw Error("CalendarAll::ParseField: size data unknown in calendar field");
539 continue;
541 case CALALLFC_MAIL_ACCOUNT:
542 MailAccount = ParseFieldString(field);
543 continue;
545 case CALALLFC_UNIQUEID:
546 if( btohs(field->size) == 4 ) {
547 RecordId = btohl(field->u.uint32);
549 else {
550 throw Error("CalendarAll::ParseHeader: size data unknown in calendar field");
552 continue;
555 // if still not handled, add to the Unknowns list
556 UnknownField uf;
557 uf.type = field->type;
558 uf.data.assign((const char*)field->u.raw, btohs(field->size));
559 Unknowns.push_back(uf);
562 offset += b - (data.GetData() + offset);
565 void CalendarAll::DumpSpecialFields(std::ostream &os) const
567 ios_format_state state(os);
569 Calendar::DumpSpecialFields(os);
570 os << " Mail Account: " << MailAccount << "\n";
573 } // namespace Barry