Add Browser Bookmark reverse documentation.
[barry.git] / src / r_calendar.cc
blob3419b7ca1f3498bb689d218d2e75aef1fc5af9d8
1 ///
2 /// \file r_calendar.cc
3 /// Blackberry database record parser class for calendar records.
4 ///
6 /*
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"
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_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 }
114 Calendar::Calendar()
116 Clear();
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
132 return begin;
134 if( !btohs(field->size) ) // if field has no size, something's up
135 return begin;
137 // cycle through the type table
138 for( FieldLink<Calendar> *b = CalendarFieldLinks;
139 b->type != CALFC_END;
140 b++ )
142 if( b->type == field->type ) {
143 if( b->strMember ) {
144 std::string &s = this->*(b->strMember);
145 s = ParseFieldString(field);
146 if( b->iconvNeeded && ic )
147 s = ic->FromBB(s);
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);
154 return begin;
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));
165 EmailAddress a;
167 // assign first string, using null terminator
168 // letting std::string add it for us if it
169 // doesn't exist
170 a.Email = dual.c_str();
172 // assign second string, using first size
173 // as starting point
174 a.Name = dual.c_str() + a.Email.size() + 1;
176 // if the address is non-empty, add to list
177 if( a.size() ) {
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);
185 al.push_back(a);
188 return begin;
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
200 Recurring = false;
201 return begin;
203 case '*': // recurring appointment
204 Recurring = true;
205 return begin;
207 default:
208 throw Error("Calendar::ParseField: unknown appointment type");
210 break;
212 case CALFC_ALLDAYEVENT_FLAG:
213 AllDayEvent = field->u.raw[0] == 1;
214 return begin;
216 case CALFC_TIMEZONE_CODE:
217 if( btohs(field->size) == 2 ) {
218 // good data
219 TimeZoneCode = btohs(field->u.code);
220 TimeZoneValid = true;
222 else {
223 throw Error("Calendar::ParseField: not enough data in time zone code field");
225 return begin;
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]);
232 return begin;
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]);
239 return begin;
242 // base class handles recurring data
243 if( RecurBase::ParseField(field->type, field->u.raw, btohs(field->size), ic) )
244 return begin;
246 // if still not handled, add to the Unknowns list
247 UnknownField uf;
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
253 return begin;
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
274 // Build
276 /// Build fields part of record.
278 void Calendar::BuildFields(Data &data, size_t &offset, const IConverter *ic) const
280 data.Zap();
282 // output the type first
283 BuildField(data, offset, CALFC_APPT_TYPE_FLAG, Recurring ? '*' : 'a');
285 // output all day event flag only if set
286 if( AllDayEvent )
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;
292 b++ )
294 if( b->strMember ) {
295 const std::string &s = this->*(b->strMember);
296 if( s.size() )
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);
301 if( t > 0 )
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
312 if( !lb->size() )
313 continue;
315 std::string Name = lb->Name,
316 Email = lb->Email;
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
326 // null terminated.
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
338 if( Recurring ) {
339 CalendarRecurrenceDataField recur;
340 BuildRecurrenceData(StartTime, &recur);
341 BuildField(data, offset, RecurBase::RecurringFieldType(),
342 &recur, CALENDAR_RECURRENCE_DATA_FIELD_SIZE);
345 if( TimeZoneValid )
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()
363 RecurBase::Clear();
365 RecType = Calendar::GetDefaultRecType();
367 AllDayEvent = false;
368 Subject.clear();
369 Notes.clear();
370 Location.clear();
371 NotificationTime = StartTime = EndTime = 0;
373 FreeBusyFlag = Free;
374 ClassFlag = Public;
376 TimeZoneCode = GetTimeZoneCode(0, 0); // default to GMT
377 TimeZoneValid = false;
379 Unknowns.clear();
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";
396 if( TimeZoneValid )
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;
402 b++ )
404 if( b->strMember ) {
405 const std::string &s = this->*(b->strMember);
406 if( s.size() )
407 os << " " << b->name << ": " << s << "\n";
409 else if( b->timeMember ) {
410 time_t t = this->*(b->timeMember);
411 if( t > 0 )
412 os << " " << b->name << ": " << ctime(&t);
413 else
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 ) {
421 if( !lb->size() )
422 continue;
424 os << " " << b->name << ": " << *lb << "\n";
429 // print recurrence data if available
430 RecurBase::Dump(os);
432 // print any unknowns
433 os << Unknowns;
437 } // namespace Barry