e-book-client: add e_book_client_add_contacts*() methods
authorChristophe Dumez <christophe.dumez@intel.com>
Tue, 27 Sep 2011 10:35:36 +0000 (13:35 +0300)
committerChristophe Dumez <christophe.dumez@intel.com>
Sat, 8 Oct 2011 07:03:45 +0000 (10:03 +0300)
Enables optimizations for batch additions of contacts such as
less DBus round trips and use of database transactions in file
backend.

16 files changed:
addressbook/backends/file/e-book-backend-file.c
addressbook/backends/google/e-book-backend-google.c
addressbook/backends/ldap/e-book-backend-ldap.c
addressbook/backends/vcf/e-book-backend-vcf.c
addressbook/backends/webdav/e-book-backend-webdav.c
addressbook/libebook/e-book-client.c
addressbook/libebook/e-book-client.h
addressbook/libebook/e-book.c
addressbook/libedata-book/e-book-backend-sync.c
addressbook/libedata-book/e-book-backend-sync.h
addressbook/libedata-book/e-book-backend.c
addressbook/libedata-book/e-book-backend.h
addressbook/libedata-book/e-data-book.c
addressbook/libedata-book/e-data-book.h
addressbook/libegdbus/e-gdbus-book.c
addressbook/libegdbus/e-gdbus-book.h

index 38652b8..da34e5a 100644 (file)
@@ -296,79 +296,133 @@ set_revision (EContact *contact)
 
 }
 
