blob: e2ccef7c49e15c0726af7ffca15698431f307009 [file] [log] [blame]
Stepan Salenikovich6f687072015-03-26 10:43:37 -04001/*
2 * Copyright (C) 2015 Savoir-Faire Linux Inc.
3 * Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Additional permission under GNU GPL version 3 section 7:
20 *
21 * If you modify this program, or any covered work, by linking or
22 * combining it with the OpenSSL project's OpenSSL library (or a
23 * modified version of that library), containing parts covered by the
24 * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
25 * grants you additional permission to convey the resulting work.
26 * Corresponding Source for a non-source form of such a combination
27 * shall include the source code for the parts of OpenSSL used as well
28 * as that of the covered work.
29 */
30
31#include "edscontactbackend.h"
32
Stepan Salenikovicha1b8cb32015-09-11 14:58:35 -040033#include <glib/gi18n.h>
Stepan Salenikovich6f687072015-03-26 10:43:37 -040034#include <person.h>
35#include <personmodel.h>
36#include <contactmethod.h>
37#include <collectioneditor.h>
38#include <memory>
39
40static void
41client_cb(G_GNUC_UNUSED ESource *source, GAsyncResult *result, G_GNUC_UNUSED gpointer user_data)
42{
43 GError *error = NULL;
44 EClient *client = e_book_client_connect_finish(result, &error);
45 if (!client) {
46 g_warning("%s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -040047 g_clear_error(&error);
Stepan Salenikovich6f687072015-03-26 10:43:37 -040048 } else {
49 /* got a client for this addressbook, add as backend */
Guillaume Roguez5d1514b2015-10-22 15:55:31 -040050 PersonModel::instance().addCollection<EdsContactBackend, EClient *>(
Stepan Salenikovich6f687072015-03-26 10:43:37 -040051 client, LoadOptions::FORCE_ENABLED);
52 }
53}
54
55static void
56registry_cb(G_GNUC_UNUSED GObject *source, GAsyncResult *result, GCancellable *cancellable)
57{
58 GError *error = NULL;
59 ESourceRegistry *registry = e_source_registry_new_finish(result, &error);
60 if(!registry) {
61 g_critical("Unable to create EDS registry: %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -040062 g_clear_error(&error);
Stepan Salenikovich6f687072015-03-26 10:43:37 -040063 return;
64 } else {
65 GList *list = e_source_registry_list_enabled(registry, E_SOURCE_EXTENSION_ADDRESS_BOOK);
66
67 for (GList *l = list ; l; l = l->next) {
68 ESource *source = E_SOURCE(l->data);
69 /* try to connect to each source ansynch */
Stepan Salenikovichb53e9c02015-04-14 17:41:10 -040070#if EDS_CHECK_VERSION(3,16,0)
Edric Milaret66c50a32015-05-06 10:38:42 -040071 e_book_client_connect(source,
72 EdsContactBackend::WAIT_FOR_CONNECTED_SECONDS,
73 cancellable,
74 (GAsyncReadyCallback)client_cb,
75 NULL);
76#else
77 e_book_client_connect(source, cancellable, (GAsyncReadyCallback)client_cb, NULL);
Stepan Salenikovichb53e9c02015-04-14 17:41:10 -040078#endif
Stepan Salenikovich6f687072015-03-26 10:43:37 -040079 }
80
81 g_list_free_full(list, g_object_unref);
82 }
83}
84
85void load_eds_sources(GCancellable *cancellable)
86{
87 /* load the registery asynchronously, and then each source asynch as well
88 * pass the cancellable as a param so that the loading of each source can
89 * also be cancelled
90 */
91 e_source_registry_new(cancellable, (GAsyncReadyCallback)registry_cb, cancellable);
92}
93
94class EdsContactEditor : public CollectionEditor<Person>
95{
96public:
97 EdsContactEditor(CollectionMediator<Person>* m, EdsContactBackend* parent);
98 ~EdsContactEditor();
99 virtual bool save ( const Person* item ) override;
100 virtual bool remove ( const Person* item ) override;
101 virtual bool edit ( Person* item ) override;
Stepan Salenikovich58dd9e12015-08-05 10:39:36 -0400102 virtual bool addNew ( Person* item ) override;
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400103 virtual bool addExisting( const Person* item ) override;
104
105private:
106 virtual QVector<Person*> items() const override;
107
108 QVector<Person*> items_;
109 EdsContactBackend* collection_;
110};
111
112EdsContactEditor::EdsContactEditor(CollectionMediator<Person>* m, EdsContactBackend* parent) :
113CollectionEditor<Person>(m),collection_(parent)
114{
115}
116
117EdsContactEditor::~EdsContactEditor()
118{
119}
120
121bool EdsContactEditor::save(const Person* item)
122{
Stepan Salenikovich0cf247d2015-07-24 17:36:32 -0400123 return collection_->savePerson(item);
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400124}
125
126bool EdsContactEditor::remove(const Person* item)
127{
Stepan Salenikovichbb358592015-07-10 14:45:50 -0400128 mediator()->removeItem(item);
129 return true;
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400130}
131
132bool EdsContactEditor::edit( Person* item)
133{
134 Q_UNUSED(item)
135 return false;
136}
137
Stepan Salenikovich58dd9e12015-08-05 10:39:36 -0400138bool EdsContactEditor::addNew(Person* item)
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400139{
Stepan Salenikovich58dd9e12015-08-05 10:39:36 -0400140 bool ret = collection_->addNewPerson(item);
Stepan Salenikovichf2d76c52015-07-17 17:54:56 -0400141 if (ret) {
Stepan Salenikovich58dd9e12015-08-05 10:39:36 -0400142 items_ << item;
Stepan Salenikovichf2d76c52015-07-17 17:54:56 -0400143 mediator()->addItem(item);
144 }
145 return ret;
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400146}
147
148bool EdsContactEditor::addExisting(const Person* item)
149{
150 items_ << const_cast<Person*>(item);
151 mediator()->addItem(item);
152 return true;
153}
154
155QVector<Person*> EdsContactEditor::items() const
156{
157 return items_;
158}
159
Stepan Salenikovich8e882cd2015-05-28 14:18:06 -0400160static void free_client_view(EBookClientView *client_view) {
161 g_return_if_fail(client_view);
162 GError *error = NULL;
163 e_book_client_view_stop(client_view, &error);
164 if (error) {
165 g_warning("error stopping EBookClientView: %s", error->message);
166 g_clear_error(&error);
167 }
168 g_object_unref(client_view);
169}
170
171static void
Stepan Salenikovich21366ed2015-06-26 17:11:45 -0400172free_object_list(GSList *list)
Stepan Salenikovich8e882cd2015-05-28 14:18:06 -0400173{
174 g_slist_free_full(list, g_object_unref);
175};
176
Stepan Salenikovich21366ed2015-06-26 17:11:45 -0400177static void
178free_string_list(GSList *list)
179{
180 g_slist_free_full(list, g_free);
181};
182
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400183EdsContactBackend::EdsContactBackend(CollectionMediator<Person>* mediator, EClient *client, CollectionInterface* parent)
184 : CollectionInterface(new EdsContactEditor(mediator,this), parent)
185 , mediator_(mediator)
186 , client_(client, g_object_unref)
187 , cancellable_(g_cancellable_new(), g_object_unref)
Stepan Salenikovich8e882cd2015-05-28 14:18:06 -0400188 , client_view_(nullptr, &free_client_view)
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400189{
Stepan Salenikovichf4149272015-07-16 16:12:19 -0400190 // cache the name
191 if (client_) {
192 auto source = e_client_get_source(client_.get());
193 auto extension = (ESourceAddressBook *)e_source_get_extension(source, E_SOURCE_EXTENSION_ADDRESS_BOOK);
194 auto backend = e_source_backend_get_backend_name(E_SOURCE_BACKEND(extension));
195 auto addressbook = e_source_get_display_name(source);
196
197 gchar *name = g_strdup_printf("%s (%s)", addressbook, backend);
Stepan Salenikovicha1b8cb32015-09-11 14:58:35 -0400198 name_ = name;
Stepan Salenikovichf4149272015-07-16 16:12:19 -0400199 g_free(name);
200 } else
Stepan Salenikovicha1b8cb32015-09-11 14:58:35 -0400201 name_ = _("Unknown EDS addressbook");
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400202}
203
204EdsContactBackend::~EdsContactBackend()
205{
206 /* cancel any cancellable operations */
207 g_cancellable_cancel(cancellable_.get());
Stepan Salenikovichdc7178b2015-04-13 17:38:46 -0400208
209 /* cancel contact loading timeout source, if its not finished */
210 if (add_contacts_source_id != 0)
211 g_source_remove(add_contacts_source_id);
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400212}
213
214QString EdsContactBackend::name() const
215{
Stepan Salenikovichf4149272015-07-16 16:12:19 -0400216 return name_;
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400217}
218
219QString EdsContactBackend::category() const
220{
Stepan Salenikovicha1b8cb32015-09-11 14:58:35 -0400221 return C_("Backend type", "Contacts");
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400222}
223
224bool EdsContactBackend::isEnabled() const
225{
226 return true;
227}
228
229static void
Stepan Salenikovich8e882cd2015-05-28 14:18:06 -0400230contacts_added(G_GNUC_UNUSED EBookClientView *client_view, const GSList *objects, EdsContactBackend *self)
231{
232 std::unique_ptr<GSList,void(*)(GSList *)> contacts(
Stepan Salenikovich21366ed2015-06-26 17:11:45 -0400233 g_slist_copy_deep((GSList *)objects, (GCopyFunc)g_object_ref, NULL ), &free_object_list);
Stepan Salenikovich8e882cd2015-05-28 14:18:06 -0400234 self->addContacts(std::move(contacts));
235}
236
237static void
Stepan Salenikovicheccd3102015-06-26 15:49:34 -0400238contacts_modified(EBookClientView *client_view, const GSList *objects, EdsContactBackend *self)
239{
240 /* The parseContact function will check if the contacts we're "adding" have
241 * the same URI as any existing ones and if so will update those instead */
242 contacts_added(client_view, objects, self);
243}
244
245static void
Stepan Salenikovich21366ed2015-06-26 17:11:45 -0400246contacts_removed(G_GNUC_UNUSED EBookClientView *client_view, const GSList *uids, EdsContactBackend *self)
247{
248 std::unique_ptr<GSList,void(*)(GSList *)> contact_uids(
249 g_slist_copy_deep((GSList *)uids, (GCopyFunc)g_strdup, NULL ), &free_string_list);
250 self->removeContacts(std::move(contact_uids));
251}
252
253static void
Stepan Salenikovich8e882cd2015-05-28 14:18:06 -0400254client_view_cb(EBookClient *client, GAsyncResult *result, EdsContactBackend *self)
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400255{
256 g_return_if_fail(E_IS_BOOK_CLIENT(client));
Stepan Salenikovich8e882cd2015-05-28 14:18:06 -0400257 EBookClientView *client_view = NULL;
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400258 GError *error = NULL;
Stepan Salenikovich8e882cd2015-05-28 14:18:06 -0400259 if(!e_book_client_get_view_finish(client, result, &client_view, &error)) {
260 g_critical("Unable to get client view: %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -0400261 g_clear_error(&error);
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400262 return;
263 } else {
Stepan Salenikovich8e882cd2015-05-28 14:18:06 -0400264 /* we want the EBookClientView to have the same life cycle as the backend */
265 std::unique_ptr<EBookClientView, void(*)(EBookClientView *)> client_view_ptr(
266 client_view, &free_client_view);
267 self->addClientView(std::move(client_view_ptr));
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400268 }
269}
270
Stepan Salenikovichdc7178b2015-04-13 17:38:46 -0400271void EdsContactBackend::parseContact(EContact *contact)
272{
273 /* Check if the photo is in-line or a URI, in the case that it is a URI,
274 * try to make it inline so that the lrc vcard parser is able to get the
275 * photo. Note that this will only work on local URIs
276 */
277 EContactPhoto *photo = (EContactPhoto *)e_contact_get(contact, E_CONTACT_PHOTO);
278 if (photo) {
279 if (photo->type == E_CONTACT_PHOTO_TYPE_URI) {
280 GError *error = NULL;
281 if (!e_contact_inline_local_photos(contact, &error)) {
282 g_warning("could not inline photo from vcard URI: %s", error->message);
Stepan Salenikovich8a287fc2015-05-01 16:53:20 -0400283 g_clear_error(&error);
Stepan Salenikovichdc7178b2015-04-13 17:38:46 -0400284 }
285 }
286 }
287 e_contact_photo_free(photo);
288
289 EVCard *vcard = E_VCARD(contact);
290 gchar *vcard_str = e_vcard_to_string(vcard, EVC_FORMAT_VCARD_30);
Stepan Salenikovicheccd3102015-06-26 15:49:34 -0400291
292 /* check if this person already exists */
Stepan Salenikoviche86ffba2015-07-13 11:32:40 -0400293 Person *existing = nullptr;
294
295 gchar *uid = (gchar *)e_contact_get(contact, E_CONTACT_UID);
296 if (uid) {
Stepan Salenikovichf2d76c52015-07-17 17:54:56 -0400297 // g_warning("got uid: %s", uid);
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400298 existing = PersonModel::instance().getPersonByUid(uid);
Stepan Salenikoviche86ffba2015-07-13 11:32:40 -0400299 g_free(uid);
300 }
301
Stepan Salenikovicheccd3102015-06-26 15:49:34 -0400302 if (existing) {
303 /* update existing person */
Stepan Salenikoviche86ffba2015-07-13 11:32:40 -0400304 existing->updateFromVCard(vcard_str);
Stepan Salenikovicheccd3102015-06-26 15:49:34 -0400305 } else {
Stepan Salenikoviche86ffba2015-07-13 11:32:40 -0400306 Person *p = new Person(vcard_str, Person::Encoding::vCard, this);
Stepan Salenikovicheccd3102015-06-26 15:49:34 -0400307 editor<Person>()->addExisting(p);
308 }
Stepan Salenikoviche86ffba2015-07-13 11:32:40 -0400309
310 g_free(vcard_str);
Stepan Salenikovichdc7178b2015-04-13 17:38:46 -0400311}
312
313typedef struct AddContactsData_
314{
315 EdsContactBackend* backend;
316 GSList *next;
Stepan Salenikovich8e882cd2015-05-28 14:18:06 -0400317 std::unique_ptr<GSList, void(*)(GSList *)> contacts;
Stepan Salenikovichdc7178b2015-04-13 17:38:46 -0400318} AddContactsData;
319
Stepan Salenikovich8e882cd2015-05-28 14:18:06 -0400320void
321free_add_contacts_data(AddContactsData *data)
322{
323 g_return_if_fail(data && data->contacts);
324 data->contacts.reset();
325 g_free(data);
326}
327
Stepan Salenikovichdc7178b2015-04-13 17:38:46 -0400328static gboolean
329add_contacts(AddContactsData *data)
330{
331 for (int i = 0; i < data->backend->CONTACT_ADD_LIMIT && data->next; i++) {
332 data->backend->parseContact(E_CONTACT(data->next->data));
333 data->next = data->next->next;
334 }
335
336 if (!data->next) {
337 data->backend->lastContactAdded();
338 return G_SOURCE_REMOVE;
339 }
340
341 return G_SOURCE_CONTINUE;
342}
343
344void EdsContactBackend::lastContactAdded()
345{
346 /* Sets the source id to 0 to make sure we don't try to remove this source
347 * after it has already finished */
348 add_contacts_source_id = 0;
349}
350
Stepan Salenikovich8e882cd2015-05-28 14:18:06 -0400351void EdsContactBackend::addClientView(std::unique_ptr<EBookClientView, void(*)(EBookClientView *)> client_view)
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400352{
Stepan Salenikovich8e882cd2015-05-28 14:18:06 -0400353 client_view_ = std::move(client_view);
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400354
Stepan Salenikovich8e882cd2015-05-28 14:18:06 -0400355 /* connect signals for adding, removing, and modifying contacts */
356 g_signal_connect(client_view_.get(), "objects-added", G_CALLBACK(contacts_added), this);
Stepan Salenikovicheccd3102015-06-26 15:49:34 -0400357 g_signal_connect(client_view_.get(), "objects-modified", G_CALLBACK(contacts_modified), this);
Stepan Salenikovich21366ed2015-06-26 17:11:45 -0400358 g_signal_connect(client_view_.get(), "objects-removed", G_CALLBACK(contacts_removed), this);
Stepan Salenikovich8e882cd2015-05-28 14:18:06 -0400359
360 /* start processing the signals */
361 GError *error = NULL;
362 e_book_client_view_start(client_view_.get(), &error);
363 if (error) {
364 g_critical("Unable to get start client view: %s", error->message);
365 g_clear_error(&error);
366 }
367}
368
369void EdsContactBackend::addContacts(std::unique_ptr<GSList, void(*)(GSList *)> contacts)
370{
Stepan Salenikovichdc7178b2015-04-13 17:38:46 -0400371 /* add CONTACT_ADD_LIMIT # of contacts every CONTACT_ADD_INTERVAL miliseconds */
372 AddContactsData *data = g_new0(AddContactsData, 1);
373 data->backend = this;
Stepan Salenikovich8e882cd2015-05-28 14:18:06 -0400374 data->contacts = std::move(contacts);
375 data->next = data->contacts.get();
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400376
Stepan Salenikovichdc7178b2015-04-13 17:38:46 -0400377 g_timeout_add_full(G_PRIORITY_DEFAULT,
378 CONTACT_ADD_INTERVAL,
379 (GSourceFunc)add_contacts,
380 data,
Stepan Salenikovich8e882cd2015-05-28 14:18:06 -0400381 (GDestroyNotify)free_add_contacts_data);
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400382}
383
Stepan Salenikovich21366ed2015-06-26 17:11:45 -0400384void EdsContactBackend::removeContacts(std::unique_ptr<GSList, void(*)(GSList *)> contact_uids)
385{
386 GSList *next = contact_uids.get();
387 while(next) {
388 gchar *uid = (gchar *)next->data;
389 if (uid) {
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400390 Person *p = PersonModel::instance().getPersonByUid(uid);
Stepan Salenikovich21366ed2015-06-26 17:11:45 -0400391 if (p) {
392 g_debug("removing: %s", p->formattedName().toUtf8().constData());
393 deactivate(p);
Stepan Salenikovichbb358592015-07-10 14:45:50 -0400394 editor<Person>()->remove(p);
Stepan Salenikovich21366ed2015-06-26 17:11:45 -0400395 } else {
396 g_warning("person with given UID doesn't exist: %s", uid);
397 }
398 } else {
399 g_warning("null UID in list");
400 }
401 next = g_slist_next(next);
402 }
403}
404
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400405bool EdsContactBackend::load()
406{
407 /**
408 * load the contacts by querying for them,
409 * we want the contact to have some kind of name
410 */
411 EBookQuery *queries[4];
412 int idx = 0;
413 queries[idx++] = e_book_query_field_exists(E_CONTACT_NAME_OR_ORG);
414 queries[idx++] = e_book_query_field_exists(E_CONTACT_GIVEN_NAME);
415 queries[idx++] = e_book_query_field_exists(E_CONTACT_FAMILY_NAME);
416 queries[idx++] = e_book_query_field_exists(E_CONTACT_NICKNAME);
417
418 EBookQuery *name_query = e_book_query_or(idx, queries, TRUE);
419 gchar *query_str = e_book_query_to_string(name_query);
420 e_book_query_unref(name_query);
Stepan Salenikovich8e882cd2015-05-28 14:18:06 -0400421
422 /* test */
423 e_book_client_get_view(E_BOOK_CLIENT(client_.get()),
424 query_str,
425 cancellable_.get(),
426 (GAsyncReadyCallback)client_view_cb,
427 this);
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400428 g_free(query_str);
429
430 return true;
431}
432
433bool EdsContactBackend::reload()
434{
435 return false;
436}
437
Stepan Salenikovich4e409932015-04-24 12:12:39 -0400438FlagPack<CollectionInterface::SupportedFeatures> EdsContactBackend::supportedFeatures() const
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400439{
Stepan Salenikovich4e409932015-04-24 12:12:39 -0400440 return (CollectionInterface::SupportedFeatures::NONE |
Stepan Salenikovichf2d76c52015-07-17 17:54:56 -0400441 CollectionInterface::SupportedFeatures::LOAD |
Stepan Salenikovich0cf247d2015-07-24 17:36:32 -0400442 CollectionInterface::SupportedFeatures::ADD |
443 CollectionInterface::SupportedFeatures::SAVE );
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400444}
445
446bool EdsContactBackend::clear()
447{
448 return false;
449}
450
451QByteArray EdsContactBackend::id() const
452{
Stepan Salenikovichf4149272015-07-16 16:12:19 -0400453 if (client_)
454 return e_source_get_uid(e_client_get_source(client_.get()));
455 return "edscb";
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400456}
Stepan Salenikovichf2d76c52015-07-17 17:54:56 -0400457
458bool EdsContactBackend::addNewPerson(Person *item)
459{
Stepan Salenikovich0cf247d2015-07-24 17:36:32 -0400460 g_return_val_if_fail(client_.get(), false);
Stepan Salenikovichf2d76c52015-07-17 17:54:56 -0400461
462 auto contact = e_contact_new_from_vcard(item->toVCard().constData());
463 gchar *uid = NULL;
464 GError *error = NULL;
465
Stepan Salenikovich0cf247d2015-07-24 17:36:32 -0400466 /* FIXME: this methods returns True for a google addressbook, but it never
467 * actually adds the new contact... not clear if this is possible */
Stepan Salenikovichf2d76c52015-07-17 17:54:56 -0400468 bool ret = e_book_client_add_contact_sync(
469 E_BOOK_CLIENT(client_.get()),
470 contact,
471 &uid,
472 cancellable_.get(),
473 &error
474 );
475
476 if (!ret) {
477 if (error) {
478 g_warning("could not add contact to collection: %s", error->message);
479 g_clear_error(&error);
480 } else {
481 g_warning("could not add contact to collection");
482 }
483 } else {
484 item->setUid(uid);
485 }
486
487 g_free(uid);
488 g_object_unref(contact);
489
490 return ret;
491}
Stepan Salenikovich0cf247d2015-07-24 17:36:32 -0400492
493bool EdsContactBackend::savePerson(const Person *item)
494{
495 g_return_val_if_fail(client_.get(), false);
496
497 g_debug("saving person");
498
499 auto contact = e_contact_new_from_vcard(item->toVCard().constData());
500 GError *error = NULL;
501
502 /* FIXME: this methods fails for a google addressbook, not clear if it is
503 * possible to edit a google address book... gnome contacts simply creates
504 * a local contact with the same uid */
505 bool ret = e_book_client_modify_contact_sync(
506 E_BOOK_CLIENT(client_.get()),
507 contact,
508 cancellable_.get(),
509 &error
510 );
511
512 if (!ret) {
513 if (error) {
514 g_warning("could not modify contact: %s", error->message);
515 g_clear_error(&error);
516 } else {
517 g_warning("could not modify contact");
518 }
519 }
520
521 g_object_unref(contact);
522
523 return ret;
524}