From dba66f33304abe2b5c3fe817371b491fc75da818 Mon Sep 17 00:00:00 2001 From: Chris Frey Date: Sat, 17 Jul 2010 01:18:51 -0400 Subject: [PATCH] os4x: removed redundant vformat code, and use libbarrysync --- opensync-plugin-0.4x/configure.ac | 2 +- opensync-plugin-0.4x/src/Makefile.am | 2 - opensync-plugin-0.4x/src/vbase.cc | 381 ------ opensync-plugin-0.4x/src/vbase.h | 159 --- opensync-plugin-0.4x/src/vcard.cc | 901 +++----------- opensync-plugin-0.4x/src/vcard.h | 49 +- opensync-plugin-0.4x/src/vevent.cc | 1059 ++++------------- opensync-plugin-0.4x/src/vevent.h | 51 +- opensync-plugin-0.4x/src/vformat.c | 2130 ---------------------------------- opensync-plugin-0.4x/src/vformat.h | 168 --- opensync-plugin-0.4x/src/vjournal.cc | 156 +-- opensync-plugin-0.4x/src/vjournal.h | 40 +- opensync-plugin-0.4x/src/vtodo.cc | 630 +++------- opensync-plugin-0.4x/src/vtodo.h | 40 +- 14 files changed, 590 insertions(+), 5178 deletions(-) delete mode 100644 opensync-plugin-0.4x/src/vbase.cc delete mode 100644 opensync-plugin-0.4x/src/vbase.h rewrite opensync-plugin-0.4x/src/vcard.cc (78%) rewrite opensync-plugin-0.4x/src/vevent.cc (78%) delete mode 100644 opensync-plugin-0.4x/src/vformat.c delete mode 100644 opensync-plugin-0.4x/src/vformat.h rewrite opensync-plugin-0.4x/src/vtodo.cc (62%) diff --git a/opensync-plugin-0.4x/configure.ac b/opensync-plugin-0.4x/configure.ac index 87672f68..56ccdb52 100644 --- a/opensync-plugin-0.4x/configure.ac +++ b/opensync-plugin-0.4x/configure.ac @@ -31,7 +31,7 @@ AM_CONDITIONAL([WITH_GCCVISIBILITY], [test "$HAVE_C_GCCVISIBILITY" = "1" -a "$HA AC_LANG([C++]) -PKG_CHECK_MODULES([BARRY], [libbarry-0]) +PKG_CHECK_MODULES([BARRY], [libbarry-0 libbarrysync-0]) # Carry the special tree build environment variables from parent configure, # just in case user is doing a complete tree build with --enable-opensync-plugin diff --git a/opensync-plugin-0.4x/src/Makefile.am b/opensync-plugin-0.4x/src/Makefile.am index ad94f9ff..0a8350ce 100644 --- a/opensync-plugin-0.4x/src/Makefile.am +++ b/opensync-plugin-0.4x/src/Makefile.am @@ -28,12 +28,10 @@ barry_sync_LTLIBRARIES = barry_sync.la barry_sync_la_SOURCES = \ barry_sync.cc barry_sync.h \ environment.cc environment.h \ - vbase.cc vbase.h \ vevent.cc vevent.h \ vcard.cc vcard.h \ vjournal.cc vjournal.h \ vtodo.cc vtodo.h \ - vformat.c vformat.h \ format_check.h \ trace.h \ tosserror.h diff --git a/opensync-plugin-0.4x/src/vbase.cc b/opensync-plugin-0.4x/src/vbase.cc deleted file mode 100644 index 1966d908..00000000 --- a/opensync-plugin-0.4x/src/vbase.cc +++ /dev/null @@ -1,381 +0,0 @@ -// -// \file vbase.cc -// vformat support routines in base class -// - -/* - Copyright (C) 2006-2010, Net Direct Inc. (http://www.netdirect.ca/) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License in the COPYING file at the - root directory of this project for more details. -*/ - -#include "vbase.h" -#include "trace.h" -#include "vformat.h" // comes from opensync, but not a public header yet -#include -#include -#include - - -////////////////////////////////////////////////////////////////////////////// -// vAttr - -std::string vAttr::GetName() -{ - std::string ret; - - if( !m_attr ) - return ret; - - const char *name = b_vformat_attribute_get_name(m_attr); - if( name ) - ret = name; - return ret; -} - -std::string vAttr::GetValue(int nth) -{ - std::string ret; - const char *value = 0; - - if( m_attr ) { - if( b_vformat_attribute_is_single_valued(m_attr) ) { - if( nth == 0 ) - value = b_vformat_attribute_get_value(m_attr); - } - else { - value = b_vformat_attribute_get_nth_value(m_attr, nth); - } - } - - if( value ) - ret = value; - - return ret; -} - -std::string vAttr::GetDecodedValue() -{ - std::string ret; - GString *value = NULL; - - if( m_attr ) { - if( b_vformat_attribute_is_single_valued(m_attr) ) { - value = b_vformat_attribute_get_value_decoded(m_attr); - } - } - - if( value ) - ret.assign(value->str, value->len); - - return ret; -} - -std::string vAttr::GetParam(const char *name, int nth) -{ - std::string ret; - - if( !m_attr ) - return ret; - - b_VFormatParam *param = b_vformat_attribute_find_param(m_attr, name, 0); - if( !param ) - return ret; - - const char *value = b_vformat_attribute_param_get_nth_value(param, nth); - if( value ) - ret = value; - - return ret; -} - -/// Does an exhaustive search through the attribute, searching for all -/// param values that exist for the given name, and returns all values -/// in a comma delimited string. -std::string vAttr::GetAllParams(const char *name) -{ - std::string ret; - - if( !m_attr ) - return ret; - - b_VFormatParam *param = 0; - for( int level = 0; - (param = b_vformat_attribute_find_param(m_attr, name, level)); - level++ ) - { - const char *value = 0; - for( int nth = 0; - (value = b_vformat_attribute_param_get_nth_value(param, nth)); - nth++ ) - { - if( ret.size() ) - ret += ","; - ret += value; - } - } - - return ret; -} - - -////////////////////////////////////////////////////////////////////////////// -// vCalendar - -vBase::vBase() - : m_format(0) -{ -} - -vBase::~vBase() -{ - if( m_format ) { - b_vformat_free(m_format); - m_format = 0; - } -} - -void vBase::SetFormat(b_VFormat *format) -{ - if( m_format ) { - b_vformat_free(m_format); - m_format = 0; - } - m_format = format; -} - -void vBase::Clear() -{ - if( m_format ) { - b_vformat_free(m_format); - m_format = 0; - } -} - -vAttrPtr vBase::NewAttr(const char *name) -{ - Trace trace("vBase::NewAttr"); - - trace.logf("creating valueless attr: %s", name); - - vAttrPtr attr(b_vformat_attribute_new(NULL, name)); - if( !attr.Get() ) - throw ConvertError("resource error allocating vformat attribute"); - return attr; -} - -vAttrPtr vBase::NewAttr(const char *name, const char *value) -{ - Trace trace("vBase::NewAttr"); - -/* -some vCard values are positional (like name), so blank should be allowed... - - if( strlen(value) == 0 ) { - trace.logf("attribute '%s' contains no data, skipping", name); - return vAttrPtr(); - } -*/ - - trace.logf("creating attr: %s, %s", name, value); - - vAttrPtr attr(b_vformat_attribute_new(NULL, name)); - if( !attr.Get() ) - throw ConvertError("resource error allocating vformat attribute"); - - b_vformat_attribute_add_value(attr.Get(), value); - return attr; -} - -void vBase::AddAttr(vAttrPtr attr) -{ - Trace trace("vBase::AddAttr"); - - if( !attr.Get() ) { - trace.log("attribute contains no data, skipping"); - return; - } - - b_vformat_add_attribute(m_format, attr.Extract()); -} - -void vBase::AddValue(vAttrPtr &attr, const char *value) -{ - Trace trace("vBase::AddValue"); - if( !attr.Get() ) { - trace.log("attribute pointer contains no data, skipping"); - return; - } -/* - if( strlen(value) == 0 ) { - trace.log("attribute value is empty, skipping"); - return; - } -*/ - b_vformat_attribute_add_value(attr.Get(), value); -} - -void vBase::AddEncodedValue(vAttrPtr &attr, b_VFormatEncoding encoding, const char *value, int len) -{ - Trace trace("vBase::AddValue"); - if( !attr.Get() ) { - trace.log("attribute pointer contains no data, skipping"); - return; - } - - attr.Get()->encoding = encoding; - attr.Get()->encoding_set = TRUE; - - b_vformat_attribute_add_value_decoded(attr.Get(), value, len); -} - -void vBase::AddParam(vAttrPtr &attr, const char *name, const char *value) -{ - Trace trace("vBase::AddParam"); - - if( !attr.Get() ) { - trace.log("attribute pointer contains no data, skipping"); - return; - } -/* - if( strlen(value) == 0 ) { - trace.log("parameter value is empty, skipping"); - return; - } -*/ - - b_VFormatParam *pParam = b_vformat_attribute_param_new(name); - b_vformat_attribute_param_add_value(pParam, value); - b_vformat_attribute_add_param(attr.Get(), pParam); -} - -std::string vBase::GetAttr(const char *attrname, const char *block) -{ - Trace trace("vBase::GetAttr"); - trace.logf("getting attr: %s", attrname); - - std::string ret; - const char *value = 0; - - bool needs_freeing = false; - - b_VFormatAttribute *attr = b_vformat_find_attribute(m_format, attrname, 0, block); - if( attr ) { - if( b_vformat_attribute_is_single_valued(attr) ) { - value = b_vformat_attribute_get_value(attr); - needs_freeing = true; - } - else { - // FIXME, this is hardcoded - value = b_vformat_attribute_get_nth_value(attr, 0); - } - } - - if( value ) - ret = value; - - if( needs_freeing ) - g_free((char *)value); - - trace.logf("attr value: %s", ret.c_str()); - return ret; -} - -std::vector vBase::GetValueVector(const char *attrname, const char *block) -{ - Trace trace("vBase::GetValueVector"); - trace.logf("getting value vector for: %s", attrname); - - std::vector ret; - const char *value = 0; - bool needs_freeing = false; - - b_VFormatAttribute *attr = b_vformat_find_attribute(m_format, attrname, 0, block); - if( attr ) { - if( b_vformat_attribute_is_single_valued(attr) ) { - value = b_vformat_attribute_get_value(attr); - needs_freeing = true; - } else { - // nasty, but avoids tweaking vformat. - int idx = 0; - do { - value = b_vformat_attribute_get_nth_value(attr, idx++); - if( value ) { - ret.push_back(value); - } - } while( value ); - } - } - - if( needs_freeing ) - g_free((char *)value); - - return ret; -} - -vAttr vBase::GetAttrObj(const char *attrname, int nth, const char *block) -{ - Trace trace("vBase::GetAttrObj"); - trace.logf("getting attr: %s", attrname); - - return vAttr(b_vformat_find_attribute(m_format, attrname, nth, block)); -} - -std::vector vBase::Tokenize(const std::string& str, const char delim) -{ - std::vector tokens; - std::string::size_type delimPos = 0, tokenPos = 0, pos = 0; - - if( str.length() < 1 ) { - return tokens; - } - - while( 1 ) { - delimPos = str.find_first_of(delim, pos); - tokenPos = str.find_first_not_of(delim, pos); - - if( std::string::npos != delimPos ) { - if( std::string::npos != tokenPos ) { - if( tokenPos < delimPos ) { - tokens.push_back(str.substr(pos, delimPos-pos)); - } else { - tokens.push_back(""); - } - } else { - tokens.push_back(""); - } - pos = delimPos + 1; - } else { - if( std::string::npos != tokenPos ){ - tokens.push_back(str.substr(pos)); - } else { - tokens.push_back(""); - } - break; - } - } - return tokens; -} - -std::string vBase::ToStringList(const std::vector &list, const char delim) -{ - std::string str; - for( unsigned int idx = 0; idx < list.size(); idx++ ) { - if( idx ) { - str += delim; - } - str += list[idx]; - } - return str; -} - diff --git a/opensync-plugin-0.4x/src/vbase.h b/opensync-plugin-0.4x/src/vbase.h deleted file mode 100644 index 0930394e..00000000 --- a/opensync-plugin-0.4x/src/vbase.h +++ /dev/null @@ -1,159 +0,0 @@ -/// -/// \file vbase.h -/// Base class for vformat support -/// - -/* - Copyright (C) 2006-2010, Net Direct Inc. (http://www.netdirect.ca/) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License in the COPYING file at the - root directory of this project for more details. -*/ - -#ifndef __BARRY_SYNC_VBASE_H__ -#define __BARRY_SYNC_VBASE_H__ - -#include -#include -#include -#include -#include "vformat.h" - -// forward declarations -class BarryEnvironment; - - -// FIXME - possibly scrap this in favour of opensync's xml time routines, -// but this will require an overhaul of the plugin itself. -class vTimeZone -{ -public: - struct ZoneBlock - { - bool m_daylightSaving; - std::string m_name; - std::string m_dtstart; - std::string m_tzoffsetfrom; - std::string m_tzoffsetto; - }; - -private: - bool m_valid; - -public: - std::string m_id; - -public: - vTimeZone(); - ~vTimeZone(); - - void Clear(); - - bool IsValid(); - - /// used for comparing by TZID - bool operator==(const std::string &tzid) const; -}; - - -typedef Barry::vSmartPtr vAttrPtr; -typedef Barry::vSmartPtr vParamPtr; -typedef Barry::vSmartPtr gStringPtr; - - -// -// vAttr -// -/// Class for reading a b_VFormatAttribute. Reading does not require -/// memory management, so none is done. -/// -class vAttr -{ - b_VFormatAttribute *m_attr; - -public: - vAttr() - : m_attr(0) - { - } - - vAttr(b_VFormatAttribute *attr) - : m_attr(attr) - { - } - - vAttr& operator=(b_VFormatAttribute *attr) - { - m_attr = attr; - return *this; - } - - b_VFormatAttribute* Get() { return m_attr; } - - // These functions do not throw an error if the value - // is NULL or does not exist (for example, if you ask for - // value #5 and there are only 4). - std::string GetName(); - std::string GetValue(int nth = 0); - std::string GetDecodedValue(); - std::string GetParam(const char *name, int nth = 0); - std::string GetAllParams(const char *name); -}; - - -// -// vBase -// -/// Base class containing vformat helper API. -/// -class vBase -{ - // internal data for managing the vformat - b_VFormat *m_format; - -public: - // FIXME - if you put this class in the Barry library, - // you'll need to change the class hierarchy - class ConvertError : public std::runtime_error - { - public: - ConvertError(const std::string &msg) : std::runtime_error(msg) {} - }; - -protected: - vBase(); - virtual ~vBase(); - - b_VFormat* Format() { return m_format; } - const b_VFormat* Format() const { return m_format; } - void SetFormat(b_VFormat *format); - - void Clear(); - - vAttrPtr NewAttr(const char *name); - vAttrPtr NewAttr(const char *name, const char *value); - void AddAttr(vAttrPtr attr); - void AddValue(vAttrPtr &attr, const char *value); - void AddEncodedValue(vAttrPtr &attr, b_VFormatEncoding encoding, const char *value, int len); - void AddParam(vAttrPtr &attr, const char *name, const char *value); - - std::string GetAttr(const char *attrname, const char *block = 0); - std::vector GetValueVector(const char *attrname, const char *block = 0); - vAttr GetAttrObj(const char *attrname, int nth = 0, const char *block = 0); - - std::vector Tokenize(const std::string &str, const char delim = ','); - std::string ToStringList(const std::vector &list, const char delim = ','); - -}; - -#endif - diff --git a/opensync-plugin-0.4x/src/vcard.cc b/opensync-plugin-0.4x/src/vcard.cc dissimilarity index 78% index 8ad24691..92a05a53 100644 --- a/opensync-plugin-0.4x/src/vcard.cc +++ b/opensync-plugin-0.4x/src/vcard.cc @@ -1,723 +1,178 @@ -/// -/// \file vcard.cc -/// Conversion routines for vcards -/// - -/* - Copyright (C) 2006-2010, Net Direct Inc. (http://www.netdirect.ca/) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License in the COPYING file at the - root directory of this project for more details. -*/ - -#include "vcard.h" -#include "environment.h" -#include "trace.h" -#include "vformat.h" // comes from opensync, but not a public header yet -#include -#include -#include -#include -#include - - -////////////////////////////////////////////////////////////////////////////// -// Utility functions - -static void ToLower(std::string &str) -{ - size_t i = 0; - while( i < str.size() ) { - str[i] = tolower(str[i]); - i++; - } -} - -////////////////////////////////////////////////////////////////////////////// -// vCard - -vCard::vCard() - : m_gCardData(0) -{ -} - -vCard::~vCard() -{ - if( m_gCardData ) { - g_free(m_gCardData); - } -} - -void vCard::AddAddress(const char *rfc_type, const Barry::PostalAddress &address) -{ - // add label first - vAttrPtr label = NewAttr("LABEL"); - AddParam(label, "TYPE", rfc_type); - AddValue(label, address.GetLabel().c_str()); - AddAttr(label); - - // add breakout address form - vAttrPtr adr = NewAttr("ADR"); // RFC 2426, 3.2.1 - AddParam(adr, "TYPE", rfc_type); - AddValue(adr, address.Address3.c_str()); // PO Box - AddValue(adr, address.Address2.c_str()); // Extended address - AddValue(adr, address.Address1.c_str()); // Street address - AddValue(adr, address.City.c_str()); // Locality (city) - AddValue(adr, address.Province.c_str()); // Region (province) - AddValue(adr, address.PostalCode.c_str()); // Postal code - AddValue(adr, address.Country.c_str()); // Country name - AddAttr(adr); -} - - -void vCard::AddCategories(const Barry::CategoryList &categories) -{ - if( !categories.size() ) - return; - - vAttrPtr cat = NewAttr("CATEGORIES"); // RFC 2426, 3.6.1 - Barry::CategoryList::const_iterator i = categories.begin(); - for( ; i < categories.end(); ++i ) { - AddValue(cat, i->c_str()); - } - AddAttr(cat); -} - -/// Add phone conditionally, only if phone has data in it. This version -/// does not add a TYPE parameter to the item. -void vCard::AddPhoneCond(const std::string &phone) -{ - if( phone.size() ) { - vAttrPtr tel = NewAttr("TEL", phone.c_str()); - AddAttr(tel); - } -} - -/// Add phone conditionally, only if phone has data in it -void vCard::AddPhoneCond(const char *rfc_type, const std::string &phone) -{ - if( phone.size() ) { - vAttrPtr tel = NewAttr("TEL", phone.c_str()); - AddParam(tel, "TYPE", rfc_type); - AddAttr(tel); - } -} - -void vCard::ParseAddress(vAttr &adr, Barry::PostalAddress &address) -{ - // RFC 2426, 3.2.1 - address.Address3 = adr.GetValue(0); // PO Box - address.Address2 = adr.GetValue(1); // Extended address - address.Address1 = adr.GetValue(2); // Street address - address.City = adr.GetValue(3); // Locality (city) - address.Province = adr.GetValue(4); // Region (province) - address.PostalCode = adr.GetValue(5); // Postal code - address.Country = adr.GetValue(6); // Country name -} - -void vCard::ParseCategories(vAttr &cat, Barry::CategoryList &cats) -{ - int i = 0; - std::string value = cat.GetValue(i); - while( value.size() ) { - cats.push_back(value); - i++; - value = cat.GetValue(i); - } -} - - - -// Main conversion routine for converting from Barry::Contact to -// a vCard string of data. -const std::string& vCard::ToVCard(const Barry::Contact &con) -{ - Trace trace("vCard::ToVCard"); - std::ostringstream oss; - con.Dump(oss); - trace.logf("ToVCard, initial Barry record: %s", oss.str().c_str()); - - // start fresh - Clear(); - SetFormat( b_vformat_new() ); - if( !Format() ) - throw ConvertError("resource error allocating vformat"); - - // store the Barry object we're working with - m_BarryContact = con; - - // - // begin building vCard data - // - - AddAttr(NewAttr("PRODID", "-//OpenSync//NONSGML Barry Contact Record//EN")); - - std::string fullname = con.GetFullName(); - if( fullname.size() ) { - AddAttr(NewAttr("FN", fullname.c_str())); - } - else { - // - // RFC 2426, 3.1.1 states that FN MUST be present in the - // vcard object. Unfortunately, the Blackberry doesn't - // require a name, only a name or company name. - // - // In this case we do nothing, and generate an invalid - // vcard, since if we try to fix our output here, we'll - // likely end up with duplicated company names in the - // Blackberry record after a few syncs. - // - } - - if( con.FirstName.size() || con.LastName.size() ) { - vAttrPtr name = NewAttr("N"); // RFC 2426, 3.1.2 - AddValue(name, con.LastName.c_str()); // Family Name - AddValue(name, con.FirstName.c_str()); // Given Name - AddValue(name, ""); // Additional Names - AddValue(name, con.Prefix.c_str()); // Honorific Prefixes - AddValue(name, ""); // Honorific Suffixes - AddAttr(name); - } - - if( con.WorkAddress.HasData() ) - AddAddress("work", con.WorkAddress); - if( con.HomeAddress.HasData() ) - AddAddress("home", con.HomeAddress); - - // add all applicable phone numbers... there can be multiple - // TEL fields, even with the same TYPE value... therefore, the - // second TEL field with a TYPE=work, will be stored in WorkPhone2 - AddPhoneCond("voice,pref", con.Phone); - AddPhoneCond("fax", con.Fax); - AddPhoneCond("voice,work", con.WorkPhone); - AddPhoneCond("voice,work", con.WorkPhone2); - AddPhoneCond("voice,home", con.HomePhone); - AddPhoneCond("voice,home", con.HomePhone2); - AddPhoneCond("msg,cell", con.MobilePhone); - AddPhoneCond("msg,pager", con.Pager); - AddPhoneCond("voice", con.OtherPhone); - - // add all email addresses, marking first one as "pref" - Barry::Contact::EmailList::const_iterator eai = con.EmailAddresses.begin(); - for( unsigned int i = 0; eai != con.EmailAddresses.end(); ++eai, ++i ) { - const std::string& e = con.GetEmail(i); - if( e.size() ) { - vAttrPtr email = NewAttr("EMAIL", e.c_str()); - if( i == 0 ) { - AddParam(email, "TYPE", "internet,pref"); - } - else { - AddParam(email, "TYPE", "internet"); - } - AddAttr(email); - } - } - - if( con.JobTitle.size() ) { - AddAttr(NewAttr("TITLE", con.JobTitle.c_str())); - AddAttr(NewAttr("ROLE", con.JobTitle.c_str())); - } - - if( con.Company.size() ) { - // RFC 2426, 3.5.5 - vAttrPtr org = NewAttr("ORG", con.Company.c_str()); // Organization name - AddValue(org, ""); // Division name - AddAttr(org); - } - - if( con.Birthday.HasData() ) - AddAttr(NewAttr("BDAY", con.Birthday.ToYYYYMMDD().c_str())); - - if( con.Notes.size() ) - AddAttr(NewAttr("NOTE", con.Notes.c_str())); - if( con.URL.size() ) - AddAttr(NewAttr("URL", con.URL.c_str())); - if( con.Categories.size() ) - AddCategories(con.Categories); - - // Image / Photo - if (con.Image.size()) { - vAttrPtr photo = NewAttr("PHOTO"); - AddEncodedValue(photo, VF_ENCODING_BASE64, con.Image.c_str(), con.Image.size()); - AddParam(photo, "ENCODING", "BASE64"); - AddAttr(photo); - } - - // generate the raw VCARD data - m_gCardData = b_vformat_to_string(Format(), VFORMAT_CARD_30); - m_vCardData = m_gCardData; - - trace.logf("ToVCard, resulting vcard data: %s", m_vCardData.c_str()); - return m_vCardData; -} - -// -// NOTE: -// Treat the following pairs of variables like -// sliding buffers, where higher priority values -// can push existings values from 1 to 2, or from -// 2 to oblivion: -// -// HomePhone + HomePhone2 -// WorkPhone + WorkPhone2 -// Phone + OtherPhone -// -// -// class SlidingPair -// -// This class handles the sliding pair logic for a pair of std::strings. -// -class SlidingPair -{ - std::string &m_first; - std::string &m_second; - int m_evolutionSlot1, m_evolutionSlot2; -public: - static const int DefaultSlot = 99; - SlidingPair(std::string &first, std::string &second) - : m_first(first) - , m_second(second) - , m_evolutionSlot1(DefaultSlot) - , m_evolutionSlot2(DefaultSlot) - { - } - - bool assign(const std::string &value, const char *type_str, int evolutionSlot) - { - bool used = false; - - if( strstr(type_str, "pref") || evolutionSlot < m_evolutionSlot1 ) { - m_second = m_first; - m_evolutionSlot2 = m_evolutionSlot1; - - m_first = value; - m_evolutionSlot1 = evolutionSlot; - - used = true; - } - else if( evolutionSlot < m_evolutionSlot2 ) { - m_second = value; - m_evolutionSlot2 = evolutionSlot; - used = true; - } - else if( m_first.size() == 0 ) { - m_first = value; - m_evolutionSlot1 = evolutionSlot; - used = true; - } - else if( m_second.size() == 0 ) { - m_second = value; - m_evolutionSlot2 = evolutionSlot; - used = true; - } - - return used; - } -}; - - -// Main conversion routine for converting from vCard data string -// to a Barry::Contact object. -const Barry::Contact& vCard::ToBarry(const char *vcard, uint32_t RecordId) -{ - using namespace std; - - Trace trace("vCard::ToBarry"); - trace.logf("ToBarry, working on vcard data: %s", vcard); - - // start fresh - Clear(); - - // store the vCard raw data - m_vCardData = vcard; - - // create format parser structures - SetFormat( b_vformat_new_from_string(vcard) ); - if( !Format() ) - throw ConvertError("resource error allocating vformat"); - - - // - // Parse the vcard data - // - - Barry::Contact &con = m_BarryContact; - con.SetIds(Barry::Contact::GetDefaultRecType(), RecordId); - - vAttr name = GetAttrObj("N"); - if( name.Get() ) { - // RFC 2426, 3.1.2 - con.LastName = name.GetValue(0); // Family Name - con.FirstName = name.GetValue(1); // Given Name - con.Prefix = name.GetValue(3); // Honorific Prefixes - } - - vAttr adr = GetAttrObj("ADR"); - for( int i = 0; adr.Get(); adr = GetAttrObj("ADR", ++i) ) - { - std::string type = adr.GetAllParams("TYPE"); - ToLower(type); - - // do not use "else" here, since TYPE can have multiple keys - if( strstr(type.c_str(), "work") ) - ParseAddress(adr, con.WorkAddress); - if( strstr(type.c_str(), "home") ) - ParseAddress(adr, con.HomeAddress); - } - - - // - // NOTE: - // Treat the following pairs of variables like - // sliding buffers, where higher priority values - // can push existings values from 1 to 2, or from - // 2 to oblivion: - // - // HomePhone + HomePhone2 - // WorkPhone + WorkPhone2 - // Phone + OtherPhone - // - SlidingPair HomePair(con.HomePhone, con.HomePhone2); - SlidingPair WorkPair(con.WorkPhone, con.WorkPhone2); - SlidingPair OtherPair(con.Phone, con.OtherPhone); - - // add all applicable phone numbers... there can be multiple - // TEL fields, even with the same TYPE value... therefore, the - // second TEL field with a TYPE=work, will be stored in WorkPhone2 - vAttr tel = GetAttrObj("TEL"); - for( int i = 0; tel.Get(); tel = GetAttrObj("TEL", ++i) ) - { - // grab all parameter values for this param name - std::string stype = tel.GetAllParams("TYPE"); - - // grab evolution-specific parameter... evolution is too - // lazy to sort its VCARD output, but instead it does - // its own non-standard tagging... so we try to - // accommodate it, so Work and Home phone numbers keep - // their order if possible - int evolutionSlot = atoi(tel.GetAllParams("X-EVOLUTION-UI-SLOT").c_str()); - if( evolutionSlot == 0 ) - evolutionSlot = SlidingPair::DefaultSlot; - - // turn to lower case for comparison - // FIXME - is this i18n safe? - ToLower(stype); - - // state - const char *type = stype.c_str(); - bool used = false; - - // Check for possible TYPE conflicts: - // pager can coexist with cell/pcs/car - // fax conflicts with cell/pcs/car - // fax conflicts with pager - bool mobile_type = strstr(type, "cell") || - strstr(type, "pcs") || - strstr(type, "car"); - bool fax_type = strstr(type, "fax"); - bool pager_type = strstr(type, "pager"); - if( fax_type && (mobile_type || pager_type) ) { - // conflict found, log and skip - trace.logf("ToBarry: skipping phone number due to TYPE conflict: fax cannot coexist with %s: %s", - mobile_type ? "cell/pcs/car" : "pager", - type); - continue; - } - - // If phone number has the "pref" flag - if( strstr(type, "pref") ) { - // Always use cell phone if the "pref" flag is set - if( strstr(type, "cell") ) { - used = OtherPair.assign(tel.GetValue(), type, evolutionSlot); - } - // Otherwise, the phone has to be "voice" type - else if( strstr(type, "voice") && con.Phone.size() == 0 ) { - used = OtherPair.assign(tel.GetValue(), type, evolutionSlot); - } - } - - // For each known phone type - // Fax : - if( strstr(type, "fax") && (strstr(type, "pref") || con.Fax.size() == 0) ) { - con.Fax = tel.GetValue(); - used = true; - } - // Mobile phone : - else if( mobile_type && (strstr(type, "pref") || con.MobilePhone.size() == 0) ) { - con.MobilePhone = tel.GetValue(); - used = true; - } - // Pager : - else if( strstr(type, "pager") && (strstr(type, "pref") || con.Pager.size() == 0) ) { - con.Pager = tel.GetValue(); - used = true; - } - // Check for any TEL-ignore types, and use other phone field if possible - // bbs/video/modem entire TEL ignored by Barry - // isdn entire TEL ignored by Barry - else if( strstr(type, "bbs") || strstr(type, "video") || strstr(type, "modem") ) { - } - else if( strstr(type, "isdn") ) { - } - // Voice telephone : - else { - if( strstr(type, "work") ) { - used = WorkPair.assign(tel.GetValue(), type, evolutionSlot); - } - - if( strstr(type, "home") ) { - used = HomePair.assign(tel.GetValue(), type, evolutionSlot); - } - } - - // if this value has not been claimed by any of the - // cases above, claim it now as "OtherPhone" - if( !used && con.OtherPhone.size() == 0 ) { - OtherPair.assign(tel.GetValue(), type, evolutionSlot); - } - } - - // scan for all email addresses... append addresses to the - // list by default, but prepend if its type is set to "pref" - // i.e. we want the preferred email address first - vAttr email = GetAttrObj("EMAIL"); - for( int i = 0; email.Get(); email = GetAttrObj("EMAIL", ++i) ) - { - std::string type = email.GetAllParams("TYPE"); - ToLower(type); - - bool of_interest = (i == 0 || strstr(type.c_str(), "pref")); - bool x400 = strstr(type.c_str(), "x400"); - - if( of_interest && !x400 ) { - con.EmailAddresses.insert(con.EmailAddresses.begin(), email.GetValue()); - } - else { - con.EmailAddresses.push_back( email.GetValue() ); - } - } - - // figure out which company title we want to keep... - // favour the TITLE field, but if it's empty, use ROLE - con.JobTitle = GetAttr("TITLE"); - if( !con.JobTitle.size() ) - con.JobTitle = GetAttr("ROLE"); - - con.Company = GetAttr("ORG"); - con.Notes = GetAttr("NOTE"); - con.URL = GetAttr("URL"); - if( GetAttr("BDAY").size() && !con.Birthday.FromYYYYMMDD( GetAttr("BDAY") ) ) - throw ConvertError("Unable to parse BDAY field"); - - // Photo vCard ? - vAttr photo = GetAttrObj("PHOTO"); - if (photo.Get()) { - std::string sencoding = photo.GetAllParams("ENCODING"); - - ToLower(sencoding); - - const char *encoding = sencoding.c_str(); - - if (strstr(encoding, "quoted-printable")) { - photo.Get()->encoding = VF_ENCODING_QP; - - con.Image = photo.GetDecodedValue(); - } - else if (strstr(encoding, "b")) { - photo.Get()->encoding = VF_ENCODING_BASE64; - - con.Image = photo.GetDecodedValue(); - } - // Else - // We ignore the photo, I don't know decoded ! - } - - vAttr cat = GetAttrObj("CATEGORIES"); - if( cat.Get() ) - ParseCategories(cat, con.Categories); - - // Last sanity check: Blackberry requires that at least - // name or Company has data. - if( !con.GetFullName().size() && !con.Company.size() ) - throw ConvertError("FN and ORG fields both blank in VCARD data"); - - return m_BarryContact; -} - -// Transfers ownership of m_gCardData to the caller. -char* vCard::ExtractVCard() -{ - char *ret = m_gCardData; - m_gCardData = 0; - return ret; -} - -void vCard::Clear() -{ - vBase::Clear(); - m_vCardData.clear(); - m_BarryContact.Clear(); - - if( m_gCardData ) { - g_free(m_gCardData); - m_gCardData = 0; - } -} - - - -////////////////////////////////////////////////////////////////////////////// -// - -VCardConverter::VCardConverter() - : m_Data(0) -{ -} - -VCardConverter::VCardConverter(uint32_t newRecordId) - : m_Data(0), - m_RecordId(newRecordId) -{ -} - -VCardConverter::~VCardConverter() -{ - if( m_Data ) - g_free(m_Data); -} - -// Transfers ownership of m_Data to the caller -char* VCardConverter::ExtractData() -{ - Trace trace("VCardConverter::ExtractData"); - char *ret = m_Data; - m_Data = 0; - return ret; -} - -bool VCardConverter::ParseData(const char *data) -{ - Trace trace("VCardConverter::ParseData"); - - try { - - vCard vcard; - m_Contact = vcard.ToBarry(data, m_RecordId); - - } - catch( vCard::ConvertError &ce ) { - trace.logf("ERROR: vCard::ConvertError exception: %s", ce.what()); - return false; - } - - return true; -} - -// Barry storage operator -void VCardConverter::operator()(const Barry::Contact &rec) -{ - Trace trace("VCardConverter::operator()"); - - // Delete data if some already exists - if( m_Data ) { - g_free(m_Data); - m_Data = 0; - } - - try { - - vCard vcard; - vcard.ToVCard(rec); - m_Data = vcard.ExtractVCard(); - - } - catch( vCard::ConvertError &ce ) { - trace.logf("ERROR: vCard::ConvertError exception: %s", ce.what()); - } -} - -// Barry builder operator -bool VCardConverter::operator()(Barry::Contact &rec, unsigned int dbId) -{ - Trace trace("VCardConverter::builder operator()"); - - rec = m_Contact; - return true; -} - -// Handles calling of the Barry::Controller to fetch a specific -// record, indicated by index (into the RecordStateTable). -// Returns a g_malloc'd string of data containing the vcard30 -// data. It is the responsibility of the caller to free it. -// This is intended to be passed into the GetChanges() function. -char* VCardConverter::GetRecordData(BarryEnvironment *env, unsigned int dbId, - Barry::RecordStateTable::IndexType index) -{ - Trace trace("VCardConverter::GetRecordData()"); - - using namespace Barry; - - VCardConverter contact2vcard; - RecordParser parser(contact2vcard); - env->m_pDesktop->GetRecord(dbId, index, parser); - return contact2vcard.ExtractData(); -} - -bool VCardConverter::CommitRecordData(BarryEnvironment *env, unsigned int dbId, - Barry::RecordStateTable::IndexType StateIndex, uint32_t recordId, - const char *data, bool add, std::string &errmsg) -{ - Trace trace("VCardConverter::CommitRecordData()"); - - uint32_t newRecordId; - if( add ) { - // use given id if possible - if( recordId && !env->m_ContactsSync.m_Table.GetIndex(recordId) ) { - // recordId is unique and non-zero - newRecordId = recordId; - } - else { - trace.log("Can't use recommended recordId, generating new one."); - newRecordId = env->m_ContactsSync.m_Table.MakeNewRecordId(); - } - } - else { - newRecordId = env->m_ContactsSync.m_Table.StateMap[StateIndex].RecordId; - } - trace.logf("newRecordId: %u", newRecordId); - - VCardConverter convert(newRecordId); - if( !convert.ParseData(data) ) { - std::ostringstream oss; - oss << "unable to parse change data for new RecordId: " - << newRecordId << " data: " << data; - errmsg = oss.str(); - trace.log(errmsg.c_str()); - return false; - } - - Barry::RecordBuilder builder(convert); - - if( add ) { - trace.log("adding record"); - env->m_pDesktop->AddRecord(dbId, builder); - } - else { - trace.log("setting record"); - env->m_pDesktop->SetRecord(dbId, StateIndex, builder); - trace.log("clearing dirty flag"); - env->m_pDesktop->ClearDirty(dbId, StateIndex); - } - - return true; -} - +/// +/// \file vcard.cc +/// Conversion routines for vcards +/// + +/* + Copyright (C) 2006-2010, Net Direct Inc. (http://www.netdirect.ca/) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License in the COPYING file at the + root directory of this project for more details. +*/ + +#include "vcard.h" +#include "environment.h" +#include "trace.h" +#include +#include +#include +#include +#include + +using namespace Barry::Sync; + +////////////////////////////////////////////////////////////////////////////// +// + +VCardConverter::VCardConverter() + : m_Data(0) +{ +} + +VCardConverter::VCardConverter(uint32_t newRecordId) + : m_Data(0), + m_RecordId(newRecordId) +{ +} + +VCardConverter::~VCardConverter() +{ + if( m_Data ) + g_free(m_Data); +} + +// Transfers ownership of m_Data to the caller +char* VCardConverter::ExtractData() +{ + Trace trace("VCardConverter::ExtractData"); + char *ret = m_Data; + m_Data = 0; + return ret; +} + +bool VCardConverter::ParseData(const char *data) +{ + Trace trace("VCardConverter::ParseData"); + + try { + + vCard vcard; + m_Contact = vcard.ToBarry(data, m_RecordId); + + } + catch( Barry::ConvertError &ce ) { + trace.logf("ERROR: vcard:Barry::ConvertError exception: %s", ce.what()); + return false; + } + + return true; +} + +// Barry storage operator +void VCardConverter::operator()(const Barry::Contact &rec) +{ + Trace trace("VCardConverter::operator()"); + + // Delete data if some already exists + if( m_Data ) { + g_free(m_Data); + m_Data = 0; + } + + try { + + vCard vcard; + vcard.ToVCard(rec); + m_Data = vcard.ExtractVCard(); + + } + catch( Barry::ConvertError &ce ) { + trace.logf("ERROR: vcard:Barry::ConvertError exception: %s", ce.what()); + } +} + +// Barry builder operator +bool VCardConverter::operator()(Barry::Contact &rec, unsigned int dbId) +{ + Trace trace("VCardConverter::builder operator()"); + + rec = m_Contact; + return true; +} + +// Handles calling of the Barry::Controller to fetch a specific +// record, indicated by index (into the RecordStateTable). +// Returns a g_malloc'd string of data containing the vcard30 +// data. It is the responsibility of the caller to free it. +// This is intended to be passed into the GetChanges() function. +char* VCardConverter::GetRecordData(BarryEnvironment *env, unsigned int dbId, + Barry::RecordStateTable::IndexType index) +{ + Trace trace("VCardConverter::GetRecordData()"); + + using namespace Barry; + + VCardConverter contact2vcard; + RecordParser parser(contact2vcard); + env->m_pDesktop->GetRecord(dbId, index, parser); + return contact2vcard.ExtractData(); +} + +bool VCardConverter::CommitRecordData(BarryEnvironment *env, unsigned int dbId, + Barry::RecordStateTable::IndexType StateIndex, uint32_t recordId, + const char *data, bool add, std::string &errmsg) +{ + Trace trace("VCardConverter::CommitRecordData()"); + + uint32_t newRecordId; + if( add ) { + // use given id if possible + if( recordId && !env->m_ContactsSync.m_Table.GetIndex(recordId) ) { + // recordId is unique and non-zero + newRecordId = recordId; + } + else { + trace.log("Can't use recommended recordId, generating new one."); + newRecordId = env->m_ContactsSync.m_Table.MakeNewRecordId(); + } + } + else { + newRecordId = env->m_ContactsSync.m_Table.StateMap[StateIndex].RecordId; + } + trace.logf("newRecordId: %u", newRecordId); + + VCardConverter convert(newRecordId); + if( !convert.ParseData(data) ) { + std::ostringstream oss; + oss << "unable to parse change data for new RecordId: " + << newRecordId << " data: " << data; + errmsg = oss.str(); + trace.log(errmsg.c_str()); + return false; + } + + Barry::RecordBuilder builder(convert); + + if( add ) { + trace.log("adding record"); + env->m_pDesktop->AddRecord(dbId, builder); + } + else { + trace.log("setting record"); + env->m_pDesktop->SetRecord(dbId, StateIndex, builder); + trace.log("clearing dirty flag"); + env->m_pDesktop->ClearDirty(dbId, StateIndex); + } + + return true; +} + diff --git a/opensync-plugin-0.4x/src/vcard.h b/opensync-plugin-0.4x/src/vcard.h index 6ccf8acf..af12504c 100644 --- a/opensync-plugin-0.4x/src/vcard.h +++ b/opensync-plugin-0.4x/src/vcard.h @@ -19,59 +19,17 @@ root directory of this project for more details. */ -#ifndef __BARRY_SYNC_VCARD_H__ -#define __BARRY_SYNC_VCARD_H__ +#ifndef __BARRYSYNC_VCARD_H__ +#define __BARRYSYNC_VCARD_H__ #include +#include #include #include -#include "vbase.h" -#include "vformat.h" // forward declarations class BarryEnvironment; - -// -// vCard -// -/// Class for converting between RFC 2425/2426 vCard data format, -/// and the Barry::Contact class. -/// -class vCard : public vBase -{ - // data to pass to external requests - char *m_gCardData; // dynamic memory returned by vformat()... can - // be used directly by the plugin, without - // overmuch allocation and freeing (see Extract()) - std::string m_vCardData;// copy of m_gCardData, for C++ use - Barry::Contact m_BarryContact; - -protected: - void AddAddress(const char *rfc_type, const Barry::PostalAddress &addr); - void AddCategories(const Barry::CategoryList &categories); - void AddPhoneCond(const std::string &phone); - void AddPhoneCond(const char *rfc_type, const std::string &phone); - - void ParseAddress(vAttr &adr, Barry::PostalAddress &address); - void ParseCategories(vAttr &cat, Barry::CategoryList &cats); - -public: - vCard(); - ~vCard(); - - const std::string& ToVCard(const Barry::Contact &con); - const Barry::Contact& ToBarry(const char *vcal, uint32_t RecordId); - - const std::string& GetVCard() const { return m_vCardData; } - const Barry::Contact& GetBarryContact() const { return m_BarryContact; } - - char* ExtractVCard(); - - void Clear(); -}; - - class VCardConverter { char *m_Data; @@ -111,6 +69,5 @@ public: const char *data, bool add, std::string &errmsg); }; - #endif diff --git a/opensync-plugin-0.4x/src/vevent.cc b/opensync-plugin-0.4x/src/vevent.cc dissimilarity index 78% index 25fa2217..749daebd 100644 --- a/opensync-plugin-0.4x/src/vevent.cc +++ b/opensync-plugin-0.4x/src/vevent.cc @@ -1,849 +1,210 @@ -// -// \file vevent.cc -// Conversion routines for vevents (VCALENDAR, etc) -// - -/* - Copyright (C) 2006-2010, Net Direct Inc. (http://www.netdirect.ca/) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License in the COPYING file at the - root directory of this project for more details. -*/ - -#include -#include - -#include "vevent.h" -#include "environment.h" -#include "trace.h" -#include "tosserror.h" -#include "vformat.h" // comes from opensync, but not a public header yet -#include -#include -#include -#include -#include - - -////////////////////////////////////////////////////////////////////////////// -// vCalendar - -vCalendar::vCalendar() - : m_gCalData(0) -{ -} - -vCalendar::~vCalendar() -{ - if( m_gCalData ) { - g_free(m_gCalData); - } -} - -const char *vCalendar::WeekDays[] = { "SU", "MO", "TU", "WE", "TH", "FR", "SA" }; - -unsigned short vCalendar::GetWeekDayIndex(const char *dayname) -{ - for( int i = 0; i < 7; i++ ) { - if( strcasecmp(dayname, WeekDays[i]) == 0 ) - return i; - } - return 0; -} - -unsigned short vCalendar::GetMonthWeekNumFromBYDAY(const std::string& ByDay) -{ - return atoi(ByDay.substr(0,ByDay.length()-2).c_str()); -} - -unsigned short vCalendar::GetWeekDayIndexFromBYDAY(const std::string& ByDay) -{ - return GetWeekDayIndex(ByDay.substr(ByDay.length()-2).c_str()); -} - - -bool vCalendar::HasMultipleVEvents() const -{ - int count = 0; - b_VFormat *format = const_cast(Format()); - GList *attrs = format ? b_vformat_get_attributes(format) : 0; - for( ; attrs; attrs = attrs->next ) { - b_VFormatAttribute *attr = (b_VFormatAttribute*) attrs->data; - if( strcasecmp(b_vformat_attribute_get_name(attr), "BEGIN") == 0 && - strcasecmp(b_vformat_attribute_get_nth_value(attr, 0), "VEVENT") == 0 ) - { - count++; - } - } - return count > 1; -} - -void vCalendar::RecurToVCal() -{ - using namespace Barry; - using namespace std; - Barry::Calendar &cal = m_BarryCal; - Trace trace("vCalendar::RecurToVCal"); - - if( !cal.Recurring ) - return; - - vAttrPtr attr = NewAttr("RRULE"); - - switch( cal.RecurringType ) - { - case Calendar::Day: // eg. every day - //AddParam(attr, "FREQ", "DAILY"); - AddValue(attr,"FREQ=DAILY"); - break; - - case Calendar::MonthByDate: // eg. every month on the 12th - // see: DayOfMonth - //AddParam(attr, "FREQ", "MONTHLY"); - AddValue(attr,"FREQ=MONTHLY"); - { - ostringstream oss; - oss << "BYMONTHDAY=" << cal.DayOfMonth; - AddValue(attr, oss.str().c_str()); - } - break; - - case Calendar::MonthByDay: // eg. every month on 3rd Wed - // see: DayOfWeek and WeekOfMonth - AddValue(attr, "FREQ=MONTHLY"); - if( cal.DayOfWeek <= 6 ) { // DayOfWeek is unsigned - ostringstream oss; - oss << "BYDAY=" << cal.WeekOfMonth << WeekDays[cal.DayOfWeek]; - AddValue(attr, oss.str().c_str()); - } - break; - - case Calendar::YearByDate: // eg. every year on March 5 - // see: DayOfMonth and MonthOfYear - AddValue(attr, "FREQ=YEARLY"); - { - ostringstream oss; - oss << "BYMONTH=" << cal.MonthOfYear; - AddValue(attr, oss.str().c_str()); - - } - { - ostringstream oss; - oss << "BYMONTHDAY=" << cal.DayOfMonth; - AddValue(attr, oss.str().c_str()); - } - break; - - case Calendar::YearByDay: // eg. every year on 3rd Wed of Jan - // see: DayOfWeek, WeekOfMonth, and - // MonthOfYear - AddValue(attr, "FREQ=YEARLY"); - if( cal.DayOfWeek <= 6 ) { // DayOfWeek is unsigned - ostringstream oss; - oss << "BYDAY=" << cal.WeekOfMonth << WeekDays[cal.DayOfWeek]; - AddValue(attr, oss.str().c_str()); - - oss.str(""); - oss << "BYMONTH=" << cal.MonthOfYear; - AddValue(attr, oss.str().c_str()); - } - break; - - case Calendar::Week: // eg. every week on Mon and Fri - // see: WeekDays - AddValue(attr, "FREQ=WEEKLY"); - { - ostringstream oss; - oss << "BYDAY="; - for( int i = 0, bm = 1, cnt = 0; i < 7; i++, bm <<= 1 ) { - if( cal.WeekDays & bm ) { - if( cnt ) - oss << ","; - oss << WeekDays[i]; - cnt++; - } - } - AddValue(attr, oss.str().c_str()); - } - break; - - default: - throw ConvertError("Unknown RecurringType in Barry Calendar object"); - } - - // add some common parameters - if( cal.Interval > 1 ) { - ostringstream oss; - oss << "INTERVAL=" << cal.Interval; - AddValue(attr, oss.str().c_str()); - } - if( !cal.Perpetual ) { - TossError te("RecurToVCal, RecurringEndTime 2 vtime", trace); - gStringPtr rend(osync_time_unix2vtime(&cal.RecurringEndTime, te)); - if( !te.IsSet() ) { - ostringstream oss; - oss << "UNTIL=" << rend.Get(); - AddValue(attr, oss.str().c_str()); - } - } - - AddAttr(attr); - -/* - bool AllDayEvent; - - /// - /// Recurring data - /// - /// Note: interval can be used on all of these recurring types to - /// make it happen "every other time" or more, etc. - /// - - bool Recurring; - RecurringCodeType RecurringType; - unsigned short Interval; // must be >= 1 - time_t RecurringEndTime; // only pertains if Recurring is true - // sets the date and time when - // recurrence of this appointment - // should no longer occur - // If a perpetual appointment, this - // is 0xFFFFFFFF in the low level data - // Instead, set the following flag. - bool Perpetual; // if true, this will always recur - unsigned short TimeZoneCode; // the time zone originally used - // for the recurrence data... - // seems to have little use, but - // set to your current time zone - // as a good default - - unsigned short // recurring details, depending on type - DayOfWeek, // 0-6 - WeekOfMonth, // 1-5 - DayOfMonth, // 1-31 - MonthOfYear; // 1-12 - unsigned char WeekDays; // bitmask, bit 0 = sunday - - #define CAL_WD_SUN 0x01 - #define CAL_WD_MON 0x02 - #define CAL_WD_TUE 0x04 - #define CAL_WD_WED 0x08 - #define CAL_WD_THU 0x10 - #define CAL_WD_FRI 0x20 - #define CAL_WD_SAT 0x40 - -*/ - -} - -void vCalendar::RecurToBarryCal(vAttr& rrule, time_t starttime) -{ - using namespace Barry; - using namespace std; - Barry::Calendar &cal = m_BarryCal; - Trace trace("vCalendar::RecurToBarryCal"); - std::map pmap; - pmap["SU"] = CAL_WD_SUN; - pmap["MO"] = CAL_WD_MON; - pmap["TU"] = CAL_WD_TUE; - pmap["WE"] = CAL_WD_WED; - pmap["TH"] = CAL_WD_THU; - pmap["FR"] = CAL_WD_FRI; - pmap["SA"] = CAL_WD_SAT; - - - int i=0; - unsigned int count=0; - string val; - std::map args; - do { - val=rrule.GetValue(i++); - if(val.length()==0) { - break; - } - string n=val.substr(0,val.find("=")); - string v=val.substr(val.find("=")+1); - args[n]=v; - trace.logf("RecurToBarryCal: |%s|%s|",n.c_str(),v.c_str()); - } while(1); - - // now process the interval. - - time_t now = time(NULL); - int zoneoffset; - - { - TossError te; - zoneoffset = osync_time_timezone_diff(localtime(&now), te); - if( te.IsSet() ) { - trace.log("RecurToBarryCal: Cannot find local timezone diff... assuming 0"); - zoneoffset = 0; - } - } - - cal.Recurring=TRUE; - - if(args.find(string("INTERVAL"))!=args.end()) { - cal.Interval = atoi(args["INTERVAL"].c_str()); - } - if(args.find(string("UNTIL"))!=args.end()) { - TossError te; - cal.Perpetual = FALSE; - cal.RecurringEndTime=osync_time_vtime2unix(args["UNTIL"].c_str(), zoneoffset, te); - if( te.IsSet() ) { - trace.logf("osync_time_vtime2unix() failed: UNTIL = %s, zoneoffset = %d", args["UNTIL"].c_str(), zoneoffset); - } - } else { - // if we do not also have COUNT, then we must be forerver - if(args.find(string("COUNT"))==args.end()) { - cal.Perpetual=TRUE; - } else { - // we do have COUNT. This means we won't have UNTIL. - // So we need to process the RecurringEndTime from - // the current start date. Set the count level to - // something other than zero to indicate we need - // to process it as the exact end date will - // depend upon the frequency. - count=atoi(args["COUNT"].c_str()); - if( count == 0 ) { - throw std::runtime_error("Invalid COUNT in recurring rule: " + args["COUNT"]); - } - } - } - - // we need these if COUNT is true, or if we are a yearly job. - - // TO-DO: we must process COUNT in terms of an end date if we have it. - - // Now deal with the freq - - if(args.find(string("FREQ"))==args.end()) { - trace.logf("RecurToBarryCal: No frequency specified!"); - return; - } - - if(args["FREQ"]==string("DAILY")) { - cal.RecurringType=Calendar::Day; - - } else if(args["FREQ"]==string("WEEKLY")) { - cal.RecurringType=Calendar::Week; - // we must have a dayofweek entry - if(args.find(string("BYDAY"))!=args.end()) { - std::vector v=Tokenize(args["BYDAY"]); - // iterate along our vector and convert - for(unsigned int idx=0;idx28 and the resulting - // month falls on a February. We don't need - // to worry about day of week as mktime() - // clobbers it. - datestruct.tm_year += (datestruct.tm_mon+count)/12; - datestruct.tm_mon = (datestruct.tm_mon+count)%12; - if(datestruct.tm_mday>28 && datestruct.tm_mon==1) { - // force it to 1st Mar - // TODO Potential bug on leap years - datestruct.tm_mon=2; - datestruct.tm_mday=1; - } - if(datestruct.tm_mday==31 && (datestruct.tm_mon==8 || - datestruct.tm_mon==3 || - datestruct.tm_mon==5 || - datestruct.tm_mon==10)) { - datestruct.tm_mon+=1; - datestruct.tm_mday=1; - } - cal.RecurringEndTime=mktime(&datestruct); - } - } else if(args["FREQ"]=="YEARLY") { - if(args.find(string("BYMONTH"))!=args.end()) { - cal.MonthOfYear=atoi(args["BYMONTH"].c_str()); - if(args.find(string("BYMONTHDAY"))!=args.end()) { - cal.RecurringType=Calendar::YearByDate; - cal.DayOfMonth=atoi(args["BYMONTHDAY"].c_str()); - } else { - if(args.find(string("BYDAY"))!=args.end()) { - cal.RecurringType=Calendar::YearByDay; - cal.WeekOfMonth=GetMonthWeekNumFromBYDAY(args["BYDAY"]); - cal.DayOfWeek=GetWeekDayIndexFromBYDAY(args["BYDAY"]); - } else { - trace.logf("RecurToBarryCal: No qualifier on YEARLY freq"); - } - } - } else { - // otherwise use the start date and translate - // to a BYMONTHDAY. - // cal.StartTime has already been processed - // when we get here we need month of year, - // and day of month. - struct tm datestruct; - localtime_r(&starttime,&datestruct); - cal.RecurringType=Calendar::YearByDate; - cal.MonthOfYear=datestruct.tm_mon; - cal.DayOfMonth=datestruct.tm_mday; - } - if(count) { - // convert to struct tm, then simply add to the year. - struct tm datestruct; - localtime_r(&starttime,&datestruct); - datestruct.tm_year += count; - cal.RecurringEndTime=mktime(&datestruct); - } - } - -// unsigned char WeekDays; // bitmask, bit 0 = sunday -// -// #define CAL_WD_SUN 0x01 -// #define CAL_WD_MON 0x02 -// #define CAL_WD_TUE 0x04 -// #define CAL_WD_WED 0x08 -// #define CAL_WD_THU 0x10 -// #define CAL_WD_FRI 0x20 -// #define CAL_WD_SAT 0x40 -} - -// Main conversion routine for converting from Barry::Calendar to -// a vCalendar string of data. -const std::string& vCalendar::ToVCal(const Barry::Calendar &cal) -{ - Trace trace("vCalendar::ToVCal"); - std::ostringstream oss; - cal.Dump(oss); - trace.logf("ToVCal, initial Barry record: %s", oss.str().c_str()); - - // start fresh - Clear(); - SetFormat( b_vformat_new() ); - if( !Format() ) - throw ConvertError("resource error allocating vformat"); - - // store the Barry object we're working with - m_BarryCal = cal; - - // begin building vCalendar data - AddAttr(NewAttr("PRODID", "-//OpenSync//NONSGML Barry Calendar Record//EN")); - AddAttr(NewAttr("BEGIN", "VEVENT")); - AddAttr(NewAttr("SEQUENCE", "0")); - AddAttr(NewAttr("SUMMARY", cal.Subject.c_str())); - AddAttr(NewAttr("DESCRIPTION", cal.Notes.c_str())); - AddAttr(NewAttr("LOCATION", cal.Location.c_str())); - - // grab Start, End, and Notification time strings (with error checking) - TossError te; - gStringPtr start(osync_time_unix2vtime(&cal.StartTime, te)); - if( te.IsSet() ) - trace.logf("osync_time_unix2vtime failed: StartTime = %ld", (long)cal.StartTime); - - te.Clear(); - gStringPtr end(osync_time_unix2vtime(&cal.EndTime, te)); - if( te.IsSet() ) - trace.logf("osync_time_unix2vtime failed: EndTime = %ld", (long)cal.EndTime); - - te.Clear(); - gStringPtr notify(osync_time_unix2vtime(&cal.NotificationTime, te)); - if( te.IsSet() ) - trace.logf("osync_time_unix2vtime failed: NotificationTime = %ld", (long)cal.NotificationTime); - - AddAttr(NewAttr("DTSTART", start.Get())); - AddAttr(NewAttr("DTEND", end.Get())); - // FIXME - add a truly globally unique "UID" string? - - - AddAttr(NewAttr("BEGIN", "VALARM")); - AddAttr(NewAttr("ACTION", "AUDIO")); - - // notify must be UTC, when specified in DATE-TIME - vAttrPtr trigger = NewAttr("TRIGGER", notify.Get()); - AddParam(trigger, "VALUE", "DATE-TIME"); - AddAttr(trigger); - - AddAttr(NewAttr("END", "VALARM")); - - - if( cal.Recurring ) { - RecurToVCal(); - } - - AddAttr(NewAttr("END", "VEVENT")); - - // generate the raw VCALENDAR data - m_gCalData = b_vformat_to_string(Format(), VFORMAT_EVENT_20); - m_vCalData = m_gCalData; - - trace.logf("ToVCal, resulting vcal data: %s", m_vCalData.c_str()); - return m_vCalData; -} - -// Main conversion routine for converting from vCalendar data string -// to a Barry::Calendar object. -const Barry::Calendar& vCalendar::ToBarry(const char *vcal, uint32_t RecordId) -{ - using namespace std; - - Trace trace("vCalendar::ToBarry"); - trace.logf("ToBarry, working on vcal data: %s", vcal); - - // we only handle vCalendar data with one vevent block - if( HasMultipleVEvents() ) - throw ConvertError("vCalendar data contains more than one VEVENT block, unsupported"); - - // start fresh - Clear(); - - // store the vCalendar raw data - m_vCalData = vcal; - - // create format parser structures - SetFormat( b_vformat_new_from_string(vcal) ); - if( !Format() ) - throw ConvertError("resource error allocating vformat"); - - string start = GetAttr("DTSTART", "/vevent"); - trace.logf("DTSTART attr retrieved: %s", start.c_str()); - string end = GetAttr("DTEND", "/vevent"); - trace.logf("DTEND attr retrieved: %s", end.c_str()); - string subject = GetAttr("SUMMARY", "/vevent"); - trace.logf("SUMMARY attr retrieved: %s", subject.c_str()); - if( subject.size() == 0 ) { - subject = ""; - trace.logf("ERROR: bad data, blank SUMMARY: %s", vcal); - } - vAttr trigger_obj = GetAttrObj("TRIGGER", 0, "/valarm"); - - string location = GetAttr("LOCATION", "/vevent"); - trace.logf("LOCATION attr retrieved: %s", location.c_str()); - - string notes = GetAttr("DESCRIPTION", "/vevent"); - trace.logf("DESCRIPTION attr retrieved: %s", notes.c_str()); - - vAttr rrule = GetAttrObj("RRULE",0,"/vevent"); - - - // - // Now, run checks and convert into Barry object - // - - - // FIXME - we are assuming that any non-UTC timestamps - // in the vcalendar record will be in the current timezone... - // This is wrong! fix this later. - // - // Also, we current ignore any time zone - // parameters that might be in the vcalendar format... this - // must be fixed. - // - time_t now = time(NULL); - int zoneoffset; - { - TossError te; - zoneoffset = osync_time_timezone_diff(localtime(&now), te); - if( te.IsSet() ) { - trace.log("ToBarry: Cannot find local timezone diff... assuming 0"); - zoneoffset = 0; - } - } - - Barry::Calendar &rec = m_BarryCal; - rec.SetIds(Barry::Calendar::GetDefaultRecType(), RecordId); - - if( !start.size() ) - throw ConvertError("Blank DTSTART"); - TossError te("ToBarry, StartTime 2 unix", trace); - rec.StartTime = osync_time_vtime2unix(start.c_str(), zoneoffset, te); - - if( !end.size() ) { - // DTEND is actually optional! According to the - // RFC, a DTSTART with no DTEND should be treated - // like a "special day" like an anniversary, which occupies - // no time. - // - // Since the Blackberry doesn't really map well to this - // case, we'll set the end time to 1 day past start. - // - rec.EndTime = rec.StartTime + 24 * 60 * 60; - } - else { - TossError te("ToBarry, EndTime 2 unix", trace); - rec.EndTime = osync_time_vtime2unix(end.c_str(), zoneoffset, te); - } - - rec.Subject = subject; - rec.Location = location; - rec.Notes = notes; - - if(rrule.Get()) { - RecurToBarryCal(rrule, rec.StartTime); - } - - // convert trigger time into notification time - // assume no notification, by default - rec.NotificationTime = 0; - if( trigger_obj.Get() ) { - string trigger_type = trigger_obj.GetParam("VALUE"); - string trigger = trigger_obj.GetValue(); - - if( trigger.size() == 0 ) { - trace.logf("ERROR: no TRIGGER found in calendar entry, assuming notification time as 15 minutes before start."); - } - else if( trigger_type == "DATE-TIME" ) { - TossError te("ToBarry, NotificationTime 2 unix", trace); - rec.NotificationTime = osync_time_vtime2unix(trigger.c_str(), zoneoffset, te); - } - else if( trigger_type == "DURATION" || trigger_type.size() == 0 ) { - // default is DURATION (RFC 4.8.6.3) - string related = trigger_obj.GetParam("RELATED"); - - // default to relative to start time - time_t *relative = &rec.StartTime; - if( related == "END" ) - relative = &rec.EndTime; - - rec.NotificationTime = *relative + osync_time_alarmdu2sec(trigger.c_str()); - } - else { - throw ConvertError("Unknown TRIGGER VALUE"); - } - } - else { - trace.logf("ERROR: no TRIGGER found in calendar entry, assuming notification time as 15 minutes before start."); - } - - std::ostringstream oss; - m_BarryCal.Dump(oss); - trace.logf("ToBarry, resulting Barry record: %s", oss.str().c_str()); - return m_BarryCal; -} - -// Transfers ownership of m_gCalData to the caller. -char* vCalendar::ExtractVCal() -{ - char *ret = m_gCalData; - m_gCalData = 0; - return ret; -} - -void vCalendar::Clear() -{ - vBase::Clear(); - m_vCalData.clear(); - m_BarryCal.Clear(); - - if( m_gCalData ) { - g_free(m_gCalData); - m_gCalData = 0; - } -} - - - -////////////////////////////////////////////////////////////////////////////// -// - -VEventConverter::VEventConverter() - : m_Data(0) -{ -} - -VEventConverter::VEventConverter(uint32_t newRecordId) - : m_Data(0), - m_RecordId(newRecordId) -{ -} - -VEventConverter::~VEventConverter() -{ - if( m_Data ) - g_free(m_Data); -} - -// Transfers ownership of m_Data to the caller -char* VEventConverter::ExtractData() -{ - Trace trace("VEventConverter::ExtractData"); - char *ret = m_Data; - m_Data = 0; - return ret; -} - -bool VEventConverter::ParseData(const char *data) -{ - Trace trace("VEventConverter::ParseData"); - - try { - - vCalendar vcal; - m_Cal = vcal.ToBarry(data, m_RecordId); - - } - catch( vCalendar::ConvertError &ce ) { - trace.logf("ERROR: vCalendar::ConvertError exception: %s", ce.what()); - return false; - } - - return true; -} - -bool VEventConverter::MergeData(const Barry::Calendar &origin) -{ - // Save CalendarID value - // CalendarID field is used to link an entry event to an account mail - if (origin.CalendarID != m_Cal.CalendarID) - m_Cal.CalendarID = origin.CalendarID; - - return true; -} - -// Barry storage operator -void VEventConverter::operator()(const Barry::Calendar &rec) -{ - Trace trace("VEventConverter::operator()"); - - // Delete data if some already exists - if( m_Data ) { - g_free(m_Data); - m_Data = 0; - } - - // Keep a trace of Calendar object (need to merge with the new event) - m_Cal = rec; - - try { - vCalendar vcal; - vcal.ToVCal(rec); - m_Data = vcal.ExtractVCal(); - - } - catch( vCalendar::ConvertError &ce ) { - trace.logf("ERROR: vCalendar::ConvertError exception: %s", ce.what()); - } -} - -// Barry builder operator -bool VEventConverter::operator()(Barry::Calendar &rec, unsigned int dbId) -{ - Trace trace("VEventConverter::builder operator()"); - - rec = m_Cal; - return true; -} - -// Handles calling of the Barry::Controller to fetch a specific -// record, indicated by index (into the RecordStateTable). -// Returns a g_malloc'd string of data containing the vevent20 -// data. It is the responsibility of the caller to free it. -// This is intended to be passed into the GetChanges() function. -char* VEventConverter::GetRecordData(BarryEnvironment *env, unsigned int dbId, - Barry::RecordStateTable::IndexType index) -{ - Trace trace("VEventConverter::GetRecordData()"); - - using namespace Barry; - - VEventConverter cal2event; - RecordParser parser(cal2event); - env->m_pDesktop->GetRecord(dbId, index, parser); - return cal2event.ExtractData(); -} - -bool VEventConverter::CommitRecordData(BarryEnvironment *env, unsigned int dbId, - Barry::RecordStateTable::IndexType StateIndex, uint32_t recordId, - const char *data, bool add, std::string &errmsg) -{ - Trace trace("VEventConverter::CommitRecordData()"); - - uint32_t newRecordId; - if( add ) { - // use given id if possible - if( recordId && !env->m_CalendarSync.m_Table.GetIndex(recordId) ) { - // recordId is unique and non-zero - newRecordId = recordId; - } - else { - trace.log("Can't use recommended recordId, generating new one."); - newRecordId = env->m_CalendarSync.m_Table.MakeNewRecordId(); - } - } - else { - newRecordId = env->m_CalendarSync.m_Table.StateMap[StateIndex].RecordId; - } - trace.logf("newRecordId: %u", newRecordId); - - VEventConverter convert(newRecordId); - if( !convert.ParseData(data) ) { - std::ostringstream oss; - oss << "unable to parse change data for new RecordId: " - << newRecordId << " data: " << data; - errmsg = oss.str(); - trace.log(errmsg.c_str()); - return false; - } - - // If we modify a data, we read at first its current value - // then we merge with the parsed value from the other opensync member - // Merge function is important because, we have to save some BlackBerry fields. - // Fix an issue with the new OS release who supports several calendar. - if( !add ) { - using namespace Barry; - - VEventConverter cal2event; - RecordParser parser(cal2event); - env->m_pDesktop->GetRecord(dbId, StateIndex, parser); - - convert.MergeData(cal2event.GetCalendar()); - } - - Barry::RecordBuilder builder(convert); - - if( add ) { - trace.log("adding record"); - env->m_pDesktop->AddRecord(dbId, builder); - } - else { - trace.log("setting record"); - env->m_pDesktop->SetRecord(dbId, StateIndex, builder); - trace.log("clearing dirty flag"); - env->m_pDesktop->ClearDirty(dbId, StateIndex); - } - - return true; -} - +// +// \file vevent.cc +// Conversion routines for vevents (VCALENDAR, etc) +// + +/* + Copyright (C) 2006-2010, Net Direct Inc. (http://www.netdirect.ca/) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License in the COPYING file at the + root directory of this project for more details. +*/ + +#include +#include + +#include "vevent.h" +#include "environment.h" +#include "trace.h" +#include "tosserror.h" +#include +#include +#include +#include +#include + +using namespace Barry::Sync; + +////////////////////////////////////////////////////////////////////////////// +// + +VEventConverter::VEventConverter() + : m_Data(0) +{ +} + +VEventConverter::VEventConverter(uint32_t newRecordId) + : m_Data(0), + m_RecordId(newRecordId) +{ +} + +VEventConverter::~VEventConverter() +{ + if( m_Data ) + g_free(m_Data); +} + +// Transfers ownership of m_Data to the caller +char* VEventConverter::ExtractData() +{ + Trace trace("VEventConverter::ExtractData"); + char *ret = m_Data; + m_Data = 0; + return ret; +} + +bool VEventConverter::ParseData(const char *data) +{ + Trace trace("VEventConverter::ParseData"); + + try { + + vTimeConverter vtc; + vCalendar vcal(vtc); + m_Cal = vcal.ToBarry(data, m_RecordId); + + } + catch( Barry::ConvertError &ce ) { + trace.logf("ERROR: vevent:Barry::ConvertError exception: %s", ce.what()); + return false; + } + + return true; +} + +bool VEventConverter::MergeData(const Barry::Calendar &origin) +{ + // Save CalendarID value + // CalendarID field is used to link an entry event to an account mail + if (origin.CalendarID != m_Cal.CalendarID) + m_Cal.CalendarID = origin.CalendarID; + + return true; +} + +// Barry storage operator +void VEventConverter::operator()(const Barry::Calendar &rec) +{ + Trace trace("VEventConverter::operator()"); + + // Delete data if some already exists + if( m_Data ) { + g_free(m_Data); + m_Data = 0; + } + + // Keep a trace of Calendar object (need to merge with the new event) + m_Cal = rec; + + try { + vTimeConverter vtc; + vCalendar vcal(vtc); + vcal.ToVCal(rec); + m_Data = vcal.ExtractVCal(); + + } + catch( Barry::ConvertError &ce ) { + trace.logf("ERROR: vevent:Barry::ConvertError exception: %s", ce.what()); + } +} + +// Barry builder operator +bool VEventConverter::operator()(Barry::Calendar &rec, unsigned int dbId) +{ + Trace trace("VEventConverter::builder operator()"); + + rec = m_Cal; + return true; +} + +// Handles calling of the Barry::Controller to fetch a specific +// record, indicated by index (into the RecordStateTable). +// Returns a g_malloc'd string of data containing the vevent20 +// data. It is the responsibility of the caller to free it. +// This is intended to be passed into the GetChanges() function. +char* VEventConverter::GetRecordData(BarryEnvironment *env, unsigned int dbId, + Barry::RecordStateTable::IndexType index) +{ + Trace trace("VEventConverter::GetRecordData()"); + + using namespace Barry; + + VEventConverter cal2event; + RecordParser parser(cal2event); + env->m_pDesktop->GetRecord(dbId, index, parser); + return cal2event.ExtractData(); +} + +bool VEventConverter::CommitRecordData(BarryEnvironment *env, unsigned int dbId, + Barry::RecordStateTable::IndexType StateIndex, uint32_t recordId, + const char *data, bool add, std::string &errmsg) +{ + Trace trace("VEventConverter::CommitRecordData()"); + + uint32_t newRecordId; + if( add ) { + // use given id if possible + if( recordId && !env->m_CalendarSync.m_Table.GetIndex(recordId) ) { + // recordId is unique and non-zero + newRecordId = recordId; + } + else { + trace.log("Can't use recommended recordId, generating new one."); + newRecordId = env->m_CalendarSync.m_Table.MakeNewRecordId(); + } + } + else { + newRecordId = env->m_CalendarSync.m_Table.StateMap[StateIndex].RecordId; + } + trace.logf("newRecordId: %u", newRecordId); + + VEventConverter convert(newRecordId); + if( !convert.ParseData(data) ) { + std::ostringstream oss; + oss << "unable to parse change data for new RecordId: " + << newRecordId << " data: " << data; + errmsg = oss.str(); + trace.log(errmsg.c_str()); + return false; + } + + // If we modify a data, we read at first its current value + // then we merge with the parsed value from the other opensync member + // Merge function is important because, we have to save some BlackBerry fields. + // Fix an issue with the new OS release who supports several calendar. + if( !add ) { + using namespace Barry; + + VEventConverter cal2event; + RecordParser parser(cal2event); + env->m_pDesktop->GetRecord(dbId, StateIndex, parser); + + convert.MergeData(cal2event.GetCalendar()); + } + + Barry::RecordBuilder builder(convert); + + if( add ) { + trace.log("adding record"); + env->m_pDesktop->AddRecord(dbId, builder); + } + else { + trace.log("setting record"); + env->m_pDesktop->SetRecord(dbId, StateIndex, builder); + trace.log("clearing dirty flag"); + env->m_pDesktop->ClearDirty(dbId, StateIndex); + } + + return true; +} + diff --git a/opensync-plugin-0.4x/src/vevent.h b/opensync-plugin-0.4x/src/vevent.h index 1e54a4e9..4ad39a2c 100644 --- a/opensync-plugin-0.4x/src/vevent.h +++ b/opensync-plugin-0.4x/src/vevent.h @@ -19,61 +19,17 @@ root directory of this project for more details. */ -#ifndef __BARRY_SYNC_VEVENT_H__ -#define __BARRY_SYNC_VEVENT_H__ +#ifndef __BARRYSYNC_VEVENT_H__ +#define __BARRYSYNC_VEVENT_H__ #include +#include #include #include -#include "vbase.h" -#include "vformat.h" // forward declarations class BarryEnvironment; -// -// vCalendar -// -/// Class for converting between RFC 2445 iCalendar data format, -/// and the Barry::Calendar class. -/// -class vCalendar : public vBase -{ - // data to pass to external requests - char *m_gCalData; // dynamic memory returned by vformat()... can - // be used directly by the plugin, without - // overmuch allocation and freeing (see Extract()) - std::string m_vCalData; // copy of m_gCalData, for C++ use - Barry::Calendar m_BarryCal; - - static const char *WeekDays[7]; - - unsigned short GetMonthWeekNumFromBYDAY(const std::string& ByDay); - unsigned short GetWeekDayIndexFromBYDAY(const std::string& ByDay); - -protected: - void RecurToVCal(); - void RecurToBarryCal(vAttr& rrule, time_t starttime); - - static unsigned short GetWeekDayIndex(const char *dayname); - bool HasMultipleVEvents() const; - -public: - vCalendar(); - ~vCalendar(); - - const std::string& ToVCal(const Barry::Calendar &cal); - const Barry::Calendar& ToBarry(const char *vcal, uint32_t RecordId); - - const std::string& GetVCal() const { return m_vCalData; } - const Barry::Calendar& GetBarryCal() const { return m_BarryCal; } - - char* ExtractVCal(); - - void Clear(); -}; - - class VEventConverter { char *m_Data; @@ -118,6 +74,5 @@ public: const char *data, bool add, std::string &errmsg); }; - #endif diff --git a/opensync-plugin-0.4x/src/vformat.c b/opensync-plugin-0.4x/src/vformat.c deleted file mode 100644 index 43b125cb..00000000 --- a/opensync-plugin-0.4x/src/vformat.c +++ /dev/null @@ -1,2130 +0,0 @@ -/* - * Copyright (C) 2003 Ximian, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Author: Chris Toshok (toshok@ximian.com) - * Author: Armin Bauer (armin.bauer@opensync.org) - * - */ - -#include "vformat.h" - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include -#include -#include -#include - -static size_t base64_encode_step(const unsigned char *in, size_t len, gboolean break_lines, unsigned char *out, int *state, int *save); -static size_t base64_decode_step(const unsigned char *in, size_t len, unsigned char *out, int *state, unsigned int *save); -static size_t base64_decode_simple (char *data, size_t len); -static char *base64_encode_simple (const char *data, size_t len); - -static size_t quoted_decode_simple (char *data, size_t len); -static char *quoted_encode_simple (const unsigned char *string, int len); - - -/** - * _helper_is_base64 is helper function to check i a string is "b" or "base64" - * @param check_string string that should be compared with "b" or "base64" - * @return 0 if check_string is not base64 and 1 if it is - */ -static int _helper_is_base64(const char *check_string) -{ - if(!g_ascii_strcasecmp ((char *) check_string, "BASE64") || - !g_ascii_strcasecmp ((char *) check_string, "b") ) - return (1); - return (0); -} - -time_t b_vformat_time_to_unix(const char *inptime) -{ - char *date = NULL; - char *time = NULL; - char *ftime = NULL; - if ((ftime = g_strrstr(inptime, "T"))) { - - date = g_strndup(inptime, ftime - inptime); - if (ftime[3] == ':') - time = g_strndup(ftime + 1, 8); - else - time = g_strndup(ftime + 1, 6); - } else { - date = g_strdup(inptime); - } - - struct tm btime; - memset(&btime, 0, sizeof(struct tm)); - btime.tm_isdst = -1; - - if (strlen(date) == 10) { - btime.tm_year = date[0] * 1000 + date[1] * 100 + date[2] * 10 + date[3] - '0' * 1111 - 1900; - btime.tm_mon = date[5] * 10 + date[6] - '0' * 11 - 1; - btime.tm_mday = date[8] * 10 + date[9] - '0' * 11; - } else { - btime.tm_year = date[0] * 1000 + date[1] * 100 + date[2] * 10 + date[3] - '0' * 1111- 1900; - btime.tm_mon = date[4] * 10 + date[5] - '0' * 11 - 1; - btime.tm_mday = date[6] * 10 + date[7] - '0' * 11; - } - - if (time && strlen(time) == 8) { - //Time - btime.tm_hour = time[0] * 10 + time[1] - '0' * 11; - btime.tm_min = time[3] * 10 + time[4] - '0' * 11; - btime.tm_sec = time[6] * 10 + time[7] - '0' * 11; - } else if (time && strlen(time) == 6) { - btime.tm_hour = time[0] * 10 + time[1] - '0' * 11; - btime.tm_min = time[2] * 10 + time[3] - '0' * 11; - btime.tm_sec = time[4] * 10 + time[5] - '0' * 11; - } - - time_t utime = mktime(&btime); - return utime; -} - -static char *_fold_lines (char *buf) -{ - GString *str = g_string_new (""); - GString *line = g_string_new (""); - char *p = buf; - char *next, *next2, *q; - gboolean newline = TRUE; - gboolean quotedprintable = FALSE; - - /* - * We're pretty liberal with line folding here. We handle - * lines folded with \r\n, \n\r, \n, =\r\n and =\n\r. - * We also turn single \r's and \n's not followed by into \r\n's. - */ - - while (*p) { - - /* search new lines for quoted printable encoding */ - if (newline) { - for (q=p; *q != '\n' && *q != '\0'; q++) - line = g_string_append_unichar (line, g_utf8_get_char (q)); - - if (strstr(line->str, "ENCODING=QUOTED-PRINTABLE")) - quotedprintable = TRUE; - - g_string_free(line, TRUE); - line = g_string_new (""); - - newline = FALSE; - } - - - if ((quotedprintable && *p == '=') || *p == '\r' || *p == '\n') { - next = g_utf8_next_char (p); - if (*next == '\n' || *next == '\r') { - next2 = g_utf8_next_char (next); - if (*next2 == '\n' || *next2 == '\r' || *next2 == ' ' || *next2 == '\t') { - p = g_utf8_next_char (next2); - } - else { - str = g_string_append (str, CRLF); - p = g_utf8_next_char (next); - newline = TRUE; - quotedprintable = FALSE; - } - } - else if (*p == '=') { - str = g_string_append_unichar (str, g_utf8_get_char (p)); - p = g_utf8_next_char (p); - } - else if (*next == ' ' || *next == '\t') { - p = g_utf8_next_char (next); - } - else { - str = g_string_append (str, CRLF); - p = g_utf8_next_char (p); - newline = TRUE; - quotedprintable = FALSE; - } - } - else { - str = g_string_append_unichar (str, g_utf8_get_char (p)); - p = g_utf8_next_char (p); - } - } - - g_free (buf); - g_string_free(line, TRUE); - - return g_string_free (str, FALSE); -} - -/* skip forward until we hit the CRLF, or \0 */ -static void _skip_to_next_line (char **p) -{ - char *lp; - lp = *p; - - while (*lp != '\r' && *lp != '\0') - lp = g_utf8_next_char (lp); - - if (*lp == '\r') { - lp = g_utf8_next_char (lp); /* \n */ - lp = g_utf8_next_char (lp); /* start of the next line */ - } - - *p = lp; -} - -/* skip forward until we hit a character in @s, CRLF, or \0. leave *p - pointing at the character that causes us to stop */ -static void _skip_until (char **p, char *s) -{ - char *lp; - - lp = *p; - - while (*lp != '\r' && *lp != '\0') { - gboolean s_matches = FALSE; - char *ls; - for (ls = s; *ls; ls = g_utf8_next_char (ls)) { - if (g_utf8_get_char (ls) == g_utf8_get_char (lp)) { - s_matches = TRUE; - break; - } - } - - if (s_matches) - break; - lp++; - } - - *p = lp; -} - -static void _read_attribute_value_add (b_VFormatAttribute *attr, GString *str, GString *charset) -{ - /* don't convert empty strings */ - if (str->len == 0) { - b_vformat_attribute_add_value(attr, str->str); - return; - } - - char *inbuf, *outbuf, *p; - size_t inbytesleft, outbytesleft; - - inbuf = str->str; - p = outbuf = malloc(str->len*2); - inbytesleft = str->len; - outbytesleft = str->len*2; - - iconv_t cd; - - /* if a CHARSET was given, let's try to convert inbuf to UTF-8 */ - if (charset) { - - cd = iconv_open("UTF-8", charset->str); -#ifdef SOLARIS - if (iconv(cd, (const char**)&inbuf, &inbytesleft, &p, &outbytesleft) != (size_t)(-1)) { -#else - if (iconv(cd, &inbuf, &inbytesleft, &p, &outbytesleft) != (size_t)(-1)) { -#endif - *p = 0; - b_vformat_attribute_add_value(attr, outbuf); - - } else { - - /* hmm, should not happen */ - b_vformat_attribute_add_value(attr, str->str); - - } - - iconv_close(cd); - - } else { - - /* no CHARSET was given, if inbuf is already UTF-8 we add str->str */ - if (g_utf8_validate (inbuf, -1, NULL)) { - - b_vformat_attribute_add_value (attr, str->str); - - } else { - - /* because inbuf is not UTF-8, we think it is ISO-8859-1 */ - cd = iconv_open("UTF-8", "ISO-8859-1"); -#ifdef SOLARIS - if (iconv(cd, (const char**)&inbuf, &inbytesleft, &p, &outbytesleft) != (size_t)(-1)) { -#else - if (iconv(cd, &inbuf, &inbytesleft, &p, &outbytesleft) != (size_t)(-1)) { -#endif - *p = 0; - b_vformat_attribute_add_value (attr, outbuf); - - } else { - - b_vformat_attribute_add_value (attr, str->str); - - } - - iconv_close(cd); - - } - - } - - free(outbuf); - -} - -static void _read_attribute_value (b_VFormatAttribute *attr, char **p, int format_encoding, GString *charset) -{ - char *lp = *p; - GString *str; - - /* read in the value */ - str = g_string_new (""); - while (*lp != '\r' && *lp != '\0') { - if (*lp == '=' && format_encoding == VF_ENCODING_QP) { - char a, b, x1=0, x2=0; - - if ((a = *(++lp)) == '\0') break; - if ((b = *(++lp)) == '\0') break; - - if (isalnum(a)) { - if (isalnum(b)) { - /* e.g. ...N=C3=BCrnberg\r\n - * ^^^ - */ - x1=a; - x2=b; - } - else if (b == '=') { - /* e.g. ...N=C=\r\n - * ^^^ - * 3=BCrnberg... - * ^ - */ - char *tmplp = lp; - if (*(++tmplp) == '\r' && *(++tmplp) == '\n' && isalnum(*(++tmplp))) { - x1 = a; - x2 = *tmplp; - lp = tmplp; - } - } - else { - /* append malformed input, and - continue parsing */ - str = g_string_append_c(str, a); - str = g_string_append_c(str, b); - } - } - else if (a == '=') { - char *tmplp = lp; - char c, d, e; - c = *(++tmplp); - d = *(++tmplp); - e = *(++tmplp); - if (b == '\r' && c == '\n' && isalnum(d) && isalnum(e)) { - x1 = d; - x2 = e; - lp = tmplp; - } - else { - /* append malformed input, and - continue parsing */ - str = g_string_append_c(str, a); - str = g_string_append_c(str, b); - } - } - else { - /* append malformed input, and - continue parsing */ - str = g_string_append_c(str, a); - str = g_string_append_c(str, b); - } - if (x1 && x2) { - char c; - - a = tolower (x1); - b = tolower (x2); - - c = (((a>='a'?a-'a'+10:a-'0')&0x0f) << 4) - | ((b>='a'?b-'a'+10:b-'0')&0x0f); - - str = g_string_append_c (str, c); - } - lp++; - x1 = x2 = 0; - } - else if (format_encoding == VF_ENCODING_BASE64) { - if((*lp != ' ') && (*lp != '\t') ) - str = g_string_append_unichar (str, g_utf8_get_char (lp)); - lp = g_utf8_next_char(lp); - } - else if (*lp == '\\') { - /* convert back to the non-escaped version of - the characters */ - lp = g_utf8_next_char(lp); - if (*lp == '\0') { - str = g_string_append_c (str, '\\'); - break; - } - switch (*lp) { - case 'n': str = g_string_append_c (str, '\n'); break; - case 'r': str = g_string_append_c (str, '\r'); break; - case ';': str = g_string_append_c (str, ';'); break; - case ',': - if (!g_ascii_strcasecmp (attr->name, "CATEGORIES")) { - //We need to handle categories here to work - //aroung a bug in evo2 - _read_attribute_value_add (attr, str, charset); - g_string_assign (str, ""); - } else - str = g_string_append_c (str, ','); - break; - case '\\': str = g_string_append_c (str, '\\'); break; - case '"': str = g_string_append_c (str, '"'); break; - /* \t is (incorrectly) used by kOrganizer, so handle it here */ - case 't': str = g_string_append_c (str, '\t'); break; - default: - osync_trace(TRACE_INTERNAL, "invalid escape, passing it through. escaped char was %u", (unsigned int)*lp); - str = g_string_append_c (str, '\\'); - str = g_string_append_unichar (str, g_utf8_get_char(lp)); - break; - } - lp = g_utf8_next_char(lp); - } - else if ((*lp == ';') || - (*lp == ',' && !g_ascii_strcasecmp (attr->name, "CATEGORIES"))) { - _read_attribute_value_add (attr, str, charset); - g_string_assign (str, ""); - lp = g_utf8_next_char(lp); - } - else { - str = g_string_append_unichar (str, g_utf8_get_char (lp)); - lp = g_utf8_next_char(lp); - } - } - if (str) { - _read_attribute_value_add (attr, str, charset); - g_string_free (str, TRUE); - } - - if (*lp == '\r') { - lp = g_utf8_next_char (lp); /* \n */ - lp = g_utf8_next_char (lp); /* start of the next line */ - } - - *p = lp; -} - -static void _read_attribute_params(b_VFormatAttribute *attr, char **p, int *format_encoding, GString **charset) -{ - char *lp = *p; - GString *str; - b_VFormatParam *param = NULL; - gboolean in_quote = FALSE; - str = g_string_new (""); - - while (*lp != '\0') { - if (*lp == '"') { - in_quote = !in_quote; - lp = g_utf8_next_char (lp); - } - else if (in_quote || g_unichar_isalnum (g_utf8_get_char (lp)) || *lp == '-' || *lp == '_' || *lp == '/' || *lp == '.' || *lp == ' ') { - str = g_string_append_unichar (str, g_utf8_get_char (lp)); - lp = g_utf8_next_char (lp); - } - /* accumulate until we hit the '=' or ';'. If we hit - * a '=' the string contains the parameter name. if - * we hit a ';' the string contains the parameter - * value and the name is either ENCODING (if value == - * QUOTED-PRINTABLE) or TYPE (in any other case.) - */ - else if (*lp == '=') { - if (str->len > 0) { - param = b_vformat_attribute_param_new (str->str); - g_string_assign (str, ""); - lp = g_utf8_next_char (lp); - } - else { - _skip_until (&lp, ":;"); - if (*lp == '\r') { - lp = g_utf8_next_char (lp); /* \n */ - lp = g_utf8_next_char (lp); /* start of the next line */ - break; - } - else if (*lp == ';') - lp = g_utf8_next_char (lp); - } - } - else if (*lp == ';' || *lp == ':' || *lp == ',') { - gboolean colon = (*lp == ':'); - gboolean comma = (*lp == ','); - - if (param) { - if (str->len > 0) { - b_vformat_attribute_param_add_value (param, str->str); - g_string_assign (str, ""); - if (!colon) - lp = g_utf8_next_char (lp); - } - else { - /* we've got a parameter of the form: - * PARAM=(.*,)?[:;] - * so what we do depends on if there are already values - * for the parameter. If there are, we just finish - * this parameter and skip past the offending character - * (unless it's the ':'). If there aren't values, we free - * the parameter then skip past the character. - */ - if (!param->values) { - b_vformat_attribute_param_free (param); - param = NULL; - if (!colon) - lp = g_utf8_next_char (lp); - } - } - - if (param - && !g_ascii_strcasecmp (param->name, "encoding")) { - if (!g_ascii_strcasecmp (param->values->data, "quoted-printable")) { - *format_encoding = VF_ENCODING_QP; - b_vformat_attribute_param_free (param); - param = NULL; - } else if ( _helper_is_base64(param->values->data)) { - *format_encoding = VF_ENCODING_BASE64; -// b_vformat_attribute_param_free (param); -// param = NULL; - } - } else if (param && !g_ascii_strcasecmp(param->name, "charset")) { - *charset = g_string_new(param->values->data); - b_vformat_attribute_param_free (param); - param = NULL; - } - } - else { - if (str->len > 0) { - char *param_name; - if (!g_ascii_strcasecmp (str->str, - "quoted-printable")) { - param_name = "ENCODING"; - *format_encoding = VF_ENCODING_QP; - } - /* apple's broken addressbook app outputs naked BASE64 - parameters, which aren't even vcard 3.0 compliant. */ - else if (!g_ascii_strcasecmp (str->str, - "base64")) { - param_name = "ENCODING"; - g_string_assign (str, "b"); - *format_encoding = VF_ENCODING_BASE64; - } - else { - param_name = "TYPE"; - } - - if (param_name) { - param = b_vformat_attribute_param_new (param_name); - b_vformat_attribute_param_add_value (param, str->str); - } - g_string_assign (str, ""); - if (!colon) - lp = g_utf8_next_char (lp); - } - else { - /* we've got an attribute with a truly empty - attribute parameter. So it's of the form: - - ATTR;[PARAM=value;]*;[PARAM=value;]*: - - (note the extra ';') - - the only thing to do here is, well.. nothing. - we skip over the character if it's not a colon, - and the rest is handled for us: We'll either - continue through the loop again if we hit a ';', - or we'll break out correct below if it was a ':' */ - if (!colon) - lp = g_utf8_next_char (lp); - } - } - if (param && !comma) { - b_vformat_attribute_add_param (attr, param); - param = NULL; - } - if (colon) - break; - } - else { - osync_trace(TRACE_INTERNAL, "invalid character found in parameter spec: \"%i\" String so far: %s", lp[0], str->str); - g_string_assign (str, ""); - _skip_until (&lp, ":;"); - } - } - - if (str) - g_string_free (str, TRUE); - - *p = lp; -} - -/* reads an entire attribute from the input buffer, leaving p pointing - at the start of the next line (past the \r\n) */ -static b_VFormatAttribute *_read_attribute (char **p) -{ - char *attr_group = NULL; - char *attr_name = NULL; - b_VFormatAttribute *attr = NULL; - GString *str, *charset = NULL; - char *lp = *p; - - gboolean is_qp = FALSE; - - /* first read in the group/name */ - str = g_string_new (""); - while (*lp != '\r' && *lp != '\0') { - if (*lp == ':' || *lp == ';') { - if (str->len != 0) { - /* we've got a name, break out to the value/attribute parsing */ - attr_name = g_string_free (str, FALSE); - break; - } - else { - /* a line of the form: - * (group.)?[:;] - * - * since we don't have an attribute - * name, skip to the end of the line - * and try again. - */ - g_string_free (str, TRUE); - *p = lp; - _skip_to_next_line(p); - goto lose; - } - } - else if (*lp == '.') { - if (attr_group) { - osync_trace(TRACE_INTERNAL, "extra `.' in attribute specification. ignoring extra group `%s'", - str->str); - g_string_free (str, TRUE); - str = g_string_new (""); - } - if (str->len != 0) { - attr_group = g_string_free (str, FALSE); - str = g_string_new (""); - } - } - else if (g_unichar_isalnum (g_utf8_get_char (lp)) || *lp == '-' || *lp == '_' || *lp == '/') { - str = g_string_append_unichar (str, g_utf8_get_char (lp)); - } - else { - osync_trace(TRACE_INTERNAL, "invalid character found in attribute group/name: \"%i\" String so far: %s", lp[0], str->str); - g_string_free (str, TRUE); - *p = lp; - _skip_to_next_line(p); - goto lose; - } - - lp = g_utf8_next_char(lp); - } - - if (!attr_name) { - _skip_to_next_line (p); - goto lose; - } - - attr = b_vformat_attribute_new (attr_group, attr_name); - g_free (attr_group); - g_free (attr_name); - - if (*lp == ';') { - /* skip past the ';' */ - lp = g_utf8_next_char(lp); - _read_attribute_params (attr, &lp, &is_qp, &charset); - } - if (*lp == ':') { - /* skip past the ':' */ - lp = g_utf8_next_char(lp); - _read_attribute_value (attr, &lp, is_qp, charset); - } - - if (charset) g_string_free(charset, TRUE); - *p = lp; - - if (!attr->values) - goto lose; - - return attr; - lose: - if (attr) - b_vformat_attribute_free (attr); - return NULL; -} - -static void open_block(char **block, const char *block_name) -{ - char *start = *block ? *block : ""; - char *result = NULL; - - result = g_strconcat(start, "/", block_name, NULL); - if( *block ) - g_free(*block); - *block = result; -} - -static void close_block(char **block, const char *block_name) -{ - int name_len = strlen(block_name); - int block_len = *block ? strlen(*block) : 0; - char *cmp_start = NULL; - - if( block_len < name_len + 1 ) - return; - - cmp_start = *block + (block_len - name_len - 1); - if( cmp_start[0] == '/' && - g_ascii_strcasecmp(cmp_start+1, block_name) == 0 ) - { - // end of block hierarchy contains block name, - // so safe to remove - - // cut off the end of the string... no need to free/realloc - *cmp_start = '\0'; - } -} - -/* we try to be as forgiving as we possibly can here - this isn't a - * validator. Almost nothing is considered a fatal error. We always - * try to return *something*. - */ -static void _parse(b_VFormat *evc, const char *str) -{ - char *buf = g_strdup (str); - char *p, *end; - b_VFormatAttribute *attr; - - /* first validate the string is valid utf8 */ - if (!g_utf8_validate (buf, -1, (const char **)&end)) { - /* if the string isn't valid, we parse as much as we can from it */ - osync_trace(TRACE_INTERNAL, "invalid utf8 passed to b_VFormat. Limping along."); - *end = '\0'; - } - - buf = _fold_lines (buf); - - p = buf; - - attr = _read_attribute (&p); - if (!attr) - attr = _read_attribute (&p); - - if (!attr || attr->group || g_ascii_strcasecmp (attr->name, "begin")) { - osync_trace(TRACE_INTERNAL, "vformat began without a BEGIN\n"); - } - if (attr && !g_ascii_strcasecmp (attr->name, "begin")) - b_vformat_attribute_free (attr); - else if (attr) - b_vformat_add_attribute (evc, attr); - - char *block = NULL; - while (*p) { - b_VFormatAttribute *next_attr = _read_attribute (&p); - - if (next_attr) { - if( g_ascii_strcasecmp(next_attr->name, "begin") == 0 ) { - // add to block hierarchy string - char *value = b_vformat_attribute_get_value(next_attr); - open_block(&block, value); - //osync_trace(TRACE_INTERNAL, "open block: %s", block); - g_free(value); - } - else if( g_ascii_strcasecmp(next_attr->name, "end") == 0 ) { - // close off the block - char *value = b_vformat_attribute_get_value(next_attr); - close_block(&block, value); - //osync_trace(TRACE_INTERNAL, "close block: %s", block); - g_free(value); - } - - // apply the block to the attr - next_attr->block = g_strdup(block); - - // add! - b_vformat_add_attribute (evc, next_attr); - attr = next_attr; - } - } - - if (!attr || attr->group || g_ascii_strcasecmp (attr->name, "end")) { - osync_trace(TRACE_INTERNAL, "vformat ended without END"); - } - - g_free (buf); - g_free (block); -} - -char *b_vformat_escape_string (const char *s, b_VFormatType type) -{ - GString *str; - const char *p; - - str = g_string_new (""); - - /* Escape a string as described in RFC2426, section 5 */ - for (p = s; p && *p; p++) { - switch (*p) { - case '\n': - str = g_string_append (str, "\\n"); - break; - case '\r': - if (*(p+1) == '\n') - p++; - str = g_string_append (str, "\\n"); - break; - case ';': - str = g_string_append (str, "\\;"); - break; - case ',': - if (type == VFORMAT_CARD_30 || type == VFORMAT_EVENT_20 || type == VFORMAT_TODO_20) - str = g_string_append (str, "\\,"); - else - str = g_string_append_c (str, *p); - break; - case '\\': - /** - * We won't escape backslashes - * on vcard 2.1, unless it is in the end of a value. - * See comments above for a better explanation - **/ - if (*p != '\0' && type == VFORMAT_CARD_21) { - osync_trace(TRACE_INTERNAL, "[%s]We won't escape backslashes", __func__); - str = g_string_append_c(str, *p); - } - else { - osync_trace(TRACE_INTERNAL, "[%s] escape backslashes!!", __func__); - str = g_string_append (str, "\\\\"); - } - break; - default: - str = g_string_append_c (str, *p); - break; - } - } - - return g_string_free (str, FALSE); -} - -char* -b_vformat_unescape_string (const char *s) -{ - GString *str; - const char *p; - - g_return_val_if_fail (s != NULL, NULL); - - str = g_string_new (""); - - /* Unescape a string as described in RFC2426, section 4 (Formal Grammar) */ - for (p = s; *p; p++) { - if (*p == '\\') { - p++; - if (*p == '\0') { - str = g_string_append_c (str, '\\'); - break; - } - switch (*p) { - case 'n': str = g_string_append_c (str, '\n'); break; - case 'r': str = g_string_append_c (str, '\r'); break; - case ';': str = g_string_append_c (str, ';'); break; - case ',': str = g_string_append_c (str, ','); break; - case '\\': str = g_string_append_c (str, '\\'); break; - case '"': str = g_string_append_c (str, '"'); break; - /* \t is (incorrectly) used by kOrganizer, so handle it here */ - case 't': str = g_string_append_c (str, '\t'); break; - default: - osync_trace(TRACE_INTERNAL, "invalid escape, passing it through. escaped char was %u", (unsigned int)*p); - str = g_string_append_c (str, '\\'); - str = g_string_append_unichar (str, g_utf8_get_char(p)); - break; - } - } - } - - return g_string_free (str, FALSE); -} - -void -b_vformat_construct (b_VFormat *evc, const char *str) -{ - g_return_if_fail (str != NULL); - - if (*str) - _parse (evc, str); -} - -void b_vformat_free(b_VFormat *format) -{ - g_list_foreach (format->attributes, (GFunc)b_vformat_attribute_free, NULL); - g_list_free (format->attributes); - g_free(format); -} - -b_VFormat *b_vformat_new_from_string (const char *str) -{ - g_return_val_if_fail (str != NULL, NULL); - b_VFormat *evc = g_malloc0(sizeof(b_VFormat)); - - b_vformat_construct (evc, str); - - return evc; -} - -b_VFormat *b_vformat_new(void) -{ - return b_vformat_new_from_string (""); -} - -static int _block_match(b_VFormatAttribute *attr, const char *block) -{ - // a block matches if the end of the attribute's block - // string matches a case insensitive compare with block - // - // for example, a calendar may or may not start with a - // BEGIN: VCALENDAR, so DTSTART's block string could be - // "/vcalendar/vevent" or just "/vevent". By passing - // "/vevent" or even "vevent" as the block argument above, - // we should get a match for any of the above. - - int attr_len = attr->block ? strlen(attr->block) : 0; - int block_len = block ? strlen(block) : 0; - - if( block == NULL ) - return 1; // if block is null, match everything - - if( attr_len < block_len ) - return 0; // not enough string to compare - - if( attr_len == 0 && block_len == 0 ) - return 1; // empty and null strings match - - if( attr->block == NULL ) - return 0; // don't compare if one side is null - - return g_ascii_strcasecmp(&attr->block[attr_len - block_len], block) == 0; -} - -b_VFormatAttribute *b_vformat_find_attribute(b_VFormat *vcard, const char *name, int nth, const char *block) -{ - GList *attributes = b_vformat_get_attributes(vcard); - GList *a = NULL; - int i = 0; - for (a = attributes; a; a = a->next) { - b_VFormatAttribute *attr = a->data; - if (!g_ascii_strcasecmp(b_vformat_attribute_get_name(attr), name)) { - if( block == NULL || _block_match(attr, block) ) { - if( i == nth ) - return attr; - i++; - } - } - } - return NULL; -} - -/* -b_VFormatAttribute *b_vformat_find_attribute_next(b_VFormatAttribute *last, - const char *name, - int nth) -{ - GList *attributes = last ? last->next : 0; - GList *a = NULL; - int i = 0; - for (a = attributes; a; a = a->next) { - b_VFormatAttribute *attr = a->data; - if (!g_ascii_strcasecmp(b_vformat_attribute_get_name(attr), name)) { - if( i == nth ) - return attr; - i++; - } - } - return NULL; -} -*/ - -char *b_vformat_to_string (b_VFormat *evc, b_VFormatType type) -{ - osync_trace(TRACE_ENTRY, "%s(%p, %i)", __func__, evc, type); - GList *l; - GList *v; - - GString *str = g_string_new (""); - - switch (type) { - case VFORMAT_CARD_21: - str = g_string_append (str, "BEGIN:VCARD\r\nVERSION:2.1\r\n"); - break; - case VFORMAT_CARD_30: - str = g_string_append (str, "BEGIN:VCARD\r\nVERSION:3.0\r\n"); - break; - case VFORMAT_TODO_10: - case VFORMAT_EVENT_10: - str = g_string_append (str, "BEGIN:VCALENDAR\r\nVERSION:1.0\r\n"); - break; - case VFORMAT_TODO_20: - case VFORMAT_EVENT_20: - case VFORMAT_JOURNAL: - str = g_string_append (str, "BEGIN:VCALENDAR\r\nVERSION:2.0\r\n"); - break; - case VFORMAT_NOTE: - str = g_string_append (str, "BEGIN:VNOTE\r\nVERSION:1.1\r\n"); - break; - } - - for (l = evc->attributes; l; l = l->next) { - GList *p; - b_VFormatAttribute *attr = l->data; - GString *attr_str; - int l; - int format_encoding = VF_ENCODING_RAW; - - attr_str = g_string_new (""); - - /* From rfc2425, 5.8.2 - * - * contentline = [group "."] name *(";" param) ":" value CRLF - */ - - if (attr->group) { - attr_str = g_string_append (attr_str, attr->group); - attr_str = g_string_append_c (attr_str, '.'); - } - attr_str = g_string_append (attr_str, attr->name); - /* handle the parameters */ - for (p = attr->params; p; p = p->next) { - b_VFormatParam *param = p->data; - /* 5.8.2: - * param = param-name "=" param-value *("," param-value) - */ - if( type == VFORMAT_CARD_30 || type == VFORMAT_TODO_20 - || type == VFORMAT_EVENT_20 || type == VFORMAT_JOURNAL) { - - /** - * Character set can only be specified on the CHARSET - * parameter on the Content-Type MIME header field. - **/ - if (!g_ascii_strcasecmp (param->name, "CHARSET")) - continue; - attr_str = g_string_append_c (attr_str, ';'); - attr_str = g_string_append (attr_str, param->name); - if (param->values) { - attr_str = g_string_append_c (attr_str, '='); - } - for (v = param->values; v; v = v->next) { - if (_helper_is_base64((const char *) v->data)) { - format_encoding = VF_ENCODING_BASE64; - /*Only the "B" encoding of [RFC 2047] is an allowed*/ - v->data=g_strdup("B"); - } - /** - * QUOTED-PRINTABLE inline encoding has been - * eliminated. - **/ - if (!g_ascii_strcasecmp (param->name, "ENCODING") && !g_ascii_strcasecmp ((char *) v->data, "QUOTED-PRINTABLE")) { - osync_trace(TRACE_ERROR, "%s false encoding QUOTED-PRINTABLE is not allowed", __func__); - format_encoding = VF_ENCODING_QP; - } - attr_str = g_string_append (attr_str, v->data); - - if (v->next) - attr_str = g_string_append_c (attr_str, ','); - } - } - else { - attr_str = g_string_append_c (attr_str, ';'); - /** - * The "TYPE=" is optional skip it. - * LOGO, PHOTO and SOUND multimedia formats MUST - * have a "TYPE=" parameter - **/ - gboolean must_have_type = FALSE; - if (!g_ascii_strcasecmp (attr->name, "PHOTO") || !g_ascii_strcasecmp (attr->name, "LOGO") || !g_ascii_strcasecmp (attr->name, "SOUND") ) - must_have_type = TRUE; - if ( must_have_type || g_ascii_strcasecmp (param->name, "TYPE") ) - attr_str = g_string_append (attr_str, param->name); - if ( param->values && (must_have_type || g_ascii_strcasecmp (param->name, "TYPE")) ) - attr_str = g_string_append_c (attr_str, '='); - for (v = param->values; v; v = v->next) { - // check for quoted-printable encoding - if (!g_ascii_strcasecmp (param->name, "ENCODING") && !g_ascii_strcasecmp ((char *) v->data, "QUOTED-PRINTABLE")) - format_encoding = VF_ENCODING_QP; - // check for base64 encoding - if (_helper_is_base64((const char *) v->data)) { - format_encoding = VF_ENCODING_BASE64; - v->data=g_strdup("BASE64"); - } - attr_str = g_string_append (attr_str, v->data); - if (v->next) - attr_str = g_string_append_c (attr_str, ','); - } - } - } - - attr_str = g_string_append_c (attr_str, ':'); - - for (v = attr->values; v; v = v->next) { - char *value = v->data; - char *escaped_value = NULL; - - if (!g_ascii_strcasecmp (attr->name, "RRULE") && - strstr (value, "BYDAY") == v->data) { - attr_str = g_string_append (attr_str, value); - } else { - escaped_value = b_vformat_escape_string (value, type); - attr_str = g_string_append (attr_str, escaped_value); - } - - if (v->next) { - - /* XXX toshok - i hate you, rfc 2426. - why doesn't CATEGORIES use a ; like - a normal list attribute? */ - if (!g_ascii_strcasecmp (attr->name, "CATEGORIES")) - attr_str = g_string_append_c (attr_str, ','); - else - attr_str = g_string_append_c (attr_str, ';'); - } - - g_free (escaped_value); - } - - /* Folding lines: - * ^^^^^^^^^^^^^^ - * - * rfc 2426 (vCard), 2.6 Line Delimiting and Folding: - * After generating a content line, - * lines longer than 75 characters SHOULD be folded according to the - * folding procedure described in [MIME-DIR]. - * - * rfc 2445 (iCalendar), 4.1 Content Lines: - * Lines of text SHOULD NOT be longer than 75 octets, excluding the line - * break. Long content lines SHOULD be split into a multiple line - * representations using a line "folding" technique. That is, a long - * line can be split between any two characters by inserting a CRLF - * immediately followed by a single linear white space character (i.e., - * SPACE, US-ASCII decimal 32 or HTAB, US-ASCII decimal 9). Any sequence - * of CRLF followed immediately by a single linear white space character - * is ignored (i.e., removed) when processing the content type. - * - * SUMMARY: When generating a content line, lines longer then 75 characters SHOULD be folded! - * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - * - * Differences between encodings: - * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - * - * rfc 2425 [MIME-DIR], 5.8.1: - * A logical line MAY be continued on the next physical line anywhere - * between two characters by inserting a CRLF immediately followed by a - * single (white space) character. - * - * rfc 2045, 6.7, chapter 5: - * The quoted-printable specs says that softbreaks should be generated by inserting a =\r\n - * without follwing - * - * UTF-8 - * ^^^^^ - * - * Note that all the line folding above is described in terms of characters - * not bytes. In particular, it would be an error to put a line break - * within a UTF-8 character. - */ - - l = 0; - do { - if (g_utf8_strlen(attr_str->str, attr_str->len) - l > 75) { - l += 75; - - /* If using QP, must be sure that we do not fold within a quote sequence */ - if (format_encoding == VF_ENCODING_QP) { - if (g_utf8_get_char(g_utf8_offset_to_pointer(attr_str->str, l-1)) == '=') l--; - else if (g_utf8_get_char(g_utf8_offset_to_pointer(attr_str->str, l-2)) == '=') l -= 2; - } - - char *p = g_utf8_offset_to_pointer(attr_str->str, l); - - if (format_encoding == VF_ENCODING_QP) - attr_str = g_string_insert_len (attr_str, p - attr_str->str, "=" CRLF "", sizeof ("=" CRLF "") - 1); - else - attr_str = g_string_insert_len (attr_str, p - attr_str->str, CRLF " ", sizeof (CRLF " ") - 1); - } - else - break; - } while (l < g_utf8_strlen(attr_str->str, attr_str->len)); - - attr_str = g_string_append (attr_str, CRLF); - /** - * base64= - * the end of the text is marked with two CRLF sequences - * this results in one blank line before the start of the - * next property - **/ - if( format_encoding == VF_ENCODING_BASE64 - && (type == VFORMAT_CARD_21)) - attr_str = g_string_append (attr_str, CRLF); - - str = g_string_append (str, attr_str->str); - g_string_free (attr_str, TRUE); - } - - switch (type) { - case VFORMAT_CARD_21: - str = g_string_append (str, "END:VCARD\r\n"); - break; - case VFORMAT_CARD_30: - str = g_string_append (str, "END:VCARD\r\n"); - break; - case VFORMAT_TODO_10: - case VFORMAT_EVENT_10: - str = g_string_append (str, "END:VCALENDAR\r\n"); - break; - case VFORMAT_TODO_20: - case VFORMAT_EVENT_20: - case VFORMAT_JOURNAL: - str = g_string_append (str, "END:VCALENDAR\r\n"); - break; - case VFORMAT_NOTE: - str = g_string_append (str, "END:VNOTE\r\n"); - break; - } - - osync_trace(TRACE_EXIT, "%s", __func__); - return g_string_free (str, FALSE); -} - -void b_vformat_dump_structure (b_VFormat *evc) -{ - GList *a; - GList *v; - int i; - - printf ("b_VFormat\n"); - for (a = evc->attributes; a; a = a->next) { - GList *p; - b_VFormatAttribute *attr = a->data; - printf ("+-- %s\n", attr->name); - if (attr->params) { - printf (" +- params=\n"); - - for (p = attr->params, i = 0; p; p = p->next, i++) { - b_VFormatParam *param = p->data; - printf (" | [%d] = %s", i,param->name); - printf ("("); - for (v = param->values; v; v = v->next) { - char *value = b_vformat_escape_string ((char*)v->data, VFORMAT_CARD_21); - printf ("%s", value); - if (v->next) - printf (","); - g_free (value); - } - - printf (")\n"); - } - } - printf (" +- values=\n"); - for (v = attr->values, i = 0; v; v = v->next, i++) { - printf (" [%d] = `%s'\n", i, (char*)v->data); - } - } -} - -b_VFormatAttribute *b_vformat_attribute_new (const char *attr_group, const char *attr_name) -{ - b_VFormatAttribute *attr; - - attr = g_new0 (b_VFormatAttribute, 1); - - attr->group = g_strdup (attr_group); - attr->name = g_strdup (attr_name); - - return attr; -} - -void -b_vformat_attribute_free (b_VFormatAttribute *attr) -{ - g_return_if_fail (attr != NULL); - - g_free (attr->block); - g_free (attr->group); - g_free (attr->name); - - b_vformat_attribute_remove_values (attr); - - b_vformat_attribute_remove_params (attr); - - g_free (attr); -} - -b_VFormatAttribute* -b_vformat_attribute_copy (b_VFormatAttribute *attr) -{ - b_VFormatAttribute *a; - GList *p; - - g_return_val_if_fail (attr != NULL, NULL); - - a = b_vformat_attribute_new (b_vformat_attribute_get_group (attr), - b_vformat_attribute_get_name (attr)); - - for (p = attr->values; p; p = p->next) - b_vformat_attribute_add_value (a, p->data); - - for (p = attr->params; p; p = p->next) - b_vformat_attribute_add_param (a, b_vformat_attribute_param_copy (p->data)); - - return a; -} - -void -b_vformat_remove_attributes (b_VFormat *evc, const char *attr_group, const char *attr_name) -{ - GList *attr; - - g_return_if_fail (attr_name != NULL); - - attr = evc->attributes; - while (attr) { - GList *next_attr; - b_VFormatAttribute *a = attr->data; - - next_attr = attr->next; - - if (((!attr_group && !a->group) || - (attr_group && !g_ascii_strcasecmp (attr_group, a->group))) && - ((!attr_name && !a->name) || !g_ascii_strcasecmp (attr_name, a->name))) { - - /* matches, remove/delete the attribute */ - evc->attributes = g_list_remove_link (evc->attributes, attr); - - b_vformat_attribute_free (a); - } - - attr = next_attr; - } -} - -void -b_vformat_remove_attribute (b_VFormat *evc, b_VFormatAttribute *attr) -{ - g_return_if_fail (attr != NULL); - - evc->attributes = g_list_remove (evc->attributes, attr); - b_vformat_attribute_free (attr); -} - -void -b_vformat_add_attribute (b_VFormat *evc, b_VFormatAttribute *attr) -{ - g_return_if_fail (attr != NULL); - - evc->attributes = g_list_append (evc->attributes, attr); -} - -void -b_vformat_add_attribute_with_value (b_VFormat *b_VFormat, - b_VFormatAttribute *attr, const char *value) -{ - g_return_if_fail (attr != NULL); - - b_vformat_attribute_add_value (attr, value); - - b_vformat_add_attribute (b_VFormat, attr); -} - -void -b_vformat_add_attribute_with_values (b_VFormat *b_VFormat, b_VFormatAttribute *attr, ...) -{ - va_list ap; - char *v; - - g_return_if_fail (attr != NULL); - - va_start (ap, attr); - - while ((v = va_arg (ap, char*))) { - b_vformat_attribute_add_value (attr, v); - } - - va_end (ap); - - b_vformat_add_attribute (b_VFormat, attr); -} - -void -b_vformat_attribute_add_value (b_VFormatAttribute *attr, const char *value) -{ - g_return_if_fail (attr != NULL); - - attr->values = g_list_append (attr->values, g_strdup (value)); -} - -void -b_vformat_attribute_add_value_decoded (b_VFormatAttribute *attr, const char *value, int len) -{ - g_return_if_fail (attr != NULL); - - switch (attr->encoding) { - case VF_ENCODING_RAW: - osync_trace(TRACE_INTERNAL, "can't add_value_decoded with an attribute using RAW encoding. you must set the ENCODING parameter first"); - break; - case VF_ENCODING_BASE64: { - char *b64_data = base64_encode_simple (value, len); - GString *decoded = g_string_new_len (value, len); - - /* make sure the decoded list is up to date */ - b_vformat_attribute_get_values_decoded (attr); - - attr->values = g_list_append (attr->values, b64_data); - attr->decoded_values = g_list_append (attr->decoded_values, decoded); - break; - } - case VF_ENCODING_QP: { - char *qp_data = quoted_encode_simple ((unsigned char*)value, len); - GString *decoded = g_string_new (value); - - /* make sure the decoded list is up to date */ - b_vformat_attribute_get_values_decoded (attr); - - attr->values = g_list_append (attr->values, qp_data); - attr->decoded_values = g_list_append (attr->decoded_values, decoded); - break; - } - case VF_ENCODING_8BIT: { - char *data = g_strdup(value); - GString *decoded = g_string_new (value); - - /* make sure the decoded list is up to date */ - b_vformat_attribute_get_values_decoded (attr); - - attr->values = g_list_append (attr->values, data); - attr->decoded_values = g_list_append (attr->decoded_values, decoded); - break; - } - } -} - -void -b_vformat_attribute_add_values (b_VFormatAttribute *attr, ...) -{ - va_list ap; - char *v; - - g_return_if_fail (attr != NULL); - - va_start (ap, attr); - - while ((v = va_arg (ap, char*))) { - b_vformat_attribute_add_value (attr, v); - } - - va_end (ap); -} - -static void -free_gstring (GString *str) -{ - g_string_free (str, TRUE); -} - -void -b_vformat_attribute_remove_values (b_VFormatAttribute *attr) -{ - g_return_if_fail (attr != NULL); - - g_list_foreach (attr->values, (GFunc)g_free, NULL); - g_list_free (attr->values); - attr->values = NULL; - - g_list_foreach (attr->decoded_values, (GFunc)free_gstring, NULL); - g_list_free (attr->decoded_values); - attr->decoded_values = NULL; -} - -void -b_vformat_attribute_remove_params (b_VFormatAttribute *attr) -{ - g_return_if_fail (attr != NULL); - - g_list_foreach (attr->params, (GFunc)b_vformat_attribute_param_free, NULL); - g_list_free (attr->params); - attr->params = NULL; - - /* also remove the cached encoding on this attribute */ - attr->encoding_set = FALSE; - attr->encoding = VF_ENCODING_RAW; -} - -b_VFormatParam* -b_vformat_attribute_param_new (const char *name) -{ - b_VFormatParam *param = g_new0 (b_VFormatParam, 1); - param->name = g_strdup (name); - - return param; -} - -void -b_vformat_attribute_param_free (b_VFormatParam *param) -{ - g_return_if_fail (param != NULL); - - g_free (param->name); - - b_vformat_attribute_param_remove_values (param); - - g_free (param); -} - -b_VFormatParam* -b_vformat_attribute_param_copy (b_VFormatParam *param) -{ - b_VFormatParam *p; - GList *l; - - g_return_val_if_fail (param != NULL, NULL); - - p = b_vformat_attribute_param_new (b_vformat_attribute_param_get_name (param)); - - for (l = param->values; l; l = l->next) { - b_vformat_attribute_param_add_value (p, l->data); - } - - return p; -} - -void -b_vformat_attribute_add_param (b_VFormatAttribute *attr, - b_VFormatParam *param) -{ - g_return_if_fail (attr != NULL); - g_return_if_fail (param != NULL); - - attr->params = g_list_append (attr->params, param); - - /* we handle our special encoding stuff here */ - - if (!g_ascii_strcasecmp (param->name, "ENCODING")) { - if (attr->encoding_set) { - osync_trace(TRACE_INTERNAL, "ENCODING specified twice"); - return; - } - - if (param->values && param->values->data) { - if (_helper_is_base64((const char*)param->values->data)) - attr->encoding = VF_ENCODING_BASE64; - else if (!g_ascii_strcasecmp ((char*)param->values->data, "QUOTED-PRINTABLE")) - attr->encoding = VF_ENCODING_QP; - else if (!g_ascii_strcasecmp ((char *)param->values->data, "8BIT")) - attr->encoding = VF_ENCODING_8BIT; - else { - osync_trace(TRACE_INTERNAL, "Unknown value `%s' for ENCODING parameter. values will be treated as raw", - (char*)param->values->data); - } - - attr->encoding_set = TRUE; - } - else { - osync_trace(TRACE_INTERNAL, "ENCODING parameter added with no value"); - } - } -} - -b_VFormatParam *b_vformat_attribute_find_param(b_VFormatAttribute *attr, const char *name, int level) -{ - g_return_val_if_fail (attr != NULL, NULL); - GList *p = NULL; - for (p = attr->params; p; p = p->next) { - b_VFormatParam *param = p->data; - if (!g_ascii_strcasecmp (param->name, name)) { - if( level == 0 ) - return param; - else - level--; - } - } - return NULL; -} - -void -b_vformat_attribute_set_value (b_VFormatAttribute *attr, - int nth, const char *value) -{ - GList *param = g_list_nth(attr->values, nth); - g_free(param->data); - param->data = g_strdup(value); -} - -void -b_vformat_attribute_param_add_value (b_VFormatParam *param, - const char *value) -{ - g_return_if_fail (param != NULL); - - param->values = g_list_append (param->values, g_strdup (value)); -} - -void -b_vformat_attribute_param_add_values (b_VFormatParam *param, - ...) -{ - va_list ap; - char *v; - - g_return_if_fail (param != NULL); - - va_start (ap, param); - - while ((v = va_arg (ap, char*))) { - b_vformat_attribute_param_add_value (param, v); - } - - va_end (ap); -} - -void -b_vformat_attribute_add_param_with_value (b_VFormatAttribute *attr, const char *name, const char *value) -{ - g_return_if_fail (attr != NULL); - g_return_if_fail (name != NULL); - - if (!value) - return; - - b_VFormatParam *param = b_vformat_attribute_param_new(name); - - b_vformat_attribute_param_add_value (param, value); - - b_vformat_attribute_add_param (attr, param); -} - -void -b_vformat_attribute_add_param_with_values (b_VFormatAttribute *attr, - b_VFormatParam *param, ...) -{ - va_list ap; - char *v; - - g_return_if_fail (attr != NULL); - g_return_if_fail (param != NULL); - - va_start (ap, param); - - while ((v = va_arg (ap, char*))) { - b_vformat_attribute_param_add_value (param, v); - } - - va_end (ap); - - b_vformat_attribute_add_param (attr, param); -} - -void -b_vformat_attribute_param_remove_values (b_VFormatParam *param) -{ - g_return_if_fail (param != NULL); - - g_list_foreach (param->values, (GFunc)g_free, NULL); - g_list_free (param->values); - param->values = NULL; -} - -GList* -b_vformat_get_attributes (b_VFormat *format) -{ - return format->attributes; -} - -const char* -b_vformat_attribute_get_group (b_VFormatAttribute *attr) -{ - g_return_val_if_fail (attr != NULL, NULL); - - return attr->group; -} - -const char* -b_vformat_attribute_get_name (b_VFormatAttribute *attr) -{ - g_return_val_if_fail (attr != NULL, NULL); - - return attr->name; -} - -const char* -b_vformat_attribute_get_block (b_VFormatAttribute *attr) -{ - g_return_val_if_fail (attr != NULL, NULL); - - return attr->block; -} - -GList* -b_vformat_attribute_get_values (b_VFormatAttribute *attr) -{ - g_return_val_if_fail (attr != NULL, NULL); - - return attr->values; -} - -GList* -b_vformat_attribute_get_values_decoded (b_VFormatAttribute *attr) -{ - g_return_val_if_fail (attr != NULL, NULL); - - if (!attr->decoded_values) { - GList *l; - switch (attr->encoding) { - case VF_ENCODING_RAW: - case VF_ENCODING_8BIT: - for (l = attr->values; l; l = l->next) - attr->decoded_values = g_list_append (attr->decoded_values, g_string_new ((char*)l->data)); - break; - case VF_ENCODING_BASE64: - for (l = attr->values; l; l = l->next) { - char *decoded = g_strdup ((char*)l->data); - int len = base64_decode_simple (decoded, strlen (decoded)); - attr->decoded_values = g_list_append (attr->decoded_values, g_string_new_len (decoded, len)); - g_free (decoded); - } - break; - case VF_ENCODING_QP: - for (l = attr->values; l; l = l->next) { - if (!(l->data)) - continue; - char *decoded = g_strdup ((char*)l->data); - int len = quoted_decode_simple (decoded, strlen (decoded)); - attr->decoded_values = g_list_append (attr->decoded_values, g_string_new_len (decoded, len)); - g_free (decoded); - } - break; - } - } - - return attr->decoded_values; -} - -gboolean -b_vformat_attribute_is_single_valued (b_VFormatAttribute *attr) -{ - g_return_val_if_fail (attr != NULL, FALSE); - - if (attr->values == NULL - || attr->values->next != NULL) - return FALSE; - - return TRUE; -} - -char* -b_vformat_attribute_get_value (b_VFormatAttribute *attr) -{ - GList *values; - - g_return_val_if_fail (attr != NULL, NULL); - - values = b_vformat_attribute_get_values (attr); - - if (!b_vformat_attribute_is_single_valued (attr)) - osync_trace(TRACE_INTERNAL, "b_vformat_attribute_get_value called on multivalued attribute"); - - return values ? g_strdup ((char*)values->data) : NULL; -} - -GString* -b_vformat_attribute_get_value_decoded (b_VFormatAttribute *attr) -{ - GList *values; - GString *str = NULL; - - g_return_val_if_fail (attr != NULL, NULL); - - values = b_vformat_attribute_get_values_decoded (attr); - - if (!b_vformat_attribute_is_single_valued (attr)) - osync_trace(TRACE_INTERNAL, "b_vformat_attribute_get_value_decoded called on multivalued attribute"); - - if (values) - str = values->data; - - return str ? g_string_new_len (str->str, str->len) : NULL; -} - -const char *b_vformat_attribute_get_nth_value(b_VFormatAttribute *attr, int nth) -{ - GList *values = b_vformat_attribute_get_values_decoded(attr); - if (!values) - return NULL; - GString *retstr = (GString *)g_list_nth_data(values, nth); - if (!retstr) - return NULL; - - if (!g_utf8_validate(retstr->str, -1, NULL)) { - values = b_vformat_attribute_get_values(attr); - if (!values) - return NULL; - return g_list_nth_data(values, nth); - } - - return retstr->str; -} - -gboolean -b_vformat_attribute_has_type (b_VFormatAttribute *attr, const char *typestr) -{ - GList *params; - GList *p; - - g_return_val_if_fail (attr != NULL, FALSE); - g_return_val_if_fail (typestr != NULL, FALSE); - - params = b_vformat_attribute_get_params (attr); - - for (p = params; p; p = p->next) { - b_VFormatParam *param = p->data; - - if (!strcasecmp (b_vformat_attribute_param_get_name (param), "TYPE")) { - GList *values = b_vformat_attribute_param_get_values (param); - GList *v; - - for (v = values; v; v = v->next) { - if (!strcasecmp ((char*)v->data, typestr)) - return TRUE; - } - } - } - - return FALSE; -} - - -gboolean b_vformat_attribute_has_param(b_VFormatAttribute *attr, const char *name) -{ - g_return_val_if_fail (attr != NULL, FALSE); - g_return_val_if_fail (name != NULL, FALSE); - - GList *params = b_vformat_attribute_get_params(attr); - GList *p; - for (p = params; p; p = p->next) { - b_VFormatParam *param = p->data; - if (!strcasecmp(name, b_vformat_attribute_param_get_name(param))) - return TRUE; - } - return FALSE; -} - -GList* -b_vformat_attribute_get_params (b_VFormatAttribute *attr) -{ - g_return_val_if_fail (attr != NULL, NULL); - - return attr->params; -} - -const char* -b_vformat_attribute_param_get_name (b_VFormatParam *param) -{ - g_return_val_if_fail (param != NULL, NULL); - - return param->name; -} - -GList* -b_vformat_attribute_param_get_values (b_VFormatParam *param) -{ - g_return_val_if_fail (param != NULL, NULL); - - return param->values; -} - -const char *b_vformat_attribute_param_get_nth_value(b_VFormatParam *param, int nth) -{ - const char *ret = NULL; - GList *values = b_vformat_attribute_param_get_values(param); - if (!values) - return NULL; - ret = g_list_nth_data(values, nth); - return ret; -} - -static const char *base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - -//static unsigned char _evc_base64_rank[256]; - -static void base64_init(char *rank) -{ - int i; - - memset(rank, 0xff, sizeof(rank)); - for (i=0;i<64;i++) { - rank[(unsigned int)base64_alphabet[i]] = i; - } - rank['='] = 0; -} - -/* call this when finished encoding everything, to - flush off the last little bit */ -static size_t base64_encode_close(const unsigned char *in, size_t inlen, gboolean break_lines, unsigned char *out, int *state, int *save) -{ - int c1, c2; - unsigned char *outptr = out; - - if (inlen>0) - outptr += base64_encode_step(in, inlen, break_lines, outptr, state, save); - - c1 = ((unsigned char *)save)[1]; - c2 = ((unsigned char *)save)[2]; - - switch (((char *)save)[0]) { - case 2: - outptr[2] = base64_alphabet[ ( (c2 &0x0f) << 2 ) ]; - g_assert(outptr[2] != 0); - goto skip; - case 1: - outptr[2] = '='; - skip: - outptr[0] = base64_alphabet[ c1 >> 2 ]; - outptr[1] = base64_alphabet[ c2 >> 4 | ( (c1&0x3) << 4 )]; - outptr[3] = '='; - outptr += 4; - break; - } - if (break_lines) - *outptr++ = '\n'; - - *save = 0; - *state = 0; - - return outptr-out; -} - -/* - performs an 'encode step', only encodes blocks of 3 characters to the - output at a time, saves left-over state in state and save (initialise to - 0 on first invocation). -*/ -static size_t base64_encode_step(const unsigned char *in, size_t len, gboolean break_lines, unsigned char *out, int *state, int *save) -{ - register const unsigned char *inptr; - register unsigned char *outptr; - - if (len<=0) - return 0; - - inptr = in; - outptr = out; - - if (len + ((char *)save)[0] > 2) { - const unsigned char *inend = in+len-2; - register int c1, c2, c3; - register int already; - - already = *state; - - switch (((char *)save)[0]) { - case 1: c1 = ((unsigned char *)save)[1]; goto skip1; - case 2: c1 = ((unsigned char *)save)[1]; - c2 = ((unsigned char *)save)[2]; goto skip2; - } - - /* yes, we jump into the loop, no i'm not going to change it, it's beautiful! */ - while (inptr < inend) { - c1 = *inptr++; - skip1: - c2 = *inptr++; - skip2: - c3 = *inptr++; - *outptr++ = base64_alphabet[ c1 >> 2 ]; - *outptr++ = base64_alphabet[ c2 >> 4 | ( (c1&0x3) << 4 ) ]; - *outptr++ = base64_alphabet[ ( (c2 &0x0f) << 2 ) | (c3 >> 6) ]; - *outptr++ = base64_alphabet[ c3 & 0x3f ]; - /* this is a bit ugly ... */ - if (break_lines && (++already)>=19) { - *outptr++='\n'; - already = 0; - } - } - - ((char *)save)[0] = 0; - len = 2-(inptr-inend); - *state = already; - } - - if (len>0) { - register char *saveout; - - /* points to the slot for the next char to save */ - saveout = & (((char *)save)[1]) + ((char *)save)[0]; - - /* len can only be 0 1 or 2 */ - switch(len) { - case 2: *saveout++ = *inptr++; - case 1: *saveout++ = *inptr++; - } - ((char *)save)[0]+=len; - } - - return outptr-out; -} - - -/** - * base64_decode_step: decode a chunk of base64 encoded data - * @in: input stream - * @len: max length of data to decode - * @out: output stream - * @state: holds the number of bits that are stored in @save - * @save: leftover bits that have not yet been decoded - * - * Decodes a chunk of base64 encoded data - **/ -static size_t base64_decode_step(const unsigned char *in, size_t len, unsigned char *out, int *state, unsigned int *save) -{ - unsigned char base64_rank[256]; - base64_init((char*)base64_rank); - - register const unsigned char *inptr; - register unsigned char *outptr; - const unsigned char *inend; - unsigned char c; - register unsigned int v; - int i; - - inend = in+len; - outptr = out; - - /* convert 4 base64 bytes to 3 normal bytes */ - v=*save; - i=*state; - inptr = in; - while (inptr>16; - *outptr++ = v>>8; - *outptr++ = v; - i=0; - } - } - } - - *save = v; - *state = i; - - /* quick scan back for '=' on the end somewhere */ - /* fortunately we can drop 1 output char for each trailing = (upto 2) */ - i=2; - while (inptr>in && i) { - inptr--; - if (base64_rank[*inptr] != 0xff) { - if (*inptr == '=' && outptr>out) - outptr--; - i--; - } - } - - /* if i!= 0 then there is a truncation error! */ - return outptr-out; -} - -static char *base64_encode_simple (const char *data, size_t len) -{ - unsigned char *out; - int state = 0, outlen; - unsigned int save = 0; - - g_return_val_if_fail (data != NULL, NULL); - - out = g_malloc (len * 4 / 3 + 5); - outlen = base64_encode_close ((unsigned char *)data, len, FALSE, - out, &state, (int*)&save); - out[outlen] = '\0'; - return (char *)out; -} - -static size_t base64_decode_simple (char *data, size_t len) -{ - int state = 0; - unsigned int save = 0; - - g_return_val_if_fail (data != NULL, 0); - - return base64_decode_step ((unsigned char *)data, len, - (unsigned char *)data, &state, &save); -} - -static char *quoted_encode_simple(const unsigned char *string, int len) -{ - GString *tmp = g_string_new(""); - - int i = 0; - while(string[i] != 0) { - if (string[i] > 127 || string[i] == 13 || string[i] == 10 || string[i] == '=') { - g_string_append_printf(tmp, "=%02X", string[i]); - } else { - g_string_append_c(tmp, string[i]); - } - i++; - } - - char *ret = tmp->str; - g_string_free(tmp, FALSE); - return ret; -} - - -static size_t quoted_decode_simple (char *data, size_t len) -{ - g_return_val_if_fail (data != NULL, 0); - - GString *string = g_string_new(data); - if (!string) - return 0; - - char hex[5]; - hex[4] = 0; - - while (1) { - //Get the index of the next encoded char - int i = strcspn(string->str, "="); - if (i >= strlen(string->str)) - break; - - strcpy(hex, "0x"); - strncat(hex, &string->str[i + 1], 2); - char rep = ((int)(strtod(hex, NULL))); - g_string_erase(string, i, 2); - g_string_insert_c(string, i, rep); - } - - memset(data, 0, strlen(data)); - strcpy(data, string->str); - g_string_free(string, 1); - - return strlen(data); -} diff --git a/opensync-plugin-0.4x/src/vformat.h b/opensync-plugin-0.4x/src/vformat.h deleted file mode 100644 index 6b29b155..00000000 --- a/opensync-plugin-0.4x/src/vformat.h +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (C) 2003 Ximian, Inc. 2005 Armin Bauer - * - * Copyright (C) 2003 Ximian, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Author: Chris Toshok (toshok@ximian.com) - * Author: Armin Bauer (armin.bauer@opensync.org) - * - */ - -#ifndef _VFORMAT_H -#define _VFORMAT_H - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { - VFORMAT_CARD_21, - VFORMAT_CARD_30, - VFORMAT_NOTE, - VFORMAT_EVENT_10, - VFORMAT_EVENT_20, - VFORMAT_TODO_10, - VFORMAT_TODO_20, - VFORMAT_JOURNAL -} b_VFormatType; - -typedef struct b_VFormat { - //b_VFormatType type; - GList *attributes; -} b_VFormat; - -#define CRLF "\r\n" - -typedef enum { - VF_ENCODING_RAW, /* no encoding */ - VF_ENCODING_BASE64, /* base64 */ - VF_ENCODING_QP, /* quoted-printable */ - VF_ENCODING_8BIT -} b_VFormatEncoding; - -typedef struct b_VFormatAttribute { - char *block; /* "vtimezone/standard", or "vevent", depending on - current begin/end location... may be null */ - char *group; - char *name; - GList *params; /* b_VFormatParam */ - GList *values; - GList *decoded_values; - b_VFormatEncoding encoding; - gboolean encoding_set; -} b_VFormatAttribute; - -typedef struct b_VFormatParam { - char *name; - GList *values; /* GList of char*'s*/ -} b_VFormatParam; - - -/*b_VFormat *vcard_new(b_VFormatType type); -b_VFormat *vcard_new_from_string (const char *str, b_VFormatType type); -//char *vcard_to_string(VFormat *card, VFormatType format); - -VFormat *vnote_new(void); -VFormat *vnote_new_from_string(const char *str); -//char *vnote_to_string(VFormat *note); - - -VFormat *vevent_new(void); -VFormat *vevent_new_from_string(const char *str); -//char *vevent_to_string(VFormat *event); - -VFormat *vtodo_new(void); -VFormat *vtodo_new_from_string(const char *str);*/ -//char *vtodo_to_string(VFormat *todo); - -/* mostly for debugging */ -b_VFormat *b_vformat_new(void); -b_VFormat *b_vformat_new_from_string(const char *str); -void b_vformat_dump_structure(b_VFormat *format); -char *b_vformat_to_string(b_VFormat *evc, b_VFormatType type); -time_t b_vformat_time_to_unix(const char *inptime); -void b_vformat_free(b_VFormat *format); - -/* attributes */ -b_VFormatAttribute *b_vformat_attribute_new (const char *attr_group, const char *attr_name); -void b_vformat_attribute_free (b_VFormatAttribute *attr); -b_VFormatAttribute *b_vformat_attribute_copy (b_VFormatAttribute *attr); -void b_vformat_remove_attributes (b_VFormat *vformat, const char *attr_group, const char *attr_name); -void b_vformat_remove_attribute (b_VFormat *vformat, b_VFormatAttribute *attr); -void b_vformat_add_attribute (b_VFormat *vformat, b_VFormatAttribute *attr); -void b_vformat_add_attribute_with_value (b_VFormat *vformat, b_VFormatAttribute *attr, const char *value); -void b_vformat_add_attribute_with_values (b_VFormat *vformat, b_VFormatAttribute *attr, ...); -void b_vformat_attribute_add_value (b_VFormatAttribute *attr, const char *value); -void b_vformat_attribute_set_value (b_VFormatAttribute *attr, int nth, const char *value); -void b_vformat_attribute_add_value_decoded (b_VFormatAttribute *attr, const char *value, int len); -void b_vformat_attribute_add_values (b_VFormatAttribute *attr, ...); -void b_vformat_attribute_remove_values (b_VFormatAttribute *attr); -void b_vformat_attribute_remove_params (b_VFormatAttribute *attr); -b_VFormatAttribute *b_vformat_find_attribute (b_VFormat *evc, const char *name, int nth, const char *block); - -/* attribute parameters */ -b_VFormatParam* b_vformat_attribute_param_new (const char *param_name); -void b_vformat_attribute_param_free (b_VFormatParam *param); -b_VFormatParam* b_vformat_attribute_param_copy (b_VFormatParam *param); -void b_vformat_attribute_add_param (b_VFormatAttribute *attr, b_VFormatParam *param); -b_VFormatParam *b_vformat_attribute_find_param(b_VFormatAttribute *attr, const char *name, int level); -void b_vformat_attribute_add_param_with_value (b_VFormatAttribute *attr, const char *name, const char *value); -void b_vformat_attribute_add_param_with_values (b_VFormatAttribute *attr, - b_VFormatParam *param, ...); - -void b_vformat_attribute_param_add_value (b_VFormatParam *param, - const char *value); -void b_vformat_attribute_param_add_values (b_VFormatParam *param, - ...); -void b_vformat_attribute_param_remove_values (b_VFormatParam *param); -gboolean b_vformat_attribute_has_param(b_VFormatAttribute *attr, const char *name); - -/* b_VFormat* accessors. nothing returned from these functions should be - freed by the caller. */ -GList* b_vformat_get_attributes (b_VFormat *vformat); -const char* b_vformat_attribute_get_group (b_VFormatAttribute *attr); -const char* b_vformat_attribute_get_name (b_VFormatAttribute *attr); -const char* b_vformat_attribute_get_block (b_VFormatAttribute *attr); -GList* b_vformat_attribute_get_values (b_VFormatAttribute *attr); /* GList elements are of type char* */ -GList* b_vformat_attribute_get_values_decoded (b_VFormatAttribute *attr); /* GList elements are of type GString* */ -const char *b_vformat_attribute_get_nth_value(b_VFormatAttribute *attr, int nth); - -/* special accessors for single valued attributes */ -gboolean b_vformat_attribute_is_single_valued (b_VFormatAttribute *attr); -char* b_vformat_attribute_get_value (b_VFormatAttribute *attr); -GString* b_vformat_attribute_get_value_decoded (b_VFormatAttribute *attr); - -GList* b_vformat_attribute_get_params (b_VFormatAttribute *attr); -const char* b_vformat_attribute_param_get_name (b_VFormatParam *param); -GList* b_vformat_attribute_param_get_values (b_VFormatParam *param); -const char *b_vformat_attribute_param_get_nth_value(b_VFormatParam *param, int nth); - -/* special TYPE= parameter predicate (checks for TYPE=@typestr */ -gboolean b_vformat_attribute_has_type (b_VFormatAttribute *attr, const char *typestr); - -/* Utility functions. */ -char* b_vformat_escape_string (const char *str, b_VFormatType type); -char* b_vformat_unescape_string (const char *str); - -#ifdef __cplusplus -} -#endif - -#endif /* _VFORMAT_H */ diff --git a/opensync-plugin-0.4x/src/vjournal.cc b/opensync-plugin-0.4x/src/vjournal.cc index 143a9b1e..d230b958 100644 --- a/opensync-plugin-0.4x/src/vjournal.cc +++ b/opensync-plugin-0.4x/src/vjournal.cc @@ -26,158 +26,12 @@ #include "vjournal.h" #include "environment.h" #include "trace.h" -#include "vformat.h" // comes from opensync, but not a public header yet #include #include #include #include - -////////////////////////////////////////////////////////////////////////////// -// vJournal - -vJournal::vJournal() - : m_gJournalData(0) -{ -} - -vJournal::~vJournal() -{ - if( m_gJournalData ) { - g_free(m_gJournalData); - } -} - -bool vJournal::HasMultipleVJournals() const -{ - int count = 0; - b_VFormat *format = const_cast(Format()); - GList *attrs = format ? b_vformat_get_attributes(format) : 0; - for( ; attrs; attrs = attrs->next ) { - b_VFormatAttribute *attr = (b_VFormatAttribute*) attrs->data; - if( strcasecmp(b_vformat_attribute_get_name(attr), "BEGIN") == 0 && - strcasecmp(b_vformat_attribute_get_nth_value(attr, 0), "VJOURNAL") == 0 ) - { - count++; - } - } - return count > 1; -} - - -// Main conversion routine for converting from Barry::Memo to -// a vJournal string of data. -const std::string& vJournal::ToMemo(const Barry::Memo &memo) -{ - Trace trace("vJournal::ToMemo"); - std::ostringstream oss; - memo.Dump(oss); - trace.logf("ToMemo, initial Barry record: %s", oss.str().c_str()); - - // start fresh - Clear(); - SetFormat( b_vformat_new() ); - if( !Format() ) - throw ConvertError("resource error allocating vformat"); - - // store the Barry object we're working with - m_BarryMemo = memo; - - // begin building vJournal data - AddAttr(NewAttr("PRODID", "-//OpenSync//NONSGML Barry Memo Record//EN")); - AddAttr(NewAttr("BEGIN", "VJOURNAL")); - AddAttr(NewAttr("SEQUENCE", "0")); - AddAttr(NewAttr("SUMMARY", memo.Title.c_str())); - AddAttr(NewAttr("DESCRIPTION", memo.Body.c_str())); - AddAttr(NewAttr("CATEGORIES", ToStringList(memo.Categories).c_str())); - - - // FIXME - add a truly globally unique "UID" string? - - AddAttr(NewAttr("END", "VJOURNAL")); - - // generate the raw VJOURNAL data - m_gJournalData = b_vformat_to_string(Format(), VFORMAT_NOTE); - m_vJournalData = m_gJournalData; - - trace.logf("ToMemo, resulting vjournal data: %s", m_vJournalData.c_str()); - return m_vJournalData; -} - -// Main conversion routine for converting from vJournal data string -// to a Barry::Memo object. -const Barry::Memo& vJournal::ToBarry(const char *vjournal, uint32_t RecordId) -{ - using namespace std; - - Trace trace("vJournal::ToBarry"); - trace.logf("ToBarry, working on vmemo data: %s", vjournal); - - // we only handle vJournal data with one vmemo block - if( HasMultipleVJournals() ) - throw ConvertError("vCalendar data contains more than one VJOURNAL block, unsupported"); - - // start fresh - Clear(); - - // store the vJournal raw data - m_vJournalData = vjournal; - - // create format parser structures - SetFormat( b_vformat_new_from_string(vjournal) ); - if( !Format() ) - throw ConvertError("resource error allocating vjournal"); - - string title = GetAttr("SUMMARY", "/vjournal"); - trace.logf("SUMMARY attr retrieved: %s", title.c_str()); - if( title.size() == 0 ) { - title = ""; - trace.logf("ERROR: bad data, blank SUMMARY: %s", vjournal); - } - - string body = GetAttr("DESCRIPTION", "/vjournal"); - trace.logf("DESCRIPTION attr retrieved: %s", body.c_str()); - - - // - // Now, run checks and convert into Barry object - // - - - Barry::Memo &rec = m_BarryMemo; - rec.SetIds(Barry::Memo::GetDefaultRecType(), RecordId); - - rec.Title = title; - rec.Body = body; - rec.Categories = GetValueVector("CATEGORIES","/vjournal"); - - std::ostringstream oss; - m_BarryMemo.Dump(oss); - trace.logf("ToBarry, resulting Barry record: %s", oss.str().c_str()); - return m_BarryMemo; -} - -// Transfers ownership of m_gMemoData to the caller. -char* vJournal::ExtractVJournal() -{ - char *ret = m_gJournalData; - m_gJournalData = 0; - return ret; -} - -void vJournal::Clear() -{ - vBase::Clear(); - m_vJournalData.clear(); - m_BarryMemo.Clear(); - - if( m_gJournalData ) { - g_free(m_gJournalData); - m_gJournalData = 0; - } -} - - +using namespace Barry::Sync; ////////////////////////////////////////////////////////////////////////////// // @@ -218,8 +72,8 @@ bool VJournalConverter::ParseData(const char *data) m_Memo = vjournal.ToBarry(data, m_RecordId); } - catch( vJournal::ConvertError &ce ) { - trace.logf("ERROR: vJournal::ConvertError exception: %s", ce.what()); + catch( Barry::ConvertError &ce ) { + trace.logf("ERROR: vjournal:Barry::ConvertError exception: %s", ce.what()); return false; } @@ -244,8 +98,8 @@ void VJournalConverter::operator()(const Barry::Memo &rec) m_Data = vjournal.ExtractVJournal(); } - catch( vJournal::ConvertError &ce ) { - trace.logf("ERROR: vJournal::ConvertError exception: %s", ce.what()); + catch( Barry::ConvertError &ce ) { + trace.logf("ERROR: vjournal:Barry::ConvertError exception: %s", ce.what()); } } diff --git a/opensync-plugin-0.4x/src/vjournal.h b/opensync-plugin-0.4x/src/vjournal.h index f0057364..3035ffd4 100644 --- a/opensync-plugin-0.4x/src/vjournal.h +++ b/opensync-plugin-0.4x/src/vjournal.h @@ -20,51 +20,18 @@ root directory of this project for more details. */ -#ifndef __BARRY_SYNC_VJOURNAL_H__ -#define __BARRY_SYNC_VJOURNAL_H__ +#ifndef __BARRYSYNC_VJOURNAL_H__ +#define __BARRYSYNC_VJOURNAL_H__ #include +#include #include #include -#include "vbase.h" -#include "vformat.h" // forward declarations class BarryEnvironment; - -// -// vJournal -// -/// Class for converting between RFC 2445 iCalendar data format, -/// and the Barry::Memo class. -/// -class vJournal : public vBase -{ - // data to pass to external requests - char *m_gJournalData; // dynamic memory returned by vformat()... can - // be used directly by the plugin, without - // overmuch allocation and freeing (see Extract()) - std::string m_vJournalData; // copy of m_gJournalData, for C++ use - Barry::Memo m_BarryMemo; - -protected: - bool HasMultipleVJournals() const; - -public: - vJournal(); - ~vJournal(); - - const std::string& ToMemo(const Barry::Memo &memo); - const Barry::Memo& ToBarry(const char *vjournal, uint32_t RecordId); - - char* ExtractVJournal(); - - void Clear(); -}; - - class VJournalConverter { char *m_Data; @@ -104,6 +71,5 @@ public: const char *data, bool add, std::string &errmsg); }; - #endif diff --git a/opensync-plugin-0.4x/src/vtodo.cc b/opensync-plugin-0.4x/src/vtodo.cc dissimilarity index 62% index 2af4b293..8e2f4c8b 100644 --- a/opensync-plugin-0.4x/src/vtodo.cc +++ b/opensync-plugin-0.4x/src/vtodo.cc @@ -1,446 +1,184 @@ -// -// \file vtodo.cc -// Conversion routines for vtodos (VCALENDAR, etc) -// - -/* - Copyright (C) 2008-2009, Nicolas VIVIEN - Copyright (C) 2006-2010, Net Direct Inc. (http://www.netdirect.ca/) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - - See the GNU General Public License in the COPYING file at the - root directory of this project for more details. -*/ - -#include -#include - -#include "vtodo.h" -#include "environment.h" -#include "trace.h" -#include "tosserror.h" -#include "vformat.h" // comes from opensync, but not a public header yet -#include -#include -#include -#include - - -////////////////////////////////////////////////////////////////////////////// -// Utility functions - -static void ToLower(std::string &str) -{ - size_t i = 0; - while( i < str.size() ) { - str[i] = tolower(str[i]); - i++; - } -} - -////////////////////////////////////////////////////////////////////////////// -// vTodo - -vTodo::vTodo() - : m_gTodoData(0) -{ -} - -vTodo::~vTodo() -{ - if( m_gTodoData ) { - g_free(m_gTodoData); - } -} - -bool vTodo::HasMultipleVTodos() const -{ - int count = 0; - b_VFormat *format = const_cast(Format()); - GList *attrs = format ? b_vformat_get_attributes(format) : 0; - for( ; attrs; attrs = attrs->next ) { - b_VFormatAttribute *attr = (b_VFormatAttribute*) attrs->data; - if( strcasecmp(b_vformat_attribute_get_name(attr), "BEGIN") == 0 && - strcasecmp(b_vformat_attribute_get_nth_value(attr, 0), "VTODO") == 0 ) - { - count++; - } - } - return count > 1; -} - - -// Main conversion routine for converting from Barry::Task to -// a vTodo string of data. -const std::string& vTodo::ToTask(const Barry::Task &task) -{ - Trace trace("vTodo::ToTask"); - std::ostringstream oss; - task.Dump(oss); - trace.logf("ToTask, initial Barry record: %s", oss.str().c_str()); - - // start fresh - Clear(); - SetFormat( b_vformat_new() ); - if( !Format() ) - throw ConvertError("resource error allocating vformat"); - - // store the Barry object we're working with - m_BarryTask = task; - - // begin building vCalendar data - AddAttr(NewAttr("PRODID", "-//OpenSync//NONSGML Barry Task Record//EN")); - AddAttr(NewAttr("BEGIN", "VTODO")); - AddAttr(NewAttr("SEQUENCE", "0")); - AddAttr(NewAttr("SUMMARY", task.Summary.c_str())); - AddAttr(NewAttr("DESCRIPTION", task.Notes.c_str())); - AddAttr(NewAttr("CATEGORIES", ToStringList(task.Categories).c_str())); - - // Status - if (task.StatusFlag == Barry::Task::InProgress) - AddAttr(NewAttr("STATUS", "IN-PROCESS")); - else if (task.StatusFlag == Barry::Task::Completed) - AddAttr(NewAttr("STATUS", "COMPLETED")); - else if (task.StatusFlag == Barry::Task::Deferred) - AddAttr(NewAttr("STATUS", "CANCELLED")); - else if (task.StatusFlag == Barry::Task::Waiting) - AddAttr(NewAttr("STATUS", "NEEDS-ACTION")); - - // Priority - if (task.PriorityFlag == Barry::Task::High) - AddAttr(NewAttr("PRIORITY", "3")); - else if (task.PriorityFlag == Barry::Task::Normal) - AddAttr(NewAttr("PRIORITY", "5")); - else - AddAttr(NewAttr("PRIORITY", "7")); - - // StartTime - if( task.StartTime ) { - TossError te("ToTask, StartTime 2 vtime", trace); - gStringPtr start(osync_time_unix2vtime(&task.StartTime, te)); - AddAttr(NewAttr("DTSTART", start.Get())); - } - - // DueTime DueFlag - if( task.DueDateFlag ) { - TossError te("ToTask, DueTime 2 vtime", trace); - gStringPtr due(osync_time_unix2vtime(&task.DueTime, te)); - AddAttr(NewAttr("DUE", due.Get())); - } - - // FIXME - add a truly globally unique "UID" string? - - AddAttr(NewAttr("END", "VTODO")); - - // generate the raw VTODO data - m_gTodoData = b_vformat_to_string(Format(), VFORMAT_TODO_20); - m_vTodoData = m_gTodoData; - - trace.logf("ToTask, resulting vtodo data: %s", m_vTodoData.c_str()); - return m_vTodoData; -} - -// Main conversion routine for converting from vTodo data string -// to a Barry::Task object. -const Barry::Task& vTodo::ToBarry(const char *vtodo, uint32_t RecordId) -{ - using namespace std; - - Trace trace("vTodo::ToBarry"); - trace.logf("ToBarry, working on vtodo data: %s", vtodo); - - // we only handle vTodo data with one vtodo block - if( HasMultipleVTodos() ) - throw ConvertError("vCalendar data contains more than one VTODO block, unsupported"); - - // start fresh - Clear(); - - // store the vTodo raw data - m_vTodoData = vtodo; - - // create format parser structures - SetFormat( b_vformat_new_from_string(vtodo) ); - if( !Format() ) - throw ConvertError("resource error allocating vtodo"); - - string summary = GetAttr("SUMMARY", "/vtodo"); - trace.logf("SUMMARY attr retrieved: %s", summary.c_str()); - if( summary.size() == 0 ) { - summary = ""; - trace.logf("ERROR: bad data, blank SUMMARY: %s", vtodo); - } - - string notes = GetAttr("DESCRIPTION", "/vtodo"); - trace.logf("DESCRIPTION attr retrieved: %s", notes.c_str()); - - string status = GetAttr("STATUS", "/vtodo"); - trace.logf("STATUS attr retrieved: %s", status.c_str()); - - string priority = GetAttr("PRIORITY", "/vtodo"); - trace.logf("PRIORITY attr retrieved: %s", priority.c_str()); - - string start = GetAttr("DTSTART", "/vtodo"); - trace.logf("DTSTART attr retrieved: %s", start.c_str()); - - string due = GetAttr("DUE", "/vtodo"); - trace.logf("DUE attr retrieved: %s", due.c_str()); - - - // - // Now, run checks and convert into Barry object - // - - // FIXME - we are assuming that any non-UTC timestamps - // in the vcalendar record will be in the current timezone... - // This is wrong! fix this later. - // - // Also, we current ignore any time zone - // parameters that might be in the vcalendar format... this - // must be fixed. - // - time_t now = time(NULL); - TossError te("ToBarry, local timezone detection", trace); - int zoneoffset = osync_time_timezone_diff(localtime(&now), te); - - - Barry::Task &rec = m_BarryTask; - rec.SetIds(Barry::Task::GetDefaultRecType(), RecordId); - - // Categories - - rec.Categories = GetValueVector("CATEGORIES","/vtodo"); - - // SUMMARY & DESCRIPTION fields - rec.Summary = summary; - rec.Notes = notes; - - // STATUS field - if (status.size()) { - ToLower(status); - - const char *s = status.c_str(); - - if (strstr(s, "in-process")) - rec.StatusFlag = Barry::Task::InProgress; - else if (strstr(s, "completed")) - rec.StatusFlag = Barry::Task::Completed; - else if (strstr(s, "cancelled")) - rec.StatusFlag = Barry::Task::Deferred; - else if (strstr(s, "needs-action")) - rec.StatusFlag = Barry::Task::Waiting; - else - rec.StatusFlag = Barry::Task::NotStarted; - } - - // PRIORITY field - if (priority.size()) { - ToLower(priority); - - const char *s = priority.c_str(); - - const int val = atoi(s); - - if (val < 4) - rec.PriorityFlag = Barry::Task::High; - else if (val < 7) - rec.PriorityFlag = Barry::Task::Normal; - else - rec.PriorityFlag = Barry::Task::Low; - } - - - // STARTTIME & DUETIME - if (start.size()) { - TossError te("ToBarry, StartTime 2 unix", trace); - rec.StartTime = osync_time_vtime2unix(start.c_str(), zoneoffset, te); - } - - if (due.size()) { - TossError te("ToBarry, DueTime 2 unix", trace); - rec.DueDateFlag = true; - rec.DueTime = osync_time_vtime2unix(due.c_str(), zoneoffset, te); - } - - std::ostringstream oss; - m_BarryTask.Dump(oss); - trace.logf("ToBarry, resulting Barry record: %s", oss.str().c_str()); - return m_BarryTask; -} - -// Transfers ownership of m_gTaskData to the caller. -char* vTodo::ExtractVTodo() -{ - char *ret = m_gTodoData; - m_gTodoData = 0; - return ret; -} - -void vTodo::Clear() -{ - vBase::Clear(); - m_vTodoData.clear(); - m_BarryTask.Clear(); - - if( m_gTodoData ) { - g_free(m_gTodoData); - m_gTodoData = 0; - } -} - - - -////////////////////////////////////////////////////////////////////////////// -// - -VTodoConverter::VTodoConverter() - : m_Data(0) -{ -} - -VTodoConverter::VTodoConverter(uint32_t newRecordId) - : m_Data(0), - m_RecordId(newRecordId) -{ -} - -VTodoConverter::~VTodoConverter() -{ - if( m_Data ) - g_free(m_Data); -} - -// Transfers ownership of m_Data to the caller -char* VTodoConverter::ExtractData() -{ - Trace trace("VTodoConverter::ExtractData"); - char *ret = m_Data; - m_Data = 0; - return ret; -} - -bool VTodoConverter::ParseData(const char *data) -{ - Trace trace("VTodoConverter::ParseData"); - - try { - - vTodo vtodo; - m_Task = vtodo.ToBarry(data, m_RecordId); - - } - catch( vTodo::ConvertError &ce ) { - trace.logf("ERROR: vTodo::ConvertError exception: %s", ce.what()); - return false; - } - - return true; -} - -// Barry storage operator -void VTodoConverter::operator()(const Barry::Task &rec) -{ - Trace trace("VTodoConverter::operator()"); - - // Delete data if some already exists - if( m_Data ) { - g_free(m_Data); - m_Data = 0; - } - - try { - - vTodo vtodo; - vtodo.ToTask(rec); - m_Data = vtodo.ExtractVTodo(); - - } - catch( vTodo::ConvertError &ce ) { - trace.logf("ERROR: vTodo::ConvertError exception: %s", ce.what()); - } -} - -// Barry builder operator -bool VTodoConverter::operator()(Barry::Task &rec, unsigned int dbId) -{ - Trace trace("VTodoConverter::builder operator()"); - - rec = m_Task; - return true; -} - -// Handles calling of the Barry::Controller to fetch a specific -// record, indicated by index (into the RecordStateTable). -// Returns a g_malloc'd string of data containing the vevent20 -// data. It is the responsibility of the caller to free it. -// This is intended to be passed into the GetChanges() function. -char* VTodoConverter::GetRecordData(BarryEnvironment *env, unsigned int dbId, - Barry::RecordStateTable::IndexType index) -{ - Trace trace("VTodoConverter::GetRecordData()"); - - using namespace Barry; - - VTodoConverter task2todo; - RecordParser parser(task2todo); - env->m_pDesktop->GetRecord(dbId, index, parser); - return task2todo.ExtractData(); -} - -bool VTodoConverter::CommitRecordData(BarryEnvironment *env, unsigned int dbId, - Barry::RecordStateTable::IndexType StateIndex, uint32_t recordId, - const char *data, bool add, std::string &errmsg) -{ - Trace trace("VTodoConverter::CommitRecordData()"); - - uint32_t newRecordId; - if( add ) { - // use given id if possible - if( recordId && !env->m_TodoSync.m_Table.GetIndex(recordId) ) { - // recordId is unique and non-zero - newRecordId = recordId; - } - else { - trace.log("Can't use recommended recordId, generating new one."); - newRecordId = env->m_TodoSync.m_Table.MakeNewRecordId(); - } - } - else { - newRecordId = env->m_TodoSync.m_Table.StateMap[StateIndex].RecordId; - } - trace.logf("newRecordId: %u", newRecordId); - - VTodoConverter convert(newRecordId); - if( !convert.ParseData(data) ) { - std::ostringstream oss; - oss << "unable to parse change data for new RecordId: " - << newRecordId << " data: " << data; - errmsg = oss.str(); - trace.log(errmsg.c_str()); - return false; - } - - Barry::RecordBuilder builder(convert); - - if( add ) { - trace.log("adding record"); - env->m_pDesktop->AddRecord(dbId, builder); - } - else { - trace.log("setting record"); - env->m_pDesktop->SetRecord(dbId, StateIndex, builder); - trace.log("clearing dirty flag"); - env->m_pDesktop->ClearDirty(dbId, StateIndex); - } - - return true; -} - +// +// \file vtodo.cc +// Conversion routines for vtodos (VCALENDAR, etc) +// + +/* + Copyright (C) 2008-2009, Nicolas VIVIEN + Copyright (C) 2006-2010, Net Direct Inc. (http://www.netdirect.ca/) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License in the COPYING file at the + root directory of this project for more details. +*/ + +#include +#include + +#include "vtodo.h" +#include "environment.h" +#include "trace.h" +#include "tosserror.h" +#include +#include +#include +#include + +using namespace Barry::Sync; + +////////////////////////////////////////////////////////////////////////////// +// + +VTodoConverter::VTodoConverter() + : m_Data(0) +{ +} + +VTodoConverter::VTodoConverter(uint32_t newRecordId) + : m_Data(0), + m_RecordId(newRecordId) +{ +} + +VTodoConverter::~VTodoConverter() +{ + if( m_Data ) + g_free(m_Data); +} + +// Transfers ownership of m_Data to the caller +char* VTodoConverter::ExtractData() +{ + Trace trace("VTodoConverter::ExtractData"); + char *ret = m_Data; + m_Data = 0; + return ret; +} + +bool VTodoConverter::ParseData(const char *data) +{ + Trace trace("VTodoConverter::ParseData"); + + try { + + vTimeConverter vtc; + vTodo vtodo(vtc); + m_Task = vtodo.ToBarry(data, m_RecordId); + + } + catch( Barry::ConvertError &ce ) { + trace.logf("ERROR: vtodo:Barry::ConvertError exception: %s", ce.what()); + return false; + } + + return true; +} + +// Barry storage operator +void VTodoConverter::operator()(const Barry::Task &rec) +{ + Trace trace("VTodoConverter::operator()"); + + // Delete data if some already exists + if( m_Data ) { + g_free(m_Data); + m_Data = 0; + } + + try { + + vTimeConverter vtc; + vTodo vtodo(vtc); + vtodo.ToTask(rec); + m_Data = vtodo.ExtractVTodo(); + + } + catch( Barry::ConvertError &ce ) { + trace.logf("ERROR: vtodo:Barry::ConvertError exception: %s", ce.what()); + } +} + +// Barry builder operator +bool VTodoConverter::operator()(Barry::Task &rec, unsigned int dbId) +{ + Trace trace("VTodoConverter::builder operator()"); + + rec = m_Task; + return true; +} + +// Handles calling of the Barry::Controller to fetch a specific +// record, indicated by index (into the RecordStateTable). +// Returns a g_malloc'd string of data containing the vevent20 +// data. It is the responsibility of the caller to free it. +// This is intended to be passed into the GetChanges() function. +char* VTodoConverter::GetRecordData(BarryEnvironment *env, unsigned int dbId, + Barry::RecordStateTable::IndexType index) +{ + Trace trace("VTodoConverter::GetRecordData()"); + + using namespace Barry; + + VTodoConverter task2todo; + RecordParser parser(task2todo); + env->m_pDesktop->GetRecord(dbId, index, parser); + return task2todo.ExtractData(); +} + +bool VTodoConverter::CommitRecordData(BarryEnvironment *env, unsigned int dbId, + Barry::RecordStateTable::IndexType StateIndex, uint32_t recordId, + const char *data, bool add, std::string &errmsg) +{ + Trace trace("VTodoConverter::CommitRecordData()"); + + uint32_t newRecordId; + if( add ) { + // use given id if possible + if( recordId && !env->m_TodoSync.m_Table.GetIndex(recordId) ) { + // recordId is unique and non-zero + newRecordId = recordId; + } + else { + trace.log("Can't use recommended recordId, generating new one."); + newRecordId = env->m_TodoSync.m_Table.MakeNewRecordId(); + } + } + else { + newRecordId = env->m_TodoSync.m_Table.StateMap[StateIndex].RecordId; + } + trace.logf("newRecordId: %u", newRecordId); + + VTodoConverter convert(newRecordId); + if( !convert.ParseData(data) ) { + std::ostringstream oss; + oss << "unable to parse change data for new RecordId: " + << newRecordId << " data: " << data; + errmsg = oss.str(); + trace.log(errmsg.c_str()); + return false; + } + + Barry::RecordBuilder builder(convert); + + if( add ) { + trace.log("adding record"); + env->m_pDesktop->AddRecord(dbId, builder); + } + else { + trace.log("setting record"); + env->m_pDesktop->SetRecord(dbId, StateIndex, builder); + trace.log("clearing dirty flag"); + env->m_pDesktop->ClearDirty(dbId, StateIndex); + } + + return true; +} + diff --git a/opensync-plugin-0.4x/src/vtodo.h b/opensync-plugin-0.4x/src/vtodo.h index a3b47876..bf6b2c2a 100644 --- a/opensync-plugin-0.4x/src/vtodo.h +++ b/opensync-plugin-0.4x/src/vtodo.h @@ -20,50 +20,17 @@ root directory of this project for more details. */ -#ifndef __BARRY_SYNC_VTODO_H__ -#define __BARRY_SYNC_VTODO_H__ +#ifndef __BARRYSYNC_VTODO_H__ +#define __BARRYSYNC_VTODO_H__ #include +#include #include #include -#include "vbase.h" -#include "vformat.h" - // forward declarations class BarryEnvironment; -// -// vTodo -// -/// Class for converting between RFC 2445 iCalendar data format, -/// and the Barry::Task class. -/// -class vTodo : public vBase -{ - // data to pass to external requests - char *m_gTodoData; // dynamic memory returned by vformat()... can - // be used directly by the plugin, without - // overmuch allocation and freeing (see Extract()) - std::string m_vTodoData; // copy of m_gJournalData, for C++ use - Barry::Task m_BarryTask; - -protected: - bool HasMultipleVTodos() const; - -public: - vTodo(); - ~vTodo(); - - const std::string& ToTask(const Barry::Task &task); - const Barry::Task& ToBarry(const char *vtodo, uint32_t RecordId); - - char* ExtractVTodo(); - - void Clear(); -}; - - class VTodoConverter { char *m_Data; @@ -103,6 +70,5 @@ public: const char *data, bool add, std::string &errmsg); }; - #endif -- 2.11.4.GIT