vtodo.cc

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-2011, 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         // begin building vCalendar data
00099         AddAttr(NewAttr("PRODID", "-//OpenSync//NONSGML Barry Task Record//EN"));
00100         AddAttr(NewAttr("BEGIN", "VTODO"));
00101         AddAttr(NewAttr("SEQUENCE", "0"));
00102         AddAttr(NewAttr("SUMMARY", task.Summary.c_str()));
00103         AddAttr(NewAttr("DESCRIPTION", task.Notes.c_str()));
00104         AddAttr(NewAttr("CATEGORIES", ToStringList(task.Categories).c_str()));
00105 
00106         // Status
00107         if (task.StatusFlag == Barry::Task::InProgress)
00108                 AddAttr(NewAttr("STATUS", "IN-PROCESS"));
00109         else if (task.StatusFlag == Barry::Task::Completed)
00110                 AddAttr(NewAttr("STATUS", "COMPLETED"));
00111         else if (task.StatusFlag == Barry::Task::Deferred)
00112                 AddAttr(NewAttr("STATUS", "CANCELLED"));
00113         else if (task.StatusFlag == Barry::Task::Waiting)
00114                 AddAttr(NewAttr("STATUS", "NEEDS-ACTION"));
00115 
00116         // Priority
00117         if (task.PriorityFlag == Barry::Task::High)
00118                 AddAttr(NewAttr("PRIORITY", "3"));
00119         else if (task.PriorityFlag == Barry::Task::Normal)
00120                 AddAttr(NewAttr("PRIORITY", "5"));
00121         else
00122                 AddAttr(NewAttr("PRIORITY", "7"));
00123 
00124         // StartTime
00125         if( task.StartTime ) {
00126                 AddAttr(NewAttr("DTSTART",
00127                         m_vtc.unix2vtime(&task.StartTime).c_str()));
00128         }
00129 
00130         // DueTime DueFlag
00131         if( task.DueDateFlag ) {
00132                 AddAttr(NewAttr("DUE",
00133                         m_vtc.unix2vtime(&task.DueTime).c_str()));
00134         }
00135 
00136         // FIXME - add a truly globally unique "UID" string?
00137 
00138         AddAttr(NewAttr("END", "VTODO"));
00139 
00140         // generate the raw VTODO data
00141         m_gTodoData = b_vformat_to_string(Format(), VFORMAT_TODO_20);
00142         m_vTodoData = m_gTodoData;
00143 
00144 //      trace.logf("ToTask, resulting vtodo data: %s", m_vTodoData.c_str());
00145         return m_vTodoData;
00146 }
00147 
00148 // Main conversion routine for converting from vTodo data string
00149 // to a Barry::Task object.
00150 const Barry::Task& vTodo::ToBarry(const char *vtodo, uint32_t RecordId)
00151 {
00152         using namespace std;
00153 
00154 //      Trace trace("vTodo::ToBarry");
00155 //      trace.logf("ToBarry, working on vtodo data: %s", vtodo);
00156 
00157         // we only handle vTodo data with one vtodo block
00158         if( HasMultipleVTodos() )
00159                 throw ConvertError("vCalendar data contains more than one VTODO block, unsupported");
00160 
00161         // start fresh
00162         Clear();
00163 
00164         // store the vTodo raw data
00165         m_vTodoData = vtodo;
00166 
00167         // create format parser structures
00168         SetFormat( b_vformat_new_from_string(vtodo) );
00169         if( !Format() )
00170                 throw ConvertError("resource error allocating vtodo");
00171 
00172         string summary = GetAttr("SUMMARY", "/vtodo");
00173 //      trace.logf("SUMMARY attr retrieved: %s", summary.c_str());
00174         if( summary.size() == 0 ) {
00175                 summary = "<blank subject>";
00176 //              trace.logf("ERROR: bad data, blank SUMMARY: %s", vtodo);
00177         }
00178 
00179         string notes = GetAttr("DESCRIPTION", "/vtodo");
00180 //      trace.logf("DESCRIPTION attr retrieved: %s", notes.c_str());
00181 
00182         string status = GetAttr("STATUS", "/vtodo");
00183 //      trace.logf("STATUS attr retrieved: %s", status.c_str());
00184 
00185         string priority = GetAttr("PRIORITY", "/vtodo");
00186 //      trace.logf("PRIORITY attr retrieved: %s", priority.c_str());
00187 
00188         string start = GetAttr("DTSTART", "/vtodo");
00189 //      trace.logf("DTSTART attr retrieved: %s", start.c_str());
00190 
00191         string due = GetAttr("DUE", "/vtodo");
00192 //      trace.logf("DUE attr retrieved: %s", due.c_str());
00193 
00194 
00195         //
00196         // Now, run checks and convert into Barry object
00197         //
00198 
00199         // FIXME - we are assuming that any non-UTC timestamps
00200         // in the vcalendar record will be in the current timezone...
00201         // This is wrong!  fix this later.
00202         //
00203         // Also, we current ignore any time zone
00204         // parameters that might be in the vcalendar format... this
00205         // must be fixed.
00206         //
00207 
00208         Barry::Task &rec = m_BarryTask;
00209         rec.SetIds(Barry::Task::GetDefaultRecType(), RecordId);
00210 
00211         // Categories
00212 
00213         rec.Categories = GetValueVector("CATEGORIES","/vtodo");
00214 
00215         // SUMMARY & DESCRIPTION fields
00216         rec.Summary = summary;
00217         rec.Notes = notes;
00218 
00219         // STATUS field
00220         if (status.size()) {
00221                 ToLower(status);
00222 
00223                 const char *s = status.c_str();
00224 
00225                 if (strstr(s, "in-process"))
00226                         rec.StatusFlag = Barry::Task::InProgress;
00227                 else if (strstr(s, "completed"))
00228                         rec.StatusFlag = Barry::Task::Completed;
00229                 else if (strstr(s, "cancelled"))
00230                         rec.StatusFlag = Barry::Task::Deferred;
00231                 else if (strstr(s, "needs-action"))
00232                         rec.StatusFlag = Barry::Task::Waiting;
00233                 else
00234                         rec.StatusFlag = Barry::Task::NotStarted;
00235         }
00236 
00237         // PRIORITY field
00238         if (priority.size()) {
00239                 ToLower(priority);
00240 
00241                 const char *s = priority.c_str();
00242 
00243                 const int val = atoi(s);
00244 
00245                 if (val < 4)
00246                         rec.PriorityFlag = Barry::Task::High;
00247                 else if (val < 7)
00248                         rec.PriorityFlag = Barry::Task::Normal;
00249                 else
00250                         rec.PriorityFlag = Barry::Task::Low;
00251         }
00252 
00253 
00254         // STARTTIME & DUETIME
00255         if (start.size()) {
00256                 rec.StartTime = m_vtc.vtime2unix(start.c_str());
00257         }
00258 
00259         if (due.size()) {
00260                 rec.DueDateFlag = true;
00261                 rec.DueTime = m_vtc.vtime2unix(due.c_str());
00262         }
00263 
00264         std::ostringstream oss;
00265         m_BarryTask.Dump(oss);
00266 //      trace.logf("ToBarry, resulting Barry record: %s", oss.str().c_str());
00267         return m_BarryTask;
00268 }
00269 
00270 // Transfers ownership of m_gTaskData to the caller.
00271 char* vTodo::ExtractVTodo()
00272 {
00273         char *ret = m_gTodoData;
00274         m_gTodoData = 0;
00275         return ret;
00276 }
00277 
00278 void vTodo::Clear()
00279 {
00280         vBase::Clear();
00281         m_vTodoData.clear();
00282         m_BarryTask.Clear();
00283 
00284         if( m_gTodoData ) {
00285                 g_free(m_gTodoData);
00286                 m_gTodoData = 0;
00287         }
00288 }
00289 
00290 }} // namespace Barry::Sync
00291