static void
scan_objects (gpointer data, gpointer user_data)
{
    ScanContext *context;
    ScalixContact *contact;
    EBookBackendSExp *sexp;
    char *ostr;
    gboolean is_search;

    context = (ScanContext *) user_data;
    contact = SCALIX_CONTACT (data);
    sexp = context->sexp;
    is_search = context->search_needed;

    if (!is_search
        || e_book_backend_sexp_match_contact (sexp, E_CONTACT (contact))) {

        if (context->return_objects == TRUE) {
            context->obj_list =
                g_list_prepend (context->obj_list, g_object_ref (contact));
        } else {
            ostr = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
            context->obj_list = g_list_prepend (context->obj_list, ostr);
        }
    }
}
/**
 * e_book_backend_db_cache_get_contacts:
 * @db: DB Handle
 * @query: an s-expression
 *
 * Returns a list of #EContact elements from @cache matching @query.
 * When done with the list, the caller must unref the contacts and
 * free the list.
 *
 * Returns: A #GList of pointers to #EContact.
 **/
GList *
e_book_backend_db_cache_get_contacts (DB *db,
                                      const gchar *query)
{
	DBC	*dbc;
	DBT	uid_dbt, vcard_dbt;
	gint	db_error;
	GList *list = NULL;
	EBookBackendSExp *sexp = NULL;
	EContact *contact;

	if (query) {
		sexp = e_book_backend_sexp_new (query);
		if (!sexp)
			return NULL;
	}

	db_error = db->cursor (db, NULL, &dbc, 0);
	if (db_error != 0) {
		g_warning ("db->cursor failed with %d", db_error);
		if (sexp)
			g_object_unref (sexp);
		return NULL;
	}

	memset (&vcard_dbt, 0 , sizeof (vcard_dbt));
	memset (&uid_dbt, 0, sizeof (uid_dbt));
	db_error = dbc->c_get (dbc, &uid_dbt, &vcard_dbt, DB_FIRST);

	while (db_error == 0) {
		if (vcard_dbt.data && !strncmp (vcard_dbt.data, "BEGIN:VCARD", 11)) {
			contact = e_contact_new_from_vcard (vcard_dbt.data);

			if (!sexp || e_book_backend_sexp_match_contact (sexp, contact))
				list = g_list_prepend (list, contact);
			else
				g_object_unref (contact);
		}
		db_error = dbc->c_get (dbc, &uid_dbt, &vcard_dbt, DB_NEXT);
	}

	db_error = dbc->c_close (dbc);
	if (db_error != 0)
		g_warning ("db->c_close failed with %d", db_error);

	if (sexp)
		g_object_unref (sexp);

	return g_list_reverse (list);
}
/**
 * e_book_backend_sexp_match_vcard:
 * @sexp: an #EBookBackendSExp
 * @vcard: a VCard string
 *
 * Checks if @vcard matches @sexp.
 *
 * Returns: %TRUE if the VCard matches, %FALSE otherwise.
 **/
gboolean
e_book_backend_sexp_match_vcard (EBookBackendSExp *sexp,
                                 const gchar *vcard)
{
	EContact *contact;
	gboolean retval;

	contact = e_contact_new_from_vcard (vcard);

	retval = e_book_backend_sexp_match_contact (sexp, contact);

	g_object_unref (contact);

	return retval;
}
/**
 * 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);
}
/**
 * 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);
}