(Pardon my dust if I'm not following patch protocol exactly)

Attached is a patch file, and the associated files that were changed, to
support birthday in the Barry::Contact and for syncing in the opensync
plugin.

Regarding OpenSync... it seems that adding support for a new field does
not automatically cause that new data to be sync'd... makes sense
because it doesn't appear as though the record changed.

To see the birthdays I had in evo-2 show up, I had to clear my BB
contacts and the opensync group and start over to get the birthdays
transferred.

-Lee


On Wed, 2008-08-06 at 17:30 -0400, Chris Frey wrote:
> On Wed, Aug 06, 2008 at 03:30:14PM -0400, Lee Dixon wrote:
> > Hi all!
> > First of all, BIG HUGE THANKS for Barry.  I use it to sync with evo2 and
> > things work pretty dang well.  I am using Barry 0.13 and OpenSync 0.22
> > on Gutsy with a Blackberry Pearl 8130.
> > 
> > Of course, as a programmer, I want to improve and fix stuff.  I found a
> > couple issues and have some resolutions:
> 
> Welcome to Barry!  Glad to hear Barry is working well for you.
> 
> 
> > 1) Endless loop in vCard::ParseCategories()
> > I'm surprised that no one else has seen this: if I have an evo contact
> > with a category set, then the call to vAttr.GetValue(index) will keep
> > returning the same value as the index increases unbounded.  This causes
> > an endless loop and unbounded memory consumption, causing the OS to
> > gobble up the swap and the machine slows heavily.  I put a simple check
> > in to make sure the value returned doesn't match the previous one, but I
> > don't think that is the long-term answer.  vAttr should be fixed.
> > Anybody else see this?
> 
> Well, a huge thanks right back at you! :-)  People have been reporting this
> memory consumption bug for a while, and I haven't been able to find it.
> With this report, I've added the one-liner fix to vbase.cc, and it is
> now in the latest CVS (and git).
> 
> 
> > 2) No support for Birthday field.  I added this quite easily thanks to
> > debug data from btool.  Unfortunately, birthdays don't show up in the
> > Blackberry calendar like they do when you *author* the birthday entry on
> > the device.
> > 
> > 3) No anniversary date support.  I think I can add Anniversary support
> > too like Birthday.
> 
> Please send a patch with what you have, so we can see and discuss it better.
> I've found that sometimes calendar records tend to use special flags,
> for recurring, "all day event", etc.  Birthday and Anniversary might just
> be flags similar to this.
> 
> 
> > 4) I did a calendar sync and recurring events didn't seem to work right.
> > In vevent.cc I see:
> > void vCalendar::RecurToBarryCal()
> > {
> >         // FIXME - needs to be implemented
> > 
> >         // GetWeekDayIndex()
> > }
> > 
> > But I don't see this in the TODO list from CVS.  Anybody else working
> > this?  Can I help out?
> 
> I've added this to the TODO list.  In fact, it is very safe to assume that
> anything in the code marked "FIXME" is a todo list item by default. :-)
> 
> And yes you can definitely help out!  No one else is working on the
> calendar part of the plugin right now.
> 
> You can send patches to the mailing list, or you can use the Barry
> git repository if you prefer working that way, and then let me know
> where to pull from.  You can find more info here:
> 
>       http://www.netdirect.ca/software/packages/barry/patches.php
> 
> - Chris
> 
> 
> -------------------------------------------------------------------------
> This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
> Build the coolest Linux based applications with Moblin SDK & win great prizes
> Grand prize is a trip for two to an Open Source event anywhere in the world
> http://moblin-contest.org/redirect.php?banner_id=100&url=/
> _______________________________________________
> Barry-devel mailing list
> Barry-devel@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/barry-devel
diff -Naur barry-0.13.old/opensync-plugin/src/vcard.cc barry-0.13.new/opensync-plugin/src/vcard.cc
--- barry-0.13.old/opensync-plugin/src/vcard.cc	2008-07-24 18:20:32.000000000 -0400
+++ barry-0.13.new/opensync-plugin/src/vcard.cc	2008-08-08 17:29:56.000000000 -0400
@@ -124,6 +124,43 @@
 	}
 }
 
