- fixed in opensync plugin:
[barry.git] / opensync-plugin / src / vcard.cc
blobe434cead0cedf307db78e0e8364a7fe7ff8bbf36
1 ///
2 /// \file vcard.cc
3 /// Conversion routines for vcards
4 ///
6 /*
7 Copyright (C) 2006-2007, Net Direct Inc. (http://www.netdirect.ca/)
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU General Public License in the COPYING file at the
19 root directory of this project for more details.
22 #include "vcard.h"
23 #include "environment.h"
24 #include "trace.h"
25 #include "vformat.h" // comes from opensync, but not a public header yet
26 #include <stdint.h>
27 #include <glib.h>
28 #include <sstream>
29 #include <ctype.h>
32 //////////////////////////////////////////////////////////////////////////////
33 // Utility functions
35 void ToLower(std::string &str)
37 size_t i = 0;
38 while( i < str.size() ) {
39 str[i] = tolower(str[i]);
40 i++;
44 //////////////////////////////////////////////////////////////////////////////
45 // vCard
47 vCard::vCard()
48 : m_gCardData(0)
52 vCard::~vCard()
54 if( m_gCardData ) {
55 g_free(m_gCardData);
59 void vCard::AddAddress(const char *rfc_type, const Barry::PostalAddress &address)
61 // add label first
62 vAttrPtr label = NewAttr("LABEL");
63 AddParam(label, "TYPE", rfc_type);
64 AddValue(label, address.GetLabel().c_str());
65 AddAttr(label);
67 // add breakout address form
68 vAttrPtr adr = NewAttr("ADR"); // RFC 2426, 3.2.1
69 AddParam(adr, "TYPE", rfc_type);
70 AddValue(adr, address.Address3.c_str()); // PO Box
71 AddValue(adr, address.Address2.c_str()); // Extended address
72 AddValue(adr, address.Address1.c_str()); // Street address
73 AddValue(adr, address.City.c_str()); // Locality (city)
74 AddValue(adr, address.Province.c_str()); // Region (province)
75 AddValue(adr, address.PostalCode.c_str()); // Postal code
76 AddValue(adr, address.Country.c_str()); // Country name
77 AddAttr(adr);
80 /// Add phone conditionally, only if phone has data in it
81 void vCard::AddPhoneCond(const char *rfc_type, const std::string &phone)
83 if( phone.size() ) {
84 vAttrPtr tel = NewAttr("TEL", phone.c_str());
85 AddParam(tel, "TYPE", rfc_type);
86 AddAttr(tel);
90 void vCard::ParseAddress(vAttr &adr, Barry::PostalAddress &address)
92 // RFC 2426, 3.2.1
93 address.Address3 = adr.GetValue(0); // PO Box
94 address.Address2 = adr.GetValue(1); // Extended address
95 address.Address1 = adr.GetValue(2); // Street address
96 address.City = adr.GetValue(3); // Locality (city)
97 address.Province = adr.GetValue(4); // Region (province)
98 address.PostalCode = adr.GetValue(5); // Postal code
99 address.Country = adr.GetValue(6); // Country name
102 // Main conversion routine for converting from Barry::Contact to
103 // a vCard string of data.
104 const std::string& vCard::ToVCard(const Barry::Contact &con)
106 Trace trace("vCard::ToVCard");
107 std::ostringstream oss;
108 con.Dump(oss);
109 trace.logf("ToVCard, initial Barry record: %s", oss.str().c_str());
111 // start fresh
112 Clear();
113 SetFormat( vformat_new() );
114 if( !Format() )
115 throw ConvertError("resource error allocating vformat");
117 // store the Barry object we're working with
118 m_BarryContact = con;
121 // begin building vCard data
124 AddAttr(NewAttr("PRODID", "-//OpenSync//NONSGML Barry Contact Record//EN"));
126 std::string fullname = con.GetFullName();
127 if( fullname.size() )
128 AddAttr(NewAttr("FN", fullname.c_str()));
130 if( con.FirstName.size() || con.LastName.size() ) {
131 vAttrPtr name = NewAttr("N"); // RFC 2426, 3.1.2
132 AddValue(name, con.LastName.c_str()); // Family Name
133 AddValue(name, con.FirstName.c_str()); // Given Name
134 AddValue(name, ""); // Additional Names
135 AddValue(name, con.Prefix.c_str()); // Honorific Prefixes
136 AddValue(name, ""); // Honorific Suffixes
137 AddAttr(name);
140 if( con.WorkAddress.HasData() )
141 AddAddress("work", con.WorkAddress);
142 if( con.HomeAddress.HasData() )
143 AddAddress("home", con.HomeAddress);
145 // add all applicable phone numbers... can't add the WorkPhone2
146 // since VCARD30 TEL fields only take one number, as far as I know
147 AddPhoneCond("pref", con.Phone);
148 AddPhoneCond("fax", con.Fax);
149 AddPhoneCond("work", con.WorkPhone);
150 AddPhoneCond("home", con.HomePhone);
151 AddPhoneCond("cell", con.MobilePhone);
152 AddPhoneCond("msg", con.Pager);
154 if( con.Email.size() ) {
155 vAttrPtr email = NewAttr("EMAIL", con.Email.c_str());
156 AddParam(email, "TYPE", "internet");
157 AddAttr(email);
160 if( con.JobTitle.size() ) {
161 AddAttr(NewAttr("TITLE", con.JobTitle.c_str()));
162 AddAttr(NewAttr("ROLE", con.JobTitle.c_str()));
165 // Image not supported, since vformat routines probably don't
166 // support binary VCARD fields....
168 if( con.Company.size() ) {
169 // RFC 2426, 3.5.5
170 vAttrPtr org = NewAttr("ORG", con.Company.c_str()); // Organization name
171 AddValue(org, ""); // Division name
172 AddAttr(org);
175 if( con.Notes.size() )
176 AddAttr(NewAttr("NOTE", con.Notes.c_str()));
177 if( con.URL.size() )
178 AddAttr(NewAttr("URL", con.URL.c_str()));
180 // generate the raw VCARD data
181 m_gCardData = vformat_to_string(Format(), VFORMAT_CARD_30);
182 m_vCardData = m_gCardData;
184 trace.logf("ToVCard, resulting vcard data: %s", m_vCardData.c_str());
185 return m_vCardData;
188 // Main conversion routine for converting from vCard data string
189 // to a Barry::Contact object.
190 const Barry::Contact& vCard::ToBarry(const char *vcard, uint32_t RecordId)
192 using namespace std;
194 Trace trace("vCard::ToBarry");
195 trace.logf("ToBarry, working on vcard data: %s", vcard);
197 // start fresh
198 Clear();
200 // store the vCard raw data
201 m_vCardData = vcard;
203 // create format parser structures
204 SetFormat( vformat_new_from_string(vcard) );
205 if( !Format() )
206 throw ConvertError("resource error allocating vformat");
210 // Parse the vcard data
213 Barry::Contact &con = m_BarryContact;
214 con.SetIds(Barry::Contact::GetDefaultRecType(), RecordId);
216 vAttr name = GetAttrObj("N");
217 if( !name.Get() )
218 throw ConvertError("no FN field in VCARD data");
219 // RFC 2426, 3.1.2
220 con.LastName = name.GetValue(0); // Family Name
221 con.FirstName = name.GetValue(1); // Given Name
222 con.Prefix = name.GetValue(3); // Honorific Prefixes
224 vAttr adr = GetAttrObj("ADR");
225 for( int i = 0; adr.Get(); adr = GetAttrObj("ADR", ++i) )
227 std::string type = adr.GetParam("TYPE");
228 ToLower(type);
230 // do not use "else" here, since TYPE can have multiple keys
231 if( strstr(type.c_str(), "work") )
232 ParseAddress(adr, con.WorkAddress);
233 if( strstr(type.c_str(), "home") )
234 ParseAddress(adr, con.HomeAddress);
238 // add all applicable phone numbers... can't add the WorkPhone2
239 // since VCARD30 TEL fields only take one number, as far as I know
240 vAttr tel = GetAttrObj("TEL");
241 for( int i = 0; tel.Get(); tel = GetAttrObj("TEL", ++i) )
243 std::string stype = tel.GetParam("TYPE");
244 ToLower(stype);
245 const char *type = stype.c_str();
247 // do not use "else" here, since TYPE can have multiple keys
248 if( strstr(type, "pref") )
249 con.Phone = tel.GetValue();
250 if( strstr(type, "fax") )
251 con.Fax = tel.GetValue();
252 if( strstr(type, "work") )
253 con.WorkPhone = tel.GetValue();
254 if( strstr(type, "home") )
255 con.HomePhone = tel.GetValue();
256 if( strstr(type, "cell") )
257 con.MobilePhone = tel.GetValue();
258 if( strstr(type, "msg") )
259 con.Pager = tel.GetValue();
262 // scan for all email addresses... save the first one found
263 // by default, then overwrite it with any following email
264 // address if its type is set to "pref"... i.e. we want
265 // the preferred email address here.
266 vAttr email = GetAttrObj("EMAIL");
267 for( int i = 0; email.Get(); email = GetAttrObj("EMAIL", ++i) )
269 std::string type = email.GetParam("TYPE");
270 ToLower(type);
272 bool of_interest = (i == 0 || strstr(type.c_str(), "pref"));
273 bool x400 = strstr(type.c_str(), "x400");
275 if( of_interest && !x400 ) {
276 con.Email = GetAttr("EMAIL");
280 // figure out which company title we want to keep...
281 // favour the TITLE field, but if it's empty, use ROLE
282 con.JobTitle = GetAttr("TITLE");
283 if( !con.JobTitle.size() )
284 con.JobTitle = GetAttr("ROLE");
286 con.Company = GetAttr("ORG");
287 con.Notes = GetAttr("NOTE");
288 con.URL = GetAttr("URL");
290 return m_BarryContact;
293 // Transfers ownership of m_gCardData to the caller.
294 char* vCard::ExtractVCard()
296 char *ret = m_gCardData;
297 m_gCardData = 0;
298 return ret;
301 void vCard::Clear()
303 vBase::Clear();
304 m_vCardData.clear();
305 m_BarryContact.Clear();
307 if( m_gCardData ) {
308 g_free(m_gCardData);
309 m_gCardData = 0;
315 //////////////////////////////////////////////////////////////////////////////
318 VCardConverter::VCardConverter()
319 : m_Data(0)
323 VCardConverter::VCardConverter(uint32_t newRecordId)
324 : m_Data(0),
325 m_RecordId(newRecordId)
329 VCardConverter::~VCardConverter()
331 if( m_Data )
332 g_free(m_Data);
335 // Transfers ownership of m_Data to the caller
336 char* VCardConverter::ExtractData()
338 Trace trace("VCardConverter::ExtractData");
339 char *ret = m_Data;
340 m_Data = 0;
341 return ret;
344 bool VCardConverter::ParseData(const char *data)
346 Trace trace("VCardConverter::ParseData");
348 try {
350 vCard vcard;
351 m_Contact = vcard.ToBarry(data, m_RecordId);
354 catch( vCard::ConvertError &ce ) {
355 trace.logf("ERROR: vCard::ConvertError exception: %s", ce.what());
356 return false;
359 return true;
362 // Barry storage operator
363 void VCardConverter::operator()(const Barry::Contact &rec)
365 Trace trace("VCardConverter::operator()");
367 // Delete data if some already exists
368 if( m_Data ) {
369 g_free(m_Data);
370 m_Data = 0;
373 try {
375 vCard vcard;
376 vcard.ToVCard(rec);
377 m_Data = vcard.ExtractVCard();
380 catch( vCard::ConvertError &ce ) {
381 trace.logf("ERROR: vCard::ConvertError exception: %s", ce.what());
385 // Barry builder operator
386 bool VCardConverter::operator()(Barry::Contact &rec, unsigned int dbId)
388 Trace trace("VCardConverter::builder operator()");
390 rec = m_Contact;
391 return true;
394 // Handles calling of the Barry::Controller to fetch a specific
395 // record, indicated by index (into the RecordStateTable).
396 // Returns a g_malloc'd string of data containing the vcard30
397 // data. It is the responsibility of the caller to free it.
398 // This is intended to be passed into the GetChanges() function.
399 char* VCardConverter::GetRecordData(BarryEnvironment *env, unsigned int dbId,
400 Barry::RecordStateTable::IndexType index)
402 Trace trace("VCardConverter::GetRecordData()");
404 using namespace Barry;
406 VCardConverter contact2vcard;
407 RecordParser<Contact, VCardConverter> parser(contact2vcard);
408 env->m_pCon->GetRecord(dbId, index, parser);
409 return contact2vcard.ExtractData();
412 bool VCardConverter::CommitRecordData(BarryEnvironment *env, unsigned int dbId,
413 Barry::RecordStateTable::IndexType StateIndex, uint32_t recordId,
414 const char *data, bool add, std::string &errmsg)
416 Trace trace("VCardConverter::CommitRecordData()");
418 uint32_t newRecordId;
419 if( add ) {
420 // use given id if possible
421 if( recordId && !env->m_ContactsSync.m_Table.GetIndex(recordId) ) {
422 // recordId is unique and non-zero
423 newRecordId = recordId;
425 else {
426 trace.log("Can't use recommended recordId, generating new one.");
427 newRecordId = env->m_ContactsSync.m_Table.MakeNewRecordId();
430 else {
431 newRecordId = env->m_ContactsSync.m_Table.StateMap[StateIndex].RecordId;
433 trace.logf("newRecordId: %lu", newRecordId);
435 VCardConverter convert(newRecordId);
436 if( !convert.ParseData(data) ) {
437 std::ostringstream oss;
438 oss << "unable to parse change data for new RecordId: "
439 << newRecordId << " data: " << data;
440 errmsg = oss.str();
441 trace.logf(errmsg.c_str());
442 return false;
445 Barry::RecordBuilder<Barry::Contact, VCardConverter> builder(convert);
447 if( add ) {
448 trace.log("adding record");
449 env->m_pCon->AddRecord(dbId, builder);
451 else {
452 trace.log("setting record");
453 env->m_pCon->SetRecord(dbId, StateIndex, builder);
454 trace.log("clearing dirty flag");
455 env->m_pCon->ClearDirty(dbId, StateIndex);
458 return true;