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 /// Add phone conditionally, only if phone has data in it
81 void vCard::AddPhoneCond(const char *rfc_type
, const std::string
&phone
)
84 vAttrPtr tel
= NewAttr("TEL", phone
.c_str());
85 AddParam(tel
, "TYPE", rfc_type
);
90 void vCard::ParseAddress(vAttr
&adr
, Barry::PostalAddress
&address
)
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
;
109 trace
.logf("ToVCard, initial Barry record: %s", oss
.str().c_str());
113 SetFormat( vformat_new() );
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
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");
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() ) {
170 vAttrPtr org
= NewAttr("ORG", con
.Company
.c_str()); // Organization name
171 AddValue(org
, ""); // Division name
175 if( con
.Notes
.size() )
176 AddAttr(NewAttr("NOTE", con
.Notes
.c_str()));
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());
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
)
194 Trace
trace("vCard::ToBarry");
195 trace
.logf("ToBarry, working on vcard data: %s", vcard
);
200 // store the vCard raw data
203 // create format parser structures
204 SetFormat( vformat_new_from_string(vcard
) );
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");
218 throw ConvertError("no FN field in VCARD data");
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");
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");
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");
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
;
305 m_BarryContact
.Clear();
315 //////////////////////////////////////////////////////////////////////////////
318 VCardConverter::VCardConverter()
323 VCardConverter::VCardConverter(uint32_t newRecordId
)
325 m_RecordId(newRecordId
)
329 VCardConverter::~VCardConverter()
335 // Transfers ownership of m_Data to the caller
336 char* VCardConverter::ExtractData()
338 Trace
trace("VCardConverter::ExtractData");
344 bool VCardConverter::ParseData(const char *data
)
346 Trace
trace("VCardConverter::ParseData");
351 m_Contact
= vcard
.ToBarry(data
, m_RecordId
);
354 catch( vCard::ConvertError
&ce
) {
355 trace
.logf("ERROR: vCard::ConvertError exception: %s", ce
.what());
362 // Barry storage operator
363 void VCardConverter::operator()(const Barry::Contact
&rec
)
365 Trace
trace("VCardConverter::operator()");
367 // Delete data if some already exists
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()");
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
;
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
;
426 trace
.log("Can't use recommended recordId, generating new one.");
427 newRecordId
= env
->m_ContactsSync
.m_Table
.MakeNewRecordId();
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
;
441 trace
.logf(errmsg
.c_str());
445 Barry::RecordBuilder
<Barry::Contact
, VCardConverter
> builder(convert
);
448 trace
.log("adding record");
449 env
->m_pCon
->AddRecord(dbId
, builder
);
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
);