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 void Calendar::DumpSpecialFields(std::ostream &os) const
00406 {
00407         static const char *ClassTypes[] = { "Public", "Confidential", "Private" };
00408         static const char *FreeBusy[] = { "Free", "Tentative", "Busy", "Out of Office" };
00409 
00410         os << "   Calendar ID: 0x" << setbase(16) << CalendarID << "\n";
00411         os << "   All Day Event: " << (AllDayEvent ? "yes" : "no") << "\n";
00412         os << "   Class: " << ClassTypes[ClassFlag] << "\n";
00413         os << "   Free/Busy: " << FreeBusy[FreeBusyFlag] << "\n";
00414         if( TimeZoneValid )
00415                 os << "   Time Zone: " << GetTimeZone(TimeZoneCode)->Name << "\n";
00416 }
00417 
00418 void Calendar::Dump(std::ostream &os) const
00419 {
00420 
00421 // FIXME - need a "check all data" function that make sure that all
00422 // recurrence data is within range.  Then call that before using
00423 // the data, such as in Build and in Dump.
00424 
00425         os << "Calendar entry: 0x" << setbase(16) << RecordId
00426                 << " (" << (unsigned int)RecType << ")\n";
00427         DumpSpecialFields(os);
00428 
00429         // cycle through the type table
00430         for(    const FieldLink<Calendar> *b = CalendarFieldLinks;
00431                 b->type != CALFC_END;
00432                 b++ )
00433         {
00434                 if( b->strMember ) {
00435                         const std::string &s = this->*(b->strMember);
00436                         if( s.size() )
00437                                 os << "   " << b->name << ": " << s << "\n";
00438                 }
00439                 else if( b->timeMember ) {
00440                         time_t t = this->*(b->timeMember);
00441                         if( t > 0 )
00442                                 os << "   " << b->name << ": " << ctime(&t);
00443                         else
00444                                 os << "   " << b->name << ": disabled\n";
00445                 }
00446                 else if( b->addrMember ) {
00447                         const EmailAddressList &al = this->*(b->addrMember);
00448                         EmailAddressList::const_iterator lb = al.begin(), le = al.end();
00449 
00450                         for( ; lb != le; ++lb ) {
00451                                 if( !lb->size() )
00452                                         continue;
00453 
00454                                 os << "   " << b->name << ": " << *lb << "\n";
00455                         }
00456                 }
00457         }
00458 
00459         // print recurrence data if available
00460         RecurBase::Dump(os);
00461 
00462         // print any unknowns
00463         os << Unknowns;
00464 }
00465 
00466 bool Calendar::operator<(const Calendar &other) const
00467 {
00468         if( StartTime < other.StartTime )
00469                 return true;
00470         else if( StartTime > other.StartTime )
00471                 return false;
00472 
00473         int cmp = Subject.compare(other.Subject);
00474         if( cmp == 0 )
00475                 cmp = Location.compare(other.Location);
00476         return cmp < 0;
00477 }
00478 
00479 
00480 ///////////////////////////////////////////////////////////////////////////////
00481 // Calendar-All class
00482 
00483 // calendar-all field codes
00484 #define CALALLFC_CALENDAR_ID            0x02    // Calendar using (new devices have several calendar)
00485 #define CALALLFC_MAIL_ACCOUNT           0x03
00486 #define CALALLFC_UNIQUEID                       0x05
00487 #define CALALLFC_CAL_OBJECT                     0x0a
00488 #define CALALLFC_END                            0xffff
00489 
00490 void CalendarAll::Clear()
00491 {
00492         Calendar::Clear();
00493 
00494         MailAccount.clear();
00495 }
00496 
00497 void CalendarAll::ParseHeader(const Data &data, size_t &offset)
00498 {
00499         const unsigned char *b = (const unsigned char*) (data.GetData() + offset);
00500         const unsigned char *e = (const unsigned char*) (data.GetData() + data.GetSize());
00501 
00502         while( (b + COMMON_FIELD_HEADER_SIZE) < e ) {
00503                 const CommonField *field = (const CommonField *) b;
00504 
00505                 // advance and check size
00506                 b += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
00507                 if( b > e )                                     // if begin==end, we are ok
00508                         continue;
00509 
00510                 if( !btohs(field->size) )       // if field has no size, something's up
00511                         continue;
00512 
00513                 // handle special cases
00514                 if( field->type == CALALLFC_CAL_OBJECT )
00515                 {
00516                         b -= btohs(field->size);
00517                         // end of header
00518                         break;
00519                 }
00520 
00521                 switch( field->type )
00522                 {
00523                 case CALALLFC_CALENDAR_ID:
00524                         if( btohs(field->size) == 8 ) {
00525                                 CalendarID = btohll(field->u.uint64);
00526                         }
00527                         else {
00528                                 throw Error("CalendarAll::ParseField: size data unknown in calendar field");
00529                         }
00530                         continue;
00531 
00532                 case CALALLFC_MAIL_ACCOUNT:
00533                         MailAccount = ParseFieldString(field);
00534                         continue;
00535 
00536                 case CALALLFC_UNIQUEID:
00537                         if( btohs(field->size) == 4 ) {
00538                                 RecordId = btohl(field->u.uint32);
00539                         }
00540                         else {
00541                                 throw Error("CalendarAll::ParseHeader: size data unknown in calendar field");
00542                         }
00543                         continue;
00544                 }
00545 
00546                 // if still not handled, add to the Unknowns list
00547                 UnknownField uf;
00548                 uf.type = field->type;
00549                 uf.data.assign((const char*)field->u.raw, btohs(field->size));
00550                 Unknowns.push_back(uf);
00551         }
00552 
00553         offset += b - (data.GetData() + offset);
00554 }
00555 
00556 void CalendarAll::DumpSpecialFields(std::ostream &os) const
00557 {
00558         Calendar::DumpSpecialFields(os);
00559         os << "   Mail Account: " << MailAccount << "\n";
00560 }
00561 
00562 } // namespace Barry
00563