lib: implemented CalendarAll::GetFieldHandles()
[barry/progweb.git] / src / r_calendar.cc
blobb0884308c5a170acf86aa8bae99ba601bbe3e9c8
1 ///
2 /// \file r_calendar.cc
3 /// Blackberry database record parser class for calendar records.
4 ///
6 /*
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"
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 const std::vector<FieldHandle<Calendar> >& Calendar::GetFieldHandles()
408 static std::vector<FieldHandle<Calendar> > fhv;
410 if( fhv.size() )
411 return 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);
456 return fhv;
459 std::string Calendar::GetDescription() const
461 return Subject;
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";
475 if( TimeZoneValid )
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;
494 b++ )
496 if( b->strMember ) {
497 const std::string &s = this->*(b->strMember);
498 if( s.size() )
499 os << " " << b->name << ": " << s << "\n";
501 else if( b->timeMember ) {
502 time_t t = this->*(b->timeMember);
503 if( t > 0 )
504 os << " " << b->name << ": " << ctime(&t);
505 else
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 ) {
513 if( !lb->size() )
514 continue;
516 os << " " << b->name << ": " << *lb << "\n";
521 // print recurrence data if available
522 RecurBase::Dump(os);
524 // print any unknowns
525 os << Unknowns;
528 bool Calendar::operator<(const Calendar &other) const
530 if( StartTime < other.StartTime )
531 return true;
532 else if( StartTime > other.StartTime )
533 return false;
535 int cmp = Subject.compare(other.Subject);
536 if( cmp == 0 )
537 cmp = Location.compare(other.Location);
538 return cmp < 0;
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()
554 Calendar::Clear();
556 MailAccount.clear();
559 const std::vector<FieldHandle<CalendarAll> >& CalendarAll::GetFieldHandles()
561 static std::vector<FieldHandle<CalendarAll> > fhv;
563 if( fhv.size() )
564 return 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);
580 return fhv;
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
594 continue;
596 if( !btohs(field->size) ) // if field has no size, something's up
597 continue;
599 // handle special cases
600 if( field->type == CALALLFC_CAL_OBJECT )
602 b -= btohs(field->size);
603 // end of header
604 break;
607 switch( field->type )
609 case CALALLFC_CALENDAR_ID:
610 if( btohs(field->size) == 8 ) {
611 CalendarID = btohll(field->u.uint64);
613 else {
614 throw Error("CalendarAll::ParseField: size data unknown in calendar field");
616 continue;
618 case CALALLFC_MAIL_ACCOUNT:
619 MailAccount = ParseFieldString(field);
620 continue;
622 case CALALLFC_UNIQUEID:
623 if( btohs(field->size) == 4 ) {
624 RecordId = btohl(field->u.uint32);
626 else {
627 throw Error("CalendarAll::ParseHeader: size data unknown in calendar field");
629 continue;
632 // if still not handled, add to the Unknowns list
633 UnknownField uf;
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";
650 } // namespace Barry