From kde-extra-gear Sun Jan 03 12:30:58 2010 From: Stefano Avallone Date: Sun, 03 Jan 2010 12:30:58 +0000 To: kde-extra-gear Subject: [Kde-extra-gear] [PATCH] Add support for multiple phone numbers in Message-Id: <201001031331.08745.stavallo () unina ! it> X-MARC-Message: https://marc.info/?l=kde-extra-gear&m=126252195306857 MIME-Version: 1 Content-Type: multipart/mixed; boundary="--Boundary-00=_C4IQLOK+zAPVpoR" --Boundary-00=_C4IQLOK+zAPVpoR Content-Type: Text/Plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Hi list, I implemented the support for multiple phone numbers and multiple email addresses in the akonadi googledata agent and libgcal. The type of a phone number and an email is also stored. Thanks to Kevin's suggestion, the google group membership info for each contact is stored as a custom property, and thus the membership is not lost upon a contact update made within kaddressbook. Please find attached the patches against: svn://anonsvn.kde.org/home/kde/trunk/extragear/pim/googledata and git://repo.or.cz/libgcal.git I have done some tests and it seems to work. Please review the patches and test it. It would be great if those patches could be applied, as they provide a much needed functionality, in my opinion. Thanks, Stefano --Boundary-00=_C4IQLOK+zAPVpoR Content-Type: text/x-patch; charset="UTF-8"; name="googledata.diff" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="googledata.diff" Index: contacts/googledataresource.cpp =================================================================== --- contacts/googledataresource.cpp (revision 1066667) +++ contacts/googledataresource.cpp (working copy) @@ -124,6 +124,26 @@ return result; } +KABC::PhoneNumber::Type GoogleLabelToAkonadiType(char *label) { + if (!strcmp(label,"home")) + return KABC::PhoneNumber::Home; + if (!strcmp(label,"work")) + return KABC::PhoneNumber::Work; + if (!strcmp(label,"mobile")) + return KABC::PhoneNumber::Cell; + if (!strcmp(label,"car")) + return KABC::PhoneNumber::Car; + if (!strcmp(label,"isdn")) + return KABC::PhoneNumber::Isdn; + if (!strcmp(label,"pager")) + return KABC::PhoneNumber::Pager; + if (!strcmp(label,"home_fax")) + return KABC::PhoneNumber::Home | KABC::PhoneNumber::Fax; + if (!strcmp(label,"work_fax")) + return KABC::PhoneNumber::Work | KABC::PhoneNumber::Fax; + return KABC::PhoneNumber::Home | KABC::PhoneNumber::Work; // other +} + void GoogleContactsResource::retrieveItems( const Akonadi::Collection &collection ) { Q_UNUSED( collection ); @@ -175,32 +195,62 @@ contact = gcal_contact_element(&all_contacts, i); KABC::Addressee addressee; - KABC::PhoneNumber number; KABC::Address address; KABC::Picture photo; QImage image; QString temp; + int num_elem; + int pref; + int j; + char **values; + char **types; /* name */ temp = QString::fromUtf8(gcal_contact_get_title(contact)); addressee.setNameFromString(temp); /* email */ - temp = QString::fromUtf8(gcal_contact_get_email(contact)); - addressee.insertEmail(temp, true); + num_elem = gcal_contact_get_emails_nr(contact); + pref = gcal_contact_get_pref_email(contact); + values = gcal_contact_get_emails_field(contact); + types = gcal_contact_get_emails_type(contact); + for (j = 0; j < num_elem; j++) { + temp = QString::fromUtf8(values[j]); + addressee.insertEmail(temp, (j == pref)); + temp = QString::fromUtf8(types[j]); + addressee.insertCustom(QString::fromUtf8("Google"), QString::fromUtf8("typeof_email_").append(QString::number(j)), temp); + } /* address */ temp = QString::fromUtf8(gcal_contact_get_address(contact)); address.setExtended(temp); addressee.insertAddress(address); /* telephone */ - temp = QString::fromUtf8(gcal_contact_get_phone(contact)); - number.setNumber(temp); - addressee.insertPhoneNumber(number); + num_elem = gcal_contact_get_phone_numbers_nr(contact); + values = gcal_contact_get_phone_numbers_field(contact); + types = gcal_contact_get_phone_numbers_type(contact); + + for (j = 0; j < num_elem; j++) { + KABC::PhoneNumber number; + temp = QString::fromUtf8(values[j]); + number.setNumber(temp); + number.setType(GoogleLabelToAkonadiType(types[j])); + addressee.insertPhoneNumber(number); + } + /* profission */ temp = QString::fromUtf8(gcal_contact_get_profission(contact)); addressee.setTitle(temp); /* company */ temp = QString::fromUtf8(gcal_contact_get_organization(contact)); addressee.setOrganization(temp); + /* Google group membership */ + num_elem = gcal_contact_get_groupMembership_nr(contact); + values = gcal_contact_get_groupMembership(contact); + addressee.insertCustom(QString::fromUtf8("Google"), QString::fromUtf8("groupMembership_nr"), QString::number(num_elem)); + + for (j = 0; j < num_elem; j++) { + temp = QString::fromUtf8(values[j]); + addressee.insertCustom(QString::fromUtf8("Google"), QString::fromUtf8("groupMembership_").append(QString::number(j)), temp); + } /* description */ temp = QString::fromUtf8(gcal_contact_get_content(contact)); addressee.setNote(temp); @@ -274,6 +324,11 @@ QString temp; KABC::Picture photo; QImage image; + int num_elem; + int pref; + int j; + char **values; + char **types; /* Just in case, I'm not sure when this member function is called */ pending.clear(); @@ -290,7 +345,8 @@ /* Query is inclusive regarding timestamp */ - if (all_contacts.length == 1) { + /* RFC: don't think so... */ + if (all_contacts.length == 0) { kError() << "no updates, done!"; itemsRetrievedIncremental(pending, deleted); return result; @@ -303,35 +359,58 @@ contact = gcal_contact_element(&all_contacts, i); Item item(QLatin1String("text/directory")); if (!strcmp(timestamp, gcal_contact_get_updated(contact))) { - kError() << "This is an old contact... continue."; + kError() << "This is an old contact... continue."; continue; } if (!gcal_contact_is_deleted(contact)) { KABC::Addressee addressee; - KABC::PhoneNumber number; KABC::Address address; /* name */ temp = QString::fromUtf8(gcal_contact_get_title(contact)); addressee.setNameFromString(temp); kError() << "index: " << i <<"updated: " << temp; /* email */ - temp = QString::fromUtf8(gcal_contact_get_email(contact)); - addressee.insertEmail(temp, true); + num_elem = gcal_contact_get_emails_nr(contact); + pref = gcal_contact_get_pref_email(contact); + values = gcal_contact_get_emails_field(contact); + types = gcal_contact_get_emails_type(contact); + for (j = 0; j < num_elem; j++) { + temp = QString::fromUtf8(values[j]); + addressee.insertEmail(temp, (j == pref)); + temp = QString::fromUtf8(types[j]); + addressee.insertCustom(QString::fromUtf8("Google"), QString::fromUtf8("typeof_email_").append(QString::number(j)), temp); + } /* address */ temp = QString::fromUtf8(gcal_contact_get_address(contact)); address.setExtended(temp); addressee.insertAddress(address); /* telephone */ - temp = QString::fromUtf8(gcal_contact_get_phone(contact)); - number.setNumber(temp); - addressee.insertPhoneNumber(number); + num_elem = gcal_contact_get_phone_numbers_nr(contact); + values = gcal_contact_get_phone_numbers_field(contact); + types = gcal_contact_get_phone_numbers_type(contact); + for (j = 0; j < num_elem; j++) { + KABC::PhoneNumber number; + temp = QString::fromUtf8(values[j]); + number.setNumber(temp); + number.setType(GoogleLabelToAkonadiType(types[j])); + addressee.insertPhoneNumber(number); + } /* profission */ temp = QString::fromUtf8(gcal_contact_get_profission(contact)); addressee.setTitle(temp); /* company */ temp = QString::fromUtf8(gcal_contact_get_organization(contact)); addressee.setOrganization(temp); + /* Google group membership */ + num_elem = gcal_contact_get_groupMembership_nr(contact); + values = gcal_contact_get_groupMembership(contact); + addressee.insertCustom(QString::fromUtf8("Google"), QString::fromUtf8("groupMembership_nr"), QString::number(num_elem)); + + for (j = 0; j < num_elem; j++) { + temp = QString::fromUtf8(values[j]); + addressee.insertCustom(QString::fromUtf8("Google"), QString::fromUtf8("groupMembership_").append(QString::number(j)), temp); + } /* description */ temp = QString::fromUtf8(gcal_contact_get_content(contact)); addressee.setNote(temp); @@ -414,21 +493,60 @@ synchronize(); } +char *AkonadiTypeToGoogleLabel(KABC::PhoneNumber::Type type) { + switch ( type ) { + case KABC::PhoneNumber::Home: + return strdup("home"); + break; + case KABC::PhoneNumber::Work: + return strdup("work"); + break; + case KABC::PhoneNumber::Cell: + return strdup("mobile"); + break; + case KABC::PhoneNumber::Car: + return strdup("car"); + break; + case KABC::PhoneNumber::Isdn: + return strdup("isdn"); + break; + case KABC::PhoneNumber::Pager: + return strdup("pager"); + break; + case KABC::PhoneNumber::Home + KABC::PhoneNumber::Fax: + return strdup("home_fax"); + break; + case KABC::PhoneNumber::Work + KABC::PhoneNumber::Fax: + return strdup("work_fax"); + break; + default: + return strdup("other"); + } +} + void GoogleContactsResource::itemAdded( const Akonadi::Item &item, const Akonadi::Collection &collection ) { Q_UNUSED(collection); KABC::Addressee addressee; - KABC::PhoneNumber number; KABC::Address address; gcal_contact_t contact; QString temp; + QStringList listEmail; + QStringList::iterator email; QByteArray t_byte; QList listAddress; QList listNumber; + QList::iterator number; KABC::Picture photo; int result; + int num_elem; + int pref = 0; + int j; + bool ok; + const char **values; + const char **types; if (!authenticated) configure(0); @@ -453,9 +571,34 @@ t_byte = temp.toUtf8(); gcal_contact_set_title(contact, t_byte.data()); - temp = addressee.preferredEmail(); - t_byte = temp.toUtf8(); - gcal_contact_set_email(contact, t_byte.data()); + listEmail = addressee.emails(); + if (!listEmail.empty()) { + values = (const char**) malloc(listEmail.size() * sizeof(char*)); + types = (const char**) malloc(listEmail.size() * sizeof(char*)); + num_elem = 0; + for (email = listEmail.begin(); email != listEmail.end(); email++) { + temp = *email; + if (temp.length()) { + t_byte = temp.toUtf8(); + values[num_elem] = strdup(t_byte.data()); + if (temp == addressee.preferredEmail()) + pref = num_elem; + temp = addressee.custom(QString::fromUtf8("Google"), QString::fromUtf8("typeof_email_").append(QString::number(num_elem))); + if (temp.length()) { + t_byte = temp.toUtf8(); + types[num_elem] = strdup(t_byte.data()); + } + else + types[num_elem] = strdup("other"); + num_elem++; + } + } + if (num_elem) + gcal_contact_set_email(contact, num_elem, values, types, pref); + + free(values); + free(types); + } /* Bellow are optional */ listAddress = addressee.addresses(); @@ -471,12 +614,23 @@ listNumber = addressee.phoneNumbers(); if (!listNumber.empty()) { - number = listNumber.first(); - temp = number.number(); - if (temp.length()) { - t_byte = temp.toUtf8(); - gcal_contact_set_phone(contact, t_byte.data()); + values = (const char**) malloc(listNumber.size() * sizeof(char*)); + types = (const char**) malloc(listNumber.size() * sizeof(char*)); + num_elem = 0; + for (number = listNumber.begin(); number != listNumber.end(); number++) { + temp = number->number(); + if (temp.length()) { + t_byte = temp.toUtf8(); + values[num_elem] = strdup(t_byte.data()); + types[num_elem] = AkonadiTypeToGoogleLabel(number->type()); + num_elem++; + } } + if (num_elem) + gcal_contact_set_phone(contact, num_elem, values, types); + + free(values); + free(types); } temp = addressee.title(); @@ -491,6 +645,21 @@ gcal_contact_set_organization(contact, t_byte.data()); } + temp = addressee.custom(QString::fromUtf8("Google"), QString::fromUtf8("groupMembership_nr")); + num_elem = temp.toInt(&ok); + if (ok) { + values = (const char**) malloc(num_elem * sizeof(char*)); + for (j = 0; j < num_elem; j++) { + temp = addressee.custom(QString::fromUtf8("Google"), QString::fromUtf8("groupMembership_").append(QString::number(j))); + if (temp.length()) { + t_byte = temp.toUtf8(); + values[j] = strdup(t_byte.data()); + } + } + gcal_contact_set_groupMembership(contact, num_elem, values); + free(values); + } + temp = addressee.note(); if (temp.length()) { t_byte = temp.toUtf8(); @@ -540,15 +709,23 @@ Q_UNUSED(parts); KABC::Addressee addressee; - KABC::PhoneNumber number; KABC::Address address; QList listAddress; + QStringList listEmail; + QStringList::iterator email; QList listNumber; + QList::iterator number; gcal_contact_t contact; QByteArray t_byte; QString temp; KABC::Picture photo; int result; + int num_elem; + int j; + bool ok; + int pref = 0; + const char **values; + const char **types; if (!authenticated) configure(0); @@ -580,9 +757,34 @@ t_byte = temp.toUtf8(); gcal_contact_set_title(contact, t_byte.data()); - temp = addressee.preferredEmail(); - t_byte = temp.toUtf8(); - gcal_contact_set_email(contact, t_byte.data()); + listEmail = addressee.emails(); + if (!listEmail.empty()) { + values = (const char**) malloc(listEmail.size() * sizeof(char*)); + types = (const char**) malloc(listEmail.size() * sizeof(char*)); + num_elem = 0; + for (email = listEmail.begin(); email != listEmail.end(); email++) { + temp = *email; + if (temp.length()) { + t_byte = temp.toUtf8(); + values[num_elem] = strdup(t_byte.data()); + if (temp == addressee.preferredEmail()) + pref = num_elem; + temp = addressee.custom(QString::fromUtf8("Google"), QString::fromUtf8("typeof_email_").append(QString::number(num_elem))); + if (temp.length()) { + t_byte = temp.toUtf8(); + types[num_elem] = strdup(t_byte.data()); + } + else + types[num_elem] = strdup("other"); + num_elem++; + } + } + if (num_elem) + gcal_contact_set_email(contact, num_elem, values, types, pref); + + free(values); + free(types); + } /* Bellow are optional */ listAddress = addressee.addresses(); @@ -597,13 +799,23 @@ listNumber = addressee.phoneNumbers(); if (!listNumber.empty()) { - number = listNumber.first(); - temp = number.number(); - if (temp.length()) { - t_byte = temp.toUtf8(); - gcal_contact_set_phone(contact, t_byte.data()); + values = (const char**) malloc(listNumber.size() * sizeof(char*)); + types = (const char**) malloc(listNumber.size() * sizeof(char*)); + num_elem = 0; + for (number = listNumber.begin(); number != listNumber.end(); number++) { + temp = number->number(); + if (temp.length()) { + t_byte = temp.toUtf8(); + values[num_elem] = strdup(t_byte.data()); + types[num_elem] = AkonadiTypeToGoogleLabel(number->type()); + num_elem++; + } } + if (num_elem) + gcal_contact_set_phone(contact, num_elem, values, types); + free(values); + free(types); } temp = addressee.title(); @@ -618,6 +830,21 @@ gcal_contact_set_organization(contact, t_byte.data()); } + temp = addressee.custom(QString::fromUtf8("Google"), QString::fromUtf8("groupMembership_nr")); + num_elem = temp.toInt(&ok); + if (ok) { + values = (const char**) malloc(num_elem * sizeof(char*)); + for (j = 0; j < num_elem; j++) { + temp = addressee.custom(QString::fromUtf8("Google"), QString::fromUtf8("groupMembership_").append(QString::number(j))); + if (temp.length()) { + t_byte = temp.toUtf8(); + values[j] = strdup(t_byte.data()); + } + } + gcal_contact_set_groupMembership(contact, num_elem, values); + free(values); + } + temp = addressee.note(); if (temp.length()) { t_byte = temp.toUtf8(); --Boundary-00=_C4IQLOK+zAPVpoR Content-Type: text/x-patch; charset="UTF-8"; name="libgcal.diff" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="libgcal.diff" diff --git a/inc/gcontact.h b/inc/gcontact.h index dabf528..67d3437 100644 --- a/inc/gcontact.h +++ b/inc/gcontact.h @@ -303,7 +303,10 @@ char gcal_contact_is_deleted(gcal_contact_t contact); * atom stream, it will be set to an empty string (i.e. ""). */ -char *gcal_contact_get_email(gcal_contact_t contact); +int gcal_contact_get_emails_nr(gcal_contact_t contact); +int gcal_contact_get_pref_email(gcal_contact_t contact); +char **gcal_contact_get_emails_field(gcal_contact_t contact); +char **gcal_contact_get_emails_type(gcal_contact_t contact); /** Access contact description. * @@ -362,7 +365,9 @@ char *gcal_contact_get_im(gcal_contact_t contact); * atom stream, it will be set to an empty string (i.e. ""). */ -char *gcal_contact_get_phone(gcal_contact_t contact); +int gcal_contact_get_phone_numbers_nr(gcal_contact_t contact); +char **gcal_contact_get_phone_numbers_field(gcal_contact_t contact); +char **gcal_contact_get_phone_numbers_type(gcal_contact_t contact); /** Access contact telephone. * @@ -377,6 +382,18 @@ char *gcal_contact_get_phone(gcal_contact_t contact); */ char *gcal_contact_get_address(gcal_contact_t contact); +/** Access Google group membership info. + * + * @param contact A contact object, see \ref gcal_contact. + * + * @return Pointer to internal object field (dont free it!) or NULL (in error + * case or if the field is not set). If the entry hasn't this field in the + * atom stream, it will be set to an empty string (i.e. ""). + + */ +int gcal_contact_get_groupMembership_nr(gcal_contact_t contact); +char **gcal_contact_get_groupMembership(gcal_contact_t contact); + /** Access contact photo data. * * @param contact A contact object, see \ref gcal_contact. @@ -425,11 +442,15 @@ int gcal_contact_set_title(gcal_contact_t contact, const char *field); * * @param contact A contact object, see \ref gcal_contact. * - * @param field String with contact email (e.g. "joe.doe@nobody.com"). + * @param num_elem Number of email addresses. * - * @return 0 for sucess, -1 otherwise. + * @param fields Email addresses. + * + * @param types Email addresses types. + * + * @return 0 for success, -1 otherwise */ -int gcal_contact_set_email(gcal_contact_t contact, const char *field); +int gcal_contact_set_email(gcal_contact_t contact, int num_elem, const char **fields, const char **types, int pref); /** Sets contact edit url. * @@ -480,11 +501,15 @@ int gcal_contact_set_etag(gcal_contact_t contact, const char *field); * * @param contact A contact object, see \ref gcal_contact. * - * @param field Phone number. + * @param num_elem Number of phone numbers. + * + * @param fields Phone numbers. + * + * @param types Phone number types. * * @return 0 for success, -1 otherwise */ -int gcal_contact_set_phone(gcal_contact_t contact, const char *field); +int gcal_contact_set_phone(gcal_contact_t contact, int num_elem, const char **fields, const char **types); /** Sets the contact address (for while only a single address is supported). * @@ -498,6 +523,18 @@ int gcal_contact_set_phone(gcal_contact_t contact, const char *field); */ int gcal_contact_set_address(gcal_contact_t contact, const char *field); +/** Sets the contact group membership info. + * + * @param contact A contact object, see \ref gcal_contact. + * + * @param num_elem Number of groups. + * + * @param field Group names. + * + * @return 0 for success, -1 otherwise + */ +int gcal_contact_set_groupMembership(gcal_contact_t contact, int num_elem, const char **fields); + /** Sets the organization title. * * diff --git a/inc/internal_gcal.h b/inc/internal_gcal.h index 849d3e9..abf42ef 100644 --- a/inc/internal_gcal.h +++ b/inc/internal_gcal.h @@ -188,8 +188,14 @@ struct gcal_contact { /** Has the common entry data fields (id, updated, title, edit_uri) */ struct gcal_entry common; /* Here starts google contact unique fields */ - /** Contact email */ - char *email; + /** Contact emails */ + char **emails_field; + /** Contact email types */ + char **emails_type; + /** Number of contact emails */ + int emails_nr; + /** Index of the preferred email */ + int pref_email; /* Here starts the extra fields */ /** Notes about contact */ @@ -200,10 +206,18 @@ struct gcal_contact { char *org_title; /** IM contact */ char *im; - /** Phone number */ - char *phone_number; + /** Phone numbers */ + char **phone_numbers_field; + /** Phone number types */ + char **phone_numbers_type; + /** Number of phone numbers */ + int phone_numbers_nr; /** Address */ char *post_address; + /** Google group membership info */ + char **groupMembership; + /** Google group membership info */ + int groupMembership_nr; /** Photo edit url */ char *photo; /** Photo byte array */ diff --git a/inc/xml_aux.h b/inc/xml_aux.h index 0138644..97c8e3c 100644 --- a/inc/xml_aux.h +++ b/inc/xml_aux.h @@ -57,6 +57,10 @@ static const char atom_ns[] = "atom"; static const char gd_href[] = "http://schemas.google.com/g/2005"; static const char gd_ns[] = "gd"; +/** Google group membership URL/URI */ +static const char gContact_href[] = "http://schemas.google.com/contact/2008"; +static const char gContact_ns[] = "gContact"; + /** Opensearch URL/URI */ static const char open_search_href[] = "http://a9.com/-/spec/opensearch/1.1/"; static const char open_search_ns[] = "openSearch"; diff --git a/src/atom_parser.c b/src/atom_parser.c index 95e5ac2..86b34c7 100644 --- a/src/atom_parser.c +++ b/src/atom_parser.c @@ -226,6 +226,74 @@ exit: return result; } +static int extract_and_check_multi(xmlDoc *doc, char *xpath_expression, int getContent, char *attr1, char *attr2, char* attr3, char ***values, char ***types, int *pref) +{ + xmlXPathObject *xpath_obj; + xmlNodeSet *node; + xmlChar *tmp; + int result = -1; + int i; + + xpath_obj = execute_xpath_expression(doc, + xpath_expression, + NULL); + + if ( (!values) || (attr2 && !types) || (attr3 && !pref) ) { + fprintf(stderr, "extract_and_check: null pointers received"); + goto exit; + } + + if (!xpath_obj) { + fprintf(stderr, "extract_and_check: failed to extract data"); + goto exit; + } + + node = xpath_obj->nodesetval; + + if (!node) { + result = 0; + goto cleanup; + } + result = node->nodeNr; + + *values = (char **)malloc( node->nodeNr * sizeof(char*) ); + if (attr2) + *types = (char **)malloc( node->nodeNr * sizeof(char*) ); + + for (i = 0; i < node->nodeNr; i++) { + if (getContent) + (*values)[i] = xmlNodeGetContent(node->nodeTab[i]); + else if (xmlHasProp(node->nodeTab[i],attr1)) + (*values)[i] = xmlGetProp(node->nodeTab[i],attr1); + else + (*values)[i] = strdup(" "); + + if (attr2) { + if (xmlHasProp(node->nodeTab[i],attr2)) { + tmp = xmlGetProp(node->nodeTab[i], attr2); + (*types)[i] = strdup(strchr(tmp,'#')+1); + xmlFree(tmp); + } + else + (*types)[i] = strdup(""); + } + + if (attr3) { + if (xmlHasProp(node->nodeTab[i],attr3)) { + tmp = xmlGetProp(node->nodeTab[i], attr3); + if (!strcmp(tmp,"true")) + *pref = i; + xmlFree(tmp); + } + } + } + +cleanup: + xmlXPathFreeObject(xpath_obj); +exit: + return result; +} + char *get_etag_attribute(xmlNode * a_node) { xmlChar *uri = NULL; @@ -478,12 +546,21 @@ int atom_extract_contact(xmlNode *entry, struct gcal_contact *ptr_entry) if (!ptr_entry->common.edit_uri) goto cleanup; - /* Gets the email contact field */ - ptr_entry->email = extract_and_check(doc, "//atom:entry/" - "gd:email", - "address"); + /* Gets email addressess */ + ptr_entry->emails_nr = extract_and_check_multi(doc, + "//atom:entry/" + "gd:email", + 0, + "address", + "rel", + "primary", + &ptr_entry->emails_field, + &ptr_entry->emails_type, + &ptr_entry->pref_email); + + /* TODO Commented to allow contacts without an email address if (!ptr_entry->email) - goto cleanup; + goto cleanup; */ /* Here begins extra fields */ @@ -508,10 +585,16 @@ int atom_extract_contact(xmlNode *entry, struct gcal_contact *ptr_entry) NULL); - /* Gets contact first phone number */ - ptr_entry->phone_number = extract_and_check(doc, + /* Gets contact phone numbers */ + ptr_entry->phone_numbers_nr = extract_and_check_multi(doc, "//atom:entry/" - "gd:phoneNumber/text()", + "gd:phoneNumber", + 1, + NULL, + "rel", + NULL, + &ptr_entry->phone_numbers_field, + &ptr_entry->phone_numbers_type, NULL); /* Gets contact first address */ @@ -520,6 +603,17 @@ int atom_extract_contact(xmlNode *entry, struct gcal_contact *ptr_entry) "gd:postalAddress/text()", NULL); + /* Gets contact group membership info */ + ptr_entry->groupMembership_nr = extract_and_check_multi(doc, + "//atom:entry/" + "gContact:groupMembershipInfo[@deleted='false']", + 0, + "href", + NULL, + NULL, + &ptr_entry->groupMembership, + NULL, + NULL); /* Gets contact photo edit url and test for etag */ ptr_entry->photo = extract_and_check(doc, "//atom:entry/" diff --git a/src/gcal_parser.c b/src/gcal_parser.c index 5ca0467..c36493e 100644 --- a/src/gcal_parser.c +++ b/src/gcal_parser.c @@ -457,12 +457,16 @@ int xmlcontact_create(struct gcal_contact *contact, char **xml_contact, * contact X calendar. */ int result = -1; + int i; xmlDoc *doc = NULL; xmlNode *root = NULL; xmlNode *node = NULL; xmlNode *child = NULL; xmlNs *ns; + xmlNs *ns2; xmlChar *xml_str = NULL; + char *temp; + const char * rel_prefix = "http://schemas.google.com/g/2005#"; doc = xmlNewDoc(BAD_CAST "1.0"); root = xmlNewNode(NULL, BAD_CAST "entry"); @@ -478,6 +482,9 @@ int xmlcontact_create(struct gcal_contact *contact, char **xml_contact, ns = xmlNewNs(root, BAD_CAST gd_href, BAD_CAST "gd"); + /* Google contact group */ + ns2 = xmlNewNs(root, BAD_CAST gContact_href, BAD_CAST "gContact"); + xmlDocSetRootElement(doc, root); /* category element */ @@ -522,18 +529,25 @@ int xmlcontact_create(struct gcal_contact *contact, char **xml_contact, xmlAddChild(root, node); } - - /* There are 3 types of e-mail: other, work, home */ - node = xmlNewNode(ns, "email"); - if (!node) - goto cleanup; - - xmlSetProp(node, BAD_CAST "rel", - BAD_CAST "http://schemas.google.com/g/2005#other"); - xmlSetProp(node, BAD_CAST "address", - BAD_CAST contact->email); - xmlAddChild(root, node); - + /* email addresses */ + if (contact->emails_nr > 0) { + for (i = 0; i < contact->emails_nr; i++) { + if (!(node = xmlNewNode(ns, "email"))) + goto cleanup; + temp = (char *)malloc((strlen(contact->emails_type[i])+strlen(rel_prefix)+1) * sizeof(char)); + strcpy(temp, rel_prefix); + strcat(temp, contact->emails_type[i]); + xmlSetProp(node, BAD_CAST "rel", + BAD_CAST temp); + xmlSetProp(node, BAD_CAST "address", + BAD_CAST contact->emails_field[i]); + if (i == contact->pref_email) + xmlSetProp(node, BAD_CAST "primary", + BAD_CAST "true"); + xmlAddChild(root, node); + free(temp); + } + } /* Here begin extra fields */ /* content element */ @@ -569,18 +583,23 @@ int xmlcontact_create(struct gcal_contact *contact, char **xml_contact, xmlAddChild(root, node); } - /* For while I will get only the first phone number - * TODO: use an array in gcal_contact to support multiple - */ - if (contact->phone_number) { - if (!(node = xmlNewNode(ns, "phoneNumber"))) - goto cleanup; - /* TODO: support user settting phone type */ - xmlSetProp(node, BAD_CAST "rel", - BAD_CAST "http://schemas.google.com/g/2005#other"); - xmlNodeAddContent(node, contact->phone_number); - xmlAddChild(root, node); + /* Get phone numbers */ + if (contact->phone_numbers_nr > 0) { + for (i = 0; i < contact->phone_numbers_nr; i++) { + if (!(node = xmlNewNode(ns, "phoneNumber"))) + goto cleanup; + /* TODO: support user settting phone type */ + temp = (char *)malloc((strlen(contact->phone_numbers_type[i])+strlen(rel_prefix)+1) * sizeof(char)); + strcpy(temp, rel_prefix); + strcat(temp, contact->phone_numbers_type[i]); + xmlSetProp(node, BAD_CAST "rel", + BAD_CAST temp); + + xmlNodeAddContent(node, contact->phone_numbers_field[i]); + xmlAddChild(root, node); + free(temp); + } } /* For while I will get only the first postal address @@ -596,6 +615,18 @@ int xmlcontact_create(struct gcal_contact *contact, char **xml_contact, xmlAddChild(root, node); } + /* Google group membership info */ + if (contact->groupMembership_nr > 0) { + for (i = 0; i < contact->groupMembership_nr; i++) { + if (!(node = xmlNewNode(ns2, "groupMembershipInfo"))) + goto cleanup; + xmlSetProp(node, BAD_CAST "deleted", + BAD_CAST "false"); + xmlSetProp(node, BAD_CAST "href", + BAD_CAST contact->groupMembership[i]); + xmlAddChild(root, node); + } + } /* TODO: implement missing fields (im) */ diff --git a/src/gcont.c b/src/gcont.c index 9a1b125..d2e7ffd 100644 --- a/src/gcont.c +++ b/src/gcont.c @@ -156,6 +156,17 @@ static void clean_string(char *ptr_str) free(ptr_str); } +static void clean_multi_string(char **ptr_str, int n) +{ + int i; + + for (i = 0; i < n; i++) + if (ptr_str[i]) + free(ptr_str[i]); + if (ptr_str) + free(ptr_str); +} + void gcal_init_contact(struct gcal_contact *contact) { if (!contact) @@ -165,9 +176,13 @@ void gcal_init_contact(struct gcal_contact *contact) contact->common.id = contact->common.updated = NULL; contact->common.title = contact->common.xml = NULL; contact->common.edit_uri = contact->common.etag = NULL; - contact->email = contact->content = NULL; + contact->emails_field = contact->emails_type = NULL; + contact->emails_nr = contact->pref_email = 0; + contact->content = NULL; contact->org_name = contact->org_title = contact->im = NULL; - contact->phone_number = contact->post_address = NULL; + contact->phone_numbers_field = contact->phone_numbers_type = NULL; + contact->phone_numbers_nr = contact->groupMembership_nr = 0; + contact->post_address = contact->groupMembership = NULL; contact->photo = contact->photo_data = NULL; contact->photo_length = 0; } @@ -182,7 +197,9 @@ void gcal_destroy_contact(struct gcal_contact *contact) clean_string(contact->common.title); clean_string(contact->common.edit_uri); clean_string(contact->common.etag); - clean_string(contact->email); + clean_multi_string(contact->emails_field, contact->emails_nr); + clean_multi_string(contact->emails_type, contact->emails_nr); + contact->emails_nr = contact->pref_email = 0; clean_string(contact->common.xml); /* Extra fields */ @@ -190,7 +207,10 @@ void gcal_destroy_contact(struct gcal_contact *contact) clean_string(contact->org_name); clean_string(contact->org_title); clean_string(contact->im); - clean_string(contact->phone_number); + clean_multi_string(contact->phone_numbers_field, contact->phone_numbers_nr); + clean_multi_string(contact->phone_numbers_type, contact->phone_numbers_nr); + clean_multi_string(contact->groupMembership, contact->groupMembership_nr); + contact->phone_numbers_nr = contact->groupMembership_nr = 0; clean_string(contact->post_address); clean_string(contact->photo); clean_string(contact->photo_data); diff --git a/src/gcontact.c b/src/gcontact.c index d3d8218..93ec44e 100644 --- a/src/gcontact.c +++ b/src/gcontact.c @@ -317,11 +317,32 @@ char gcal_contact_is_deleted(gcal_contact_t contact) /* This are the fields unique to calendar contacts */ -char *gcal_contact_get_email(gcal_contact_t contact) +int gcal_contact_get_emails_nr(gcal_contact_t contact) +{ + if ((!contact)) + return 0; + return contact->emails_nr; +} + +int gcal_contact_get_pref_email(gcal_contact_t contact) +{ + if ((!contact)) + return 0; + return contact->pref_email; +} + +char **gcal_contact_get_emails_field(gcal_contact_t contact) +{ + if ((!contact)) + return NULL; + return contact->emails_field; +} + +char **gcal_contact_get_emails_type(gcal_contact_t contact) { if ((!contact)) return NULL; - return contact->email; + return contact->emails_type; } char *gcal_contact_get_content(gcal_contact_t contact) @@ -352,11 +373,25 @@ char *gcal_contact_get_im(gcal_contact_t contact) return contact->im; } -char *gcal_contact_get_phone(gcal_contact_t contact) +int gcal_contact_get_phone_numbers_nr(gcal_contact_t contact) +{ + if ((!contact)) + return 0; + return contact->phone_numbers_nr; +} + +char **gcal_contact_get_phone_numbers_field(gcal_contact_t contact) +{ + if ((!contact)) + return NULL; + return contact->phone_numbers_field; +} + +char **gcal_contact_get_phone_numbers_type(gcal_contact_t contact) { if ((!contact)) return NULL; - return contact->phone_number; + return contact->phone_numbers_type; } char *gcal_contact_get_address(gcal_contact_t contact) @@ -366,6 +401,20 @@ char *gcal_contact_get_address(gcal_contact_t contact) return contact->post_address; } +int gcal_contact_get_groupMembership_nr(gcal_contact_t contact) +{ + if ((!contact)) + return 0; + return contact->groupMembership_nr; +} + +char **gcal_contact_get_groupMembership(gcal_contact_t contact) +{ + if ((!contact)) + return NULL; + return contact->groupMembership; +} + char *gcal_contact_get_photo(gcal_contact_t contact) { if ((!contact)) @@ -400,18 +449,34 @@ int gcal_contact_set_title(gcal_contact_t contact, const char *field) return result; } -int gcal_contact_set_email(gcal_contact_t contact, const char *field) +int gcal_contact_set_email(gcal_contact_t contact, int num_elem, const char **fields, const char **types, int pref) { int result = -1; + int temp; - if ((!contact) || (!field)) + if ((!contact) || (!num_elem) || (!fields) || (!types)) return result; - if (contact->email) - free(contact->email); + if (contact->emails_nr > 0) { + for (temp = 0; temp < contact->emails_nr; temp++) { + if (contact->emails_field[temp]) + free(contact->emails_field[temp]); + if (contact->emails_type[temp]) + free(contact->emails_type[temp]); + } + free(contact->emails_field); + free(contact->emails_type); + } - contact->email = strdup(field); - if (contact->email) + contact->emails_nr = num_elem; + contact->pref_email = pref; + contact->emails_field = (char**) malloc(num_elem * sizeof(char*)); + contact->emails_type = (char**) malloc(num_elem * sizeof(char*)); + for (temp = 0; temp < contact->emails_nr; temp++) { + contact->emails_field[temp] = fields[temp]; + contact->emails_type[temp] = types[temp]; + } + if (contact->emails_nr) result = 0; return result; @@ -470,18 +535,33 @@ int gcal_contact_set_etag(gcal_contact_t contact, const char *field) return result; } -int gcal_contact_set_phone(gcal_contact_t contact, const char *field) +int gcal_contact_set_phone(gcal_contact_t contact, int num_elem, const char **fields, const char **types) { int result = -1; + int temp; - if ((!contact) || (!field)) + if ((!contact) || (!num_elem) || (!fields) || (!types)) return result; - if (contact->phone_number) - free(contact->phone_number); + if (contact->phone_numbers_nr > 0) { + for (temp = 0; temp < contact->phone_numbers_nr; temp++) { + if (contact->phone_numbers_field[temp]) + free(contact->phone_numbers_field[temp]); + if (contact->phone_numbers_type[temp]) + free(contact->phone_numbers_type[temp]); + } + free(contact->phone_numbers_field); + free(contact->phone_numbers_type); + } - contact->phone_number = strdup(field); - if (contact->phone_number) + contact->phone_numbers_nr = num_elem; + contact->phone_numbers_field = (char**) malloc(num_elem * sizeof(char*)); + contact->phone_numbers_type = (char**) malloc(num_elem * sizeof(char*)); + for (temp = 0; temp < contact->phone_numbers_nr; temp++) { + contact->phone_numbers_field[temp] = fields[temp]; + contact->phone_numbers_type[temp] = types[temp]; + } + if (contact->phone_numbers_nr) result = 0; return result; @@ -504,6 +584,33 @@ int gcal_contact_set_address(gcal_contact_t contact, const char *field) return result; } +int gcal_contact_set_groupMembership(gcal_contact_t contact, int num_elem, const char **fields) +{ + int result = -1; + int temp; + + if ((!contact) || (!num_elem) || (!fields)) + return result; + + if (contact->groupMembership_nr > 0) { + for (temp = 0; temp < contact->groupMembership_nr; temp++) { + if (contact->groupMembership[temp]) + free(contact->groupMembership[temp]); + } + free(contact->groupMembership); + } + + contact->groupMembership_nr = num_elem; + contact->groupMembership = (char**) malloc(num_elem * sizeof(char*)); + for (temp = 0; temp < contact->groupMembership_nr; temp++) { + contact->groupMembership[temp] = fields[temp]; + } + if (contact->groupMembership_nr) + result = 0; + + return result; +} + int gcal_contact_set_profission(gcal_contact_t contact, const char *field) { int result = -1; diff --git a/src/xml_aux.c b/src/xml_aux.c index 26a1ef1..bc69d6c 100644 --- a/src/xml_aux.c +++ b/src/xml_aux.c @@ -61,6 +61,7 @@ int register_namespaces(xmlXPathContext *xpathCtx, const xmlChar *name_space, else { if (register_namespaces(xpathCtx, atom_ns, atom_href) || register_namespaces(xpathCtx, gd_ns, gd_href) || + register_namespaces(xpathCtx, gContact_ns, gContact_href) || register_namespaces(xpathCtx, open_search_ns, open_search_href)) goto exit; --Boundary-00=_C4IQLOK+zAPVpoR Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline _______________________________________________ Kde-extra-gear mailing list Kde-extra-gear@kde.org https://mail.kde.org/mailman/listinfo/kde-extra-gear --Boundary-00=_C4IQLOK+zAPVpoR--