rpm/barry.spec file now assumes gui and opensync, with conditional checks
[barry/pauldeden.git] / src / r_task.cc
blobcf877e74b90fd1d227d7620c8d4a9796ed957278
1 ///
2 /// \file r_task.cc
3 /// Record parsing class for the task database.
4 ///
6 /*
7 Copyright (C) 2005-2008, 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>
31 #include <string.h>
33 using namespace std;
34 using namespace Barry::Protocol;
36 namespace Barry {
38 ///////////////////////////////////////////////////////////////////////////////
39 // Task Class
41 // Task Field Codes
42 #define TSKFC_TASK_TYPE 0x01
43 #define TSKFC_TITLE 0x02
44 #define TSKFC_NOTES 0x03
45 #define TSKFC_START_TIME 0x05
46 #define TSKFC_DUE_TIME 0x06
47 #define TSKFC_DUE_FLAG 0x08
48 #define TSKFC_STATUS 0x09
49 #define TSKFC_PRIORITY 0x0a
50 #define TSKFC_RECURRENCE_DATA 0x0c
51 #define TSKFC_ALARM_TYPE 0x0e
52 #define TSKFC_ALARM_TIME 0x0f
53 #define TSKFC_TIMEZONE_CODE 0x10
54 #define TSKFC_CATEGORIES 0x11
55 #define TSKFC_END 0xffff
57 FieldLink<Task> TaskFieldLinks[] = {
58 { TSKFC_TITLE, "Summary", 0, 0, &Task::Summary, 0, 0 },
59 { TSKFC_NOTES, "Notes", 0, 0, &Task::Notes, 0, 0 },
60 { TSKFC_START_TIME, "Start Time", 0, 0, 0, 0, &Task::StartTime },
61 { TSKFC_DUE_TIME, "Due Time", 0, 0, 0, 0, &Task::DueTime },
62 { TSKFC_ALARM_TIME, "Alarm Time", 0, 0, 0, 0, &Task::AlarmTime },
63 { TSKFC_CATEGORIES, "Categories", 0, 0, &Task::Categories, 0, 0 },
64 { TSKFC_END, "End of List", 0, 0, 0, 0, 0 },
67 Task::Task()
69 Clear();
72 Task::~Task()
76 const unsigned char* Task::ParseField(const unsigned char *begin,
77 const unsigned char *end)
79 const CommonField *field = (const CommonField *) begin;
81 // advance and check size
82 begin += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
83 if( begin > end ) // if begin==end, we are ok
84 return begin;
86 if( !btohs(field->size) ) // if field has no size, something's up
87 return begin;
89 if( field->type == TSKFC_TASK_TYPE ) {
90 if( ( TaskType = field->u.raw[0] ) != 't' ) {
91 throw Error("Task::ParseField: Task Type is not 't'");
93 return begin;
96 // cycle through the type table
97 for( FieldLink<Task> *b = TaskFieldLinks;
98 b->type != TSKFC_END;
99 b++ )
101 if( b->type == field->type ) {
102 if( b->strMember ) {
103 std::string &s = this->*(b->strMember);
104 s = ParseFieldString(field);
105 return begin; // done!
107 else if( b->timeMember && btohs(field->size) == 4 ) {
108 time_t &t = this->*(b->timeMember);
109 t = min2time(field->u.min1900);
110 return begin;
114 // handle special cases
115 switch( field->type )
117 case TSKFC_PRIORITY:
118 if( field->u.raw[0] > Low ) {
119 throw Error( "Task::ParseField: priority field out of bounds" );
121 else {
122 PriorityFlag = (PriorityFlagType)field->u.raw[0];
124 return begin;
126 case TSKFC_STATUS:
127 if( field->u.raw[0] > Deferred ) {
128 throw Error( "Task::ParseField: priority field out of bounds" );
130 else {
131 StatusFlag = (StatusFlagType)field->u.raw[0];
133 return begin;
135 case TSKFC_TIMEZONE_CODE:
136 if( btohs(field->size) == 4 ) {
137 TimeZoneCode = btohs(field->u.code);
139 else {
140 throw Error("Task::ParseField: not enough data in time zone code field");
142 return begin;
144 case TSKFC_RECURRENCE_DATA:
145 if( btohs(field->size) >= CALENDAR_RECURRENCE_DATA_FIELD_SIZE ) {
146 Recurring = true;
147 ParseRecurrenceData(&field->u.raw[0]);
149 else {
150 throw Error("Task::ParseField: not enough data in recurrence data field");
152 return begin;
154 case TSKFC_DUE_FLAG:
155 DueDateFlag = field->u.raw[0];
156 return begin;
158 case TSKFC_ALARM_TYPE:
159 if( field->u.raw[0] > Relative ) {
160 throw Error("Task::ParseField: AlarmType out of bounds" );
162 else {
163 AlarmType = (AlarmFlagType)field->u.raw[0];
165 return begin;
168 // if still not handled, add to the Unknowns list
169 UnknownField uf;
170 uf.type = field->type;
171 uf.data.assign((const char*)field->u.raw, btohs(field->size));
172 Unknowns.push_back(uf);
174 // return new pointer for next field
175 return begin;
178 // this function assumes the size has already been checked
179 void Task::ParseRecurrenceData(const void *data)
181 const CalendarRecurrenceDataField *rec =
182 (const CalendarRecurrenceDataField*) data;
184 Interval = btohs(rec->interval);
185 if( Interval < 1 )
186 Interval = 1; // must always be >= 1
188 if( rec->endTime == 0xffffffff ) {
189 Perpetual = true;
191 else {
192 RecurringEndTime = min2time(rec->endTime);
193 Perpetual = false;
196 switch( rec->type )
198 case CRDF_TYPE_DAY:
199 RecurringType = Day;
200 // no extra data
201 break;
203 case CRDF_TYPE_MONTH_BY_DATE:
204 RecurringType = MonthByDate;
205 DayOfMonth = rec->u.month_by_date.monthDay;
206 break;
208 case CRDF_TYPE_MONTH_BY_DAY:
209 RecurringType = MonthByDay;
210 DayOfWeek = rec->u.month_by_day.weekDay;
211 WeekOfMonth = rec->u.month_by_day.week;
212 break;
214 case CRDF_TYPE_YEAR_BY_DATE:
215 RecurringType = YearByDate;
216 DayOfMonth = rec->u.year_by_date.monthDay;
217 MonthOfYear = rec->u.year_by_date.month;
218 break;
220 case CRDF_TYPE_YEAR_BY_DAY:
221 RecurringType = YearByDay;
222 DayOfWeek = rec->u.year_by_day.weekDay;
223 WeekOfMonth = rec->u.year_by_day.week;
224 MonthOfYear = rec->u.year_by_day.month;
225 break;
227 case CRDF_TYPE_WEEK:
228 RecurringType = Week;
230 // Note: this simple copy is only possible since
231 // the CAL_WD_* constants are the same as CRDF_WD_* constants.
232 // If this ever changes, this code will need to change.
233 WeekDays = rec->u.week.days;
234 break;
236 default:
237 eout("Unknown recurrence data type: 0x"
238 << setbase(16) << (unsigned int) rec->type);
239 throw Error("Unknown recurrence data type");
243 // this function assumes there is CALENDAR_RECURRENCE_DATA_FIELD_SIZE bytes
244 // available in data
245 void Task::BuildRecurrenceData(void *data)
247 if( !Recurring )
248 throw Error("Task::BuildRecurrenceData: Attempting to build recurrence data on non-recurring record.");
250 CalendarRecurrenceDataField *rec = (CalendarRecurrenceDataField*) data;
252 // set all to zero
253 memset(data, 0, CALENDAR_RECURRENCE_DATA_FIELD_SIZE);
255 rec->interval = htobs(Interval);
256 rec->startTime = time2min(StartTime);
257 if( Perpetual )
258 rec->endTime = 0xffffffff;
259 else
260 rec->endTime = time2min(RecurringEndTime);
262 switch( RecurringType )
264 case Day:
265 rec->type = CRDF_TYPE_DAY;
266 // no extra data
267 break;
269 case MonthByDate:
270 rec->type = CRDF_TYPE_MONTH_BY_DATE;
271 rec->u.month_by_date.monthDay = DayOfMonth;
272 break;
274 case MonthByDay:
275 rec->type = CRDF_TYPE_MONTH_BY_DAY;
276 rec->u.month_by_day.weekDay = DayOfWeek;
277 rec->u.month_by_day.week = WeekOfMonth;
278 break;
280 case YearByDate:
281 rec->type = CRDF_TYPE_YEAR_BY_DATE;
282 rec->u.year_by_date.monthDay = DayOfMonth;
283 rec->u.year_by_date.month = MonthOfYear;
284 break;
286 case YearByDay:
287 rec->type = CRDF_TYPE_YEAR_BY_DAY;
288 rec->u.year_by_day.weekDay = DayOfWeek;
289 rec->u.year_by_day.week = WeekOfMonth;
290 rec->u.year_by_day.month = MonthOfYear;
291 break;
293 case Week:
294 rec->type = CRDF_TYPE_WEEK;
296 // Note: this simple copy is only possible since
297 // the CAL_WD_* constants are the same as CRDF_WD_* constants.
298 // If this ever changes, this code will need to change.
299 rec->u.week.days = WeekDays;
300 break;
302 default:
303 eout("Task::BuildRecurrenceData: "
304 "Unknown recurrence data type: " << rec->type);
305 throw Error("Task::BuildRecurrenceData: Unknown recurrence data type");
309 void Task::ParseHeader(const Data &data, size_t &offset)
311 // no header in Task records
314 void Task::ParseFields(const Data &data, size_t &offset)
316 const unsigned char *finish = ParseCommonFields(*this,
317 data.GetData() + offset, data.GetData() + data.GetSize());
318 offset += finish - (data.GetData() + offset);
321 void Task::Clear()
323 Summary.clear();
324 Notes.clear();
325 Categories.clear();
326 StartTime = DueTime = AlarmTime = 0;
328 PriorityFlag = (PriorityFlagType)0;
329 StatusFlag = (StatusFlagType)0;
330 AlarmType = (AlarmFlagType)0;
332 TaskType = 0;
334 Perpetual = false;
335 DueDateFlag = false;
336 Recurring = false;
338 TimeZoneCode = GetTimeZoneCode( 0, 0 );
340 Unknowns.clear();
343 void Task::Dump(std::ostream &os) const
345 static const char *PriorityName[] = { "High", "Normal", "Low" };
346 static const char *StatusName[] = { "Not Started", "In Progress",
347 "Completed", "Waiting", "Deferred" };
348 static const char *DayNames[] = { "Sun", "Mon", "Tue", "Wed",
349 "Thu", "Fri", "Sat" };
350 static const char *MonthNames[] = { "Jan", "Feb", "Mar", "Apr",
351 "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
352 static const char *AlarmTypeName[] = { "None", "By Date", "Relative" };
354 os << "Task entry: 0x" << setbase(16) << RecordId
355 << " (" << (unsigned int)RecType << ")\n";
357 // cycle through the type table
358 for( const FieldLink<Task> *b = TaskFieldLinks;
359 b->type != TSKFC_END;
360 b++ )
362 if( b->strMember ) {
363 const std::string &s = this->*(b->strMember);
364 if( s.size() )
365 os << " " << b->name << ": " << s << "\n";
367 else if( b->timeMember ) {
368 time_t t = this->*(b->timeMember);
369 if( t > 0 )
370 os << " " << b->name << ": " << ctime(&t);
374 os << " Priority: " << PriorityName[PriorityFlag] << "\n";
375 os << " Status: " << StatusName[StatusFlag] << "\n";
376 if( AlarmType ) {
377 os << " Alarm Type: " << AlarmTypeName[AlarmType] << "\n";
380 // print recurrence data if available
381 os << " Recurring: " << (Recurring ? "yes" : "no") << "\n";
382 if( Recurring ) {
383 switch( RecurringType )
385 case Day:
386 os << " Every day.\n";
387 break;
389 case MonthByDate:
390 os << " Every month on the "
391 << DayOfMonth
392 << (DayOfMonth == 1 ? "st" : "")
393 << (DayOfMonth == 2 ? "nd" : "")
394 << (DayOfMonth == 3 ? "rd" : "")
395 << (DayOfMonth > 3 ? "th" : "")
396 << "\n";
397 break;
399 case MonthByDay:
400 os << " Every month on the "
401 << DayNames[DayOfWeek]
402 << " of week "
403 << WeekOfMonth
404 << "\n";
405 break;
407 case YearByDate:
408 os << " Every year on "
409 << MonthNames[MonthOfYear-1]
410 << " " << DayOfMonth << "\n";
411 break;
413 case YearByDay:
414 os << " Every year in " << MonthNames[MonthOfYear-1]
415 << " on "
416 << DayNames[DayOfWeek]
417 << " of week " << WeekOfMonth << "\n";
418 break;
420 case Week:
421 os << " Every week on: ";
422 if( WeekDays & CAL_WD_SUN ) os << "Sun ";
423 if( WeekDays & CAL_WD_MON ) os << "Mon ";
424 if( WeekDays & CAL_WD_TUE ) os << "Tue ";
425 if( WeekDays & CAL_WD_WED ) os << "Wed ";
426 if( WeekDays & CAL_WD_THU ) os << "Thu ";
427 if( WeekDays & CAL_WD_FRI ) os << "Fri ";
428 if( WeekDays & CAL_WD_SAT ) os << "Sat ";
429 os << "\n";
430 break;
432 default:
433 os << " Unknown recurrence type\n";
434 break;
437 os << " Interval: " << Interval << "\n";
439 if( Perpetual )
440 os << " Ends: never\n";
441 else
442 os << " Ends: " << ctime(&RecurringEndTime);
445 os << Unknowns;
446 os << "\n\n";
449 } // namespace Barry