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
00048 
00049 // calendar field codes
00050 #define CALFC_APPT_TYPE_FLAG            0x01
00051 #define CALFC_SUBJECT                   0x02
00052 #define CALFC_NOTES                     0x03
00053 #define CALFC_LOCATION                  0x04
00054 #define CALFC_NOTIFICATION_TIME         0x05
00055 #define CALFC_START_TIME                0x06
00056 #define CALFC_END_TIME                  0x07
00057 #define CALFC_RECURRENCE_DATA           0x0c
00058 #define CALFC_VERSION_DATA              0x10
00059 #define CALFC_NOTIFICATION_DATA         0x1a
00060 #define CALFC_FREEBUSY_FLAG             0x1c
00061 #define CALFC_TIMEZONE_CODE             0x1e    // only seems to show up if recurring
00062 #define CALFC_CLASS_FLAG                0x28    // private flag from outlook
00063 #define CALFC_ALLDAYEVENT_FLAG          0xff
00064 #define CALFC_END                       0xffff
00065 
00066 static FieldLink<Calendar> CalendarFieldLinks[] = {
00067    { CALFC_SUBJECT,    "Subject",    0, 0,    &Calendar::Subject, 0, 0, 0, 0, true },
00068    { CALFC_NOTES,      "Notes",      0, 0,    &Calendar::Notes, 0, 0, 0, 0, true },
00069    { CALFC_LOCATION,   "Location",   0, 0,    &Calendar::Location, 0, 0, 0, 0, true },
00070    { CALFC_NOTIFICATION_TIME,"Notification Time",0,0, 0, 0, &Calendar::NotificationTime, 0, 0, false },
00071    { CALFC_START_TIME, "Start Time", 0, 0,    0, 0, &Calendar::StartTime, 0, 0, false },
00072    { CALFC_END_TIME,   "End Time",   0, 0,    0, 0, &Calendar::EndTime, 0, 0, false },
00073    { CALFC_END,        "End of List",0, 0,    0, 0, 0, 0, 0, false }
00074 };
00075 
00076 Calendar::Calendar()
00077 {
00078         Clear();
00079 }
00080 
00081 Calendar::~Calendar()
00082 {
00083 }
00084 
00085 const unsigned char* Calendar::ParseField(const unsigned char *begin,
00086                                           const unsigned char *end,
00087                                           const IConverter *ic)
00088 {
00089         const CommonField *field = (const CommonField *) begin;
00090 
00091         // advance and check size
00092         begin += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
00093         if( begin > end )               // if begin==end, we are ok
00094                 return begin;
00095 
00096         if( !btohs(field->size) )       // if field has no size, something's up
00097                 return begin;
00098 
00099         // cycle through the type table
00100         for(    FieldLink<Calendar> *b = CalendarFieldLinks;
00101                 b->type != CALFC_END;
00102                 b++ )
00103         {
00104                 if( b->type == field->type ) {
00105                         if( b->strMember ) {
00106                                 std::string &s = this->*(b->strMember);
00107                                 s = ParseFieldString(field);
00108                                 if( b->iconvNeeded && ic )
00109                                         s = ic->FromBB(s);
00110                                 return begin;   // done!
00111                         }
00112                         else if( b->timeMember && btohs(field->size) == 4 ) {
00113                                 time_t &t = this->*(b->timeMember);
00114                                 dout("min1900: " << field->u.min1900);
00115                                 t = min2time(field->u.min1900);
00116                                 return begin;
00117                         }
00118                 }
00119         }
00120 
00121         // handle special cases
00122         switch( field->type )
00123         {
00124         case CALFC_APPT_TYPE_FLAG:
00125                 switch( field->u.raw[0] )
00126                 {
00127                 case 'a':                       // regular non-recurring appointment
00128                         Recurring = false;
00129                         return begin;
00130 
00131                 case '*':                       // recurring appointment
00132                         Recurring = true;
00133                         return begin;
00134 
00135                 default:
00136                         throw Error("Calendar::ParseField: unknown appointment type");
00137                 }
00138                 break;
00139 
00140         case CALFC_ALLDAYEVENT_FLAG:
00141                 AllDayEvent = field->u.raw[0] == 1;
00142                 return begin;
00143 
00144         case CALFC_RECURRENCE_DATA:
00145                 if( btohs(field->size) >= CALENDAR_RECURRENCE_DATA_FIELD_SIZE ) {
00146                         // good data
00147                         ParseRecurrenceData(&field->u.raw[0]);
00148                 }
00149                 else {
00150                         // not enough data!
00151                         throw Error("Calendar::ParseField: not enough data in recurrence data field");
00152                 }
00153                 return begin;
00154 
00155         case CALFC_TIMEZONE_CODE:
00156                 if( btohs(field->size) == 2 ) {
00157                         // good data
00158                         TimeZoneCode = btohs(field->u.code);
00159                 }
00160                 else {
00161                         throw Error("Calendar::ParseField: not enough data in time zone code field");
00162                 }
00163                 return begin;
00164 
00165         case CALFC_FREEBUSY_FLAG:
00166                 FreeBusyFlag = (FreeBusyFlagType)field->u.raw[0];
00167                 if( FreeBusyFlag > OutOfOffice ) {
00168                         throw Error("Calendar::ParseField: FreeBusyFlag out of range" );
00169                 }
00170                 return begin;
00171 
00172         case CALFC_CLASS_FLAG:
00173                 ClassFlag = (ClassFlagType)field->u.raw[0];
00174                 if( ClassFlag > Private ) {
00175                         throw Error("Calendar::ParseField: ClassFlag out of range" );
00176                 }
00177                 return begin;
00178         }
00179 
00180         // if still not handled, add to the Unknowns list
00181         UnknownField uf;
00182         uf.type = field->type;
00183         uf.data.assign((const char*)field->u.raw, btohs(field->size));
00184         Unknowns.push_back(uf);
00185 
00186         // return new pointer for next field
00187         return begin;
00188 }
00189 
00190 // this function assumes the size has already been checked
00191 void Calendar::ParseRecurrenceData(const void *data)
00192 {
00193         const CalendarRecurrenceDataField *rec =
00194                 (const CalendarRecurrenceDataField*) data;
00195 
00196         Interval = btohs(rec->interval);
00197         if( Interval < 1 )
00198                 Interval = 1;   // must always be >= 1
00199 
00200         if( rec->endTime == 0xffffffff ) {
00201                 Perpetual = true;
00202         }
00203         else {
00204                 RecurringEndTime = min2time(rec->endTime);
00205                 Perpetual = false;
00206         }
00207 
00208         switch( rec->type )
00209         {
00210         case CRDF_TYPE_DAY:
00211                 RecurringType = Day;
00212                 // no extra data
00213                 break;
00214 
00215         case CRDF_TYPE_MONTH_BY_DATE:
00216                 RecurringType = MonthByDate;
00217                 DayOfMonth = rec->u.month_by_date.monthDay;
00218                 break;
00219 
00220         case CRDF_TYPE_MONTH_BY_DAY:
00221                 RecurringType = MonthByDay;
00222                 DayOfWeek = rec->u.month_by_day.weekDay;
00223                 WeekOfMonth = rec->u.month_by_day.week;
00224                 break;
00225 
00226         case CRDF_TYPE_YEAR_BY_DATE:
00227                 RecurringType = YearByDate;
00228                 DayOfMonth = rec->u.year_by_date.monthDay;
00229                 MonthOfYear = rec->u.year_by_date.month;
00230                 break;
00231 
00232         case CRDF_TYPE_YEAR_BY_DAY:
00233                 RecurringType = YearByDay;
00234                 DayOfWeek = rec->u.year_by_day.weekDay;
00235                 WeekOfMonth = rec->u.year_by_day.week;
00236                 MonthOfYear = rec->u.year_by_day.month;
00237                 break;
00238 
00239         case CRDF_TYPE_WEEK:
00240                 RecurringType = Week;
00241 
00242                 // Note: this simple copy is only possible since
00243                 // the CAL_WD_* constants are the same as CRDF_WD_* constants.
00244                 // If this ever changes, this code will need to change.
00245                 WeekDays = rec->u.week.days;
00246                 break;
00247 
00248         default:
00249                 eout("Unknown recurrence data type: " << rec->type);
00250                 throw Error("Unknown recurrence data type");
00251         }
00252 }
00253 
00254 // this function assumes there is CALENDAR_RECURRENCE_DATA_FIELD_SIZE bytes
00255 // available in data
00256 void Calendar::BuildRecurrenceData(void *data) const
00257 {
00258         if( !Recurring )
00259                 throw Error("Calendar::BuildRecurrenceData: Attempting to build recurrence data on non-recurring record.");
00260 
00261         CalendarRecurrenceDataField *rec = (CalendarRecurrenceDataField*) data;
00262 
00263         // set all to zero
00264         memset(data, 0, CALENDAR_RECURRENCE_DATA_FIELD_SIZE);
00265 
00266         rec->interval = htobs(Interval);
00267         rec->startTime = time2min(StartTime);
00268         if( Perpetual )
00269                 rec->endTime = 0xffffffff;
00270         else
00271                 rec->endTime = time2min(RecurringEndTime);
00272 
00273         switch( RecurringType )
00274         {
00275         case Day:
00276                 rec->type = CRDF_TYPE_DAY;
00277                 // no extra data
00278                 break;
00279 
00280         case MonthByDate:
00281                 rec->type = CRDF_TYPE_MONTH_BY_DATE;
00282                 rec->u.month_by_date.monthDay = DayOfMonth;
00283                 break;
00284 
00285         case MonthByDay:
00286                 rec->type = CRDF_TYPE_MONTH_BY_DAY;
00287                 rec->u.month_by_day.weekDay = DayOfWeek;
00288                 rec->u.month_by_day.week = WeekOfMonth;
00289                 break;
00290 
00291         case YearByDate:
00292                 rec->type = CRDF_TYPE_YEAR_BY_DATE;
00293                 rec->u.year_by_date.monthDay = DayOfMonth;
00294                 rec->u.year_by_date.month = MonthOfYear;
00295                 break;
00296 
00297         case YearByDay:
00298                 rec->type = CRDF_TYPE_YEAR_BY_DAY;
00299                 rec->u.year_by_day.weekDay = DayOfWeek;
00300                 rec->u.year_by_day.week = WeekOfMonth;
00301                 rec->u.year_by_day.month = MonthOfYear;
00302                 break;
00303 
00304         case Week:
00305                 rec->type = CRDF_TYPE_WEEK;
00306 
00307                 // Note: this simple copy is only possible since
00308                 // the CAL_WD_* constants are the same as CRDF_WD_* constants.
00309                 // If this ever changes, this code will need to change.
00310                 rec->u.week.days = WeekDays;
00311                 break;
00312 
00313         default:
00314                 eout("Calendar::BuildRecurrenceData: "
00315                         "Unknown recurrence data type: " << rec->type);
00316                 throw Error("Calendar::BuildRecurrenceData: Unknown recurrence data type");
00317         }
00318 }
00319 
00320 void Calendar::ParseHeader(const Data &data, size_t &offset)
00321 {
00322         // no header in Calendar records
00323 }
00324 
00325 void Calendar::ParseFields(const Data &data, size_t &offset, const IConverter *ic)
00326 {
00327         const unsigned char *finish = ParseCommonFields(*this,
00328                 data.GetData() + offset, data.GetData() + data.GetSize(), ic);
00329         offset += finish - (data.GetData() + offset);
00330 }
00331 
00332 void Calendar::BuildHeader(Data &data, size_t &offset) const
00333 {
00334         // no header in Calendar records
00335 }
00336 
00337 //
00338 // Build
00339 //
00340 /// Build fields part of record.
00341 ///
00342 void Calendar::BuildFields(Data &data, size_t &offset, const IConverter *ic) const
00343 {
00344         data.Zap();
00345 
00346         // output the type first
00347         BuildField(data, offset, CALFC_APPT_TYPE_FLAG, Recurring ? '*' : 'a');
00348 
00349         // output all day event flag only if set
00350         if( AllDayEvent )
00351                 BuildField(data, offset, CALFC_ALLDAYEVENT_FLAG, (char)1);
00352 
00353         // cycle through the type table
00354         for(    const FieldLink<Calendar> *b = CalendarFieldLinks;
00355                 b->type != CALFC_END;
00356                 b++ )
00357         {
00358                 if( b->strMember ) {
00359                         const std::string &s = this->*(b->strMember);
00360                         if( s.size() )
00361                                 BuildField(data, offset, b->type, (b->iconvNeeded && ic) ? ic->ToBB(s) : s);
00362                 }
00363                 else if( b->timeMember ) {
00364                         time_t t = this->*(b->timeMember);
00365                         if( t > 0 )
00366                                 BuildField1900(data, offset, b->type, t);
00367                 }
00368         }
00369 
00370         // handle special cases
00371 
00372         if( Recurring ) {
00373                 CalendarRecurrenceDataField recur;
00374                 BuildRecurrenceData(&recur);
00375                 BuildField(data, offset, CALFC_RECURRENCE_DATA,
00376                         &recur, CALENDAR_RECURRENCE_DATA_FIELD_SIZE);
00377         }
00378 
00379         if( TimeZoneValid )
00380                 BuildField(data, offset, CALFC_TIMEZONE_CODE, TimeZoneCode);
00381 
00382         BuildField(data, offset, CALFC_FREEBUSY_FLAG, (char)FreeBusyFlag);
00383         BuildField(data, offset, CALFC_CLASS_FLAG, (char)ClassFlag);
00384 
00385         // and finally save unknowns
00386         UnknownsType::const_iterator
00387                 ub = Unknowns.begin(), ue = Unknowns.end();
00388         for( ; ub != ue; ub++ ) {
00389                 BuildField(data, offset, *ub);
00390         }
00391 
00392         data.ReleaseBuffer(offset);
00393 }
00394 
00395 void Calendar::Clear()
00396 {
00397         RecType = Calendar::GetDefaultRecType();
00398 
00399         AllDayEvent = false;
00400         Subject.clear();
00401         Notes.clear();
00402         Location.clear();
00403         NotificationTime = StartTime = EndTime = 0;
00404 
00405         FreeBusyFlag = Free;
00406         ClassFlag = Public;
00407 
00408         Recurring = false;
00409         RecurringType = Calendar::Week;
00410         Interval = 1;
00411         RecurringEndTime = 0;
00412         Perpetual = false;
00413         TimeZoneCode = GetTimeZoneCode(0, 0);   // default to GMT
00414         TimeZoneValid = false;
00415         DayOfWeek = WeekOfMonth = DayOfMonth = MonthOfYear = 0;
00416         WeekDays = 0;
00417 
00418         Unknowns.clear();
00419 }
00420 
00421 void Calendar::Dump(std::ostream &os) const
00422 {
00423         static const char *DayNames[] = { "Sun", "Mon", "Tue", "Wed",
00424                 "Thu", "Fri", "Sat" };
00425         static const char *MonthNames[] = { "Jan", "Feb", "Mar", "Apr",
00426                 "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
00427         static const char *ClassTypes[] = { "Public", "Confidential", "Private" };
00428         static const char *FreeBusy[] = { "Free", "Tentative", "Busy", "Out of Office" };
00429 
00430 // FIXME - need a "check all data" function that make sure that all
00431 // recurrence data is within range.  Then call that before using
00432 // the data, such as in Build and in Dump.
00433 
00434         os << "Calendar entry: 0x" << setbase(16) << RecordId
00435                 << " (" << (unsigned int)RecType << ")\n";
00436         os << "   All Day Event: " << (AllDayEvent ? "yes" : "no") << "\n";
00437         os << "   Class: " << ClassTypes[ClassFlag] << "\n";
00438         os << "   Free/Busy: " << FreeBusy[FreeBusyFlag] << "\n";
00439         if( TimeZoneValid )
00440                 os << "   Time Zone: " << GetTimeZone(TimeZoneCode)->Name << "\n";
00441 
00442         // cycle through the type table
00443         for(    const FieldLink<Calendar> *b = CalendarFieldLinks;
00444                 b->type != CALFC_END;
00445                 b++ )
00446         {
00447                 if( b->strMember ) {
00448                         const std::string &s = this->*(b->strMember);
00449                         if( s.size() )
00450                                 os << "   " << b->name << ": " << s << "\n";
00451                 }
00452                 else if( b->timeMember ) {
00453                         time_t t = this->*(b->timeMember);
00454                         if( t > 0 )
00455                                 os << "   " << b->name << ": " << ctime(&t);
00456                         else
00457                                 os << "   " << b->name << ": disabled\n";
00458                 }
00459         }
00460 
00461         // print recurrence data if available
00462         os << "   Recurring: " << (Recurring ? "yes" : "no") << "\n";
00463         if( Recurring ) {
00464                 switch( RecurringType )
00465                 {
00466                 case Day:
00467                         os << "      Every day.\n";
00468                         break;
00469 
00470                 case MonthByDate:
00471                         os << "      Every month on the "
00472                            << DayOfMonth
00473                            << (DayOfMonth == 1 ? "st" : "")
00474                            << (DayOfMonth == 2 ? "nd" : "")
00475                            << (DayOfMonth == 3 ? "rd" : "")
00476                            << (DayOfMonth > 3  ? "th" : "")
00477                            << "\n";
00478                         break;
00479 
00480                 case MonthByDay:
00481                         os << "      Every month on the "
00482                            << DayNames[DayOfWeek]
00483                            << " of week "
00484                            << WeekOfMonth
00485                            << "\n";
00486                         break;
00487 
00488                 case YearByDate:
00489                         os << "      Every year on "
00490                            << MonthNames[MonthOfYear-1]
00491                            << " " << DayOfMonth << "\n";
00492                         break;
00493 
00494                 case YearByDay:
00495                         os << "      Every year in " << MonthNames[MonthOfYear-1]
00496                            << " on "
00497                            << DayNames[DayOfWeek]
00498                            << " of week " << WeekOfMonth << "\n";
00499                         break;
00500 
00501                 case Week:
00502                         os << "      Every week on: ";
00503                         if( WeekDays & CAL_WD_SUN ) os << "Sun ";
00504                         if( WeekDays & CAL_WD_MON ) os << "Mon ";
00505                         if( WeekDays & CAL_WD_TUE ) os << "Tue ";
00506                         if( WeekDays & CAL_WD_WED ) os << "Wed ";
00507                         if( WeekDays & CAL_WD_THU ) os << "Thu ";
00508                         if( WeekDays & CAL_WD_FRI ) os << "Fri ";
00509                         if( WeekDays & CAL_WD_SAT ) os << "Sat ";
00510                         os << "\n";
00511                         break;
00512 
00513                 default:
00514                         os << "      Unknown recurrence type\n";
00515                         break;
00516                 }
00517 
00518                 os << "      Interval: " << Interval << "\n";
00519 
00520                 if( Perpetual )
00521                         os << "      Ends: never\n";
00522                 else
00523                         os << "      Ends: "
00524                            << ctime(&RecurringEndTime);
00525         }
00526 
00527         // print any unknowns
00528         os << Unknowns;
00529 }
00530 
00531 
00532 } // namespace Barry
00533 

Generated on Mon Jan 12 10:51:13 2009 for Barry by  doxygen 1.5.7.1