Hi Chris, Finally, I have implemented your logic.
I think that it's interessant to fix this bug for your next release 0.15. Because with Evolution, this bug is quiet boring :( Regards, Le vendredi 10 juillet 2009 à 13:16 -0400, Chris Frey a écrit : > On Fri, Jul 10, 2009 at 07:05:28PM +0200, Nicolas wrote: > > I agree with your solution. > > > > You can implemented it :) > > *chuckle* It's on my list :-) > > - Chris > > > ------------------------------------------------------------------------------ > Enter the BlackBerry Developer Challenge > This is your chance to win up to $100,000 in prizes! For a limited time, > vendors submitting new applications to BlackBerry App World(TM) will have > the opportunity to enter the BlackBerry Developer Challenge. See full prize > details at: http://p.sf.net/sfu/Challenge > _______________________________________________ > Barry-devel mailing list > Barry-devel@lists.sourceforge.net > https://lists.sourceforge.net/lists/listinfo/barry-devel -- Nicolas VIVIEN
0004-Fix-phone-duplication.patch.gz
Description: GNU Zip compressed data
/// /// \file vcard.cc /// Conversion routines for vcards /// /* Copyright (C) 2006-2009, 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 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())); } // 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.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; } // 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); } // 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"); // turn to lower case for comparison // FIXME - is this i18n safe? ToLower(stype); // state const char *type = stype.c_str(); bool used = false; // 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") ) { con.Phone = tel.GetValue(); used = true; } // Otherwise, the phone has to be "voice" type else if( strstr(type, "voice") && con.Phone.size() == 0 ) { con.Phone = tel.GetValue(); used = true; } } // 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( strstr(type, "cell") && (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 // cell/pcs/car MobilePhone field only // bbs/video/modem entire TEL ignored by Barry // isdn entire TEL ignored by Barry else if( strstr(type, "pcs") || strstr(type, "car") ) { } else if( strstr(type, "bbs") || strstr(type, "video") || strstr(type, "modem") ) { } else if( strstr(type, "isdn") ) { } // Voice telephone : else { if( strstr(type, "work") ) { if( strstr(type, "pref") ) { con.WorkPhone2 = con.WorkPhone; con.WorkPhone = tel.GetValue(); used = true; } else if( con.WorkPhone.size() == 0 ) { con.WorkPhone = tel.GetValue(); used = true; } else if( con.WorkPhone2.size() == 0 ) { con.WorkPhone2 = tel.GetValue(); used = true; } } if( strstr(type, "home") ) { if( strstr(type, "pref") ) { con.HomePhone2 = con.HomePhone; con.HomePhone = tel.GetValue(); used = true; } if( con.HomePhone.size() == 0 ) { con.HomePhone = tel.GetValue(); used = true; } else if( con.HomePhone2.size() == 0 ) { con.HomePhone2 = tel.GetValue(); used = true; } } } // if this value has not been claimed by any of the // cases above, claim it now as "OtherPhone" if( !used && con.OtherPhone.size() == 0 ) { con.OtherPhone = tel.GetValue(); } } // Final check: If Phone is blank, and OtherPhone has a value, swap. if( con.OtherPhone.size() && !con.Phone.size() ) { con.Phone.swap(con.OtherPhone); } // 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<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.log(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; }
------------------------------------------------------------------------------ Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day trial. Simplify your report design, integration and deployment - and focus on what you do best, core application coding. Discover what's new with Crystal Reports now. http://p.sf.net/sfu/bobj-july
_______________________________________________ Barry-devel mailing list Barry-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/barry-devel