"Fossies" - the Fresh Open Source Software Archive 
Member "evolution-mapi-3.46.1/src/collection/e-mapi-backend.c" (2 Dec 2022, 33306 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-mapi-backend.c" see the
Fossies "Dox" file reference documentation.
1 /*
2 * e-mapi-backend.c
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with the program; if not, see <http://www.gnu.org/licenses/>
16 *
17 */
18
19 #include "evolution-mapi-config.h"
20
21 #include <glib/gi18n-lib.h>
22
23 #include <e-mapi-connection.h>
24 #include <e-mapi-folder.h>
25 #include <e-mapi-utils.h>
26 #include <e-source-mapi-folder.h>
27 #include <camel-mapi-settings.h>
28
29 #include "e-mapi-backend.h"
30
31 struct _EMapiBackendPrivate {
32 /* Folder ID -> ESource */
33 GHashTable *folders;
34
35 gboolean need_update_folders;
36 gulong source_changed_handler_id;
37
38 GMutex credentials_lock;
39 ENamedParameters *credentials;
40 };
41
42 G_DEFINE_DYNAMIC_TYPE_EXTENDED (EMapiBackend, e_mapi_backend, E_TYPE_COLLECTION_BACKEND, 0, G_ADD_PRIVATE_DYNAMIC (EMapiBackend))
43
44 typedef gboolean (* EMapiBackendAuthenticatorFunc) (EBackend *backend,
45 CamelMapiSettings *settings,
46 EMapiConnection *conn,
47 gpointer user_data,
48 GCancellable *cancellable,
49 GError **error);
50
51 static gboolean
52 e_mapi_backend_authenticator_run (EBackend *backend,
53 CamelMapiSettings *settings,
54 const ENamedParameters *credentials,
55 EMapiBackendAuthenticatorFunc func,
56 gpointer user_data,
57 GCancellable *cancellable,
58 GError **error)
59 {
60 EMapiProfileData empd = { 0 };
61 EMapiConnection *conn;
62 CamelNetworkSettings *network_settings;
63 GError *mapi_error = NULL;
64 gboolean success;
65
66 g_return_val_if_fail (E_IS_BACKEND (backend), FALSE);
67 g_return_val_if_fail (CAMEL_IS_MAPI_SETTINGS (settings), FALSE);
68 g_return_val_if_fail (func != NULL, FALSE);
69
70 if (!credentials) {
71 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED,
72 _("Cannot connect, no credentials provided"));
73 return FALSE;
74 }
75
76 g_object_ref (backend);
77 g_object_ref (settings);
78
79 network_settings = CAMEL_NETWORK_SETTINGS (settings);
80
81 empd.server = camel_network_settings_get_host (network_settings);
82 empd.username = camel_network_settings_get_user (network_settings);
83 e_mapi_util_profiledata_from_settings (&empd, settings);
84
85 conn = e_mapi_connection_new (
86 NULL,
87 camel_mapi_settings_get_profile (settings),
88 credentials, cancellable, &mapi_error);
89
90 if (mapi_error) {
91 g_warn_if_fail (!conn);
92
93 g_object_unref (backend);
94 g_object_unref (settings);
95
96 g_propagate_error (error, mapi_error);
97
98 return FALSE;
99 }
100
101 g_warn_if_fail (conn != NULL);
102
103 success = func (backend, settings, conn, user_data, cancellable, error);
104
105 g_object_unref (conn);
106 g_object_unref (backend);
107 g_object_unref (settings);
108
109 return success;
110 }
111
112 static CamelMapiSettings *
113 mapi_backend_get_settings (EMapiBackend *backend)
114 {
115 ESource *source;
116 ESourceCamel *extension;
117 CamelSettings *settings;
118 const gchar *extension_name;
119
120 source = e_backend_get_source (E_BACKEND (backend));
121 extension_name = e_source_camel_get_extension_name ("mapi");
122 extension = e_source_get_extension (source, extension_name);
123 settings = e_source_camel_get_settings (extension);
124
125 return CAMEL_MAPI_SETTINGS (settings);
126 }
127
128 struct SyndFoldersData
129 {
130 EMapiBackend *backend;
131 GSList *folders;
132 gchar *profile;
133 };
134
135 static void
136 sync_folders_data_free (gpointer data)
137 {
138 struct SyndFoldersData *sfd = data;
139
140 if (!sfd)
141 return;
142
143 e_mapi_folder_free_list (sfd->folders);
144 g_object_unref (sfd->backend);
145 g_free (sfd->profile);
146 g_slice_free (struct SyndFoldersData, sfd);
147 }
148
149 static void
150 mapi_backend_update_enabled (ESource *data_source,
151 ESource *collection_source)
152 {
153 ESourceCollection *collection_extension = NULL;
154 gboolean part_enabled = TRUE;
155
156 g_return_if_fail (E_IS_SOURCE (data_source));
157
158 if (!collection_source || !e_source_get_enabled (collection_source)) {
159 e_source_set_enabled (data_source, FALSE);
160 return;
161 }
162
163 if (e_source_has_extension (collection_source, E_SOURCE_EXTENSION_COLLECTION))
164 collection_extension = e_source_get_extension (collection_source, E_SOURCE_EXTENSION_COLLECTION);
165
166 if (e_source_has_extension (data_source, E_SOURCE_EXTENSION_CALENDAR) ||
167 e_source_has_extension (data_source, E_SOURCE_EXTENSION_TASK_LIST) ||
168 e_source_has_extension (data_source, E_SOURCE_EXTENSION_MEMO_LIST)) {
169 part_enabled = !collection_extension || e_source_collection_get_calendar_enabled (collection_extension);
170 } else if (e_source_has_extension (data_source, E_SOURCE_EXTENSION_ADDRESS_BOOK)) {
171 part_enabled = !collection_extension || e_source_collection_get_contacts_enabled (collection_extension);
172 } else if (e_source_has_extension (data_source, E_SOURCE_EXTENSION_MAIL_ACCOUNT) ||
173 e_source_has_extension (data_source, E_SOURCE_EXTENSION_MAIL_IDENTITY) ||
174 e_source_has_extension (data_source, E_SOURCE_EXTENSION_MAIL_TRANSPORT)) {
175 part_enabled = !collection_extension || e_source_collection_get_mail_enabled (collection_extension);
176 }
177
178 e_source_set_enabled (data_source, part_enabled);
179 }
180
181 static gboolean
182 mapi_backend_sync_folders_idle_cb (gpointer user_data)
183 {
184 struct SyndFoldersData *sfd = user_data;
185 GSList *iter;
186 GList *configured, *all_sources, *citer;
187 ESourceRegistryServer *server;
188 EMapiBackend *backend;
189 GSList *mapi_folders;
190 gboolean has_gal = FALSE, is_online;
191 gint color_seed;
192
193 g_return_val_if_fail (sfd != NULL, FALSE);
194 g_return_val_if_fail (sfd->backend != NULL, FALSE);
195 g_return_val_if_fail (sfd->profile != NULL, FALSE);
196
197 backend = sfd->backend;
198 mapi_folders = sfd->folders;
199 is_online = e_backend_get_online (E_BACKEND (backend));
200
201 server = e_collection_backend_ref_server (E_COLLECTION_BACKEND (backend));
202 all_sources = e_source_registry_server_list_sources (server, NULL);
203 configured = e_mapi_utils_filter_sources_for_profile (all_sources, sfd->profile);
204 g_list_free_full (all_sources, g_object_unref);
205
206 color_seed = g_list_length (configured);
207
208 for (iter = mapi_folders; iter; iter = iter->next) {
209 EMapiFolder *folder = iter->data;
210 ESource *source;
211
212 if (e_mapi_folder_get_category (folder) != E_MAPI_FOLDER_CATEGORY_PERSONAL)
213 continue;
214
215 switch (e_mapi_folder_get_type (folder)) {
216 case E_MAPI_FOLDER_TYPE_APPOINTMENT:
217 case E_MAPI_FOLDER_TYPE_CONTACT:
218 case E_MAPI_FOLDER_TYPE_MEMO:
219 case E_MAPI_FOLDER_TYPE_JOURNAL:
220 case E_MAPI_FOLDER_TYPE_TASK:
221 break;
222 default:
223 continue;
224 }
225
226 source = e_mapi_utils_get_source_for_folder (configured, sfd->profile, e_mapi_folder_get_id (folder));
227 if (source) {
228 mapi_backend_update_enabled (source, e_backend_get_source (E_BACKEND (backend)));
229
230 if (g_strcmp0 (e_source_get_display_name (source), e_mapi_folder_get_name (folder)) != 0)
231 e_source_set_display_name (source, e_mapi_folder_get_name (folder));
232
233 configured = g_list_remove (configured, source);
234 g_object_unref (source);
235 } else {
236 gchar *fid_str, *res_id;
237 const gchar *parent_id;
238
239 source = e_backend_get_source (E_BACKEND (backend));
240
241 parent_id = e_source_get_uid (source);
242 fid_str = e_mapi_util_mapi_id_to_string (e_mapi_folder_get_id (folder));
243 res_id = g_strconcat (parent_id ? parent_id : "mapi", ".", fid_str, NULL);
244 g_free (fid_str);
245
246 source = e_collection_backend_new_child (E_COLLECTION_BACKEND (backend), res_id);
247
248 if (e_mapi_folder_populate_esource (
249 source,
250 configured,
251 e_mapi_folder_get_type (folder),
252 sfd->profile,
253 TRUE,
254 E_MAPI_FOLDER_CATEGORY_PERSONAL,
255 NULL,
256 e_mapi_folder_get_name (folder),
257 e_mapi_folder_get_id (folder),
258 color_seed,
259 NULL,
260 NULL)) {
261 color_seed++;
262 mapi_backend_update_enabled (source, e_backend_get_source (E_BACKEND (backend)));
263 e_server_side_source_set_writable (E_SERVER_SIDE_SOURCE (source), TRUE);
264 e_server_side_source_set_remote_deletable (E_SERVER_SIDE_SOURCE (source), TRUE);
265 e_source_registry_server_add_source (server, source);
266 }
267
268 g_free (res_id);
269 g_object_unref (source);
270 }
271 }
272
273 /* those which left are either mail sources, GAL or removed from the server */
274 for (citer = configured; citer && is_online; citer = citer->next) {
275 ESource *source = citer->data;
276 ESourceMapiFolder *folder_ext;
277 const gchar *foreign_user_name;
278
279 if (!e_source_has_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER))
280 continue;
281
282 if (!e_source_has_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK) &&
283 !e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR) &&
284 !e_source_has_extension (source, E_SOURCE_EXTENSION_MEMO_LIST) &&
285 !e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST))
286 continue;
287
288 folder_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER);
289 if (e_source_mapi_folder_is_public (folder_ext))
290 continue;
291
292 foreign_user_name = e_source_mapi_folder_get_foreign_username (folder_ext);
293 if (foreign_user_name && *foreign_user_name)
294 continue;
295
296 /* test GAL */
297 if (!has_gal && e_source_has_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK)) {
298 ESourceAddressBook *book_ext;
299
300 book_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK);
301 has_gal = g_strcmp0 ("mapigal", e_source_backend_get_backend_name (E_SOURCE_BACKEND (book_ext))) == 0;
302 if (has_gal)
303 continue;
304 }
305
306 e_source_remove_sync (source, NULL, NULL);
307 }
308
309 all_sources = e_collection_backend_claim_all_resources (E_COLLECTION_BACKEND (backend));
310 for (citer = all_sources; citer; citer = citer->next) {
311 ESource *source = citer->data;
312 ESourceMapiFolder *extension;
313 const gchar *foreign_username;
314 gboolean remove = FALSE;
315
316 if (!e_source_has_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER))
317 continue;
318
319 /* foreign folders are just added */
320 extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER);
321 foreign_username = e_source_mapi_folder_get_foreign_username (extension);
322 if (e_source_mapi_folder_is_public (extension) || (foreign_username && *foreign_username)) {
323 e_server_side_source_set_writable (E_SERVER_SIDE_SOURCE (source), TRUE);
324 e_server_side_source_set_remote_deletable (E_SERVER_SIDE_SOURCE (source), TRUE);
325 e_source_registry_server_add_source (server, source);
326 } else if (!has_gal && e_source_has_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK)) {
327 ESourceAddressBook *book_ext;
328
329 book_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK);
330 has_gal = g_strcmp0 ("mapigal", e_source_backend_get_backend_name (E_SOURCE_BACKEND (book_ext))) == 0;
331 if (has_gal)
332 e_source_registry_server_add_source (server, source);
333 else
334 remove = TRUE;
335 } else {
336 remove = TRUE;
337 }
338
339 if (remove) {
340 /* in online mode remove means remove, while in offline mode
341 there are used only discovered sources from the last run,
342 thus re-add them all
343 */
344 if (is_online) {
345 e_source_remove_sync (source, NULL, NULL);
346 } else {
347 e_server_side_source_set_writable (E_SERVER_SIDE_SOURCE (source), TRUE);
348 e_server_side_source_set_remote_deletable (E_SERVER_SIDE_SOURCE (source), TRUE);
349 e_source_registry_server_add_source (server, source);
350 }
351 }
352 }
353 g_list_free_full (all_sources, g_object_unref);
354
355 /* add GAL, if not there already */
356 if (!has_gal) {
357 ESource *source;
358
359 source = e_collection_backend_new_child (E_COLLECTION_BACKEND (backend), "mapigal");
360
361 if (e_mapi_folder_populate_esource (
362 source,
363 configured,
364 E_MAPI_FOLDER_TYPE_CONTACT,
365 sfd->profile,
366 FALSE,
367 E_MAPI_FOLDER_CATEGORY_PERSONAL,
368 NULL,
369 _("Global Address List"),
370 -1,
371 0,
372 NULL,
373 NULL)) {
374 ESourceAddressBook *book_ext;
375 /* ESourceContancts *contacts_ext; */
376
377 book_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK);
378 e_source_backend_set_backend_name (E_SOURCE_BACKEND (book_ext), "mapigal");
379
380 /* exclude GAL from Birthday & Anniversaries calendar by default */
381 /* but it is not accessible from outside (yet)
382 contacts_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_CONTACTS_BACKEND);
383 e_source_contacts_set_include_me (contacts_ext, FALSE); */
384
385 e_source_registry_server_add_source (server, source);
386 }
387
388 g_object_unref (source);
389 }
390
391 g_list_free_full (configured, g_object_unref);
392 g_object_unref (server);
393
394 e_collection_backend_thaw_populate (E_COLLECTION_BACKEND (backend));
395
396 return FALSE;
397 }
398
399 static void
400 mapi_backend_queue_auth_session (EMapiBackend *backend)
401 {
402 CamelMapiSettings *mapi_settings;
403
404 mapi_settings = mapi_backend_get_settings (backend);
405
406 if (!e_backend_get_online (E_BACKEND (backend))) {
407 struct SyndFoldersData *sfd;
408
409 sfd = g_slice_new0 (struct SyndFoldersData);
410 sfd->folders = NULL;
411 sfd->backend = g_object_ref (backend);
412 sfd->profile = camel_mapi_settings_dup_profile (mapi_settings);
413
414 /* Needed, because the mapi_backend_sync_folders_idle_cb() calls 'thaw' */
415 e_collection_backend_freeze_populate (E_COLLECTION_BACKEND (backend));
416
417 mapi_backend_sync_folders_idle_cb (sfd);
418
419 sync_folders_data_free (sfd);
420
421 return;
422 }
423
424 backend->priv->need_update_folders = FALSE;
425
426 /* kerberos doesn't use passwords, do it directly */
427 if (camel_mapi_settings_get_kerberos (mapi_settings)) {
428 e_backend_schedule_authenticate (E_BACKEND (backend), NULL);
429 return;
430 }
431
432 /* For now at least, we don't need to know the
433 * results, so no callback function is needed. */
434 e_backend_credentials_required (
435 E_BACKEND (backend), E_SOURCE_CREDENTIALS_REASON_REQUIRED, NULL, 0, NULL,
436 NULL, NULL, NULL);
437 }
438
439 static void
440 mapi_backend_source_changed_cb (ESource *source,
441 EMapiBackend *backend)
442 {
443 /* does nothing currently */
444 if (!e_collection_backend_get_part_enabled (E_COLLECTION_BACKEND (backend), E_COLLECTION_BACKEND_PART_ANY)) {
445 backend->priv->need_update_folders = TRUE;
446 return;
447 }
448
449 if (e_backend_get_online (E_BACKEND (backend)) &&
450 backend->priv->need_update_folders)
451 mapi_backend_queue_auth_session (backend);
452 }
453
454 static void
455 mapi_backend_constructed (GObject *object)
456 {
457 EBackend *backend = E_BACKEND (object);
458 ESource *source;
459
460 /* Chain up to parent's constructed() method. */
461 G_OBJECT_CLASS (e_mapi_backend_parent_class)->constructed (object);
462
463 source = e_backend_get_source (backend);
464
465 /* XXX Wondering if we ought to delay this until after folders
466 * are initially populated, just to remove the possibility
467 * of weird races with clients trying to create folders. */
468 e_server_side_source_set_remote_creatable (
469 E_SERVER_SIDE_SOURCE (source), TRUE);
470 }
471
472 static void
473 mapi_backend_dispose (GObject *object)
474 {
475 EMapiBackend *mapi_backend = E_MAPI_BACKEND (object);
476
477 g_hash_table_remove_all (mapi_backend->priv->folders);
478
479 if (mapi_backend->priv->source_changed_handler_id) {
480 g_signal_handler_disconnect (e_backend_get_source (E_BACKEND (object)), mapi_backend->priv->source_changed_handler_id);
481 mapi_backend->priv->source_changed_handler_id = 0;
482 }
483
484 /* Chain up to parent's dispose() method. */
485 G_OBJECT_CLASS (e_mapi_backend_parent_class)->dispose (object);
486 }
487
488 static void
489 mapi_backend_finalize (GObject *object)
490 {
491 EMapiBackend *mapi_backend = E_MAPI_BACKEND (object);
492
493 g_hash_table_destroy (mapi_backend->priv->folders);
494
495 g_mutex_clear (&mapi_backend->priv->credentials_lock);
496 e_named_parameters_free (mapi_backend->priv->credentials);
497
498 /* Chain up to parent's finalize() method. */
499 G_OBJECT_CLASS (e_mapi_backend_parent_class)->finalize (object);
500 }
501
502 static void
503 mapi_backend_populate (ECollectionBackend *backend)
504 {
505 ESource *source;
506 EMapiBackend *mapi_backend = E_MAPI_BACKEND (backend);
507
508 source = e_backend_get_source (E_BACKEND (backend));
509
510 mapi_backend->priv->need_update_folders = TRUE;
511
512 /* do not do anything, if account is disabled */
513 if (!e_collection_backend_get_part_enabled (backend, E_COLLECTION_BACKEND_PART_ANY))
514 return;
515
516 if (!e_collection_backend_freeze_populate (backend)) {
517 e_collection_backend_thaw_populate (backend);
518 return;
519 }
520
521 if (!mapi_backend->priv->source_changed_handler_id)
522 mapi_backend->priv->source_changed_handler_id = g_signal_connect (
523 source, "changed",
524 G_CALLBACK (mapi_backend_source_changed_cb), backend);
525
526 /* We test authentication passwords by attempting to synchronize
527 * the folder hierarchy. Since we want to synchronize the folder
528 * hierarchy immediately on startup, schedule an authentication
529 * session first thing. */
530 mapi_backend_queue_auth_session (mapi_backend);
531
532 e_collection_backend_thaw_populate (backend);
533 }
534
535 static gchar *
536 mapi_backend_dup_resource_id (ECollectionBackend *backend,
537 ESource *child_source)
538 {
539 ESourceMapiFolder *extension;
540 const gchar *extension_name;
541 gchar *fid_str, *res_id;
542 const gchar *parent_id;
543 ESource *source;
544
545 extension_name = E_SOURCE_EXTENSION_MAPI_FOLDER;
546 extension = e_source_get_extension (child_source, extension_name);
547 source = e_backend_get_source (E_BACKEND (backend));
548
549 parent_id = e_source_get_uid (source);
550 fid_str = e_mapi_util_mapi_id_to_string (e_source_mapi_folder_get_id (extension));
551 res_id = g_strconcat (parent_id ? parent_id : "mapi", ".", fid_str, NULL);
552 g_free (fid_str);
553
554 return res_id;
555 }
556
557 static void
558 mapi_backend_child_added (ECollectionBackend *backend,
559 ESource *child_source)
560 {
561 ESource *collection_source;
562 const gchar *extension_name;
563 gboolean is_mail = FALSE;
564
565 collection_source = e_backend_get_source (E_BACKEND (backend));
566
567 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
568 is_mail |= e_source_has_extension (child_source, extension_name);
569
570 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
571 is_mail |= e_source_has_extension (child_source, extension_name);
572
573 extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
574 is_mail |= e_source_has_extension (child_source, extension_name);
575
576 /* Synchronize mail-related user with the collection identity. */
577 extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
578 if (is_mail && e_source_has_extension (child_source, extension_name)) {
579 ESourceAuthentication *auth_child_extension;
580 ESourceCollection *collection_extension;
581
582 extension_name = E_SOURCE_EXTENSION_COLLECTION;
583 collection_extension = e_source_get_extension (
584 collection_source, extension_name);
585
586 extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
587 auth_child_extension = e_source_get_extension (
588 child_source, extension_name);
589
590 e_binding_bind_property (
591 collection_extension, "identity",
592 auth_child_extension, "user",
593 G_BINDING_SYNC_CREATE);
594 }
595
596 /* We track MAPI folders in a hash table by folder ID. */
597 extension_name = E_SOURCE_EXTENSION_MAPI_FOLDER;
598 if (e_source_has_extension (child_source, extension_name)) {
599 ESourceMapiFolder *extension;
600 gchar *folder_id;
601
602 extension = e_source_get_extension (
603 child_source, extension_name);
604 folder_id = e_mapi_util_mapi_id_to_string (e_source_mapi_folder_get_id (extension));
605 if (folder_id != NULL) {
606 EMapiBackend *mapi_backend = E_MAPI_BACKEND (backend);
607
608 g_hash_table_insert (
609 mapi_backend->priv->folders, folder_id,
610 g_object_ref (child_source));
611 }
612 }
613
614 /* Chain up to parent's child_added() method. */
615 E_COLLECTION_BACKEND_CLASS (e_mapi_backend_parent_class)->
616 child_added (backend, child_source);
617 }
618
619 static void
620 mapi_backend_child_removed (ECollectionBackend *backend,
621 ESource *child_source)
622 {
623 const gchar *extension_name;
624
625 /* We track MAPI folders in a hash table by folder ID. */
626 extension_name = E_SOURCE_EXTENSION_MAPI_FOLDER;
627 if (e_source_has_extension (child_source, extension_name)) {
628 ESourceMapiFolder *extension;
629 gchar *folder_id;
630
631 extension = e_source_get_extension (child_source, extension_name);
632 folder_id = e_mapi_util_mapi_id_to_string (e_source_mapi_folder_get_id (extension));
633 if (folder_id != NULL) {
634 EMapiBackend *mapi_backend = E_MAPI_BACKEND (backend);
635
636 g_hash_table_remove (mapi_backend->priv->folders, folder_id);
637 }
638 g_free (folder_id);
639 }
640
641 /* Chain up to parent's child_removed() method. */
642 E_COLLECTION_BACKEND_CLASS (e_mapi_backend_parent_class)->
643 child_removed (backend, child_source);
644 }
645
646 static gboolean
647 mapi_backend_create_resource_cb (EBackend *backend,
648 CamelMapiSettings *settings,
649 EMapiConnection *conn,
650 gpointer user_data,
651 GCancellable *cancellable,
652 GError **error)
653 {
654 ESourceBackend *backend_ext = NULL;
655 const gchar *folder_type_str = NULL;
656 ESource *source = user_data;
657 ESourceMapiFolder *folder_ext;
658 mapi_object_t obj_folder;
659 const gchar *foreign_username;
660 gboolean res = FALSE;
661 guint64 fid;
662
663 g_return_val_if_fail (e_source_has_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER), FALSE);
664
665 folder_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER);
666 foreign_username = e_source_mapi_folder_get_foreign_username (folder_ext);
667
668 fid = e_source_mapi_folder_get_id (folder_ext);
669 g_return_val_if_fail (fid == 0, FALSE);
670
671 if (e_source_has_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK)) {
672 backend_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK);
673 folder_type_str = IPF_CONTACT;
674 } else if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR)) {
675 backend_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR);
676 folder_type_str = IPF_APPOINTMENT;
677 } else if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST)) {
678 backend_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_TASK_LIST);
679 folder_type_str = IPF_TASK;
680 } else if (e_source_has_extension (source, E_SOURCE_EXTENSION_MEMO_LIST)) {
681 backend_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_MEMO_LIST);
682 folder_type_str = IPF_STICKYNOTE;
683 }
684
685 if (!backend_ext || g_strcmp0 (e_source_backend_get_backend_name (backend_ext), "mapi") != 0)
686 return FALSE;
687
688 fid = e_source_mapi_folder_get_parent_id (folder_ext);
689
690 if (foreign_username && *foreign_username)
691 res = e_mapi_connection_open_foreign_folder (conn, foreign_username, fid, &obj_folder, cancellable, error);
692 else if (e_source_mapi_folder_is_public (folder_ext))
693 res = e_mapi_connection_open_public_folder (conn, fid, &obj_folder, cancellable, error);
694 else
695 res = e_mapi_connection_open_personal_folder (conn, fid, &obj_folder, cancellable, error);
696
697 if (res) {
698 fid = 0;
699 if (!e_mapi_connection_create_folder (conn, &obj_folder, e_source_get_display_name (source), folder_type_str, &fid, cancellable, error))
700 fid = 0;
701 e_mapi_connection_close_folder (conn, &obj_folder, cancellable, error);
702
703 if (fid)
704 e_source_mapi_folder_set_id (folder_ext, fid);
705 else
706 res = FALSE;
707 }
708
709 return res;
710 }
711
712 static gboolean
713 mapi_backend_create_resource_sync (ECollectionBackend *backend,
714 ESource *source,
715 GCancellable *cancellable,
716 GError **error)
717 {
718 ESourceRegistryServer *server;
719 ESource *parent_source;
720 CamelMapiSettings *settings;
721 ESourceMapiFolder *folder_ext;
722 EMapiBackend *mapi_backend;
723 ENamedParameters *credentials;
724 const gchar *foreign_username;
725 const gchar *cache_dir;
726 const gchar *parent_uid;
727
728 if (!e_source_has_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER)) {
729 g_set_error (
730 error, G_IO_ERROR,
731 G_IO_ERROR_INVALID_ARGUMENT,
732 _("Data source “%s” does not represent a MAPI folder"),
733 e_source_get_display_name (source));
734 return FALSE;
735 }
736
737 mapi_backend = E_MAPI_BACKEND (backend);
738 settings = mapi_backend_get_settings (mapi_backend);
739 g_return_val_if_fail (settings != NULL, FALSE);
740
741 folder_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER);
742 foreign_username = e_source_mapi_folder_get_foreign_username (folder_ext);
743
744 g_mutex_lock (&mapi_backend->priv->credentials_lock);
745 credentials = mapi_backend->priv->credentials ? e_named_parameters_new_clone (mapi_backend->priv->credentials) : NULL;
746 g_mutex_unlock (&mapi_backend->priv->credentials_lock);
747
748 if (!e_source_mapi_folder_is_public (folder_ext) &&
749 !(foreign_username && *foreign_username) &&
750 !e_mapi_backend_authenticator_run (
751 E_BACKEND (backend), settings, credentials, mapi_backend_create_resource_cb, source, cancellable, error)) {
752 e_named_parameters_free (credentials);
753 return FALSE;
754 }
755
756 e_named_parameters_free (credentials);
757
758 /* Configure the source as a collection member. */
759 parent_source = e_backend_get_source (E_BACKEND (backend));
760 parent_uid = e_source_get_uid (parent_source);
761 e_source_set_parent (source, parent_uid);
762
763 /* Changes should be written back to the cache directory. */
764 cache_dir = e_collection_backend_get_cache_dir (backend);
765 e_server_side_source_set_write_directory (
766 E_SERVER_SIDE_SOURCE (source), cache_dir);
767
768 /* Set permissions for clients. */
769 e_server_side_source_set_writable (
770 E_SERVER_SIDE_SOURCE (source), TRUE);
771 e_server_side_source_set_remote_deletable (
772 E_SERVER_SIDE_SOURCE (source), TRUE);
773
774 server = e_collection_backend_ref_server (backend);
775 e_source_registry_server_add_source (server, source);
776 g_object_unref (server);
777
778 return TRUE;
779 }
780
781 static gboolean
782 mapi_backend_delete_resource_cb (EBackend *backend,
783 CamelMapiSettings *settings,
784 EMapiConnection *conn,
785 gpointer user_data,
786 GCancellable *cancellable,
787 GError **error)
788 {
789 ESource *source = user_data;
790 ESourceMapiFolder *folder_ext;
791 mapi_object_t *obj_store = NULL;
792 const gchar *foreign_username;
793 gboolean res = FALSE;
794 guint64 fid;
795
796 g_return_val_if_fail (e_source_has_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER), FALSE);
797
798 folder_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER);
799 g_return_val_if_fail (!e_source_mapi_folder_is_public (folder_ext), FALSE);
800
801 foreign_username = e_source_mapi_folder_get_foreign_username (folder_ext);
802 g_return_val_if_fail (!foreign_username || !*foreign_username, FALSE);
803
804 fid = e_source_mapi_folder_get_id (folder_ext);
805 g_return_val_if_fail (fid != 0, FALSE);
806
807 if (e_mapi_connection_peek_store (conn, FALSE, NULL, &obj_store, cancellable, error))
808 res = e_mapi_connection_remove_folder (conn, obj_store, fid, cancellable, error);
809
810 return res;
811 }
812
813 static gboolean
814 mapi_backend_delete_resource_sync (ECollectionBackend *backend,
815 ESource *source,
816 GCancellable *cancellable,
817 GError **error)
818 {
819 CamelMapiSettings *settings;
820 ESourceMapiFolder *folder_ext;
821 EMapiBackend *mapi_backend;
822 const gchar *foreign_username;
823 ENamedParameters *credentials;
824
825 if (!e_source_has_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER)) {
826 g_set_error (
827 error, G_IO_ERROR,
828 G_IO_ERROR_INVALID_ARGUMENT,
829 _("Data source “%s” does not represent a MAPI folder"),
830 e_source_get_display_name (source));
831 return FALSE;
832 }
833
834 mapi_backend = E_MAPI_BACKEND (backend);
835 settings = mapi_backend_get_settings (mapi_backend);
836 g_return_val_if_fail (settings != NULL, FALSE);
837
838 folder_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER);
839 foreign_username = e_source_mapi_folder_get_foreign_username (folder_ext);
840
841 g_mutex_lock (&mapi_backend->priv->credentials_lock);
842 credentials = mapi_backend->priv->credentials ? e_named_parameters_new_clone (mapi_backend->priv->credentials) : NULL;
843 g_mutex_unlock (&mapi_backend->priv->credentials_lock);
844
845 if (!e_source_mapi_folder_is_public (folder_ext) &&
846 !(foreign_username && *foreign_username) &&
847 !e_mapi_backend_authenticator_run (
848 E_BACKEND (backend), settings, credentials, mapi_backend_delete_resource_cb, source, cancellable, error)) {
849 e_named_parameters_free (credentials);
850 return FALSE;
851 }
852
853 e_named_parameters_free (credentials);
854
855 return e_source_remove_sync (source, cancellable, error);
856 }
857
858 static ESourceAuthenticationResult
859 mapi_backend_authenticate_sync (EBackend *backend,
860 const ENamedParameters *credentials,
861 gchar **out_certificate_pem,
862 GTlsCertificateFlags *out_certificate_errors,
863 GCancellable *cancellable,
864 GError **error)
865 {
866 EMapiBackend *mapi_backend;
867 EMapiConnection *conn;
868 CamelMapiSettings *settings;
869 GSList *mapi_folders = NULL;
870 gboolean in_sync_folders = FALSE;
871 GError *mapi_error = NULL, *krb_error = NULL;
872
873 g_return_val_if_fail (E_IS_MAPI_BACKEND (backend), E_SOURCE_AUTHENTICATION_ERROR);
874
875 e_collection_backend_freeze_populate (E_COLLECTION_BACKEND (backend));
876
877 mapi_backend = E_MAPI_BACKEND (backend);
878 settings = mapi_backend_get_settings (mapi_backend);
879
880 if (camel_mapi_settings_get_kerberos (settings))
881 e_mapi_util_trigger_krb_auth_from_settings (settings, &krb_error);
882
883 conn = e_mapi_connection_new (NULL,
884 camel_mapi_settings_get_profile (settings),
885 credentials, cancellable, &mapi_error);
886
887 if (!conn) {
888 ESourceAuthenticationResult res = E_SOURCE_AUTHENTICATION_ERROR;
889
890 mapi_backend->priv->need_update_folders = TRUE;
891
892 if (g_error_matches (mapi_error, E_MAPI_ERROR, MAPI_E_PASSWORD_CHANGE_REQUIRED) ||
893 g_error_matches (mapi_error, E_MAPI_ERROR, MAPI_E_PASSWORD_EXPIRED)) {
894 res = E_SOURCE_AUTHENTICATION_REJECTED;
895 } else if ((!mapi_error || mapi_error->domain == E_MAPI_ERROR) &&
896 (!credentials || !e_named_parameters_count (credentials)) &&
897 !camel_mapi_settings_get_kerberos (settings)) {
898 res = E_SOURCE_AUTHENTICATION_REQUIRED;
899 }
900
901 if (res == E_SOURCE_AUTHENTICATION_ERROR) {
902 if (krb_error) {
903 GError *new_error;
904
905 if (mapi_error) {
906 new_error = g_error_new (mapi_error->domain, mapi_error->code,
907 /* Translators: the first '%s' is replaced with a generic error message,
908 the second '%s' is replaced with additional error information. */
909 C_("gssapi_error", "%s (%s)"), mapi_error->message, krb_error->message);
910 } else {
911 new_error = g_error_copy (krb_error);
912 }
913
914 g_clear_error (&mapi_error);
915 mapi_error = new_error;
916 }
917
918 g_propagate_error (error, mapi_error);
919 } else {
920 g_clear_error (&mapi_error);
921 }
922
923 g_clear_error (&krb_error);
924
925 e_collection_backend_thaw_populate (E_COLLECTION_BACKEND (backend));
926
927 return res;
928 }
929
930 if (e_mapi_connection_get_folders_list (conn, &mapi_folders, NULL, NULL, cancellable, &mapi_error)) {
931 struct SyndFoldersData *sfd;
932
933 g_mutex_lock (&mapi_backend->priv->credentials_lock);
934 e_named_parameters_free (mapi_backend->priv->credentials);
935 mapi_backend->priv->credentials = credentials ? e_named_parameters_new_clone (credentials) : NULL;
936 g_mutex_unlock (&mapi_backend->priv->credentials_lock);
937
938 sfd = g_slice_new0 (struct SyndFoldersData);
939 sfd->folders = mapi_folders;
940 sfd->backend = g_object_ref (mapi_backend);
941 sfd->profile = camel_mapi_settings_dup_profile (settings);
942
943 g_idle_add_full (
944 G_PRIORITY_DEFAULT_IDLE,
945 mapi_backend_sync_folders_idle_cb, sfd,
946 sync_folders_data_free);
947
948 e_collection_backend_authenticate_children (E_COLLECTION_BACKEND (backend), credentials);
949
950 in_sync_folders = TRUE;
951 } else {
952 ESource *source = e_backend_get_source (backend);
953
954 mapi_backend->priv->need_update_folders = TRUE;
955
956 g_message ("%s: Failed to get list of user's folders for '%s': %s",
957 G_STRFUNC, e_source_get_display_name (source), mapi_error ? mapi_error->message : "Unknown error");
958 }
959
960 g_object_unref (conn);
961 g_clear_error (&mapi_error);
962 g_clear_error (&krb_error);
963
964 if (!in_sync_folders)
965 e_collection_backend_thaw_populate (E_COLLECTION_BACKEND (backend));
966
967 return E_SOURCE_AUTHENTICATION_ACCEPTED;
968 }
969
970 static void
971 e_mapi_backend_class_init (EMapiBackendClass *class)
972 {
973 GObjectClass *object_class;
974 EBackendClass *backend_class;
975 ECollectionBackendClass *collection_backend_class;
976
977 object_class = G_OBJECT_CLASS (class);
978 object_class->constructed = mapi_backend_constructed;
979 object_class->dispose = mapi_backend_dispose;
980 object_class->finalize = mapi_backend_finalize;
981
982 backend_class = E_BACKEND_CLASS (class);
983 backend_class->authenticate_sync = mapi_backend_authenticate_sync;
984
985 collection_backend_class = E_COLLECTION_BACKEND_CLASS (class);
986 collection_backend_class->populate = mapi_backend_populate;
987 collection_backend_class->dup_resource_id = mapi_backend_dup_resource_id;
988 collection_backend_class->child_added = mapi_backend_child_added;
989 collection_backend_class->child_removed = mapi_backend_child_removed;
990 collection_backend_class->create_resource_sync = mapi_backend_create_resource_sync;
991 collection_backend_class->delete_resource_sync = mapi_backend_delete_resource_sync;
992
993 /* This generates an ESourceCamel subtype for CamelMapiSettings. */
994 e_source_camel_generate_subtype ("mapi", CAMEL_TYPE_MAPI_SETTINGS);
995 }
996
997 static void
998 e_mapi_backend_class_finalize (EMapiBackendClass *class)
999 {
1000 }
1001
1002 static void
1003 e_mapi_backend_init (EMapiBackend *backend)
1004 {
1005 backend->priv = e_mapi_backend_get_instance_private (backend);
1006
1007 backend->priv->folders = g_hash_table_new_full (
1008 g_str_hash,
1009 g_str_equal,
1010 g_free,
1011 g_object_unref);
1012
1013 g_mutex_init (&backend->priv->credentials_lock);
1014 backend->priv->credentials = NULL;
1015 }
1016
1017 void
1018 e_mapi_backend_type_register (GTypeModule *type_module)
1019 {
1020 /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
1021 * function, so we have to wrap it with a public function in
1022 * order to register types from a separate compilation unit. */
1023 e_mapi_backend_register_type (type_module);
1024 }