+
+// Parse a Barry date in the form of DD/MM/YY into the format
+// for VCard YYYYMMDD
+std::string vCard::ParseBarryDate( const std::string& s )
+{
+  std::string dateStr;
+  int m, d, y;
+  if( 3==sscanf( s.c_str(), "%d/%d/%d", &d, &m, &y ) )
+  {
+    char buf[32];
+    sprintf( buf, "%4d%02d%02d", y, m, d );
+    dateStr.assign( buf );
+  }
+  return dateStr;
+}
+
+
+// Parse a VCard date in the form of YYYYMMDD into the format
+// for Barry DD/MM/YY
+std::string vCard::ParseVCardDate( const std::string& s )
+{
+  std::string dateStr;
+  if( s.size() )
+  {
+    int m, d, y;
+    if( 3==sscanf( s.c_str(), "%4d%2d%2d", &y, &m, &d ) )
+    {
+      char buf[32];
+      sprintf( buf, "%02d/%02d/%04d", d, m, y );
+      dateStr.assign( buf );
+    }
+  }
+  return dateStr;
+}
+
+
+
 // Main conversion routine for converting from Barry::Contact to
 // a vCard string of data.
 const std::string& vCard::ToVCard(const Barry::Contact &con)
@@ -207,6 +244,9 @@
 		AddAttr(org);
 	}
 
+        if( con.Birthday.size() )
+          AddAttr(NewAttr("BDAY", ParseBarryDate( con.Birthday ).c_str() ));
+
 	if( con.Notes.size() )
 		AddAttr(NewAttr("NOTE", con.Notes.c_str()));
 	if( con.URL.size() )
@@ -325,6 +365,7 @@
 	con.Company = GetAttr("ORG");
 	con.Notes = GetAttr("NOTE");
 	con.URL = GetAttr("URL");
+        con.Birthday = ParseVCardDate( GetAttr("BDAY") );
 
 	vAttr cat = GetAttrObj("CATEGORIES");
 	if( cat.Get() )
diff -Naur barry-0.13.old/opensync-plugin/src/vcard.h barry-0.13.new/opensync-plugin/src/vcard.h
--- barry-0.13.old/opensync-plugin/src/vcard.h	2008-01-25 22:57:31.000000000 -0500
+++ barry-0.13.new/opensync-plugin/src/vcard.h	2008-08-08 17:30:03.000000000 -0400
@@ -55,6 +55,8 @@
 	void ParseAddress(vAttr &adr, Barry::PostalAddress &address);
 	void ParseCategories(vAttr &cat, Barry::CategoryList &cats);
 
+        std::string ParseBarryDate( const std::string& s );
+        std::string ParseVCardDate( const std::string& s );
 public:
 	vCard();
 	~vCard();
diff -Naur barry-0.13.old/src/r_contact.cc barry-0.13.new/src/r_contact.cc
--- barry-0.13.old/src/r_contact.cc	2008-07-24 18:20:30.000000000 -0400
+++ barry-0.13.new/src/r_contact.cc	2008-08-08 17:30:25.000000000 -0400
@@ -90,6 +90,7 @@
 #define CFC_HOME_POSTAL_CODE	71	// 0x47
 #define CFC_HOME_COUNTRY	72	// 0x48
 #define CFC_IMAGE		77	// 0x4D
+#define CFC_BIRTHDAY		82	// 0x52
 #define CFC_INVALID_FIELD	255
 
 // Contact code to field table
@@ -131,6 +132,7 @@
    { CFC_HOME_POSTAL_CODE, "HomePostalCode", 0,0,         0, 0, 0, &Contact::HomeAddress, &PostalAddress::PostalCode, },
    { CFC_HOME_COUNTRY, "HomeCountry",0,0,                 0, 0, 0, &Contact::HomeAddress, &PostalAddress::Country, },
    { CFC_IMAGE,        "Image",      0,0,                 &Contact::Image, 0, 0 },
+   { CFC_BIRTHDAY,     "Birthday",      0,0,              &Contact::Birthday, 0, 0 },
    { CFC_INVALID_FIELD,"EndOfList",  0, 0, 0 }
 };
 
@@ -363,6 +365,7 @@
 	UserDefined3.clear();
 	UserDefined4.clear();
 	Image.clear();
+        Birthday.clear();
 
 	WorkAddress.Clear();
 	HomeAddress.Clear();
diff -Naur barry-0.13.old/src/r_contact.h barry-0.13.new/src/r_contact.h
--- barry-0.13.old/src/r_contact.h	2008-07-24 18:20:30.000000000 -0400
+++ barry-0.13.new/src/r_contact.h	2008-08-08 17:30:16.000000000 -0400
@@ -92,7 +92,8 @@
 		UserDefined2,
 		UserDefined3,
 		UserDefined4,
-		Image;
+		Image,
+                Birthday;
 
 	PostalAddress WorkAddress;
 	PostalAddress HomeAddress;
///
/// \file	r_contact.cc
///		Blackberry database record parser class for contact records.
///

