- updated r_saved_messages.cc comment to match header
[barry.git] / src / r_task.cc
blob57be52cbc7740f15f669867356b027f175a4553a
1 ///
2 /// \file r_task.cc
3 /// Record parsing class for the task database.
4 ///
6 /*
7 Copyright (C) 2005-2007, Net Direct Inc. (http://www.netdirect.ca/)
8 Copyright (C) 2007, Brian Edginton
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19 See the GNU General Public License in the COPYING file at the
20 root directory of this project for more details.
23 #include "r_task.h"
24 #include "record-internal.h"
25 #include "protostructs.h"
26 #include "data.h"
27 #include "time.h"
28 #include "debug.h"
29 #include <ostream>
30 #include <iomanip>
32 using namespace std;
33 using namespace Barry::Protocol;
35 namespace Barry {
37 ///////////////////////////////////////////////////////////////////////////////
38 // Task Class
40 // Task Field Codes
41 #define TSKFC_TASK_TYPE 0x01
42 #define TSKFC_TITLE 0x02
43 #define TSKFC_NOTES 0x03
44 #define TSKFC_START_TIME 0x05
45 #define TSKFC_DUE_TIME 0x06
46 #define TSKFC_DUE_FLAG 0x08
47 #define TSKFC_STATUS 0x09
48 #define TSKFC_PRIORITY 0x0a
49 #define TSKFC_RECURRENCE_DATA 0x0c
50 #define TSKFC_ALARM_TYPE 0x0e
51 #define TSKFC_ALARM_TIME 0x0f
52 #define TSKFC_TIMEZONE_CODE 0x10
53 #define TSKFC_CATEGORIES 0x11
54 #define TSKFC_END 0xffff
56 FieldLink<Task> TaskFieldLinks[] = {
57 { TSKFC_TITLE, "Summary", 0, 0, &Task::Summary, 0, 0 },
58 { TSKFC_NOTES, "Notes", 0, 0, &Task::Notes, 0, 0 },
59 { TSKFC_START_TIME, "Start Time", 0, 0, 0, 0, &Task::StartTime },
60 { TSKFC_DUE_TIME, "Due Time", 0, 0, 0, 0, &Task::DueTime },
61 { TSKFC_ALARM_TIME, "Alarm Time", 0, 0, 0, 0, &Task::AlarmTime },
62 { TSKFC_CATEGORIES, "Categories", 0, 0, &Task::Categories, 0, 0 },
63 { TSKFC_END, "End of List", 0, 0, 0, 0, 0 },
66 Task::Task()
68 Clear();
71 Task::~Task()
75 const unsigned char* Task::ParseField(const unsigned char *begin,
76 const unsigned char *end)
78 const CommonField *field = (const CommonField *) begin;
80 // advance and check size
81 begin += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
82 if( begin > end ) // if begin==end, we are ok
83 return begin;
85 if( !btohs(field->size) ) // if field has no size, something's up
86 return begin;
88 if( field->type == TSKFC_TASK_TYPE ) {
89 if( ( TaskType = field->u.raw[0] ) != 't' ) {
90 throw Error("Task::ParseField: Task Type is not 't'");
92 return begin;
95 // cycle through the type table
96 for( FieldLink<Task> *b = TaskFieldLinks;
97 b->type != TSKFC_END;
98 b++ )
100 if( b->type == field->type ) {
101 if( b->strMember ) {
102 std::string &s = this->*(b->strMember);
103 s.assign((const char *)field->u.raw, btohs(field->size)-1);
104 return begin; // done!
106 else if( b->timeMember && btohs(field->size) == 4 ) {
107 time_t &t = this->*(b->timeMember);
108 t = min2time(field->u.min1900);
109 return begin;
113 // handle special cases
114 switch( field->type )
116 case TSKFC_PRIORITY:
117 if( field->u.raw[0] > Low ) {
118 throw Error( "Task::ParseField: priority field out of bounds" );
120 else {
121 PriorityFlag = (PriorityFlagType)field->u.raw[0];
123 return begin;
125 case TSKFC_STATUS:
126 if( field->u.raw[0] > Deferred ) {
127 throw Error( "Task::ParseField: priority field out of bounds" );
129 else {
130 StatusFlag = (StatusFlagType)field->u.raw[0];
132 return begin;
134 case TSKFC_TIMEZONE_CODE:
135 if( btohs(field->size) == 4 ) {
136 TimeZoneCode = btohs(field->u.code);
138 else {
139 throw Error("Task::ParseField: not enough data in time zone code field");
141 return begin;
143 case TSKFC_RECURRENCE_DATA:
144 if( btohs(field->size) >= CALENDAR_RECURRENCE_DATA_FIELD_SIZE ) {
145 Recurring = true;
146 ParseRecurrenceData(&field->u.raw[0]);
148 else {
149 throw Error("Task::ParseField: not enough data in recurrence data field");
151 return begin;
153 case TSKFC_DUE_FLAG:
154 DueDateFlag = field->u.raw[0];
155 return begin;
157 case TSKFC_ALARM_TYPE:
158 if( field->u.raw[0] > Relative ) {
159 throw Error("Task::ParseField: AlarmType out of bounds" );
161 else {
162 AlarmType = (AlarmFlagType)field->u.raw[0];
164 return begin;
167 // if still not handled, add to the Unknowns list
168 UnknownField uf;
169 uf.type = field->type;
170 uf.data.assign((const char*)field->u.raw, btohs(field->size));
171 Unknowns.push_back(uf);
173 // return new pointer for next field
174 return begin;
177 // this function assumes the size has already been checked
178 void Task::ParseRecurrenceData(const void *data)
180 const CalendarRecurrenceDataField *rec =
181 (const CalendarRecurrenceDataField*) data;
183 Interval = btohs(rec->interval);
184 if( Interval < 1 )
185 Interval = 1; // must always be >= 1
187 if( rec->endTime == 0xffffffff ) {
188 Perpetual = true;
190 else {
191 RecurringEndTime = min2time(rec->endTime);
192 Perpetual = false;
195 switch( rec->type )
197 case CRDF_TYPE_DAY:
198 RecurringType = Day;
199 // no extra data
200 break;
202 case CRDF_TYPE_MONTH_BY_DATE:
203 RecurringType = MonthByDate;
204 DayOfMonth = rec->u.month_by_date.monthDay;
205 break;
207 case CRDF_TYPE_MONTH_BY_DAY:
208 RecurringType = MonthByDay;
209 DayOfWeek = rec->u.month_by_day.weekDay;
210 WeekOfMonth = rec->u.month_by_day.week;
211 break;
213 case CRDF_TYPE_YEAR_BY_DATE:
214 RecurringType = YearByDate;
215 DayOfMonth = rec->u.year_by_date.monthDay;
216 MonthOfYear = rec->u.year_by_date.month;
217 break;
219 case CRDF_TYPE_YEAR_BY_DAY:
220 RecurringType = YearByDay;
221 DayOfWeek = rec->u.year_by_day.weekDay;
222 WeekOfMonth = rec->u.year_by_day.week;
223 MonthOfYear = rec->u.year_by_day.month;
224 break;
226 case CRDF_TYPE_WEEK:
227 RecurringType = Week;
229 // Note: this simple copy is only possible since
230 // the CAL_WD_* constants are the same as CRDF_WD_* constants.
231 // If this ever changes, this code will need to change.
232 WeekDays = rec->u.week.days;
233 break;
235 default:
236 eout("Unknown recurrence data type: 0x"
237 << setbase(16) << (unsigned int) rec->type);
238 throw Error("Unknown recurrence data type");
242 // this function assumes there is CALENDAR_RECURRENCE_DATA_FIELD_SIZE bytes
243 // available in data
244 void Task::BuildRecurrenceData(void *data)
246 if( !Recurring )
247 throw Error("Task::BuildRecurrenceData: Attempting to build recurrence data on non-recurring record.");
249 CalendarRecurrenceDataField *rec = (CalendarRecurrenceDataField*) data;
251 // set all to zero
252 memset(data, 0, CALENDAR_RECURRENCE_DATA_FIELD_SIZE);
254 rec->interval = htobs(Interval);
255 rec->startTime = time2min(StartTime);
256 if( Perpetual )
257 rec->endTime = 0xffffffff;
258 else
259 rec->endTime = time2min(RecurringEndTime);
261 switch( RecurringType )
263 case Day:
264 rec->type = CRDF_TYPE_DAY;
265 // no extra data
266 break;
268 case MonthByDate:
269 rec->type = CRDF_TYPE_MONTH_BY_DATE;
270 rec->u.month_by_date.monthDay = DayOfMonth;
271 break;
273 case MonthByDay:
274 rec->type = CRDF_TYPE_MONTH_BY_DAY;
275 rec->u.month_by_day.weekDay = DayOfWeek;
276 rec->u.month_by_day.week = WeekOfMonth;
277 break;
279 case YearByDate:
280 rec->type = CRDF_TYPE_YEAR_BY_DATE;
281 rec->u.year_by_date.monthDay = DayOfMonth;
282 rec->u.year_by_date.month = MonthOfYear;
283 break;
285 case YearByDay:
286 rec->type = CRDF_TYPE_YEAR_BY_DAY;
287 rec->u.year_by_day.weekDay = DayOfWeek;
288 rec->u.year_by_day.week = WeekOfMonth;
289 rec->u.year_by_day.month = MonthOfYear;
290 break;
292 case Week:
293 rec->type = CRDF_TYPE_WEEK;
295 // Note: this simple copy is only possible since
296 // the CAL_WD_* constants are the same as CRDF_WD_* constants.
297 // If this ever changes, this code will need to change.
298 rec->u.week.days = WeekDays;
299 break;
301 default:
302 eout("Task::BuildRecurrenceData: "
303 "Unknown recurrence data type: " << rec->type);
304 throw Error("Task::BuildRecurrenceData: Unknown recurrence data type");
308 void Task::ParseHeader(const Data &data, size_t &offset)
310 // no header in Task records
313 void Task::ParseFields(const Data &data, size_t &offset)
315 const unsigned char *finish = ParseCommonFields(*this,
316 data.GetData() + offset, data.GetData() + data.GetSize());
317 offset += finish - (data.GetData() + offset);
320 void Task::Clear()
322 Summary.clear();
323 Notes.clear();
324 Categories.clear();
325 StartTime = DueTime = AlarmTime = 0;
327 PriorityFlag = (PriorityFlagType)0;
328 StatusFlag = (StatusFlagType)0;
329 AlarmType = (AlarmFlagType)0;
331 TaskType = 0;
333 Perpetual = false;
334 DueDateFlag = false;
335 Recurring = false;
337 TimeZoneCode = GetTimeZoneCode( 0, 0 );
339 Unknowns.clear();
342 void Task::Dump(std::ostream &os) const
344 static const char *PriorityName[] = { "High", "Normal", "Low" };
345 static const char *StatusName[] = { "Not Started", "In Progress",
346 "Completed", "Waiting", "Deferred" };
347 static const char *DayNames[] = { "Sun", "Mon", "Tue", "Wed",
348 "Thu", "Fri", "Sat" };
349 static const char *MonthNames[] = { "Jan", "Feb", "Mar", "Apr",
350 "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
351 static const char *AlarmTypeName[] = { "None", "By Date", "Relative" };
353 os << "Task entry: 0x" << setbase(16) << RecordId
354 << " (" << (unsigned int)RecType << ")\n";
356 // cycle through the type table
357 for( const FieldLink<Task> *b = TaskFieldLinks;
358 b->type != TSKFC_END;
359 b++ )
361 if( b->strMember ) {
362 const std::string &s = this->*(b->strMember);
363 if( s.size() )
364 os << " " << b->name << ": " << s << "\n";
366 else if( b->timeMember ) {
367 time_t t = this->*(b->timeMember);
368 if( t > 0 )
369 os << " " << b->name << ": " << ctime(&t);
373 os << " Priority: " << PriorityName[PriorityFlag] << "\n";
374 os << " Status: " << StatusName[StatusFlag] << "\n";
375 if( AlarmType ) {
376 os << " Alarm Type: " << AlarmTypeName[AlarmType] << "\n";
379 // print recurrence data if available
380 os << " Recurring: " << (Recurring ? "yes" : "no") << "\n";
381 if( Recurring ) {
382 switch( RecurringType )
384 case Day:
385 os << " Every day.\n";
386 break;
388 case MonthByDate:
389 os << " Every month on the "
390 << DayOfMonth
391 << (DayOfMonth == 1 ? "st" : "")
392 << (DayOfMonth == 2 ? "nd" : "")
393 << (DayOfMonth == 3 ? "rd" : "")
394 << (DayOfMonth > 3 ? "th" : "")
395 << "\n";
396 break;
398 case MonthByDay:
399 os << " Every month on the "
400 << DayNames[DayOfWeek]
401 << " of week "
402 << WeekOfMonth
403 << "\n";
404 break;
406 case YearByDate:
407 os << " Every year on "
408 << MonthNames[MonthOfYear-1]
409 << " " << DayOfMonth << "\n";
410 break;
412 case YearByDay:
413 os << " Every year in " << MonthNames[MonthOfYear-1]
414 << " on "
415 << DayNames[DayOfWeek]
416 << " of week " << WeekOfMonth << "\n";
417 break;
419 case Week:
420 os << " Every week on: ";
421 if( WeekDays & CAL_WD_SUN ) os << "Sun ";
422 if( WeekDays & CAL_WD_MON ) os << "Mon ";
423 if( WeekDays & CAL_WD_TUE ) os << "Tue ";
424 if( WeekDays & CAL_WD_WED ) os << "Wed ";
425 if( WeekDays & CAL_WD_THU ) os << "Thu ";
426 if( WeekDays & CAL_WD_FRI ) os << "Fri ";
427 if( WeekDays & CAL_WD_SAT ) os << "Sat ";
428 os << "\n";
429 break;
431 default:
432 os << " Unknown recurrence type\n";
433 break;
436 os << " Interval: " << Interval << "\n";
438 if( Perpetual )
439 os << " Ends: never\n";
440 else
441 os << " Ends: " << ctime(&RecurringEndTime);
444 os << Unknowns;
445 os << "\n\n";
448 } // namespace Barry