r_task.cc

Go to the documentation of this file.
00001 ///
00002 /// \file       r_task.cc
00003 ///             Record parsing class for the task database.
00004 ///
00005 
00006 /*
00007     Copyright (C) 2005-2012, Net Direct Inc. (http://www.netdirect.ca/)
00008     Copyright (C) 2007, Brian Edginton
00009 
00010     This program is free software; you can redistribute it and/or modify
00011     it under the terms of the GNU General Public License as published by
00012     the Free Software Foundation; either version 2 of the License, or
00013     (at your option) any later version.
00014 
00015     This program is distributed in the hope that it will be useful,
00016     but WITHOUT ANY WARRANTY; without even the implied warranty of
00017     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00018 
00019     See the GNU General Public License in the COPYING file at the
00020     root directory of this project for more details.
00021 */
00022 
00023 #include "r_task.h"
00024 #include "r_calendar.h"                        // for CAL_* defines
00025 #include "r_recur_base-int.h"
00026 #include "record-internal.h"
00027 #include "protostructs.h"
00028 #include "data.h"
00029 #include "time.h"
00030 #include "iconv.h"
00031 #include "debug.h"
00032 #include <ostream>
00033 #include <iomanip>
00034 #include <string.h>
00035 #include "ios_state.h"
00036 
00037 using namespace std;
00038 using namespace Barry::Protocol;
00039 
00040 namespace Barry {
00041 
00042 
00043 ///////////////////////////////////////////////////////////////////////////////
00044 // Task Class, static members
00045 
00046 //
00047 // Note! These functions currently only pass the same values through.
00048 //       In actuality, these are technically two different values:
00049 //       one on the raw protocol side, and the other part of the
00050 //       guaranteed Barry API.  If the Blackberry ever changes the
00051 //       meanings for these codes, do the translation here.
00052 //
00053 
00054 Task::AlarmFlagType Task::AlarmProto2Rec(uint8_t a)
00055 {
00056         return (AlarmFlagType)a;
00057 }
00058 
00059 uint8_t Task::AlarmRec2Proto(AlarmFlagType a)
00060 {
00061         return a;
00062 }
00063 
00064 Task::PriorityFlagType Task::PriorityProto2Rec(uint8_t p)
00065 {
00066         return (PriorityFlagType)p;
00067 }
00068 
00069 uint8_t Task::PriorityRec2Proto(PriorityFlagType p)
00070 {
00071         return p;
00072 }
00073 
00074 Task::StatusFlagType Task::StatusProto2Rec(uint8_t s)
00075 {
00076         return (StatusFlagType)s;
00077 }
00078 
00079 uint8_t Task::StatusRec2Proto(StatusFlagType s)
00080 {
00081         return s;
00082 }
00083 
00084 
00085 ///////////////////////////////////////////////////////////////////////////////
00086 // Task Class
00087 
00088 // Task Field Codes
00089 #define TSKFC_TASK_TYPE         0x01
00090 #define TSKFC_TITLE             0x02
00091 #define TSKFC_NOTES             0x03
00092 #define TSKFC_DUE_TIME          0x05
00093 #define TSKFC_START_TIME        0x06    // This is fuzzy... most devices seem
00094                                         // to anchor this value == to DUE_TIME
00095 #define TSKFC_DUE_FLAG          0x08
00096 #define TSKFC_STATUS            0x09
00097 #define TSKFC_PRIORITY          0x0a
00098 #define TSKFC_ALARM_TYPE        0x0e
00099 #define TSKFC_ALARM_TIME        0x0f
00100 #define TSKFC_TIMEZONE_CODE     0x10
00101 #define TSKFC_CATEGORIES        0x11
00102 #define TSKFC_ALARM_FLAG        0x12
00103 #define TSKFC_END               0xffff
00104 
00105 static FieldLink<Task> TaskFieldLinks[] = {
00106    { TSKFC_TITLE,      "Summary",     0, 0, &Task::Summary, 0, 0, 0, 0, true },
00107    { TSKFC_NOTES,      "Notes",       0, 0, &Task::Notes, 0, 0, 0, 0, true },
00108    { TSKFC_START_TIME, "Start Time",  0, 0, 0, 0, &Task::StartTime, 0, 0, false },
00109    { TSKFC_DUE_TIME,   "Due Time",    0, 0, 0, 0, &Task::DueTime, 0, 0, false },
00110    { TSKFC_ALARM_TIME, "Alarm Time",  0, 0, 0, 0, &Task::AlarmTime, 0, 0, false },
00111    { TSKFC_END,        "End of List", 0, 0, 0, 0, 0, 0, 0, false },
00112 };
00113 
00114 Task::Task()
00115 {
00116         Clear();
00117 }
00118 
00119 Task::~Task()
00120 {
00121 }
00122 
00123 const unsigned char* Task::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         if( field->type == TSKFC_TASK_TYPE ) {
00138                 if( field->u.raw[0] != 't' ) {
00139                         throw Error("Task::ParseField: Task Type is not 't'");
00140                 }
00141                 return begin;
00142         }
00143 
00144         // cycle through the type table
00145         for(    FieldLink<Task> *b = TaskFieldLinks;
00146                 b->type != TSKFC_END;
00147                 b++ )
00148         {
00149                 if( b->type == field->type ) {
00150                         if( b->strMember ) {
00151                                 std::string &s = this->*(b->strMember);
00152                                 s = ParseFieldString(field);
00153                                 if( b->iconvNeeded && ic )
00154                                         s = ic->FromBB(s);
00155                                 return begin;   // done!
00156                         }
00157                         else if( b->timeMember && btohs(field->size) == 4 ) {
00158                                 TimeT &t = this->*(b->timeMember);
00159                                 t.Time = min2time(field->u.min1900);
00160                                 return begin;
00161                         }
00162                 }
00163         }
00164 
00165         // handle special cases
00166         switch( field->type )
00167         {
00168         case TSKFC_PRIORITY:
00169                 if( field->u.raw[0] > TR_PRIORITY_RANGE_HIGH ) {
00170                         throw Error( "Task::ParseField: priority field out of bounds" );
00171                 }
00172                 else {
00173                         PriorityFlag = PriorityProto2Rec(field->u.raw[0]);
00174                 }
00175                 return begin;
00176 
00177         case TSKFC_STATUS:
00178                 if( field->u.raw[0] > TR_STATUS_RANGE_HIGH ) {
00179                         throw Error( "Task::ParseField: priority field out of bounds" );
00180                 }
00181                 else {
00182                         StatusFlag = StatusProto2Rec(field->u.raw[0]);
00183                 }
00184                 return begin;
00185 
00186         case TSKFC_TIMEZONE_CODE:
00187                 if( btohs(field->size) == 4 ) {
00188                         TimeZoneCode = btohs(field->u.code);
00189                         TimeZoneValid = true;
00190                 }
00191                 else {
00192                         throw Error("Task::ParseField: not enough data in time zone code field");
00193                 }
00194                 return begin;
00195 
00196         case TSKFC_DUE_FLAG:
00197                 // the DueDateFlag is not available on really old devices
00198                 // such as the 7750, and if the DueTime is available,
00199                 // we'll just save it.  There is no further need for this
00200                 // value that we know of yet, so just ignore it for now.
00201                 return begin;
00202 
00203         case TSKFC_ALARM_FLAG:
00204                 // the AlarmFlag is not available on really old devices
00205                 // such as the 7750, and if the AlarmTime is available,
00206                 // we'll just save it.  There is no further need for this
00207                 // value that we know of yet, so just ignore it for now.
00208                 return begin;
00209 
00210         case TSKFC_ALARM_TYPE:
00211                 if( field->u.raw[0] > TR_ALARM_RANGE_HIGH ) {
00212                         throw Error("Task::ParseField: AlarmType out of bounds" );
00213                 }
00214                 else {
00215                         AlarmType = AlarmProto2Rec(field->u.raw[0]);
00216                 }
00217                 return begin;
00218 
00219         case TSKFC_CATEGORIES:
00220                 {
00221                         std::string catstring = ParseFieldString(field);
00222                         if( ic )
00223                                 catstring = ic->FromBB(catstring);
00224                         Categories.CategoryStr2List(catstring);
00225                 }
00226                 return begin;
00227         }
00228 
00229         // base class handles recurring data
00230         if( RecurBase::ParseField(field->type, field->u.raw, btohs(field->size), ic) )
00231                 return begin;
00232 
00233         // if still not handled, add to the Unknowns list
00234         UnknownField uf;
00235         uf.type = field->type;
00236         uf.data.assign((const char*)field->u.raw, btohs(field->size));
00237         Unknowns.push_back(uf);
00238 
00239         // return new pointer for next field
00240         return begin;
00241 }
00242 
00243 void Task::ParseHeader(const Data &data, size_t &offset)
00244 {
00245         // no header in Task records
00246 }
00247 
00248 void Task::ParseFields(const Data &data, size_t &offset, const IConverter *ic)
00249 {
00250         const unsigned char *finish = ParseCommonFields(*this,
00251                 data.GetData() + offset, data.GetData() + data.GetSize(), ic);
00252         offset += finish - (data.GetData() + offset);
00253 }
00254 
00255 
00256 void Task::Validate() const
00257 {
00258         RecurBase::Validate();
00259 }
00260 
00261 void Task::BuildHeader(Data &data, size_t &offset) const
00262 {
00263         // no header in Task records
00264 }
00265 
00266 
00267 //
00268 // Build
00269 //
00270 /// Build fields part of record.
00271 ///
00272 void Task::BuildFields(Data &data, size_t &offset, const IConverter *ic) const
00273 {
00274         data.Zap();
00275 
00276         //
00277         // Note: we do not use the FieldLink table, since the firmware
00278         // on many devices apears flakey, so we try to write as close to
00279         // the same order as we see coming from the device.  Unfortunately,
00280         // this doesn't really help, for the Tasks corruption bug, but
00281         // we'll keep the flexibility for now.
00282         //
00283 
00284         // tack on the 't' task type field first
00285         BuildField(data, offset, TSKFC_TASK_TYPE, 't');
00286 
00287         // Summary / Title
00288         if( Summary.size() ) {
00289                 std::string s = (ic) ? ic->ToBB(Summary) : Summary;
00290                 BuildField(data, offset, TSKFC_TITLE, s);
00291         }
00292 
00293         BuildField(data, offset, TSKFC_STATUS, (uint32_t) StatusRec2Proto(StatusFlag));
00294         BuildField(data, offset, TSKFC_PRIORITY, (uint32_t) PriorityRec2Proto(PriorityFlag));
00295 
00296         if( TimeZoneValid ) {
00297                 // the time zone code field is 4 bytes, but we only use
00298                 // the first two... pad it with zeros
00299                 uint32_t code = TimeZoneCode;
00300                 BuildField(data, offset, TSKFC_TIMEZONE_CODE, code);
00301         }
00302 
00303         // make sure StartTime matches DueTime, by writing it manually...
00304         /// not sure why StartTime exists, but oh well. :-)
00305         if( DueTime.IsValid() ) {
00306                 // we use DueTime here, with the START_TIME code,
00307                 // instead of StartTime, since the function is const
00308                 BuildField1900(data, offset, TSKFC_START_TIME, DueTime);
00309 
00310                 // then DueTime, with flag first, then time
00311                 BuildField(data, offset, TSKFC_DUE_FLAG, (uint32_t) 1);
00312                 BuildField1900(data, offset, TSKFC_DUE_TIME, DueTime);
00313         }
00314 
00315         if( AlarmTime.IsValid() ) {
00316                 BuildField(data, offset, TSKFC_ALARM_FLAG, (uint32_t) 1);
00317                 BuildField(data, offset, TSKFC_ALARM_TYPE, AlarmRec2Proto(AlarmType));
00318                 BuildField1900(data, offset, TSKFC_ALARM_TIME, AlarmTime);
00319         }
00320 
00321         // Categories
00322         if( Categories.size() ) {
00323                 string store;
00324                 Categories.CategoryList2Str(store);
00325                 BuildField(data, offset, TSKFC_CATEGORIES, ic ? ic->ToBB(store) : store);
00326         }
00327 
00328         // Notes
00329         if( Notes.size() ) {
00330                 std::string s = (ic) ? ic->ToBB(Notes) : Notes;
00331                 BuildField(data, offset, TSKFC_NOTES, s);
00332         }
00333 
00334         if( Recurring ) {
00335                 CalendarRecurrenceDataField recur;
00336                 BuildRecurrenceData(StartTime.Time, &recur);
00337                 BuildField(data, offset, RecurBase::RecurringFieldType(),
00338                         &recur, CALENDAR_RECURRENCE_DATA_FIELD_SIZE);
00339         }
00340 
00341         // and finally save unknowns
00342         UnknownsType::const_iterator
00343                 ub = Unknowns.begin(), ue = Unknowns.end();
00344         for( ; ub != ue; ub++ ) {
00345                 BuildField(data, offset, *ub);
00346         }
00347 
00348         data.ReleaseBuffer(offset);
00349 }
00350 
00351 
00352 
00353 void Task::Clear()
00354 {
00355         // clear the base class first
00356         RecurBase::Clear();
00357 
00358         // our variables...
00359         RecType = GetDefaultRecType();
00360         RecordId = 0;
00361 
00362         Summary.clear();
00363         Notes.clear();
00364         Categories.clear();
00365         UID.clear();
00366 
00367         StartTime.clear();
00368         DueTime.clear();
00369         AlarmTime.clear();
00370 
00371         TimeZoneCode = GetStaticTimeZoneCode( 0, 0 );   // default to GMT
00372         TimeZoneValid = false;
00373 
00374         AlarmType = Date;
00375         PriorityFlag = Normal;
00376         StatusFlag = NotStarted;
00377 
00378         Unknowns.clear();
00379 }
00380 
00381 const FieldHandle<Task>::ListT& Task::GetFieldHandles()
00382 {
00383         static FieldHandle<Task>::ListT fhv;
00384 
00385         if( fhv.size() )
00386                 return fhv;
00387 
00388 #undef CONTAINER_OBJECT_NAME
00389 #define CONTAINER_OBJECT_NAME fhv
00390 
00391 #undef RECORD_CLASS_NAME
00392 #define RECORD_CLASS_NAME Task
00393 
00394         FHP(RecType, "Record Type Code");
00395         FHP(RecordId, "Unique Record ID");
00396 
00397         FHD(Summary, "Summary", TSKFC_TITLE, true);
00398         FHD(Notes, "Notes", TSKFC_NOTES, true);
00399         FHD(Categories, "Categories", TSKFC_CATEGORIES, true);
00400         FHP(UID, "UID");        // FIXME - not linked to any device field??
00401 
00402         FHD(StartTime, "Start Time", TSKFC_START_TIME, false);
00403         FHD(DueTime, "Due Time", TSKFC_DUE_TIME, false);
00404         FHD(AlarmTime, "Alarm Time", TSKFC_ALARM_TIME, false);
00405         FHD(TimeZoneCode, "Time Zone Code", TSKFC_TIMEZONE_CODE, false);
00406         FHP(TimeZoneValid, "Time Zone Code Valid");
00407 
00408         FHE(aft, AlarmFlagType, AlarmType, "Alarm Type");
00409         FHE_CONST(aft, Date, "Date");
00410         FHE_CONST(aft, Relative, "Relative");
00411 
00412         FHE(pft, PriorityFlagType, PriorityFlag, "Priority");
00413         FHE_CONST(pft, High, "High");
00414         FHE_CONST(pft, Normal, "Normal");
00415         FHE_CONST(pft, Low, "Low");
00416 
00417         FHE(sft, StatusFlagType, StatusFlag, "Status");
00418         FHE_CONST(sft, NotStarted, "Not Started");
00419         FHE_CONST(sft, InProgress, "In Progress");
00420         FHE_CONST(sft, Completed, "Completed");
00421         FHE_CONST(sft, Waiting, "Waiting");
00422         FHE_CONST(sft, Deferred, "Deferred");
00423 
00424         FHP(Unknowns, "Unknown Fields");
00425 
00426         // and finally, the RecurBase fields
00427         RECUR_BASE_FIELD_HANDLES
00428 
00429         return fhv;
00430 }
00431 
00432 std::string Task::GetDescription() const
00433 {
00434         return Summary;
00435 }
00436 
00437 void Task::Dump(std::ostream &os) const
00438 {
00439         ios_format_state state(os);
00440 
00441         static const char *PriorityName[] = { "High", "Normal", "Low" };
00442         static const char *StatusName[] = { "Not Started", "In Progress",
00443                 "Completed", "Waiting", "Deferred" };
00444         static const char *DayNames[] = { "Sun", "Mon", "Tue", "Wed",
00445                 "Thu", "Fri", "Sat" };
00446         static const char *MonthNames[] = { "Jan", "Feb", "Mar", "Apr",
00447                 "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
00448         static const char *AlarmTypeName[] = { "None", "By Date", "Relative" };
00449 
00450         os << "Task entry: 0x" << setbase(16) << RecordId
00451            << " (" << (unsigned int)RecType << ")\n";
00452 
00453         // cycle through the type table
00454         for(    const FieldLink<Task> *b = TaskFieldLinks;
00455                 b->type != TSKFC_END;
00456                 b++ )
00457         {
00458                 if( b->strMember ) {
00459                         const std::string &s = this->*(b->strMember);
00460                         if( s.size() )
00461                                 os << "   " << b->name << ": " << Cr2LfWrapper(s) << "\n";
00462                 }
00463                 else if( b->timeMember ) {
00464                         TimeT t = this->*(b->timeMember);
00465                         if( t.IsValid() )
00466                                 os << "   " << b->name << ": " << t << "\n";
00467                 }
00468         }
00469 
00470         os << "   Priority: " << PriorityName[PriorityFlag] << "\n";
00471         os << "   Status: " << StatusName[StatusFlag] << "\n";
00472         if( AlarmType ) {
00473                 os << "   Alarm Type: " << AlarmTypeName[AlarmType] << "\n";
00474         }
00475         if( TimeZoneValid )
00476                 os << "   Time Zone: " << GetStaticTimeZone(TimeZoneCode)->Name << "\n";
00477 
00478         // print recurrence data if available
00479         os << "   Recurring: " << (Recurring ? "yes" : "no") << "\n";
00480         if( Recurring ) {
00481                 switch( RecurringType )
00482                 {
00483                 case Day:
00484                         os << "      Every day.\n";
00485                         break;
00486 
00487                 case MonthByDate:
00488                         os << "      Every month on the "
00489                            << DayOfMonth
00490                            << (DayOfMonth == 1 ? "st" : "")
00491                            << (DayOfMonth == 2 ? "nd" : "")
00492                            << (DayOfMonth == 3 ? "rd" : "")
00493                            << (DayOfMonth > 3  ? "th" : "")
00494                            << "\n";
00495                         break;
00496 
00497                 case MonthByDay:
00498                         os << "      Every month on the "
00499                            << DayNames[DayOfWeek]
00500                            << " of week "
00501                            << WeekOfMonth
00502                            << "\n";
00503                         break;
00504 
00505                 case YearByDate:
00506                         os << "      Every year on "
00507                            << MonthNames[MonthOfYear-1]
00508                            << " " << DayOfMonth << "\n";
00509                         break;
00510 
00511                 case YearByDay:
00512                         os << "      Every year in " << MonthNames[MonthOfYear-1]
00513                            << " on "
00514                            << DayNames[DayOfWeek]
00515                            << " of week " << WeekOfMonth << "\n";
00516                         break;
00517 
00518                 case Week:
00519                         os << "      Every week on: ";
00520                         if( WeekDays & CAL_WD_SUN ) os << "Sun ";
00521                         if( WeekDays & CAL_WD_MON ) os << "Mon ";
00522                         if( WeekDays & CAL_WD_TUE ) os << "Tue ";
00523                         if( WeekDays & CAL_WD_WED ) os << "Wed ";
00524                         if( WeekDays & CAL_WD_THU ) os << "Thu ";
00525                         if( WeekDays & CAL_WD_FRI ) os << "Fri ";
00526                         if( WeekDays & CAL_WD_SAT ) os << "Sat ";
00527                         os << "\n";
00528                         break;
00529 
00530                 default:
00531                         os << "      Unknown recurrence type\n";
00532                         break;
00533                 }
00534 
00535                 os << "      Interval: " << Interval << "\n";
00536 
00537                 if( Perpetual )
00538                         os << "      Ends: never\n";
00539                 else
00540                         os << "      Ends: " << RecurringEndTime << "\n";
00541         }
00542 
00543         if( Categories.size() ) {
00544                 string display;
00545                 Categories.CategoryList2Str(display);
00546                 os << "   Categories: " << display << "\n";
00547         }
00548 
00549         os << Unknowns;
00550         os << "\n\n";
00551 }
00552 
00553 bool Task::operator<(const Task &other) const
00554 {
00555         if( StartTime != other.StartTime )
00556                 return StartTime < other.StartTime;
00557         if( AlarmTime != other.AlarmTime )
00558                 return AlarmTime < other.AlarmTime;
00559 
00560         int cmp = Summary.compare(other.Summary);
00561         if( cmp == 0 )
00562                 cmp = Notes.compare(other.Notes);
00563         return cmp < 0;
00564 }
00565 
00566 } // namespace Barry
00567