3 /// Conversion routines for vcards
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.
23 #include "environment.h"
25 #include "vformat.h" // comes from opensync, but not a public header yet
32 //////////////////////////////////////////////////////////////////////////////
35 void ToLower(std::string
&str
)
38 while( i
< str
.size() ) {
39 str
[i
] = tolower(str
[i
]);
44 //////////////////////////////////////////////////////////////////////////////
59 void vCard::AddAddress(const char *rfc_type
, const Barry::PostalAddress
&address
)
62 vAttrPtr label
= NewAttr("LABEL");
63 AddParam(label
, "TYPE", rfc_type
);
64 AddValue(label
, address
.GetLabel().c_str());
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
80 void vCard::AddCategories(const Barry::CategoryList
&categories
)
82 if( !categories
.size() )
85 vAttrPtr cat
= NewAttr("CATEGORIES"); // RFC 2426, 3.6.1
86 Barry::CategoryList::const_iterator i
= categories
.begin();
87 for( ; i
< categories
.end(); ++i
) {
88 AddValue(cat
, i
->c_str());
93 /// Add phone conditionally, only if phone has data in it
94 void vCard::AddPhoneCond(const char *rfc_type
, const std::string
&phone
)
97 vAttrPtr tel
= NewAttr("TEL", phone
.c_str());
98 AddParam(tel
, "TYPE", rfc_type
);
103 void vCard::ParseAddress(vAttr
&adr
, Barry::PostalAddress
&address
)
106 address
.Address3
= adr
.GetValue(0); // PO Box
107 address
.Address2
= adr
.GetValue(1); // Extended address
108 address
.Address1
= adr
.GetValue(2); // Street address
109 address
.City
= adr
.GetValue(3); // Locality (city)
110 address
.Province
= adr
.GetValue(4); // Region (province)
111 address
.PostalCode
= adr
.GetValue(5); // Postal code
112 address
.Country
= adr
.GetValue(6); // Country name
115 void vCard::ParseCategories(vAttr
&cat
, Barry::CategoryList
&cats
)
118 std::string value
= cat
.GetValue(i
);
119 while( value
.size() ) {
120 cats
.push_back(value
);
122 value
= cat
.GetValue(i
);
126 // Main conversion routine for converting from Barry::Contact to
127 // a vCard string of data.
128 const std::string
& vCard::ToVCard(const Barry::Contact
&con
)
130 Trace
trace("vCard::ToVCard");
131 std::ostringstream oss
;
133 trace
.logf("ToVCard, initial Barry record: %s", oss
.str().c_str());
137 SetFormat( vformat_new() );
139 throw ConvertError("resource error allocating vformat");
141 // store the Barry object we're working with
142 m_BarryContact
= con
;
145 // begin building vCard data
148 AddAttr(NewAttr("PRODID", "-//OpenSync//NONSGML Barry Contact Record//EN"));
150 std::string fullname
= con
.GetFullName();
151 if( fullname
.size() )
152 AddAttr(NewAttr("FN", fullname
.c_str()));
154 if( con
.FirstName
.size() || con
.LastName
.size() ) {
155 vAttrPtr name
= NewAttr("N"); // RFC 2426, 3.1.2
156 AddValue(name
, con
.LastName
.c_str()); // Family Name
157 AddValue(name
, con
.FirstName
.c_str()); // Given Name
158 AddValue(name
, ""); // Additional Names
159 AddValue(name
, con
.Prefix
.c_str()); // Honorific Prefixes
160 AddValue(name
, ""); // Honorific Suffixes
164 if( con
.WorkAddress
.HasData() )
165 AddAddress("work", con
.WorkAddress
);
166 if( con
.HomeAddress
.HasData() )
167 AddAddress("home", con
.HomeAddress
);
169 // add all applicable phone numbers... can't add the WorkPhone2
170 // since VCARD30 TEL fields only take one number, as far as I know
171 AddPhoneCond("pref", con
.Phone
);
172 AddPhoneCond("fax", con
.Fax
);
173 AddPhoneCond("work", con
.WorkPhone
);
174 AddPhoneCond("home", con
.HomePhone
);
175 AddPhoneCond("cell", con
.MobilePhone
);
176 AddPhoneCond("msg", con
.Pager
);
178 if( con
.Email
.size() ) {
179 vAttrPtr email
= NewAttr("EMAIL", con
.Email
.c_str());
180 AddParam(email
, "TYPE", "internet");
184 if( con
.JobTitle
.size() ) {
185 AddAttr(NewAttr("TITLE", con
.JobTitle
.c_str()));
186 AddAttr(NewAttr("ROLE", con
.JobTitle
.c_str()));
189 // Image not supported, since vformat routines probably don't
190 // support binary VCARD fields....
192 if( con
.Company
.size() ) {
194 vAttrPtr org
= NewAttr("ORG", con
.Company
.c_str()); // Organization name
195 AddValue(org
, ""); // Division name
199 if( con
.Notes
.size() )
200 AddAttr(NewAttr("NOTE", con
.Notes
.c_str()));
202 AddAttr(NewAttr("URL", con
.URL
.c_str()));
203 if( con
.Categories
.size() )
204 AddCategories(con
.Categories
);
206 // generate the raw VCARD data
207 m_gCardData
= vformat_to_string(Format(), VFORMAT_CARD_30
);
208 m_vCardData
= m_gCardData
;
210 trace
.logf("ToVCard, resulting vcard data: %s", m_vCardData
.c_str());
214 // Main conversion routine for converting from vCard data string
215 // to a Barry::Contact object.
216 const Barry::Contact
& vCard::ToBarry(const char *vcard
, uint32_t RecordId
)
220 Trace
trace("vCard::ToBarry");
221 trace
.logf("ToBarry, working on vcard data: %s", vcard
);
226 // store the vCard raw data
229 // create format parser structures
230 SetFormat( vformat_new_from_string(vcard
) );
232 throw ConvertError("resource error allocating vformat");
236 // Parse the vcard data
239 Barry::Contact
&con
= m_BarryContact
;
240 con
.SetIds(Barry::Contact::GetDefaultRecType(), RecordId
);
242 vAttr name
= GetAttrObj("N");
244 throw ConvertError("no FN field in VCARD data");
246 con
.LastName
= name
.GetValue(0); // Family Name
247 con
.FirstName
= name
.GetValue(1); // Given Name
248 con
.Prefix
= name
.GetValue(3); // Honorific Prefixes
250 vAttr adr
= GetAttrObj("ADR");
251 for( int i
= 0; adr
.Get(); adr
= GetAttrObj("ADR", ++i
) )
253 std::string type
= adr
.GetParam("TYPE");
256 // do not use "else" here, since TYPE can have multiple keys
257 if( strstr(type
.c_str(), "work") )
258 ParseAddress(adr
, con
.WorkAddress
);
259 if( strstr(type
.c_str(), "home") )
260 ParseAddress(adr
, con
.HomeAddress
);
264 // add all applicable phone numbers... can't add the WorkPhone2
265 // since VCARD30 TEL fields only take one number, as far as I know
266 vAttr tel
= GetAttrObj("TEL");
267 for( int i
= 0; tel
.Get(); tel
= GetAttrObj("TEL", ++i
) )
269 std::string stype
= tel
.GetParam("TYPE");
271 const char *type
= stype
.c_str();
273 // do not use "else" here, since TYPE can have multiple keys
274 if( strstr(type
, "pref") )
275 con
.Phone
= tel
.GetValue();
276 if( strstr(type
, "fax") )
277 con
.Fax
= tel
.GetValue();
278 if( strstr(type
, "work") )
279 con
.WorkPhone
= tel
.GetValue();
280 if( strstr(type
, "home") )
281 con
.HomePhone
= tel
.GetValue();
282 if( strstr(type
, "cell") )
283 con
.MobilePhone
= tel
.GetValue();
284 if( strstr(type
, "msg") )
285 con
.Pager
= tel
.GetValue();
288 // scan for all email addresses... save the first one found
289 // by default, then overwrite it with any following email
290 // address if its type is set to "pref"... i.e. we want
291 // the preferred email address here.
292 vAttr email
= GetAttrObj("EMAIL");
293 for( int i
= 0; email
.Get(); email
= GetAttrObj("EMAIL", ++i
) )
295 std::string type
= email
.GetParam("TYPE");
298 bool of_interest
= (i
== 0 || strstr(type
.c_str(), "pref"));
299 bool x400
= strstr(type
.c_str(), "x400");
301 if( of_interest
&& !x400
) {
302 con
.Email
= GetAttr("EMAIL");
306 // figure out which company title we want to keep...
307 // favour the TITLE field, but if it's empty, use ROLE
308 con
.JobTitle
= GetAttr("TITLE");
309 if( !con
.JobTitle
.size() )
310 con
.JobTitle
= GetAttr("ROLE");
312 con
.Company
= GetAttr("ORG");
313 con
.Notes
= GetAttr("NOTE");
314 con
.URL
= GetAttr("URL");
316 vAttr cat
= GetAttrObj("CATEGORIES");
318 ParseCategories(cat
, con
.Categories
);
320 return m_BarryContact
;
323 // Transfers ownership of m_gCardData to the caller.
324 char* vCard::ExtractVCard()
326 char *ret
= m_gCardData
;
335 m_BarryContact
.Clear();
345 //////////////////////////////////////////////////////////////////////////////
348 VCardConverter::VCardConverter()
353 VCardConverter::VCardConverter(uint32_t newRecordId
)
355 m_RecordId(newRecordId
)
359 VCardConverter::~VCardConverter()
365 // Transfers ownership of m_Data to the caller
366 char* VCardConverter::ExtractData()
368 Trace
trace("VCardConverter::ExtractData");
374 bool VCardConverter::ParseData(const char *data
)
376 Trace
trace("VCardConverter::ParseData");
381 m_Contact
= vcard
.ToBarry(data
, m_RecordId
);
384 catch( vCard::ConvertError
&ce
) {
385 trace
.logf("ERROR: vCard::ConvertError exception: %s", ce
.what());
392 // Barry storage operator
393 void VCardConverter::operator()(const Barry::Contact
&rec
)
395 Trace
trace("VCardConverter::operator()");
397 // Delete data if some already exists
407 m_Data
= vcard
.ExtractVCard();
410 catch( vCard::ConvertError
&ce
) {
411 trace
.logf("ERROR: vCard::ConvertError exception: %s", ce
.what());
415 // Barry builder operator
416 bool VCardConverter::operator()(Barry::Contact
&rec
, unsigned int dbId
)
418 Trace
trace("VCardConverter::builder operator()");
424 // Handles calling of the Barry::Controller to fetch a specific
425 // record, indicated by index (into the RecordStateTable).
426 // Returns a g_malloc'd string of data containing the vcard30
427 // data. It is the responsibility of the caller to free it.
428 // This is intended to be passed into the GetChanges() function.
429 char* VCardConverter::GetRecordData(BarryEnvironment
*env
, unsigned int dbId
,
430 Barry::RecordStateTable::IndexType index
)
432 Trace
trace("VCardConverter::GetRecordData()");
434 using namespace Barry
;
436 VCardConverter contact2vcard
;
437 RecordParser
<Contact
, VCardConverter
> parser(contact2vcard
);
438 env
->m_pCon
->GetRecord(dbId
, index
, parser
);
439 return contact2vcard
.ExtractData();
442 bool VCardConverter::CommitRecordData(BarryEnvironment
*env
, unsigned int dbId
,
443 Barry::RecordStateTable::IndexType StateIndex
, uint32_t recordId
,
444 const char *data
, bool add
, std::string
&errmsg
)
446 Trace
trace("VCardConverter::CommitRecordData()");
448 uint32_t newRecordId
;
450 // use given id if possible
451 if( recordId
&& !env
->m_ContactsSync
.m_Table
.GetIndex(recordId
) ) {
452 // recordId is unique and non-zero
453 newRecordId
= recordId
;
456 trace
.log("Can't use recommended recordId, generating new one.");
457 newRecordId
= env
->m_ContactsSync
.m_Table
.MakeNewRecordId();
461 newRecordId
= env
->m_ContactsSync
.m_Table
.StateMap
[StateIndex
].RecordId
;
463 trace
.logf("newRecordId: %lu", newRecordId
);
465 VCardConverter
convert(newRecordId
);
466 if( !convert
.ParseData(data
) ) {
467 std::ostringstream oss
;
468 oss
<< "unable to parse change data for new RecordId: "
469 << newRecordId
<< " data: " << data
;
471 trace
.logf(errmsg
.c_str());
475 Barry::RecordBuilder
<Barry::Contact
, VCardConverter
> builder(convert
);
478 trace
.log("adding record");
479 env
->m_pCon
->AddRecord(dbId
, builder
);
482 trace
.log("setting record");
483 env
->m_pCon
->SetRecord(dbId
, StateIndex
, builder
);
484 trace
.log("clearing dirty flag");
485 env
->m_pCon
->ClearDirty(dbId
, StateIndex
);