static void
print_contact (EContact *contact)
{
	GList *emails, *e;

	g_print ("Contact: %s\n", (gchar *) e_contact_get_const (contact, E_CONTACT_FULL_NAME));
	g_print ("UID: %s\n", (gchar *) e_contact_get_const (contact, E_CONTACT_UID));
	g_print ("Email addresses:\n");

	emails = e_contact_get (contact, E_CONTACT_EMAIL);
	for (e = emails; e; e = e->next) {
		g_print ("\t%s\n",  (gchar *) e->data);
	}
	g_list_foreach (emails, (GFunc) g_free, NULL);
	g_list_free (emails);

	g_print ("\n");
}
示例#2
0
void
print_email (EContact *contact)
{
	const gchar *file_as = e_contact_get_const (contact, E_CONTACT_FILE_AS);
	const gchar *name_or_org = e_contact_get_const (contact, E_CONTACT_NAME_OR_ORG);
	GList *emails, *e;

	g_print ("   Contact: %s\n", file_as);
	g_print ("   Name or org: %s\n", name_or_org);
	g_print ("   Email addresses:\n");
	emails = e_contact_get (contact, E_CONTACT_EMAIL);
	for (e = emails; e; e = e->next) {
		g_print ("\t%s\n",  (gchar *) e->data);
	}
	g_list_foreach (emails, (GFunc) g_free, NULL);
	g_list_free (emails);

	g_print ("\n");
}
示例#3
0
static gboolean
contacts_are_equal_shallow (EContact *a,
                            EContact *b)
{
	const gchar *uid_a, *uid_b;

        /* Avoid warnings if one or more are NULL, to make this function
         * "NULL-friendly" */
	if (!a && !b)
		return TRUE;

	if (!E_IS_CONTACT (a) || !E_IS_CONTACT (b))
		return FALSE;

	uid_a = e_contact_get_const (a, E_CONTACT_UID);
	uid_b = e_contact_get_const (b, E_CONTACT_UID);

	return g_strcmp0 (uid_a, uid_b) == 0;
}
示例#4
0
static void
view_remove_contact_cb (EBookClientView *client_view,
                        const GSList *ids,
                        EAddressbookModel *model)
{
	/* XXX we should keep a hash around instead of this O(n*m) loop */
	const GSList *iter;
	GArray *indices;
	GPtrArray *array;
	gint ii;

	array = model->priv->contacts;
	indices = g_array_new (FALSE, FALSE, sizeof (gint));

	for (iter = ids; iter != NULL; iter = iter->next) {
		const gchar *target_uid = iter->data;

		for (ii = 0; ii < array->len; ii++) {
			EContact *contact;
			const gchar *uid;

			contact = array->pdata[ii];
			/* check if already removed */
			if (!contact)
				continue;

			uid = e_contact_get_const (contact, E_CONTACT_UID);
			g_return_if_fail (uid != NULL);

			if (strcmp (uid, target_uid) == 0) {
				g_object_unref (contact);
				g_array_append_val (indices, ii);
				array->pdata[ii] = NULL;
				break;
			}
		}
	}

	/* Sort the 'indices' array in descending order, since
	 * g_ptr_array_remove_index() shifts subsequent elements
	 * down one position to fill the gap. */
	g_array_sort (indices, sort_descending);

	for (ii = 0; ii < indices->len; ii++) {
		gint index;

		index = g_array_index (indices, gint, ii);
		g_ptr_array_remove_index (array, index);
	}

	g_signal_emit (model, signals[CONTACTS_REMOVED], 0, indices);
	g_array_free (indices, FALSE);

	update_folder_bar_message (model);
}
示例#5
0
static void
search_changed_cb(GtkEntry *entry, GevoAddBuddyDialog *dialog)
{
	const char *text = gtk_entry_get_text(entry);
	GList *l;

	gtk_list_store_clear(dialog->model);

	for (l = dialog->contacts; l != NULL; l = l->next)
	{
		EContact *contact = E_CONTACT(l->data);
		const char *name;
		GList *aims, *jabbers, *yahoos, *msns, *icqs, *novells;

		name = e_contact_get_const(contact, E_CONTACT_FULL_NAME);

		if (text != NULL && *text != '\0' && name != NULL &&
			g_ascii_strncasecmp(name, text, strlen(text)))
		{
			continue;
		}

		aims    = e_contact_get(contact, E_CONTACT_IM_AIM);
		jabbers = e_contact_get(contact, E_CONTACT_IM_JABBER);
		yahoos  = e_contact_get(contact, E_CONTACT_IM_YAHOO);
		msns    = e_contact_get(contact, E_CONTACT_IM_MSN);
		icqs    = e_contact_get(contact, E_CONTACT_IM_ICQ);
		novells = e_contact_get(contact, E_CONTACT_IM_GROUPWISE);

		if (aims == NULL && jabbers == NULL && yahoos == NULL &&
			msns == NULL && icqs == NULL && novells == NULL)
		{
			GtkTreeIter iter;

			gtk_list_store_append(dialog->model, &iter);

			gtk_list_store_set(dialog->model, &iter,
							   COLUMN_NAME, name,
							   COLUMN_DATA, contact,
							   -1);
		}
		else
		{
			add_ims(dialog, contact, name, aims,    "prpl-aim");
			add_ims(dialog, contact, name, aims,    "prpl-oscar");
			add_ims(dialog, contact, name, jabbers, "prpl-jabber");
			add_ims(dialog, contact, name, yahoos,  "prpl-yahoo");
			add_ims(dialog, contact, name, msns,    "prpl-msn");
			add_ims(dialog, contact, name, icqs,    "prpl-icq");
			add_ims(dialog, contact, name, icqs,    "prpl-oscar");
			add_ims(dialog, contact, name, novells, "prpl-novell");
		}
	}
}
static void
objects_added (EBookClientView *view,
               const GSList *contacts)
{
	const GSList *l;

	for (l = contacts; l; l = l->next) {
		EContact *contact = l->data;

		print_contact (contact);

		if (e_contact_get_const (contact, E_CONTACT_FULL_NAME) != NULL)
			g_error ("received contact name `%s' when only the uid and revision was requested",
				 (gchar *) e_contact_get_const (contact, E_CONTACT_FULL_NAME));
	}

	if (!loading_view)
		finish_test (view);

}
static int
addressbook_compare (EReflowModel *erm, int n1, int n2)
{
	EAddressbookReflowAdapter *adapter = E_ADDRESSBOOK_REFLOW_ADAPTER(erm);
	EAddressbookReflowAdapterPrivate *priv = adapter->priv;
	EContact *contact1, *contact2;

	if (priv->loading) {
		return n1-n2;
	}
	else {
		contact1 = (EContact*)eab_model_contact_at (priv->model, n1);
		contact2 = (EContact*)eab_model_contact_at (priv->model, n2);

		if (contact1 && contact2) {
			const char *file_as1, *file_as2;
			const char *uid1, *uid2;
			file_as1 = e_contact_get_const (contact1, E_CONTACT_FILE_AS);
			file_as2 = e_contact_get_const (contact2, E_CONTACT_FILE_AS);
			if (file_as1 && file_as2)
				return g_utf8_collate(file_as1, file_as2);
			if (file_as1)
				return -1;
			if (file_as2)
				return 1;
			uid1 = e_contact_get_const (contact1, E_CONTACT_UID);
			uid2 = e_contact_get_const (contact2, E_CONTACT_UID);
			if (uid1 && uid2)
				return strcmp(uid1, uid2);
			if (uid1)
				return -1;
			if (uid2)
				return 1;
		}
		if (contact1)
			return -1;
		if (contact2)
			return 1;
		return 0;
	}
}
示例#8
0
static gint
set_pilot_id (GnomePilotConduitSyncAbs *conduit,
	      EAddrLocalRecord *local,
	      guint32 ID,
	      EAddrConduitContext *ctxt)
{
	LOG (g_message ( "set_pilot_id: setting to %d\n", ID ));

	e_pilot_map_insert (ctxt->map, ID, e_contact_get_const (local->contact, E_CONTACT_UID), FALSE);

        return 0;
}
示例#9
0
static void
view_modify_contact_cb (EBookClientView *client_view,
                        const GSList *contact_list,
                        EAddressbookModel *model)
{
	GPtrArray *array;

	array = model->priv->contacts;

	while (contact_list != NULL) {
		EContact *new_contact = contact_list->data;
		const gchar *target_uid;
		gint ii;

		target_uid = e_contact_get_const (new_contact, E_CONTACT_UID);

		for (ii = 0; ii < array->len; ii++) {
			EContact *old_contact;
			const gchar *uid;

			old_contact = array->pdata[ii];
			g_return_if_fail (old_contact != NULL);

			uid = e_contact_get_const (old_contact, E_CONTACT_UID);
			g_return_if_fail (uid != NULL);

			if (strcmp (uid, target_uid) != 0)
				continue;

			g_object_unref (old_contact);
			array->pdata[ii] = e_contact_duplicate (new_contact);

			g_signal_emit (
				model, signals[CONTACT_CHANGED], 0, ii);
			break;
		}

		contact_list = contact_list->next;
	}
}
gint
main (gint argc,
      gchar **argv)
{
    EBook *book;
    EContact *contact;
    GList *changes;
    GError *error = NULL;
    EBookChange *change;
    gchar *uri;

    g_type_init ();

    book = ebook_test_utils_book_new_temp (&uri);
    ebook_test_utils_book_open (book, FALSE);

    /* get an initial change set */
    if (!e_book_get_changes (book, "changeidtest", &changes, &error)) {
        printf ("failed to get changes: %s\n", error->message);
        exit (0);
    }

    /* make a change to the book */
    contact = e_contact_new_from_vcard (NEW_VCARD);
    ebook_test_utils_book_add_contact (book, contact);

    /* get another change set */
    if (!e_book_get_changes (book, "changeidtest", &changes, &error)) {
        printf ("failed to get second set of changes: %s\n", error->message);
        exit (0);
    }

    /* make sure that 1 change has occurred */
    if (g_list_length (changes) != 1) {
        printf ("got back %d changes, was expecting 1\n", g_list_length (changes));
        exit (0);
    }

    change = changes->data;
    if (change->change_type != E_BOOK_CHANGE_CARD_ADDED) {
        printf ("was expecting a CARD_ADDED change, but didn't get it.\n");
        exit (0);
    }

    printf ("got changed vcard back: %s\n", (gchar *) e_contact_get_const (change->contact, E_CONTACT_UID));

    e_book_free_change_list (changes);

    g_object_unref (contact);

    return 0;
}
示例#11
0
static gint
set_status_cleared (GnomePilotConduitSyncAbs *conduit,
		    EAddrLocalRecord *local,
		    EAddrConduitContext *ctxt)
{
	const char *uid;

	LOG (g_message ( "set_status_cleared: clearing status\n" ));

	if ((uid = e_contact_get_const (local->contact, E_CONTACT_UID)))
		g_hash_table_remove (ctxt->changed_hash, uid);

        return 0;
}
示例#12
0
static gint
delete_record (GnomePilotConduitSyncAbs *conduit,
	       EAddrLocalRecord *local,
	       EAddrConduitContext *ctxt)
{
	GError *error = NULL;
	int retval = 0;

	g_return_val_if_fail (local != NULL, -1);
	g_return_val_if_fail (local->contact != NULL, -1);

	LOG (g_message ( "delete_record: delete %s\n", print_local (local) ));

	e_pilot_map_remove_by_uid (ctxt->map, e_contact_get_const (local->contact, E_CONTACT_UID));
	if (!e_book_remove_contact (ctxt->ebook, e_contact_get_const (local->contact, E_CONTACT_UID), &error) && error->code != E_BOOK_ERROR_CONTACT_NOT_FOUND) {
		WARN ("delete_record: failed to delete card in ebook\n");
		g_error_free (error);

		retval = -1;
	}

	return retval;
}
示例#13
0
static GList *
next_changed_item (EAddrConduitContext *ctxt, GList *changes)
{
	EBookChange *ebc;
	GList *l;

	for (l = changes; l != NULL; l = l->next) {
		ebc = l->data;

		if (g_hash_table_lookup (ctxt->changed_hash, e_contact_get_const (ebc->contact, E_CONTACT_UID)))
			return l;
	}

	return NULL;
}
static void
add_to_notes (EContact *contact,
              EContactField field)
{
	const gchar *old_text;
	const gchar *field_text;
	gchar       *new_text;

	old_text = e_contact_get_const (contact, E_CONTACT_NOTE);
	if (old_text && strstr (old_text, e_contact_pretty_name (field)))
		return;

	field_text = e_contact_get_const (contact, field);
	if (!field_text || !*field_text)
		return;

	new_text = g_strdup_printf ("%s%s%s: %s",
				    old_text ? old_text : "",
				    old_text && *old_text &&
				    *(old_text + strlen (old_text) - 1) != '\n' ? "\n" : "",
				    e_contact_pretty_name (field), field_text);
	e_contact_set (contact, E_CONTACT_NOTE, new_text);
	g_free (new_text);
}
示例#15
0
static gint
archive_record (GnomePilotConduitSyncAbs *conduit,
		EAddrLocalRecord *local,
		gboolean archive,
		EAddrConduitContext *ctxt)
{
	int retval = 0;

	g_return_val_if_fail (local != NULL, -1);

	LOG (g_message ( "archive_record: %s\n", archive ? "yes" : "no" ));

	e_pilot_map_insert (ctxt->map, local->local.ID, e_contact_get_const (local->contact, E_CONTACT_UID), archive);

        return retval;
}
static void
contact_modified_cb (GObject *source_object,
                     GAsyncResult *result,
                     gpointer user_data)
{
	ModifyData *data = (ModifyData *) user_data;
	GError *error = NULL;

	if (!e_book_client_modify_contact_finish (E_BOOK_CLIENT (source_object), result, &error))
		g_error ("modify contact finish: %s", error->message);

	e_book_client_get_contact (
		E_BOOK_CLIENT (source_object),
		e_contact_get_const (data->contact, E_CONTACT_UID),
		NULL, contact_ready_cb, data->loop);
}
static gboolean
compare_email (EContact *contact,
               const gchar *str,
               gchar * (*compare) (const gchar *,
                                   const gchar *))
{
	gint i;

	for (i = E_CONTACT_EMAIL_1; i <= E_CONTACT_EMAIL_4; i++) {
		const gchar *email = e_contact_get_const (contact, i);

		if (email && compare (email, str))
			return TRUE;
	}

	return FALSE;
}
示例#18
0
gint
main (gint argc,
      gchar **argv)
{
	EContact *contact;

	g_type_init ();

	contact = e_contact_new ();

	e_contact_set (contact, E_CONTACT_UID, TEST_ID);

	if (!strcmp (e_contact_get_const (contact, E_CONTACT_UID), TEST_ID))
	  printf ("passed\n");
	else
	  printf ("failed\n");

	return 0;
}
示例#19
0
static void
final_cb_as_id (EBookClient *book_client,
                const GError *error,
                gpointer closure)
{
	EContactMergingLookup *lookup = closure;

	if (lookup->id_cb)
		lookup->id_cb (
			lookup->book_client,
			error, lookup->contact ?
				e_contact_get_const (
				lookup->contact, E_CONTACT_UID) : NULL,
			lookup->closure);

	free_lookup (lookup);

	finished_lookup ();
}
示例#20
0
static gdouble
e_contact_get_contact_height (EContact *contact,
                              EContactPrintContext *ctxt)
{
	gchar *file_as;
	gint field;
	gdouble cntct_height = 0.0;

	cntct_height += get_font_height (ctxt->style->headings_font) * .2;

	file_as = e_contact_get (contact, E_CONTACT_FILE_AS);

	cntct_height += e_contact_text_height (
		ctxt->context, ctxt->style->headings_font, file_as);

	g_free (file_as);

	cntct_height += get_font_height (ctxt->style->headings_font) * .2;

	for (field = E_CONTACT_FILE_AS; field != E_CONTACT_LAST_SIMPLE_STRING; field++)
	{
		const gchar *value;
		gchar *text;

		value = e_contact_get_const (contact, field);
		if (value == NULL || *value == '\0')
			continue;

		text = g_strdup_printf ("%s:  %s",
			e_contact_pretty_name (field), value);

		cntct_height += e_contact_text_height (
			ctxt->context, ctxt->style->body_font, text);

		cntct_height += .2 * get_font_height (ctxt->style->body_font);

		g_free (text);
	}

	cntct_height += get_font_height (ctxt->style->headings_font) * .4 + 8;

	return cntct_height;
}
示例#21
0
static void
add_to_notes (EContact *contact,
              const gchar *field_text,
              gchar *val)
{
	GString *new_text;

	if (!field_text || !val || !*val)
		return;

	new_text = g_string_new (e_contact_get_const (contact, E_CONTACT_NOTE));
	if (strlen (new_text->str) != 0)
		new_text = g_string_append_c (new_text, '\n');
	new_text = g_string_append (new_text, field_text);
	new_text = g_string_append_c (new_text, ':');
	new_text = g_string_append (new_text, val);

	e_contact_set (contact, E_CONTACT_NOTE, new_text->str);
	g_string_free (new_text, TRUE);
}
示例#22
0
static const gchar *
get_email (EContact *contact,
           EContactField field_id,
           gchar **to_free)
{
	gchar *name = NULL, *mail = NULL;
	const gchar *value = e_contact_get_const (contact, field_id);

	*to_free = NULL;

	if (eab_parse_qp_email (value, &name, &mail)) {
		*to_free = g_strdup_printf ("%s <%s>", name, mail);
		value = *to_free;
	}

	g_free (name);
	g_free (mail);

	return value;
}
static EContact *
do_create (EBookBackendVCF  *bvcf,
	  const gchar     *vcard_req,
	  gboolean        dirty_the_file)
{
	gchar           *id;
	EContact       *contact;
	gchar           *vcard;
	const gchar     *rev;

	/* at the very least we need the unique_id generation to be
	   protected by the lock, even if the actual vcard parsing
	   isn't. */
	g_mutex_lock (bvcf->priv->mutex);
	id = e_book_backend_vcf_create_unique_id ();

	contact = e_contact_new_from_vcard (vcard_req);
	e_contact_set (contact, E_CONTACT_UID, id);
	g_free (id);

	rev = e_contact_get_const (contact,  E_CONTACT_REV);
	if (!(rev && *rev))
		set_revision (contact);

	vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);

	insert_contact (bvcf, vcard);

	if (dirty_the_file) {
		bvcf->priv->dirty = TRUE;

		if (!bvcf->priv->flush_timeout_tag)
			bvcf->priv->flush_timeout_tag = g_timeout_add (FILE_FLUSH_TIMEOUT,
								       vcf_flush_file, bvcf);
	}

	g_mutex_unlock (bvcf->priv->mutex);

	return contact;
}
static void
contact_editor_contact_modified_cb (EABEditor *editor,
                                    const GError *error,
                                    EContact *contact,
                                    gpointer user_data)
{
	EContactMapWindow *window = user_data;
	EContactMap *map;
	const gchar *contact_uid;

	if (error) {
		g_warning ("Error modifying contact: %s", error->message);
		return;
	}

	map = e_contact_map_window_get_map (window);

	contact_uid = e_contact_get_const (contact, E_CONTACT_UID);

	e_contact_map_remove_contact (map, contact_uid);
	e_contact_map_add_contact (map, contact);
}
/**
 * e_data_book_view_notify_update:
 * @book_view: an #EDataBookView
 * @contact: an #EContact
 *
 * Notify listeners that @contact has changed. This can
 * trigger an add, change or removal event depending on
 * whether the change causes the contact to start matching,
 * no longer match, or stay matching the query specified
 * by @book_view.
 **/
