vtodo.cc

Go to the documentation of this file.
00001 ///
00002 /// \file       vtodo.cc
00003 ///             Conversion routines for vtodos (VCALENDAR, etc)
00004 ///
00005 
00006 /*
00007     Copyright (C) 2008-2009, Nicolas VIVIEN
00008     Copyright (C) 2006-2012, Net Direct Inc. (http://www.netdirect.ca/)
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 "vtodo.h"
00024 //#include "trace.h"
00025 #include <stdint.h>
00026 #include <stdlib.h>
00027 #include <glib.h>
00028 #include <string.h>
00029 #include <sstream>
00030 
00031 namespace Barry { namespace Sync {
00032 
00033 //////////////////////////////////////////////////////////////////////////////
00034 // Utility functions
00035 
00036 namespace {
00037         static void ToLower(std::string &str)
00038         {
00039                 size_t i = 0;
00040                 while( i < str.size() ) {
00041                         str[i] = tolower(str[i]);
00042                         i++;
00043                 }
00044         }
00045 }
00046 
00047 //////////////////////////////////////////////////////////////////////////////
00048 // vTodo
00049 
00050 vTodo::vTodo(vTimeConverter &vtc)
00051         : m_vtc(vtc)
00052         , m_gTodoData(0)
00053 {
00054 }
00055 
00056 vTodo::~vTodo()
00057 {
00058         if( m_gTodoData ) {
00059                 g_free(m_gTodoData);
00060         }
00061 }
00062 
00063 bool vTodo::HasMultipleVTodos() const
00064 {
00065         int count = 0;
00066         b_VFormat *format = const_cast<b_VFormat*>(Format());
00067         GList *attrs = format ? b_vformat_get_attributes(format) : 0;
00068         for( ; attrs; attrs = attrs->next ) {
00069                 b_VFormatAttribute *attr = (b_VFormatAttribute*) attrs->data;
00070                 if( strcasecmp(b_vformat_attribute_get_name(attr), "BEGIN") == 0 &&
00071                     strcasecmp(b_vformat_attribute_get_nth_value(attr, 0), "VTODO") == 0 )
00072                 {
00073                         count++;
00074                 }
00075         }
00076         return count > 1;
00077 }
00078 
00079 
00080 // Main conversion routine for converting from Barry::Task to
00081 // a vTodo string of data.
00082 const std::string& vTodo::ToTask(const Barry::Task &task)
00083 {
00084 //      Trace trace("vTodo::ToTask");
00085         std::ostringstream oss;
00086         task.Dump(oss);
00087 //      trace.logf("ToTask, initial Barry record: %s", oss.str().c_str());
00088 
00089         // start fresh
00090         Clear();
00091         SetFormat( b_vformat_new() );
00092         if( !Format() )
00093                 throw ConvertError("resource error allocating vformat");
00094 
00095         // store the Barry object we're working with
00096         m_BarryTask = task;
00097 
00098         // RFC section 4.8.7.2 requires DTSTAMP in all VEVENT, VTODO,
00099         // VJOURNAL, and VFREEBUSY calendar components, and it must be
00100         // in UTC.  DTSTAMP holds the timestamp of when the iCal object itself
00101         // was created, not when the object was created in the device or app.
00102         // So, find out what time it is "now".
00103         time_t now = time(NULL);
00104 
00105         // begin building vCalendar data
00106         AddAttr(NewAttr("PRODID", "-//OpenSync//NONSGML Barry Task Record//EN"));
00107         AddAttr(NewAttr("BEGIN", "VTODO"));
00108         AddAttr(NewAttr("DTSTAMP", m_vtc.unix2vtime(&now).c_str())); // see note above
00109         AddAttr(NewAttr("SEQUENCE", "0"));
00110         AddAttr(NewAttr("SUMMARY", task.Summary.c_str()));
00111         AddAttr(NewAttr("DESCRIPTION", task.Notes.c_str()));
00112         AddAttr(NewAttr("CATEGORIES", ToStringList(task.Categories).c_str()));
00113 
00114         // Status
00115         if (task.StatusFlag == Barry::Task::InProgress)
00116                 AddAttr(NewAttr("STATUS", "IN-PROCESS"));
00117         else if (task.StatusFlag == Barry::Task::Completed)
00118                 AddAttr(NewAttr("STATUS", "COMPLETED"));
00119         else if (task.StatusFlag == Barry::Task::Deferred)
00120                 AddAttr(NewAttr("STATUS", "CANCELLED"));
00121         else if (task.StatusFlag == Barry::Task::Waiting)
00122                 AddAttr(NewAttr("STATUS", "NEEDS-ACTION"));
00123 
00124         // Priority
00125         if (task.PriorityFlag == Barry::Task::High)
00126                 AddAttr(NewAttr("PRIORITY", "3"));
00127         else if (task.PriorityFlag == Barry::Task::Normal)
00128                 AddAttr(NewAttr("PRIORITY", "5"));
00129         else
00130                 AddAttr(NewAttr("PRIORITY", "7"));
00131 
00132         // StartTime
00133         if( task.StartTime.Time ) {
00134                 AddAttr(NewAttr("DTSTART",
00135                         m_vtc.unix2vtime(&task.StartTime.Time).c_str()));
00136         }
00137 
00138         // DueTime DueFlag
00139         if( task.DueTime.IsValid() ) {
00140                 AddAttr(NewAttr("DUE",
00141                         m_vtc.unix2vtime(&task.DueTime.Time).c_str()));
00142         }
00143 
00144         // FIXME - add a truly globally unique "UID" string?
00145 
00146         AddAttr(NewAttr("END", "VTODO"));
00147 
00148         // generate the raw VTODO data
00149         m_gTodoData = b_vformat_to_string(Format(), VFORMAT_TODO_20);
00150         m_vTodoData = m_gTodoData;
00151 
00152 //      trace.logf("ToTask, resulting vtodo data: %s", m_vTodoData.c_str());
00153         return m_vTodoData;
00154 }
00155 
00156 // Main conversion routine for converting from vTodo data string
00157 // to a Barry::Task object.
00158 const Barry::Task& vTodo::ToBarry(const char *vtodo, uint32_t RecordId)
00159 {
00160         using namespace std;
00161 
00162 //      Trace trace("vTodo::ToBarry");
00163 //      trace.logf("ToBarry, working on vtodo data: %s", vtodo);
00164 
00165         // we only handle vTodo data with one vtodo block
00166         if( HasMultipleVTodos() )
00167                 throw ConvertError("vCalendar data contains more than one VTODO block, unsupported");
00168 
00169         // start fresh
00170         Clear();
00171 
00172         // store the vTodo raw data
00173         m_vTodoData = vtodo;
00174 
00175         // create format parser structures
00176         SetFormat( b_vformat_new_from_string(vtodo) );
00177         if( !Format() )
00178                 throw ConvertError("resource error allocating vtodo");
00179 
00180         string summary = GetAttr("SUMMARY", "/vtodo");
00181 //      trace.logf("SUMMARY attr retrieved: %s", summary.c_str());
00182         if( summary.size() == 0 ) {
00183                 summary = "<blank subject>";
00184 //              trace.logf("ERROR: bad data, blank SUMMARY: %s", vtodo);
00185         }
00186 
00187         string notes = GetAttr("DESCRIPTION", "/vtodo");
00188 //      trace.logf("DESCRIPTION attr retrieved: %s", notes.c_str());
00189 
00190         string status = GetAttr("STATUS", "/vtodo");
00191 //      trace.logf("STATUS attr retrieved: %s", status.c_str());
00192 
00193         string priority = GetAttr("PRIORITY", "/vtodo");
00194 //      trace.logf("PRIORITY attr retrieved: %s", priority.c_str());
00195 
00196         string start = GetAttr("DTSTART", "/vtodo");
00197 //      trace.logf("DTSTART attr retrieved: %s", start.c_str());
00198 
00199         string due = GetAttr("DUE", "/vtodo");
00200 //      trace.logf("DUE attr retrieved: %s", due.c_str());
00201 
00202 
00203         //
00204         // Now, run checks and convert into Barry object
00205         //
00206 
00207         // FIXME - we are assuming that any non-UTC timestamps
00208         // in the vcalendar record will be in the current timezone...
00209         // This is wrong!  fix this later.
00210         //
00211         // Also, we current ignore any time zone
00212         // parameters that might be in the vcalendar format... this
00213         // must be fixed.
00214         //
00215 
00216         Barry::Task &rec = m_BarryTask;
00217         rec.SetIds(Barry::Task::GetDefaultRecType(), RecordId);
00218 
00219         // Categories
00220 
00221         rec.Categories = GetValueVector("CATEGORIES","/vtodo");
00222 
00223         // SUMMARY & DESCRIPTION fields
00224         rec.Summary = summary;
00225         rec.Notes = notes;
00226 
00227         // STATUS field
00228         if (status.size()) {
00229                 ToLower(status);
00230 
00231                 const char *s = status.c_str();
00232 
00233                 if (strstr(s, "in-process"))
00234                         rec.StatusFlag = Barry::Task::InProgress;
00235                 else if (strstr(s, "completed"))
00236                         rec.StatusFlag = Barry::Task::Completed;
00237                 else if (strstr(s, "cancelled"))
00238                         rec.StatusFlag = Barry::Task::Deferred;
00239                 else if (strstr(s, "needs-action"))
00240                         rec.StatusFlag = Barry::Task::Waiting;
00241                 else
00242                         rec.StatusFlag = Barry::Task::NotStarted;
00243         }
00244 
00245         // PRIORITY field
00246         if (priority.size()) {
00247                 ToLower(priority);
00248 
00249                 const char *s = priority.c_str();
00250 
00251                 const int val = atoi(s);
00252 
00253                 if (val < 4)
00254                         rec.PriorityFlag = Barry::Task::High;
00255                 else if (val < 7)
00256                         rec.PriorityFlag = Barry::Task::Normal;
00257                 else
00258                         rec.PriorityFlag = Barry::Task::Low;
00259         }
00260 
00261 
00262         // STARTTIME & DUETIME
00263         if (start.size()) {
00264                 rec.StartTime.Time = m_vtc.vtime2unix(start.c_str());
00265         }
00266 
00267         if (due.size()) {
00268                 rec.DueTime.Time = m_vtc.vtime2unix(due.c_str());
00269         }
00270 
00271         std::ostringstream oss;
00272         m_BarryTask.Dump(oss);
00273 //      trace.logf("ToBarry, resulting Barry record: %s", oss.str().c_str());
00274         return m_BarryTask;
00275 }
00276 
00277 // Transfers ownership of m_gTaskData to the caller.
00278 char* vTodo::ExtractVTodo()
00279 {
00280         char *ret = m_gTodoData;
00281         m_gTodoData = 0;
00282         return ret;
00283 }
00284 
00285 void vTodo::Clear()
00286 {
00287         vBase::Clear();
00288         m_vTodoData.clear();
00289         m_BarryTask.Clear();
00290 
00291         if( m_gTodoData ) {
00292                 g_free(m_gTodoData);
00293                 m_gTodoData = 0;
00294         }
00295 }
00296 
00297 }} // namespace Barry::Sync
00298