Merge phonebook entries belong to one person
authorYang Gu <yang.gu@intel.com>
Mon, 22 Jun 2009 02:59:25 +0000 (10:59 +0800)
committerDenis Kenzior <denkenz@gmail.com>
Mon, 22 Jun 2009 18:22:13 +0000 (13:22 -0500)
drivers/atmodem/phonebook.c
src/phonebook.c

index 82adf10..05ef090 100644 (file)
@@ -55,7 +55,7 @@ static void at_cpbr_notify(GAtResult *result, gpointer user_data)
                const char *number;
                int type;
                const char *text;
-               int hidden = 0;
+               int hidden = -1;
                const char *group = NULL;
                const char *adnumber = NULL;
                int adtype = -1;
index fa05d43..4cfb04e 100644 (file)
 
 #define PHONEBOOK_FLAG_CACHED 0x1
 
+enum {
+       TEL_TYPE_VOICE,
+       TEL_TYPE_HOME,
+       TEL_TYPE_MOBILE,
+       TEL_TYPE_FAX,
+       TEL_TYPE_WORK
+};
+
 struct phonebook_data {
        struct ofono_phonebook_ops *ops;
        DBusMessage *pending;
        int storage_index; /* go through all supported storage */
        int flags;
        GString *vcards; /* entries with vcard 3.0 format */
+       GSList *merge_list; /* cache the entries that may need a merge */
+};
+
+struct phonebook_number {
+       char *number;
+       int type; /* international or not */
+       int category; /* represent for "WORK", "HOME", etc */
+       int prefer;
+};
+
+struct phonebook_person {
+       GSList *number_list; /* one person may have more than one numbers */
+       char *text;
+       int hidden;
+       char *group;
+       char *secondtext;
+       char *email;
+       char *sip_uri;
+       char *tel_uri;
 };
 
 static const char *storage_support[] = { "\"SM\"", "\"ME\"", NULL };
@@ -135,20 +162,93 @@ static void add_slash(char *dest, const char *src, int len_max, int len)
        return;
 }
 
