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-2009, 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_ALLDAYEVENT_FLAG          0xff
00099 #define CALFC_END                       0xffff
00100 
00101 static FieldLink<Calendar> CalendarFieldLinks[] = {
00102    { CALFC_SUBJECT,    "Subject",    0, 0,    &Calendar::Subject, 0, 0, 0, 0, true },
00103    { CALFC_NOTES,      "Notes",      0, 0,    &Calendar::Notes, 0, 0, 0, 0, true },
00104    { CALFC_LOCATION,   "Location",   0, 0,    &Calendar::Location, 0, 0, 0, 0, true },
00105    { CALFC_NOTIFICATION_TIME,"Notification Time",0,0, 0, 0, &Calendar::NotificationTime, 0, 0, false },
00106    { CALFC_START_TIME, "Start Time", 0, 0,    0, 0, &Calendar::StartTime, 0, 0, false },
00107    { CALFC_END_TIME,   "End Time",   0, 0,    0, 0, &Calendar::EndTime, 0, 0, false },
00108    { CALFC_ORGANIZER,  "Organizer",  0, 0,    0, &Calendar::Organizer, 0, 0, 0, true },
00109    { CALFC_ACCEPTED_BY,"Accepted By",0, 0,    0, &Calendar::AcceptedBy, 0, 0, 0, true },
00110    { CALFC_INVITED,    "Invited",    0, 0,    0, &Calendar::Invited, 0, 0, 0, true },
00111    { CALFC_END,        "End of List",0, 0,    0, 0, 0, 0, 0, false }
00112 };
00113 
00114 Calendar::Calendar()
00115 {
00116         Clear();
00117 }
00118 
00119 Calendar::~Calendar()
00120 {
00121 }
00122 
00123 const unsigned char* Calendar::ParseField(const unsigned char *begin,
00124                                           const unsigned char *end,
00125                                           const IConverter *ic)
00126 {
00127         const CommonField *field = (const CommonField *) begin;
00128 
00129         // advance and check size
00130         begin += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
00131         if( begin > end )               // if begin==end, we are ok
00132                 return begin;
00133 
00134         if( !btohs(field->size) )       // if field has no size, something's up
00135                 return begin;
00136 
00137         // cycle through the type table
00138         for(    FieldLink<Calendar> *b = CalendarFieldLinks;
00139                 b->type != CALFC_END;
00140                 b++ )
00141         {
00142                 if( b->type == field->type ) {
00143                         if( b->strMember ) {
00144                                 std::string &s = this->*(b->strMember);
00145                                 s = ParseFieldString(field);
00146                                 if( b->iconvNeeded && ic )
00147                                         s = ic->FromBB(s);
00148                                 return begin;   // done!
00149                         }
00150                         else if( b->timeMember && btohs(field->size) == 4 ) {
00151                                 time_t &t = this->*(b->timeMember);
00152                                 dout("min1900: " << field->u.min1900);
00153                                 t = min2time(field->u.min1900);
00154                                 return begin;
00155                         }
00156                         else if( b->addrMember ) {
00157                                 //
00158                                 // parse email address
00159                                 // get dual addr+name string first
00160                                 // Note: this is a different format than
00161                                 // used in r_message*.cc
00162                                 //
00163                                 std::string dual((const char*)field->u.raw, btohs(field->size));
00164 
00165                                 EmailAddress a;
00166 
00167                                 // assign first string, using null terminator
00168                                 // letting std::string add it for us if it
00169                                 // doesn't exist
00170                                 a.Email = dual.c_str();
00171 
00172                                 // assign second string, using first size
00173                                 // as starting point
00174                                 a.Name = dual.c_str() + a.Email.size() + 1;
00175 
00176                                 // if the address is non-empty, add to list
00177                                 if( a.size() ) {
00178                                         // i18n convert if needed
00179                                         if( b->iconvNeeded && ic ) {
00180                                                 a.Name = ic->FromBB(a.Name);
00181                                                 a.Email = ic->FromBB(a.Email);
00182                                         }
00183 
00184                                         EmailAddressList &al = this->*(b->addrMember);
00185                                         al.push_back(a);
00186                                 }
00187 
00188                                 return begin;
00189                         }
00190                 }
00191         }
00192 
00193         // handle special cases
00194         switch( field->type )
00195         {
00196         case CALFC_APPT_TYPE_FLAG:
00197                 switch( field->u.raw[0] )
00198                 {
00199                 case 'a':                       // regular non-recurring appointment
00200                         Recurring = false;
00201                         return begin;
00202 
00203                 case '*':                       // recurring appointment
00204                         Recurring = true;
00205                         return begin;
00206 
00207                 default:
00208                         throw Error("Calendar::ParseField: unknown appointment type");
00209                 }
00210                 break;
00211 
00212         case CALFC_ALLDAYEVENT_FLAG:
00213                 AllDayEvent = field->u.raw[0] == 1;
00214                 return begin;
00215 
00216         case CALFC_TIMEZONE_CODE:
00217                 if( btohs(field->size) == 2 ) {
00218                         // good data
00219                         TimeZoneCode = btohs(field->u.code);
00220                         TimeZoneValid = true;
00221                 }
00222                 else {
00223                         throw Error("Calendar::ParseField: not enough data in time zone code field");
00224                 }
00225                 return begin;
00226 
00227         case CALFC_FREEBUSY_FLAG:
00228                 if( field->u.raw[0] > CR_FREEBUSY_RANGE_HIGH ) {
00229                         throw Error("Calendar::ParseField: FreeBusyFlag out of range" );
00230                 }
00231                 FreeBusyFlag = FreeBusyFlagProto2Rec(field->u.raw[0]);
00232                 return begin;
00233 
00234         case CALFC_CLASS_FLAG:
00235                 if( field->u.raw[0] > CR_CLASS_RANGE_HIGH ) {
00236                         throw Error("Calendar::ParseField: ClassFlag out of range" );
00237                 }
00238                 ClassFlag = ClassFlagProto2Rec(field->u.raw[0]);
00239                 return begin;
00240         }
00241 
00242         // base class handles recurring data
00243         if( RecurBase::ParseField(field->type, field->u.raw, btohs(field->size), ic) )
00244                 return begin;
00245 
00246         // if still not handled, add to the Unknowns list
00247         UnknownField uf;
00248         uf.type = field->type;
00249         uf.data.assign((const char*)field->u.raw, btohs(field->size));
00250         Unknowns.push_back(uf);
00251 
00252         // return new pointer for next field
00253         return begin;
00254 }
00255 
00256 void Calendar::ParseHeader(const Data &data, size_t &offset)
00257 {
00258         // no header in Calendar records
00259 }
00260 
00261 void Calendar::ParseFields(const Data &data, size_t &offset, const IConverter *ic)
00262 {
00263         const unsigned char *finish = ParseCommonFields(*this,
00264                 data.GetData() + offset, data.GetData() + data.GetSize(), ic);
00265         offset += finish - (data.GetData() + offset);
00266 }
00267 
00268 void Calendar::BuildHeader(Data &data, size_t &offset) const
00269 {
00270         // no header in Calendar records
00271 }
00272 
00273 //
00274 // Build
00275 //
00276 /// Build fields part of record.
00277 ///
00278 void Calendar::BuildFields(Data &data, size_t &offset, const IConverter *ic) const
00279 {
00280         data.Zap();
00281 
00282         // output the type first
00283         BuildField(data, offset, CALFC_APPT_TYPE_FLAG, Recurring ? '*' : 'a');
00284 
00285         // output all day event flag only if set
00286         if( AllDayEvent )
00287                 BuildField(data, offset, CALFC_ALLDAYEVENT_FLAG, (char)1);
00288 
00289         // cycle through the type table
00290         for(    const FieldLink<Calendar> *b = CalendarFieldLinks;
00291                 b->type != CALFC_END;
00292                 b++ )
00293         {
00294                 if( b->strMember ) {
00295                         const std::string &s = this->*(b->strMember);
00296                         if( s.size() )
00297                                 BuildField(data, offset, b->type, (b->iconvNeeded && ic) ? ic->ToBB(s) : s);
00298                 }
00299                 else if( b->timeMember ) {
00300                         time_t t = this->*(b->timeMember);
00301                         if( t > 0 )
00302                                 BuildField1900(data, offset, b->type, t);
00303                 }
00304                 else if( b->addrMember ) {
00305                         const EmailAddressList &al = this->*(b->addrMember);
00306                         EmailAddressList::const_iterator lb = al.begin(), le = al.end();
00307 
00308                         // add all entries in list
00309                         for( ; lb != le; ++lb ) {
00310 
00311                                 // skip empty entries
00312                                 if( !lb->size() )
00313                                         continue;
00314 
00315                                 std::string Name = lb->Name,
00316                                         Email = lb->Email;
00317 
00318                                 // do i18n conversion only if needed
00319                                 if( b->iconvNeeded && ic ) {
00320                                         Name = ic->ToBB(Name);
00321                                         Email = ic->ToBB(Email);
00322                                 }
00323 
00324                                 //
00325                                 // Build an addr+name field, each string
00326                                 // null terminated.
00327                                 // Note: this is a different format than
00328                                 // what is used in r_message*.cc
00329                                 //
00330                                 std::string field(lb->Email.c_str(), lb->Email.size() + 1);
00331                                 field.append(lb->Name.c_str(), lb->Name.size() + 1);
00332                                 BuildField(data, offset, b->type, field.data(), field.size());
00333                         }
00334                 }
00335         }
00336 
00337         // handle special cases
00338         if( Recurring ) {
00339                 CalendarRecurrenceDataField recur;
00340                 BuildRecurrenceData(StartTime, &recur);
00341                 BuildField(data, offset, RecurBase::RecurringFieldType(),
00342                         &recur, CALENDAR_RECURRENCE_DATA_FIELD_SIZE);
00343         }
00344 
00345         if( TimeZoneValid )
00346                 BuildField(data, offset, CALFC_TIMEZONE_CODE, TimeZoneCode);
00347 
00348         BuildField(data, offset, CALFC_FREEBUSY_FLAG, FreeBusyFlagRec2Proto(FreeBusyFlag));
00349         BuildField(data, offset, CALFC_CLASS_FLAG, ClassFlagRec2Proto(ClassFlag));
00350 
00351         // and finally save unknowns
00352         UnknownsType::const_iterator
00353                 ub = Unknowns.begin(), ue = Unknowns.end();
00354         for( ; ub != ue; ub++ ) {
00355                 BuildField(data, offset, *ub);
00356         }
00357 
00358         data.ReleaseBuffer(offset);
00359 }
00360 
00361 void Calendar::Clear()
00362 {
00363         RecurBase::Clear();
00364 
00365         RecType = Calendar::GetDefaultRecType();
00366 
00367         AllDayEvent = false;
00368         Subject.clear();
00369         Notes.clear();
00370         Location.clear();
00371         NotificationTime = StartTime = EndTime = 0;
00372 
00373         FreeBusyFlag = Free;
00374         ClassFlag = Public;
00375 
00376         TimeZoneCode = GetTimeZoneCode(0, 0);   // default to GMT
00377         TimeZoneValid = false;
00378 
00379         Unknowns.clear();
00380 }
00381 
00382 void Calendar::Dump(std::ostream &os) const
00383 {
00384         static const char *ClassTypes[] = { "Public", "Confidential", "Private" };
00385         static const char *FreeBusy[] = { "Free", "Tentative", "Busy", "Out of Office" };
00386 
00387 // FIXME - need a "check all data" function that make sure that all
00388 // recurrence data is within range.  Then call that before using
00389 // the data, such as in Build and in Dump.
00390 
00391         os << "Calendar entry: 0x" << setbase(16) << RecordId
00392                 << " (" << (unsigned int)RecType << ")\n";
00393         os << "   All Day Event: " << (AllDayEvent ? "yes" : "no") << "\n";
00394         os << "   Class: " << ClassTypes[ClassFlag] << "\n";
00395         os << "   Free/Busy: " << FreeBusy[FreeBusyFlag] << "\n";
00396         if( TimeZoneValid )
00397                 os << "   Time Zone: " << GetTimeZone(TimeZoneCode)->Name << "\n";
00398 
00399         // cycle through the type table
00400         for(    const FieldLink<Calendar> *b = CalendarFieldLinks;
00401                 b->type != CALFC_END;
00402                 b++ )
00403         {
00404                 if( b->strMember ) {
00405                         const std::string &s = this->*(b->strMember);
00406                         if( s.size() )
00407                                 os << "   " << b->name << ": " << s << "\n";
00408                 }
00409                 else if( b->timeMember ) {
00410                         time_t t = this->*(b->timeMember);
00411                         if( t > 0 )
00412                                 os << "   " << b->name << ": " << ctime(&t);
00413                         else
00414                                 os << "   " << b->name << ": disabled\n";
00415                 }
00416                 else if( b->addrMember ) {
00417                         const EmailAddressList &al = this->*(b->addrMember);
00418                         EmailAddressList::const_iterator lb = al.begin(), le = al.end();
00419 
00420                         for( ; lb != le; ++lb ) {
00421                                 if( !lb->size() )
00422                                         continue;
00423 
00424                                 os << "   " << b->name << ": " << *lb << "\n";
00425                         }
00426                 }
00427         }
00428 
00429         // print recurrence data if available
00430         RecurBase::Dump(os);
00431 
00432         // print any unknowns
00433         os << Unknowns;
00434 }
00435 
00436 
00437 } // namespace Barry
00438 

Generated on Tue Jun 30 16:08:14 2009 for Barry by  doxygen 1.5.8