static char *evcard_to_string(EVCard *evcard, unsigned int format, uint64_t filter) { EVCard *evcard2; GList *l; char *vcard; if (!filter) return e_vcard_to_string(evcard, EVC_FORMAT_VCARD_30); /* XXX There is no support for VCARD 2.1 at this time */ /* * Mandatory attributes for vCard 2.1 are VERSION ,N and TEL. * Mandatory attributes for vCard 3.0 are VERSION, N, FN and TEL */ filter = format == EVC_FORMAT_VCARD_30 ? filter | 0x87: filter | 0x85; l = e_vcard_get_attributes(evcard); evcard2 = e_vcard_new(); for (; l; l = g_list_next(l)) { EVCardAttribute *attrib = l->data; const char *name; int i; if (!attrib) continue; name = e_vcard_attribute_get_name(attrib); for (i = 0; attribute_mask[i] != NULL; i++) { if (!(filter & (1 << i))) continue; if (g_strcmp0(name, attribute_mask[i]) != 0) continue; e_vcard_add_attribute(evcard2, e_vcard_attribute_copy(attrib)); } } vcard = e_vcard_to_string(evcard2, format); g_object_unref(evcard2); return vcard; }
static gboolean has_only_one (EVCard *vcard, const gchar *attrname) { gboolean found = FALSE; GList *iter; for (iter = e_vcard_get_attributes (vcard); iter; iter = iter->next) { EVCardAttribute *attr = iter->data; if (g_strcmp0 (e_vcard_attribute_get_name (attr), attrname) == 0) { if (found) return FALSE; found = TRUE; } } return found; }
/* This function returns a GtkEntry derived from field_id for a particular * contact. * Returns GtkWidget * on success, NULL on failure */ GtkWidget * contacts_label_widget_new (EContact *contact, EVCardAttribute *attr, const gchar *name, gboolean multi_line, gboolean *changed) { GtkWidget *label_widget, *type_edit = NULL, *container; gchar *label_markup; /* Create label/button text */ label_markup = g_strdup_printf (_("<b>%s:</b>"), name); /* Create widget */ label_widget = gtk_label_new (NULL); gtk_label_set_use_markup (GTK_LABEL (label_widget), TRUE); gtk_label_set_markup (GTK_LABEL (label_widget), label_markup); gtk_label_set_justify (GTK_LABEL (label_widget), GTK_JUSTIFY_RIGHT); if (/*(e_vcard_attribute_is_single_valued (attr)) &&*/ (multi_line == FALSE)) gtk_misc_set_alignment (GTK_MISC (label_widget), 1, 0.5); else gtk_misc_set_alignment (GTK_MISC (label_widget), 1, 0); g_free (label_markup); if (e_vcard_attribute_is_single_valued (attr)) type_edit = contacts_type_edit_widget_new (attr, multi_line, changed); if (type_edit) { gtk_widget_show (type_edit); gtk_widget_show (label_widget); container = gtk_hbox_new (FALSE, 6); gtk_box_pack_start (GTK_BOX (container), label_widget, FALSE, TRUE, 0); gtk_box_pack_end (GTK_BOX (container), type_edit, FALSE, FALSE, 0); label_widget = container; } gtk_widget_set_name (label_widget, e_vcard_attribute_get_name (attr)); return label_widget; }
/* Get a list of the specified attributes from a contact */ GList * hito_vcard_get_named_attributes (EVCard *contact, const char *name) { GList *attrs = NULL, *l; g_return_val_if_fail (E_IS_VCARD (contact), NULL); g_return_val_if_fail (name != NULL, NULL); for (l = e_vcard_get_attributes (E_VCARD (contact)); l; l = l->next) { EVCardAttribute *attr; const char *n; attr = (EVCardAttribute *) l->data; n = e_vcard_attribute_get_name (attr); if (strcmp (n, name) == 0) attrs = g_list_prepend (attrs, attr); } return g_list_reverse (attrs); }
static EVCardAttribute * contacts_add_attr (EVCard *contact, const gchar *vcard_field) { const ContactsField *field = contacts_get_contacts_field (vcard_field); if (field) { guint j; /* Create missing attribute */ EVCardAttribute *attr = e_vcard_attribute_new ( "", vcard_field); /* Initialise values */ for (j = contacts_get_structured_field_size ( e_vcard_attribute_get_name (attr)); j > 0; j--) e_vcard_attribute_add_value (attr, ""); /* Add to contact */ e_vcard_add_attribute (contact, attr); return attr; } return NULL; }
/* Calbacks */ static void moko_contacts_add_contact (MokoContacts *contacts, EContact *e_contact) { MokoContactsPrivate *priv; MokoContact *m_contact = NULL; const gchar *name, *uid; GList *attributes, *params, *numbers; g_return_if_fail (MOKO_IS_CONTACTS (contacts)); g_return_if_fail (E_IS_CONTACT (e_contact)); priv = contacts->priv; uid = e_contact_get_const (e_contact, E_CONTACT_UID); if (g_hash_table_lookup (priv->uids, uid)) return; name = e_contact_get_const (e_contact, E_CONTACT_FULL_NAME); if (!name || (g_utf8_strlen (name, -1) <= 0)) name = "Unknown"; /* Create the contact & append to the list */ m_contact = g_new0 (MokoContact, 1); m_contact->name = g_strdup (name); m_contact->uid = g_strdup (uid); m_contact->photo = NULL; priv->contacts = g_list_append (priv->contacts, m_contact); g_hash_table_insert (priv->uids, g_strdup (uid), m_contact); /* Now go through the numbers,creating MokoNumber for them */ for (attributes = e_vcard_get_attributes (E_VCARD(e_contact)); attributes; attributes = attributes->next) { MokoContactEntry *entry; const gchar *phone; const char *attr; attr = e_vcard_attribute_get_name (attributes->data); if (!strcmp (attr, EVC_TEL)) { for (numbers = e_vcard_attribute_get_values (attributes->data); numbers; numbers = numbers->next) { phone = numbers->data; if (phone) { entry = g_new0 (MokoContactEntry, 1); params = e_vcard_attribute_get_param (attributes->data, "TYPE"); if (params) entry->desc = g_strdup (params->data); entry->number = normalize (phone); entry->contact = m_contact; priv->entries = g_list_append (priv->entries, (gpointer)entry); g_hash_table_insert (priv->prefixes, g_strdup (entry->number), (gpointer)entry); add_number (&priv->start, entry); } } } } }
static ESExpResult * entry_compare (SearchContext *ctx, struct _ESExp *f, gint argc, struct _ESExpResult **argv, CompareFunc compare) { ESExpResult *r; gint truth = FALSE; if ((argc == 2 && argv[0]->type == ESEXP_RES_STRING && argv[1]->type == ESEXP_RES_STRING) || (argc == 3 && argv[0]->type == ESEXP_RES_STRING && argv[1]->type == ESEXP_RES_STRING && argv[2]->type == ESEXP_RES_STRING)) { gchar *propname; struct prop_info *info = NULL; const gchar *region = NULL; gint i; gboolean any_field; gboolean saw_any = FALSE; if (argc > 2) region = argv[2]->value.string; propname = argv[0]->value.string; any_field = !strcmp (propname, "x-evolution-any-field"); for (i = 0; i < G_N_ELEMENTS (prop_info_table); i++) { if (any_field || !strcmp (prop_info_table[i].query_prop, propname)) { saw_any = TRUE; info = &prop_info_table[i]; if (any_field && info->field_id == E_CONTACT_UID) { /* We need to skip UID from any field contains search * any-field search should be supported for the * visible fields only. */ truth = FALSE; } else if (info->prop_type == PROP_TYPE_NORMAL) { const gchar *prop = NULL; /* straight string property matches */ prop = e_contact_get_const (ctx->contact, info->field_id); if (prop && compare (prop, argv[1]->value.string, region)) { truth = TRUE; } if ((!prop) && compare ("", argv[1]->value.string, region)) { truth = TRUE; } } else if (info->prop_type == PROP_TYPE_LIST) { /* the special searches that match any of the list elements */ truth = info->list_compare (ctx->contact, argv[1]->value.string, region, compare); } else if (info->prop_type == PROP_TYPE_DATE) { /* the special searches that match dates */ EContactDate *date; date = e_contact_get (ctx->contact, info->field_id); if (date) { truth = compare_date (date, argv[1]->value.string, region, compare); e_contact_date_free (date); } } else { g_warn_if_reached (); saw_any = FALSE; break; } /* if we're looking at all fields and find a match, * or if we're just looking at this one field, * break. */ if ((any_field && truth) || !any_field) break; } } if (!saw_any) { /* propname didn't match to any of our known "special" properties, * so try to find if it isn't a real field and if so, then compare * against value in this field only */ EContactField fid = e_contact_field_id (propname); if (fid >= E_CONTACT_FIELD_FIRST && fid < E_CONTACT_FIELD_LAST) { const gchar *prop = e_contact_get_const (ctx->contact, fid); if (prop && compare (prop, argv[1]->value.string, region)) { truth = TRUE; } if ((!prop) && compare ("", argv[1]->value.string, region)) { truth = TRUE; } } else { /* it is not direct EContact known field, so try to find * it in EVCard attributes */ GList *a, *attrs = e_vcard_get_attributes (E_VCARD (ctx->contact)); for (a = attrs; a && !truth; a = a->next) { EVCardAttribute *attr = (EVCardAttribute *) a->data; if (g_ascii_strcasecmp (e_vcard_attribute_get_name (attr), propname) == 0) { GList *l, *values = e_vcard_attribute_get_values (attr); for (l = values; l && !truth; l = l->next) { const gchar *value = l->data; if (value && compare (value, argv[1]->value.string, region)) { truth = TRUE; } else if ((!value) && compare ("", argv[1]->value.string, region)) { truth = TRUE; } } } } } } } r = e_sexp_result_new (f, ESEXP_RES_BOOL); r->value.boolean = truth; return r; }
/* This function returns a GtkEntry derived from field_id for a particular * contact. * Returns GtkWidget * on success, NULL on failure * TODO: Lots of duplicated code here, perhaps add a few bits to utils? */ static GtkWidget * contacts_edit_widget_new (EContact *contact, EVCardAttribute *attr, gboolean multi_line, gboolean *changed) { GtkWidget *type_edit; EContactChangeData *data; const gchar *attr_name = e_vcard_attribute_get_name (attr); /* Create data structure for changes */ data = g_new0 (EContactChangeData, 1); data->contact = contact; data->attr = attr; data->changed = changed; /* Create widget */ if (!e_vcard_attribute_is_single_valued (attr)) { /* Handle structured fields */ GtkWidget *adr_table; guint field; GList *values = e_vcard_attribute_get_values (attr); adr_table = gtk_table_new (1, 2, FALSE); gtk_table_set_col_spacings (GTK_TABLE (adr_table), 6); gtk_table_set_row_spacings (GTK_TABLE (adr_table), 6); gtk_container_set_border_width (GTK_CONTAINER (adr_table), 6); /* Set widget that contains attribute data */ data->widget = adr_table; /* Add type editor */ type_edit = contacts_type_edit_widget_new (attr, multi_line, changed); if (type_edit) { GtkWidget *label = gtk_label_new (NULL); gtk_label_set_use_markup (GTK_LABEL (label), TRUE); gtk_label_set_markup (GTK_LABEL (label), /* TODO: make nicer for i18n */ _("<b>Type:</b>")); gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5); gtk_table_attach (GTK_TABLE (adr_table), label, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0); gtk_table_attach (GTK_TABLE (adr_table), type_edit, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (label); gtk_widget_show (type_edit); } /* Create sub-fields */ for (field = 0; values; values = values->next, field++) { /* If no information exists, assume field is * single-lined. * TODO: It may be more intelligent in the future * to look at the value and search for new-line * characters. */ gboolean multiline = FALSE; GtkWidget *label = NULL, *entry; const ContactsStructuredField *sfield = contacts_get_structured_field (attr_name, field); const gchar *string = (const gchar *)values->data; /* If we have the information, label the field */ if (sfield) { gchar *label_markup; if (sfield->priority == 0) continue; label = gtk_label_new (NULL); multiline = sfield->multiline; gtk_label_set_use_markup (GTK_LABEL (label), TRUE); label_markup = g_strdup_printf ( /** Translators, the first * argument is the field's * name, ex. <b>Country:</b> */ _("<b>%s:</b>"), gettext(sfield->subfield_name)); gtk_label_set_markup (GTK_LABEL (label), label_markup); g_free (label_markup); gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_RIGHT); if (!multiline) gtk_misc_set_alignment ( GTK_MISC (label), 1, 0.5); else gtk_misc_set_alignment ( GTK_MISC (label), 1, 0); gtk_table_attach (GTK_TABLE (adr_table), label, 0, 1, field + 1, field + 2, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (label); } /* This code is pretty much a verbatim copy of the * code below to handle single-valued fields */ if (multiline) { GtkTextBuffer *buffer; GtkTextView *view; view = GTK_TEXT_VIEW (gtk_text_view_new ()); gtk_widget_set_name (GTK_WIDGET (view), attr_name); buffer = gtk_text_view_get_buffer (view); gtk_text_buffer_set_text (buffer, string ? string : "", -1); gtk_text_view_set_editable (view, TRUE); gtk_text_view_set_accepts_tab (view, FALSE); entry = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (entry), GTK_SHADOW_IN); gtk_container_add (GTK_CONTAINER (entry), GTK_WIDGET (view)); gtk_widget_show (GTK_WIDGET (view)); /* Connect signal for changes */ g_signal_connect (G_OBJECT (buffer), "changed", G_CALLBACK (contacts_entry_changed), data); } else { entry = gtk_entry_new (); gtk_widget_set_name (entry, attr_name); gtk_entry_set_text (GTK_ENTRY (entry), string ? string : ""); /* Connect signal for changes */ g_signal_connect (G_OBJECT (entry), "changed", G_CALLBACK (contacts_entry_changed), data); } gtk_widget_show (entry); /* Hide the label when the entry is hidden */ g_signal_connect_swapped (G_OBJECT (entry), "hide", G_CALLBACK (gtk_widget_hide), label); gtk_table_attach (GTK_TABLE (adr_table), entry, 1, 2, field + 1, field + 2, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); } } else if (multi_line) { /* Handle single-valued fields that span multiple lines */ gchar *string = e_vcard_attribute_get_value (attr); GtkWidget *container = NULL; GtkTextView *view = GTK_TEXT_VIEW (gtk_text_view_new ()); GtkTextBuffer *buffer = gtk_text_view_get_buffer (view); gtk_widget_set_name (GTK_WIDGET (view), attr_name); gtk_text_buffer_set_text (buffer, string ? string : "", -1); gtk_text_view_set_editable (view, TRUE); gtk_text_view_set_accepts_tab (view, FALSE); g_free (string); container = gtk_frame_new (NULL); gtk_frame_set_shadow_type ( GTK_FRAME (container), GTK_SHADOW_IN); gtk_container_add (GTK_CONTAINER (container), GTK_WIDGET (view)); gtk_widget_show (GTK_WIDGET (view)); /* if (type_edit) { gtk_widget_show (type_edit); gtk_widget_show (window); container = gtk_hbox_new (FALSE, 6); gtk_box_pack_start (GTK_BOX (container), type_edit, FALSE, TRUE, 0); gtk_box_pack_end (GTK_BOX (container), window, TRUE, TRUE, 0); }*/ /* Set widget that contains attribute data */ data->widget = container; /* Connect signal for changes */ g_signal_connect (G_OBJECT (buffer), "changed", G_CALLBACK (contacts_entry_changed), data); } else { /* Handle simple single-valued single-line fields */ gchar *string = e_vcard_attribute_get_value (attr); GtkWidget *entry, *container = NULL; entry = gtk_entry_new (); gtk_entry_set_text (GTK_ENTRY (entry), string ? string : ""); gtk_widget_set_name (entry, attr_name); g_free (string); /* if (type_edit) { gtk_widget_show (type_edit); gtk_widget_show (entry); container = gtk_hbox_new (FALSE, 6); gtk_box_pack_start (GTK_BOX (container), type_edit, FALSE, TRUE, 0); gtk_box_pack_end (GTK_BOX (container), entry, TRUE, TRUE, 0); }*/ /* Set widget that contains attribute data */ data->widget = container ? container : entry; /* Connect signal for changes */ g_signal_connect (G_OBJECT (entry), "changed", G_CALLBACK (contacts_entry_changed), data); } /* Free change data structure on destruction */ g_signal_connect_swapped (G_OBJECT (data->widget), "destroy", G_CALLBACK (g_free), data); gtk_widget_set_name (data->widget, attr_name); return data->widget; }
static GtkWidget * contacts_type_edit_widget_new (EVCardAttribute *attr, gboolean multi_line, gboolean *changed) { const TypeTuple *types; types = contacts_get_field_types (e_vcard_attribute_get_name (attr)); if (types) { guint i; GtkWidget *combo = gtk_combo_box_entry_new_text (); GtkWidget *align; gchar *first_type = ""; EVCardAttributeParam *param; EContactTypeChangeData *data; /* Retrieve all types, but we only look at the first one * TODO: A sane way of selecting multiple types, to conform * with spec? (Almost no other vCard-using apps do this, * so not high priority) */ GList *contact_types = contacts_get_types ( e_vcard_attribute_get_params (attr)); if (contact_types) { param = (EVCardAttributeParam *)contact_types->data; GList *values = e_vcard_attribute_param_get_values (param); first_type = values ? (gchar *)values->data : ""; g_list_free (contact_types); } else { param = e_vcard_attribute_param_new ("TYPE"); e_vcard_attribute_add_param_with_value ( attr, param, ""); } data = g_new (EContactTypeChangeData, 1); data->param = param; data->attr_name = e_vcard_attribute_get_name (attr); data->changed = changed; for (i = 0; types[i].index; i++) { gtk_combo_box_append_text ( GTK_COMBO_BOX (combo), _(types[i].value)); /* Note: We use a case-insensitive search here, as * specified in the spec (as the types are predefined, * we can use non-locale-friendly strcasecmp) */ if (first_type) { if (g_ascii_strcasecmp (types[i].index, first_type) == 0) { first_type = NULL; gtk_combo_box_set_active ( GTK_COMBO_BOX (combo), i); } } } /* Support custom types, as per spec */ if ((first_type) && (g_ascii_strncasecmp (first_type, "X-", 2) == 0)) { gtk_entry_set_text ( GTK_ENTRY (GTK_BIN (combo)->child), (const gchar *)(first_type+2)); first_type = NULL; } if (first_type) gtk_combo_box_set_active (GTK_COMBO_BOX (combo), i-1); gtk_widget_show (combo); if (/*(e_vcard_attribute_is_single_valued (attr)) &&*/ (multi_line == FALSE)) align = gtk_alignment_new (0, 0.5, 0, 0); else align = gtk_alignment_new (0, 0, 0, 0); /* TODO: Find something better than this? */ gtk_widget_set_size_request (combo, 80, -1); gtk_container_add (GTK_CONTAINER (align), combo); /* Connect signal for changes */ g_signal_connect (G_OBJECT (combo), "changed", G_CALLBACK (contacts_type_entry_changed), data); /* Free change data structure on destruction */ g_signal_connect_swapped (G_OBJECT (align), "destroy", G_CALLBACK (g_free), data); return align; } return NULL; }
static void contacts_entry_changed (GtkWidget *widget, EContactChangeData *data) { GList *v, *values = contacts_entries_get_values (data->widget, NULL); e_vcard_attribute_remove_values (data->attr); /* Note: First element of a structured field is type, so remove it */ if (g_list_length (values) > 1) { GList *type = g_list_last (values); values = g_list_remove_link (values, type); g_free (type->data); g_list_free (type); } for (v = g_list_last (values); v; v = v->prev) { e_vcard_attribute_add_value (data->attr, (const gchar *)v->data); } if (!g_ascii_strcasecmp (e_vcard_attribute_get_name (data->attr), EVC_FN)) { /* add N and X-EVOLUTION-FILE-AS attributes */ ENameWestern *name; gchar *file_as; EVCardAttribute *attr; EVCard *evc = E_VCARD (data->contact); name = e_name_western_parse ((char*)values->data); /* Add "N" attribute */ attr = e_vcard_get_attribute (evc, EVC_N); if (attr) e_vcard_attribute_remove_values (attr); else { attr = e_vcard_attribute_new ("", EVC_N); e_vcard_add_attribute (evc, attr); } #define SAFESTR(x) (x) ? x : "" e_vcard_attribute_add_value (attr, SAFESTR (name->last)); e_vcard_attribute_add_value (attr, SAFESTR (name->first)); e_vcard_attribute_add_value (attr, SAFESTR (name->middle)); e_vcard_attribute_add_value (attr, SAFESTR (name->prefix)); e_vcard_attribute_add_value (attr, SAFESTR (name->suffix)); /* Add file-as attribute for evolution */ file_as = g_strdup_printf ("%s, %s", name->last, name->first); attr = e_vcard_get_attribute (evc, EVC_X_FILE_AS); if (attr) e_vcard_remove_attribute (evc, attr); attr = e_vcard_attribute_new ("", EVC_X_FILE_AS); e_vcard_add_attribute_with_value (evc, attr, file_as); g_free (file_as); g_free (name); } g_list_foreach (values, (GFunc)g_free, NULL); g_list_free (values); *data->changed = TRUE; }
static void vcard_import_contact (VCardImporter *gci, EContact *contact) { EContactPhoto *photo; GList *attrs, *attr; gchar *uid = NULL; /* Apple's addressbook.app exports PHOTO's without a TYPE * param, so let's figure out the format here if there's a * PHOTO attribute missing a TYPE param. * * this is sort of a hack, as EContact sets the type for us if * we use the setter. so let's e_contact_get + e_contact_set * on E_CONTACT_PHOTO. */ photo = e_contact_get (contact, E_CONTACT_PHOTO); if (photo) { e_contact_set (contact, E_CONTACT_PHOTO, photo); e_contact_photo_free (photo); } /* Deal with our XML EDestination stuff in EMAIL attributes, if there is any. */ attrs = e_contact_get_attributes (contact, E_CONTACT_EMAIL); for (attr = attrs; attr; attr = attr->next) { EVCardAttribute *a = attr->data; GList *v = e_vcard_attribute_get_values (a); if (v && v->data) { if (!strncmp ((gchar *)v->data, "<?xml", 5)) { EDestination *dest = e_destination_import ((gchar *) v->data); e_destination_export_to_vcard_attribute (dest, a); g_object_unref (dest); } } } e_contact_set_attributes (contact, E_CONTACT_EMAIL, attrs); /* Deal with TEL attributes that don't conform to what we need. * * 1. if there's no location (HOME/WORK/OTHER), default to OTHER. * 2. if there's *only* a location specified, default to VOICE. */ attrs = e_vcard_get_attributes (E_VCARD (contact)); for (attr = attrs; attr; attr = attr->next) { EVCardAttribute *a = attr->data; gboolean location_only = TRUE; gboolean no_location = TRUE; gboolean is_work_home = FALSE; GList *params, *param; if (g_ascii_strcasecmp (e_vcard_attribute_get_name (a), EVC_TEL)) continue; params = e_vcard_attribute_get_params (a); for (param = params; param; param = param->next) { EVCardAttributeParam *p = param->data; GList *vs, *v; if (g_ascii_strcasecmp (e_vcard_attribute_param_get_name (p), EVC_TYPE)) continue; vs = e_vcard_attribute_param_get_values (p); for (v = vs; v; v = v->next) { is_work_home = is_work_home || !g_ascii_strcasecmp ((gchar *)v->data, "WORK") || !g_ascii_strcasecmp ((gchar *)v->data, "HOME"); if (!g_ascii_strcasecmp ((gchar *)v->data, "WORK") || !g_ascii_strcasecmp ((gchar *)v->data, "HOME") || !g_ascii_strcasecmp ((gchar *)v->data, "OTHER")) no_location = FALSE; else location_only = FALSE; } } if (is_work_home) { /* only WORK and HOME phone numbers require locations, * the rest should be kept as is */ if (location_only) { /* add VOICE */ e_vcard_attribute_add_param_with_value (a, e_vcard_attribute_param_new (EVC_TYPE), "VOICE"); } if (no_location) { /* add OTHER */ e_vcard_attribute_add_param_with_value (a, e_vcard_attribute_param_new (EVC_TYPE), "OTHER"); } } } /* Deal with ADR and EMAIL attributes that don't conform to what * we need. If HOME or WORK isn't specified, add TYPE=OTHER. */ attrs = e_vcard_get_attributes (E_VCARD (contact)); for (attr = attrs; attr; attr = attr->next) { EVCardAttribute *a = attr->data; gboolean no_location = TRUE; GList *params, *param; if (g_ascii_strcasecmp (e_vcard_attribute_get_name (a), EVC_ADR) && g_ascii_strcasecmp (e_vcard_attribute_get_name (a), EVC_EMAIL)) continue; params = e_vcard_attribute_get_params (a); for (param = params; param; param = param->next) { EVCardAttributeParam *p = param->data; GList *vs, *v; if (g_ascii_strcasecmp (e_vcard_attribute_param_get_name (p), EVC_TYPE)) continue; vs = e_vcard_attribute_param_get_values (p); for (v = vs; v; v = v->next) { if (!g_ascii_strcasecmp ((gchar *)v->data, "WORK") || !g_ascii_strcasecmp ((gchar *)v->data, "HOME")) no_location = FALSE; } } if (no_location) { /* add OTHER */ e_vcard_attribute_add_param_with_value (a, e_vcard_attribute_param_new (EVC_TYPE), "OTHER"); } } /* Work around the fact that these fields no longer show up in the UI */ add_to_notes (contact, E_CONTACT_OFFICE); add_to_notes (contact, E_CONTACT_SPOUSE); add_to_notes (contact, E_CONTACT_BLOG_URL); /* FIXME Error checking */ if (e_book_client_add_contact_sync (gci->book_client, contact, &uid, NULL, NULL) && uid) { e_contact_set (contact, E_CONTACT_UID, uid); g_free (uid); } }