void
e_data_book_view_notify_update (EDataBookView *book_view,
                                EContact      *contact)
{
    EDataBookViewPrivate *priv = book_view->priv;
    gboolean currently_in_view, want_in_view;
    const gchar *id;
    gchar *vcard;

    if (!priv->running)
        return;

    g_mutex_lock (priv->pending_mutex);

    id = e_contact_get_const (contact, E_CONTACT_UID);

    currently_in_view = id_is_in_view (book_view, id);
    want_in_view =
        e_book_backend_sexp_match_contact (priv->card_sexp, contact);

    if (want_in_view) {
        vcard = e_vcard_to_string (E_VCARD (contact),
                                   EVC_FORMAT_VCARD_30);

        if (currently_in_view)
            notify_change (book_view, vcard);
        else
            notify_add (book_view, id, vcard);

        g_free (vcard);
    } else {
        if (currently_in_view)
            notify_remove (book_view, id);
        /* else nothing; we're removing a card that wasn't there */
    }

    g_mutex_unlock (priv->pending_mutex);
}
static void
container_object_removed_cb (ScalixContainer * container,
                             ScalixObject * object, gpointer data)
{
    EBookBackend *backend;
    EContact *contact;
    const char *uid;

    backend = E_BOOK_BACKEND (data);

    if (!E_IS_CONTACT (object)) {
        g_warning ("Invalid object");
        return;
    }

    contact = E_CONTACT (object);
    uid = e_contact_get_const (contact, E_CONTACT_UID);

    g_assert (uid != NULL);

    e_book_backend_notify_remove (backend, uid);
    e_book_backend_notify_complete (backend);
}
/**
 * e_data_book_view_notify_update_vcard:
 * @book_view: an #EDataBookView
 * @vcard: a plain vCard
 *
 * Notify listeners that @vcard has changed. This can
 * trigger an add, change or removal event depending on
 * whether the change causes the contact to start matching,
 * no longer match, or stay matching the query specified
 * by @book_view.  This method should be preferred over
 * #e_data_book_view_notify_update when the native
 * representation of a contact is a vCard.
 **/