/*
    Copyright (C) 2005-2008, 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 "r_contact.h"
#include "record-internal.h"
#include "protocol.h"
#include "protostructs.h"
#include "data.h"
#include "time.h"
#include "error.h"
#include "endian.h"
#include <ostream>
#include <iomanip>
#include <time.h>
#include <stdexcept>

#define __DEBUG_MODE__
#include "debug.h"

using namespace std;
using namespace Barry::Protocol;

namespace Barry {



///////////////////////////////////////////////////////////////////////////////
// Contact class

// Contact field codes
#define CFC_EMAIL		1
#define CFC_PHONE		2
#define CFC_FAX			3
#define CFC_WORK_PHONE		6
#define CFC_HOME_PHONE		7
#define CFC_MOBILE_PHONE	8
#define CFC_PAGER		9
#define CFC_PIN			10
#define CFC_RADIO		14	// 0x0e
#define CFC_WORK_PHONE_2	16	// 0x10
#define CFC_HOME_PHONE_2	17	// 0x11
#define CFC_OTHER_PHONE		18	// 0x12
#define CFC_NAME		32	// 0x20 used twice, in first/last name order
#define CFC_COMPANY		33
#define CFC_DEFAULT_COMM_METHOD	34
#define CFC_ADDRESS1		35
#define CFC_ADDRESS2		36
#define CFC_ADDRESS3		37
#define CFC_CITY		38
#define CFC_PROVINCE		39
#define CFC_POSTAL_CODE		40
#define CFC_COUNTRY		41
#define CFC_TITLE		42	// 0x2a
#define CFC_PUBLIC_KEY		43
#define CFC_GROUP_FLAG		44
#define CFC_GROUP_LINK		52
#define CFC_URL			54	// 0x36
#define CFC_PREFIX		55	// 0x37
#define CFC_CATEGORY		59	// 0x3B
#define CFC_HOME_ADDRESS1	61	// 0x3D
#define CFC_HOME_ADDRESS2	62	// 0x3E
  // If the address 3 isn't mapped then it appears
  // in the same field as address2 with a space
#define CFC_HOME_ADDRESS3	63 	// 0x3F
#define CFC_NOTES		64	// 0x40
#define CFC_USER_DEFINED_1	65	// 0x41
#define CFC_USER_DEFINED_2	66	// 0x42
#define CFC_USER_DEFINED_3	67	// 0x43
#define CFC_USER_DEFINED_4	68	// 0x44
#define CFC_HOME_CITY		69	// 0x45
#define CFC_HOME_PROVINCE	70	// 0x46
#define CFC_HOME_POSTAL_CODE	71	// 0x47
#define CFC_HOME_COUNTRY	72	// 0x48
#define CFC_IMAGE		77	// 0x4D
#define CFC_BIRTHDAY		82	// 0x52
#define CFC_INVALID_FIELD	255

// Contact code to field table
FieldLink<Contact> ContactFieldLinks[] = {
   { CFC_PHONE,        "Phone",      0,0,                 &Contact::Phone, 0, 0 },
   { CFC_FAX,          "Fax",        "facsimileTelephoneNumber",0, &Contact::Fax, 0, 0 },
   { CFC_WORK_PHONE,   "WorkPhone",  "telephoneNumber",0, &Contact::WorkPhone, 0, 0 },
   { CFC_HOME_PHONE,   "HomePhone",  "homePhone",0,       &Contact::HomePhone, 0, 0 },
   { CFC_MOBILE_PHONE, "MobilePhone","mobile",0,          &Contact::MobilePhone, 0, 0 },
   { CFC_PAGER,        "Pager",      "pager",0,           &Contact::Pager, 0, 0 },
   { CFC_PIN,          "PIN",        0,0,                 &Contact::PIN, 0, 0 },
   { CFC_RADIO,        "Radio",      0,0,                 &Contact::Radio, 0, 0 },
   { CFC_WORK_PHONE_2, "WorkPhone2", 0,0,                 &Contact::WorkPhone2, 0, 0 },
   { CFC_HOME_PHONE_2, "HomePhone2", 0,0,                 &Contact::HomePhone2, 0, 0 },
   { CFC_OTHER_PHONE,  "OtherPhone", 0,0,                 &Contact::OtherPhone, 0, 0 },
   { CFC_COMPANY,      "Company",    "o",0,               &Contact::Company, 0, 0 },
   { CFC_DEFAULT_COMM_METHOD,"DefaultCommMethod",0,0,     &Contact::DefaultCommunicationsMethod, 0, 0 },
   { CFC_ADDRESS1,     "WorkAddress1",   0,0,             0, 0, 0, &Contact::WorkAddress, &PostalAddress::Address1 },
   { CFC_ADDRESS2,     "WorkAddress2",   0,0,             0, 0, 0, &Contact::WorkAddress, &PostalAddress::Address2 },
   { CFC_ADDRESS3,     "WorkAddress3",   0,0,             0, 0, 0, &Contact::WorkAddress, &PostalAddress::Address3 },
   { CFC_CITY,         "WorkCity",       "l",0,           0, 0, 0, &Contact::WorkAddress, &PostalAddress::City },
   { CFC_PROVINCE,     "WorkProvince",   "st",0,          0, 0, 0, &Contact::WorkAddress, &PostalAddress::Province },
   { CFC_POSTAL_CODE,  "WorkPostalCode", "postalCode",0,  0, 0, 0, &Contact::WorkAddress, &PostalAddress::PostalCode },
   { CFC_COUNTRY,      "WorkCountry",    "c", "country",  0, 0, 0, &Contact::WorkAddress, &PostalAddress::Country },
   { CFC_TITLE,        "JobTitle",   "title",0,           &Contact::JobTitle, 0, 0 },
   { CFC_PUBLIC_KEY,   "PublicKey",  0,0,                 &Contact::PublicKey, 0, 0 },
   { CFC_URL,          "URL",        0,0,                 &Contact::URL, 0, 0 },
   { CFC_PREFIX,       "Prefix",     0,0,                 &Contact::Prefix, 0, 0 },
   { CFC_HOME_ADDRESS1,"HomeAddress1", 0,0,               0, 0, 0, &Contact::HomeAddress, &PostalAddress::Address1, },
   { CFC_HOME_ADDRESS2,"HomeAddress2", 0,0,               0, 0, 0, &Contact::HomeAddress, &PostalAddress::Address2, },
   { CFC_HOME_ADDRESS3,"HomeAddress3", 0,0,               0, 0, 0, &Contact::HomeAddress, &PostalAddress::Address3, },
   { CFC_NOTES,        "Notes",      0,0,                 &Contact::Notes, 0, 0 },
   { CFC_USER_DEFINED_1, "UserDefined1", 0,0,             &Contact::UserDefined1, 0, 0 },
   { CFC_USER_DEFINED_2, "UserDefined2", 0,0,             &Contact::UserDefined2, 0, 0 },
   { CFC_USER_DEFINED_3, "UserDefined3", 0,0,             &Contact::UserDefined3, 0, 0 },
   { CFC_USER_DEFINED_4, "UserDefined4", 0,0,             &Contact::UserDefined4, 0, 0 },
   { CFC_HOME_CITY,    "HomeCity",   0,0,                 0, 0, 0, &Contact::HomeAddress, &PostalAddress::City, },
   { CFC_HOME_PROVINCE,"HomeProvince", 0,0,               0, 0, 0, &Contact::HomeAddress, &PostalAddress::Province, },
   { CFC_HOME_POSTAL_CODE, "HomePostalCode", 0,0,         0, 0, 0, &Contact::HomeAddress, &PostalAddress::PostalCode, },
   { CFC_HOME_COUNTRY, "HomeCountry",0,0,                 0, 0, 0, &Contact::HomeAddress, &PostalAddress::Country, },
   { CFC_IMAGE,        "Image",      0,0,                 &Contact::Image, 0, 0 },
   { CFC_BIRTHDAY,     "Birthday",      0,0,              &Contact::Birthday, 0, 0 },
   { CFC_INVALID_FIELD,"EndOfList",  0, 0, 0 }
};

Contact::Contact()
	: RecType(Contact::GetDefaultRecType()),
	RecordId(0),
	m_FirstNameSeen(false)
{
}

Contact::~Contact()
{
}

const unsigned char* Contact::ParseField(const unsigned char *begin,
					 const unsigned char *end)
{
	const CommonField *field = (const CommonField *) begin;

	// advance and check size
	begin += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
	if( begin > end )		// if begin==end, we are ok
		return begin;

	if( !btohs(field->size) )	// if field has no size, something's up
		return begin;

	// cycle through the type table
	for(	FieldLink<Contact> *b = ContactFieldLinks;
		b->type != CFC_INVALID_FIELD;
		b++ )
	{
		if( b->type == field->type ) {
			if( b->strMember ) {
				std::string &s = this->*(b->strMember);
				s = ParseFieldString(field);
				return begin;	// done!
			}
			else if( b->postMember && b->postField ) {
				std::string &s = (this->*(b->postMember)).*(b->postField);
				s = ParseFieldString(field);
				return begin;
			}
			else {
				break;	// fall through to special handling
			}
		}
	}

	// if not found in the type table, check for special handling
	switch( field->type )
	{
	case CFC_EMAIL: {
		EmailAddresses.push_back( ParseFieldString(field) );
		}
		return begin;

	case CFC_NAME: {
		// can be used multiple times, for first/last names
		std::string *name;
		if( FirstName.size() || m_FirstNameSeen ) {
			// first name already filled, use last name
			name = &LastName;
			m_FirstNameSeen = false;
		}
		else {
			name = &FirstName;
			m_FirstNameSeen = true;
		}

		*name = ParseFieldString(field);
		}
		return begin;

	case CFC_GROUP_LINK:
		// just add the unique ID to the list
		GroupLinks.push_back(
			GroupLink(field->u.link.uniqueId,
				field->u.link.unknown));
		return begin;

	case CFC_GROUP_FLAG:
		// ignore the group flag... the presense of group link items
		// behaves as the flag in this class
		return begin;

	case CFC_CATEGORY: {
		std::string catstring = ParseFieldString(field);
		CategoryStr2List(catstring, Categories);
		}
		return begin;
	}

	// if still not handled, add to the Unknowns list
	UnknownField uf;
	uf.type = field->type;
	uf.data.assign((const char*)field->u.raw, btohs(field->size));
	Unknowns.push_back(uf);

	// return new pointer for next field
	return begin;
}

void Contact::ParseHeader(const Data &data, size_t &offset)
{
	// no header to parse in Contact records
}

// this is called by the RecordParser<> class, which checks size for us
void Contact::ParseFields(const Data &data, size_t &offset)
{
	const unsigned char *finish = ParseCommonFields(*this,
		data.GetData() + offset, data.GetData() + data.GetSize());
	offset += finish - (data.GetData() + offset);
}

void Contact::BuildHeader(Data &data, size_t &offset) const
{
	// no header in Contact records
}

//
// BuildFields
//
/// Build fields part of record
///
void Contact::BuildFields(Data &data, size_t &offset) const
{
	data.Zap();

	// check if this is a group link record, and if so, output
	// the group flag
	if( GroupLinks.size() )
		BuildField(data, offset, CFC_GROUP_FLAG, 'G');

	// special fields not in type table
	if( FirstName.size() )
		BuildField(data, offset, CFC_NAME, FirstName);
	if( LastName.size() ) {
		if( !FirstName.size() ) {
			// order matters with first/last name, and if
			// last name exists, and first name doesn't,
			// insert blank first name ahead of it
			BuildField(data, offset, CFC_NAME, FirstName);
		}
		BuildField(data, offset, CFC_NAME, LastName);
	}

	// add all email addresses
	EmailList::const_iterator eai = EmailAddresses.begin();
	for( ; eai != EmailAddresses.end(); ++eai ) {
		if( eai->size() ) {
			BuildField(data, offset, CFC_EMAIL, *eai);
		}
	}

	// cycle through the type table
	for(	FieldLink<Contact> *b = ContactFieldLinks;
		b->type != CFC_INVALID_FIELD;
		b++ )
	{
		// print only fields with data
		if( b->strMember ) {
			const std::string &field = this->*(b->strMember);
			if( field.size() ) {
				BuildField(data, offset, b->type, field);
			}
		}
		else if( b->postMember && b->postField ) {
			const std::string &field = (this->*(b->postMember)).*(b->postField);
			if( field.size() ) {
				BuildField(data, offset, b->type, field);
			}
		}
	}

	// save any group links
	GroupLinksType::const_iterator
		gb = GroupLinks.begin(), ge = GroupLinks.end();
	for( ; gb != ge; gb++ ) {
		Barry::Protocol::GroupLink link;
		link.uniqueId = htobl(gb->Link);
		link.unknown = htobs(gb->Unknown);
		BuildField(data, offset, CFC_GROUP_LINK, link);
	}

	if( Categories.size() ) {
		string store;
		CategoryList2Str(Categories, store);
		BuildField(data, offset, CFC_CATEGORY, store);
	}

	// and finally save unknowns
	UnknownsType::const_iterator
		ub = Unknowns.begin(), ue = Unknowns.end();
	for( ; ub != ue; ub++ ) {
		BuildField(data, offset, *ub);
	}

	data.ReleaseBuffer(offset);
}

void Contact::Clear()
{
	RecType = Contact::GetDefaultRecType();

	EmailAddresses.clear();
	Phone.clear();
	Fax.clear();
	WorkPhone.clear();
	HomePhone.clear();
	MobilePhone.clear();
	Pager.clear();
	PIN.clear();
	Radio.clear();
	WorkPhone2.clear();
	HomePhone2.clear();
	OtherPhone.clear();
	FirstName.clear();
	LastName.clear();
	Company.clear();
	DefaultCommunicationsMethod.clear();
	JobTitle.clear();
	PublicKey.clear();
	URL.clear();
	Prefix.clear();
	Notes.clear();
	UserDefined1.clear();
	UserDefined2.clear();
	UserDefined3.clear();
	UserDefined4.clear();
	Image.clear();
        Birthday.clear();

	WorkAddress.Clear();
	HomeAddress.Clear();

	Categories.clear();

 	GroupLinks.clear();
	Unknowns.clear();

	m_FirstNameSeen = false;
}

//
// GetFullName
//
/// Helper function that returns a formatted full name
///
std::string Contact::GetFullName() const
{
	std::string Full = FirstName;
	if( Full.size() && LastName.size() )
		Full += " ";
	Full += LastName;
	return Full;
}

//
// GetEmail
//
/// Helper function that always returns a valid string.  The string
/// may be empty if there is no address at the specified index.
///
const std::string& Contact::GetEmail(unsigned int index) const
{
	static const std::string blank;
	if( index < EmailAddresses.size() )
		return EmailAddresses[index];
	return blank;
}

void Contact::Dump(std::ostream &os) const
{
	ios::fmtflags oldflags = os.setf(ios::left);
	char fill = os.fill(' ');

	os << "Contact: 0x" << setbase(16) << GetID()
		<< " (" << (unsigned int)RecType << ")\n";

	// special fields not in type table
	os << "    " << setw(20) << "FirstName";
	os << ": " << FirstName << "\n";
	os << "    " << setw(20) << "LastName";
	os << ": " << LastName << "\n";

	// cycle through email addresses
	EmailList::const_iterator eai = EmailAddresses.begin();
	for( ; eai != EmailAddresses.end(); ++eai ) {
		if( eai->size() ) {
			os << "    Email               : " << *eai << "\n";
		}
	}

	// cycle through the type table
	for(	FieldLink<Contact> *b = ContactFieldLinks;
		b->type != CFC_INVALID_FIELD;
		b++ )
	{
		const std::string *pField = 0;
		if( b->strMember ) {
			pField = &(this->*(b->strMember));
		}
		else if( b->postMember && b->postField ) {
			pField = &((this->*(b->postMember)).*(b->postField));
		}

		// print only fields with data
		if( pField && pField->size() ) {
			os << "    " << setw(20) << b->name;
			os << ": " << *pField << "\n";
		}
	}

	if( Categories.size() ) {
		string display;
		CategoryList2Str(Categories, display);
		os << "    Categories          : " << display << "\n";
	}

	// print any group links
	GroupLinksType::const_iterator
		gb = GroupLinks.begin(), ge = GroupLinks.end();
	if( gb != ge )
		os << "    GroupLinks:\n";
	for( ; gb != ge; gb++ ) {
		os << "        ID: 0x" << setbase(16) << gb->Link << "\n";
	}

	// and finally print unknowns
	os << Unknowns;

	// cleanup the stream
	os.flags(oldflags);
	os.fill(fill);
}

void Contact::SplitName(const std::string &full, std::string &first, std::string &last)
{
	first.clear();
	last.clear();

	string::size_type pos = full.find_last_of(' ');
	if( pos != string::npos ) {
		// has space, assume last word is last name
		last = full.c_str() + pos + 1;
		first = full.substr(0, pos);
	}
	else {
		// no space, assume only first name
		first = full.substr(0);
	}
}

void Contact::CategoryStr2List(const std::string &str,
			       Barry::CategoryList &list)
{
	// start fresh
	list.clear();

	if( !str.size() )
		return;

	// parse the comma-delimited string to a list, stripping away
	// any white space around each category name
	string::size_type start = 0, end = 0, delim = str.find(',', start);
	while( start != string::npos ) {
		if( delim == string::npos )
			end = str.size() - 1;
		else
			end = delim - 1;

		// strip surrounding whitespace
		while( str[start] == ' ' )
			start++;
		while( end && str[end] == ' ' )
			end--;

		if( start <= end ) {
			string token = str.substr(start, end-start+1);
			list.push_back(token);
		}

		// next
		start = delim;
		if( start != string::npos )
			start++;
		delim = str.find(',', start);
	}
}

void Contact::CategoryList2Str(const Barry::CategoryList &list,
			       std::string &str)
{
	str.clear();

	Barry::CategoryList::const_iterator i = list.begin();
	for( ; i != list.end(); ++i ) {
		if( str.size() )
			str += ", ";
		str += *i;
	}
}

} // namespace Barry

///
/// \file	r_contact.h
///		Blackberry database record parser class for contact records.
///

/*
    Copyright (C) 2005-2008, 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_RECORD_CONTACT_H__
#define __BARRY_RECORD_CONTACT_H__

#include "dll.h"
#include "record.h"
#include <iosfwd>
#include <string>
#include <vector>
#include <map>
#include <stdint.h>

namespace Barry {

//
// NOTE:  All classes here must be container-safe!  Perhaps add sorting
//        operators in the future.
//

struct BXEXPORT ContactGroupLink
{
	uint32_t Link;
	uint16_t Unknown;

	ContactGroupLink() : Link(0), Unknown(0) {}
	ContactGroupLink(uint32_t link, uint16_t unknown)
		: Link(link), Unknown(unknown)
	{}
};

typedef std::vector<std::string> CategoryList;

/// \addtogroup RecordParserClasses
/// @{

class BXEXPORT Contact
{
public:
	typedef Barry::CategoryList			CategoryList;
	typedef ContactGroupLink			GroupLink;
	typedef std::vector<GroupLink>			GroupLinksType;
	typedef std::vector<UnknownField>		UnknownsType;
	typedef std::string				EmailType;
	typedef std::vector<EmailType>			EmailList;

	// contact specific data
	uint8_t RecType;
	uint32_t RecordId;
	EmailList EmailAddresses;
	std::string
		Phone,
		Fax,
		WorkPhone,
		HomePhone,
		MobilePhone,
		Pager,
		PIN,
		Radio,
		WorkPhone2,
		HomePhone2,
		OtherPhone,
		FirstName,
		LastName,
		Company,
		DefaultCommunicationsMethod,
		JobTitle,
		PublicKey,
		URL,
		Prefix,
		Notes,
		UserDefined1,
		UserDefined2,
		UserDefined3,
		UserDefined4,
		Image,
                Birthday;

	PostalAddress WorkAddress;
	PostalAddress HomeAddress;

	// Categories are not allowed to have commas in them.
	// A category name containing a comma will be split into
	// two categories, not only by this library, but by the
	// device itself.
	CategoryList Categories;

	GroupLinksType GroupLinks;
	UnknownsType Unknowns;

private:
	bool m_FirstNameSeen;

//protected:
public:
	const unsigned char* ParseField(const unsigned char *begin,
		const unsigned char *end);

public:
	Contact();
	~Contact();

	uint32_t GetID() const { return RecordId; }
	std::string GetFullName() const;
	const std::string& GetEmail(unsigned int index = 0) const;

	// Parser / Builder API (see parser.h / builder.h)
	uint8_t GetRecType() const { return RecType; }
	uint32_t GetUniqueId() const { return RecordId; }
	void SetIds(uint8_t Type, uint32_t Id) { RecType = Type; RecordId = Id; }
	void ParseHeader(const Data &data, size_t &offset);
	void ParseFields(const Data &data, size_t &offset);
	void BuildHeader(Data &data, size_t &offset) const;
	void BuildFields(Data &data, size_t &offset) const;

	void Clear();			// erase everything

	void Dump(std::ostream &os) const;

	// sorting - put group links at the end
	bool operator<(const Contact &other) const {
		return GroupLinks.size() == 0 && other.GroupLinks.size() > 0;
//		// testing - put group links at the top
//		return GroupLinks.size() > 0 && other.GroupLinks.size() == 0;
	}

	// database name
	static const char * GetDBName() { return "Address Book"; }
	static uint8_t GetDefaultRecType() { return 0; }

	// helpers
	static void SplitName(const std::string &full, std::string &first, std::string &last);
	static void CategoryStr2List(const std::string &str, Barry::CategoryList &list);
	static void CategoryList2Str(const Barry::CategoryList &list, std::string &str);
};

BXEXPORT inline std::ostream& operator<< (std::ostream &os, const Contact &contact) {
	contact.Dump(os);
	return os;
}

/// @}

} // namespace Barry

#endif

///
/// \file	vcard.cc
///		Conversion routines for vcards
///

/*
    Copyright (C) 2006-2008, 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 <stdint.h>
#include <glib.h>
#include <string.h>
#include <sstream>
#include <ctype.h>


//////////////////////////////////////////////////////////////////////////////
// Utility functions

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
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);
	}
}


// Parse a Barry date in the form of DD/MM/YY into the format
// for VCard YYYYMMDD
std::string vCard::ParseBarryDate( const std::string& s )
{
  std::string dateStr;
  int m, d, y;
  if( 3==sscanf( s.c_str(), "%d/%d/%d", &d, &m, &y ) )
  {
    char buf[32];
    sprintf( buf, "%4d%02d%02d", y, m, d );
    dateStr.assign( buf );
  }
  return dateStr;
}


// Parse a VCard date in the form of YYYYMMDD into the format
// for Barry DD/MM/YY
std::string vCard::ParseVCardDate( const std::string& s )
{
  std::string dateStr;
  if( s.size() )
  {
    int m, d, y;
    if( 3==sscanf( s.c_str(), "%4d%2d%2d", &y, &m, &d ) )
    {
      char buf[32];
      sprintf( buf, "%02d/%02d/%04d", d, m, y );
      dateStr.assign( buf );
    }
  }
  return dateStr;
}



// 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()));

	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... can't add the WorkPhone2
	// since VCARD30 TEL fields only take one number, as far as I know
	AddPhoneCond("pref", con.Phone);
	AddPhoneCond("fax", con.Fax);
	AddPhoneCond("work", con.WorkPhone);
	AddPhoneCond("home", con.HomePhone);
	AddPhoneCond("cell", con.MobilePhone);
	AddPhoneCond("msg", con.Pager);

	// 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()));
	}

	// Image not supported, since vformat routines probably don't
	// support binary VCARD fields....

	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.size() )
          AddAttr(NewAttr("BDAY", ParseBarryDate( con.Birthday ).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);

	// 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;
}

// 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() )
		throw ConvertError("no FN field in VCARD data");
						// 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.GetParam("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);
	}


	// add all applicable phone numbers... can't add the WorkPhone2
	// since VCARD30 TEL fields only take one number, as far as I know
	vAttr tel = GetAttrObj("TEL");
	for( int i = 0; tel.Get(); tel = GetAttrObj("TEL", ++i) )
	{
		std::string stype = tel.GetParam("TYPE");
		ToLower(stype);
		const char *type = stype.c_str();

		// do not use "else" here, since TYPE can have multiple keys
		if( strstr(type, "pref") )
			con.Phone = tel.GetValue();
		if( strstr(type, "fax") )
			con.Fax = tel.GetValue();
		if( strstr(type, "work") )
			con.WorkPhone = tel.GetValue();
		if( strstr(type, "home") )
			con.HomePhone = tel.GetValue();
		if( strstr(type, "cell") )
			con.MobilePhone = tel.GetValue();
		if( strstr(type, "msg") )
			con.Pager = tel.GetValue();
	}

	// 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.GetParam("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");
        con.Birthday = ParseVCardDate( GetAttr("BDAY") );

	vAttr cat = GetAttrObj("CATEGORIES");
	if( cat.Get() )
		ParseCategories(cat, con.Categories);

	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<Contact, VCardConverter> 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: %lu", 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.logf(errmsg.c_str());
		return false;
	}

	Barry::RecordBuilder<Barry::Contact, VCardConverter> 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.h
///		Conversion routines for vcards
///

/*
    Copyright (C) 2006-2008, 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_VCARD_H__
#define __BARRY_SYNC_VCARD_H__

#include <barry/barry.h>
#include <stdint.h>
#include <string>
#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 char *rfc_type, const std::string &phone);

	void ParseAddress(vAttr &adr, Barry::PostalAddress &address);
	void ParseCategories(vAttr &cat, Barry::CategoryList &cats);

        std::string ParseBarryDate( const std::string& s );
        std::string ParseVCardDate( const std::string& s );
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;
	Barry::Contact m_Contact;
	uint32_t m_RecordId;

public:
	VCardConverter();
	explicit VCardConverter(uint32_t newRecordId);
	~VCardConverter();

	// Transfers ownership of m_Data to the caller
	char* ExtractData();

	// Parses vevent data
	bool ParseData(const char *data);

	// Barry storage operator
	void operator()(const Barry::Contact &rec);

	// Barry builder operator
	bool operator()(Barry::Contact &rec, unsigned int dbId);

	// 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.
	static char* GetRecordData(BarryEnvironment *env, unsigned int dbId,
		Barry::RecordStateTable::IndexType index);

	// Handles either adding or overwriting a calendar record,
	// given vevent20 data in data, and the proper environmebnt,
	// dbId, StateIndex.  Set add to true if adding.
	static bool CommitRecordData(BarryEnvironment *env, unsigned int dbId,
		Barry::RecordStateTable::IndexType StateIndex, uint32_t recordId,
		const char *data, bool add, std::string &errmsg);
};


#endif

-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
Barry-devel mailing list
Barry-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/barry-devel

Reply via email to