lib: libbarryalx depends on libbarry for the ErrnoError exception
[barry.git] / src / r_calendar.cc
blobf4684d076bc4162748940f18b4aa2b8c2e846e2a
1 ///
2 /// \file r_calendar.cc
3 /// Blackberry database record parser class for calendar records.
4 ///
6 /*
7 Copyright (C) 2005-2010, 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>
37 #define __DEBUG_MODE__
38 #include "debug.h"
40 using namespace std;
41 using namespace Barry::Protocol;
43 namespace Barry {
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)
64 return f;
67 Calendar::ClassFlagType Calendar::ClassFlagProto2Rec(uint8_t f)
69 return (ClassFlagType)f;
72 uint8_t Calendar::ClassFlagRec2Proto(ClassFlagType f)
74 return f;
79 ///////////////////////////////////////////////////////////////////////////////
80 // Calendar class
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_CALENDAR_ID 0x2b // Calendar using (new devices have several calendar)
99 #define CALFC_ALLDAYEVENT_FLAG 0xff
100 #define CALFC_END 0xffff
102 static FieldLink<Calendar> CalendarFieldLinks[] = {
103 { CALFC_SUBJECT, "Subject", 0, 0, &Calendar::Subject, 0, 0, 0, 0, true },
104 { CALFC_NOTES, "Notes", 0, 0, &Calendar::Notes, 0, 0, 0, 0, true },
105 { CALFC_LOCATION, "Location", 0, 0, &Calendar::Location, 0, 0, 0, 0, true },
106 { CALFC_NOTIFICATION_TIME,"Notification Time",0,0, 0, 0, &Calendar::NotificationTime, 0, 0, false },
107 { CALFC_START_TIME, "Start Time", 0, 0, 0, 0, &Calendar::StartTime, 0, 0, false },
108 { CALFC_END_TIME, "End Time", 0, 0, 0, 0, &Calendar::EndTime, 0, 0, false },
109 { CALFC_ORGANIZER, "Organizer", 0, 0, 0, &Calendar::Organizer, 0, 0, 0, true },
110 { CALFC_ACCEPTED_BY,"Accepted By",0, 0, 0, &Calendar::AcceptedBy, 0, 0, 0, true },
111 { CALFC_INVITED, "Invited", 0, 0, 0, &Calendar::Invited, 0, 0, 0, true },
112 { CALFC_END, "End of List",0, 0, 0, 0, 0, 0, 0, false }
115 Calendar::Calendar()
117 Clear();
120 Calendar::~Calendar()
124 const unsigned char* Calendar::ParseField(const unsigned char *begin,
125 const unsigned char *end,
126 const IConverter *ic)
128 const CommonField *field = (const CommonField *) begin;
130 // advance and check size
131 begin += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
132 if( begin > end ) // if begin==end, we are ok
133 return begin;
135 if( !btohs(field->size) ) // if field has no size, something's up
136 return begin;
138 // cycle through the type table
139 for( FieldLink<Calendar> *b = CalendarFieldLinks;
140 b->type != CALFC_END;
141 b++ )
143 if( b->type == field->type ) {
144 if( b->strMember ) {
145 std::string &s = this->*(b->strMember);
146 s = ParseFieldString(field);
147 if( b->iconvNeeded && ic )
148 s = ic->FromBB(s);
149 return begin; // done!
151 else if( b->timeMember && btohs(field->size) == 4 ) {
152 time_t &t = this->*(b->timeMember);
153 dout("min1900: " << field->u.min1900);
154 t = min2time(field->u.min1900);
155 return begin;
157 else if( b->addrMember ) {
159 // parse email address
160 // get dual addr+name string first
161 // Note: this is a different format than
162 // used in r_message*.cc
164 std::string dual((const char*)field->u.raw, btohs(field->size));
166 EmailAddress a;
168 // assign first string, using null terminator
169 // letting std::string add it for us if it
170 // doesn't exist
171 a.Email = dual.c_str();
173 // assign second string, using first size
174 // as starting point
175 a.Name = dual.c_str() + a.Email.size() + 1;
177 // if the address is non-empty, add to list
178 if( a.size() ) {
179 // i18n convert if needed
180 if( b->iconvNeeded && ic ) {
181 a.Name = ic->FromBB(a.Name);
182 a.Email = ic->FromBB(a.Email);
185 EmailAddressList &al = this->*(b->addrMember);
186 al.push_back(a);
189 return begin;
194 // handle special cases
195 switch( field->type )
197 case CALFC_APPT_TYPE_FLAG:
198 switch( field->u.raw[0] )
200 case 'a': // regular non-recurring appointment
201 Recurring = false;
202 return begin;
204 case '*': // recurring appointment
205 Recurring = true;
206 return begin;
208 default:
209 throw Error("Calendar::ParseField: unknown appointment type");
211 break;
213 case CALFC_ALLDAYEVENT_FLAG:
214 AllDayEvent = field->u.raw[0] == 1;
215 return begin;
217 case CALFC_TIMEZONE_CODE:
218 if( btohs(field->size) == 2 ) {
219 // good data
220 TimeZoneCode = btohs(field->u.code);
221 TimeZoneValid = true;
223 else {
224 throw Error("Calendar::ParseField: not enough data in time zone code field");
226 return begin;
228 case CALFC_FREEBUSY_FLAG:
229 if( field->u.raw[0] > CR_FREEBUSY_RANGE_HIGH ) {
230 throw Error("Calendar::ParseField: FreeBusyFlag out of range" );
232 FreeBusyFlag = FreeBusyFlagProto2Rec(field->u.raw[0]);
233 return begin;
235 case CALFC_CALENDAR_ID:
236 if( btohs(field->size) == 8 ) {
237 CalendarID = btohll(field->u.uint64);
239 else {
240 throw Error("Calendar::ParseField: size data unknown in calendar field");
242 return begin;
244 case CALFC_CLASS_FLAG:
245 if( field->u.raw[0] > CR_CLASS_RANGE_HIGH ) {
246 throw Error("Calendar::ParseField: ClassFlag out of range" );
248 ClassFlag = ClassFlagProto2Rec(field->u.raw[0]);
249 return begin;
252 // base class handles recurring data
253 if( RecurBase::ParseField(field->type, field->u.raw, btohs(field->size), ic) )
254 return begin;
256 // if still not handled, add to the Unknowns list
257 UnknownField uf;
258 uf.type = field->type;
259 uf.data.assign((const char*)field->u.raw, btohs(field->size));
260 Unknowns.push_back(uf);
262 // return new pointer for next field
263 return begin;
266 void Calendar::ParseHeader(const Data &data, size_t &offset)
268 // no header in Calendar records
271 void Calendar::ParseFields(const Data &data, size_t &offset, const IConverter *ic)
273 const unsigned char *finish = ParseCommonFields(*this,
274 data.GetData() + offset, data.GetData() + data.GetSize(), ic);
275 offset += finish - (data.GetData() + offset);
278 void Calendar::BuildHeader(Data &data, size_t &offset) const
280 // no header in Calendar records
284 // Build
286 /// Build fields part of record.
288 void Calendar::BuildFields(Data &data, size_t &offset, const IConverter *ic) const
290 data.Zap();
292 // output the type first
293 BuildField(data, offset, CALFC_APPT_TYPE_FLAG, Recurring ? '*' : 'a');
295 // output all day event flag only if set
296 if( AllDayEvent )
297 BuildField(data, offset, CALFC_ALLDAYEVENT_FLAG, (char)1);
299 // cycle through the type table
300 for( const FieldLink<Calendar> *b = CalendarFieldLinks;
301 b->type != CALFC_END;
302 b++ )
304 if( b->strMember ) {
305 const std::string &s = this->*(b->strMember);
306 if( s.size() )
307 BuildField(data, offset, b->type, (b->iconvNeeded && ic) ? ic->ToBB(s) : s);
309 else if( b->timeMember ) {
310 time_t t = this->*(b->timeMember);
311 if( t > 0 )
312 BuildField1900(data, offset, b->type, t);
314 else if( b->addrMember ) {
315 const EmailAddressList &al = this->*(b->addrMember);
316 EmailAddressList::const_iterator lb = al.begin(), le = al.end();
318 // add all entries in list
319 for( ; lb != le; ++lb ) {
321 // skip empty entries
322 if( !lb->size() )
323 continue;
325 std::string Name = lb->Name,
326 Email = lb->Email;
328 // do i18n conversion only if needed
329 if( b->iconvNeeded && ic ) {
330 Name = ic->ToBB(Name);
331 Email = ic->ToBB(Email);
335 // Build an addr+name field, each string
336 // null terminated.
337 // Note: this is a different format than
338 // what is used in r_message*.cc
340 std::string field(lb->Email.c_str(), lb->Email.size() + 1);
341 field.append(lb->Name.c_str(), lb->Name.size() + 1);
342 BuildField(data, offset, b->type, field.data(), field.size());
347 // handle special cases
348 if( Recurring ) {
349 CalendarRecurrenceDataField recur;
350 BuildRecurrenceData(StartTime, &recur);
351 BuildField(data, offset, RecurBase::RecurringFieldType(),
352 &recur, CALENDAR_RECURRENCE_DATA_FIELD_SIZE);
355 if( TimeZoneValid )
356 BuildField(data, offset, CALFC_TIMEZONE_CODE, TimeZoneCode);
358 BuildField(data, offset, CALFC_FREEBUSY_FLAG, FreeBusyFlagRec2Proto(FreeBusyFlag));
359 BuildField(data, offset, CALFC_CLASS_FLAG, ClassFlagRec2Proto(ClassFlag));
361 // If CalendarID is defined and most of supported !
362 // (by default 0xffff ffff ffff ffff)
363 if( CalendarID != (uint64_t) -1 )
364 BuildField(data, offset, CALFC_CALENDAR_ID, CalendarID);
366 // and finally save unknowns
367 UnknownsType::const_iterator
368 ub = Unknowns.begin(), ue = Unknowns.end();
369 for( ; ub != ue; ub++ ) {
370 BuildField(data, offset, *ub);
373 data.ReleaseBuffer(offset);
376 void Calendar::Clear()
378 // clear the base class too
379 RecurBase::Clear();
381 // clear our fields
382 RecType = GetDefaultRecType();
383 RecordId = 0;
385 AllDayEvent = false;
386 Subject.clear();
387 Notes.clear();
388 Location.clear();
389 NotificationTime = StartTime = EndTime = 0;
390 Organizer.clear();
391 AcceptedBy.clear();
392 Invited.clear();
394 FreeBusyFlag = Free;
395 ClassFlag = Public;
397 CalendarID = btohll((uint64_t) -1);
399 TimeZoneCode = GetTimeZoneCode(0, 0); // default to GMT
400 TimeZoneValid = false;
402 Unknowns.clear();
405 void Calendar::DumpSpecialFields(std::ostream &os) const
407 static const char *ClassTypes[] = { "Public", "Confidential", "Private" };
408 static const char *FreeBusy[] = { "Free", "Tentative", "Busy", "Out of Office" };
410 os << " Calendar ID: 0x" << setbase(16) << CalendarID << "\n";
411 os << " All Day Event: " << (AllDayEvent ? "yes" : "no") << "\n";
412 os << " Class: " << ClassTypes[ClassFlag] << "\n";
413 os << " Free/Busy: " << FreeBusy[FreeBusyFlag] << "\n";
414 if( TimeZoneValid )
415 os << " Time Zone: " << GetTimeZone(TimeZoneCode)->Name << "\n";
418 void Calendar::Dump(std::ostream &os) const
421 // FIXME - need a "check all data" function that make sure that all
422 // recurrence data is within range. Then call that before using
423 // the data, such as in Build and in Dump.
425 os << "Calendar entry: 0x" << setbase(16) << RecordId
426 << " (" << (unsigned int)RecType << ")\n";
427 DumpSpecialFields(os);
429 // cycle through the type table
430 for( const FieldLink<Calendar> *b = CalendarFieldLinks;
431 b->type != CALFC_END;
432 b++ )
434 if( b->strMember ) {
435 const std::string &s = this->*(b->strMember);
436 if( s.size() )
437 os << " " << b->name << ": " << s << "\n";
439 else if( b->timeMember ) {
440 time_t t = this->*(b->timeMember);
441 if( t > 0 )
442 os << " " << b->name << ": " << ctime(&t);
443 else
444 os << " " << b->name << ": disabled\n";
446 else if( b->addrMember ) {
447 const EmailAddressList &al = this->*(b->addrMember);
448 EmailAddressList::const_iterator lb = al.begin(), le = al.end();
450 for( ; lb != le; ++lb ) {
451 if( !lb->size() )
452 continue;
454 os << " " << b->name << ": " << *lb << "\n";
459 // print recurrence data if available
460 RecurBase::Dump(os);
462 // print any unknowns
463 os << Unknowns;
466 bool Calendar::operator<(const Calendar &other) const
468 if( StartTime < other.StartTime )
469 return true;
470 else if( StartTime > other.StartTime )
471 return false;
473 int cmp = Subject.compare(other.Subject);
474 if( cmp == 0 )
475 cmp = Location.compare(other.Location);
476 return cmp < 0;
480 ///////////////////////////////////////////////////////////////////////////////
481 // Calendar-All class
483 // calendar-all field codes
484 #define CALALLFC_CALENDAR_ID 0x02 // Calendar using (new devices have several calendar)
485 #define CALALLFC_MAIL_ACCOUNT 0x03
486 #define CALALLFC_UNIQUEID 0x05
487 #define CALALLFC_CAL_OBJECT 0x0a
488 #define CALALLFC_END 0xffff
490 void CalendarAll::Clear()
492 Calendar::Clear();
494 MailAccount.clear();
497 void CalendarAll::ParseHeader(const Data &data, size_t &offset)
499 const unsigned char *b = (const unsigned char*) (data.GetData() + offset);
500 const unsigned char *e = (const unsigned char*) (data.GetData() + data.GetSize());
502 while( (b + COMMON_FIELD_HEADER_SIZE) < e ) {
503 const CommonField *field = (const CommonField *) b;
505 // advance and check size
506 b += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
507 if( b > e ) // if begin==end, we are ok
508 continue;
510 if( !btohs(field->size) ) // if field has no size, something's up
511 continue;
513 // handle special cases
514 if( field->type == CALALLFC_CAL_OBJECT )
516 b -= btohs(field->size);
517 // end of header
518 break;
521 switch( field->type )
523 case CALALLFC_CALENDAR_ID:
524 if( btohs(field->size) == 8 ) {
525 CalendarID = btohll(field->u.uint64);
527 else {
528 throw Error("CalendarAll::ParseField: size data unknown in calendar field");
530 continue;
532 case CALALLFC_MAIL_ACCOUNT:
533 MailAccount = ParseFieldString(field);
534 continue;
536 case CALALLFC_UNIQUEID:
537 if( btohs(field->size) == 4 ) {
538 RecordId = btohl(field->u.uint32);
540 else {
541 throw Error("CalendarAll::ParseHeader: size data unknown in calendar field");
543 continue;
546 // if still not handled, add to the Unknowns list
547 UnknownField uf;
548 uf.type = field->type;
549 uf.data.assign((const char*)field->u.raw, btohs(field->size));
550 Unknowns.push_back(uf);
553 offset += b - (data.GetData() + offset);
556 void CalendarAll::DumpSpecialFields(std::ostream &os) const
558 Calendar::DumpSpecialFields(os);
559 os << " Mail Account: " << MailAccount << "\n";
562 } // namespace Barry