void
e_data_book_view_notify_update_vcard (EDataBookView *book_view, gchar *vcard)
{
    EDataBookViewPrivate *priv = book_view->priv;
    gboolean currently_in_view, want_in_view;
    const gchar *id;
    EContact *contact;

    if (!priv->running) {
        g_free (vcard);
        return;
    }

    g_mutex_lock (priv->pending_mutex);

    contact = e_contact_new_from_vcard (vcard);
    id = e_contact_get_const (contact, E_CONTACT_UID);
    currently_in_view = id_is_in_view (book_view, id);
    want_in_view =
        e_book_backend_sexp_match_contact (priv->card_sexp, contact);

    if (want_in_view) {
        if (currently_in_view)
            notify_change (book_view, vcard);
        else
            notify_add (book_view, id, vcard);
    } else {
        if (currently_in_view)
            notify_remove (book_view, id);
    }

    /* Do this last so that id is still valid when notify_ is called */
    g_object_unref (contact);
    g_free (vcard);

    g_mutex_unlock (priv->pending_mutex);
}
static gboolean
ebbm_contact_to_object (EMapiConnection *conn,
			TALLOC_CTX *mem_ctx,
			EMapiObject **pobject, /* out */
			gpointer user_data,
			GCancellable *cancellable,
			GError **perror)
{
	EMapiCreateitemData *mcd = user_data;
	const gchar *uid = NULL;
	EContact *old_contact = NULL;
	gboolean res;
	GError *error = NULL;

	g_return_val_if_fail (mcd != NULL, FALSE);
	g_return_val_if_fail (mcd->contact != NULL, FALSE);
	g_return_val_if_fail (mcd->db != NULL, FALSE);
	g_return_val_if_fail (conn != NULL, FALSE);
	g_return_val_if_fail (mem_ctx != NULL, FALSE);
	g_return_val_if_fail (pobject != NULL, FALSE);

	uid = e_contact_get_const (mcd->contact, E_CONTACT_UID);
	if (uid)
		old_contact = e_book_backend_sqlitedb_get_contact (mcd->db, EMA_EBB_CACHE_FOLDERID, uid, NULL, NULL, &error);

	if (error) {
		old_contact = NULL;
		g_clear_error (&error);
	}

	res = e_mapi_book_utils_contact_to_object (mcd->contact, old_contact, pobject, mem_ctx, cancellable, perror);

	if (old_contact)
		g_object_unref (old_contact);

	return res;
}
示例#29
0
static void
accum_attribute (GString *buffer,
                 EContact *contact,
                 const gchar *html_label,
                 EContactField field,
                 const gchar *icon,
                 guint html_flags)
{
	const gchar *str;

	str = e_contact_get_const (contact, field);

	if (str != NULL && *str != '\0') {
		gchar *tmp = NULL;

		tmp = maybe_create_url (str, html_flags);
		if (tmp)
			str = tmp;

		render_table_row (buffer, html_label, str, icon, html_flags);

		g_free (tmp);
	}
}
static gboolean
test_bulk_modify (EBookClient *client,
                  const gchar *vcard_str,
                  gint batch_size)
{
	gint i;
	EContact *contact;
	GSList *contacts = NULL;
	GSList *added_uids = NULL;
	const GSList *l, *ll;
	const gchar *old_first_name = "xyz mix";
	const gchar *new_first_name = "abc mix";

	for (i = 0; i < batch_size; ++i) {
		EContact *contact = e_contact_new_from_vcard (vcard_str);
		contacts = g_slist_append (contacts, contact);
	}

	g_print ("  * Bulk addition of %d contacts...\n", batch_size);
	/* Bulk addition */
	g_return_val_if_fail (e_book_client_add_contacts_sync (client, contacts, &added_uids, NULL, NULL), FALSE);
	g_return_val_if_fail (added_uids != NULL, FALSE);
	g_return_val_if_fail (added_uids->data != NULL, FALSE);
	g_return_val_if_fail (g_slist_length (added_uids) == batch_size, FALSE);

	g_print ("  * Bulk modification of %d contacts...\n", batch_size);
	ll = added_uids;
	for (l = contacts; l != NULL; l = l->next) {
		const gchar * uid = ll->data;
		contact = E_CONTACT (l->data);

		/* Set contact UID */
		e_contact_set (contact, E_CONTACT_UID, uid);

		/* Change contact first name */
		e_contact_set (contact, E_CONTACT_GIVEN_NAME, new_first_name);

		ll = ll->next;
	}
	g_return_val_if_fail (e_book_client_modify_contacts_sync (client, contacts, NULL, NULL), FALSE);

	/* Validate */
	for (ll = added_uids; ll != NULL; ll = ll->next) {
		const gchar *first_name;
		const gchar *uid = ll->data;
		contact = NULL;

		g_return_val_if_fail (e_book_client_get_contact_sync (client, uid, &contact, NULL, NULL), FALSE);

		/* Check contact first name */
		first_name = e_contact_get_const (contact, E_CONTACT_GIVEN_NAME);
		g_return_val_if_fail (g_strcmp0 (first_name, new_first_name) == 0, FALSE);

		g_object_unref (contact);
	}

	/* Test failure case */
	g_print ("  * Bulk modification of %d contacts (expected failure)...\n", batch_size);
	contact = E_CONTACT (g_slist_nth_data (contacts, g_slist_length (contacts) - 2));
	g_return_val_if_fail (e_book_client_remove_contact_sync (client, contact, NULL, NULL), FALSE);
	for (l = contacts; l != NULL; l = l->next) {
		contact = E_CONTACT (l->data);
		/* Change contact first name */
		e_contact_set (contact, E_CONTACT_GIVEN_NAME, old_first_name);
	}
	g_return_val_if_fail (!e_book_client_modify_contacts_sync (client, contacts, NULL, NULL), FALSE);

	/* Remove the UID that no longer exists from the added_uid list */
	added_uids = g_slist_delete_link (added_uids, g_slist_nth (added_uids, g_slist_length (added_uids) - 2));

	/* Validate */
	for (ll = added_uids; ll != NULL; ll = ll->next) {
		const gchar *first_name;
		const gchar *uid = ll->data;
		contact = NULL;

		g_return_val_if_fail (e_book_client_get_contact_sync (client, uid, &contact, NULL, NULL), FALSE);

		/* Check contact first name */
		first_name = e_contact_get_const (contact, E_CONTACT_GIVEN_NAME);
		g_return_val_if_fail (g_strcmp0 (first_name, new_first_name) == 0, FALSE);

		g_object_unref (contact);
	}

	g_print ("  * Bulk removal of %d contacts...\n", batch_size);

	/* Bulk removal */
	g_return_val_if_fail (e_book_client_remove_contacts_sync (client, added_uids, NULL, NULL), FALSE);

	g_slist_free_full (added_uids, g_free);
	g_slist_free_full (contacts, g_object_unref);
	return TRUE;
}