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

Generated on Wed Sep 24 21:27:32 2008 for Barry by  doxygen 1.5.1