+/**
+ * This method will return TRUE if all the contacts were properly created.
+ * If at least one contact fails, the method will return FALSE, all
+ * changes will be reverted (the @contacts list will stay empty) and
+ * @perror will be set.
+ */
 static gboolean
 do_create (EBookBackendFile *bf,
-          const gchar *vcard_req,
-          EContact **contact,
+          const GSList *vcards_req,
+          GSList **contacts,
           GError **perror)
 {
-       DB             *db = bf->priv->file_db;
-       DBT            id_dbt, vcard_dbt;
-       gint            db_error;
-       gchar           *id;
-       gchar           *vcard;
-       const gchar *rev;
+       DB *db = bf->priv->file_db;
+       DB_ENV *env = bf->priv->env;
+       DB_TXN *txn = NULL;
+       GSList *slist = NULL;
+       const GSList *l;
+       gint db_error = 0;
 
        g_assert (bf);
-       g_assert (vcard_req);
-       g_assert (contact);
+       g_assert (vcards_req);
 
        if (!db) {
                g_propagate_error (perror, EDB_NOT_OPENED_ERROR);
                return FALSE;
        }
 
-       id = e_book_backend_file_create_unique_id ();
+       /* Begin transaction */
+       db_error = env->txn_begin(env, NULL, &txn, 0);
+       if (db_error != 0) {
+               g_warning (G_STRLOC ": env->txn_begin failed with %s", db_strerror (db_error));
+               db_error_to_gerror (db_error, perror);
+               return FALSE;
+       }
+
+       for (l = vcards_req; l != NULL; l = l->next) {
+               DBT            id_dbt, vcard_dbt;
+               gchar           *id;
+               gchar           *vcard;
+               const gchar *rev;
+               const gchar *vcard_req;
+               EContact *contact;
 
-       string_to_dbt (id, &id_dbt);
+               vcard_req = (const gchar*) l->data;
 
-       *contact = e_contact_new_from_vcard_with_uid (vcard_req, id);
-       rev = e_contact_get_const (*contact,  E_CONTACT_REV);
-       if (!(rev && *rev))
-               set_revision (*contact);
+               id = e_book_backend_file_create_unique_id ();
 
-       vcard = e_vcard_to_string (E_VCARD (*contact), EVC_FORMAT_VCARD_30);
+               string_to_dbt (id, &id_dbt);
 
-       string_to_dbt (vcard, &vcard_dbt);
+               contact = e_contact_new_from_vcard_with_uid (vcard_req, id);
 
-       db_error = db->put (db, NULL, &id_dbt, &vcard_dbt, 0);
+               rev = e_contact_get_const (contact, E_CONTACT_REV);
+               if (!(rev && *rev))
+                       set_revision (contact);
 
-       g_free (vcard);
+               vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
 
-       if (0 == db_error) {
-               db_error = db->sync (db, 0);
-               if (db_error != 0) {
-                       g_warning ("db->sync failed with %s", db_strerror (db_error));
+               string_to_dbt (vcard, &vcard_dbt);
+
+               db_error = db->put (db, txn, &id_dbt, &vcard_dbt, 0);
+
+               g_free (vcard);
+               g_free (id);
+
+               if (db_error == 0) {
+                       /* Contact was added successfully, add it to the return list */
+                       if (contacts != NULL)
+                               slist = g_slist_prepend (slist, contact);
+               } else {
+                       /* Contact could not be added */
+                       g_warning (G_STRLOC ": db->put failed with %s", db_strerror (db_error));
+                       g_object_unref (contact);
+                       db_error_to_gerror (db_error, perror);
+
+                       /* Abort as soon as an error occurs */
+                       break;
+               }
+       }
+
+       if (db_error == 0) {
+               /* Commit transaction */
+               db_error = txn->commit (txn, 0);
+               if (db_error == 0) {
+                       /* Flush cache information to disk */
+                       if (db->sync (db, 0) != 0) {
+                               g_warning ("db->sync failed with %s", db_strerror (db_error));
+                       }
+               } else {
+                       g_warning (G_STRLOC ": txn->commit failed with %s", db_strerror (db_error));
+                       db_error_to_gerror (db_error, perror);
                }
        } else {
-               g_warning (G_STRLOC ": db->put failed with %s", db_strerror (db_error));
-               g_object_unref (*contact);
-               *contact = NULL;
+               /* Rollback transaction */
+               txn->abort (txn);
        }
 
-       g_free (id);
-       db_error_to_gerror (db_error, perror);
+       if (db_error == 0) {
+               if (contacts != NULL)
+                       *contacts = g_slist_reverse (slist);
+
+               return TRUE;
+       } else {
+               if (contacts != NULL)
+                       *contacts = NULL;
 
-       return db_error == 0;
+               e_util_free_object_slist (slist);
+               return FALSE;
+       }
 }
 
 static void
-e_book_backend_file_create_contact (EBookBackendSync *backend,
-                                    EDataBook *book,
-                                    GCancellable *cancellable,
-                                    const gchar *vcard,
-                                    EContact **contact,
-                                    GError **perror)
+e_book_backend_file_create_contacts (EBookBackendSync *backend,
+                                     EDataBook *book,
+                                     GCancellable *cancellable,
+                                     const GSList *vcards,
+                                     GSList **added_contacts,
+                                     GError **perror)
 {
        EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
 
-       if (do_create (bf, vcard, contact, perror)) {
+       if (do_create (bf, vcards, added_contacts, perror)) {
                GError *error = NULL;
 
-               if (!e_book_backend_sqlitedb_add_contact (bf->priv->sqlitedb,
+               if (!e_book_backend_sqlitedb_add_contacts (bf->priv->sqlitedb,
                                                          SQLITEDB_FOLDER_ID,
-                                                         *contact, FALSE, &error)) {
-                       g_warning ("Failed to add contact to summary: %s", error->message);
+                                                         *added_contacts, FALSE, &error)) {
+                       g_warning ("Failed to add contacts to summary: %s", error->message);
                        g_error_free (error);
                }
        }
@@ -1219,18 +1273,31 @@ e_book_backend_file_open (EBookBackendSync *backend,
                                (gpointer (*)(gpointer , gsize)) g_try_realloc,
                                g_free);
 
+               /* Make sure the database directory is created
+                  or env->open will fail */
+               if (!only_if_exists) {
+                       if (!create_directory (dirname, perror)) {
+                               g_warning ("failed to create directory at %s", dirname);
+                               G_UNLOCK (global_env);
+                               g_free (dirname);
+                               g_free (filename);
+                               return;
+                       }
+               }
+
                /*
-                * We need either DB_INIT_CDB or DB_INIT_LOCK, because we will have
-                * multiple threads reading and writing concurrently without
-                * any locking above libdb.
+                * DB_INIT_TXN enables transaction support. It requires DB_INIT_LOCK to
+                * initialize the locking subsystem and DB_INIT_LOG for the logging
+                * subsystem.
+                *
+                * DB_INIT_MPOOL enables the in-memory cache.
                 *
-                * DB_INIT_CDB enforces multiple reader/single writer by locking inside
-                * the database. It is used instead of DB_INIT_LOCK because DB_INIT_LOCK
-                * may deadlock, which would have to be called in a separate thread.
-                * Considered too complicated for not enough gain (= concurrent writes)
-                * at this point.
+                * Note that we need either DB_INIT_CDB or DB_INIT_LOCK, because we will
+                * have multiple threads reading and writing concurrently without
+                * any locking above libdb. Right now DB_INIT_LOCK is used because
+                * DB_INIT_TXN conflicts with DB_INIT_CDB.
                 */
-               db_error = (*env->open) (env, NULL, DB_INIT_CDB | DB_CREATE | DB_INIT_MPOOL | DB_PRIVATE | DB_THREAD, 0);
+               db_error = (*env->open) (env, dirname, DB_INIT_LOCK | DB_INIT_TXN | DB_INIT_LOG | DB_CREATE | DB_INIT_MPOOL | DB_PRIVATE | DB_THREAD, 0);
                if (db_error != 0) {
                        env->close (env, 0);
                        g_warning ("db_env_open failed with %s", db_strerror (db_error));
@@ -1257,7 +1324,7 @@ e_book_backend_file_open (EBookBackendSync *backend,
                return;
        }
 
-       db_error = (*db->open) (db, NULL, filename, NULL, DB_HASH, DB_THREAD, 0666);
+       db_error = (*db->open) (db, NULL, filename, NULL, DB_HASH, DB_THREAD | DB_AUTO_COMMIT, 0666);
 
        if (db_error == DB_OLD_VERSION) {
                db_error = e_db3_utils_upgrade_format (filename);
@@ -1280,7 +1347,7 @@ e_book_backend_file_open (EBookBackendSync *backend,
                        return;
                }
 
-               db_error = (*db->open) (db, NULL, filename, NULL, DB_HASH, DB_THREAD, 0666);
+               db_error = (*db->open) (db, NULL, filename, NULL, DB_HASH, DB_THREAD | DB_AUTO_COMMIT, 0666);
        }
 
        if (db_error == 0) {
@@ -1296,7 +1363,7 @@ e_book_backend_file_open (EBookBackendSync *backend,
                        return;
                }
 
-               db_error = (*db->open) (db, NULL, filename, NULL, DB_HASH, DB_RDONLY | DB_THREAD, 0666);
+               db_error = (*db->open) (db, NULL, filename, NULL, DB_HASH, DB_RDONLY | DB_THREAD | DB_AUTO_COMMIT, 0666);
 
                if (db_error != 0 && !only_if_exists) {
 
@@ -1319,7 +1386,7 @@ e_book_backend_file_open (EBookBackendSync *backend,
                                return;
                        }
 
-                       db_error = (*db->open) (db, NULL, filename, NULL, DB_HASH, DB_CREATE | DB_THREAD, 0666);
+                       db_error = (*db->open) (db, NULL, filename, NULL, DB_HASH, DB_CREATE | DB_THREAD | DB_AUTO_COMMIT, 0666);
                        if (db_error != 0) {
                                db->close (db, 0);
                                g_warning ("db->open (... %s ... DB_CREATE ...) failed with %s", filename, db_strerror (db_error));
@@ -1346,12 +1413,12 @@ e_book_backend_file_open (EBookBackendSync *backend,
 
 #ifdef CREATE_DEFAULT_VCARD
        if (create_default_vcard) {
-               EContact *contact = NULL;
+               GSList l;
+               l.data = XIMIAN_VCARD;
+               l.next = NULL;
 
-               if (!do_create (bf, XIMIAN_VCARD, &contact, NULL))
+               if (!do_create (bf, &l, NULL, NULL))
                        g_warning ("Cannot create default contact");
-               if (contact)
-                       g_object_unref (contact);
        }
 #endif
 
@@ -1474,7 +1541,7 @@ e_book_backend_file_get_backend_property (EBookBackendSync *backend,
        g_return_val_if_fail (prop_value != NULL, FALSE);
 
        if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
-               *prop_value = g_strdup ("local,do-initial-query,bulk-removes,contact-lists");
+               *prop_value = g_strdup ("local,do-initial-query,bulk-adds,bulk-removes,contact-lists");
        } else if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS)) {
                *prop_value = g_strdup (e_contact_field_name (E_CONTACT_FILE_AS));
        } else if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS)) {
@@ -1680,7 +1747,7 @@ e_book_backend_file_class_init (EBookBackendFileClass *klass)
        sync_class->open_sync                   = e_book_backend_file_open;
        sync_class->remove_sync                 = e_book_backend_file_remove;
        sync_class->get_backend_property_sync   = e_book_backend_file_get_backend_property;
-       sync_class->create_contact_sync         = e_book_backend_file_create_contact;
+       sync_class->create_contacts_sync        = e_book_backend_file_create_contacts;
        sync_class->remove_contacts_sync        = e_book_backend_file_remove_contacts;
        sync_class->modify_contact_sync         = e_book_backend_file_modify_contact;
        sync_class->get_contact_sync            = e_book_backend_file_get_contact;
index 1438959..c6f490d 100644 (file)
@@ -1203,22 +1203,24 @@ create_contact_finish (CreateContactData *data,
                        GDataContactsContact *new_contact,
                        const GError *gdata_error)
 {
-       EContact *e_contact;
-
        __debug__ (G_STRFUNC);
 
        if (gdata_error == NULL) {
                /* Add the new contact to the cache. If uploading the photo was successful, the photo's data is stored on the contact as the "photo"
                 * key, which the cache will pick up and store. */
+               EContact *e_contact;
+               GSList added_contacts = {NULL,};
                e_contact = cache_add_contact (data->backend, GDATA_ENTRY (new_contact));
-               e_data_book_respond_create (data->book, data->opid, NULL, e_contact);
+
+               added_contacts.data = e_contact;
+               e_data_book_respond_create_contacts (data->book, data->opid, NULL, &added_contacts);
                g_object_unref (e_contact);
        } else {
                GError *book_error = NULL;
 
                /* Report the error. */
                data_book_error_from_gdata_error (&book_error, gdata_error);
-               e_data_book_respond_create (data->book, data->opid, book_error, NULL);
+               e_data_book_respond_create_contacts (data->book, data->opid, book_error, NULL);
        }
 
        finish_operation (data->backend, data->opid, gdata_error);
@@ -1367,24 +1369,35 @@ finish:
  * operation responded to in create_contact_photo_query_cb().
  */
 static void
-e_book_backend_google_create_contact (EBookBackend *backend,
-                                      EDataBook *book,
-                                      guint32 opid,
-                                      GCancellable *cancellable,
-                                      const gchar *vcard_str)
+e_book_backend_google_create_contacts (EBookBackend *backend,
+                                       EDataBook *book,
+                                       guint32 opid,
+                                       GCancellable *cancellable,
+                                       const GSList *vcards)
 {
        EBookBackendGooglePrivate *priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
        EContact *contact;
        GDataEntry *entry;
        gchar *xml;
        CreateContactData *data;
+       const gchar *vcard_str = (const gchar *) vcards->data;
+
+       /* We make the assumption that the vCard list we're passed is always exactly one element long, since we haven't specified "bulk-adds"
+        * in our static capability list. This simplifies a lot of the logic, especially around asynchronous results. */
+       if (vcards->next != NULL) {
+               e_data_book_respond_create_contacts (book, opid,
+                                                    EDB_ERROR_EX (NOT_SUPPORTED,
+                                                    _("The backend does not support bulk additions")),
+                                                    NULL);
+               return;
+       }
 
        __debug__ (G_STRFUNC);
 
        __debug__ ("Creating: %s", vcard_str);
 
        if (!e_backend_get_online (E_BACKEND (backend))) {
-               e_data_book_respond_create (book, opid, EDB_ERROR (OFFLINE_UNAVAILABLE), NULL);
+               e_data_book_respond_create_contacts (book, opid, EDB_ERROR (OFFLINE_UNAVAILABLE), NULL);
                return;
        }
 
@@ -1487,6 +1500,13 @@ e_book_backend_google_remove_contacts (EBookBackend *backend,
 
        /* We make the assumption that the ID list we're passed is always exactly one element long, since we haven't specified "bulk-removes"
         * in our static capability list. This simplifies a lot of the logic, especially around asynchronous results. */
+       if (id_list->next != NULL) {
+               e_data_book_respond_remove_contacts (book, opid,
+                                                    EDB_ERROR_EX (NOT_SUPPORTED,
+                                                    _("The backend does not support bulk removals")),
+                                                    NULL);
+               return;
+       }
        g_return_if_fail (!id_list->next);
 
        /* Get the contact and associated GDataEntry from the cache */
@@ -2542,7 +2562,7 @@ e_book_backend_google_class_init (EBookBackendGoogleClass *klass)
        backend_class->start_book_view          = e_book_backend_google_start_book_view;
        backend_class->stop_book_view           = e_book_backend_google_stop_book_view;
        backend_class->remove                   = e_book_backend_google_remove;
-       backend_class->create_contact           = e_book_backend_google_create_contact;
+       backend_class->create_contacts          = e_book_backend_google_create_contacts;
        backend_class->remove_contacts          = e_book_backend_google_remove_contacts;
        backend_class->modify_contact           = e_book_backend_google_modify_contact;
        backend_class->get_contact              = e_book_backend_google_get_contact;
index 1412cfd..f10a294 100644 (file)
@@ -1553,24 +1553,25 @@ create_contact_handler (LDAPOp *op,
        EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (op->backend);
        gchar *ldap_error_msg;
        gint ldap_error;
+       GSList added_contacts = {NULL,};
 
        g_static_rec_mutex_lock (&eds_ldap_handler_lock);
        if (!bl->priv->ldap) {
                g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
-               e_data_book_respond_create (op->book,
-                                           op->opid,
-                                           EDB_ERROR_NOT_CONNECTED (),
-                                           NULL);
+               e_data_book_respond_create_contacts (op->book,
+                                                    op->opid,
+                                                    EDB_ERROR_NOT_CONNECTED (),
+                                                    NULL);
                ldap_op_finished (op);
                return;
        }
        g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
 
        if (LDAP_RES_ADD != ldap_msgtype (res)) {
-               e_data_book_respond_create (op->book,
-                                           op->opid,
-                                           EDB_ERROR_MSG_TYPE (ldap_msgtype (res)),
-                                           NULL);
+               e_data_book_respond_create_contacts (op->book,
+                                                    op->opid,
+                                                    EDB_ERROR_MSG_TYPE (ldap_msgtype (res)),
+                                                    NULL);
                ldap_op_finished (op);
                return;
        }
@@ -1590,10 +1591,11 @@ create_contact_handler (LDAPOp *op,
        ldap_memfree (ldap_error_msg);
 
        /* and lastly respond */
-       e_data_book_respond_create (op->book,
-                                   op->opid,
-                                   ldap_error_to_response (ldap_error),
-                                   create_op->new_contact);
+       added_contacts.data = create_op->new_contact;
+       e_data_book_respond_create_contacts (op->book,
+                                            op->opid,
+                                            ldap_error_to_response (ldap_error),
+                                            &added_contacts);
 
        ldap_op_finished (op);
 }
@@ -1609,11 +1611,11 @@ create_contact_dtor (LDAPOp *op)
 }
 
 static void
-e_book_backend_ldap_create_contact (EBookBackend *backend,
-                                    EDataBook *book,
-                                    guint32 opid,
-                                    GCancellable *cancellable,
-                                    const gchar *vcard)
+e_book_backend_ldap_create_contacts (EBookBackend *backend,
+                                     EDataBook *book,
+                                     guint32 opid,
+                                     GCancellable *cancellable,
+                                     const GSList *vcards)
 {
        LDAPCreateOp *create_op = g_new0 (LDAPCreateOp, 1);
        EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (backend);
@@ -1623,16 +1625,27 @@ e_book_backend_ldap_create_contact (EBookBackend *backend,
        GPtrArray *mod_array;
        LDAPMod **ldap_mods;
        gchar *new_uid;
+       const gchar *vcard = (const gchar *) vcards->data;
+
+       /* We make the assumption that the vCard list we're passed is always exactly one element long, since we haven't specified "bulk-adds"
+        * in our static capability list. This is because there is no clean way to roll back changes in case of an error. */
+       if (vcards->next != NULL) {
+               e_data_book_respond_create_contacts (book, opid,
+                                                    EDB_ERROR_EX (NOT_SUPPORTED,
+                                                    _("The backend does not support bulk additions")),
+                                                    NULL);
+               return;
+       }
 
        if (!e_backend_get_online (E_BACKEND (backend))) {
-               e_data_book_respond_create (book, opid, EDB_ERROR (REPOSITORY_OFFLINE), NULL);
+               e_data_book_respond_create_contacts (book, opid, EDB_ERROR (REPOSITORY_OFFLINE), NULL);
                return;
        }
 
        g_static_rec_mutex_lock (&eds_ldap_handler_lock);
        if (!bl->priv->ldap) {
                g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
-               e_data_book_respond_create (book, opid, EDB_ERROR_NOT_CONNECTED (), NULL);
+               e_data_book_respond_create_contacts (book, opid, EDB_ERROR_NOT_CONNECTED (), NULL);
                return;
        }
        g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
@@ -1730,10 +1743,10 @@ e_book_backend_ldap_create_contact (EBookBackend *backend,
        free_mods (mod_array);
 
        if (LDAP_SUCCESS != err) {
-               e_data_book_respond_create (create_op->op.book,
-                                           opid,
-                                           ldap_error_to_response (err),
-                                           NULL);
+               e_data_book_respond_create_contacts (create_op->op.book,
+                                                    opid,
+                                                    ldap_error_to_response (err),
+                                                    NULL);
                create_contact_dtor ((LDAPOp *) create_op);
                return;
        } else {
@@ -5669,7 +5682,7 @@ e_book_backend_ldap_class_init (EBookBackendLDAPClass *klass)
        parent_class->remove                    = e_book_backend_ldap_remove;
        parent_class->get_backend_property      = e_book_backend_ldap_get_backend_property;
 
-       parent_class->create_contact            = e_book_backend_ldap_create_contact;
+       parent_class->create_contacts           = e_book_backend_ldap_create_contacts;
        parent_class->remove_contacts           = e_book_backend_ldap_remove_contacts;
        parent_class->modify_contact            = e_book_backend_ldap_modify_contact;
        parent_class->get_contact               = e_book_backend_ldap_get_contact;
index fb84ec6..9318409 100644 (file)
@@ -56,6 +56,7 @@
 #define d(x)
 
 #define EDB_ERROR(_code) e_data_book_create_error (E_DATA_BOOK_STATUS_ ## _code, NULL)
+#define EDB_ERROR_EX(_code, _msg) e_data_book_create_error (E_DATA_BOOK_STATUS_ ## _code, _msg)
 
 G_DEFINE_TYPE (EBookBackendVCF, e_book_backend_vcf, E_TYPE_BOOK_BACKEND_SYNC)
 
@@ -271,17 +272,30 @@ do_create (EBookBackendVCF *bvcf,
 }
 
 static void
-e_book_backend_vcf_create_contact (EBookBackendSync *backend,
+e_book_backend_vcf_create_contacts (EBookBackendSync *backend,
                                    EDataBook *book,
                                    GCancellable *cancellable,
-                                   const gchar *vcard,
-                                   EContact **contact,
+                                   const GSList *vcards,
+                                   GSList **added_contacts,
                                    GError **perror)
 {
+       EContact *contact = NULL;
        EBookBackendVCF *bvcf = E_BOOK_BACKEND_VCF (backend);
+       const gchar *vcard = vcards->data;
+
+       /* We make the assumption that the vCard list we're passed is always exactly one element long, since we haven't specified "bulk-adds"
+        * in our static capability list. */
+       if (vcards->next != NULL) {
+               g_propagate_error (perror,
+                                  EDB_ERROR_EX (NOT_SUPPORTED,
+                                  _("The backend does not support bulk additions")));
+               return;
+       }
 
-       *contact = do_create(bvcf, vcard, TRUE);
-       if (!*contact) {
+       contact = do_create(bvcf, vcard, TRUE);
+       if (added_contacts) {
+               *added_contacts = g_slist_append (*added_contacts, contact);
+       } else {
                /* XXX need a different call status for this case, i
                 * think */
                g_propagate_error (perror, EDB_ERROR (CONTACT_NOT_FOUND));
@@ -301,6 +315,15 @@ e_book_backend_vcf_remove_contacts (EBookBackendSync *backend,
        const gchar *id = id_list->data;
        GList *elem;
 
+       /* We make the assumption that the ID list we're passed is always exactly one element long, since we haven't specified "bulk-removes"
+        * in our static capability list. */
+       if (id_list->next != NULL) {
+               g_propagate_error (perror,
+                                  EDB_ERROR_EX (NOT_SUPPORTED,
+                                  _("The backend does not support bulk removals")));
+               return;
+       }
+
        g_mutex_lock (bvcf->priv->mutex);
        elem = g_hash_table_lookup (bvcf->priv->contacts, id);
        if (!elem) {
@@ -734,7 +757,7 @@ e_book_backend_vcf_class_init (EBookBackendVCFClass *klass)
 
        sync_class->open_sync                   = e_book_backend_vcf_open;
        sync_class->get_backend_property_sync   = e_book_backend_vcf_get_backend_property;
-       sync_class->create_contact_sync         = e_book_backend_vcf_create_contact;
+       sync_class->create_contacts_sync        = e_book_backend_vcf_create_contacts;
        sync_class->remove_contacts_sync        = e_book_backend_vcf_remove_contacts;
        sync_class->modify_contact_sync         = e_book_backend_vcf_modify_contact;
        sync_class->get_contact_sync            = e_book_backend_vcf_get_contact;
index a399441..2b0fc0d 100644 (file)
@@ -301,21 +301,33 @@ webdav_handle_auth_request (EBookBackendWebdav *webdav)
 }
 
 static void
-e_book_backend_webdav_create_contact (EBookBackend *backend,
-                                      EDataBook *book,
-                                      guint32 opid,
-                                      GCancellable *cancellable,
-                                      const gchar *vcard)
+e_book_backend_webdav_create_contacts (EBookBackend *backend,
+                                       EDataBook *book,
+                                       guint32 opid,
+                                       GCancellable *cancellable,
+                                       const GSList *vcards)
 {
        EBookBackendWebdav        *webdav = E_BOOK_BACKEND_WEBDAV (backend);
        EBookBackendWebdavPrivate *priv   = webdav->priv;
        EContact                  *contact;
        gchar                     *uid;
        guint                      status;
-       gchar                     *status_reason = NULL;
+       gchar                     *status_reason = NULL;
+       const gchar               *vcard = (const gchar *) vcards->data;
+       GSList                     added_contacts = {NULL,};
+
+       /* We make the assumption that the vCard list we're passed is always exactly one element long, since we haven't specified "bulk-adds"
+        * in our static capability list. This is because there is no clean way to roll back changes in case of an error. */
+       if (vcards->next != NULL) {
+               e_data_book_respond_create_contacts (book, opid,
+                                                    EDB_ERROR_EX (NOT_SUPPORTED,
+                                                    _("The backend does not support bulk additions")),
+                                                    NULL);
+               return;
+       }
 
        if (!e_backend_get_online (E_BACKEND (backend))) {
-               e_data_book_respond_create (book, opid, EDB_ERROR (REPOSITORY_OFFLINE), NULL);
+               e_data_book_respond_create_contacts (book, opid, EDB_ERROR (REPOSITORY_OFFLINE), NULL);
                return;
        }
 
@@ -323,7 +335,7 @@ e_book_backend_webdav_create_contact (EBookBackend *backend,
         * good enough for us */
        uid = g_strdup_printf("%s%08X-%08X-%08X.vcf", priv->uri, rand(), rand(),
                              rand ());
-                             
+
        contact = e_contact_new_from_vcard_with_uid (vcard, uid);
 
        /* kill revision field (might have been set by some other backend) */
@@ -333,12 +345,12 @@ e_book_backend_webdav_create_contact (EBookBackend *backend,
        if (status != 201 && status != 204) {
                g_object_unref (contact);
                if (status == 401 || status == 407) {
-                       e_data_book_respond_create (book, opid, webdav_handle_auth_request (webdav), NULL);
+                       e_data_book_respond_create_contacts (book, opid, webdav_handle_auth_request (webdav), NULL);
                } else {
-                       e_data_book_respond_create (book, opid,
-                                       e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_OTHER_ERROR,
-                                               _("Create resource '%s' failed with HTTP status: %d (%s)"), uid, status, status_reason),
-                                       NULL);
+                       e_data_book_respond_create_contacts (book, opid,
+                                       e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_OTHER_ERROR,
+                                       _("Create resource '%s' failed with HTTP status: %d (%s)"), uid, status, status_reason),
+                                       NULL);
                }
                g_free (uid);
                g_free (status_reason);
@@ -358,7 +370,7 @@ e_book_backend_webdav_create_contact (EBookBackend *backend,
                g_object_unref (contact);
 
                if (new_contact == NULL) {
-                       e_data_book_respond_create (book, opid,
+                       e_data_book_respond_create_contacts (book, opid,
                                        EDB_ERROR (OTHER_ERROR), NULL);
                        g_free (uid);
                        return;
@@ -367,10 +379,11 @@ e_book_backend_webdav_create_contact (EBookBackend *backend,
        }
 
        e_book_backend_cache_add_contact (priv->cache, contact);
-       e_data_book_respond_create (book, opid, EDB_ERROR (SUCCESS), contact);
 
-       if (contact)
-               g_object_unref (contact);
+       added_contacts.data = contact;
+       e_data_book_respond_create_contacts (book, opid, EDB_ERROR (SUCCESS), &added_contacts);
+
+       g_object_unref (contact);
        g_free (uid);
 }
 
@@ -449,7 +462,7 @@ e_book_backend_webdav_modify_contact (EBookBackend *backend,
        gchar *status_reason = NULL;
 
        if (!e_backend_get_online (E_BACKEND (backend))) {
-               e_data_book_respond_create (book, opid,
+               e_data_book_respond_create_contacts (book, opid,
                                EDB_ERROR (REPOSITORY_OFFLINE), NULL);
                g_object_unref (contact);
                return;
@@ -1444,7 +1457,7 @@ e_book_backend_webdav_class_init (EBookBackendWebdavClass *klass)
        backend_class->open                     = e_book_backend_webdav_open;
        backend_class->get_backend_property     = e_book_backend_webdav_get_backend_property;
 
-       backend_class->create_contact           = e_book_backend_webdav_create_contact;
+       backend_class->create_contacts          = e_book_backend_webdav_create_contacts;
        backend_class->remove_contacts          = e_book_backend_webdav_remove_contacts;
        backend_class->modify_contact           = e_book_backend_webdav_modify_contact;
        backend_class->get_contact              = e_book_backend_webdav_get_contact;
index 90df80e..9d6acfd 100644 (file)
@@ -1273,15 +1273,18 @@ e_book_client_add_contact (EBookClient *client,
                            gpointer user_data)
 {
        gchar *vcard, *gdbus_vcard = NULL;
+       const gchar *strv[2];
 
        g_return_if_fail (contact != NULL);
        g_return_if_fail (E_IS_CONTACT (contact));
 
        vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
+       strv[0] = e_util_ensure_gdbus_string (vcard, &gdbus_vcard);
+       strv[1] = NULL;
 
-       e_client_proxy_call_string (E_CLIENT (client), e_util_ensure_gdbus_string (vcard, &gdbus_vcard), cancellable, callback, user_data, e_book_client_add_contact,
-                       e_gdbus_book_call_add_contact,
-                       NULL, NULL, e_gdbus_book_call_add_contact_finish, NULL, NULL);
+       e_client_proxy_call_strv (E_CLIENT (client), strv, cancellable, callback, user_data, e_book_client_add_contact,
+                       e_gdbus_book_call_add_contacts,
+                       NULL, NULL, NULL, e_gdbus_book_call_add_contacts_finish, NULL);
 
        g_free (vcard);
        g_free (gdbus_vcard);
@@ -1311,17 +1314,17 @@ e_book_client_add_contact_finish (EBookClient *client,
                                   GError **error)
 {
        gboolean res;
-       gchar *out_uid = NULL;
+       gchar **out_uids = NULL;
 
-       res = e_client_proxy_call_finish_string (E_CLIENT (client), result, &out_uid, error, e_book_client_add_contact);
+       res = e_client_proxy_call_finish_strv (E_CLIENT (client), result, &out_uids, error, e_book_client_add_contact);
 
-       if (res && out_uid && added_uid) {
-               *added_uid = out_uid;
+       if (res && out_uids && added_uid) {
+               *added_uid = g_strdup (out_uids[0]);
        } else {
-               g_free (out_uid);
                if (added_uid)
                        *added_uid = NULL;
        }
+       g_strfreev (out_uids);
 
        return res;
 }
@@ -1353,7 +1356,8 @@ e_book_client_add_contact_sync (EBookClient *client,
                                 GError **error)
 {
        gboolean res;
-       gchar *vcard, *gdbus_vcard = NULL, *out_uid = NULL;
+       gchar *vcard, *gdbus_vcard = NULL, **out_uids = NULL;
+       const gchar *strv[2];
 
        g_return_val_if_fail (client != NULL, FALSE);
        g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
@@ -1365,17 +1369,19 @@ e_book_client_add_contact_sync (EBookClient *client,
        }
 
        vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
+       strv[0] = e_util_ensure_gdbus_string (vcard, &gdbus_vcard);
+       strv[1] = NULL;
 
-       res = e_client_proxy_call_sync_string__string (E_CLIENT (client), e_util_ensure_gdbus_string (vcard, &gdbus_vcard), &out_uid, cancellable, error, e_gdbus_book_call_add_contact_sync);
+       res = e_client_proxy_call_sync_strv__strv (E_CLIENT (client), strv, &out_uids, cancellable, error, e_gdbus_book_call_add_contacts_sync);
 
-       if (res && out_uid && added_uid) {
-               *added_uid = out_uid;
+       if (res && out_uids && added_uid) {
+               *added_uid = g_strdup (out_uids[0]);
        } else {
-               g_free (out_uid);
                if (added_uid)
                        *added_uid = NULL;
        }
 
+       g_strfreev (out_uids);
        g_free (vcard);
        g_free (gdbus_vcard);
 
@@ -1383,6 +1389,155 @@ e_book_client_add_contact_sync (EBookClient *client,
 }
 
 /**
+ * e_book_client_add_contacts:
+ * @client: an #EBookClient
+ * @contacts: a #GSList of #EContact objects to add
+ * @cancellable: a #GCancellable; can be %NULL
+ * @callback: callback to call when a result is ready
+ * @user_data: user data for the @callback
+ *
+ * Adds @contacts to @client.
+ * The call is finished by e_book_client_add_contacts_finish()
+ * from the @callback.
+ *
+ * Since: 3.4
+ **/
+void
+e_book_client_add_contacts (EBookClient *client,
+                            /* const */ GSList *contacts,
+                            GCancellable *cancellable,
+                            GAsyncReadyCallback callback,
+                            gpointer user_data)
+{
+       gchar **array;
+       const GSList *l;
+       gint i = 0;
+
+       g_return_if_fail (contacts != NULL);
+
+       array = g_new0 (gchar *, g_slist_length (contacts) + 1);
+       for (l = contacts; l != NULL; l = l->next) {
+               gchar *vcard = e_vcard_to_string (E_VCARD (l->data), EVC_FORMAT_VCARD_30);
+               array[i++] = e_util_utf8_make_valid (vcard);
+               g_free (vcard);
+       }
+
+       e_client_proxy_call_strv (E_CLIENT (client), (const gchar * const *) array, cancellable, callback, user_data, e_book_client_add_contacts,
+                       e_gdbus_book_call_add_contacts,
+                       NULL, NULL, NULL, e_gdbus_book_call_add_contacts_finish, NULL);
+
+       g_strfreev (array);
+}
+
+/**
+ * e_book_client_add_contacts_finish:
+ * @client: an #EBookClient
+ * @result: a #GAsyncResult
+ * @added_uids: (out): UIDs of newly added contacts; can be %NULL
+ * @error: (out): a #GError to set an error, if any
+ *
+ * Finishes previous call of e_book_client_add_contacts() and
+ * sets @added_uids to the UIDs of newly added contacts if successful.
+ * This #GSList should be freed with e_client_util_free_string_slist().
+ *
+ * If any of the contacts cannot be inserted, all of the insertions will be
+ * reverted and this method will return %FALSE.
+ *
+ * Note: This is not modifying original #EContact objects.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ *
+ * Since: 3.4
+ **/
+gboolean
+e_book_client_add_contacts_finish (EBookClient *client,
+                                   GAsyncResult *result,
+                                   GSList **added_uids,
+                                   GError **error)
+{
+       gboolean res;
+       gchar **out_uids = NULL;
+
+       res = e_client_proxy_call_finish_strv (E_CLIENT (client), result, &out_uids, error, e_book_client_add_contacts);
+
+       if (res && out_uids && added_uids) {
+               *added_uids = e_client_util_strv_to_slist ((const gchar * const*) out_uids);
+       } else {
+               if (added_uids)
+                       *added_uids = NULL;
+       }
+
+       g_strfreev (out_uids);
+
+       return res;
+}
+
+/**
+ * e_book_client_add_contacts_sync:
+ * @client: an #EBookClient
+ * @contacts: a #GSList of #EContact objects to add
+ * @added_uids: (out): UIDs of newly added contacts; can be %NULL
+ * @cancellable: a #GCancellable; can be %NULL
+ * @error: (out): a #GError to set an error, if any
+ *
+ * Adds @contacts to @client and
+ * sets @added_uids to the UIDs of newly added contacts if successful.
+ * This #GSList should be freed with e_client_util_free_string_slist().
+ *
+ * If any of the contacts cannot be inserted, all of the insertions will be
+ * reverted and this method will return %FALSE.
+ *
+ * Note: This is not modifying original @contacts, thus if it's needed,
+ * then use e_contact_set (contact, E_CONTACT_UID, new_uid).
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ *
+ * Since: 3.4
+ **/
+gboolean
+e_book_client_add_contacts_sync        (EBookClient *client,
+                                 /* const */ GSList *contacts,
+                                 GSList **added_uids,
+                                 GCancellable *cancellable,
+                                 GError **error)
+{
+       gboolean res;
+       gint i = 0;
+       gchar **array, **out_uids = NULL;
+       const GSList *l;
+
+       g_return_val_if_fail (client != NULL, FALSE);
+       g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
+       g_return_val_if_fail (client->priv != NULL, FALSE);
+
+       if (!client->priv->gdbus_book) {
+               set_proxy_gone_error (error);
+               return FALSE;
+       }
+
+       array = g_new0 (gchar *, g_slist_length (contacts) + 1);
+       for (l = contacts; l != NULL; l = l->next) {
+               gchar *vcard = e_vcard_to_string (E_VCARD (l->data), EVC_FORMAT_VCARD_30);
+               array[i++] = e_util_utf8_make_valid (vcard);
+               g_free (vcard);
+       }
+
+       res = e_client_proxy_call_sync_strv__strv (E_CLIENT (client), (const gchar * const*) array, &out_uids, cancellable, error, e_gdbus_book_call_add_contacts_sync);
+
+       if (res && out_uids && added_uids) {
+               *added_uids = e_client_util_strv_to_slist ((const gchar * const*) out_uids);
+       } else {
+               if (added_uids)
+                       *added_uids = NULL;
+       }
+
+       g_strfreev (out_uids);
+       g_strfreev (array);
+
+       return res;
+}
+
+/**
  * e_book_client_modify_contact:
  * @client: an #EBookClient
  * @contact: an #EContact
@@ -1501,7 +1656,7 @@ e_book_client_remove_contact (EBookClient *client,
                               gpointer user_data)
 {
        gchar *uid;
-       const gchar *lst[2];
+       const gchar *strv[2];
 
        g_return_if_fail (contact != NULL);
        g_return_if_fail (E_IS_CONTACT (contact));
@@ -1509,10 +1664,10 @@ e_book_client_remove_contact (EBookClient *client,
        uid = e_util_utf8_make_valid (e_contact_get_const ((EContact *) contact, E_CONTACT_UID));
        g_return_if_fail (uid != NULL);
 
-       lst[0] = uid;
-       lst[1] = NULL;
+       strv[0] = uid;
+       strv[1] = NULL;
 
-       e_client_proxy_call_strv (E_CLIENT (client), lst, cancellable, callback, user_data, e_book_client_remove_contact,
+       e_client_proxy_call_strv (E_CLIENT (client), strv, cancellable, callback, user_data, e_book_client_remove_contact,
                        e_gdbus_book_call_remove_contacts,
                        e_gdbus_book_call_remove_contacts_finish, NULL, NULL, NULL, NULL);
 
@@ -1560,7 +1715,7 @@ e_book_client_remove_contact_sync (EBookClient *client,
 {
        gboolean res;
        gchar *uid;
-       const gchar *lst[2];
+       const gchar *strv[2];
 
        g_return_val_if_fail (client != NULL, FALSE);
        g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
@@ -1576,10 +1731,10 @@ e_book_client_remove_contact_sync (EBookClient *client,
        uid = e_util_utf8_make_valid (e_contact_get_const ((EContact *) contact, E_CONTACT_UID));
        g_return_val_if_fail (uid != NULL, 0);
 
-       lst[0] = uid;
-       lst[1] = NULL;
+       strv[0] = uid;
+       strv[1] = NULL;
 
-       res = e_client_proxy_call_sync_strv__void (E_CLIENT (client), lst, cancellable, error, e_gdbus_book_call_remove_contacts_sync);
+       res = e_client_proxy_call_sync_strv__void (E_CLIENT (client), strv, cancellable, error, e_gdbus_book_call_remove_contacts_sync);
 
        g_free (uid);
 
@@ -1608,17 +1763,17 @@ e_book_client_remove_contact_by_uid (EBookClient *client,
                                      gpointer user_data)
 {
        gchar *safe_uid;
-       const gchar *lst[2];
+       const gchar *strv[2];
 
        g_return_if_fail (uid != NULL);
 
        safe_uid = e_util_utf8_make_valid (uid);
        g_return_if_fail (safe_uid != NULL);
 
-       lst[0] = safe_uid;
-       lst[1] = NULL;
+       strv[0] = safe_uid;
+       strv[1] = NULL;
 
-       e_client_proxy_call_strv (E_CLIENT (client), lst, cancellable, callback, user_data, e_book_client_remove_contact_by_uid,
+       e_client_proxy_call_strv (E_CLIENT (client), strv, cancellable, callback, user_data, e_book_client_remove_contact_by_uid,
                        e_gdbus_book_call_remove_contacts,
                        e_gdbus_book_call_remove_contacts_finish, NULL, NULL, NULL, NULL);
 
@@ -1666,7 +1821,7 @@ e_book_client_remove_contact_by_uid_sync (EBookClient *client,
 {
        gboolean res;
        gchar *safe_uid;
-       const gchar *lst[2];
+       const gchar *strv[2];
 
        g_return_val_if_fail (client != NULL, FALSE);
        g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
@@ -1681,10 +1836,10 @@ e_book_client_remove_contact_by_uid_sync (EBookClient *client,
        safe_uid = e_util_utf8_make_valid (uid);
        g_return_val_if_fail (safe_uid != NULL, FALSE);
 
-       lst[0] = safe_uid;
-       lst[1] = NULL;
+       strv[0] = safe_uid;
+       strv[1] = NULL;
 
-       res = e_client_proxy_call_sync_strv__void (E_CLIENT (client), lst, cancellable, error, e_gdbus_book_call_remove_contacts_sync);
+       res = e_client_proxy_call_sync_strv__void (E_CLIENT (client), strv, cancellable, error, e_gdbus_book_call_remove_contacts_sync);
 
        g_free (safe_uid);
 
@@ -1715,18 +1870,18 @@ e_book_client_remove_contacts (EBookClient *client,
                                GAsyncReadyCallback callback,
                                gpointer user_data)
 {
-       gchar **lst;
+       gchar **strv;
 
        g_return_if_fail (uids != NULL);
 
-       lst = e_client_util_slist_to_strv (uids);
-       g_return_if_fail (lst != NULL);
+       strv = e_client_util_slist_to_strv (uids);
+       g_return_if_fail (strv != NULL);
 
-       e_client_proxy_call_strv (E_CLIENT (client), (const gchar * const *) lst, cancellable, callback, user_data, e_book_client_remove_contacts,
+       e_client_proxy_call_strv (E_CLIENT (client), (const gchar * const *) strv, cancellable, callback, user_data, e_book_client_remove_contacts,
                        e_gdbus_book_call_remove_contacts,
                        e_gdbus_book_call_remove_contacts_finish, NULL, NULL, NULL, NULL);
 
-       g_strfreev (lst);
+       g_strfreev (strv);
 }
 
 /**
@@ -1772,7 +1927,7 @@ e_book_client_remove_contacts_sync (EBookClient *client,
                                     GError **error)
 {
        gboolean res;
-       gchar **lst;
+       gchar **strv;
 
        g_return_val_if_fail (client != NULL, FALSE);
        g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
@@ -1784,12 +1939,12 @@ e_book_client_remove_contacts_sync (EBookClient *client,
                return FALSE;
        }
 
-       lst = e_client_util_slist_to_strv (uids);
-       g_return_val_if_fail (lst != NULL, FALSE);
+       strv = e_client_util_slist_to_strv (uids);
+       g_return_val_if_fail (strv != NULL, FALSE);
 
-       res = e_client_proxy_call_sync_strv__void (E_CLIENT (client), (const gchar * const *) lst, cancellable, error, e_gdbus_book_call_remove_contacts_sync);
+       res = e_client_proxy_call_sync_strv__void (E_CLIENT (client), (const gchar * const *) strv, cancellable, error, e_gdbus_book_call_remove_contacts_sync);
 
-       g_strfreev (lst);
+       g_strfreev (strv);
 
        return res;
 }
index 1435ce8..5482bde 100644 (file)
@@ -140,6 +140,10 @@ void               e_book_client_add_contact                       (EBookClient *client, /* const */ EContact *co
 gboolean       e_book_client_add_contact_finish                (EBookClient *client, GAsyncResult *result, gchar **added_uid, GError **error);
 gboolean       e_book_client_add_contact_sync                  (EBookClient *client, /* const */ EContact *contact, gchar **added_uid, GCancellable *cancellable, GError **error);
 
+void           e_book_client_add_contacts                      (EBookClient *client, /* const */ GSList *contacts, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
+gboolean       e_book_client_add_contacts_finish               (EBookClient *client, GAsyncResult *result, GSList **added_uids, GError **error);
+gboolean       e_book_client_add_contacts_sync                 (EBookClient *client, /* const */ GSList *contacts, GSList **added_uids, GCancellable *cancellable, GError **error);
+
 void           e_book_client_modify_contact                    (EBookClient *client, /* const */ EContact *contact, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
 gboolean       e_book_client_modify_contact_finish             (EBookClient *client, GAsyncResult *result, GError **error);
 gboolean       e_book_client_modify_contact_sync               (EBookClient *client, /* const */ EContact *contact, GCancellable *cancellable, GError **error);
index 5f4f525..6598177 100644 (file)
@@ -423,7 +423,8 @@ e_book_add_contact (EBook *book,
                     GError **error)
 {
        GError *err = NULL;
-       gchar *vcard, *uid = NULL, *gdbus_vcard = NULL;
+       gchar *vcard, **uids = NULL, *gdbus_vcard = NULL;
+       const gchar *strv[2];
 
        g_return_val_if_fail (E_IS_BOOK (book), FALSE);
        g_return_val_if_fail (E_IS_CONTACT (contact), FALSE);
@@ -432,13 +433,16 @@ e_book_add_contact (EBook *book,
                book->priv->gdbus_book, E_BOOK_ERROR_REPOSITORY_OFFLINE);
 
        vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
-       e_gdbus_book_call_add_contact_sync (book->priv->gdbus_book, e_util_ensure_gdbus_string (vcard, &gdbus_vcard), &uid, NULL, &err);
+       strv[0] = e_util_ensure_gdbus_string (vcard, &gdbus_vcard);
+       strv[1] = NULL;
+
+       e_gdbus_book_call_add_contacts_sync (book->priv->gdbus_book, strv, &uids, NULL, &err);
        g_free (vcard);
        g_free (gdbus_vcard);
 
-       if (uid) {
-               e_contact_set (contact, E_CONTACT_UID, uid);
-               g_free (uid);
+       if (uids) {
+               e_contact_set (contact, E_CONTACT_UID, uids[0]);
+               g_strfreev (uids);
        }
 
        return unwrap_gerror (err, error);
@@ -450,12 +454,12 @@ add_contact_reply (GObject *gdbus_book,
                    gpointer user_data)
 {
        GError *err = NULL, *error = NULL;
-       gchar *uid = NULL;
+       gchar *uid = NULL, **uids = NULL;
        AsyncData *data = user_data;
        EBookIdAsyncCallback excb = data->excallback;
        EBookIdCallback cb = data->callback;
 
-       e_gdbus_book_call_add_contact_finish (G_DBUS_PROXY (gdbus_book), res, &uid, &error);
+       e_gdbus_book_call_add_contacts_finish (G_DBUS_PROXY (gdbus_book), res, &uids, &error);
 
        unwrap_gerror (error, &err);
 
@@ -463,6 +467,8 @@ add_contact_reply (GObject *gdbus_book,
         * for the OUT values. This is bad. */
        if (error)
                uid = NULL;
+       else
+               uid = uids[0];
 
        if (cb)
                cb (data->book, err ? err->code : E_BOOK_ERROR_OK, uid, data->closure);
@@ -472,8 +478,8 @@ add_contact_reply (GObject *gdbus_book,
        if (err)
                g_error_free (err);
 
-       if (uid)
-               g_free (uid);
+       if (uids)
+               g_strfreev (uids);
 
        g_object_unref (data->book);
        g_slice_free (AsyncData, data);
@@ -500,6 +506,7 @@ e_book_async_add_contact (EBook *book,
 {
        gchar *vcard, *gdbus_vcard = NULL;
        AsyncData *data;
+       const gchar *strv[2];
 
        g_return_val_if_fail (E_IS_BOOK (book), FALSE);
        g_return_val_if_fail (E_IS_CONTACT (contact), FALSE);
@@ -508,13 +515,15 @@ e_book_async_add_contact (EBook *book,
                book->priv->gdbus_book, E_BOOK_ERROR_REPOSITORY_OFFLINE);
 
        vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
+       strv[0] = e_util_ensure_gdbus_string (vcard, &gdbus_vcard);
+       strv[1] = NULL;
 
        data = g_slice_new0 (AsyncData);
        data->book = g_object_ref (book);
        data->callback = cb;
        data->closure = closure;
 
-       e_gdbus_book_call_add_contact (book->priv->gdbus_book, e_util_ensure_gdbus_string (vcard, &gdbus_vcard), NULL, add_contact_reply, data);
+       e_gdbus_book_call_add_contacts (book->priv->gdbus_book, strv, NULL, add_contact_reply, data);
 
        g_free (vcard);
        g_free (gdbus_vcard);
@@ -545,6 +554,7 @@ e_book_add_contact_async (EBook *book,
 {
        gchar *vcard, *gdbus_vcard = NULL;
        AsyncData *data;
+       const gchar *strv[2];
 
        g_return_val_if_fail (E_IS_BOOK (book), FALSE);
        g_return_val_if_fail (E_IS_CONTACT (contact), FALSE);
@@ -553,13 +563,15 @@ e_book_add_contact_async (EBook *book,
                book->priv->gdbus_book, E_BOOK_ERROR_REPOSITORY_OFFLINE);
 
        vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
+       strv[0] = e_util_ensure_gdbus_string (vcard, &gdbus_vcard);
+       strv[1] = NULL;
 
        data = g_slice_new0 (AsyncData);
        data->book = g_object_ref (book);
        data->excallback = cb;
        data->closure = closure;
 
-       e_gdbus_book_call_add_contact (book->priv->gdbus_book, e_util_ensure_gdbus_string (vcard, &gdbus_vcard), NULL, add_contact_reply, data);
+       e_gdbus_book_call_add_contacts (book->priv->gdbus_book, strv, NULL, add_contact_reply, data);
 
        g_free (vcard);
        g_free (gdbus_vcard);
@@ -3755,13 +3767,13 @@ array_to_stringlist (gchar **list)
 }
 
 static EList *
-array_to_elist (gchar **list)
+array_to_elist (gchar **strv)
 {
        EList *elst = NULL;
-       gchar **i = list;
+       gchar **i = strv;
 
        elst = e_list_new (NULL, (EListFreeFunc) g_free, NULL);
-       if (!list)
+       if (!strv)
                return elst;
 
        while (*i != NULL) {
index ecb386d..7ee5635 100644 (file)
@@ -11,6 +11,7 @@
 #endif
 
 #include "e-book-backend-sync.h"
+#include "libedataserver/e-data-server-util.h"
 
 G_DEFINE_TYPE (EBookBackendSync, e_book_backend_sync, E_TYPE_BOOK_BACKEND)
 
@@ -61,31 +62,31 @@ e_book_backend_sync_open (EBookBackendSync *backend,
 }
 
 /**
- * e_book_backend_sync_create_contact:
+ * e_book_backend_sync_create_contacts:
  * @backend: an #EBookBackendSync
  * @book: an #EDataBook
  * @cancellable: a #GCancellable for the operation
- * @vcard: a VCard representation of a contact
- * @contact: a pointer to a location to store the resulting #EContact
+ * @vcards: a #GSList of vCard representations of contacts
+ * @added_contacts: a pointer to a location to store the resulting #EContact list
  * @error: #GError to set, when something fails
  *
- * Creates a new contact with the contents of @vcard in @backend.
+ * Creates new contacts with the contents of @vcards in @backend.
  **/
 void
-e_book_backend_sync_create_contact (EBookBackendSync *backend,
-                                    EDataBook *book,
-                                    GCancellable *cancellable,
-                                    const gchar *vcard,
-                                    EContact **contact,
-                                    GError **error)
+e_book_backend_sync_create_contacts (EBookBackendSync *backend,
+                                     EDataBook *book,
+                                     GCancellable *cancellable,
+                                     const GSList *vcards,
+                                     GSList **added_contacts,
+                                     GError **error)
 {
        e_return_data_book_error_if_fail (E_IS_BOOK_BACKEND_SYNC (backend), E_DATA_BOOK_STATUS_INVALID_ARG);
        e_return_data_book_error_if_fail (E_IS_DATA_BOOK (book), E_DATA_BOOK_STATUS_INVALID_ARG);
-       e_return_data_book_error_if_fail (vcard, E_DATA_BOOK_STATUS_INVALID_ARG);
-       e_return_data_book_error_if_fail (contact, E_DATA_BOOK_STATUS_INVALID_ARG);
-       e_return_data_book_error_if_fail (E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->create_contact_sync, E_DATA_BOOK_STATUS_NOT_SUPPORTED);
+       e_return_data_book_error_if_fail (vcards, E_DATA_BOOK_STATUS_INVALID_ARG);
+       e_return_data_book_error_if_fail (added_contacts, E_DATA_BOOK_STATUS_INVALID_ARG);
+       e_return_data_book_error_if_fail (E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->create_contacts_sync, E_DATA_BOOK_STATUS_NOT_SUPPORTED);
 
-       (* E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->create_contact_sync) (backend, book, cancellable, vcard, contact, error);
+       (* E_BOOK_BACKEND_SYNC_GET_CLASS (backend)->create_contacts_sync) (backend, book, cancellable, vcards, added_contacts, error);
 }
 
 /**
@@ -481,21 +482,20 @@ book_backend_set_backend_property (EBookBackend *backend,
 }
 
 static void
-book_backend_create_contact (EBookBackend *backend,
-                             EDataBook *book,
-                             guint32 opid,
-                             GCancellable *cancellable,
-                             const gchar *vcard)
+book_backend_create_contacts (EBookBackend *backend,
+                              EDataBook *book,
+                              guint32 opid,
+                              GCancellable *cancellable,
+                              const GSList *vcards)
 {
        GError *error = NULL;
-       EContact *contact = NULL;
+       GSList *added_contacts = NULL;
 
-       e_book_backend_sync_create_contact (E_BOOK_BACKEND_SYNC (backend), book, cancellable, vcard, &contact, &error);
+       e_book_backend_sync_create_contacts (E_BOOK_BACKEND_SYNC (backend), book, cancellable, vcards, &added_contacts, &error);
 
-       e_data_book_respond_create (book, opid, error, contact);
+       e_data_book_respond_create_contacts (book, opid, error, added_contacts);
 
-       if (contact)
-               g_object_unref (contact);
+       e_util_free_object_slist (added_contacts);
 }
 
 static void
@@ -668,7 +668,7 @@ e_book_backend_sync_class_init (EBookBackendSyncClass *klass)
        backend_class->refresh                  = book_backend_refresh;
        backend_class->get_backend_property     = book_backend_get_backend_property;
        backend_class->set_backend_property     = book_backend_set_backend_property;
-       backend_class->create_contact           = book_backend_create_contact;
+       backend_class->create_contacts          = book_backend_create_contacts;
        backend_class->remove_contacts          = book_backend_remove_contacts;
        backend_class->modify_contact           = book_backend_modify_contact;
        backend_class->get_contact              = book_backend_get_contact;
index ec45ac2..621cdb3 100644 (file)
@@ -33,7 +33,7 @@ struct _EBookBackendSyncClass {
        void (* refresh_sync)                   (EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, GError **error);
        gboolean (*get_backend_property_sync)   (EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const gchar *prop_name, gchar **prop_value, GError **error);
        gboolean (*set_backend_property_sync)   (EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const gchar *prop_name, const gchar *prop_value, GError **error);
-       void (*create_contact_sync)             (EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const gchar *vcard, EContact **contact, GError **error);
+       void (*create_contacts_sync)            (EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const GSList *vcards, GSList **added_contacts, GError **error);
        void (*remove_contacts_sync)            (EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const GSList *id_list, GSList **removed_ids, GError **error);
        void (*modify_contact_sync)             (EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const gchar *vcard, EContact **contact, GError **error);
        void (*get_contact_sync)                (EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const gchar *id, gchar **vcard, GError **error);
@@ -52,7 +52,7 @@ void          e_book_backend_sync_remove              (EBookBackendSync *backend, EDataBook *book, G
 void           e_book_backend_sync_refresh             (EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, GError **error);
 gboolean       e_book_backend_sync_get_backend_property (EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const gchar *prop_name, gchar **prop_value, GError **error);
 gboolean       e_book_backend_sync_set_backend_property (EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const gchar *prop_name, const gchar *prop_value, GError **error);
-void           e_book_backend_sync_create_contact      (EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const gchar *vcard, EContact **contact, GError **error);
+void           e_book_backend_sync_create_contacts     (EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const GSList *vcards, GSList **added_contacts, GError **error);
 void           e_book_backend_sync_remove_contacts     (EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const GSList *id_list, GSList **removed_ids, GError **error);
 void           e_book_backend_sync_modify_contact      (EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const gchar *vcard, EContact **contact, GError **error);
 void           e_book_backend_sync_get_contact         (EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, const gchar *id, gchar **vcard, GError **error);
index cb68604..5b69194 100644 (file)
@@ -429,35 +429,35 @@ e_book_backend_refresh (EBookBackend *backend,
 }
 
 /**
- * e_book_backend_create_contact:
+ * e_book_backend_create_contacts
  * @backend: an #EBookBackend
  * @book: an #EDataBook
  * @opid: the ID to use for this operation
  * @cancellable: a #GCancellable for the operation
- * @vcard: the VCard to add
+ * @vcards: a #GSList of vCards to add
  *
- * Executes a 'create contact' request specified by @opid on @book
+ * Executes a 'create contacts' request specified by @opid on @book
  * using @backend.
- * This might be finished with e_data_book_respond_create().
+ * This might be finished with e_data_book_respond_create_contacts().
  **/
 void
-e_book_backend_create_contact (EBookBackend *backend,
+e_book_backend_create_contacts (EBookBackend *backend,
                                EDataBook *book,
                                guint32 opid,
                                GCancellable *cancellable,
-                               const gchar *vcard)
+                               const GSList *vcards)
 {
        g_return_if_fail (E_IS_BOOK_BACKEND (backend));
        g_return_if_fail (E_IS_DATA_BOOK (book));
-       g_return_if_fail (vcard);
-       g_return_if_fail (E_BOOK_BACKEND_GET_CLASS (backend)->create_contact);
+       g_return_if_fail (vcards);
+       g_return_if_fail (E_BOOK_BACKEND_GET_CLASS (backend)->create_contacts);
 
        if (e_book_backend_is_opening (backend))
-               e_data_book_respond_create (book, opid, EDB_OPENING_ERROR, NULL);
+               e_data_book_respond_create_contacts (book, opid, EDB_OPENING_ERROR, NULL);
        else if (!e_book_backend_is_opened (backend))
-               e_data_book_respond_create (book, opid, EDB_NOT_OPENED_ERROR, NULL);
+               e_data_book_respond_create_contacts (book, opid, EDB_NOT_OPENED_ERROR, NULL);
        else
-               (* E_BOOK_BACKEND_GET_CLASS (backend)->create_contact) (backend, book, opid, cancellable, vcard);
+               (* E_BOOK_BACKEND_GET_CLASS (backend)->create_contacts) (backend, book, opid, cancellable, vcards);
 }
 
 /**
index fd209f1..7e047d9 100644 (file)
@@ -140,7 +140,7 @@ struct _EBookBackendClass {
        void    (* authenticate_user)           (EBookBackend *backend, GCancellable *cancellable, ECredentials *credentials);
 
        void    (* refresh)                     (EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable);
-       void    (* create_contact)              (EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable, const gchar *vcard);
+       void    (* create_contacts)             (EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable, const GSList *vcards);
        void    (* remove_contacts)             (EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable, const GSList *id_list);
        void    (* modify_contact)              (EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable, const gchar *vcard);
        void    (* get_contact)                 (EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable, const gchar *id);
@@ -177,7 +177,7 @@ void                e_book_backend_set_backend_property (EBookBackend *backend, EDataBook *boo
 void           e_book_backend_open             (EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable, gboolean only_if_exists);
 void           e_book_backend_remove           (EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable);
 void           e_book_backend_refresh          (EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable);
-void           e_book_backend_create_contact   (EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable, const gchar *vcard);
+void           e_book_backend_create_contacts  (EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable, const GSList *vcards);
 void           e_book_backend_remove_contacts  (EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable, const GSList *id_list);
 void           e_book_backend_modify_contact   (EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable, const gchar *vcard);
 void           e_book_backend_get_contact      (EBookBackend *backend, EDataBook *book, guint32 opid, GCancellable *cancellable, const gchar *id);
index 35c8420..3c0b5d2 100644 (file)
@@ -66,7 +66,7 @@ typedef enum {
        OP_GET_CONTACTS,
        OP_GET_CONTACTS_UIDS,
        OP_AUTHENTICATE,
-       OP_ADD_CONTACT,
+       OP_ADD_CONTACTS,
        OP_REMOVE_CONTACTS,
        OP_MODIFY_CONTACT,
        OP_GET_BACKEND_PROPERTY,
@@ -93,6 +93,7 @@ typedef struct {
                /* OP_REMOVE_CONTACTS */
                GSList *ids;
                /* OP_ADD_CONTACT */
+               GSList *vcards;
                /* OP_MODIFY_CONTACT */
                gchar *vcard;
                /* OP_GET_VIEW */
@@ -153,9 +154,9 @@ operation_thread (gpointer data,
        case OP_OPEN:
                e_book_backend_open (backend, op->book, op->id, op->cancellable, op->d.only_if_exists);
                break;
-       case OP_ADD_CONTACT:
-               e_book_backend_create_contact (backend, op->book, op->id, op->cancellable, op->d.vcard);
-               g_free (op->d.vcard);
+       case OP_ADD_CONTACTS:
+               e_book_backend_create_contacts (backend, op->book, op->id, op->cancellable, op->d.vcards);
+               e_util_free_string_slist (op->d.vcards);
                break;
        case OP_GET_CONTACT:
                e_book_backend_get_contact (backend, op->book, op->id, op->cancellable, op->d.uid);
@@ -175,8 +176,7 @@ operation_thread (gpointer data,
                break;
        case OP_REMOVE_CONTACTS:
                e_book_backend_remove_contacts (backend, op->book, op->id, op->cancellable, op->d.ids);
-               g_slist_foreach (op->d.ids, (GFunc) g_free, NULL);
-               g_slist_free (op->d.ids);
+               e_util_free_string_slist (op->d.ids);
                break;
        case OP_REMOVE:
                e_book_backend_remove (backend, op->book, op->id, op->cancellable);
@@ -614,14 +614,14 @@ impl_Book_get_contact_list_uids (EGdbusBook *object,
 }
 
 static gboolean
-impl_Book_add_contact (EGdbusBook *object,
+impl_Book_add_contacts (EGdbusBook *object,
                        GDBusMethodInvocation *invocation,
-                       const gchar *in_vcard,
+                       const gchar * const *in_vcards,
                        EDataBook *book)
 {
        OperationData *op;
 
-       if (in_vcard == NULL || !*in_vcard) {
+       if (in_vcards == NULL || !*in_vcards) {
                GError *error = e_data_book_create_error (E_DATA_BOOK_STATUS_INVALID_QUERY, NULL);
                /* Translators: This is prefix to a detailed error message */
                data_book_return_error (invocation, error, _("Cannot add contact: "));
@@ -629,10 +629,10 @@ impl_Book_add_contact (EGdbusBook *object,
                return TRUE;
        }
 
-       op = op_new (OP_ADD_CONTACT, book);
-       op->d.vcard = g_strdup (in_vcard);
+       op = op_new (OP_ADD_CONTACTS, book);
+       op->d.vcards = e_util_strv_to_slist (in_vcards);
 
-       e_gdbus_book_complete_add_contact (book->priv->gdbus_object, invocation, op->id);
+       e_gdbus_book_complete_add_contacts (book->priv->gdbus_object, invocation, op->id);
        e_operation_pool_push (ops_pool, op);
 
        return TRUE;
@@ -1013,25 +1013,39 @@ e_data_book_respond_get_contact_list_uids (EDataBook *book,
 }
 
 void
-e_data_book_respond_create (EDataBook *book,
-                            guint32 opid,
-                            GError *error,
-                            const EContact *contact)
+e_data_book_respond_create_contacts (EDataBook *book,
+                                     guint32 opid,
+                                     GError *error,
+                                     const GSList *contacts)
 {
-       gchar *gdbus_uid = NULL;
+       gchar **array = NULL;
+       const GSList *l;
+       gint i = 0;
 
        op_complete (book, opid);
 
+       array = g_new0 (gchar *, g_slist_length ((GSList *) contacts) + 1);
+       for (l = contacts; l != NULL; l = l->next) {
+               EContact *contact = E_CONTACT (l->data);
+
+               array[i++] = e_util_utf8_make_valid (e_contact_get_const (contact, E_CONTACT_UID));
+       }
+
        /* Translators: This is prefix to a detailed error message */
        g_prefix_error (&error, "%s", _("Cannot add contact: "));
 
-       e_gdbus_book_emit_add_contact_done (book->priv->gdbus_object, opid, error, e_util_ensure_gdbus_string (e_contact_get_const ((EContact *) contact, E_CONTACT_UID), &gdbus_uid));
+       e_gdbus_book_emit_add_contacts_done (book->priv->gdbus_object, opid, error, (const gchar * const *) array);
 
-       g_free (gdbus_uid);
+       g_strfreev (array);
        if (error) {
                g_error_free (error);
        } else {
-               e_book_backend_notify_update (e_data_book_get_backend (book), contact);
+               for (l = contacts; l != NULL; l = l->next) {
+                       EContact *contact = E_CONTACT (l->data);
+
+                       e_book_backend_notify_update (e_data_book_get_backend (book), contact);
+               }
+
                e_book_backend_notify_complete (e_data_book_get_backend (book));
        }
 }
@@ -1376,8 +1390,8 @@ e_data_book_init (EDataBook *ebook)
                gdbus_object, "handle-authenticate-user",
                G_CALLBACK (impl_Book_authenticate_user), ebook);
        g_signal_connect (
-               gdbus_object, "handle-add-contact",
-               G_CALLBACK (impl_Book_add_contact), ebook);
+               gdbus_object, "handle-add-contacts",
+               G_CALLBACK (impl_Book_add_contacts), ebook);
        g_signal_connect (
                gdbus_object, "handle-remove-contacts",
                G_CALLBACK (impl_Book_remove_contacts), ebook);
index 001f797..0b900c5 100644 (file)
@@ -136,7 +136,7 @@ void                e_data_book_respond_remove                      (EDataBook *book, guint32 opid, GError *error
 void           e_data_book_respond_refresh                     (EDataBook *book, guint32 opid, GError *error);
 void           e_data_book_respond_get_backend_property        (EDataBook *book, guint32 opid, GError *error, const gchar *prop_value);
 void           e_data_book_respond_set_backend_property        (EDataBook *book, guint32 opid, GError *error);
-void           e_data_book_respond_create                      (EDataBook *book, guint32 opid, GError *error, const EContact *contact);
+void           e_data_book_respond_create_contacts             (EDataBook *book, guint32 opid, GError *error, const GSList *contacts);
 void           e_data_book_respond_remove_contacts             (EDataBook *book, guint32 opid, GError *error, const GSList *ids);
 void           e_data_book_respond_modify                      (EDataBook *book, guint32 opid, GError *error, const EContact *contact);
 void           e_data_book_respond_get_contact                 (EDataBook *book, guint32 opid, GError *error, const gchar *vcard);
index 04bf496..7b0f5fa 100644 (file)
@@ -53,8 +53,8 @@ enum
        __GET_CONTACT_LIST_DONE_SIGNAL,
        __GET_CONTACT_LIST_UIDS_METHOD,
        __GET_CONTACT_LIST_UIDS_DONE_SIGNAL,
-       __ADD_CONTACT_METHOD,
-       __ADD_CONTACT_DONE_SIGNAL,
+       __ADD_CONTACTS_METHOD,
+       __ADD_CONTACTS_DONE_SIGNAL,
        __REMOVE_CONTACTS_METHOD,
        __REMOVE_CONTACTS_DONE_SIGNAL,
        __MODIFY_CONTACT_METHOD,
@@ -139,8 +139,8 @@ E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_STRV (GDBUS_BOOK_INTERFACE_NAME,
                                                       get_contact_list)
 E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_STRV (GDBUS_BOOK_INTERFACE_NAME,
                                                       get_contact_list_uids)
-E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_STRING (GDBUS_BOOK_INTERFACE_NAME,
-                                                        add_contact)
+E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_STRV (GDBUS_BOOK_INTERFACE_NAME,
+                                                        add_contacts)
 E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_VOID (GDBUS_BOOK_INTERFACE_NAME,
                                                       remove_contacts)
 E_DECLARE_GDBUS_METHOD_DONE_EMISSION_HOOK_ASYNC_VOID (GDBUS_BOOK_INTERFACE_NAME,
@@ -176,7 +176,7 @@ e_gdbus_book_default_init (EGdbusBookIface *iface)
        E_INIT_GDBUS_METHOD_ASYNC_STRING__STRING(EGdbusBookIface, "get_contact",                get_contact, __GET_CONTACT_METHOD, __GET_CONTACT_DONE_SIGNAL)
        E_INIT_GDBUS_METHOD_ASYNC_STRING__STRV  (EGdbusBookIface, "get_contact_list",           get_contact_list, __GET_CONTACT_LIST_METHOD, __GET_CONTACT_LIST_DONE_SIGNAL)
        E_INIT_GDBUS_METHOD_ASYNC_STRING__STRV  (EGdbusBookIface, "get_contact_list_uids",      get_contact_list_uids, __GET_CONTACT_LIST_UIDS_METHOD, __GET_CONTACT_LIST_UIDS_DONE_SIGNAL)
-       E_INIT_GDBUS_METHOD_ASYNC_STRING__STRING(EGdbusBookIface, "add_contact",                add_contact, __ADD_CONTACT_METHOD, __ADD_CONTACT_DONE_SIGNAL)
+       E_INIT_GDBUS_METHOD_ASYNC_STRV__STRV    (EGdbusBookIface, "add_contacts",               add_contacts, __ADD_CONTACTS_METHOD, __ADD_CONTACTS_DONE_SIGNAL)
        E_INIT_GDBUS_METHOD_ASYNC_STRV__VOID    (EGdbusBookIface, "remove_contacts",            remove_contacts, __REMOVE_CONTACTS_METHOD, __REMOVE_CONTACTS_DONE_SIGNAL)
        E_INIT_GDBUS_METHOD_ASYNC_STRING__VOID  (EGdbusBookIface, "modify_contact",             modify_contact, __MODIFY_CONTACT_METHOD, __MODIFY_CONTACT_DONE_SIGNAL)
        E_INIT_GDBUS_METHOD_ASYNC_STRING__STRING(EGdbusBookIface, "get_backend_property",       get_backend_property, __GET_BACKEND_PROPERTY_METHOD, __GET_BACKEND_PROPERTY_DONE_SIGNAL)
@@ -365,34 +365,34 @@ e_gdbus_book_call_get_contact_list_uids_sync (GDBusProxy *proxy,
 }
 
 void
-e_gdbus_book_call_add_contact (GDBusProxy *proxy,
-                               const gchar *in_vcard,
+e_gdbus_book_call_add_contacts (GDBusProxy *proxy,
+                               const gchar * const *in_vcards,
                                GCancellable *cancellable,
                                GAsyncReadyCallback callback,
                                gpointer user_data)
 {
-       e_gdbus_proxy_call_string ("add_contact", e_gdbus_book_call_add_contact, E_GDBUS_ASYNC_OP_KEEPER (proxy), in_vcard, cancellable, callback, user_data);
+       e_gdbus_proxy_call_strv ("add_contacts", e_gdbus_book_call_add_contacts, E_GDBUS_ASYNC_OP_KEEPER (proxy), in_vcards, cancellable, callback, user_data);
 }
 
 gboolean
-e_gdbus_book_call_add_contact_finish (GDBusProxy *proxy,
+e_gdbus_book_call_add_contacts_finish (GDBusProxy *proxy,
                                       GAsyncResult *result,
-                                      gchar **out_uid,
+                                      gchar ***out_uids,
                                       GError **error)
 {
-       return e_gdbus_proxy_finish_call_string (E_GDBUS_ASYNC_OP_KEEPER (proxy), result, out_uid, error, e_gdbus_book_call_add_contact);
+       return e_gdbus_proxy_finish_call_strv (E_GDBUS_ASYNC_OP_KEEPER (proxy), result, out_uids, error, e_gdbus_book_call_add_contacts);
 }
 
 gboolean
-e_gdbus_book_call_add_contact_sync (GDBusProxy *proxy,
-                                    const gchar *in_vcard,
-                                    gchar **out_uid,
+e_gdbus_book_call_add_contacts_sync (GDBusProxy *proxy,
+                                    const gchar * const *in_vcards,
+                                    gchar ***out_uids,
                                     GCancellable *cancellable,
                                     GError **error)
 {
-       return e_gdbus_proxy_call_sync_string__string (proxy, in_vcard, out_uid, cancellable, error,
-               e_gdbus_book_call_add_contact,
-               e_gdbus_book_call_add_contact_finish);
+       return e_gdbus_proxy_call_sync_strv__strv (proxy, in_vcards, out_uids, cancellable, error,
+               e_gdbus_book_call_add_contacts,
+               e_gdbus_book_call_add_contacts_finish);
 }
 
 void
@@ -694,9 +694,9 @@ DECLARE_EMIT_DONE_SIGNAL_1 (get_contact_list,
 DECLARE_EMIT_DONE_SIGNAL_1 (get_contact_list_uids,
                             __GET_CONTACT_LIST_UIDS_DONE_SIGNAL,
                             const gchar * const *)
-DECLARE_EMIT_DONE_SIGNAL_1 (add_contact,
-                            __ADD_CONTACT_DONE_SIGNAL,
-                            const gchar *)
+DECLARE_EMIT_DONE_SIGNAL_1 (add_contacts,
+                            __ADD_CONTACTS_DONE_SIGNAL,
+                            const gchar * const *)
 DECLARE_EMIT_DONE_SIGNAL_0 (remove_contacts,
                             __REMOVE_CONTACTS_DONE_SIGNAL)
 DECLARE_EMIT_DONE_SIGNAL_0 (modify_contact,
@@ -770,7 +770,7 @@ E_DECLARE_GDBUS_ASYNC_METHOD_0 (book,
 E_DECLARE_GDBUS_ASYNC_METHOD_1_WITH_RETURN     (book, get_contact, uid, "s", vcard, "s")
 E_DECLARE_GDBUS_ASYNC_METHOD_1_WITH_RETURN     (book, get_contact_list, query, "s", vcards, "as")
 E_DECLARE_GDBUS_ASYNC_METHOD_1_WITH_RETURN     (book, get_contact_list_uids, query, "s", uids, "as")
-E_DECLARE_GDBUS_ASYNC_METHOD_1_WITH_RETURN     (book, add_contact, vcard, "s", uid, "s")
+E_DECLARE_GDBUS_ASYNC_METHOD_1_WITH_RETURN     (book, add_contacts, vcards, "as", uids, "as")
 E_DECLARE_GDBUS_ASYNC_METHOD_1                 (book, remove_contacts, list, "as")
 E_DECLARE_GDBUS_ASYNC_METHOD_1                 (book, modify_contact, vcard, "s")
 E_DECLARE_GDBUS_ASYNC_METHOD_1_WITH_RETURN     (book, get_backend_property, prop_name, "s", prop_value, "s")
@@ -792,7 +792,7 @@ static const GDBusMethodInfo * const e_gdbus_book_method_info_pointers[] =
        &E_DECLARED_GDBUS_METHOD_INFO_NAME (book, get_contact),
        &E_DECLARED_GDBUS_METHOD_INFO_NAME (book, get_contact_list),
        &E_DECLARED_GDBUS_METHOD_INFO_NAME (book, get_contact_list_uids),
-       &E_DECLARED_GDBUS_METHOD_INFO_NAME (book, add_contact),
+       &E_DECLARED_GDBUS_METHOD_INFO_NAME (book, add_contacts),
        &E_DECLARED_GDBUS_METHOD_INFO_NAME (book, remove_contacts),
        &E_DECLARED_GDBUS_METHOD_INFO_NAME (book, modify_contact),
        &E_DECLARED_GDBUS_METHOD_INFO_NAME (book, get_backend_property),
@@ -820,7 +820,7 @@ static const GDBusSignalInfo * const e_gdbus_book_signal_info_pointers[] =
        &E_DECLARED_GDBUS_SIGNAL_INFO_NAME (book, get_contact_done),
        &E_DECLARED_GDBUS_SIGNAL_INFO_NAME (book, get_contact_list_done),
        &E_DECLARED_GDBUS_SIGNAL_INFO_NAME (book, get_contact_list_uids_done),
-       &E_DECLARED_GDBUS_SIGNAL_INFO_NAME (book, add_contact_done),
+       &E_DECLARED_GDBUS_SIGNAL_INFO_NAME (book, add_contacts_done),
        &E_DECLARED_GDBUS_SIGNAL_INFO_NAME (book, remove_contacts_done),
        &E_DECLARED_GDBUS_SIGNAL_INFO_NAME (book, modify_contact_done),
        &E_DECLARED_GDBUS_SIGNAL_INFO_NAME (book, get_backend_property_done),
@@ -1042,7 +1042,7 @@ e_gdbus_book_proxy_init (EGdbusBookProxy *proxy)
        E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_STRING (get_contact);
        E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_STRV   (get_contact_list);
        E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_STRV   (get_contact_list_uids);
-       E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_STRING (add_contact);
+       E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_STRV   (add_contacts);
        E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_VOID   (remove_contacts);
        E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_VOID   (modify_contact);
        E_GDBUS_CONNECT_METHOD_DONE_SIGNAL_STRING (get_backend_property);
index aea23ef..417c1f1 100644 (file)
@@ -138,8 +138,8 @@ struct _EGdbusBookIface
        gboolean (*handle_get_contact_list_uids)(EGdbusBook *object, GDBusMethodInvocation *invocation, const gchar *in_query);
        void     (*get_contact_list_uids_done)  (EGdbusBook *object, guint arg_opid, const GError *arg_error, gchar ***out_uids);
 
-       gboolean (*handle_add_contact)          (EGdbusBook *object, GDBusMethodInvocation *invocation, const gchar *in_vcard);
-       void     (*add_contact_done)            (EGdbusBook *object, guint arg_opid, const GError *arg_error, gchar **out_uid);
+       gboolean (*handle_add_contacts)         (EGdbusBook *object, GDBusMethodInvocation *invocation, const gchar * const *in_vcards);
+       void     (*add_contacts_done)           (EGdbusBook *object, guint arg_opid, const GError *arg_error, gchar ***out_uids);
 
        gboolean (*handle_remove_contacts)      (EGdbusBook *object, GDBusMethodInvocation *invocation, const gchar * const *in_list);
        void     (*remove_contacts_done)        (EGdbusBook *object, guint arg_opid, const GError *arg_error);
@@ -190,9 +190,9 @@ void                e_gdbus_book_call_get_contact_list_uids (GDBusProxy *proxy, const gchar *i
 gboolean       e_gdbus_book_call_get_contact_list_uids_finish (GDBusProxy *proxy, GAsyncResult *result, gchar ***out_uids, GError **error);
 gboolean       e_gdbus_book_call_get_contact_list_uids_sync (GDBusProxy *proxy, const gchar *in_query, gchar ***out_uids, GCancellable *cancellable, GError **error);
 
-void           e_gdbus_book_call_add_contact (GDBusProxy *proxy, const gchar *in_vcard, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
-gboolean       e_gdbus_book_call_add_contact_finish (GDBusProxy *proxy, GAsyncResult *result, gchar **out_uid, GError **error);
-gboolean       e_gdbus_book_call_add_contact_sync (GDBusProxy *proxy, const gchar *in_vcard, gchar **out_uid, GCancellable *cancellable, GError **error);
+void           e_gdbus_book_call_add_contacts (GDBusProxy *proxy, const gchar * const *in_vcards, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
+gboolean       e_gdbus_book_call_add_contacts_finish (GDBusProxy *proxy, GAsyncResult *result, gchar ***out_uids, GError **error);
+gboolean       e_gdbus_book_call_add_contacts_sync (GDBusProxy *proxy, const gchar * const *in_vcards, gchar ***out_uids, GCancellable *cancellable, GError **error);
 
 void           e_gdbus_book_call_remove_contacts (GDBusProxy *proxy, const gchar * const *in_list, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
 gboolean       e_gdbus_book_call_remove_contacts_finish (GDBusProxy *proxy, GAsyncResult *result, GError **error);
@@ -239,7 +239,7 @@ gboolean    e_gdbus_book_call_close_sync (GDBusProxy *proxy, GCancellable *cancella
 #define e_gdbus_book_complete_get_contact                      e_gdbus_complete_async_method
 #define e_gdbus_book_complete_get_contact_list                 e_gdbus_complete_async_method
 #define e_gdbus_book_complete_get_contact_list_uids            e_gdbus_complete_async_method
-#define e_gdbus_book_complete_add_contact                      e_gdbus_complete_async_method
+#define e_gdbus_book_complete_add_contacts                     e_gdbus_complete_async_method
 #define e_gdbus_book_complete_remove_contacts                  e_gdbus_complete_async_method
 #define e_gdbus_book_complete_modify_contact                   e_gdbus_complete_async_method
 #define e_gdbus_book_complete_get_backend_property             e_gdbus_complete_async_method
@@ -256,7 +256,7 @@ void e_gdbus_book_emit_refresh_done                 (EGdbusBook *object, guint arg_opid, const
 void e_gdbus_book_emit_get_contact_done                        (EGdbusBook *object, guint arg_opid, const GError *arg_error, const gchar *out_vcard);
 void e_gdbus_book_emit_get_contact_list_done           (EGdbusBook *object, guint arg_opid, const GError *arg_error, const gchar * const *out_vcards);
 void e_gdbus_book_emit_get_contact_list_uids_done      (EGdbusBook *object, guint arg_opid, const GError *arg_error, const gchar * const *out_uids);
-void e_gdbus_book_emit_add_contact_done                        (EGdbusBook *object, guint arg_opid, const GError *arg_error, const gchar *out_uid);
+void e_gdbus_book_emit_add_contacts_done               (EGdbusBook *object, guint arg_opid, const GError *arg_error, const gchar *const *out_uids);
 void e_gdbus_book_emit_remove_contacts_done            (EGdbusBook *object, guint arg_opid, const GError *arg_error);
 void e_gdbus_book_emit_modify_contact_done             (EGdbusBook *object, guint arg_opid, const GError *arg_error);
 void e_gdbus_book_emit_get_backend_property_done       (EGdbusBook *object, guint arg_opid, const GError *arg_error, const gchar *out_prop_value);