# r_calendar.cc

Go to the documentation of this file.

    00001 ///
    00002 /// \file       r_calendar.cc
    00003 ///             Blackberry database record parser class for calendar records.
    00004 ///
    00005
    00006 /*
    00007     Copyright (C) 2005-2011, Net Direct Inc. (http://www.netdirect.ca/)
    00008
    00009     This program is free software; you can redistribute it and/or modify
    00010     it under the terms of the GNU General Public License as published by
    00011     the Free Software Foundation; either version 2 of the License, or
    00012     (at your option) any later version.
    00013
    00014     This program is distributed in the hope that it will be useful,
    00015     but WITHOUT ANY WARRANTY; without even the implied warranty of
    00016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    00017
    00018     See the GNU General Public License in the COPYING file at the
    00019     root directory of this project for more details.
    00020 */
    00021
    00022 #include "r_calendar.h"
    00023 #include "record-internal.h"
    00024 #include "protocol.h"
    00025 #include "protostructs.h"
    00026 #include "data.h"
    00027 #include "time.h"
    00028 #include "error.h"
    00029 #include "endian.h"
    00030 #include "iconv.h"
    00031 #include <ostream>
    00032 #include <iomanip>
    00033 #include <time.h>
    00034 #include <string.h>
    00035 #include <stdexcept>
    00036
    00037 #define __DEBUG_MODE__
    00038 #include "debug.h"
    00039
    00040 using namespace std;
    00041 using namespace Barry::Protocol;
    00042
    00043 namespace Barry {
    00044
    00045
    00046 ///////////////////////////////////////////////////////////////////////////////
    00047 // Calendar class, static members
    00048
    00049 //
    00050 // Note! These functions currently only pass the same values through.
    00051 //       In actuality, these are technically two different values:
    00052 //       one on the raw protocol side, and the other part of the
    00053 //       guaranteed Barry API.  If the Blackberry ever changes the
    00054 //       meanings for these codes, do the translation here.
    00055 //
    00056
    00057 Calendar::FreeBusyFlagType Calendar::FreeBusyFlagProto2Rec(uint8_t f)
    00058 {
    00059         return (FreeBusyFlagType)f;
    00060 }
    00061
    00062 uint8_t Calendar::FreeBusyFlagRec2Proto(FreeBusyFlagType f)
    00063 {
    00064         return f;
    00065 }
    00066
    00067 Calendar::ClassFlagType Calendar::ClassFlagProto2Rec(uint8_t f)
    00068 {
    00069         return (ClassFlagType)f;
    00070 }
    00071
    00072 uint8_t Calendar::ClassFlagRec2Proto(ClassFlagType f)
    00073 {
    00074         return f;
    00075 }
    00076
    00077
    00078
    00079 ///////////////////////////////////////////////////////////////////////////////
    00080 // Calendar class
    00081
    00082 // calendar field codes
    00083 #define CALFC_APPT_TYPE_FLAG            0x01
    00084 #define CALFC_SUBJECT                   0x02
    00085 #define CALFC_NOTES                     0x03
    00086 #define CALFC_LOCATION                  0x04
    00087 #define CALFC_NOTIFICATION_TIME         0x05
    00088 #define CALFC_START_TIME                0x06
    00089 #define CALFC_END_TIME                  0x07
    00090 #define CALFC_ACCEPTED_BY               0x0b
    00091 #define CALFC_VERSION_DATA              0x10
    00092 #define CALFC_INVITED                   0x15
    00093 #define CALFC_ORGANIZER                 0x16
    00094 #define CALFC_NOTIFICATION_DATA         0x1a
    00095 #define CALFC_FREEBUSY_FLAG             0x1c
    00096 #define CALFC_TIMEZONE_CODE             0x1e    // only seems to show up if recurring
    00097 #define CALFC_CLASS_FLAG                0x28    // private flag from outlook
    00098 #define CALFC_CALENDAR_ID               0x2b    // Calendar using (new devices have several calendar)
    00099 #define CALFC_ALLDAYEVENT_FLAG          0xff
    00100 #define CALFC_END                       0xffff
    00101
    00102 static FieldLink<Calendar> CalendarFieldLinks[] = {
    00103    { CALFC_SUBJECT,    "Subject",    0, 0,    &Calendar::Subject, 0, 0, 0, 0, true },
    00104    { CALFC_NOTES,      "Notes",      0, 0,    &Calendar::Notes, 0, 0, 0, 0, true },
    00105    { CALFC_LOCATION,   "Location",   0, 0,    &Calendar::Location, 0, 0, 0, 0, true },
    00106    { CALFC_NOTIFICATION_TIME,"Notification Time",0,0, 0, 0, &Calendar::NotificationTime, 0, 0, false },
    00107    { CALFC_START_TIME, "Start Time", 0, 0,    0, 0, &Calendar::StartTime, 0, 0, false },
    00108    { CALFC_END_TIME,   "End Time",   0, 0,    0, 0, &Calendar::EndTime, 0, 0, false },
    00109    { CALFC_ORGANIZER,  "Organizer",  0, 0,    0, &Calendar::Organizer, 0, 0, 0, true },
    00110    { CALFC_ACCEPTED_BY,"Accepted By",0, 0,    0, &Calendar::AcceptedBy, 0, 0, 0, true },
    00111    { CALFC_INVITED,    "Invited",    0, 0,    0, &Calendar::Invited, 0, 0, 0, true },
    00112    { CALFC_END,        "End of List",0, 0,    0, 0, 0, 0, 0, false }
    00113 };
    00114
    00115 Calendar::Calendar()
    00116 {
    00117         Clear();
    00118 }
    00119
    00120 Calendar::~Calendar()
    00121 {
    00122 }
    00123
    00124 const unsigned char* Calendar::ParseField(const unsigned char *begin,
    00125                                           const unsigned char *end,
    00126                                           const IConverter *ic)
    00127 {
    00128         const CommonField *field = (const CommonField *) begin;
    00129
    00130         // advance and check size
    00131         begin += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
    00132         if( begin > end )               // if begin==end, we are ok
    00133                 return begin;
    00134
    00135         if( !btohs(field->size) )       // if field has no size, something's up
    00136                 return begin;
    00137
    00138         // cycle through the type table
    00139         for(    FieldLink<Calendar> *b = CalendarFieldLinks;
    00140                 b->type != CALFC_END;
    00141                 b++ )
    00142         {
    00143                 if( b->type == field->type ) {
    00144                         if( b->strMember ) {
    00145                                 std::string &s = this->*(b->strMember);
    00146                                 s = ParseFieldString(field);
    00147                                 if( b->iconvNeeded && ic )
    00148                                         s = ic->FromBB(s);
    00149                                 return begin;   // done!
    00150                         }
    00151                         else if( b->timeMember && btohs(field->size) == 4 ) {
    00152                                 time_t &t = this->*(b->timeMember);
    00153                                 dout("min1900: " << field->u.min1900);
    00154                                 t = min2time(field->u.min1900);
    00155                                 return begin;
    00156                         }
    00157                         else if( b->addrMember ) {
    00158                                 //
    00159                                 // parse email address
    00160                                 // get dual addr+name string first
    00161                                 // Note: this is a different format than
    00162                                 // used in r_message*.cc
    00163                                 //
    00164                                 std::string dual((const char*)field->u.raw, btohs(field->size));
    00165
    00166                                 EmailAddress a;
    00167
    00168                                 // assign first string, using null terminator
    00169                                 // letting std::string add it for us if it
    00170                                 // doesn't exist
    00171                                 a.Email = dual.c_str();
    00172
    00173                                 // assign second string, using first size
    00174                                 // as starting point
    00175                                 a.Name = dual.c_str() + a.Email.size() + 1;
    00176
    00177                                 // if the address is non-empty, add to list
    00178                                 if( a.size() ) {
    00179                                         // i18n convert if needed
    00180                                         if( b->iconvNeeded && ic ) {
    00181                                                 a.Name = ic->FromBB(a.Name);
    00182                                                 a.Email = ic->FromBB(a.Email);
    00183                                         }
    00184
    00185                                         EmailAddressList &al = this->*(b->addrMember);
    00186                                         al.push_back(a);
    00187                                 }
    00188
    00189                                 return begin;
    00190                         }
    00191                 }
    00192         }
    00193
    00194         // handle special cases
    00195         switch( field->type )
    00196         {
    00197         case CALFC_APPT_TYPE_FLAG:
    00198                 switch( field->u.raw[0] )
    00199                 {
    00200                 case 'a':                       // regular non-recurring appointment
    00201                         Recurring = false;
    00202                         return begin;
    00203
    00204                 case '*':                       // recurring appointment
    00205                         Recurring = true;
    00206                         return begin;
    00207
    00208                 default:
    00209                         throw Error("Calendar::ParseField: unknown appointment type");
    00210                 }
    00211                 break;
    00212
    00213         case CALFC_ALLDAYEVENT_FLAG:
    00214                 AllDayEvent = field->u.raw[0] == 1;
    00215                 return begin;
    00216
    00217         case CALFC_TIMEZONE_CODE:
    00218                 if( btohs(field->size) == 2 ) {
    00219                         // good data
    00220                         TimeZoneCode = btohs(field->u.code);
    00221                         TimeZoneValid = true;
    00222                 }
    00223                 else {
    00224                         throw Error("Calendar::ParseField: not enough data in time zone code field");
    00225                 }
    00226                 return begin;
    00227
    00228         case CALFC_FREEBUSY_FLAG:
    00229                 if( field->u.raw[0] > CR_FREEBUSY_RANGE_HIGH ) {
    00230                         throw Error("Calendar::ParseField: FreeBusyFlag out of range" );
    00231                 }
    00232                 FreeBusyFlag = FreeBusyFlagProto2Rec(field->u.raw[0]);
    00233                 return begin;
    00234
    00235         case CALFC_CALENDAR_ID:
    00236                 if( btohs(field->size) == 8 ) {
    00237                         CalendarID = btohll(field->u.uint64);
    00238                 }
    00239                 else {
    00240                         throw Error("Calendar::ParseField: size data unknown in calendar field");
    00241                 }
    00242                 return begin;
    00243
    00244         case CALFC_CLASS_FLAG:
    00245                 if( field->u.raw[0] > CR_CLASS_RANGE_HIGH ) {
    00246                         throw Error("Calendar::ParseField: ClassFlag out of range" );
    00247                 }
    00248                 ClassFlag = ClassFlagProto2Rec(field->u.raw[0]);
    00249                 return begin;
    00250         }
    00251
    00252         // base class handles recurring data
    00253         if( RecurBase::ParseField(field->type, field->u.raw, btohs(field->size), ic) )
    00254                 return begin;
    00255
    00256         // if still not handled, add to the Unknowns list
    00257         UnknownField uf;
    00258         uf.type = field->type;
    00259         uf.data.assign((const char*)field->u.raw, btohs(field->size));
    00260         Unknowns.push_back(uf);
    00261
    00262         // return new pointer for next field
    00263         return begin;
    00264 }
    00265
    00266 void Calendar::ParseHeader(const Data &data, size_t &offset)
    00267 {
    00268         // no header in Calendar records
    00269 }
    00270
    00271 void Calendar::ParseFields(const Data &data, size_t &offset, const IConverter *ic)
    00272 {
    00273         const unsigned char *finish = ParseCommonFields(*this,
    00274                 data.GetData() + offset, data.GetData() + data.GetSize(), ic);
    00275         offset += finish - (data.GetData() + offset);
    00276 }
    00277
    00278 void Calendar::BuildHeader(Data &data, size_t &offset) const
    00279 {
    00280         // no header in Calendar records
    00281 }
    00282
    00283 //
    00284 // Build
    00285 //
    00286 /// Build fields part of record.
    00287 ///
    00288 void Calendar::BuildFields(Data &data, size_t &offset, const IConverter *ic) const
    00289 {
    00290         data.Zap();
    00291
    00292         // output the type first
    00293         BuildField(data, offset, CALFC_APPT_TYPE_FLAG, Recurring ? '*' : 'a');
    00294
    00295         // output all day event flag only if set
    00296         if( AllDayEvent )
    00297                 BuildField(data, offset, CALFC_ALLDAYEVENT_FLAG, (char)1);
    00298
    00299         // cycle through the type table
    00300         for(    const FieldLink<Calendar> *b = CalendarFieldLinks;
    00301                 b->type != CALFC_END;
    00302                 b++ )
    00303         {
    00304                 if( b->strMember ) {
    00305                         const std::string &s = this->*(b->strMember);
    00306                         if( s.size() )
    00307                                 BuildField(data, offset, b->type, (b->iconvNeeded && ic) ? ic->ToBB(s) : s);
    00308                 }
    00309                 else if( b->timeMember ) {
    00310                         time_t t = this->*(b->timeMember);
    00311                         if( t > 0 )
    00312                                 BuildField1900(data, offset, b->type, t);
    00313                 }
    00314                 else if( b->addrMember ) {
    00315                         const EmailAddressList &al = this->*(b->addrMember);
    00316                         EmailAddressList::const_iterator lb = al.begin(), le = al.end();
    00317
    00318                         // add all entries in list
    00319                         for( ; lb != le; ++lb ) {
    00320
    00321                                 // skip empty entries
    00322                                 if( !lb->size() )
    00323                                         continue;
    00324
    00325                                 std::string Name = lb->Name,
    00326                                         Email = lb->Email;
    00327
    00328                                 // do i18n conversion only if needed
    00329                                 if( b->iconvNeeded && ic ) {
    00330                                         Name = ic->ToBB(Name);
    00331                                         Email = ic->ToBB(Email);
    00332                                 }
    00333
    00334                                 //
    00335                                 // Build an addr+name field, each string
    00336                                 // null terminated.
    00337                                 // Note: this is a different format than
    00338                                 // what is used in r_message*.cc
    00339                                 //
    00340                                 std::string field(lb->Email.c_str(), lb->Email.size() + 1);
    00341                                 field.append(lb->Name.c_str(), lb->Name.size() + 1);
    00342                                 BuildField(data, offset, b->type, field.data(), field.size());
    00343                         }
    00344                 }
    00345         }
    00346
    00347         // handle special cases
    00348         if( Recurring ) {
    00349                 CalendarRecurrenceDataField recur;
    00350                 BuildRecurrenceData(StartTime, &recur);
    00351                 BuildField(data, offset, RecurBase::RecurringFieldType(),
    00352                         &recur, CALENDAR_RECURRENCE_DATA_FIELD_SIZE);
    00353         }
    00354
    00355         if( TimeZoneValid )
    00356                 BuildField(data, offset, CALFC_TIMEZONE_CODE, TimeZoneCode);
    00357
    00358         BuildField(data, offset, CALFC_FREEBUSY_FLAG, FreeBusyFlagRec2Proto(FreeBusyFlag));
    00359         BuildField(data, offset, CALFC_CLASS_FLAG, ClassFlagRec2Proto(ClassFlag));
    00360
    00361         // If CalendarID is defined and most of supported !
    00362         // (by default 0xffff ffff ffff ffff)
    00363         if( CalendarID != (uint64_t) -1 )
    00364                 BuildField(data, offset, CALFC_CALENDAR_ID, CalendarID);
    00365
    00366         // and finally save unknowns
    00367         UnknownsType::const_iterator
    00368                 ub = Unknowns.begin(), ue = Unknowns.end();
    00369         for( ; ub != ue; ub++ ) {
    00370                 BuildField(data, offset, *ub);
    00371         }
    00372
    00373         data.ReleaseBuffer(offset);
    00374 }
    00375
    00376 void Calendar::Clear()
    00377 {
    00378         // clear the base class too
    00379         RecurBase::Clear();
    00380
    00381         // clear our fields
    00382         RecType = GetDefaultRecType();
    00383         RecordId = 0;
    00384
    00385         AllDayEvent = false;
    00386         Subject.clear();
    00387         Notes.clear();
    00388         Location.clear();
    00389         NotificationTime = StartTime = EndTime = 0;
    00390         Organizer.clear();
    00391         AcceptedBy.clear();
    00392         Invited.clear();
    00393
    00394         FreeBusyFlag = Free;
    00395         ClassFlag = Public;
    00396
    00397         CalendarID = btohll((uint64_t) -1);
    00398
    00399         TimeZoneCode = GetTimeZoneCode(0, 0);   // default to GMT
    00400         TimeZoneValid = false;
    00401
    00402         Unknowns.clear();
    00403 }
    00404
    00405 std::string Calendar::GetDescription() const
    00406 {
    00407         return Subject;
    00408 }
    00409
    00410 void Calendar::DumpSpecialFields(std::ostream &os) const
    00411 {
    00412         static const char *ClassTypes[] = { "Public", "Confidential", "Private" };
    00413         static const char *FreeBusy[] = { "Free", "Tentative", "Busy", "Out of Office" };
    00414
    00415         os << "   Calendar ID: 0x" << setbase(16) << CalendarID << "\n";
    00416         os << "   All Day Event: " << (AllDayEvent ? "yes" : "no") << "\n";
    00417         os << "   Class: " << ClassTypes[ClassFlag] << "\n";
    00418         os << "   Free/Busy: " << FreeBusy[FreeBusyFlag] << "\n";
    00419         if( TimeZoneValid )
    00420                 os << "   Time Zone: " << GetTimeZone(TimeZoneCode)->Name << "\n";
    00421 }
    00422
    00423 void Calendar::Dump(std::ostream &os) const
    00424 {
    00425
    00426 // FIXME - need a "check all data" function that make sure that all
    00427 // recurrence data is within range.  Then call that before using
    00428 // the data, such as in Build and in Dump.
    00429
    00430         os << "Calendar entry: 0x" << setbase(16) << RecordId
    00431                 << " (" << (unsigned int)RecType << ")\n";
    00432         DumpSpecialFields(os);
    00433
    00434         // cycle through the type table
    00435         for(    const FieldLink<Calendar> *b = CalendarFieldLinks;
    00436                 b->type != CALFC_END;
    00437                 b++ )
    00438         {
    00439                 if( b->strMember ) {
    00440                         const std::string &s = this->*(b->strMember);
    00441                         if( s.size() )
    00442                                 os << "   " << b->name << ": " << s << "\n";
    00443                 }
    00444                 else if( b->timeMember ) {
    00445                         time_t t = this->*(b->timeMember);
    00446                         if( t > 0 )
    00447                                 os << "   " << b->name << ": " << ctime(&t);
    00448                         else
    00449                                 os << "   " << b->name << ": disabled\n";
    00450                 }
    00451                 else if( b->addrMember ) {
    00452                         const EmailAddressList &al = this->*(b->addrMember);
    00453                         EmailAddressList::const_iterator lb = al.begin(), le = al.end();
    00454
    00455                         for( ; lb != le; ++lb ) {
    00456                                 if( !lb->size() )
    00457                                         continue;
    00458
    00459                                 os << "   " << b->name << ": " << *lb << "\n";
    00460                         }
    00461                 }
    00462         }
    00463
    00464         // print recurrence data if available
    00465         RecurBase::Dump(os);
    00466
    00467         // print any unknowns
    00468         os << Unknowns;
    00469 }
    00470
    00471 bool Calendar::operator<(const Calendar &other) const
    00472 {
    00473         if( StartTime < other.StartTime )
    00474                 return true;
    00475         else if( StartTime > other.StartTime )
    00476                 return false;
    00477
    00478         int cmp = Subject.compare(other.Subject);
    00479         if( cmp == 0 )
    00480                 cmp = Location.compare(other.Location);
    00481         return cmp < 0;
    00482 }
    00483
    00484
    00485 ///////////////////////////////////////////////////////////////////////////////
    00486 // Calendar-All class
    00487
    00488 // calendar-all field codes
    00489 #define CALALLFC_CALENDAR_ID            0x02    // Calendar using (new devices have several calendar)
    00490 #define CALALLFC_MAIL_ACCOUNT           0x03
    00491 #define CALALLFC_UNIQUEID                       0x05
    00492 #define CALALLFC_CAL_OBJECT                     0x0a
    00493 #define CALALLFC_END                            0xffff
    00494
    00495 void CalendarAll::Clear()
    00496 {
    00497         Calendar::Clear();
    00498
    00499         MailAccount.clear();
    00500 }
    00501
    00502 void CalendarAll::ParseHeader(const Data &data, size_t &offset)
    00503 {
    00504         const unsigned char *b = (const unsigned char*) (data.GetData() + offset);
    00505         const unsigned char *e = (const unsigned char*) (data.GetData() + data.GetSize());
    00506
    00507         while( (b + COMMON_FIELD_HEADER_SIZE) < e ) {
    00508                 const CommonField *field = (const CommonField *) b;
    00509
    00510                 // advance and check size
    00511                 b += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
    00512                 if( b > e )                                     // if begin==end, we are ok
    00513                         continue;
    00514
    00515                 if( !btohs(field->size) )       // if field has no size, something's up
    00516                         continue;
    00517
    00518                 // handle special cases
    00519                 if( field->type == CALALLFC_CAL_OBJECT )
    00520                 {
    00521                         b -= btohs(field->size);
    00522                         // end of header
    00523                         break;
    00524                 }
    00525
    00526                 switch( field->type )
    00527                 {
    00528                 case CALALLFC_CALENDAR_ID:
    00529                         if( btohs(field->size) == 8 ) {
    00530                                 CalendarID = btohll(field->u.uint64);
    00531                         }
    00532                         else {
    00533                                 throw Error("CalendarAll::ParseField: size data unknown in calendar field");
    00534                         }
    00535                         continue;
    00536
    00537                 case CALALLFC_MAIL_ACCOUNT:
    00538                         MailAccount = ParseFieldString(field);
    00539                         continue;
    00540
    00541                 case CALALLFC_UNIQUEID:
    00542                         if( btohs(field->size) == 4 ) {
    00543                                 RecordId = btohl(field->u.uint32);
    00544                         }
    00545                         else {
    00546                                 throw Error("CalendarAll::ParseHeader: size data unknown in calendar field");
    00547                         }
    00548                         continue;
    00549                 }
    00550
    00551                 // if still not handled, add to the Unknowns list
    00552                 UnknownField uf;
    00553                 uf.type = field->type;
    00554                 uf.data.assign((const char*)field->u.raw, btohs(field->size));
    00555                 Unknowns.push_back(uf);
    00556         }
    00557
    00558         offset += b - (data.GetData() + offset);
    00559 }
    00560
    00561 void CalendarAll::DumpSpecialFields(std::ostream &os) const
    00562 {
    00563         Calendar::DumpSpecialFields(os);
    00564         os << "   Mail Account: " << MailAccount << "\n";
    00565 }
    00566
    00567 } // namespace Barry
    00568