"Fossies" - the Fresh Open Source Software Archive 
Member "evolution-mapi-3.46.1/src/addressbook/e-book-backend-mapi.c" (2 Dec 2022, 34999 Bytes) of package /linux/misc/evolution-mapi-3.46.1.tar.xz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "e-book-backend-mapi.c" see the
Fossies "Dox" file reference documentation.
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
4 * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) version 3.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with the program; if not, see <http://www.gnu.org/licenses/>
18 *
19 *
20 * Authors:
21 * Srinivasa Ragavan <sragavan@novell.com>
22 */
23
24 #include "evolution-mapi-config.h"
25
26 #include <stdlib.h>
27 #include <string.h>
28 #include <glib.h>
29 #include <glib/gstdio.h>
30 #include <glib/gi18n-lib.h>
31
32 #include <libebook/libebook.h>
33 #include <libedataserver/libedataserver.h>
34 #include <camel/camel.h>
35
36 #include "e-mapi-utils.h"
37 #include "e-mapi-defs.h"
38 #include "e-source-mapi-folder.h"
39
40 #include "e-book-backend-mapi.h"
41
42 /* default value for "partial-count", upper bound of objects to download during partial search */
43 #define DEFAULT_PARTIAL_COUNT 50
44
45 struct _EBookBackendMAPIPrivate
46 {
47 GRecMutex conn_lock;
48 EMapiConnection *conn;
49
50 gboolean is_gal;
51 };
52
53 G_DEFINE_TYPE_WITH_PRIVATE (EBookBackendMAPI, e_book_backend_mapi, E_TYPE_BOOK_META_BACKEND)
54
55 static void
56 ebb_mapi_error_to_client_error (GError **perror,
57 const GError *mapi_error,
58 EClientError code,
59 const gchar *context)
60 {
61 gchar *err_msg = NULL;
62
63 if (!perror)
64 return;
65
66 if (g_error_matches (mapi_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
67 g_propagate_error (perror, g_error_copy (mapi_error));
68 return;
69 }
70
71 if (code == E_CLIENT_ERROR_OTHER_ERROR && mapi_error && mapi_error->domain == E_MAPI_ERROR) {
72 /* Change error to more accurate only with OTHER_ERROR */
73 switch (mapi_error->code) {
74 case MAPI_E_PASSWORD_CHANGE_REQUIRED:
75 case MAPI_E_PASSWORD_EXPIRED:
76 code = E_CLIENT_ERROR_AUTHENTICATION_REQUIRED;
77 break;
78 case ecRpcFailed:
79 code = E_CLIENT_ERROR_REPOSITORY_OFFLINE;
80 break;
81 default:
82 break;
83 }
84 }
85
86 if (context)
87 err_msg = g_strconcat (context, mapi_error ? ": " : NULL, mapi_error ? mapi_error->message : NULL, NULL);
88
89 g_propagate_error (perror, e_client_error_create (code, err_msg ? err_msg : mapi_error ? mapi_error->message : _("Unknown error")));
90
91 g_free (err_msg);
92 }
93
94 static void
95 ebb_mapi_lock_connection (EBookBackendMAPI *bbmapi)
96 {
97 g_return_if_fail (E_IS_BOOK_BACKEND_MAPI (bbmapi));
98
99 g_rec_mutex_lock (&bbmapi->priv->conn_lock);
100 }
101
102 static void
103 ebb_mapi_unlock_connection (EBookBackendMAPI *bbmapi)
104 {
105 g_return_if_fail (E_IS_BOOK_BACKEND_MAPI (bbmapi));
106
107 g_rec_mutex_unlock (&bbmapi->priv->conn_lock);
108 }
109
110 static CamelMapiSettings *
111 ebb_mapi_get_collection_settings (EBookBackendMAPI *ebbm)
112 {
113 ESource *source;
114 ESource *collection;
115 ESourceCamel *extension;
116 ESourceRegistry *registry;
117 CamelSettings *settings;
118 const gchar *extension_name;
119
120 source = e_backend_get_source (E_BACKEND (ebbm));
121 registry = e_book_backend_get_registry (E_BOOK_BACKEND (ebbm));
122
123 extension_name = e_source_camel_get_extension_name ("mapi");
124 e_source_camel_generate_subtype ("mapi", CAMEL_TYPE_MAPI_SETTINGS);
125
126 /* The collection settings live in our parent data source. */
127 collection = e_source_registry_find_extension (
128 registry, source, extension_name);
129 g_return_val_if_fail (collection != NULL, NULL);
130
131 extension = e_source_get_extension (collection, extension_name);
132 settings = e_source_camel_get_settings (extension);
133
134 g_object_unref (collection);
135
136 return CAMEL_MAPI_SETTINGS (settings);
137 }
138
139 static gboolean
140 ebb_mapi_contacts_open_folder (EBookBackendMAPI *bbmapi,
141 mapi_object_t *out_obj_folder,
142 GCancellable *cancellable,
143 GError **error)
144 {
145 ESource *source;
146 ESourceMapiFolder *ext_mapi_folder;
147 mapi_id_t fid;
148 gchar *foreign_username;
149 gboolean success;
150
151 g_return_val_if_fail (E_IS_BOOK_BACKEND_MAPI (bbmapi), FALSE);
152 g_return_val_if_fail (bbmapi->priv->conn != NULL, FALSE);
153 g_return_val_if_fail (out_obj_folder != NULL, FALSE);
154
155 source = e_backend_get_source (E_BACKEND (bbmapi));
156 ext_mapi_folder = e_source_get_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER);
157
158 fid = e_source_mapi_folder_get_id (ext_mapi_folder);
159 foreign_username = e_source_mapi_folder_dup_foreign_username (ext_mapi_folder);
160
161 if (foreign_username && *foreign_username)
162 success = e_mapi_connection_open_foreign_folder (bbmapi->priv->conn, foreign_username, fid, out_obj_folder, cancellable, error);
163 else if (e_source_mapi_folder_is_public (ext_mapi_folder))
164 success = e_mapi_connection_open_public_folder (bbmapi->priv->conn, fid, out_obj_folder, cancellable, error);
165 else
166 success = e_mapi_connection_open_personal_folder (bbmapi->priv->conn, fid, out_obj_folder, cancellable, error);
167
168 g_free (foreign_username);
169
170 return success;
171 }
172
173 static void
174 ebb_mapi_maybe_disconnect (EBookBackendMAPI *bbmapi,
175 const GError *mapi_error)
176 {
177 g_return_if_fail (E_IS_BOOK_BACKEND_MAPI (bbmapi));
178
179 /* no error or already disconnected */
180 if (!mapi_error || !bbmapi->priv->conn)
181 return;
182
183 if (g_error_matches (mapi_error, E_MAPI_ERROR, ecRpcFailed) ||
184 g_error_matches (mapi_error, E_MAPI_ERROR, MAPI_E_CALL_FAILED)) {
185 e_mapi_connection_disconnect (bbmapi->priv->conn,
186 !g_error_matches (mapi_error, E_MAPI_ERROR, ecRpcFailed),
187 NULL, NULL);
188 g_clear_object (&bbmapi->priv->conn);
189 }
190 }
191
192 static gboolean
193 ebb_mapi_is_marked_for_offline (EBookBackendMAPI *bbmapi)
194 {
195 ESource *source;
196 ESourceOffline *offline_extension;
197
198 g_return_val_if_fail (E_IS_BOOK_BACKEND_MAPI (bbmapi), FALSE);
199
200 source = e_backend_get_source (E_BACKEND (bbmapi));
201
202 offline_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_OFFLINE);
203
204 return e_source_offline_get_stay_synchronized (offline_extension);
205 }
206
207 static void
208 ebb_mapi_server_notification_cb (EMapiConnection *conn,
209 guint event_mask,
210 gpointer event_data,
211 gpointer user_data)
212 {
213 EBookBackendMAPI *bbmapi = user_data;
214 mapi_id_t update_folder1 = 0, update_folder2 = 0;
215
216 g_return_if_fail (E_IS_BOOK_BACKEND_MAPI (bbmapi));
217
218 switch (event_mask) {
219 case fnevNewMail:
220 case fnevNewMail | fnevMbit: {
221 struct NewMailNotification *newmail = event_data;
222
223 if (newmail)
224 update_folder1 = newmail->FID;
225 } break;
226 case fnevObjectCreated:
227 case fnevMbit | fnevObjectCreated: {
228 struct MessageCreatedNotification *msgcreated = event_data;
229
230 if (msgcreated)
231 update_folder1 = msgcreated->FID;
232 } break;
233 case fnevObjectModified:
234 case fnevMbit | fnevObjectModified: {
235 struct MessageModifiedNotification *msgmodified = event_data;
236
237 if (msgmodified)
238 update_folder1 = msgmodified->FID;
239 } break;
240 case fnevObjectDeleted:
241 case fnevMbit | fnevObjectDeleted: {
242 struct MessageDeletedNotification *msgdeleted = event_data;
243
244 if (msgdeleted)
245 update_folder1 = msgdeleted->FID;
246 } break;
247 case fnevObjectMoved:
248 case fnevMbit | fnevObjectMoved: {
249 struct MessageMoveCopyNotification *msgmoved = event_data;
250
251 if (msgmoved) {
252 update_folder1 = msgmoved->OldFID;
253 update_folder2 = msgmoved->FID;
254 }
255 } break;
256 case fnevObjectCopied:
257 case fnevMbit | fnevObjectCopied: {
258 struct MessageMoveCopyNotification *msgcopied = event_data;
259
260 if (msgcopied) {
261 update_folder1 = msgcopied->OldFID;
262 update_folder2 = msgcopied->FID;
263 }
264 } break;
265 default:
266 break;
267 }
268
269 if (update_folder1 || update_folder2) {
270 ESource *source;
271 ESourceMapiFolder *ext_mapi_folder;
272
273 source = e_backend_get_source (E_BACKEND (bbmapi));
274 ext_mapi_folder = e_source_get_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER);
275
276 if (update_folder1 == e_source_mapi_folder_get_id (ext_mapi_folder) ||
277 update_folder2 == e_source_mapi_folder_get_id (ext_mapi_folder)) {
278 e_book_meta_backend_schedule_refresh (E_BOOK_META_BACKEND (bbmapi));
279 }
280 }
281 }
282
283 static gboolean
284 ebb_mapi_connect_sync (EBookMetaBackend *meta_backend,
285 const ENamedParameters *credentials,
286 ESourceAuthenticationResult *out_auth_result,
287 gchar **out_certificate_pem,
288 GTlsCertificateFlags *out_certificate_errors,
289 GCancellable *cancellable,
290 GError **error)
291 {
292 EBookBackendMAPI *bbmapi;
293 EMapiConnection *old_conn;
294 CamelMapiSettings *settings;
295 ESource *source;
296 ESourceMapiFolder *ext_mapi_folder;
297 GError *mapi_error = NULL;
298
299 g_return_val_if_fail (E_IS_BOOK_BACKEND_MAPI (meta_backend), FALSE);
300 g_return_val_if_fail (out_auth_result != NULL, FALSE);
301
302 bbmapi = E_BOOK_BACKEND_MAPI (meta_backend);
303
304 ebb_mapi_lock_connection (bbmapi);
305
306 if (bbmapi->priv->conn &&
307 e_mapi_connection_connected (bbmapi->priv->conn)) {
308 ebb_mapi_unlock_connection (bbmapi);
309 return TRUE;
310 }
311
312 settings = ebb_mapi_get_collection_settings (bbmapi);
313 source = e_backend_get_source (E_BACKEND (bbmapi));
314 ext_mapi_folder = e_source_get_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER);
315
316 old_conn = bbmapi->priv->conn;
317
318 bbmapi->priv->conn = e_mapi_connection_new (
319 e_book_backend_get_registry (E_BOOK_BACKEND (bbmapi)),
320 camel_mapi_settings_get_profile (settings),
321 credentials, cancellable, &mapi_error);
322
323 if (!bbmapi->priv->conn) {
324 bbmapi->priv->conn = e_mapi_connection_find (camel_mapi_settings_get_profile (settings));
325 if (bbmapi->priv->conn && !e_mapi_connection_connected (bbmapi->priv->conn))
326 e_mapi_connection_reconnect (bbmapi->priv->conn, credentials, cancellable, &mapi_error);
327 }
328
329 if (old_conn)
330 g_signal_handlers_disconnect_by_func (old_conn, G_CALLBACK (ebb_mapi_server_notification_cb), bbmapi);
331
332 g_clear_object (&old_conn);
333
334 if (!bbmapi->priv->conn || mapi_error) {
335 gboolean is_network_error = mapi_error && mapi_error->domain != E_MAPI_ERROR;
336
337 g_clear_object (&bbmapi->priv->conn);
338 ebb_mapi_unlock_connection (bbmapi);
339
340 if (is_network_error)
341 ebb_mapi_error_to_client_error (error, mapi_error, E_CLIENT_ERROR_OTHER_ERROR, NULL);
342
343 g_clear_error (&mapi_error);
344
345 if (is_network_error) {
346 *out_auth_result = E_SOURCE_AUTHENTICATION_ERROR;
347 } else if ((!credentials || !e_named_parameters_count (credentials)) && !camel_mapi_settings_get_kerberos (settings)) {
348 *out_auth_result = E_SOURCE_AUTHENTICATION_REQUIRED;
349 } else {
350 *out_auth_result = E_SOURCE_AUTHENTICATION_REJECTED;
351 }
352
353 return FALSE;
354 }
355
356 if (!e_book_backend_mapi_get_is_gal (bbmapi) &&
357 e_source_mapi_folder_get_server_notification (ext_mapi_folder)) {
358 mapi_object_t obj_folder;
359 GError *mapi_error = NULL;
360
361 g_signal_connect (bbmapi->priv->conn, "server-notification", G_CALLBACK (ebb_mapi_server_notification_cb), bbmapi);
362
363 if (ebb_mapi_contacts_open_folder (bbmapi, &obj_folder, cancellable, &mapi_error)) {
364 e_mapi_connection_enable_notifications (bbmapi->priv->conn, &obj_folder,
365 fnevObjectCreated | fnevObjectModified | fnevObjectDeleted | fnevObjectMoved | fnevObjectCopied,
366 cancellable, &mapi_error);
367
368 e_mapi_connection_close_folder (bbmapi->priv->conn, &obj_folder, cancellable, &mapi_error);
369 }
370
371 if (mapi_error) {
372 ebb_mapi_maybe_disconnect (bbmapi, mapi_error);
373 g_clear_error (&mapi_error);
374 }
375 }
376
377 ebb_mapi_unlock_connection (bbmapi);
378
379 *out_auth_result = E_SOURCE_AUTHENTICATION_ACCEPTED;
380
381 return TRUE;
382 }
383
384 static gboolean
385 ebb_mapi_disconnect_sync (EBookMetaBackend *meta_backend,
386 GCancellable *cancellable,
387 GError **error)
388 {
389 EBookBackendMAPI *bbmapi;
390 gboolean success = TRUE;
391
392 g_return_val_if_fail (E_IS_BOOK_BACKEND_MAPI (meta_backend), FALSE);
393
394 bbmapi = E_BOOK_BACKEND_MAPI (meta_backend);
395
396 ebb_mapi_lock_connection (bbmapi);
397
398 if (bbmapi->priv->conn) {
399 g_signal_handlers_disconnect_by_func (bbmapi->priv->conn, G_CALLBACK (ebb_mapi_server_notification_cb), bbmapi);
400
401 success = e_mapi_connection_disconnect (bbmapi->priv->conn, FALSE, cancellable, error);
402 g_clear_object (&bbmapi->priv->conn);
403 }
404
405 ebb_mapi_unlock_connection (bbmapi);
406
407 return success;
408 }
409
410 typedef struct _LoadMultipleData {
411 gboolean is_gal;
412 gchar *book_uid;
413 GSList **out_contacts; /* EContact * */
414 } LoadMultipleData;
415
416 static gboolean
417 transfer_contacts_cb (EMapiConnection *conn,
418 TALLOC_CTX *mem_ctx,
419 /* const */ EMapiObject *object,
420 guint32 obj_index,
421 guint32 obj_total,
422 gpointer user_data,
423 GCancellable *cancellable,
424 GError **perror)
425 {
426 LoadMultipleData *lmd = user_data;
427 EContact *contact;
428
429 g_return_val_if_fail (conn != NULL, FALSE);
430 g_return_val_if_fail (object != NULL, FALSE);
431 g_return_val_if_fail (lmd != NULL, FALSE);
432
433 contact = e_mapi_book_utils_contact_from_object (conn, object, lmd->book_uid);
434 if (!contact) {
435 /* being it GAL, just ignore failures */
436 return lmd->is_gal;
437 }
438
439 *lmd->out_contacts = g_slist_prepend (*lmd->out_contacts, contact);
440
441 return TRUE;
442 }
443
444 static gboolean
445 ebb_mapi_load_multiple_sync (EBookBackendMAPI *bbmapi,
446 const GSList *uids, /* gchar * */
447 GSList **out_contacts, /* EContact * */
448 GCancellable *cancellable,
449 GError **error)
450 {
451 LoadMultipleData lmd;
452 const gchar *error_text;
453 gint partial_count = -1;
454 GSList *mids = NULL, *link;
455 ESource *source;
456 gboolean success;
457 GError *mapi_error = NULL;
458
459 g_return_val_if_fail (E_IS_BOOK_BACKEND_MAPI (bbmapi), FALSE);
460 g_return_val_if_fail (uids != NULL, FALSE);
461 g_return_val_if_fail (out_contacts != NULL, FALSE);
462
463 source = e_backend_get_source (E_BACKEND (bbmapi));
464
465 if (e_book_backend_mapi_get_is_gal (bbmapi) &&
466 !ebb_mapi_is_marked_for_offline (bbmapi)) {
467 ESourceMapiFolder *ext_mapi_folder;
468
469 ext_mapi_folder = e_source_get_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER);
470 if (e_source_mapi_folder_get_allow_partial (ext_mapi_folder)) {
471 partial_count = e_source_mapi_folder_get_partial_count (ext_mapi_folder);
472
473 if (partial_count <= 0)
474 partial_count = DEFAULT_PARTIAL_COUNT;
475 }
476 }
477
478 for (link = (GSList *) uids; link && (partial_count == -1 || partial_count > 0); link = g_slist_next (link)) {
479 mapi_id_t *pmid, mid;
480
481 if (e_mapi_util_mapi_id_from_string (link->data, &mid)) {
482 pmid = g_new0 (mapi_id_t, 1);
483 *pmid = mid;
484
485 mids = g_slist_prepend (mids, pmid);
486
487 if (partial_count > 0)
488 partial_count--;
489 }
490 }
491
492 lmd.is_gal = e_book_backend_mapi_get_is_gal (bbmapi);
493 lmd.book_uid = e_source_dup_uid (source);
494 lmd.out_contacts = out_contacts;
495
496 ebb_mapi_lock_connection (bbmapi);
497
498 if (e_book_backend_mapi_get_is_gal (bbmapi)) {
499 error_text = _("Failed to fetch GAL entries");
500
501 success = e_mapi_connection_transfer_gal_objects (bbmapi->priv->conn, mids, NULL, NULL, transfer_contacts_cb, &lmd, cancellable, &mapi_error);
502 } else {
503 mapi_object_t obj_folder;
504
505 error_text = _("Failed to transfer contacts from a server");
506
507 success = ebb_mapi_contacts_open_folder (bbmapi, &obj_folder, cancellable, &mapi_error);
508
509 if (success) {
510 success = e_mapi_connection_transfer_objects (bbmapi->priv->conn, &obj_folder, mids, transfer_contacts_cb, &lmd, cancellable, &mapi_error);
511
512 e_mapi_connection_close_folder (bbmapi->priv->conn, &obj_folder, cancellable, &mapi_error);
513 }
514 }
515
516 if (mapi_error) {
517 ebb_mapi_maybe_disconnect (bbmapi, mapi_error);
518 ebb_mapi_error_to_client_error (error, mapi_error, E_CLIENT_ERROR_OTHER_ERROR, error_text);
519 g_error_free (mapi_error);
520
521 success = FALSE;
522 }
523
524 ebb_mapi_unlock_connection (bbmapi);
525
526 g_slist_free_full (mids, g_free);
527 g_free (lmd.book_uid);
528
529 return success;
530 }
531
532 static gboolean
533 ebb_mapi_preload_infos_sync (EBookBackendMAPI *bbmapi,
534 GSList *created_objects,
535 GSList *modified_objects,
536 GCancellable *cancellable,
537 GError **error)
538 {
539 GHashTable *infos;
540 GSList *uids = NULL, *link;
541 gboolean success = TRUE;
542
543 g_return_val_if_fail (E_IS_BOOK_BACKEND_MAPI (bbmapi), FALSE);
544
545 infos = g_hash_table_new (g_str_hash, g_str_equal);
546
547 for (link = created_objects; link; link = g_slist_next (link)) {
548 EBookMetaBackendInfo *nfo = link->data;
549
550 if (nfo && nfo->uid) {
551 uids = g_slist_prepend (uids, nfo->uid);
552 g_hash_table_insert (infos, nfo->uid, nfo);
553 }
554 }
555
556 for (link = modified_objects; link; link = g_slist_next (link)) {
557 EBookMetaBackendInfo *nfo = link->data;
558
559 if (nfo && nfo->uid) {
560 uids = g_slist_prepend (uids, nfo->uid);
561 g_hash_table_insert (infos, nfo->uid, nfo);
562 }
563 }
564
565 uids = g_slist_reverse (uids);
566 if (uids) {
567 GSList *contacts = NULL;
568
569 success = ebb_mapi_load_multiple_sync (bbmapi, uids, &contacts, cancellable, error);
570 if (success) {
571 for (link = contacts; link; link = g_slist_next (link)) {
572 EContact *contact = link->data;
573
574 if (contact) {
575 EBookMetaBackendInfo *nfo = g_hash_table_lookup (infos, e_contact_get_const (contact, E_CONTACT_UID));
576
577 if (nfo && !nfo->object)
578 nfo->object = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
579 }
580 }
581 }
582
583 g_slist_free_full (contacts, g_object_unref);
584 }
585
586 g_hash_table_destroy (infos);
587 g_slist_free (uids);
588
589 return success;
590 }
591
592 static gboolean
593 ebb_mapi_get_changes_sync (EBookMetaBackend *meta_backend,
594 const gchar *last_sync_tag,
595 gboolean is_repeat,
596 gchar **out_new_sync_tag,
597 gboolean *out_repeat,
598 GSList **out_created_objects,
599 GSList **out_modified_objects,
600 GSList **out_removed_objects,
601 GCancellable *cancellable,
602 GError **error)
603 {
604 EBookBackendMAPI *bbmapi;
605
606 g_return_val_if_fail (E_IS_BOOK_BACKEND_MAPI (meta_backend), FALSE);
607 g_return_val_if_fail (out_created_objects != NULL, FALSE);
608 g_return_val_if_fail (out_modified_objects != NULL, FALSE);
609
610 /* Chain up to parent's method */
611 if (!E_BOOK_META_BACKEND_CLASS (e_book_backend_mapi_parent_class)->get_changes_sync (meta_backend,
612 last_sync_tag, is_repeat, out_new_sync_tag, out_repeat, out_created_objects,
613 out_modified_objects, out_removed_objects, cancellable, error)) {
614 return FALSE;
615 }
616
617 bbmapi = E_BOOK_BACKEND_MAPI (meta_backend);
618
619 /* Preload some of the contacts in chunk, to speed-up things;
620 ignore errors, to not break whole update process. */
621 ebb_mapi_preload_infos_sync (bbmapi, *out_created_objects, *out_modified_objects, cancellable, NULL);
622
623 return TRUE;
624 }
625
626 static gboolean
627 ebb_mapi_list_existing_uids_cb (EMapiConnection *conn,
628 TALLOC_CTX *mem_ctx,
629 const ListObjectsData *object_data,
630 guint32 obj_index,
631 guint32 obj_total,
632 gpointer user_data,
633 GCancellable *cancellable,
634 GError **perror)
635 {
636 GSList **out_existing_objects = user_data;
637 gchar *uid;
638
639 g_return_val_if_fail (conn != NULL, FALSE);
640 g_return_val_if_fail (object_data != NULL, FALSE);
641 g_return_val_if_fail (out_existing_objects != NULL, FALSE);
642
643 uid = e_mapi_util_mapi_id_to_string (object_data->mid);
644 if (uid) {
645 gchar *rev;
646
647 rev = e_mapi_book_utils_timet_to_string (object_data->last_modified);
648
649 *out_existing_objects = g_slist_prepend (*out_existing_objects,
650 e_book_meta_backend_info_new (uid, rev, NULL, NULL));
651
652 g_free (uid);
653 g_free (rev);
654 }
655
656 return TRUE;
657 }
658
659 static gboolean
660 ebb_mapi_list_existing_with_restrictions_sync (EBookMetaBackend *meta_backend,
661 BuildRestrictionsCB build_rs_cb,
662 gpointer build_rs_cb_data,
663 gchar **out_new_sync_tag,
664 GSList **out_existing_objects, /* EBookMetaBackendInfo * */
665 GCancellable *cancellable,
666 GError **error)
667 {
668 EBookBackendMAPI *bbmapi;
669 const gchar *error_text;
670 gboolean success;
671 GError *mapi_error = NULL;
672
673 g_return_val_if_fail (E_IS_BOOK_BACKEND_MAPI (meta_backend), FALSE);
674 g_return_val_if_fail (out_existing_objects, FALSE);
675
676 *out_existing_objects = NULL;
677
678 bbmapi = E_BOOK_BACKEND_MAPI (meta_backend);
679
680 ebb_mapi_lock_connection (bbmapi);
681
682 if (e_book_backend_mapi_get_is_gal (bbmapi)) {
683 error_text = _("Failed to fetch GAL entries");
684
685 success = e_mapi_connection_list_gal_objects (bbmapi->priv->conn, NULL, NULL,
686 ebb_mapi_list_existing_uids_cb, out_existing_objects, cancellable, &mapi_error);
687 } else {
688 mapi_object_t obj_folder;
689
690 error_text = _("Failed to list items from a server");
691
692 success = ebb_mapi_contacts_open_folder (bbmapi, &obj_folder, cancellable, &mapi_error);
693 if (success) {
694 success = e_mapi_connection_list_objects (bbmapi->priv->conn, &obj_folder, NULL, NULL,
695 ebb_mapi_list_existing_uids_cb, out_existing_objects, cancellable, &mapi_error);
696
697 e_mapi_connection_close_folder (bbmapi->priv->conn, &obj_folder, cancellable, &mapi_error);
698 }
699 }
700
701 if (mapi_error) {
702 ebb_mapi_maybe_disconnect (bbmapi, mapi_error);
703 ebb_mapi_error_to_client_error (error, mapi_error, E_CLIENT_ERROR_OTHER_ERROR, error_text);
704 g_error_free (mapi_error);
705
706 success = FALSE;
707 }
708
709 ebb_mapi_unlock_connection (bbmapi);
710
711 return success;
712 }
713
714
715 static gboolean
716 ebb_mapi_list_existing_sync (EBookMetaBackend *meta_backend,
717 gchar **out_new_sync_tag,
718 GSList **out_existing_objects,
719 GCancellable *cancellable,
720 GError **error)
721 {
722 g_return_val_if_fail (E_IS_BOOK_BACKEND_MAPI (meta_backend), FALSE);
723
724 return ebb_mapi_list_existing_with_restrictions_sync (meta_backend, NULL, NULL,
725 out_new_sync_tag, out_existing_objects, cancellable, error);
726 }
727
728 static gboolean
729 ebb_mapi_load_contact_sync (EBookMetaBackend *meta_backend,
730 const gchar *uid,
731 const gchar *extra,
732 EContact **out_contact,
733 gchar **out_extra,
734 GCancellable *cancellable,
735 GError **error)
736 {
737 EBookBackendMAPI *bbmapi;
738 GSList *uids, *contacts = NULL;
739 gboolean success;
740
741 g_return_val_if_fail (E_IS_BOOK_BACKEND_MAPI (meta_backend), FALSE);
742 g_return_val_if_fail (uid != NULL, FALSE);
743 g_return_val_if_fail (out_contact != NULL, FALSE);
744
745 *out_contact = NULL;
746
747 bbmapi = E_BOOK_BACKEND_MAPI (meta_backend);
748
749 uids = g_slist_prepend (NULL, (gpointer) uid);
750
751 success = ebb_mapi_load_multiple_sync (bbmapi, uids, &contacts, cancellable, error);
752
753 ebb_mapi_unlock_connection (bbmapi);
754
755 if (success && contacts) {
756 *out_contact = g_object_ref (contacts->data);
757 }
758
759 g_slist_free_full (contacts, g_object_unref);
760 g_slist_free (uids);
761
762 return success;
763 }
764
765 typedef struct _SaveContactData {
766 EBookBackendMAPI *bbmapi;
767 EContact *contact;
768 } SaveContactData;
769
770 static gboolean
771 ebb_mapi_create_object_cb (EMapiConnection *conn,
772 TALLOC_CTX *mem_ctx,
773 EMapiObject **pobject, /* out */
774 gpointer user_data,
775 GCancellable *cancellable,
776 GError **error)
777 {
778 SaveContactData *scd = user_data;
779 const gchar *uid = NULL;
780 EContact *old_contact = NULL;
781 gboolean success;
782
783 g_return_val_if_fail (scd != NULL, FALSE);
784 g_return_val_if_fail (scd->bbmapi != NULL, FALSE);
785 g_return_val_if_fail (scd->contact != NULL, FALSE);
786 g_return_val_if_fail (conn != NULL, FALSE);
787 g_return_val_if_fail (mem_ctx != NULL, FALSE);
788 g_return_val_if_fail (pobject != NULL, FALSE);
789
790 uid = e_contact_get_const (scd->contact, E_CONTACT_UID);
791 if (uid) {
792 EBookCache *book_cache;
793
794 book_cache = e_book_meta_backend_ref_cache (E_BOOK_META_BACKEND (scd->bbmapi));
795 if (book_cache &&
796 !e_book_cache_get_contact (book_cache, uid, FALSE, &old_contact, cancellable, NULL)) {
797 old_contact = NULL;
798 }
799
800 g_clear_object (&book_cache);
801 }
802
803 success = e_mapi_book_utils_contact_to_object (scd->contact, old_contact, pobject, mem_ctx, cancellable, error);
804
805 g_clear_object (&old_contact);
806
807 return success;
808 }
809
810 static gboolean
811 ebb_mapi_save_contact_sync (EBookMetaBackend *meta_backend,
812 gboolean overwrite_existing,
813 EConflictResolution conflict_resolution,
814 /* const */ EContact *contact,
815 const gchar *extra,
816 guint32 opflags,
817 gchar **out_new_uid,
818 gchar **out_new_extra,
819 GCancellable *cancellable,
820 GError **error)
821 {
822 EBookBackendMAPI *bbmapi;
823 mapi_object_t obj_folder;
824 mapi_id_t mid = 0;
825 gboolean success;
826 GError *mapi_error = NULL;
827
828 g_return_val_if_fail (E_IS_BOOK_BACKEND_MAPI (meta_backend), FALSE);
829 g_return_val_if_fail (E_IS_CONTACT (contact), FALSE);
830 g_return_val_if_fail (out_new_uid != NULL, FALSE);
831
832 *out_new_uid = NULL;
833
834 bbmapi = E_BOOK_BACKEND_MAPI (meta_backend);
835
836 if (e_book_backend_mapi_get_is_gal (bbmapi)) {
837 g_propagate_error (error, e_client_error_create (E_CLIENT_ERROR_PERMISSION_DENIED, NULL));
838 return FALSE;
839 }
840
841 ebb_mapi_lock_connection (bbmapi);
842
843 success = ebb_mapi_contacts_open_folder (bbmapi, &obj_folder, cancellable, &mapi_error);
844 if (success) {
845 SaveContactData scd;
846
847 scd.bbmapi = bbmapi;
848 scd.contact = contact;
849
850 if (overwrite_existing) {
851 success = e_mapi_util_mapi_id_from_string (e_contact_get_const (contact, E_CONTACT_UID), &mid) &&
852 e_mapi_connection_modify_object (bbmapi->priv->conn, &obj_folder, mid,
853 ebb_mapi_create_object_cb, &scd, cancellable, &mapi_error);
854
855 } else {
856 success = e_mapi_connection_create_object (bbmapi->priv->conn, &obj_folder, E_MAPI_CREATE_FLAG_NONE,
857 ebb_mapi_create_object_cb, &scd, &mid, cancellable, &mapi_error);
858 }
859
860 e_mapi_connection_close_folder (bbmapi->priv->conn, &obj_folder, cancellable, &mapi_error);
861 }
862
863 if (mapi_error || !mid) {
864 ebb_mapi_maybe_disconnect (bbmapi, mapi_error);
865 ebb_mapi_error_to_client_error (error, mapi_error, E_CLIENT_ERROR_OTHER_ERROR,
866 overwrite_existing ? _("Failed to modify item on a server") : _("Failed to create item on a server"));
867 g_clear_error (&mapi_error);
868
869 success = FALSE;
870 }
871
872 ebb_mapi_unlock_connection (bbmapi);
873
874 if (success)
875 *out_new_uid = e_mapi_util_mapi_id_to_string (mid);
876
877 return success;
878 }
879
880 static gboolean
881 ebb_mapi_remove_contact_sync (EBookMetaBackend *meta_backend,
882 EConflictResolution conflict_resolution,
883 const gchar *uid,
884 const gchar *extra,
885 const gchar *object,
886 guint32 opflags,
887 GCancellable *cancellable,
888 GError **error)
889 {
890 EBookBackendMAPI *bbmapi;
891 mapi_id_t mid = 0;
892 gboolean success = TRUE;
893 GError *mapi_error = NULL;
894
895 g_return_val_if_fail (E_IS_BOOK_BACKEND_MAPI (meta_backend), FALSE);
896 g_return_val_if_fail (uid != NULL, FALSE);
897
898 bbmapi = E_BOOK_BACKEND_MAPI (meta_backend);
899
900 if (e_book_backend_mapi_get_is_gal (bbmapi)) {
901 g_propagate_error (error, e_client_error_create (E_CLIENT_ERROR_PERMISSION_DENIED, NULL));
902 return FALSE;
903 }
904
905 if (e_mapi_util_mapi_id_from_string (uid, &mid)) {
906 mapi_object_t obj_folder;
907
908 ebb_mapi_lock_connection (bbmapi);
909
910 success = ebb_mapi_contacts_open_folder (bbmapi, &obj_folder, cancellable, &mapi_error);
911 if (success) {
912 GSList *mids;
913
914 mids = g_slist_prepend (NULL, &mid);
915
916 success = e_mapi_connection_remove_items (bbmapi->priv->conn, &obj_folder, mids, cancellable, &mapi_error);
917
918 e_mapi_connection_close_folder (bbmapi->priv->conn, &obj_folder, cancellable, &mapi_error);
919
920 g_slist_free (mids);
921 }
922
923 ebb_mapi_unlock_connection (bbmapi);
924 }
925
926 if (mapi_error || !mid) {
927 ebb_mapi_maybe_disconnect (bbmapi, mapi_error);
928 ebb_mapi_error_to_client_error (error, mapi_error, E_CLIENT_ERROR_OTHER_ERROR, _("Failed to remove item from a server"));
929 g_clear_error (&mapi_error);
930
931 success = FALSE;
932 }
933
934 return success;
935 }
936
937 static gboolean
938 ebb_mapi_update_cache_for_expression (EBookBackendMAPI *bbmapi,
939 const gchar *expr,
940 GCancellable *cancellable,
941 GError **error)
942 {
943 EBookMetaBackend *meta_backend;
944 GSList *found_infos = NULL;
945 gboolean success = TRUE;
946
947 g_return_val_if_fail (E_IS_BOOK_BACKEND_MAPI (bbmapi), FALSE);
948
949 meta_backend = E_BOOK_META_BACKEND (bbmapi);
950
951 ebb_mapi_lock_connection (bbmapi);
952
953 /* Search only if not searching for everything */
954 if (expr && *expr && g_ascii_strcasecmp (expr, "(contains \"x-evolution-any-field\" \"\")") != 0) {
955 success = e_book_meta_backend_ensure_connected_sync (meta_backend, cancellable, error) &&
956 ebb_mapi_list_existing_with_restrictions_sync (meta_backend,
957 e_mapi_book_utils_build_sexp_restriction, (gpointer) expr,
958 NULL, &found_infos, cancellable, error);
959
960 if (success) {
961 GSList *created_objects = NULL, *modified_objects = NULL;
962
963 success = e_book_meta_backend_split_changes_sync (meta_backend, found_infos, &created_objects,
964 &modified_objects, NULL, cancellable, error);
965 if (success)
966 success = ebb_mapi_preload_infos_sync (bbmapi, created_objects, modified_objects, cancellable, error);
967 if (success)
968 success = e_book_meta_backend_process_changes_sync (meta_backend, created_objects,
969 modified_objects, NULL, cancellable, error);
970
971 g_slist_free_full (created_objects, e_book_meta_backend_info_free);
972 g_slist_free_full (modified_objects, e_book_meta_backend_info_free);
973 }
974
975 g_slist_free_full (found_infos, e_book_meta_backend_info_free);
976 }
977
978 ebb_mapi_unlock_connection (bbmapi);
979
980 return success;
981 }
982
983 static gboolean
984 ebb_mapi_search_sync (EBookMetaBackend *meta_backend,
985 const gchar *expr,
986 gboolean meta_contact,
987 GSList **out_contacts,
988 GCancellable *cancellable,
989 GError **error)
990 {
991 g_return_val_if_fail (E_IS_BOOK_BACKEND_MAPI (meta_backend), FALSE);
992
993 /* Ignore errors, just try its best */
994 ebb_mapi_update_cache_for_expression (E_BOOK_BACKEND_MAPI (meta_backend), expr, cancellable, NULL);
995
996 /* Chain up to parent's method */
997 return E_BOOK_META_BACKEND_CLASS (e_book_backend_mapi_parent_class)->search_sync (meta_backend, expr, meta_contact,
998 out_contacts, cancellable, error);
999 }
1000
1001 static gboolean
1002 ebb_mapi_search_uids_sync (EBookMetaBackend *meta_backend,
1003 const gchar *expr,
1004 GSList **out_uids,
1005 GCancellable *cancellable,
1006 GError **error)
1007 {
1008 g_return_val_if_fail (E_IS_BOOK_BACKEND_MAPI (meta_backend), FALSE);
1009
1010 /* Ignore errors, just try its best */
1011 ebb_mapi_update_cache_for_expression (E_BOOK_BACKEND_MAPI (meta_backend), expr, cancellable, NULL);
1012
1013 /* Chain up to parent's method */
1014 return E_BOOK_META_BACKEND_CLASS (e_book_backend_mapi_parent_class)->search_uids_sync (meta_backend, expr,
1015 out_uids, cancellable, error);
1016 }
1017
1018 static gchar *
1019 ebb_mapi_get_backend_property (EBookBackend *backend,
1020 const gchar *prop_name)
1021 {
1022 EBookBackendMAPI *bbmapi;
1023
1024 g_return_val_if_fail (prop_name != NULL, NULL);
1025
1026 bbmapi = E_BOOK_BACKEND_MAPI (backend);
1027
1028 if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
1029 return g_strjoin (",",
1030 "net",
1031 "contact-lists",
1032 e_book_meta_backend_get_capabilities (E_BOOK_META_BACKEND (backend)),
1033 ebb_mapi_is_marked_for_offline (bbmapi) ? "do-initial-query" : NULL,
1034 NULL);
1035 } else if (g_str_equal (prop_name, E_BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS)) {
1036 return g_strdup (e_contact_field_name (E_CONTACT_FILE_AS));
1037 } else if (g_str_equal (prop_name, E_BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS)) {
1038 GSList *fields;
1039 gchar *prop_value;
1040
1041 fields = e_mapi_book_utils_get_supported_contact_fields ();
1042 prop_value = e_data_book_string_slist_to_comma_string (fields);
1043 g_slist_free (fields);
1044
1045 return prop_value;
1046 }
1047
1048 /* Chain up to parent's method */
1049 return E_BOOK_BACKEND_CLASS (e_book_backend_mapi_parent_class)->impl_get_backend_property (backend, prop_name);
1050 }
1051
1052 static gboolean
1053 ebb_mapi_get_destination_address (EBackend *backend,
1054 gchar **host,
1055 guint16 *port)
1056 {
1057 ESourceRegistry *registry;
1058 ESource *source;
1059 gboolean result = FALSE;
1060
1061 g_return_val_if_fail (host != NULL, FALSE);
1062 g_return_val_if_fail (port != NULL, FALSE);
1063
1064 registry = e_book_backend_get_registry (E_BOOK_BACKEND (backend));
1065 source = e_backend_get_source (backend);
1066
1067 /* Sanity checking */
1068 if (!registry || !source || !e_source_get_parent (source))
1069 return FALSE;
1070
1071 source = e_source_registry_ref_source (registry, e_source_get_parent (source));
1072 if (!source)
1073 return FALSE;
1074
1075 if (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
1076 ESourceAuthentication *auth = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
1077
1078 *host = g_strdup (e_source_authentication_get_host (auth));
1079 *port = e_source_authentication_get_port (auth);
1080
1081 if (!*port)
1082 *port = 135;
1083
1084 result = *host && **host;
1085 if (!result) {
1086 g_free (*host);
1087 *host = NULL;
1088 }
1089 }
1090
1091 g_object_unref (source);
1092
1093 return result;
1094 }
1095
1096 static void
1097 ebb_mapi_constructed (GObject *object)
1098 {
1099 EBookBackendMAPI *bbmapi = E_BOOK_BACKEND_MAPI (object);
1100
1101 /* Chaing up to parent's method */
1102 G_OBJECT_CLASS (e_book_backend_mapi_parent_class)->constructed (object);
1103
1104 /* Reset the connectable, it steals data from Authentication extension,
1105 where is written no address */
1106 e_backend_set_connectable (E_BACKEND (object), NULL);
1107
1108 e_book_backend_set_writable (E_BOOK_BACKEND (bbmapi), !e_book_backend_mapi_get_is_gal (bbmapi));
1109 }
1110
1111 static void
1112 ebb_mapi_dispose (GObject *object)
1113 {
1114 EBookBackendMAPI *bbmapi = E_BOOK_BACKEND_MAPI (object);
1115
1116 g_clear_object (&bbmapi->priv->conn);
1117
1118 /* Chain up to parent's method */
1119 G_OBJECT_CLASS (e_book_backend_mapi_parent_class)->dispose (object);
1120 }
1121
1122 static void
1123 ebb_mapi_finalize (GObject *object)
1124 {
1125 EBookBackendMAPI *bbmapi = E_BOOK_BACKEND_MAPI (object);
1126
1127 g_rec_mutex_clear (&bbmapi->priv->conn_lock);
1128
1129 /* Chain up to parent's method */
1130 G_OBJECT_CLASS (e_book_backend_mapi_parent_class)->finalize (object);
1131 }
1132
1133 static void
1134 e_book_backend_mapi_class_init (EBookBackendMAPIClass *klass)
1135 {
1136 GObjectClass *object_class;
1137 EBackendClass *backend_class;
1138 EBookBackendClass *book_backend_class;
1139 EBookMetaBackendClass *meta_backend_class;
1140
1141 meta_backend_class = E_BOOK_META_BACKEND_CLASS (klass);
1142 meta_backend_class->backend_module_directory = BACKENDDIR;
1143 meta_backend_class->backend_module_filename = "libebookbackendmapi.so";
1144 meta_backend_class->connect_sync = ebb_mapi_connect_sync;
1145 meta_backend_class->disconnect_sync = ebb_mapi_disconnect_sync;
1146 meta_backend_class->get_changes_sync = ebb_mapi_get_changes_sync;
1147 meta_backend_class->list_existing_sync = ebb_mapi_list_existing_sync;
1148 meta_backend_class->load_contact_sync = ebb_mapi_load_contact_sync;
1149 meta_backend_class->save_contact_sync = ebb_mapi_save_contact_sync;
1150 meta_backend_class->remove_contact_sync = ebb_mapi_remove_contact_sync;
1151 meta_backend_class->search_sync = ebb_mapi_search_sync;
1152 meta_backend_class->search_uids_sync = ebb_mapi_search_uids_sync;
1153
1154 book_backend_class = E_BOOK_BACKEND_CLASS (klass);
1155 book_backend_class->impl_get_backend_property = ebb_mapi_get_backend_property;
1156
1157 backend_class = E_BACKEND_CLASS (klass);
1158 backend_class->get_destination_address = ebb_mapi_get_destination_address;
1159
1160 object_class = G_OBJECT_CLASS (klass);
1161 object_class->constructed = ebb_mapi_constructed;
1162 object_class->dispose = ebb_mapi_dispose;
1163 object_class->finalize = ebb_mapi_finalize;
1164 }
1165
1166 static void
1167 e_book_backend_mapi_init (EBookBackendMAPI *bbmapi)
1168 {
1169 bbmapi->priv = e_book_backend_mapi_get_instance_private (bbmapi);
1170
1171 g_rec_mutex_init (&bbmapi->priv->conn_lock);
1172 }
1173
1174 void
1175 e_book_backend_mapi_set_is_gal (EBookBackendMAPI *bbmapi,
1176 gboolean is_gal)
1177 {
1178 g_return_if_fail (E_IS_BOOK_BACKEND_MAPI (bbmapi));
1179
1180 bbmapi->priv->is_gal = is_gal;
1181 }
1182
1183 gboolean
1184 e_book_backend_mapi_get_is_gal (EBookBackendMAPI *bbmapi)
1185 {
1186 g_return_val_if_fail (E_IS_BOOK_BACKEND_MAPI (bbmapi), FALSE);
1187
1188 return bbmapi->priv->is_gal;
1189 }