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;
}
Exemple #3
0
/* 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;
}
Exemple #4
0
/* 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);
}
Exemple #5
0
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;
} 
Exemple #6
0
/* 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;
}
Exemple #8
0
/* 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;
}
Exemple #9
0
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;
}
Exemple #10
0
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);
	}
}