"Fossies" - the Fresh Open Source Software Archive 
Member "evolution-mapi-3.46.1/src/camel/camel-mapi-store.c" (2 Dec 2022, 106716 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 "camel-mapi-store.c" see the
Fossies "Dox" file reference documentation and the last
Fossies "Diffs" side-by-side code changes report:
3.44.2_vs_3.45.1.
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2 of the License, or (at your option) version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with the program; if not, see <http://www.gnu.org/licenses/>
15 *
16 *
17 * Authors:
18 * Johnny Jacob <jjohnny@novell.com>
19 *
20 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
21 *
22 */
23
24 #include "evolution-mapi-config.h"
25
26 #include <stdint.h>
27 #include <stdbool.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <ctype.h>
32 #include <sys/types.h>
33 #include <errno.h>
34
35 #include <libmapi/libmapi.h>
36
37 #include <glib/gi18n-lib.h>
38 #include <glib/gstdio.h>
39
40 #include "camel-mapi-store.h"
41 #include "camel-mapi-folder.h"
42 #include "camel-mapi-sasl-krb.h"
43 #include "camel-mapi-settings.h"
44 #include "camel-mapi-store-summary.h"
45 #include "camel-mapi-folder-summary.h"
46
47 #include <e-mapi-utils.h>
48 #include <e-mapi-folder.h>
49
50 #define d(x)
51
52 struct _CamelMapiStorePrivate {
53 EMapiConnection *connection;
54 GRecMutex connection_lock;
55
56 GHashTable *id_hash; /*get names from ids*/
57 GHashTable *name_hash;/*get ids from names*/
58 GHashTable *container_hash;
59 GHashTable *parent_hash;
60 GHashTable *default_folders; /*Default Type : Folder ID*/
61
62 gboolean folders_synced; /* whether were synced folder list already */
63
64 GRecMutex updates_lock;
65 GCancellable *updates_cancellable; /* cancelled on dispose or disconnect */
66 GSList *update_folder_names; /* gchar *foldername */
67 guint update_folder_id;
68 guint update_folder_list_id;
69 };
70
71 /* Forward Declarations */
72 static void camel_subscribable_init (CamelSubscribableInterface *iface);
73
74 G_DEFINE_TYPE_WITH_CODE (
75 CamelMapiStore,
76 camel_mapi_store,
77 CAMEL_TYPE_OFFLINE_STORE,
78 G_ADD_PRIVATE (CamelMapiStore)
79 G_IMPLEMENT_INTERFACE (
80 CAMEL_TYPE_SUBSCRIBABLE,
81 camel_subscribable_init))
82
83 /* service methods */
84 static void mapi_store_constructed (GObject *object);
85 static gchar *mapi_get_name(CamelService *, gboolean );
86 static gboolean mapi_connect_sync(CamelService *, GCancellable *cancellable, GError **);
87 static gboolean mapi_disconnect_sync(CamelService *, gboolean , GCancellable *cancellable, GError **);
88 static CamelAuthenticationResult mapi_authenticate_sync (CamelService *, const gchar *mechanism, GCancellable *, GError **);
89 static GList *mapi_query_auth_types_sync(CamelService *, GCancellable *cancellable, GError **);
90 static void camel_mapi_store_server_notification_cb (EMapiConnection *conn, guint event_mask, gpointer event_data, gpointer user_data);
91
92 /* store methods */
93 static CamelFolderInfo * mapi_build_folder_info(CamelMapiStore *mapi_store, const gchar *parent_name, const gchar *folder_name);
94 static gboolean mapi_fid_is_system_folder (CamelMapiStore *mapi_store, const gchar *fid);
95 static void mapi_update_hash_table_type (CamelMapiStore *store, const gchar *full_name, guint *folder_type);
96
97 static void mapi_update_folder_hash_tables (CamelMapiStore *store, const gchar *name, const gchar *fid, const gchar *parent_id);
98 guint mapi_folders_hash_table_type_lookup (CamelMapiStore *store, const gchar *name);
99 /* static const gchar * mapi_folders_hash_table_name_lookup (CamelMapiStore *store, const gchar *fid, gboolean use_cache); */
100 #if 0
101 static const gchar * mapi_folders_hash_table_fid_lookup (CamelMapiStore *store, const gchar *name, gboolean use_cache);
102 #endif
103
104 static CamelFolderInfo *
105 mapi_store_create_folder_sync (CamelStore *store,
106 const gchar *parent_name,
107 const gchar *folder_name,
108 GCancellable *cancellable,
109 GError **error);
110
111 static gboolean
112 cms_open_folder (CamelMapiStore *mapi_store,
113 EMapiConnection *conn,
114 mapi_id_t fid,
115 mapi_object_t *obj_folder,
116 GCancellable *cancellable,
117 GError **perror)
118 {
119 CamelStoreInfo *si;
120 CamelMapiStoreInfo *msi;
121 GError *mapi_error = NULL;
122 gboolean res;
123
124 g_return_val_if_fail (mapi_store != NULL, FALSE);
125 g_return_val_if_fail (mapi_store->summary != NULL, FALSE);
126 g_return_val_if_fail (conn != NULL, FALSE);
127 g_return_val_if_fail (fid != 0, FALSE);
128 g_return_val_if_fail (obj_folder != NULL, FALSE);
129
130 si = camel_mapi_store_summary_get_folder_id (mapi_store->summary, fid);
131 if (!si) {
132 g_propagate_error (perror, g_error_new_literal (CAMEL_ERROR, CAMEL_ERROR_GENERIC, _("Cannot find folder in a local cache")));
133 return FALSE;
134 }
135
136 msi = (CamelMapiStoreInfo *) si;
137
138 if ((msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_FOREIGN) != 0)
139 res = e_mapi_connection_open_foreign_folder (conn, msi->foreign_username, fid, obj_folder, cancellable, &mapi_error);
140 else if ((msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_PUBLIC) != 0)
141 res = e_mapi_connection_open_public_folder (conn, fid, obj_folder, cancellable, &mapi_error);
142 else
143 res = e_mapi_connection_open_personal_folder (conn, fid, obj_folder, cancellable, &mapi_error);
144
145 if (mapi_error) {
146 camel_mapi_store_maybe_disconnect (mapi_store, mapi_error);
147 g_propagate_error (perror, mapi_error);
148 }
149
150 return res;
151 }
152
153 static gboolean
154 cms_peek_folder_store (CamelMapiStore *mapi_store,
155 EMapiConnection *conn,
156 mapi_id_t fid,
157 mapi_object_t **obj_store,
158 GCancellable *cancellable,
159 GError **perror)
160 {
161 CamelStoreInfo *si;
162 CamelMapiStoreInfo *msi;
163 GError *mapi_error = NULL;
164 gboolean res;
165
166 g_return_val_if_fail (mapi_store != NULL, FALSE);
167 g_return_val_if_fail (mapi_store->summary != NULL, FALSE);
168 g_return_val_if_fail (conn != NULL, FALSE);
169 g_return_val_if_fail (fid != 0, FALSE);
170 g_return_val_if_fail (obj_store != NULL, FALSE);
171
172 si = camel_mapi_store_summary_get_folder_id (mapi_store->summary, fid);
173 if (!si) {
174 g_propagate_error (perror, g_error_new_literal (CAMEL_ERROR, CAMEL_ERROR_GENERIC, _("Cannot find folder in a local cache")));
175 return FALSE;
176 }
177
178 msi = (CamelMapiStoreInfo *) si;
179
180 if ((msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_FOREIGN) != 0)
181 res = e_mapi_connection_peek_store (conn, FALSE, msi->foreign_username, obj_store, cancellable, &mapi_error);
182 else if ((msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_PUBLIC) != 0)
183 res = e_mapi_connection_peek_store (conn, TRUE, NULL, obj_store, cancellable, &mapi_error);
184 else
185 res = e_mapi_connection_peek_store (conn, FALSE, NULL, obj_store, cancellable, &mapi_error);
186
187 if (mapi_error) {
188 camel_mapi_store_maybe_disconnect (mapi_store, mapi_error);
189 g_propagate_error (perror, mapi_error);
190 }
191
192 return res;
193 }
194
195 static gboolean
196 check_for_connection (CamelService *service, GError **error)
197 {
198 CamelMapiStore *mapi_store = CAMEL_MAPI_STORE (service);
199 gboolean connected;
200
201 if (!mapi_store)
202 return FALSE;
203
204 g_rec_mutex_lock (&mapi_store->priv->connection_lock);
205 connected = mapi_store->priv->connection && e_mapi_connection_connected (mapi_store->priv->connection);
206 g_rec_mutex_unlock (&mapi_store->priv->connection_lock);
207
208 return connected;
209 }
210
211 /* escapes backslashes with \5C and forward slashes with \2F */
212 static gchar *
213 escape_slash (const gchar *str)
214 {
215 gint ii, jj, count = 0;
216 gchar *res;
217
218 if (!str)
219 return NULL;
220
221 for (ii = 0; str[ii]; ii++) {
222 if (str[ii] == '\\' || str[ii] == '/')
223 count++;
224 }
225
226 if (!count)
227 return g_strdup (str);
228
229 res = g_malloc0 (sizeof (gchar) * (1 + ii + (2 * count)));
230 for (ii = 0, jj = 0; str[ii]; ii++, jj++) {
231 if (str[ii] == '\\') {
232 res[jj] = '\\';
233 res[jj + 1] = '5';
234 res[jj + 2] = 'C';
235 jj += 2;
236 } else if (str[ii] == '/') {
237 res[jj] = '\\';
238 res[jj + 1] = '2';
239 res[jj + 2] = 'F';
240 jj += 2;
241 } else {
242 res[jj] = str[ii];
243 }
244 }
245
246 res[jj] = '\0';
247
248 return res;
249 }
250
251 /* reverses escape_slash processing */
252 static gchar *
253 unescape_slash (const gchar *str)
254 {
255 gchar *res = g_strdup (str);
256 gint ii, jj;
257
258 for (ii = 0, jj = 0; res[ii]; ii++, jj++) {
259 if (res[ii] == '\\' && g_ascii_isxdigit (res[ii + 1]) && g_ascii_isxdigit (res[ii + 2])) {
260 res[jj] = ((g_ascii_xdigit_value (res[ii + 1]) & 0xF) << 4) | (g_ascii_xdigit_value (res[ii + 2]) & 0xF);
261 ii += 2;
262 } else if (ii != jj) {
263 res[jj] = res[ii];
264 }
265 }
266
267 res[jj] = '\0';
268
269 return res;
270 }
271
272 static CamelFolder *
273 mapi_get_folder_with_type (CamelStore *store, guint folder_type, GCancellable *cancellable, GError **error)
274 {
275 CamelMapiStore *mapi_store = CAMEL_MAPI_STORE (store);
276 CamelFolderInfo *all_fi, *fi;
277 CamelFolder *folder = NULL;
278
279 g_return_val_if_fail (mapi_store != NULL, NULL);
280 g_return_val_if_fail (mapi_store->priv != NULL, NULL);
281
282 all_fi = camel_store_get_folder_info_sync (
283 store, NULL, CAMEL_STORE_FOLDER_INFO_RECURSIVE,
284 cancellable, error);
285 if (all_fi == NULL)
286 return NULL;
287
288 fi = all_fi;
289 while (fi) {
290 CamelFolderInfo *next;
291
292 if ((fi->flags & CAMEL_FOLDER_TYPE_MASK) == folder_type) {
293 folder = camel_store_get_folder_sync (
294 store, fi->full_name, 0, cancellable, error);
295 break;
296 }
297
298 /* move to the next, depth-first search */
299 next = fi->child;
300 if (!next)
301 next = fi->next;
302 if (!next) {
303 next = fi->parent;
304 while (next) {
305 CamelFolderInfo *sibl = next->next;
306 if (sibl) {
307 next = sibl;
308 break;
309 } else {
310 next = next->parent;
311 }
312 }
313 }
314
315 fi = next;
316 }
317
318 camel_folder_info_free (all_fi);
319
320 return folder;
321 }
322
323 static CamelFolderInfo *
324 mapi_convert_to_folder_info (CamelMapiStore *store,
325 EMapiFolder *folder,
326 GError **error)
327 {
328 gchar *name;
329 gchar *parent, *id = NULL;
330 mapi_id_t mapi_id_folder;
331 const gchar *par_name = NULL;
332 CamelFolderInfo *fi;
333
334 name = escape_slash (e_mapi_folder_get_name (folder));
335
336 id = g_strdup_printf ("%016" G_GINT64_MODIFIER "X", e_mapi_folder_get_id (folder));
337
338 fi = camel_folder_info_new ();
339
340 if (folder->is_default) {
341 switch (folder->default_type) {
342 case olFolderTopInformationStore:
343 fi->flags |= CAMEL_FOLDER_NOSELECT;
344 break;
345 case olFolderInbox:
346 fi->flags |= CAMEL_FOLDER_TYPE_INBOX;
347 break;
348 case olFolderSentMail:
349 fi->flags |= CAMEL_FOLDER_TYPE_SENT;
350 break;
351 case olFolderDeletedItems:
352 fi->flags |= CAMEL_FOLDER_TYPE_TRASH;
353 break;
354 case olFolderOutbox:
355 fi->flags |= CAMEL_FOLDER_TYPE_OUTBOX;
356 break;
357 case olFolderJunk:
358 fi->flags |= CAMEL_FOLDER_TYPE_JUNK;
359 break;
360 }
361
362 fi->flags |= CAMEL_FOLDER_SYSTEM;
363 } else {
364 switch (e_mapi_folder_get_type (folder)) {
365 case E_MAPI_FOLDER_TYPE_CONTACT:
366 fi->flags |= CAMEL_FOLDER_TYPE_CONTACTS;
367 break;
368 case E_MAPI_FOLDER_TYPE_APPOINTMENT:
369 fi->flags |= CAMEL_FOLDER_TYPE_EVENTS;
370 break;
371 case E_MAPI_FOLDER_TYPE_MEMO:
372 fi->flags |= CAMEL_FOLDER_TYPE_MEMOS;
373 break;
374 case E_MAPI_FOLDER_TYPE_TASK:
375 fi->flags |= CAMEL_FOLDER_TYPE_TASKS;
376 break;
377 default:
378 break;
379 }
380 }
381
382 if (folder->child_count <= 0)
383 fi->flags |= CAMEL_FOLDER_NOCHILDREN;
384 /*
385 parent_hash contains the "parent id <-> folder id" combination. So we form
386 the path for the full name in camelfolder info by looking up the hash table until
387 NULL is found
388 */
389
390 mapi_id_folder = e_mapi_folder_get_parent_id (folder);
391 parent = g_strdup_printf ("%016" G_GINT64_MODIFIER "X", mapi_id_folder);
392
393 fi->display_name = name;
394
395 par_name = mapi_folders_hash_table_name_lookup (store, parent, TRUE);
396 if (par_name != NULL) {
397 gchar *str = g_strconcat (par_name, "/", name, NULL);
398
399 fi->full_name = str; /* takes ownership of the string */
400 } else {
401 fi->full_name = g_strdup (name);
402 }
403
404 /*name_hash returns the container id given the name */
405 mapi_update_folder_hash_tables (store, fi->full_name, id, parent);
406
407 g_free (parent);
408 g_free (id);
409
410 fi->total = folder->total;
411 fi->unread = folder->unread_count;
412
413 return fi;
414 }
415
416 static void
417 remove_path_from_store_summary (const gchar *path, gpointer value, CamelMapiStore *mstore)
418 {
419 const gchar *folder_id;
420 CamelStoreInfo *si;
421
422 g_return_if_fail (path != NULL);
423 g_return_if_fail (mstore != NULL);
424
425 folder_id = g_hash_table_lookup (mstore->priv->name_hash, path);
426 if (folder_id) {
427 /* name_hash as the second, because folder_id is from there */
428 g_hash_table_remove (mstore->priv->id_hash, folder_id);
429 g_hash_table_remove (mstore->priv->name_hash, path);
430 }
431
432 si = camel_store_summary_path (mstore->summary, path);
433 if (si) {
434 CamelFolderInfo *fi;
435
436 fi = camel_folder_info_new ();
437 fi->unread = -1;
438 fi->total = -1;
439 fi->display_name = g_strdup (camel_store_info_get_name (si));
440 fi->full_name = g_strdup (camel_store_info_get_path (si));
441 if (!fi->display_name && fi->full_name) {
442 fi->display_name = strrchr (fi->full_name, '/');
443 if (fi->display_name)
444 fi->display_name = g_strdup (fi->display_name + 1);
445 }
446
447 camel_subscribable_folder_unsubscribed (CAMEL_SUBSCRIBABLE (mstore), fi);
448 camel_store_folder_deleted (CAMEL_STORE (mstore), fi);
449 camel_folder_info_free (fi);
450
451 camel_store_info_unref (si);
452 }
453
454 camel_store_summary_remove_path (mstore->summary, path);
455 }
456
457 static gboolean
458 camel_mapi_update_operation_progress_cb (EMapiConnection *conn,
459 guint32 item_index,
460 guint32 items_total,
461 gpointer user_data,
462 GCancellable *cancellable,
463 GError **perror)
464 {
465 if (items_total > 0)
466 camel_operation_progress (cancellable, 100 * item_index / items_total);
467
468 return TRUE;
469 }
470
471 static gboolean
472 mapi_folders_sync (CamelMapiStore *store, guint32 flags, GCancellable *cancellable, GError **error)
473 {
474 CamelMapiStorePrivate *priv = store->priv;
475 gboolean status;
476 GSList *folder_list = NULL, *temp_list = NULL, *list = NULL;
477 gboolean subscription_list = FALSE;
478 CamelFolderInfo *info = NULL;
479 CamelMapiStoreInfo *msi = NULL;
480 GHashTable *old_cache_folders;
481 GError *err = NULL;
482 EMapiConnection *conn;
483 GPtrArray *array;
484 gint ii;
485
486 if (!camel_mapi_store_connected (store, cancellable, NULL)) {
487 g_set_error_literal (
488 error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_UNAVAILABLE,
489 _("Folder list is not available in offline mode"));
490 return FALSE;
491 }
492
493 conn = camel_mapi_store_ref_connection (store, cancellable, error);
494 if (!conn)
495 return FALSE;
496
497 status = e_mapi_connection_get_folders_list (conn, &folder_list, camel_mapi_update_operation_progress_cb, NULL, cancellable, &err);
498 if (!status) {
499 camel_mapi_store_maybe_disconnect (store, err);
500
501 g_warning ("Could not get folder list (%s)\n", err ? err->message : "Unknown error");
502 g_clear_error (&err);
503 g_object_unref (conn);
504 return TRUE;
505 }
506
507 /* remember all folders in cache before update */
508 old_cache_folders = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
509 array = camel_store_summary_array (store->summary);
510 for (ii = 0; ii < array->len; ii++) {
511 msi = g_ptr_array_index (array, ii);
512
513 /* those whose left in old_cache_folders are removed at the end,
514 which is not good for public and foreign folders, thus preserve
515 them from an automatic removal */
516 if (((msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_PUBLIC) == 0 &&
517 (msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_FOREIGN) == 0) ||
518 ((msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_PUBLIC_REAL) != 0 &&
519 (msi->mapi_folder_flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) == 0))
520 g_hash_table_insert (old_cache_folders, g_strdup (camel_store_info_get_path ((CamelStoreInfo *) msi)), GINT_TO_POINTER (1));
521 }
522 camel_store_summary_array_free (store->summary, array);
523
524 subscription_list = (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST);
525 if (subscription_list) {
526 GError *err = NULL;
527
528 /*Consult the name <-> fid hash table for a FID.*/
529 status = e_mapi_connection_get_pf_folders_list (conn, &folder_list, camel_mapi_update_operation_progress_cb, NULL, cancellable, &err);
530 if (!status)
531 g_warning ("Could not get Public folder list (%s)\n", err ? err->message : "Unknown error");
532
533 camel_mapi_store_maybe_disconnect (store, err);
534 g_clear_error (&err);
535 }
536
537 temp_list = folder_list;
538 list = folder_list;
539
540 /*populate the hash table for finding the mapping from container id <-> folder name*/
541 for (;temp_list != NULL; temp_list = g_slist_next (temp_list) ) {
542 const gchar *full_name = NULL;
543 gchar *fid = NULL, *parent_id = NULL, *tmp = NULL;
544 guint *folder_type = g_new0 (guint, 1);
545
546 fid = g_strdup_printf ("%016" G_GINT64_MODIFIER "X", e_mapi_folder_get_id ((EMapiFolder *)(temp_list->data)));
547 parent_id = g_strdup_printf ("%016" G_GINT64_MODIFIER "X", e_mapi_folder_get_parent_id ((EMapiFolder *)(temp_list->data)));
548 full_name = g_hash_table_lookup (priv->id_hash, fid);
549 if (!full_name) {
550 const gchar *par_full_name;
551
552 par_full_name = g_hash_table_lookup (priv->id_hash, parent_id);
553 if (par_full_name) {
554 gchar *escaped = escape_slash (e_mapi_folder_get_name (temp_list->data));
555 tmp = g_strconcat (par_full_name, "/", escaped, NULL);
556 full_name = tmp;
557 g_free (escaped);
558 } else {
559 tmp = escape_slash (e_mapi_folder_get_name (temp_list->data));
560 full_name = tmp;
561 }
562 } else {
563 /* known full_name - everything is escaped already */
564 tmp = g_strdup (full_name);
565 full_name = tmp;
566 }
567
568 /* remove from here; what lefts is not on the server any more */
569 g_hash_table_remove (old_cache_folders, full_name);
570 *folder_type = ((EMapiFolder *)(temp_list->data))->container_class;
571 mapi_update_folder_hash_tables (store, full_name, fid, parent_id);
572 mapi_update_hash_table_type (store, full_name, folder_type);
573 if (((EMapiFolder *)(temp_list->data))->is_default) {
574 guint *type = g_new0 (guint, 1);
575 *type = ((EMapiFolder *)(temp_list->data))->default_type;
576 g_hash_table_insert (priv->default_folders, type,
577 g_strdup(fid));
578 }
579 g_free (fid);
580 g_free (parent_id);
581 g_free (tmp);
582 }
583
584 for (;folder_list != NULL; folder_list = g_slist_next (folder_list)) {
585 EMapiFolder *folder = (EMapiFolder *) folder_list->data;
586
587 if (folder->default_type == olPublicFoldersAllPublicFolders)
588 continue;
589
590 if (folder->container_class == E_MAPI_FOLDER_TYPE_MAIL) {
591 info = mapi_convert_to_folder_info (store, folder, NULL);
592 msi = (CamelMapiStoreInfo *) camel_store_summary_path (store->summary, info->full_name);
593
594 if (!msi) {
595 msi = (CamelMapiStoreInfo *) camel_mapi_store_summary_add_from_full (store->summary,
596 info->full_name,
597 e_mapi_folder_get_id (folder),
598 e_mapi_folder_get_parent_id (folder),
599 info->flags,
600 folder->category == E_MAPI_FOLDER_CATEGORY_PERSONAL ? CAMEL_MAPI_STORE_FOLDER_FLAG_PERSONAL :
601 (CAMEL_MAPI_STORE_FOLDER_FLAG_PUBLIC | CAMEL_MAPI_STORE_FOLDER_FLAG_PUBLIC_REAL),
602 NULL);
603 if (msi == NULL)
604 continue;
605
606 camel_store_info_ref ((CamelStoreInfo *) msi);
607
608 if (!subscription_list) {
609 camel_store_folder_created (CAMEL_STORE (store), info);
610 camel_subscribable_folder_subscribed (CAMEL_SUBSCRIBABLE (store), info);
611 }
612 } else if (e_mapi_folder_get_id (folder) != msi->folder_id ||
613 e_mapi_folder_get_parent_id (folder) != msi->parent_id) {
614 msi->folder_id = e_mapi_folder_get_id (folder);
615 msi->parent_id = e_mapi_folder_get_parent_id (folder);
616 }
617
618 msi->info.flags = info->flags;
619 msi->info.total = info->total;
620 msi->info.unread = info->unread;
621 msi->mapi_folder_flags |= CAMEL_MAPI_STORE_FOLDER_FLAG_MAIL;
622
623 camel_store_info_unref ((CamelStoreInfo *) msi);
624 camel_folder_info_free (info);
625 } else if (folder->category == E_MAPI_FOLDER_CATEGORY_PUBLIC) {
626 info = mapi_convert_to_folder_info (store, folder, NULL);
627 msi = (CamelMapiStoreInfo *) camel_store_summary_path (store->summary, info->full_name);
628
629 if (!msi) {
630 msi = (CamelMapiStoreInfo *) camel_mapi_store_summary_add_from_full (store->summary,
631 info->full_name,
632 e_mapi_folder_get_id (folder),
633 e_mapi_folder_get_parent_id (folder),
634 info->flags,
635 CAMEL_MAPI_STORE_FOLDER_FLAG_PUBLIC | CAMEL_MAPI_STORE_FOLDER_FLAG_PUBLIC_REAL,
636 NULL);
637
638 if (msi)
639 camel_store_info_ref ((CamelStoreInfo *) msi);
640 } else if (e_mapi_folder_get_id (folder) != msi->folder_id ||
641 e_mapi_folder_get_parent_id (folder) != msi->parent_id) {
642 msi->folder_id = e_mapi_folder_get_id (folder);
643 msi->parent_id = e_mapi_folder_get_parent_id (folder);
644 }
645
646 if (msi == NULL)
647 continue;
648
649 msi->info.flags = info->flags;
650
651 camel_store_info_unref ((CamelStoreInfo *) msi);
652 camel_folder_info_free (info);
653 }
654 }
655
656 /* Weed out deleted folders */
657 g_hash_table_foreach (old_cache_folders, (GHFunc) remove_path_from_store_summary, store);
658 g_hash_table_destroy (old_cache_folders);
659
660 camel_store_summary_touch (store->summary);
661 camel_store_summary_save (store->summary);
662
663 g_slist_foreach (list, (GFunc) e_mapi_folder_free, NULL);
664 g_slist_free (list);
665
666 priv->folders_synced = TRUE;
667 g_object_unref (conn);
668
669 return TRUE;
670 }
671
672 static gchar *
673 mapi_concat (const gchar *prefix, const gchar *suffix)
674 {
675 gsize len;
676
677 len = strlen (prefix);
678 if (len == 0 || prefix[len - 1] == '/')
679 return g_strdup_printf ("%s%s", prefix, suffix);
680 else
681 return g_strdup_printf ("%s%c%s", prefix, '/', suffix);
682 }
683
684 static gint
685 match_path (const gchar *path, const gchar *name)
686 {
687 gchar p, n;
688
689 p = *path++;
690 n = *name++;
691 while (n && p) {
692 if (n == p) {
693 p = *path++;
694 n = *name++;
695 } else if (p == '%') {
696 if (n != '/') {
697 n = *name++;
698 } else {
699 p = *path++;
700 }
701 } else if (p == '*') {
702 return TRUE;
703 } else
704 return FALSE;
705 }
706
707 return n == 0 && (p == '%' || p == 0);
708 }
709
710 static void
711 unescape_folder_names (CamelFolderInfo *fi)
712 {
713 while (fi) {
714 if (fi->display_name && strchr (fi->display_name, '\\')) {
715 gchar *unescaped;
716
717 unescaped = unescape_slash (fi->display_name);
718 g_free (fi->display_name);
719 fi->display_name = unescaped;
720 }
721
722 if (fi->child)
723 unescape_folder_names (fi->child);
724
725 fi = fi->next;
726 }
727 }
728
729 static CamelFolderInfo *
730 mapi_get_folder_info_offline (CamelStore *store,
731 const gchar *top,
732 guint32 flags,
733 GCancellable *cancellable,
734 GError **error)
735 {
736 CamelMapiStore *mapi_store = CAMEL_MAPI_STORE (store);
737 CamelSettings *settings;
738 CamelMapiSettings *mapi_settings;
739 CamelFolderInfo *fi;
740 CamelSession *session = NULL;
741 GList *my_sources = NULL;
742 GPtrArray *folders;
743 GPtrArray *array;
744 gchar *path;
745 gboolean subscribed, subscription_list = FALSE;
746 gboolean has_public_folders = FALSE, has_foreign_folders = FALSE;
747 gchar *profile;
748 guint ii;
749
750 subscription_list = (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST);
751 subscribed = (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED);
752
753 settings = camel_service_ref_settings (CAMEL_SERVICE (store));
754
755 mapi_settings = CAMEL_MAPI_SETTINGS (settings);
756 profile = camel_mapi_settings_dup_profile (mapi_settings);
757
758 g_object_unref (settings);
759
760 folders = g_ptr_array_new ();
761
762 if (subscription_list) {
763 session = camel_service_ref_session (CAMEL_SERVICE (store));
764 if (session) {
765 ESourceRegistry *registry;
766 GList *all_sources;
767
768 registry = e_source_registry_new_sync (NULL, NULL);
769 all_sources = e_source_registry_list_sources (registry, NULL);
770
771 my_sources = e_mapi_utils_filter_sources_for_profile (all_sources, profile);
772
773 g_list_free_full (all_sources, g_object_unref);
774 g_clear_object (®istry);
775 }
776 }
777
778 if (!top || !*top)
779 top = "";
780
781 path = mapi_concat (top, "*");
782
783 array = camel_store_summary_array (mapi_store->summary);
784
785 for (ii = 0; ii < array->len; ii++) {
786 CamelStoreInfo *si;
787 CamelMapiStoreInfo *msi;
788
789 si = g_ptr_array_index (array, ii);
790 msi = (CamelMapiStoreInfo *) si;
791
792 /* Allow only All Public Folders hierarchy;
793 Subscribed public folders are those in Favorites/... - skip them too */
794 if (subscription_list &&
795 ((msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_PUBLIC) == 0 ||
796 (msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_PUBLIC_REAL) == 0 ||
797 (msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_FOREIGN) != 0)) {
798 continue;
799 }
800
801 /* Allow Mailbox and Favourites (Subscribed public folders) */
802 if (subscribed &&
803 (((si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) == 0 &&
804 (msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_PERSONAL) == 0) ||
805 (!subscription_list && (msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_PUBLIC_REAL) != 0))) {
806 continue;
807 }
808
809 if (!subscription_list &&
810 (msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_MAIL) == 0 &&
811 (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) != 0 &&
812 ((msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_PUBLIC) != 0 ||
813 (msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_FOREIGN) != 0)) {
814 continue;
815 }
816
817 if (strcmp (top, camel_store_info_get_path (si)) == 0
818 || match_path (path, camel_store_info_get_path (si))) {
819 const gchar *store_info_path = camel_store_info_get_path (si);
820
821 has_public_folders = has_public_folders || (msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_PUBLIC) != 0;
822 has_foreign_folders = has_foreign_folders || (msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_FOREIGN) != 0;
823
824 fi = mapi_build_folder_info (mapi_store, NULL, store_info_path);
825 fi->unread = si->unread;
826 fi->total = si->total;
827 fi->flags = si->flags;
828
829 if (subscription_list) {
830 guint folder_type;
831 CamelStoreInfo *si2;
832
833 si2 = camel_mapi_store_summary_get_folder_id (mapi_store->summary, msi->folder_id);
834 if (si2) {
835 if (si != si2)
836 fi->flags = si2->flags;
837
838 camel_store_info_unref (si2);
839 }
840
841 folder_type = mapi_folders_hash_table_type_lookup (mapi_store, camel_store_info_get_path (si));
842 if (folder_type != E_MAPI_FOLDER_TYPE_UNKNOWN && folder_type != E_MAPI_FOLDER_TYPE_MAIL) {
843 if (e_mapi_folder_is_subscribed_as_esource (my_sources, profile, msi->folder_id))
844 fi->flags |= CAMEL_FOLDER_SUBSCRIBED;
845 }
846 }
847
848 g_ptr_array_add (folders, fi);
849 }
850 }
851
852 camel_store_summary_array_free (mapi_store->summary, array);
853
854 if (!subscription_list && !*top) {
855 if (has_public_folders) {
856 fi = mapi_build_folder_info (mapi_store, NULL, DISPLAY_NAME_FAVORITES);
857 fi->flags |= CAMEL_FOLDER_NOSELECT | CAMEL_FOLDER_SYSTEM;
858
859 g_ptr_array_add (folders, fi);
860 }
861
862 if (has_foreign_folders) {
863 fi = mapi_build_folder_info (mapi_store, NULL, DISPLAY_NAME_FOREIGN_FOLDERS);
864 fi->flags |= CAMEL_FOLDER_NOSELECT | CAMEL_FOLDER_SYSTEM;
865
866 g_ptr_array_add (folders, fi);
867 }
868 }
869
870 g_free (path);
871 /* this adds also fake folders, if missing */
872 fi = camel_folder_info_build (folders, top, '/', TRUE);
873 g_ptr_array_free (folders, TRUE);
874
875 unescape_folder_names (fi);
876
877 if (!fi && error && !*error)
878 g_set_error_literal (error, CAMEL_STORE_ERROR, CAMEL_STORE_ERROR_NO_FOLDER,
879 (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST) != 0 ?
880 _("No public folder found") : _("No folder found"));
881
882 g_list_free_full (my_sources, g_object_unref);
883 g_clear_object (&session);
884
885 g_free (profile);
886
887 return fi;
888 }
889
890 static gboolean
891 mapi_forget_folder (CamelMapiStore *mapi_store, const gchar *folder_name, GError **error)
892 {
893 CamelService *service;
894 const gchar *user_cache_dir;
895 gchar *state_file;
896 gchar *folder_dir, *storage_path;
897 CamelFolderInfo *fi;
898
899 service = CAMEL_SERVICE (mapi_store);
900 user_cache_dir = camel_service_get_user_cache_dir (service);
901
902 storage_path = g_build_filename (user_cache_dir, "folders", NULL);
903
904 folder_dir = g_build_filename (storage_path, folder_name, NULL);
905 g_free (storage_path);
906
907 if (g_access (folder_dir, F_OK) == 0) {
908 state_file = g_build_filename (folder_dir, "cmeta", NULL);
909 g_unlink (state_file);
910 g_free (state_file);
911
912 g_rmdir (folder_dir);
913 g_free (folder_dir);
914 }
915
916 camel_store_summary_remove_path (mapi_store->summary, folder_name);
917 camel_store_summary_save (mapi_store->summary);
918
919 fi = mapi_build_folder_info (mapi_store, NULL, folder_name);
920 camel_store_folder_deleted (CAMEL_STORE (mapi_store), fi);
921 camel_folder_info_free (fi);
922
923 return TRUE;
924 }
925
926 static void
927 mapi_rename_folder_infos (CamelMapiStore *mapi_store, const gchar *old_name, const gchar *new_name)
928 {
929 gint olen;
930 CamelStoreInfo *si = NULL;
931 GPtrArray *array;
932 guint ii;
933
934 g_return_if_fail (mapi_store != NULL);
935 g_return_if_fail (old_name != NULL);
936 g_return_if_fail (new_name != NULL);
937
938 olen = strlen (old_name);
939
940 array = camel_store_summary_array (mapi_store->summary);
941
942 for (ii = 0; ii < array->len; ii++) {
943 const gchar *full_name;
944
945 si = g_ptr_array_index (array, ii);
946
947 full_name = camel_store_info_get_path (si);
948 if (full_name && g_str_has_prefix (full_name, old_name) && !g_str_equal (full_name, old_name) &&
949 full_name [olen] == '/' && full_name [olen + 1] != '\0') {
950 /* it's a subfolder of old_name */
951 mapi_id_t fid = ((CamelMapiStoreInfo *)si)->folder_id;
952
953 if (fid) {
954 gchar *new_full_name;
955 gchar *fid_str = e_mapi_util_mapi_id_to_string (fid);
956
957 /* do not remove it from name_hash yet, because this function
958 will be called for it again */
959 /* g_hash_table_remove (mapi_store->priv->name_hash, full_name); */
960 g_hash_table_remove (mapi_store->priv->id_hash, fid_str);
961
962 /* parent is still the same, only the path changed */
963 new_full_name = g_strconcat (new_name, full_name + olen + (g_str_has_suffix (new_name, "/") ? 1 : 0), NULL);
964
965 mapi_update_folder_hash_tables (mapi_store, new_full_name, fid_str, NULL);
966
967 camel_store_info_set_value (si, CAMEL_STORE_INFO_PATH, new_full_name);
968 camel_store_summary_touch (mapi_store->summary);
969
970 g_free (new_full_name);
971 g_free (fid_str);
972 }
973 }
974 }
975
976 camel_store_summary_array_free (mapi_store->summary, array);
977 }
978
979 static void
980 stop_pending_updates (CamelMapiStore *mapi_store)
981 {
982 CamelMapiStorePrivate *priv;
983
984 g_return_if_fail (mapi_store != NULL);
985 g_return_if_fail (mapi_store->priv != NULL);
986
987 priv = mapi_store->priv;
988
989 g_rec_mutex_lock (&priv->updates_lock);
990 if (priv->updates_cancellable) {
991 g_cancellable_cancel (priv->updates_cancellable);
992 g_object_unref (priv->updates_cancellable);
993 priv->updates_cancellable = NULL;
994 }
995
996 if (priv->update_folder_names) {
997 g_slist_free_full (priv->update_folder_names, g_free);
998 priv->update_folder_names = NULL;
999 }
1000
1001 if (priv->update_folder_id) {
1002 g_source_remove (priv->update_folder_id);
1003 priv->update_folder_id = 0;
1004 }
1005
1006 if (priv->update_folder_list_id) {
1007 g_source_remove (priv->update_folder_list_id);
1008 priv->update_folder_list_id = 0;
1009 }
1010
1011 g_rec_mutex_unlock (&priv->updates_lock);
1012 }
1013
1014 static void
1015 mapi_store_dispose (GObject *object)
1016 {
1017 CamelMapiStore *mapi_store;
1018 CamelMapiStorePrivate *priv;
1019
1020 mapi_store = CAMEL_MAPI_STORE (object);
1021 priv = mapi_store->priv;
1022
1023 stop_pending_updates (CAMEL_MAPI_STORE (object));
1024
1025 if (mapi_store->summary) {
1026 camel_store_summary_save (mapi_store->summary);
1027 g_object_unref (mapi_store->summary);
1028 mapi_store->summary = NULL;
1029 }
1030
1031 g_rec_mutex_lock (&mapi_store->priv->connection_lock);
1032 if (priv->connection != NULL) {
1033 g_signal_handlers_disconnect_by_func (priv->connection, camel_mapi_store_server_notification_cb, object);
1034
1035 g_object_unref (priv->connection);
1036 priv->connection = NULL;
1037 }
1038 g_rec_mutex_unlock (&mapi_store->priv->connection_lock);
1039
1040 /* Chain up to parent's dispose() method. */
1041 G_OBJECT_CLASS (camel_mapi_store_parent_class)->dispose (object);
1042 }
1043
1044 static void
1045 mapi_store_finalize (GObject *object)
1046 {
1047 CamelMapiStore *mapi_store = CAMEL_MAPI_STORE (object);
1048
1049 g_clear_pointer (&mapi_store->priv->id_hash, g_hash_table_destroy);
1050 g_clear_pointer (&mapi_store->priv->name_hash, g_hash_table_destroy);
1051 g_clear_pointer (&mapi_store->priv->parent_hash, g_hash_table_destroy);
1052 g_clear_pointer (&mapi_store->priv->default_folders, g_hash_table_destroy);
1053 g_clear_pointer (&mapi_store->priv->container_hash, g_hash_table_destroy);
1054
1055 g_rec_mutex_clear (&mapi_store->priv->connection_lock);
1056 g_rec_mutex_clear (&mapi_store->priv->updates_lock);
1057
1058 /* Chain up to parent's finalize() method. */
1059 G_OBJECT_CLASS (camel_mapi_store_parent_class)->finalize (object);
1060 }
1061
1062 static gboolean
1063 mapi_store_can_refresh_folder (CamelStore *store,
1064 CamelFolderInfo *info,
1065 GError **error)
1066 {
1067 CamelService *service;
1068 CamelSettings *settings;
1069 CamelMapiSettings *mapi_settings;
1070 gboolean check_all;
1071
1072 /* skip unselectable folders from automatic refresh */
1073 if (info && (info->flags & CAMEL_FOLDER_NOSELECT) != 0)
1074 return FALSE;
1075
1076 service = CAMEL_SERVICE (store);
1077
1078 settings = camel_service_ref_settings (service);
1079
1080 mapi_settings = CAMEL_MAPI_SETTINGS (settings);
1081 check_all = camel_mapi_settings_get_check_all (mapi_settings);
1082
1083 g_object_unref (settings);
1084
1085 if (check_all)
1086 return TRUE;
1087
1088 return CAMEL_STORE_CLASS(camel_mapi_store_parent_class)->can_refresh_folder (store, info, error);
1089 }
1090
1091 static gchar *
1092 mapi_build_folder_dir (const gchar *user_cache_dir,
1093 const gchar *folder_name)
1094 {
1095 GString *path;
1096 gchar **elems;
1097 gint ii;
1098
1099 g_return_val_if_fail (user_cache_dir != NULL, NULL);
1100 g_return_val_if_fail (*user_cache_dir != 0, NULL);
1101 g_return_val_if_fail (folder_name != NULL, NULL);
1102
1103 elems = g_strsplit (folder_name, "/", -1);
1104 g_return_val_if_fail (elems != NULL, NULL);
1105
1106 path = g_string_new (user_cache_dir);
1107 if (path->str[path->len - 1] != G_DIR_SEPARATOR)
1108 g_string_append_c (path, G_DIR_SEPARATOR);
1109 g_string_append (path, "folders");
1110
1111 for (ii = 0; elems[ii]; ii++) {
1112 if (path->str[path->len - 1] != G_DIR_SEPARATOR)
1113 g_string_append_c (path, G_DIR_SEPARATOR);
1114
1115 if (ii != 0) {
1116 g_string_append (path, "sub");
1117 g_string_append_c (path, G_DIR_SEPARATOR);
1118 }
1119
1120 if (elems[ii + 1])
1121 g_string_append (path, elems[ii]);
1122 }
1123
1124 g_strfreev (elems);
1125
1126 return g_string_free (path, FALSE);
1127 }
1128
1129 static CamelFolder *
1130 mapi_store_get_folder_sync (CamelStore *store,
1131 const gchar *folder_name,
1132 guint32 flags,
1133 GCancellable *cancellable,
1134 GError **error)
1135 {
1136 CamelMapiStore *mapi_store = CAMEL_MAPI_STORE (store);
1137 CamelService *service;
1138 CamelStoreInfo *si;
1139 CamelFolder *folder;
1140 const gchar *user_cache_dir;
1141 gchar *folder_dir;
1142
1143 si = camel_store_summary_path (mapi_store->summary, folder_name);
1144 if (si)
1145 camel_store_info_unref (si);
1146
1147 service = CAMEL_SERVICE (store);
1148 user_cache_dir = camel_service_get_user_cache_dir (service);
1149
1150 folder_dir = mapi_build_folder_dir (user_cache_dir, folder_name);
1151 g_return_val_if_fail (folder_dir != NULL, NULL);
1152
1153 folder = camel_mapi_folder_new (store, folder_name, folder_dir, flags, error);
1154 g_free (folder_dir);
1155
1156 return folder;
1157 }
1158
1159 static CamelFolderInfo*
1160 mapi_store_get_folder_info_sync (CamelStore *store,
1161 const gchar *top,
1162 guint32 flags,
1163 GCancellable *cancellable,
1164 GError **error)
1165 {
1166 CamelMapiStore *mapi_store = CAMEL_MAPI_STORE (store);
1167 CamelService *service;
1168
1169 if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store)) &&
1170 (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST) != 0) {
1171 g_set_error_literal (
1172 error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_UNAVAILABLE,
1173 _("Folder list is not available in offline mode"));
1174 return NULL;
1175 }
1176
1177 service = CAMEL_SERVICE (store);
1178
1179 if (camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store))) {
1180 CamelServiceConnectionStatus status;
1181
1182 status = camel_service_get_connection_status (service);
1183
1184 /* update folders from the server only when asking for the top most or the 'top' is not known;
1185 otherwise believe the local cache, because folders sync is pretty slow operation to be done
1186 one every single question on the folder info */
1187 if ((flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST) != 0 ||
1188 (!(flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED)) ||
1189 (top && *top && !camel_mapi_store_folder_id_lookup (mapi_store, top)) ||
1190 camel_store_summary_count (mapi_store->summary) <= 1 ||
1191 !mapi_store->priv->folders_synced) {
1192 if (status == CAMEL_SERVICE_DISCONNECTED) {
1193 gchar *name = camel_service_get_name (service, TRUE);
1194
1195 camel_operation_push_message (cancellable, _("Connecting to “%s”"), name);
1196 camel_service_connect_sync (service, cancellable, NULL);
1197 camel_operation_pop_message (cancellable);
1198
1199 g_free (name);
1200 }
1201
1202 if (check_for_connection (service, NULL) || status == CAMEL_SERVICE_CONNECTING) {
1203 gboolean first_check = !mapi_store->priv->folders_synced;
1204
1205 if (!mapi_folders_sync (mapi_store, flags, cancellable, error))
1206 return NULL;
1207
1208 if (first_check) {
1209 camel_store_summary_touch (mapi_store->summary);
1210 camel_store_summary_save (mapi_store->summary);
1211 }
1212 }
1213 }
1214 }
1215
1216 return mapi_get_folder_info_offline (store, top, flags, cancellable, error);
1217 }
1218
1219 static CamelFolder *
1220 mapi_store_get_junk_folder_sync (CamelStore *store,
1221 GCancellable *cancellable,
1222 GError **error)
1223 {
1224 return mapi_get_folder_with_type (store, CAMEL_FOLDER_TYPE_JUNK, cancellable, error);
1225 }
1226
1227 static CamelFolder *
1228 mapi_store_get_trash_folder_sync (CamelStore *store,
1229 GCancellable *cancellable,
1230 GError **error)
1231 {
1232 return mapi_get_folder_with_type (store, CAMEL_FOLDER_TYPE_TRASH, cancellable, error);
1233 }
1234
1235 static CamelFolderInfo *
1236 mapi_store_create_folder_sync (CamelStore *store,
1237 const gchar *parent_name,
1238 const gchar *folder_name,
1239 GCancellable *cancellable,
1240 GError **error)
1241 {
1242 CamelMapiStore *mapi_store = CAMEL_MAPI_STORE (store);
1243 CamelMapiStorePrivate *priv = mapi_store->priv;
1244 CamelFolderInfo *root = NULL;
1245 gchar *parent_id;
1246 mapi_id_t parent_fid, new_folder_id;
1247 mapi_object_t obj_folder;
1248 EMapiConnection *conn;
1249 GError *mapi_error = NULL;
1250
1251 if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store))) {
1252 g_set_error_literal (
1253 error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_UNAVAILABLE,
1254 _("Cannot create MAPI folders in offline mode"));
1255 return NULL;
1256 }
1257
1258 if (mapi_fid_is_system_folder (mapi_store, camel_mapi_store_folder_id_lookup (mapi_store, folder_name))) {
1259 g_set_error (
1260 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1261 _("Cannot create new folder “%s”"),
1262 folder_name);
1263 return NULL;
1264 }
1265
1266 if (!mapi_connect_sync (CAMEL_SERVICE(store), cancellable, NULL)) {
1267 g_set_error (
1268 error, CAMEL_SERVICE_ERROR,
1269 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
1270 _("Authentication failed"));
1271 return NULL;
1272 }
1273
1274 if (parent_name && (!*parent_name ||
1275 g_str_equal (parent_name, DISPLAY_NAME_FAVORITES) ||
1276 g_str_equal (parent_name, DISPLAY_NAME_FOREIGN_FOLDERS))) {
1277 g_set_error_literal (
1278 error, CAMEL_SERVICE_ERROR,
1279 CAMEL_SERVICE_ERROR_UNAVAILABLE,
1280 _("MAPI folders can be created only within mailbox of the logged in user"));
1281 return NULL;
1282 }
1283
1284 if (parent_name && *parent_name)
1285 parent_id = g_strdup (g_hash_table_lookup (priv->name_hash, parent_name));
1286 else
1287 parent_id = NULL;
1288
1289 if (!parent_id) {
1290 g_set_error (
1291 error, CAMEL_SERVICE_ERROR,
1292 CAMEL_SERVICE_ERROR_UNAVAILABLE,
1293 _("Cannot find folder “%s”"), parent_name ? parent_name : "");
1294 return NULL;
1295 }
1296 e_mapi_util_mapi_id_from_string (parent_id, &parent_fid);
1297 new_folder_id = 0;
1298
1299 conn = camel_mapi_store_ref_connection (mapi_store, cancellable, error);
1300 if (!conn)
1301 return NULL;
1302
1303 if (!cms_open_folder (mapi_store, conn, parent_fid, &obj_folder, cancellable, error)) {
1304 g_object_unref (conn);
1305 return NULL;
1306 }
1307
1308 if (!e_mapi_connection_create_folder (conn, &obj_folder, folder_name, IPF_NOTE, &new_folder_id, cancellable, &mapi_error))
1309 new_folder_id = 0;
1310 e_mapi_connection_close_folder (conn, &obj_folder, cancellable, &mapi_error);
1311
1312 if (new_folder_id != 0) {
1313 gchar *folder_id_str;
1314 CamelMapiStoreInfo *parent_msi;
1315 gboolean is_public, is_foreign;
1316
1317 parent_msi = (CamelMapiStoreInfo *) camel_mapi_store_summary_get_folder_id (mapi_store->summary, parent_fid);
1318 is_public = parent_msi && (parent_msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_PUBLIC) != 0;
1319 is_foreign = parent_msi && (parent_msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_FOREIGN) != 0;
1320
1321 root = mapi_build_folder_info (mapi_store, parent_name, folder_name);
1322 camel_mapi_store_summary_add_from_full (mapi_store->summary,
1323 root->full_name,
1324 new_folder_id,
1325 parent_fid,
1326 root->flags | ((is_public || is_foreign) ? CAMEL_FOLDER_SUBSCRIBED | CAMEL_STORE_INFO_FOLDER_SUBSCRIBED : 0),
1327 (is_public ? CAMEL_MAPI_STORE_FOLDER_FLAG_PUBLIC :
1328 is_foreign ? CAMEL_MAPI_STORE_FOLDER_FLAG_FOREIGN :
1329 CAMEL_MAPI_STORE_FOLDER_FLAG_PERSONAL) | CAMEL_MAPI_STORE_FOLDER_FLAG_MAIL,
1330 is_foreign ? parent_msi->foreign_username : NULL);
1331
1332 if (parent_msi)
1333 camel_store_info_unref ((CamelStoreInfo *) parent_msi);
1334
1335 camel_store_summary_save (mapi_store->summary);
1336
1337 folder_id_str = e_mapi_util_mapi_id_to_string (new_folder_id);
1338 mapi_update_folder_hash_tables (mapi_store, root->full_name, folder_id_str, parent_id);
1339 g_free (folder_id_str);
1340
1341 camel_store_folder_created (store, root);
1342 camel_subscribable_folder_subscribed (CAMEL_SUBSCRIBABLE (store), root);
1343 } else {
1344 if (mapi_error) {
1345 if (!e_mapi_utils_propagate_cancelled_error (mapi_error, error))
1346 g_set_error (
1347 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1348 _("Cannot create folder “%s”: %s"), folder_name, mapi_error->message);
1349 camel_mapi_store_maybe_disconnect (mapi_store, mapi_error);
1350 g_error_free (mapi_error);
1351 } else {
1352 g_set_error (
1353 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1354 _("Cannot create folder “%s”"), folder_name);
1355 }
1356 }
1357
1358 g_object_unref (conn);
1359
1360 return root;
1361
1362 }
1363
1364 static gboolean
1365 mapi_store_delete_folder_sync (CamelStore *store,
1366 const gchar *folder_name,
1367 GCancellable *cancellable,
1368 GError **error)
1369 {
1370 CamelMapiStore *mapi_store = CAMEL_MAPI_STORE (store);
1371 CamelMapiStorePrivate *priv = mapi_store->priv;
1372 CamelMapiStoreInfo *msi;
1373 EMapiConnection *conn;
1374 mapi_object_t *obj_store = NULL;
1375 const gchar *folder_id;
1376 mapi_id_t folder_fid;
1377 gboolean status = FALSE;
1378 gboolean success = TRUE;
1379 GError *local_error = NULL;
1380
1381 if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store))) {
1382 g_set_error_literal (
1383 error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_UNAVAILABLE,
1384 _("Cannot delete MAPI folders in offline mode"));
1385 return FALSE;
1386 }
1387
1388 if (!camel_mapi_store_connected ((CamelMapiStore *)store, cancellable, &local_error)) {
1389 if (local_error != NULL) {
1390 g_propagate_error (error, local_error);
1391 return FALSE;
1392 }
1393
1394 g_set_error_literal (
1395 error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_UNAVAILABLE,
1396 _("Cannot delete MAPI folders in offline mode"));
1397
1398 return FALSE;
1399 }
1400
1401 folder_id = g_hash_table_lookup (priv->name_hash, folder_name);
1402 if (!folder_id) {
1403 g_set_error (
1404 error, CAMEL_SERVICE_ERROR,
1405 CAMEL_SERVICE_ERROR_UNAVAILABLE,
1406 _("Cannot find folder “%s”"), folder_name);
1407 return FALSE;
1408 }
1409
1410 e_mapi_util_mapi_id_from_string (folder_id, &folder_fid);
1411
1412 conn = camel_mapi_store_ref_connection (mapi_store, cancellable, error);
1413 if (!conn)
1414 return FALSE;
1415
1416 msi = (CamelMapiStoreInfo *) camel_mapi_store_summary_get_folder_id (mapi_store->summary, folder_fid);
1417 if (!msi ||
1418 (msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_PUBLIC) != 0 ||
1419 (msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_FOREIGN) != 0) {
1420 /* do not remove foreign or public folders, just unsubscribe from them,
1421 even when there are folder delete permissons on the folder
1422 */
1423 status = TRUE;
1424 } else if (cms_peek_folder_store (mapi_store, conn, folder_fid, &obj_store, cancellable, &local_error))
1425 status = e_mapi_connection_remove_folder (conn, obj_store, folder_fid, cancellable, &local_error);
1426 else
1427 status = FALSE;
1428
1429 g_object_unref (conn);
1430
1431 if (status) {
1432 success = mapi_forget_folder (mapi_store, folder_name, &local_error);
1433
1434 if (success) {
1435 /* remove from name_cache at the end, because the folder_id is from there */
1436 /*g_hash_table_remove (priv->parent_hash, folder_id);*/
1437 g_hash_table_remove (priv->id_hash, folder_id);
1438 g_hash_table_remove (priv->name_hash, folder_name);
1439 }
1440
1441 if (local_error) {
1442 camel_mapi_store_maybe_disconnect (mapi_store, local_error);
1443 g_propagate_error (error, local_error);
1444 }
1445 } else {
1446 success = FALSE;
1447
1448 if (local_error) {
1449 if (!e_mapi_utils_propagate_cancelled_error (local_error, error))
1450 g_set_error (
1451 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1452 _("Cannot remove folder “%s”: %s"),
1453 folder_name, local_error->message);
1454
1455 camel_mapi_store_maybe_disconnect (mapi_store, local_error);
1456 g_error_free (local_error);
1457 } else {
1458 g_set_error (
1459 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1460 _("Cannot remove folder “%s”"),
1461 folder_name);
1462 }
1463 }
1464
1465 return success;
1466 }
1467
1468 static gboolean
1469 mapi_store_rename_folder_sync (CamelStore *store,
1470 const gchar *old_name,
1471 const gchar *new_name,
1472 GCancellable *cancellable,
1473 GError **error)
1474 {
1475 CamelMapiStore *mapi_store = CAMEL_MAPI_STORE (store);
1476 CamelMapiStorePrivate *priv = mapi_store->priv;
1477 EMapiConnection *conn;
1478 CamelStoreInfo *si = NULL;
1479 CamelService *service;
1480 const gchar *user_cache_dir;
1481 gchar *old_parent, *new_parent, *tmp;
1482 gboolean move_cache = TRUE;
1483 const gchar *old_fid_str, *new_parent_fid_str = NULL;
1484 mapi_id_t old_fid;
1485 GError *local_error = NULL;
1486
1487 if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store))) {
1488 g_set_error_literal (
1489 error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_UNAVAILABLE,
1490 _("Cannot rename MAPI folders in offline mode"));
1491 return FALSE;
1492 }
1493
1494 service = CAMEL_SERVICE (store);
1495 user_cache_dir = camel_service_get_user_cache_dir (service);
1496
1497 if (!camel_mapi_store_connected ((CamelMapiStore *)store, cancellable, &local_error)) {
1498 if (local_error != NULL) {
1499 g_propagate_error (error, local_error);
1500 return FALSE;
1501 }
1502
1503 g_set_error_literal (
1504 error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_UNAVAILABLE,
1505 _("Cannot rename MAPI folders in offline mode"));
1506
1507 return FALSE;
1508 }
1509
1510 /* Need a full name of a folder */
1511 old_fid_str = camel_mapi_store_folder_id_lookup (mapi_store, old_name);
1512 if (!old_fid_str) {
1513 g_set_error (
1514 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1515 /* Translators: “%s” is current name of the folder */
1516 _("Cannot rename MAPI folder “%s”. Folder does not exist"),
1517 old_name);
1518 return FALSE;
1519 }
1520
1521 /*Do not allow rename for system folders.*/
1522 if (mapi_fid_is_system_folder (mapi_store, old_fid_str)) {
1523 g_set_error (
1524 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1525 /* Translators: “%s to %s” is current name of the folder and
1526 new name of the folder.*/
1527 _("Cannot rename MAPI default folder “%s” to “%s”"),
1528 old_name, new_name);
1529 return FALSE;
1530 }
1531
1532 old_parent = g_strdup (old_name);
1533 tmp = strrchr (old_parent, '/');
1534 if (tmp) {
1535 *tmp = '\0';
1536 } else {
1537 strcpy (old_parent, "");
1538 }
1539
1540 new_parent = g_strdup (new_name);
1541 tmp = strrchr (new_parent, '/');
1542 if (tmp) {
1543 *tmp = '\0';
1544 tmp++; /* here's a new folder name now */
1545 } else {
1546 strcpy (new_parent, "");
1547 tmp = NULL;
1548 }
1549
1550 if (!e_mapi_util_mapi_id_from_string (old_fid_str, &old_fid)) {
1551 g_set_error (
1552 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1553 _("Cannot rename MAPI folder “%s” to “%s”"),
1554 old_name, new_name);
1555 g_free (old_parent);
1556 g_free (new_parent);
1557 return FALSE;
1558 }
1559
1560 conn = camel_mapi_store_ref_connection (mapi_store, cancellable, error);
1561 if (!conn) {
1562 g_free (old_parent);
1563 g_free (new_parent);
1564
1565 return FALSE;
1566 }
1567
1568 if (tmp == NULL || g_str_equal (old_parent, new_parent)) {
1569 gchar *folder_id;
1570 gboolean status = FALSE;
1571 mapi_object_t obj_folder;
1572
1573 if (cms_open_folder (mapi_store, conn, old_fid, &obj_folder, cancellable, &local_error)) {
1574 status = e_mapi_connection_rename_folder (conn, &obj_folder, tmp ? tmp : new_name, cancellable, &local_error);
1575 e_mapi_connection_close_folder (conn, &obj_folder, cancellable, &local_error);
1576 }
1577
1578 /* renaming in the same folder, thus no MoveFolder necessary */
1579 if (!status) {
1580 g_object_unref (conn);
1581
1582 if (local_error) {
1583 if (!e_mapi_utils_propagate_cancelled_error (local_error, error))
1584 g_set_error (
1585 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1586 /* Translators: “%s to %s” is current name of the folder and new name of the folder.
1587 The last “%s” is a detailed error message. */
1588 _("Cannot rename MAPI folder “%s” to “%s”: %s"),
1589 old_name, new_name, local_error->message);
1590 camel_mapi_store_maybe_disconnect (mapi_store, local_error);
1591 g_error_free (local_error);
1592 } else {
1593 g_set_error (
1594 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1595 /* Translators: “%s to %s” is current name of the folder and new name of the folder. */
1596 _("Cannot rename MAPI folder “%s” to “%s”"),
1597 old_name, new_name);
1598 }
1599
1600 g_free (old_parent);
1601 g_free (new_parent);
1602 return FALSE;
1603 }
1604
1605 mapi_rename_folder_infos (mapi_store, old_name, new_name);
1606
1607 folder_id = g_strdup (old_fid_str);
1608
1609 /* this frees old_fid_str */
1610 g_hash_table_remove (priv->name_hash, old_name);
1611 g_hash_table_remove (priv->id_hash, folder_id);
1612
1613 mapi_update_folder_hash_tables (mapi_store, new_name, folder_id, NULL);
1614
1615 g_free (folder_id);
1616 } else {
1617 const gchar *old_parent_fid_str;
1618 mapi_id_t old_parent_fid, new_parent_fid;
1619 gchar *folder_id;
1620
1621 old_parent_fid_str = camel_mapi_store_folder_id_lookup (mapi_store, old_parent);
1622 new_parent_fid_str = camel_mapi_store_folder_id_lookup (mapi_store, new_parent);
1623 if (!old_parent_fid_str && new_parent_fid_str) {
1624 CamelStoreInfo *new_si;
1625
1626 /* Folder was in a store-summary, and is known, but the parent gone.
1627 The reason might be that this is a subfolder whose parent got moved
1628 a second ago. Thus just update local summary with proper paths. */
1629 move_cache = FALSE;
1630
1631 new_si = camel_store_summary_path (mapi_store->summary, new_name);
1632 if (new_si) {
1633 si = camel_store_summary_path (mapi_store->summary, old_name);
1634 if (si) {
1635 /* for cases where folder sync realized new folders before this got updated;
1636 this shouldn't duplicate the info in summary, but remove the old one */
1637 camel_store_summary_remove (mapi_store->summary, si);
1638 si = NULL;
1639 }
1640 camel_store_info_unref (new_si);
1641 }
1642 } else {
1643 gboolean status = FALSE;
1644
1645 if (old_parent_fid_str && new_parent_fid_str &&
1646 e_mapi_util_mapi_id_from_string (old_parent_fid_str, &old_parent_fid) &&
1647 e_mapi_util_mapi_id_from_string (new_parent_fid_str, &new_parent_fid)) {
1648 mapi_object_t src_obj_folder, src_parent_obj_folder, des_obj_folder;
1649
1650 if (cms_open_folder (mapi_store, conn, old_fid, &src_obj_folder, cancellable, &local_error)) {
1651 if (cms_open_folder (mapi_store, conn, old_parent_fid, &src_parent_obj_folder, cancellable, &local_error)) {
1652 if (cms_open_folder (mapi_store, conn, new_parent_fid, &des_obj_folder, cancellable, &local_error)) {
1653 status = e_mapi_connection_move_folder (conn, &src_obj_folder, &src_parent_obj_folder, &des_obj_folder, tmp, cancellable, &local_error);
1654 e_mapi_connection_close_folder (conn, &des_obj_folder, cancellable, &local_error);
1655 }
1656 e_mapi_connection_close_folder (conn, &src_parent_obj_folder, cancellable, &local_error);
1657 }
1658 e_mapi_connection_close_folder (conn, &src_obj_folder, cancellable, &local_error);
1659 }
1660 }
1661
1662 if (!status) {
1663 g_object_unref (conn);
1664
1665 if (local_error) {
1666 if (!e_mapi_utils_propagate_cancelled_error (local_error, error))
1667 g_set_error (
1668 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1669 _("Cannot rename MAPI folder “%s” to “%s”: %s"),
1670 old_name, new_name, local_error->message);
1671 camel_mapi_store_maybe_disconnect (mapi_store, local_error);
1672 g_error_free (local_error);
1673 } else {
1674 g_set_error (
1675 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1676 _("Cannot rename MAPI folder “%s” to “%s”"),
1677 old_name, new_name);
1678 }
1679 g_free (old_parent);
1680 g_free (new_parent);
1681 return FALSE;
1682 } else {
1683 /* folder was moved, update all subfolders immediately, thus
1684 the next get_folder_info call will know about them */
1685 mapi_rename_folder_infos (mapi_store, old_name, new_name);
1686 }
1687 }
1688
1689 folder_id = g_strdup (old_fid_str);
1690
1691 /* this frees old_fid_str */
1692 g_hash_table_remove (priv->name_hash, old_name);
1693 g_hash_table_remove (priv->id_hash, folder_id);
1694 /*g_hash_table_remove (priv->parent_hash, folder_id);*/
1695
1696 mapi_update_folder_hash_tables (mapi_store, new_name, folder_id, new_parent_fid_str);
1697
1698 g_free (folder_id);
1699 }
1700
1701 g_object_unref (conn);
1702
1703 si = camel_store_summary_path (mapi_store->summary, old_name);
1704 if (si) {
1705 mapi_id_t new_parent_fid;
1706
1707 camel_store_info_set_value (si, CAMEL_STORE_INFO_PATH, new_name);
1708 if (new_parent_fid_str && e_mapi_util_mapi_id_from_string (new_parent_fid_str, &new_parent_fid))
1709 ((CamelMapiStoreInfo *) si)->parent_id = new_parent_fid;
1710 camel_store_info_unref (si);
1711 camel_store_summary_touch (mapi_store->summary);
1712 }
1713
1714 if (move_cache) {
1715 gchar *oldpath, *newpath;
1716
1717 oldpath = g_build_filename (user_cache_dir, "folders", old_name, NULL);
1718 newpath = g_build_filename (user_cache_dir, "folders", new_name, NULL);
1719
1720 if (g_file_test (oldpath, G_FILE_TEST_IS_DIR) && g_rename (oldpath, newpath) == -1 && errno != ENOENT) {
1721 g_warning ("Could not rename message cache '%s' to '%s': %s: cache reset", oldpath, newpath, g_strerror (errno));
1722 }
1723
1724 g_free (oldpath);
1725 g_free (newpath);
1726 }
1727
1728 g_free (old_parent);
1729 g_free (new_parent);
1730
1731 return TRUE;
1732 }
1733
1734 static gboolean
1735 mapi_store_folder_is_subscribed (CamelSubscribable *subscribable,
1736 const gchar *folder_name)
1737 {
1738 CamelMapiStore *mapi_store = CAMEL_MAPI_STORE (subscribable);
1739 CamelStoreInfo *si;
1740 gint truth = FALSE;
1741
1742 if ((si = camel_store_summary_path (mapi_store->summary, folder_name))) {
1743 truth = (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) != 0;
1744 camel_store_info_unref (si);
1745 }
1746
1747 return truth;
1748 }
1749
1750 static gboolean
1751 mapi_store_subscribe_folder_sync (CamelSubscribable *subscribable,
1752 const gchar *folder_name,
1753 GCancellable *cancellable,
1754 GError **error)
1755 {
1756 CamelMapiStore *mapi_store = CAMEL_MAPI_STORE (subscribable);
1757 CamelFolderInfo *fi;
1758 CamelStoreInfo *si, *si2;
1759 CamelMapiStoreInfo *msi;
1760 const gchar *use_folder_name = folder_name, *f_name;
1761 gchar *path;
1762
1763 if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (mapi_store))) {
1764 g_set_error_literal (
1765 error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_UNAVAILABLE,
1766 _("Cannot subscribe MAPI folders in offline mode"));
1767 return FALSE;
1768 }
1769
1770 /* subscribe is done only with public folders, which are added to Favorites */
1771 f_name = strrchr (folder_name, '/');
1772 if (!f_name) {
1773 /* Don't process All Public Folder. */
1774 return TRUE;
1775 }
1776
1777 use_folder_name = f_name + 1;
1778
1779 si = camel_store_summary_path (mapi_store->summary, folder_name);
1780 if (!si) {
1781 g_set_error (
1782 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1783 _("Folder “%s” not found"), folder_name);
1784
1785 return FALSE;
1786 }
1787
1788 msi = (CamelMapiStoreInfo *) si;
1789 if ((msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_PUBLIC) == 0) {
1790 /* this is not a public folder, but MAPI supports subscribtions
1791 only on public folders, thus report success
1792 */
1793
1794 camel_store_info_unref (si);
1795
1796 return TRUE;
1797 }
1798
1799 path = g_strconcat (DISPLAY_NAME_FAVORITES, "/", use_folder_name, NULL);
1800 si2 = camel_store_summary_path (mapi_store->summary, path);
1801 if (si2 && ((CamelMapiStoreInfo *) si2)->folder_id == msi->folder_id && (si2->flags & CAMEL_FOLDER_SUBSCRIBED) != 0) {
1802 /* already subscribed */
1803 camel_store_info_unref (si);
1804 camel_store_info_unref (si2);
1805
1806 return TRUE;
1807 } else if (si2) {
1808 camel_store_info_unref (si2);
1809 si2 = NULL;
1810 }
1811
1812 if ((msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_MAIL) != 0) {
1813 /* make sure parent folder is known */
1814 fi = mapi_build_folder_info (mapi_store, NULL, DISPLAY_NAME_FAVORITES);
1815 fi->flags |= CAMEL_FOLDER_NOSELECT | CAMEL_FOLDER_SYSTEM;
1816
1817 camel_subscribable_folder_subscribed (CAMEL_SUBSCRIBABLE (mapi_store), fi);
1818
1819 camel_folder_info_free (fi);
1820
1821 camel_mapi_store_ensure_unique_path (mapi_store, &path);
1822
1823 /* then add copy with Favorites/xxx */
1824 si2 = camel_mapi_store_summary_add_from_full (mapi_store->summary,
1825 path,
1826 msi->folder_id,
1827 msi->parent_id,
1828 msi->camel_folder_flags | CAMEL_FOLDER_SUBSCRIBED | CAMEL_STORE_INFO_FOLDER_SUBSCRIBED | CAMEL_FOLDER_NOCHILDREN,
1829 msi->mapi_folder_flags & ~(CAMEL_MAPI_STORE_FOLDER_FLAG_PUBLIC_REAL),
1830 msi->foreign_username);
1831
1832 if (si2) {
1833 camel_store_summary_touch (mapi_store->summary);
1834
1835 fi = mapi_build_folder_info (mapi_store, NULL, path);
1836 fi->unread = si2->unread;
1837 fi->total = si2->total;
1838 fi->flags = si2->flags;
1839 camel_subscribable_folder_subscribed (subscribable, fi);
1840 camel_folder_info_free (fi);
1841 } else {
1842 g_debug ("%s: Failed to add '%s' to store's summary", G_STRFUNC, path);
1843 }
1844 } else {
1845 CamelSettings *settings;
1846 CamelMapiSettings *mapi_settings;
1847 guint folder_type = mapi_folders_hash_table_type_lookup (mapi_store, folder_name);
1848 gchar *profile;
1849
1850 /* remember the folder, thus it can be removed and checked in Subscriptions dialog */
1851 msi->camel_folder_flags = msi->camel_folder_flags | CAMEL_FOLDER_SUBSCRIBED | CAMEL_STORE_INFO_FOLDER_SUBSCRIBED | CAMEL_FOLDER_NOCHILDREN;
1852 camel_store_summary_touch (mapi_store->summary);
1853
1854 settings = camel_service_ref_settings (CAMEL_SERVICE (mapi_store));
1855 mapi_settings = CAMEL_MAPI_SETTINGS (settings);
1856 profile = camel_mapi_settings_dup_profile (mapi_settings);
1857
1858 g_object_unref (settings);
1859
1860 if (!e_mapi_folder_add_as_esource (NULL, folder_type, profile,
1861 TRUE /* camel_offline_settings_get_stay_synchronized (CAMEL_OFFLINE_SETTINGS (mapi_settings)) */,
1862 E_MAPI_FOLDER_CATEGORY_PUBLIC,
1863 NULL,
1864 use_folder_name,
1865 msi->folder_id,
1866 (gint) msi->folder_id,
1867 cancellable,
1868 error)) {
1869 camel_store_info_unref (si);
1870 g_free (profile);
1871 g_free (path);
1872
1873 return FALSE;
1874 }
1875
1876 g_free (profile);
1877 }
1878 camel_store_info_unref (si);
1879 camel_store_summary_save (mapi_store->summary);
1880
1881 g_free (path);
1882
1883 return TRUE;
1884 }
1885
1886 static gboolean
1887 mapi_store_unsubscribe_subfolders (CamelMapiStore *mapi_store,
1888 mapi_id_t parent_id,
1889 GCancellable *cancellable,
1890 GError **error);
1891
1892 static gboolean
1893 mapi_store_unsubscribe_folder_internal_sync (CamelSubscribable *subscribable,
1894 const gchar *folder_name,
1895 gboolean check_foreign_subfolders,
1896 GCancellable *cancellable,
1897 GError **error)
1898 {
1899 gboolean res = TRUE;
1900 CamelFolderInfo *fi;
1901 CamelStoreInfo *si;
1902 CamelMapiStoreInfo *msi;
1903 CamelMapiStore *mapi_store = CAMEL_MAPI_STORE (subscribable);
1904
1905 if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (mapi_store))) {
1906 g_set_error_literal (
1907 error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_UNAVAILABLE,
1908 _("Cannot unsubscribe MAPI folders in offline mode"));
1909 return FALSE;
1910 }
1911
1912 si = camel_store_summary_path (mapi_store->summary, folder_name);
1913 if (!si) {
1914 /* no such folder in the cache, might be unsubscribed already */
1915 return TRUE;
1916 }
1917
1918 msi = (CamelMapiStoreInfo *) si;
1919 if ((msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_MAIL) != 0) {
1920 CamelStoreInfo *si2 = camel_mapi_store_summary_get_folder_id (mapi_store->summary, msi->folder_id);
1921
1922 if (si2) {
1923 CamelMapiStoreInfo *msi2 = (CamelMapiStoreInfo *) si2;
1924
1925 fi = mapi_build_folder_info (mapi_store, NULL, camel_store_info_get_path (si2));
1926 camel_subscribable_folder_unsubscribed (subscribable, fi);
1927 camel_folder_info_free (fi);
1928
1929 if (((msi2->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_PUBLIC) != 0 &&
1930 (msi2->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_PUBLIC_REAL) == 0) ||
1931 (msi2->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_FOREIGN) != 0) {
1932 if (check_foreign_subfolders &&
1933 (msi2->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_FOREIGN) != 0 &&
1934 (msi2->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_FOREIGN_WITH_SUBFOLDERS) != 0) {
1935 res = mapi_store_unsubscribe_subfolders (mapi_store, msi2->folder_id, cancellable, error);
1936 }
1937
1938 if (res) {
1939 res = mapi_forget_folder (mapi_store, folder_name, error);
1940
1941 /* remove calls also free on 'si2' */
1942 camel_store_summary_remove (mapi_store->summary, si2);
1943 camel_store_summary_touch (mapi_store->summary);
1944 } else {
1945 camel_store_info_unref (si2);
1946 }
1947 } else {
1948 camel_store_info_unref (si2);
1949 }
1950 } else {
1951 g_debug ("%s: Failed to find subscribed by folder ID", G_STRFUNC);
1952 }
1953 } else {
1954 CamelSettings *settings;
1955 const gchar *profile;
1956
1957 settings = camel_service_ref_settings (CAMEL_SERVICE (mapi_store));
1958 profile = camel_mapi_settings_get_profile (CAMEL_MAPI_SETTINGS (settings));
1959
1960 res = e_mapi_folder_remove_as_esource (NULL,
1961 profile,
1962 msi->folder_id,
1963 cancellable,
1964 error);
1965
1966 g_object_unref (settings);
1967 }
1968
1969 if (res && (((msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_PUBLIC) != 0 &&
1970 (msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_PUBLIC_REAL) == 0) ||
1971 (msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_FOREIGN) != 0)) {
1972 if (check_foreign_subfolders &&
1973 (msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_FOREIGN) != 0 &&
1974 (msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_FOREIGN_WITH_SUBFOLDERS) != 0) {
1975 res = mapi_store_unsubscribe_subfolders (mapi_store, msi->folder_id, cancellable, error);
1976 }
1977
1978 if (res) {
1979 /* remove calls also free on 'si' */
1980 camel_store_summary_remove (mapi_store->summary, si);
1981 camel_store_summary_touch (mapi_store->summary);
1982 } else {
1983 camel_store_info_unref (si);
1984 }
1985 } else {
1986 camel_store_info_unref (si);
1987 }
1988
1989 camel_store_summary_save (mapi_store->summary);
1990
1991 return res;
1992 }
1993
1994 static gboolean
1995 mapi_store_unsubscribe_folder_sync (CamelSubscribable *subscribable,
1996 const gchar *folder_name,
1997 GCancellable *cancellable,
1998 GError **error)
1999 {
2000 return mapi_store_unsubscribe_folder_internal_sync (subscribable, folder_name, TRUE, cancellable, error);
2001 }
2002
2003 /* This can be particularly slow (with many folders) */
2004 static GSList * /* (transfer container) (element-type CamelMapiStoreInfo *) */
2005 mapi_store_gather_subfolders (GPtrArray *array, /* CamelMapiStoreInfo * */
2006 mapi_id_t parent_id)
2007 {
2008 GSList *subfolders = NULL;
2009 guint ii;
2010
2011 if (!array)
2012 return NULL;
2013
2014 for (ii = 0; ii < array->len; ii++) {
2015 CamelMapiStoreInfo *msi = g_ptr_array_index (array, ii);
2016
2017 if (msi && msi->parent_id == parent_id) {
2018 GSList *subsub;
2019
2020 subfolders = g_slist_prepend (subfolders, msi);
2021
2022 subsub = mapi_store_gather_subfolders (array, msi->folder_id);
2023 if (subsub)
2024 subfolders = g_slist_concat (subfolders, subsub);
2025 }
2026 }
2027
2028 return subfolders;
2029 }
2030
2031 static gboolean
2032 mapi_store_unsubscribe_subfolders (CamelMapiStore *mapi_store,
2033 mapi_id_t parent_id,
2034 GCancellable *cancellable,
2035 GError **error)
2036 {
2037 GPtrArray *array;
2038 GSList *subfolders, *link;
2039 gboolean res = TRUE;
2040
2041 array = camel_store_summary_array (mapi_store->summary);
2042 subfolders = mapi_store_gather_subfolders (array, parent_id);
2043
2044 for (link = subfolders; link && res; link = g_slist_next (link)) {
2045 CamelMapiStoreInfo *msi = link->data;
2046
2047 if (!msi || !(msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_FOREIGN))
2048 continue;
2049
2050 res = mapi_store_unsubscribe_folder_internal_sync (CAMEL_SUBSCRIBABLE (mapi_store),
2051 camel_store_info_get_path ((CamelStoreInfo *) msi),
2052 FALSE, cancellable, error);
2053 }
2054
2055 camel_store_summary_array_free (mapi_store->summary, array);
2056 g_slist_free (subfolders);
2057
2058 return res;
2059 }
2060
2061 static void
2062 mapi_migrate_to_user_cache_dir (CamelService *service)
2063 {
2064 const gchar *user_data_dir, *user_cache_dir;
2065
2066 g_return_if_fail (service != NULL);
2067 g_return_if_fail (CAMEL_IS_SERVICE (service));
2068
2069 user_data_dir = camel_service_get_user_data_dir (service);
2070 user_cache_dir = camel_service_get_user_cache_dir (service);
2071
2072 g_return_if_fail (user_data_dir != NULL);
2073 g_return_if_fail (user_cache_dir != NULL);
2074
2075 /* migrate only if the source directory exists and the destination doesn't */
2076 if (g_file_test (user_data_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) &&
2077 !g_file_test (user_cache_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
2078 gchar *parent_dir;
2079
2080 parent_dir = g_path_get_dirname (user_cache_dir);
2081 g_mkdir_with_parents (parent_dir, S_IRWXU);
2082 g_free (parent_dir);
2083
2084 if (g_rename (user_data_dir, user_cache_dir) == -1)
2085 g_debug ("%s: Failed to migrate '%s' to '%s': %s", G_STRFUNC, user_data_dir, user_cache_dir, g_strerror (errno));
2086 }
2087 }
2088
2089 static void
2090 camel_mapi_store_class_init (CamelMapiStoreClass *class)
2091 {
2092 GObjectClass *object_class;
2093 CamelServiceClass *service_class;
2094 CamelStoreClass *store_class;
2095
2096 /* register MAPIKRB auth type */
2097 CAMEL_TYPE_MAPI_SASL_KRB;
2098
2099 object_class = G_OBJECT_CLASS (class);
2100 object_class->dispose = mapi_store_dispose;
2101 object_class->finalize = mapi_store_finalize;
2102 object_class->constructed = mapi_store_constructed;
2103
2104 service_class = CAMEL_SERVICE_CLASS (class);
2105 service_class->settings_type = CAMEL_TYPE_MAPI_SETTINGS;
2106 service_class->get_name = mapi_get_name;
2107 service_class->connect_sync = mapi_connect_sync;
2108 service_class->disconnect_sync = mapi_disconnect_sync;
2109 service_class->authenticate_sync = mapi_authenticate_sync;
2110 service_class->query_auth_types_sync = mapi_query_auth_types_sync;
2111
2112 store_class = CAMEL_STORE_CLASS (class);
2113 store_class->can_refresh_folder = mapi_store_can_refresh_folder;
2114 store_class->get_folder_sync = mapi_store_get_folder_sync;
2115 store_class->get_folder_info_sync = mapi_store_get_folder_info_sync;
2116 store_class->get_junk_folder_sync = mapi_store_get_junk_folder_sync;
2117 store_class->get_trash_folder_sync = mapi_store_get_trash_folder_sync;
2118 store_class->create_folder_sync = mapi_store_create_folder_sync;
2119 store_class->delete_folder_sync = mapi_store_delete_folder_sync;
2120 store_class->rename_folder_sync = mapi_store_rename_folder_sync;
2121 }
2122
2123 static void
2124 camel_subscribable_init (CamelSubscribableInterface *iface)
2125 {
2126 iface->folder_is_subscribed = mapi_store_folder_is_subscribed;
2127 iface->subscribe_folder_sync = mapi_store_subscribe_folder_sync;
2128 iface->unsubscribe_folder_sync = mapi_store_unsubscribe_folder_sync;
2129 }
2130
2131 /*
2132 ** store is already initilyse to NULL or 0 value
2133 ** class already have a parent_class
2134 ** nothing must be doing here
2135 */
2136 static void
2137 camel_mapi_store_init (CamelMapiStore *mapi_store)
2138 {
2139 mapi_store->priv = camel_mapi_store_get_instance_private (mapi_store);
2140
2141 g_rec_mutex_init (&mapi_store->priv->connection_lock);
2142 g_rec_mutex_init (&mapi_store->priv->updates_lock);
2143 mapi_store->priv->updates_cancellable = NULL;
2144 mapi_store->priv->update_folder_names = NULL;
2145 mapi_store->priv->update_folder_id = 0;
2146 mapi_store->priv->update_folder_list_id = 0;
2147 }
2148
2149 /* service methods */
2150 static void
2151 mapi_store_constructed (GObject *object)
2152 {
2153 CamelMapiStore *mapi_store = CAMEL_MAPI_STORE (object);
2154 CamelStore *store = CAMEL_STORE (object);
2155 CamelMapiStorePrivate *priv = mapi_store->priv;
2156 CamelService *service;
2157 const gchar *user_cache_dir;
2158 gchar *path = NULL;
2159
2160 /* Chain up to parent's constructed() method. */
2161 G_OBJECT_CLASS (camel_mapi_store_parent_class)->constructed (object);
2162
2163 service = CAMEL_SERVICE (object);
2164 mapi_migrate_to_user_cache_dir (service);
2165
2166 user_cache_dir = camel_service_get_user_cache_dir (service);
2167
2168 /*store summary*/
2169 path = g_build_filename (user_cache_dir, ".summary", NULL);
2170
2171 mapi_store->summary = camel_mapi_store_summary_new ();
2172 camel_store_summary_set_filename (mapi_store->summary, path);
2173
2174 camel_store_summary_load (mapi_store->summary);
2175
2176 /*Hash Table*/
2177 priv->id_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); /* folder ID to folder Full name */
2178 priv->name_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); /* folder Full name to folder ID */
2179 /*priv->parent_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); / * folder ID to its parent folder ID */
2180 priv->default_folders = g_hash_table_new_full (g_int_hash, g_int_equal, g_free, g_free); /* default folder type to folder ID */
2181 priv->container_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
2182
2183 camel_store_set_flags (store, (camel_store_get_flags (store) & ~(CAMEL_STORE_VJUNK | CAMEL_STORE_VTRASH)) |
2184 CAMEL_STORE_REAL_JUNK_FOLDER | CAMEL_STORE_USE_CACHE_DIR);
2185
2186 g_free (path);
2187 }
2188
2189 static char *
2190 mapi_get_name(CamelService *service, gboolean brief)
2191 {
2192 CamelNetworkSettings *network_settings;
2193 CamelSettings *settings;
2194 gchar *host;
2195 gchar *name;
2196 gchar *user;
2197
2198 settings = camel_service_ref_settings (service);
2199
2200 network_settings = CAMEL_NETWORK_SETTINGS (settings);
2201 host = camel_network_settings_dup_host (network_settings);
2202 user = camel_network_settings_dup_user (network_settings);
2203
2204 g_object_unref (settings);
2205
2206 if (brief) {
2207 /* Translators: The %s is replaced with a server's host name */
2208 name = g_strdup_printf(_("Exchange MAPI server %s"), host);
2209 } else {
2210 /*To translators : Example string : Exchange MAPI service for
2211 _username_ on _server host name__*/
2212 name = g_strdup_printf(_("Exchange MAPI service for %s on %s"),
2213 user, host);
2214 }
2215
2216 g_free (host);
2217 g_free (user);
2218
2219 return name;
2220 }
2221
2222 static gboolean
2223 mapi_connect_sync (CamelService *service,
2224 GCancellable *cancellable,
2225 GError **error)
2226 {
2227 CamelMapiStore *store = CAMEL_MAPI_STORE (service);
2228 EMapiConnection *conn;
2229 CamelServiceConnectionStatus status;
2230 CamelSession *session;
2231 CamelSettings *settings;
2232 EMapiProfileData empd = { 0 };
2233 uint64_t current_size = -1, receive_quota = -1, send_quota = -1;
2234 gchar *name;
2235
2236 /* Chain up to parent's method. */
2237 if (!CAMEL_SERVICE_CLASS (camel_mapi_store_parent_class)->connect_sync (service, cancellable, error))
2238 return FALSE;
2239
2240 if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store))) {
2241 g_set_error_literal (
2242 error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_UNAVAILABLE,
2243 _("Cannot connect to MAPI store in offline mode"));
2244 return FALSE;
2245 }
2246
2247 session = camel_service_ref_session (service);
2248
2249 status = camel_service_get_connection_status (service);
2250 if (status == CAMEL_SERVICE_DISCONNECTED) {
2251 g_object_unref (session);
2252 return FALSE;
2253 }
2254
2255 if (check_for_connection (service, NULL)) {
2256 g_object_unref (session);
2257 return TRUE;
2258 }
2259
2260 name = camel_service_get_name (service, TRUE);
2261 camel_operation_push_message (cancellable, _("Connecting to “%s”"), name);
2262
2263 settings = camel_service_ref_settings (service);
2264 e_mapi_util_profiledata_from_settings (&empd, CAMEL_MAPI_SETTINGS (settings));
2265 g_object_unref (settings);
2266
2267 if (!camel_session_authenticate_sync (session, service, empd.krb_sso ? "MAPIKRB" : NULL, cancellable, error)) {
2268 camel_operation_pop_message (cancellable);
2269 g_object_unref (session);
2270 g_free (name);
2271 return FALSE;
2272 }
2273
2274 camel_operation_pop_message (cancellable);
2275
2276 camel_offline_store_set_online_sync (
2277 CAMEL_OFFLINE_STORE (store), TRUE, cancellable, NULL);
2278
2279 camel_store_summary_save (store->summary);
2280
2281 conn = camel_mapi_store_ref_connection (store, cancellable, error);
2282 if (!conn) {
2283 g_object_unref (session);
2284 g_free (name);
2285
2286 return FALSE;
2287 }
2288
2289 if (e_mapi_connection_get_store_quotas (conn, NULL, ¤t_size, &receive_quota, &send_quota, cancellable, NULL)) {
2290 if (current_size != -1) {
2291 gchar *msg = NULL;
2292
2293 /* warn/alert when the last 1% lefts from the size quota */
2294 if (send_quota != -1 && current_size * 0.95 >= send_quota) {
2295 if (send_quota != -1 && current_size >= send_quota) {
2296 msg = g_strdup_printf (_("Mailbox “%s” is full, no new messages will be received or sent."), name);
2297 } else {
2298 msg = g_strdup_printf (_("Mailbox “%s” is near its size limit, message send will be disabled soon."), name);
2299 }
2300 } else if (receive_quota != -1 && current_size * 0.95 >= receive_quota) {
2301 if (current_size >= receive_quota) {
2302 msg = g_strdup_printf (_("Mailbox “%s” is full, no new messages will be received."), name);
2303 } else {
2304 msg = g_strdup_printf (_("Mailbox “%s” is near its size limit."), name);
2305 }
2306 }
2307
2308 if (msg) {
2309 camel_session_user_alert (session, service, CAMEL_SESSION_ALERT_WARNING, msg);
2310 g_free (msg);
2311 }
2312 }
2313 }
2314
2315 g_object_unref (conn);
2316 g_free (name);
2317
2318 g_object_unref (session);
2319
2320 return TRUE;
2321 }
2322
2323 static gboolean
2324 mapi_disconnect_sync (CamelService *service,
2325 gboolean clean,
2326 GCancellable *cancellable,
2327 GError **error)
2328 {
2329 CamelMapiStore *store = CAMEL_MAPI_STORE (service);
2330
2331 stop_pending_updates (store);
2332
2333 g_rec_mutex_lock (&store->priv->connection_lock);
2334 if (store->priv->connection) {
2335 g_signal_handlers_disconnect_by_func (store->priv->connection, camel_mapi_store_server_notification_cb, store);
2336 e_mapi_connection_disable_notifications (store->priv->connection, NULL, cancellable, error);
2337
2338 /* Close the mapi subsystem */
2339 e_mapi_connection_disconnect (store->priv->connection, clean, clean ? cancellable : NULL, error);
2340
2341 g_object_unref (store->priv->connection);
2342 store->priv->connection = NULL;
2343 }
2344 g_rec_mutex_unlock (&store->priv->connection_lock);
2345
2346 store->priv->folders_synced = FALSE;
2347
2348 /* Chain up to parent's method. */
2349 return CAMEL_SERVICE_CLASS (camel_mapi_store_parent_class)->disconnect_sync (service, clean, cancellable, error);
2350 }
2351
2352 struct ScheduleUpdateData
2353 {
2354 GCancellable *cancellable;
2355 CamelMapiStore *mapi_store;
2356 GSList *foldernames;
2357 guint expected_id;
2358 };
2359
2360 static void
2361 free_schedule_update_data (gpointer ptr)
2362 {
2363 struct ScheduleUpdateData *sud = ptr;
2364
2365 if (!sud)
2366 return;
2367
2368 if (sud->cancellable)
2369 g_object_unref (sud->cancellable);
2370 g_slist_free_full (sud->foldernames, g_free);
2371 g_slice_free (struct ScheduleUpdateData, sud);
2372 }
2373
2374 static gpointer
2375 camel_mapi_folder_update_thread (gpointer user_data)
2376 {
2377 struct ScheduleUpdateData *sud = user_data;
2378 CamelMapiStore *mapi_store;
2379 GSList *fn;
2380
2381 g_return_val_if_fail (sud != NULL, NULL);
2382
2383 mapi_store = g_object_ref (sud->mapi_store);
2384
2385 for (fn = sud->foldernames; fn && !g_cancellable_is_cancelled (sud->cancellable); fn = fn->next) {
2386 const gchar *foldername = fn->data;
2387 CamelFolder *folder;
2388
2389 if (!foldername)
2390 continue;
2391
2392 folder = camel_store_get_folder_sync (CAMEL_STORE (mapi_store), foldername, 0, sud->cancellable, NULL);
2393 if (folder) {
2394 camel_folder_refresh_info_sync (folder, sud->cancellable, NULL);
2395 g_object_unref (folder);
2396 }
2397 }
2398
2399 if (!g_cancellable_is_cancelled (sud->cancellable) &&
2400 !mapi_store->priv->folders_synced)
2401 mapi_folders_sync (sud->mapi_store, CAMEL_STORE_FOLDER_INFO_RECURSIVE | CAMEL_STORE_FOLDER_INFO_SUBSCRIBED, sud->cancellable, NULL);
2402
2403 g_object_unref (mapi_store);
2404
2405 free_schedule_update_data (sud);
2406
2407 return NULL;
2408 }
2409
2410 static void
2411 run_update_thread (CamelMapiStore *mapi_store,
2412 GCancellable *cancellable,
2413 GSList *foldernames)
2414 {
2415 struct ScheduleUpdateData *sud;
2416 GThread *thread;
2417
2418 g_return_if_fail (mapi_store != NULL);
2419 g_return_if_fail (cancellable != NULL);
2420
2421 sud = g_slice_new0 (struct ScheduleUpdateData);
2422 sud->mapi_store = mapi_store;
2423 sud->cancellable = g_object_ref (cancellable);
2424 sud->foldernames = foldernames;
2425
2426 thread = g_thread_new (NULL, camel_mapi_folder_update_thread, sud);
2427 g_thread_unref (thread);
2428 }
2429
2430 static gboolean
2431 folder_update_cb (gpointer user_data)
2432 {
2433 struct ScheduleUpdateData *sud = user_data;
2434 GSList *foldernames;
2435
2436 g_return_val_if_fail (sud != NULL, FALSE);
2437
2438 if (g_cancellable_is_cancelled (sud->cancellable))
2439 return FALSE;
2440
2441 g_return_val_if_fail (sud->mapi_store != NULL, FALSE);
2442 g_return_val_if_fail (sud->mapi_store->priv != NULL, FALSE);
2443
2444 g_rec_mutex_lock (&sud->mapi_store->priv->updates_lock);
2445 if (sud->expected_id != sud->mapi_store->priv->update_folder_id) {
2446 g_rec_mutex_unlock (&sud->mapi_store->priv->updates_lock);
2447 return FALSE;
2448 }
2449
2450 foldernames = sud->mapi_store->priv->update_folder_names;
2451 sud->mapi_store->priv->update_folder_names = NULL;
2452 sud->mapi_store->priv->update_folder_id = 0;
2453
2454 if (!g_cancellable_is_cancelled (sud->cancellable))
2455 run_update_thread (sud->mapi_store, sud->cancellable, foldernames);
2456 else
2457 g_slist_free_full (foldernames, g_free);
2458
2459 g_rec_mutex_unlock (&sud->mapi_store->priv->updates_lock);
2460
2461 return FALSE;
2462 }
2463
2464 static void
2465 schedule_folder_update (CamelMapiStore *mapi_store, mapi_id_t fid)
2466 {
2467 gchar *fidstr;
2468 const gchar *foldername;
2469 struct ScheduleUpdateData *sud;
2470 CamelStoreInfo *si;
2471 CamelMapiStoreInfo *msi;
2472
2473 g_return_if_fail (mapi_store != NULL);
2474 g_return_if_fail (mapi_store->priv != NULL);
2475
2476 si = camel_mapi_store_summary_get_folder_id (mapi_store->summary, fid);
2477 if (!si)
2478 return;
2479
2480 msi = (CamelMapiStoreInfo *) si;
2481 if ((msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_MAIL) == 0) {
2482 camel_store_info_unref (si);
2483 return;
2484 }
2485
2486 camel_store_info_unref (si);
2487
2488 fidstr = e_mapi_util_mapi_id_to_string (fid);
2489 if (!fidstr)
2490 return;
2491
2492 foldername = camel_mapi_store_folder_lookup (mapi_store, fidstr);
2493 g_free (fidstr);
2494
2495 if (!foldername)
2496 return;
2497
2498 g_rec_mutex_lock (&mapi_store->priv->updates_lock);
2499 if (!mapi_store->priv->updates_cancellable ||
2500 g_slist_find_custom (mapi_store->priv->update_folder_names, foldername, (GCompareFunc) g_ascii_strcasecmp) != 0) {
2501 g_rec_mutex_unlock (&mapi_store->priv->updates_lock);
2502 return;
2503 }
2504
2505 sud = g_slice_new0 (struct ScheduleUpdateData);
2506 sud->cancellable = g_object_ref (mapi_store->priv->updates_cancellable);
2507 sud->mapi_store = mapi_store;
2508
2509 mapi_store->priv->update_folder_names = g_slist_prepend (mapi_store->priv->update_folder_names, g_strdup (foldername));
2510 if (mapi_store->priv->update_folder_id)
2511 g_source_remove (mapi_store->priv->update_folder_id);
2512 mapi_store->priv->update_folder_id = g_timeout_add_seconds_full (G_PRIORITY_LOW, 5, folder_update_cb, sud, free_schedule_update_data);
2513 sud->expected_id = mapi_store->priv->update_folder_id;
2514
2515 g_rec_mutex_unlock (&mapi_store->priv->updates_lock);
2516 }
2517
2518 static gboolean
2519 folder_list_update_cb (gpointer user_data)
2520 {
2521 struct ScheduleUpdateData *sud = user_data;
2522
2523 g_return_val_if_fail (sud != NULL, FALSE);
2524
2525 if (g_cancellable_is_cancelled (sud->cancellable))
2526 return FALSE;
2527
2528 g_return_val_if_fail (sud->mapi_store != NULL, FALSE);
2529 g_return_val_if_fail (sud->mapi_store->priv != NULL, FALSE);
2530
2531 g_rec_mutex_lock (&sud->mapi_store->priv->updates_lock);
2532 if (sud->expected_id != sud->mapi_store->priv->update_folder_list_id) {
2533 g_rec_mutex_unlock (&sud->mapi_store->priv->updates_lock);
2534 return FALSE;
2535 }
2536
2537 sud->mapi_store->priv->folders_synced = FALSE;
2538 sud->mapi_store->priv->update_folder_list_id = 0;
2539
2540 if (!g_cancellable_is_cancelled (sud->cancellable))
2541 run_update_thread (sud->mapi_store, sud->cancellable, NULL);
2542
2543 g_rec_mutex_unlock (&sud->mapi_store->priv->updates_lock);
2544
2545 return FALSE;
2546 }
2547
2548 static void
2549 schedule_folder_list_update (CamelMapiStore *mapi_store)
2550 {
2551 struct ScheduleUpdateData *sud;
2552
2553 g_return_if_fail (mapi_store != NULL);
2554 g_return_if_fail (mapi_store->priv != NULL);
2555
2556 g_rec_mutex_lock (&mapi_store->priv->updates_lock);
2557 if (!mapi_store->priv->updates_cancellable) {
2558 g_rec_mutex_unlock (&mapi_store->priv->updates_lock);
2559 return;
2560 }
2561
2562 sud = g_slice_new0 (struct ScheduleUpdateData);
2563 sud->cancellable = g_object_ref (mapi_store->priv->updates_cancellable);
2564 sud->mapi_store = mapi_store;
2565
2566 if (mapi_store->priv->update_folder_list_id)
2567 g_source_remove (mapi_store->priv->update_folder_list_id);
2568 mapi_store->priv->update_folder_list_id = g_timeout_add_seconds_full (G_PRIORITY_LOW, 5, folder_list_update_cb, sud, free_schedule_update_data);
2569 sud->expected_id = mapi_store->priv->update_folder_list_id;
2570
2571 g_rec_mutex_unlock (&mapi_store->priv->updates_lock);
2572 }
2573
2574 static void
2575 camel_mapi_store_server_notification_cb (EMapiConnection *conn,
2576 guint event_mask,
2577 gpointer event_data,
2578 gpointer user_data)
2579 {
2580 CamelMapiStore *mapi_store = user_data;
2581 mapi_id_t update_folder1 = 0, update_folder2 = 0;
2582 gboolean update_folder_list = FALSE;
2583
2584 g_return_if_fail (mapi_store != NULL);
2585 g_return_if_fail (mapi_store->priv != NULL);
2586
2587 switch (event_mask) {
2588 /* -- Folder Events -- */
2589 case fnevObjectCreated:
2590 d (printf ("Event : Folder Created\n"));
2591 d (mapidump_foldercreated (event_data, "\t"));
2592 update_folder_list = TRUE;
2593 break;
2594 case fnevObjectDeleted:
2595 d (printf ("Event : Folder Deleted\n"));
2596 d (mapidump_folderdeleted (event_data, "\t"));
2597 update_folder_list = TRUE;
2598 break;
2599 case fnevObjectMoved:
2600 d (printf ("Event : Folder Moved\n"));
2601 d (mapidump_foldermoved (event_data, "\t"));
2602 update_folder_list = TRUE;
2603 break;
2604 case fnevObjectCopied:
2605 d (printf ("Event : Folder Copied\n"));
2606 d (mapidump_foldercopied (event_data, "\t"));
2607 update_folder_list = TRUE;
2608 break;
2609
2610 /* -- Message Events -- */
2611 case fnevNewMail:
2612 case fnevNewMail | fnevMbit: {
2613 struct NewMailNotification *newmail = event_data;
2614
2615 d (printf ("Event : New mail\n"));
2616 d (mapidump_newmail (event_data, "\t"));
2617
2618 if (newmail)
2619 update_folder1 = newmail->FID;
2620 } break;
2621 case fnevMbit | fnevObjectCreated: {
2622 struct MessageCreatedNotification *msgcreated = event_data;
2623
2624 d (printf ("Event : Message created\n"));
2625 d (mapidump_messagecreated (event_data, "\t"));
2626
2627 if (msgcreated)
2628 update_folder1 = msgcreated->FID;
2629 } break;
2630 case fnevMbit | fnevObjectModified: {
2631 struct MessageModifiedNotification *msgmodified = event_data;
2632
2633 d (printf ("Event : Message modified\n"));
2634 d (mapidump_messagemodified (event_data, "\t"));
2635
2636 if (msgmodified)
2637 update_folder1 = msgmodified->FID;
2638 } break;
2639 case fnevMbit | fnevObjectDeleted: {
2640 struct MessageDeletedNotification *msgdeleted = event_data;
2641
2642 d (printf ("Event : Message deleted\n"));
2643 d (mapidump_messagedeleted (event_data, "\t"));
2644
2645 if (msgdeleted)
2646 update_folder1 = msgdeleted->FID;
2647 } break;
2648 case fnevMbit | fnevObjectMoved: {
2649 struct MessageMoveCopyNotification *msgmoved = event_data;
2650
2651 d (printf ("Event : Message moved\n"));
2652 d (mapidump_messagemoved (event_data, "\t"));
2653
2654 if (msgmoved) {
2655 update_folder1 = msgmoved->OldFID;
2656 update_folder2 = msgmoved->FID;
2657 }
2658 } break;
2659 case fnevMbit | fnevObjectCopied: {
2660 struct MessageMoveCopyNotification *msgcopied = event_data;
2661
2662 d (printf ("Event : Message copied\n"));
2663 d (mapidump_messagecopied (event_data, "\t"));
2664
2665 if (msgcopied) {
2666 update_folder1 = msgcopied->OldFID;
2667 update_folder2 = msgcopied->FID;
2668 }
2669 } break;
2670 default:
2671 /* Unsupported */
2672 break;
2673 }
2674
2675 if (update_folder1 > 0)
2676 schedule_folder_update (mapi_store, update_folder1);
2677 if (update_folder2 > 0)
2678 schedule_folder_update (mapi_store, update_folder2);
2679 if (update_folder_list)
2680 schedule_folder_list_update (mapi_store);
2681 }
2682
2683 static gboolean
2684 camel_mapi_add_foreign_folder (CamelMapiStore *mapi_store,
2685 CamelMapiStoreInfo *owner_msi,
2686 EMapiFolder *mapi_folder,
2687 CamelFolderInfo *fi,
2688 GError **error)
2689 {
2690 gboolean success;
2691
2692 g_return_val_if_fail (CAMEL_IS_MAPI_STORE (mapi_store), FALSE);
2693 g_return_val_if_fail (owner_msi != NULL, FALSE);
2694 g_return_val_if_fail (mapi_folder != NULL, FALSE);
2695 g_return_val_if_fail (fi != NULL, FALSE);
2696
2697 success = camel_mapi_store_summary_add_from_full (mapi_store->summary, fi->full_name,
2698 e_mapi_folder_get_id (mapi_folder), e_mapi_folder_get_parent_id (mapi_folder),
2699 CAMEL_STORE_INFO_FOLDER_SUBSCRIBED | CAMEL_FOLDER_NOCHILDREN | CAMEL_FOLDER_SUBSCRIBED,
2700 CAMEL_MAPI_STORE_FOLDER_FLAG_FOREIGN | CAMEL_MAPI_STORE_FOLDER_FLAG_MAIL,
2701 owner_msi->foreign_username) != NULL;
2702
2703 if (success) {
2704 CamelStoreInfo *parent_si;
2705
2706 parent_si = camel_mapi_store_summary_get_folder_id (mapi_store->summary, e_mapi_folder_get_parent_id (mapi_folder));
2707 if (parent_si) {
2708 CamelMapiStoreInfo *parent_msi = (CamelMapiStoreInfo *) parent_si;
2709
2710 parent_msi->camel_folder_flags = parent_msi->camel_folder_flags & (~CAMEL_FOLDER_NOCHILDREN);
2711
2712 camel_store_info_unref (parent_si);
2713 }
2714
2715 owner_msi->camel_folder_flags = owner_msi->camel_folder_flags & (~CAMEL_FOLDER_NOCHILDREN);
2716
2717 camel_store_summary_touch (mapi_store->summary);
2718
2719 camel_mapi_store_announce_subscribed_folder (mapi_store, fi->full_name);
2720 } else {
2721 g_set_error (error, E_MAPI_ERROR, MAPI_E_INVALID_PARAMETER,
2722 _("Cannot add folder “%s”, failed to add to store’s summary"), fi->full_name);
2723 }
2724
2725 return success;
2726 }
2727
2728 static gboolean
2729 mapi_store_unsubscribe_with_subfolders (CamelMapiStore *mapi_store,
2730 CamelMapiStoreInfo *parent_msi,
2731 GPtrArray *array, /* CamelMapiStoreInfo * */
2732 GHashTable *processed_fids, /* gchar * ~> NULL */
2733 GCancellable *cancellable,
2734 GError **error)
2735 {
2736 GSList *subfolders, *link;
2737 gboolean success = TRUE;
2738
2739 g_return_val_if_fail (CAMEL_IS_MAPI_STORE (mapi_store), FALSE);
2740 g_return_val_if_fail (parent_msi != NULL, FALSE);
2741
2742 if (!array)
2743 return TRUE;
2744
2745 subfolders = mapi_store_gather_subfolders (array, parent_msi->folder_id);
2746 subfolders = g_slist_prepend (subfolders, parent_msi);
2747
2748 for (link = subfolders; link && success; link = g_slist_next (link)) {
2749 CamelMapiStoreInfo *msi = link->data;
2750
2751 if (msi) {
2752 g_hash_table_insert (processed_fids, e_mapi_util_mapi_id_to_string (msi->folder_id), NULL);
2753
2754 success = mapi_store_unsubscribe_folder_internal_sync (CAMEL_SUBSCRIBABLE (mapi_store),
2755 camel_store_info_get_path ((CamelStoreInfo *) msi),
2756 FALSE, cancellable, error);
2757 }
2758 }
2759
2760 g_slist_free (subfolders);
2761
2762 return success;
2763 }
2764
2765 static gboolean
2766 mapi_store_merge_with_subfolders (CamelMapiStore *mapi_store,
2767 GSList *mapi_folders, /* EMapiFolder * */
2768 CamelMapiStoreInfo *parent_msi,
2769 GPtrArray *array, /* CamelMapiStoreInfo * */
2770 GHashTable *processed_fids, /* gchar * ~> NULL */
2771 GCancellable *cancellable,
2772 GError **error)
2773 {
2774 GSList *subfolders, *link;
2775 GHashTable *existing;
2776 gboolean success = TRUE;
2777
2778 g_return_val_if_fail (CAMEL_IS_MAPI_STORE (mapi_store), FALSE);
2779 g_return_val_if_fail (parent_msi != NULL, FALSE);
2780
2781 if (!array)
2782 return TRUE;
2783
2784 existing = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
2785 subfolders = mapi_store_gather_subfolders (array, parent_msi->folder_id);
2786
2787 for (link = subfolders; link; link = g_slist_next (link)) {
2788 CamelMapiStoreInfo *msi = link->data;
2789
2790 if (msi) {
2791 g_hash_table_insert (processed_fids, e_mapi_util_mapi_id_to_string (msi->folder_id), NULL);
2792 g_hash_table_insert (existing, e_mapi_util_mapi_id_to_string (msi->folder_id), msi);
2793 }
2794 }
2795
2796 for (link = mapi_folders; link && success; link = g_slist_next (link)) {
2797 EMapiFolder *mapi_folder = link->data;
2798 CamelMapiStoreInfo *msi;
2799 CamelFolderInfo *fi;
2800 gchar *fid;
2801
2802 if (!mapi_folder)
2803 continue;
2804
2805 fi = mapi_convert_to_folder_info (mapi_store, mapi_folder, NULL);
2806 if (!fi)
2807 continue;
2808
2809 fid = e_mapi_util_mapi_id_to_string (mapi_folder->folder_id);
2810 if (!fid) {
2811 camel_folder_info_free (fi);
2812 continue;
2813 }
2814
2815 msi = g_hash_table_lookup (existing, fid);
2816
2817 if (msi) {
2818 const gchar *path;
2819
2820 path = camel_store_info_get_path ((CamelStoreInfo *) msi);
2821
2822 if (g_strcmp0 (fi->full_name, path) != 0) {
2823 mapi_rename_folder_infos (mapi_store, path, fi->full_name);
2824
2825 g_hash_table_remove (mapi_store->priv->name_hash, path);
2826 g_hash_table_remove (mapi_store->priv->id_hash, fid);
2827
2828 mapi_update_folder_hash_tables (mapi_store, fi->full_name, fid, NULL);
2829
2830 camel_store_info_set_value ((CamelStoreInfo *) msi, CAMEL_STORE_INFO_PATH, fi->full_name);
2831 camel_store_summary_touch (mapi_store->summary);
2832 }
2833
2834 g_hash_table_remove (existing, fid);
2835 } else {
2836 if (e_mapi_folder_get_type (mapi_folder) == E_MAPI_FOLDER_TYPE_MAIL) {
2837 success = camel_mapi_add_foreign_folder (mapi_store, parent_msi, mapi_folder, fi, error);
2838 } else {
2839 CamelSettings *settings;
2840 gchar *profile;
2841
2842 settings = camel_service_ref_settings (CAMEL_SERVICE (mapi_store));
2843 profile = camel_mapi_settings_dup_profile (CAMEL_MAPI_SETTINGS (settings));
2844
2845 g_object_unref (settings);
2846
2847 success = e_mapi_folder_add_as_esource (NULL, e_mapi_folder_get_type (mapi_folder),
2848 profile,
2849 TRUE /* camel_offline_settings_get_stay_synchronized (CAMEL_OFFLINE_SETTINGS (mapi_settings)) */,
2850 E_MAPI_FOLDER_CATEGORY_FOREIGN,
2851 parent_msi->foreign_username,
2852 e_mapi_folder_get_name (mapi_folder),
2853 e_mapi_folder_get_id (mapi_folder),
2854 0,
2855 cancellable,
2856 error);
2857
2858 g_free (profile);
2859 }
2860 }
2861
2862 camel_folder_info_free (fi);
2863 g_free (fid);
2864 }
2865
2866 if (success && g_hash_table_size (existing)) {
2867 GHashTableIter iter;
2868 gpointer value;
2869
2870 g_hash_table_iter_init (&iter, existing);
2871 while (success && g_hash_table_iter_next (&iter, NULL, &value)) {
2872 CamelMapiStoreInfo *msi = value;
2873
2874 if (msi) {
2875 success = mapi_store_unsubscribe_folder_internal_sync (CAMEL_SUBSCRIBABLE (mapi_store),
2876 camel_store_info_get_path ((CamelStoreInfo *) msi),
2877 TRUE, cancellable, error);
2878 }
2879 }
2880 }
2881
2882 camel_store_summary_save (mapi_store->summary);
2883
2884 g_hash_table_destroy (existing);
2885 g_slist_free (subfolders);
2886
2887 return success;
2888 }
2889
2890 static void
2891 mapi_store_update_foreign_subfolders_thread (CamelSession *session,
2892 GCancellable *cancellable,
2893 gpointer user_data,
2894 GError **error)
2895 {
2896 CamelMapiStore *mapi_store = user_data;
2897 EMapiConnection *connection;
2898 GHashTable *processed_fids;
2899 GPtrArray *array;
2900 guint ii;
2901
2902 g_return_if_fail (CAMEL_IS_MAPI_STORE (mapi_store));
2903
2904 connection = camel_mapi_store_ref_connection (mapi_store, cancellable, error);
2905 if (!connection)
2906 return;
2907
2908 processed_fids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
2909
2910 array = camel_store_summary_array (mapi_store->summary);
2911 for (ii = 0; array && ii < array->len; ii++) {
2912 CamelMapiStoreInfo *msi = g_ptr_array_index (array, ii);
2913
2914 if ((msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_FOREIGN) != 0 &&
2915 (msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_FOREIGN_WITH_SUBFOLDERS) != 0) {
2916 gchar *fid = e_mapi_util_mapi_id_to_string (msi->folder_id);
2917 mapi_object_t obj_folder;
2918 GSList *mapi_folders = NULL;
2919 gboolean success = TRUE;
2920 GError *local_error = NULL;
2921
2922 if (!fid || g_hash_table_contains (processed_fids, fid)) {
2923 g_free (fid);
2924 continue;
2925 }
2926
2927 g_hash_table_insert (processed_fids, fid, NULL);
2928
2929 if (!e_mapi_connection_open_foreign_folder (connection, msi->foreign_username, msi->folder_id, &obj_folder, cancellable, &local_error)) {
2930 if (!g_cancellable_is_cancelled (cancellable) &&
2931 camel_offline_store_get_online (CAMEL_OFFLINE_STORE (mapi_store))) {
2932 /* Unsubscribe from it only if it could not be found */
2933 if (g_error_matches (local_error, E_MAPI_ERROR, MAPI_E_NOT_FOUND) &&
2934 !mapi_store_unsubscribe_with_subfolders (mapi_store, msi, array, processed_fids, cancellable, error)) {
2935 g_clear_error (&local_error);
2936 break;
2937 }
2938
2939 g_clear_error (&local_error);
2940 continue;
2941 } else {
2942 if (local_error)
2943 g_propagate_error (error, local_error);
2944
2945 make_mapi_error (error, "e_mapi_connection_open_foreign_folder", MAPI_E_CALL_FAILED);
2946 break;
2947 }
2948 }
2949
2950 if (e_mapi_connection_get_subfolders_list (connection, &obj_folder, E_MAPI_FOLDER_CATEGORY_FOREIGN,
2951 &mapi_folders, camel_mapi_update_operation_progress_cb, NULL, cancellable, NULL)) {
2952 success = mapi_store_merge_with_subfolders (mapi_store, mapi_folders, msi, array, processed_fids, cancellable, error);
2953 }
2954
2955 g_slist_free_full (mapi_folders, (GDestroyNotify) e_mapi_folder_free);
2956
2957 if (!e_mapi_connection_close_folder (connection, &obj_folder, cancellable, error) || !success)
2958 break;
2959 }
2960 }
2961
2962 camel_store_summary_array_free (mapi_store->summary, array);
2963 g_hash_table_destroy (processed_fids);
2964 g_object_unref (connection);
2965 }
2966
2967 static CamelAuthenticationResult
2968 mapi_authenticate_sync (CamelService *service,
2969 const gchar *mechanism,
2970 GCancellable *cancellable,
2971 GError **error)
2972 {
2973 CamelAuthenticationResult result;
2974 CamelMapiStore *store = CAMEL_MAPI_STORE (service);
2975 CamelSession *session;
2976 CamelSettings *settings;
2977 CamelMapiSettings *mapi_settings;
2978 CamelNetworkSettings *network_settings;
2979 ESourceRegistry *registry;
2980 EMapiProfileData empd = { 0 };
2981 const gchar *profile;
2982 const gchar *password;
2983 GError *mapi_error = NULL, *krb_error = NULL;
2984 ENamedParameters *credentials;
2985
2986 settings = camel_service_ref_settings (service);
2987 mapi_settings = CAMEL_MAPI_SETTINGS (settings);
2988 network_settings = CAMEL_NETWORK_SETTINGS (settings);
2989
2990 empd.server = camel_network_settings_get_host (network_settings);
2991 empd.username = camel_network_settings_get_user (network_settings);
2992 e_mapi_util_profiledata_from_settings (&empd, mapi_settings);
2993
2994 profile = camel_mapi_settings_get_profile (mapi_settings);
2995
2996 if (empd.krb_sso) {
2997 e_mapi_util_trigger_krb_auth (&empd, &krb_error);
2998 password = NULL;
2999 } else {
3000 password = camel_service_get_password (service);
3001
3002 if (password == NULL) {
3003 g_set_error_literal (
3004 error, CAMEL_SERVICE_ERROR,
3005 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
3006 _("Authentication password not available"));
3007 g_object_unref (settings);
3008 return CAMEL_AUTHENTICATION_ERROR;
3009 }
3010 }
3011
3012 credentials = e_named_parameters_new ();
3013 e_named_parameters_set (credentials, E_SOURCE_CREDENTIAL_PASSWORD, password);
3014 g_rec_mutex_lock (&store->priv->connection_lock);
3015 session = camel_service_ref_session (service);
3016 registry = e_source_registry_new_sync (NULL, NULL);
3017 store->priv->connection = e_mapi_connection_new (registry, profile, credentials, cancellable, &mapi_error);
3018 e_named_parameters_free (credentials);
3019 g_clear_object (®istry);
3020 if (store->priv->connection && e_mapi_connection_connected (store->priv->connection)) {
3021 GPtrArray *array;
3022 guint ii;
3023
3024 result = CAMEL_AUTHENTICATION_ACCEPTED;
3025
3026 if (!store->priv->updates_cancellable)
3027 store->priv->updates_cancellable = g_cancellable_new ();
3028
3029 g_signal_connect (store->priv->connection, "server-notification", G_CALLBACK (camel_mapi_store_server_notification_cb), store);
3030
3031 if (camel_mapi_settings_get_listen_notifications (mapi_settings))
3032 e_mapi_connection_enable_notifications (store->priv->connection, NULL, 0, NULL, NULL);
3033
3034 /* Also update folder structures of foreign folders,
3035 those which are subscribed with subfolders */
3036 array = camel_store_summary_array (store->summary);
3037 for (ii = 0; array && ii < array->len; ii++) {
3038 CamelMapiStoreInfo *msi = g_ptr_array_index (array, ii);
3039
3040 if ((msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_FOREIGN) != 0 &&
3041 (msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_FOREIGN_WITH_SUBFOLDERS) != 0) {
3042 camel_session_submit_job (session, _("Updating foreign folders"),
3043 mapi_store_update_foreign_subfolders_thread,
3044 g_object_ref (store), g_object_unref);
3045 break;
3046 }
3047 }
3048
3049 camel_store_summary_array_free (store->summary, array);
3050 } else if (!krb_error && (
3051 g_error_matches (mapi_error, E_MAPI_ERROR, MAPI_E_LOGON_FAILED) ||
3052 g_error_matches (mapi_error, E_MAPI_ERROR, ecRpcFailed))) {
3053 g_clear_error (&mapi_error);
3054 result = CAMEL_AUTHENTICATION_REJECTED;
3055 } else {
3056 /* mapi_error should be set */
3057 g_return_val_if_fail (
3058 mapi_error != NULL,
3059 CAMEL_AUTHENTICATION_ERROR);
3060 if (!e_mapi_utils_propagate_cancelled_error (mapi_error, error)) {
3061 if (krb_error && mapi_error) {
3062 GError *new_error = g_error_new (mapi_error->domain, mapi_error->code,
3063 /* Translators: the first '%s' is replaced with a generic error message,
3064 the second '%s' is replaced with additional error information. */
3065 C_("gssapi_error", "%s (%s)"), mapi_error->message, krb_error->message);
3066 g_propagate_error (error, new_error);
3067 } else if (krb_error) {
3068 g_propagate_error (error, krb_error);
3069 krb_error = NULL;
3070 } else if (mapi_error) {
3071 g_propagate_error (error, mapi_error);
3072 mapi_error = NULL;
3073 }
3074
3075 g_clear_error (&mapi_error);
3076 g_clear_error (&krb_error);
3077 } else {
3078 g_clear_error (&mapi_error);
3079 }
3080 result = CAMEL_AUTHENTICATION_ERROR;
3081 }
3082
3083 g_rec_mutex_unlock (&store->priv->connection_lock);
3084
3085 g_clear_error (&krb_error);
3086 g_object_unref (settings);
3087 g_object_unref (session);
3088
3089 return result;
3090 }
3091
3092 static GList *
3093 mapi_query_auth_types_sync (CamelService *service,
3094 GCancellable *cancellable,
3095 GError **error)
3096 {
3097 return NULL;
3098 }
3099
3100 static gboolean
3101 hash_check_fid_presence (gpointer key, gpointer value, gpointer folder_id)
3102 {
3103 return (g_ascii_strcasecmp (value, folder_id) == 0);
3104 }
3105
3106 static gboolean
3107 mapi_fid_is_system_folder (CamelMapiStore *mapi_store, const gchar *fid)
3108 {
3109 CamelMapiStorePrivate *priv = mapi_store->priv;
3110
3111 if (!(fid && *fid))
3112 return FALSE;
3113
3114 return (g_hash_table_find (priv->default_folders, hash_check_fid_presence, (gpointer) fid) != NULL);
3115 }
3116
3117 static const gchar *
3118 mapi_system_folder_fid (CamelMapiStore *mapi_store, gint folder_type)
3119 {
3120 CamelMapiStorePrivate *priv = mapi_store->priv;
3121
3122 return g_hash_table_lookup (priv->default_folders, &folder_type);
3123 }
3124
3125 static CamelFolderInfo *
3126 mapi_build_folder_info (CamelMapiStore *mapi_store, const gchar *parent_name, const gchar *folder_name)
3127 {
3128 const gchar *name;
3129 CamelFolderInfo *fi;
3130
3131 fi = camel_folder_info_new ();
3132
3133 fi->unread = -1;
3134 fi->total = -1;
3135
3136 if (parent_name && *parent_name)
3137 fi->full_name = g_strconcat (parent_name, "/", folder_name, NULL);
3138 else
3139 fi->full_name = g_strdup (folder_name);
3140
3141 name = strrchr(fi->full_name,'/');
3142 if (name == NULL)
3143 name = fi->full_name;
3144 else
3145 name++;
3146
3147 fi->display_name = g_strdup (name);
3148
3149 return fi;
3150 }
3151
3152 gboolean
3153 camel_mapi_store_connected (CamelMapiStore *mapi_store,
3154 GCancellable *cancellable,
3155 GError **error)
3156 {
3157 return camel_offline_store_get_online (CAMEL_OFFLINE_STORE (mapi_store))
3158 && camel_service_connect_sync (CAMEL_SERVICE (mapi_store), cancellable, error);
3159 }
3160
3161 void
3162 camel_mapi_store_maybe_disconnect (CamelMapiStore *mapi_store,
3163 const GError *mapi_error)
3164 {
3165 g_return_if_fail (CAMEL_IS_MAPI_STORE (mapi_store));
3166
3167 /* no error or already disconnected */
3168 g_rec_mutex_lock (&mapi_store->priv->connection_lock);
3169 if (!mapi_error || !mapi_store->priv->connection) {
3170 g_rec_mutex_unlock (&mapi_store->priv->connection_lock);
3171 return;
3172 }
3173 g_rec_mutex_unlock (&mapi_store->priv->connection_lock);
3174
3175 if (g_error_matches (mapi_error, E_MAPI_ERROR, ecRpcFailed) ||
3176 g_error_matches (mapi_error, E_MAPI_ERROR, MAPI_E_CALL_FAILED))
3177 camel_service_disconnect_sync (CAMEL_SERVICE (mapi_store),
3178 !g_error_matches (mapi_error, E_MAPI_ERROR, ecRpcFailed),
3179 NULL, NULL);
3180 }
3181
3182 static void
3183 mapi_update_hash_table_type (CamelMapiStore *store, const gchar *full_name, guint *folder_type)
3184 {
3185 CamelMapiStorePrivate *priv = store->priv;
3186 if (full_name && folder_type) {
3187 if (!g_hash_table_lookup (priv->container_hash, full_name))
3188 g_hash_table_insert (priv->container_hash, g_strdup (full_name), folder_type);
3189 else
3190 g_free (folder_type);
3191 } else {
3192 g_free (folder_type);
3193 }
3194 }
3195
3196 static void
3197 mapi_update_folder_hash_tables (CamelMapiStore *store, const gchar *full_name, const gchar *fid, const gchar *parent_id)
3198 {
3199 CamelMapiStorePrivate *priv = store->priv;
3200
3201 if (fid && full_name) {
3202 /*id_hash returns the name for a given container id*/
3203 if (!g_hash_table_lookup (priv->id_hash, fid))
3204 g_hash_table_insert (priv->id_hash, g_strdup (fid), g_strdup (full_name));
3205
3206 /* name_hash : name <-> fid mapping */
3207 if (!g_hash_table_lookup (priv->name_hash, full_name))
3208 g_hash_table_insert (priv->name_hash, g_strdup (full_name), g_strdup (fid));
3209 }
3210
3211 /*parent_hash returns the parent container id, given an id*/
3212 /*if (fid && parent_id && !g_hash_table_lookup (priv->parent_hash, fid))
3213 g_hash_table_insert (priv->parent_hash, g_strdup (fid), g_strdup (parent_id));*/
3214
3215 }
3216
3217 static void
3218 mapi_folders_update_hash_tables_from_cache (CamelMapiStore *store)
3219 {
3220 GPtrArray *array;
3221 guint ii;
3222
3223 array = camel_store_summary_array (store->summary);
3224
3225 for (ii = 0; ii < array->len; ii++) {
3226 CamelMapiStoreInfo *msi;
3227 gchar *fid, *pid;
3228
3229 msi = g_ptr_array_index (array, ii);
3230
3231 fid = e_mapi_util_mapi_id_to_string (msi->folder_id);
3232 pid = e_mapi_util_mapi_id_to_string (msi->parent_id);
3233
3234 mapi_update_folder_hash_tables (store, camel_store_info_get_path ((CamelStoreInfo *) msi), fid, pid);
3235
3236 g_free (fid);
3237 g_free (pid);
3238 }
3239
3240 camel_store_summary_array_free (store->summary, array);
3241 }
3242
3243 /* static const gchar * */
3244
3245 guint
3246 mapi_folders_hash_table_type_lookup (CamelMapiStore *store, const gchar *name)
3247 {
3248 CamelMapiStorePrivate *priv = store->priv;
3249 guint *folder_type = g_hash_table_lookup (priv->container_hash, name);
3250
3251 g_return_val_if_fail (folder_type != NULL, 0);
3252
3253 return *folder_type;
3254 }
3255
3256 const gchar *
3257 mapi_folders_hash_table_name_lookup (CamelMapiStore *store, const gchar *fid, gboolean use_cache)
3258 {
3259 CamelMapiStorePrivate *priv = store->priv;
3260 const gchar *name = g_hash_table_lookup (priv->id_hash, fid);
3261
3262 if (!name && use_cache) {
3263 mapi_folders_update_hash_tables_from_cache (store);
3264
3265 name = g_hash_table_lookup (priv->id_hash, fid);
3266 }
3267
3268 return name;
3269 }
3270
3271 #if 0
3272 static const gchar *
3273 mapi_folders_hash_table_fid_lookup (CamelMapiStore *store, const gchar *name,
3274 gboolean use_cache)
3275 {
3276 CamelMapiStorePrivate *priv = store->priv;
3277
3278 const gchar *fid = g_hash_table_lookup (priv->name_hash, name);
3279
3280 if (!fid && use_cache)
3281 mapi_folders_update_hash_tables_from_cache (store);
3282
3283 fid = g_hash_table_lookup (priv->name_hash, name);
3284
3285 return fid;
3286 }
3287 #endif
3288
3289 const gchar *
3290 camel_mapi_store_folder_id_lookup (CamelMapiStore *mapi_store, const gchar *folder_name)
3291 {
3292 CamelMapiStorePrivate *priv = mapi_store->priv;
3293
3294 return g_hash_table_lookup (priv->name_hash, folder_name);
3295 }
3296
3297 const gchar *
3298 camel_mapi_store_system_folder_fid (CamelMapiStore *mapi_store, guint folder_type)
3299 {
3300 return mapi_system_folder_fid (mapi_store, folder_type);
3301 }
3302
3303 const gchar *
3304 camel_mapi_store_folder_lookup (CamelMapiStore *mapi_store, const gchar *folder_id)
3305 {
3306 CamelMapiStorePrivate *priv = mapi_store->priv;
3307
3308 return g_hash_table_lookup (priv->id_hash, folder_id);
3309 }
3310
3311 EMapiConnection *
3312 camel_mapi_store_ref_connection (CamelMapiStore *mapi_store,
3313 GCancellable *cancellable,
3314 GError **error)
3315 {
3316 EMapiConnection *conn;
3317
3318 g_return_val_if_fail (mapi_store != NULL, NULL);
3319 g_return_val_if_fail (CAMEL_IS_MAPI_STORE (mapi_store), NULL);
3320 g_return_val_if_fail (mapi_store->priv != NULL, NULL);
3321
3322 g_rec_mutex_lock (&mapi_store->priv->connection_lock);
3323 if (!mapi_store->priv->connection) {
3324 g_rec_mutex_unlock (&mapi_store->priv->connection_lock);
3325
3326 if (!camel_mapi_store_connected (mapi_store, cancellable, error))
3327 return NULL;
3328
3329 g_rec_mutex_lock (&mapi_store->priv->connection_lock);
3330 }
3331
3332 conn = mapi_store->priv->connection;
3333 if (conn)
3334 g_object_ref (conn);
3335 g_rec_mutex_unlock (&mapi_store->priv->connection_lock);
3336
3337 return conn;
3338 }
3339
3340 /* ppath contains proposed path, this only makes sure that it's a unique path */
3341 void
3342 camel_mapi_store_ensure_unique_path (CamelMapiStore *mapi_store,
3343 gchar **ppath)
3344 {
3345 gboolean done;
3346 guint counter = 0;
3347 gchar *base_path = NULL;
3348
3349 g_return_if_fail (mapi_store != NULL);
3350 g_return_if_fail (mapi_store->summary != NULL);
3351 g_return_if_fail (ppath != NULL);
3352 g_return_if_fail (*ppath != NULL);
3353
3354 done = FALSE;
3355 while (!done) {
3356 CamelStoreInfo *si;
3357
3358 done = TRUE;
3359
3360 si = camel_store_summary_path (mapi_store->summary, *ppath);
3361 if (si) {
3362 camel_store_info_unref (si);
3363
3364 done = FALSE;
3365 counter++;
3366 if (!counter) {
3367 g_debug ("%s: Counter overflow", G_STRFUNC);
3368 break;
3369 }
3370
3371 if (!base_path)
3372 base_path = *ppath;
3373 else
3374 g_free (*ppath);
3375
3376 *ppath = g_strdup_printf ("%s_%u", base_path, counter);
3377 }
3378 }
3379
3380 g_free (base_path);
3381 }
3382
3383 void
3384 camel_mapi_store_announce_subscribed_folder (CamelMapiStore *mapi_store,
3385 const gchar *path)
3386 {
3387 CamelStoreInfo *si;
3388 CamelFolderInfo *fi;
3389 CamelMapiStoreInfo *msi;
3390 gchar **parts, *folder_id_str, *parent_id_str;
3391 GString *partial_path;
3392 gint ii;
3393
3394 g_return_if_fail (mapi_store != NULL);
3395 g_return_if_fail (mapi_store->summary != NULL);
3396 g_return_if_fail (path != NULL);
3397
3398 si = camel_store_summary_path (mapi_store->summary, path);
3399 g_return_if_fail (si != NULL);
3400
3401 camel_store_info_unref (si);
3402
3403 parts = g_strsplit (path, "/", -1);
3404 g_return_if_fail (parts != NULL);
3405
3406 partial_path = g_string_new ("");
3407
3408 /* first announce about virtual parents */
3409 for (ii = 0; parts[ii]; ii++) {
3410 if (ii > 0)
3411 g_string_append_c (partial_path, '/');
3412 g_string_append (partial_path, parts[ii]);
3413
3414 si = camel_store_summary_path (mapi_store->summary, partial_path->str);
3415 if (si) {
3416 /* it's a known path, no need to announce it */
3417 camel_store_info_unref (si);
3418 } else {
3419 /* it's an unknown path, not a real path, thus announce it too,
3420 to ensure the folder path for this new path will exist
3421 */
3422 fi = mapi_build_folder_info (mapi_store, NULL, partial_path->str);
3423 fi->flags |= CAMEL_FOLDER_NOSELECT | CAMEL_FOLDER_SYSTEM;
3424
3425 camel_store_folder_created (CAMEL_STORE (mapi_store), fi);
3426 camel_subscribable_folder_subscribed (CAMEL_SUBSCRIBABLE (mapi_store), fi);
3427
3428 camel_folder_info_free (fi);
3429 }
3430 }
3431
3432 g_string_free (partial_path, TRUE);
3433 g_strfreev (parts);
3434
3435 /* finally announce about the path itself */
3436 si = camel_store_summary_path (mapi_store->summary, path);
3437 g_return_if_fail (si != NULL);
3438
3439 msi = (CamelMapiStoreInfo *) si;
3440 folder_id_str = e_mapi_util_mapi_id_to_string (msi->folder_id);
3441 parent_id_str = e_mapi_util_mapi_id_to_string (msi->parent_id);
3442
3443 fi = mapi_build_folder_info (mapi_store, NULL, camel_store_info_get_path (si));
3444 fi->flags = msi->camel_folder_flags;
3445
3446 mapi_update_folder_hash_tables (mapi_store, fi->full_name, folder_id_str, parent_id_str);
3447
3448 camel_store_folder_created (CAMEL_STORE (mapi_store), fi);
3449 camel_subscribable_folder_subscribed (CAMEL_SUBSCRIBABLE (mapi_store), fi);
3450
3451 if ((msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_FOREIGN) != 0 &&
3452 (msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_FOREIGN_WITH_SUBFOLDERS) != 0) {
3453 CamelSession *session;
3454
3455 session = camel_service_ref_session (CAMEL_SERVICE (mapi_store));
3456
3457 if (session) {
3458 camel_session_submit_job (session, _("Updating foreign folders"),
3459 mapi_store_update_foreign_subfolders_thread,
3460 g_object_ref (mapi_store), g_object_unref);
3461 g_object_unref (session);
3462 }
3463 }
3464
3465 camel_folder_info_free (fi);
3466 camel_store_info_unref (si);
3467 g_free (folder_id_str);
3468 g_free (parent_id_str);
3469 }