-static void vcard_printf_number(GString *entries_vcard_pointer, int type,
-                               const char *number, int prefer)
+static void vcard_printf_begin(GString *vcards)
+{
+       vcard_printf(vcards, "BEGIN:VCARD");
+       vcard_printf(vcards, "VERSION:3.0");
+}
+
+static void vcard_printf_text(GString *vcards, const char *text)
 {
-       char *pref = "", *intl = "";
+       char field[LEN_MAX];
+       add_slash(field, text, LEN_MAX, strlen(text));
+       vcard_printf(vcards, "FN:%s", field);
+}
+
+static void vcard_printf_number(GString *vcards, const char *number, int type,
+                                       int category, int prefer)
+{
+       char *pref = "", *intl = "", *category_string = "";
        char buf[128];
 
+       if (!number || !strlen(number) || !type)
+               return;
+
        if (prefer)
                pref = "PREF,";
 
+       switch (category) {
+       case TEL_TYPE_HOME:
+               category_string = "HOME";
+               break;
+       case TEL_TYPE_MOBILE:
+               category_string = "CELL";
+               break;
+       case TEL_TYPE_FAX:
+               category_string = "FAX";
+               break;
+       case TEL_TYPE_WORK:
+               category_string = "WORK";
+               break;
+       case TEL_TYPE_VOICE:
+       default:
+               category_string = "VOICE";
+               break;
+       }
+
        if ((type == TYPE_INTERNATIONAL) && (number[0] != '+'))
                intl = "+";
 
-       sprintf(buf, "TEL;TYPE=\%sVOICE:\%s\%s", pref, intl, number);
-       vcard_printf(entries_vcard_pointer, buf, number);
+       sprintf(buf, "TEL;TYPE=\%s%s:\%s\%s", pref,
+                       category_string, intl, number);
+       vcard_printf(vcards, buf, number);
+}
+
+static void vcard_printf_group(GString *vcards,        const char *group)
+{
+       int len = strlen(group);
+       if (group && len) {
+               char field[LEN_MAX];
+               add_slash(field, group, LEN_MAX, len);
+               vcard_printf(vcards, "CATEGORIES:%s", field);
+       }
+}
+
+static void vcard_printf_email(GString *vcards, const char *email)
+{
+       int len = strlen(email);
+       if (email && len) {
+               char field[LEN_MAX];
+               add_slash(field, email, LEN_MAX, len);
+               vcard_printf(vcards,
+                               "EMAIL;TYPE=INTERNET:%s", field);
+       }
+}
+
+static void vcard_printf_sip_uri(GString *vcards, const char *sip_uri)
+{
+       int len = strlen(sip_uri);
+       if (sip_uri && len) {
+               char field[LEN_MAX];
+               add_slash(field, sip_uri, LEN_MAX, len);
+               vcard_printf(vcards, "IMPP;TYPE=SIP:%s", field);
+       }
+}
+
+static void vcard_printf_end(GString *vcards)
+{
+       vcard_printf(vcards, "END:VCARD");
+       vcard_printf(vcards, "");
 }
 
 static DBusMessage *generate_export_entries_reply(struct ofono_modem *modem,
@@ -169,6 +269,50 @@ static DBusMessage *generate_export_entries_reply(struct ofono_modem *modem,
        return reply;
 }
 
+static gboolean need_merge(const char *text)
+{
+       int len = strlen(text);
+       char c = text[len-1];
+       if ((text[len-2] == '/') &&
+               ((c == 'w') || (c == 'h') || (c == 'm') || (c == 'o')))
+               return TRUE;
+       return FALSE;
+}
+
+static void merge_field_generic(char **str1, const char *str2)
+{
+       if ((*str1 == NULL) && (str2 != NULL))
+               *str1 = g_strdup(str2);
+}
+
+static void merge_field_number(GSList **l, const char *number, int type,
+                               char c, int prefer)
+{
+       struct phonebook_number *pn = g_new0(struct phonebook_number, 1);
+       int category;
+
+       pn->number = g_strdup(number);
+       pn->type = type;
+       switch (c) {
+       case 'w':
+       case 'o':
+               category = TEL_TYPE_WORK;
+               break;
+       case 'h':
+               category = TEL_TYPE_HOME;
+               break;
+       case 'm':
+               category = TEL_TYPE_MOBILE;
+               break;
+       default:
+               category = TEL_TYPE_VOICE;
+               break;
+       }
+       pn->category = category;
+       pn->prefer = prefer;
+       *l = g_slist_append(*l, pn);
+}
+
 void ofono_phonebook_entry(struct ofono_modem *modem, int index,
                                const char *number, int type,
                                const char *text, int hidden,
@@ -179,37 +323,54 @@ void ofono_phonebook_entry(struct ofono_modem *modem, int index,
 {
        struct phonebook_data *phonebook = modem->phonebook;
        char field[LEN_MAX];
-       int len;
-
-       vcard_printf(phonebook->vcards, "BEGIN:VCARD");
-       vcard_printf(phonebook->vcards, "VERSION:3.0");
-
-       add_slash(field, text, LEN_MAX, strlen(text));
-
-       vcard_printf(phonebook->vcards, "FN:%s", field);
-       vcard_printf_number(phonebook->vcards, type, number, 1);
-
-       if (group && (len = strlen(group))) {
-               add_slash(field, group, LEN_MAX, len);
-               vcard_printf(phonebook->vcards, "CATEGORIES:%s", field);
-       }
-
-       if (adnumber && strlen(adnumber) && adtype != -1)
-               vcard_printf_number(phonebook->vcards, adtype, adnumber, 0);
-
-       if (email && (len = strlen(email))) {
-               add_slash(field, email, LEN_MAX, len);
-               vcard_printf(phonebook->vcards,
-                               "EMAIL;TYPE=INTERNET:%s", field);
-       }
-
-       if (sip_uri && (len = strlen(sip_uri))) {
-               add_slash(field, sip_uri, LEN_MAX, len);
-               vcard_printf(phonebook->vcards, "IMPP;TYPE=SIP:%s", field);
+       /*
+        * We need to collect all the entries that belong to one person,
+        * so that only one vCard will be generated at last.
+        * Entries only differ with '/w', '/h', '/m', etc. in field text
+        * are deemed as entries of one person.
+        */
+       if (need_merge(text)) {
+               GSList *l;
+               int has_merge = 0;
+               int len_text = strlen(text);
+               char *text_temp = g_strndup(text, len_text - 2);
+               struct phonebook_person *person;
+               for (l = phonebook->merge_list; l; l = l->next) {
+                       person = l->data;
+                       if (!strcmp(text_temp, person->text)) {
+                               has_merge = 1;
+                               break;
+                       }
+               }
+               if (has_merge == 0) {
+                       person = g_new0(struct phonebook_person, 1);
+                       phonebook->merge_list = g_slist_append(
+                                       phonebook->merge_list, person);
+               }
+               merge_field_generic(&(person->text), text_temp);
+               merge_field_number(&(person->number_list), number, type,
+                                       text[len_text - 1], 1);
+               merge_field_number(&(person->number_list), adnumber, adtype,
+                                       text[len_text - 1], 0);
+               merge_field_generic(&(person->group), group);
+               merge_field_generic(&(person->secondtext), secondtext);
+               merge_field_generic(&(person->email), email);
+               merge_field_generic(&(person->sip_uri), sip_uri);
+               merge_field_generic(&(person->tel_uri), tel_uri);
+               g_free(text_temp);
+               return;
        }
 
-       vcard_printf(phonebook->vcards, "END:VCARD");
-       vcard_printf(phonebook->vcards, "");
+       vcard_printf_begin(phonebook->vcards);
+       vcard_printf_text(phonebook->vcards, text);
+       vcard_printf_number(phonebook->vcards, number, type,
+                               TEL_TYPE_VOICE, 1);
+       vcard_printf_number(phonebook->vcards, adnumber, adtype,
+                               TEL_TYPE_VOICE, 0);
+       vcard_printf_group(phonebook->vcards, group);
+       vcard_printf_email(phonebook->vcards, email);
+       vcard_printf_sip_uri(phonebook->vcards, sip_uri);
+       vcard_printf_end(phonebook->vcards);
 }
 
 static void export_phonebook_cb(const struct ofono_error *error, void *data)
@@ -232,6 +393,7 @@ static void export_phonebook(struct ofono_modem *modem)
        struct phonebook_data *phonebook = modem->phonebook;
        DBusMessage *reply;
        const char *pb = storage_support[phonebook->storage_index];
+       GSList *l, *m;
 
        if (pb) {
                phonebook->ops->export_entries(modem, pb,
@@ -239,6 +401,22 @@ static void export_phonebook(struct ofono_modem *modem)
                return;
        }
 
+       /* convert the collected entries that are already merged to vcard */
+       for (l = phonebook->merge_list; l; l = l->next) {
+               struct phonebook_person *person = l->data;
+               vcard_printf_begin(phonebook->vcards);
+               vcard_printf_text(phonebook->vcards, person->text);
+               for (m = person->number_list; m; m = m->next) {
+                       struct phonebook_number *pn = m->data;
+                       vcard_printf_number(phonebook->vcards, pn->number,
+                                       pn->type, pn->category, pn->prefer);
+               }
+               vcard_printf_group(phonebook->vcards, person->group);
+               vcard_printf_email(phonebook->vcards, person->email);
+               vcard_printf_sip_uri(phonebook->vcards, person->sip_uri);
+               vcard_printf_end(phonebook->vcards);
+       }
+
        reply = generate_export_entries_reply(modem, phonebook->pending);
 
        if (!reply) {