1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3 * Authors: Peter Williams <peterw@ximian.com>
4 * Michael Zucchi <notzed@ximian.com>
6 * Copyright 2000,2001 Ximian, Inc. (www.ximian.com)
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of version 2 of the GNU General Public
10 * License as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
30 #define G_LOG_DOMAIN "folder tree"
37 #include <glib/gstdio.h>
39 #include <libgnome/gnome-sound.h>
40 #include <libgnome/gnome-i18n.h>
41 #include <bonobo/bonobo-exception.h>
42 #include <camel/camel-store.h>
43 #include <camel/camel-folder.h>
44 #include <camel/camel-vtrash-folder.h>
45 #include <camel/camel-vee-store.h>
46 #include <camel/camel-offline-store.h>
47 #include <camel/camel-disco-store.h>
49 #include <libedataserver/e-data-server-util.h>
50 #include "e-util/e-util.h"
53 #include "mail-folder-cache.h"
55 #include "mail-session.h"
56 #include "mail-component.h"
58 /* For notifications of changes */
59 #include "mail-vfolder.h"
60 #include "mail-autofilter.h"
61 #include "mail-config.h"
62 #include "em-folder-tree-model.h"
69 /* This code is a mess, there is no reason it should be so complicated. */
71 /* note that many things are effectively serialised by having them run in
72 the main loop thread which they need to do because of corba/gtk calls */
73 #define LOCK(x) pthread_mutex_lock(&x)
74 #define UNLOCK(x) pthread_mutex_unlock(&x)
76 static pthread_mutex_t info_lock
= PTHREAD_MUTEX_INITIALIZER
;
79 struct _store_info
*store_info
; /* 'parent' link */
81 char *full_name
; /* full name of folder/folderinfo */
82 char *uri
; /* uri of folder */
86 CamelFolder
*folder
; /* if known */
89 /* pending list of updates */
90 struct _folder_update
{
91 struct _folder_update
*next
;
92 struct _folder_update
*prev
;
94 unsigned int remove
:1; /* removing from vfolders */
95 unsigned int delete:1; /* deleting as well? */
96 unsigned int add
:1; /* add to vfolder */
97 unsigned int unsub
:1; /* unsubcribing? */
98 unsigned int new:1; /* new mail arrived? */
110 GHashTable
*folders
; /* by full_name */
111 GHashTable
*folders_uri
; /* by uri */
113 CamelStore
*store
; /* the store for these folders */
115 /* Outstanding folderinfo requests */
116 EDList folderinfo_updates
;
119 static void folder_changed(CamelObject
*o
, gpointer event_data
, gpointer user_data
);
120 static void folder_renamed(CamelObject
*o
, gpointer event_data
, gpointer user_data
);
121 static void folder_finalised(CamelObject
*o
, gpointer event_data
, gpointer user_data
);
123 static guint ping_id
= 0;
124 static gboolean
ping_cb (gpointer user_data
);
126 static guint notify_id
= 0;
127 static int notify_type
= -1;
129 static time_t last_notify
= 0;
130 static guint notify_idle_id
= 0;
131 static gboolean
notify_idle_cb (gpointer user_data
);
134 /* Store to storeinfo table, active stores */
135 static GHashTable
*stores
= NULL
;
137 /* List of folder changes to be executed in gui thread */
138 static EDList updates
= E_DLIST_INITIALISER(updates
);
139 static int update_id
= -1;
141 /* hack for people who LIKE to have unsent count */
142 static int count_sent
= FALSE
;
143 static int count_trash
= FALSE
;
146 free_update(struct _folder_update
*up
)
148 g_free(up
->full_name
);
151 camel_object_unref(up
->store
);
158 notify_idle_cb (gpointer user_data
)
163 gconf
= mail_config_get_gconf_client ();
165 switch (notify_type
) {
166 case MAIL_CONFIG_NOTIFY_PLAY_SOUND
:
167 filename
= gconf_client_get_string (gconf
, "/apps/evolution/mail/notify/sound", NULL
);
168 if (filename
!= NULL
) {
169 gnome_sound_play (filename
);
173 case MAIL_CONFIG_NOTIFY_BEEP
:
188 notify_type_changed (GConfClient
*client
, guint cnxn_id
,
189 GConfEntry
*entry
, gpointer user_data
)
191 notify_type
= gconf_client_get_int (client
, "/apps/evolution/mail/notify/type", NULL
);
195 real_flush_updates(void *o
, void *event_data
, void *data
)
197 struct _MailComponent
*component
;
198 struct _EMFolderTreeModel
*model
;
199 struct _folder_update
*up
;
202 component
= mail_component_peek ();
203 model
= mail_component_peek_tree_model (component
);
206 while ((up
= (struct _folder_update
*)e_dlist_remhead(&updates
))) {
211 mail_vfolder_delete_uri(up
->store
, up
->uri
);
212 mail_filter_delete_uri(up
->store
, up
->uri
);
213 mail_config_uri_deleted(CAMEL_STORE_CLASS(CAMEL_OBJECT_GET_CLASS(up
->store
))->compare_folder_name
, up
->uri
);
216 mail_vfolder_add_uri(up
->store
, up
->uri
, TRUE
);
218 /* We can tell the vfolder code though */
219 if (up
->olduri
&& up
->add
) {
220 d(printf("renaming folder '%s' to '%s'\n", up
->olduri
, up
->uri
));
221 mail_vfolder_rename_uri(up
->store
, up
->olduri
, up
->uri
);
222 mail_filter_rename_uri(up
->store
, up
->olduri
, up
->uri
);
223 mail_config_uri_renamed(CAMEL_STORE_CLASS(CAMEL_OBJECT_GET_CLASS(up
->store
))->compare_folder_name
,
224 up
->olduri
, up
->uri
);
227 if (!up
->olduri
&& up
->add
)
228 mail_vfolder_add_uri(up
->store
, up
->uri
, FALSE
);
231 /* update unread counts */
232 em_folder_tree_model_set_unread_count (model
, up
->store
, up
->full_name
, up
->unread
);
234 /* new mail notification */
235 if (notify_type
== -1) {
236 /* need to track the user's new-mail-notification settings... */
239 gconf
= mail_config_get_gconf_client ();
240 gconf_client_add_dir (gconf
, "/apps/evolution/mail/notify",
241 GCONF_CLIENT_PRELOAD_ONELEVEL
, NULL
);
242 notify_id
= gconf_client_notify_add (gconf
, "/apps/evolution/mail/notify",
243 notify_type_changed
, NULL
, NULL
, NULL
);
244 notify_type
= gconf_client_get_int (gconf
, "/apps/evolution/mail/notify/type", NULL
);
248 if (notify_type
!= 0 && up
->new && notify_idle_id
== 0 && (now
- last_notify
>= 5))
249 notify_idle_id
= g_idle_add_full (G_PRIORITY_LOW
, notify_idle_cb
, NULL
, NULL
);
252 EMEvent
*e
= em_event_peek();
253 EMEventTargetFolder
*t
= em_event_target_new_folder(e
, up
->uri
, up
->new?EM_EVENT_FOLDER_NEWMAIL
:0);
255 /** @Event: folder.changed
256 * @Title: Folder changed
257 * @Target: EMEventTargetFolder
259 * folder.changed is emitted whenever a folder changes. There is no detail on how the folder has changed.
261 e_event_emit((EEvent
*)e
, "folder.changed", (EEventTarget
*)t
);
275 if (update_id
== -1 && !e_dlist_empty(&updates
))
276 update_id
= mail_async_event_emit(mail_async_event
, MAIL_ASYNC_GUI
, (MailAsyncFunc
)real_flush_updates
, 0, 0, 0);
280 unset_folder_info(struct _folder_info
*mfi
, int delete, int unsub
)
282 struct _folder_update
*up
;
284 d(printf("unset folderinfo '%s'\n", mfi
->uri
));
287 CamelFolder
*folder
= mfi
->folder
;
289 camel_object_unhook_event(folder
, "folder_changed", folder_changed
, NULL
);
290 camel_object_unhook_event(folder
, "renamed", folder_renamed
, NULL
);
291 camel_object_unhook_event(folder
, "finalize", folder_finalised
, NULL
);
294 if ((mfi
->flags
& CAMEL_FOLDER_NOSELECT
) == 0) {
295 up
= g_malloc0(sizeof(*up
));
300 up
->store
= mfi
->store_info
->store
;
301 up
->full_name
= g_strdup (mfi
->full_name
);
302 camel_object_ref(up
->store
);
303 up
->uri
= g_strdup(mfi
->uri
);
305 e_dlist_addtail(&updates
, (EDListNode
*)up
);
311 free_folder_info(struct _folder_info
*mfi
)
313 g_free(mfi
->full_name
);
318 /* This is how unread counts work (and don't work):
320 * camel_folder_unread_message_count() only gives a correct answer if
321 * the store is paying attention to the folder. (Some stores always
322 * pay attention to all folders, but IMAP can only pay attention to
323 * one folder at a time.) But it doesn't have any way to know when
324 * it's lying, so it's only safe to call it when you know for sure
325 * that the store is paying attention to the folder, such as when it's
326 * just been created, or you get a folder_changed signal on it.
328 * camel_store_get_folder_info() always gives correct answers for the
329 * folders it checks, but it can also return -1 for a folder, meaning
330 * it didn't check, and so you should stick with your previous answer.
332 * update_1folder is called from three places: with info != NULL when
333 * the folder is created (or get_folder_info), with info == NULL when
334 * a folder changed event is emitted.
336 * So if info is NULL, camel_folder_unread_message_count is correct,
337 * and if it's not NULL and its unread_message_count isn't -1, then
341 update_1folder(struct _folder_info
*mfi
, int new, CamelFolderInfo
*info
)
343 struct _folder_update
*up
;
348 folder
= mfi
->folder
;
350 d(printf("update 1 folder '%s'\n", folder
->full_name
));
351 if ((count_trash
&& (CAMEL_IS_VTRASH_FOLDER (folder
)))
352 || folder
== mail_component_get_folder(NULL
, MAIL_COMPONENT_FOLDER_OUTBOX
)
353 || folder
== mail_component_get_folder(NULL
, MAIL_COMPONENT_FOLDER_DRAFTS
)
354 || (count_sent
&& folder
== mail_component_get_folder(NULL
, MAIL_COMPONENT_FOLDER_SENT
))) {
355 d(printf(" total count\n"));
356 unread
= camel_folder_get_message_count (folder
);
357 if (folder
== mail_component_get_folder(NULL
, MAIL_COMPONENT_FOLDER_OUTBOX
)
358 || folder
== mail_component_get_folder(NULL
, MAIL_COMPONENT_FOLDER_DRAFTS
)) {
359 if ((deleted
= camel_folder_get_deleted_message_count (folder
)) > 0)
363 d(printf(" unread count\n"));
365 unread
= info
->unread
;
367 unread
= camel_folder_get_unread_message_count (folder
);
370 unread
= info
->unread
;
372 d(printf("folder updated: unread %d: '%s'\n", unread
, mfi
->full_name
));
377 up
= g_malloc0(sizeof(*up
));
378 up
->full_name
= g_strdup(mfi
->full_name
);
380 up
->new = new ? 1 : 0;
381 up
->store
= mfi
->store_info
->store
;
382 up
->uri
= g_strdup(mfi
->uri
);
383 camel_object_ref(up
->store
);
384 e_dlist_addtail(&updates
, (EDListNode
*)up
);
389 setup_folder(CamelFolderInfo
*fi
, struct _store_info
*si
)
391 struct _folder_info
*mfi
;
392 struct _folder_update
*up
;
394 mfi
= g_hash_table_lookup(si
->folders
, fi
->full_name
);
396 update_1folder(mfi
, 0, fi
);
398 d(printf("Adding new folder: %s (%s)\n", fi
->full_name
, fi
->uri
));
399 mfi
= g_malloc0(sizeof(*mfi
));
400 mfi
->full_name
= g_strdup(fi
->full_name
);
401 mfi
->uri
= g_strdup(fi
->uri
);
402 mfi
->store_info
= si
;
403 mfi
->flags
= fi
->flags
;
405 g_hash_table_insert(si
->folders
, mfi
->full_name
, mfi
);
406 g_hash_table_insert(si
->folders_uri
, mfi
->uri
, mfi
);
408 up
= g_malloc0(sizeof(*up
));
409 up
->full_name
= g_strdup(mfi
->full_name
);
410 up
->uri
= g_strdup(fi
->uri
);
411 up
->unread
= fi
->unread
;
412 up
->store
= si
->store
;
413 camel_object_ref(up
->store
);
415 if ((fi
->flags
& CAMEL_FOLDER_NOSELECT
) == 0)
418 e_dlist_addtail(&updates
, (EDListNode
*)up
);
424 create_folders(CamelFolderInfo
*fi
, struct _store_info
*si
)
426 d(printf("Setup new folder: %s\n %s\n", fi
->uri
, fi
->full_name
));
429 setup_folder(fi
, si
);
432 create_folders(fi
->child
, si
);
439 folder_changed (CamelObject
*o
, gpointer event_data
, gpointer user_data
)
441 CamelFolderChangeInfo
*changes
= event_data
;
442 CamelFolder
*folder
= (CamelFolder
*)o
;
443 CamelStore
*store
= folder
->parent_store
;
444 struct _store_info
*si
;
445 struct _folder_info
*mfi
;
448 d(printf("folder '%s' changed\n", folder
->full_name
));
450 if (!CAMEL_IS_VEE_FOLDER(folder
)
451 && folder
!= mail_component_get_folder(NULL
, MAIL_COMPONENT_FOLDER_OUTBOX
)
452 && folder
!= mail_component_get_folder(NULL
, MAIL_COMPONENT_FOLDER_DRAFTS
)
453 && folder
!= mail_component_get_folder(NULL
, MAIL_COMPONENT_FOLDER_SENT
)
454 && changes
&& changes
->uid_added
)
455 new = changes
->uid_added
->len
;
459 && (si
= g_hash_table_lookup(stores
, store
)) != NULL
460 && (mfi
= g_hash_table_lookup(si
->folders
, folder
->full_name
)) != NULL
461 && mfi
->folder
== folder
) {
462 update_1folder(mfi
, new, NULL
);
468 folder_finalised(CamelObject
*o
, gpointer event_data
, gpointer user_data
)
470 CamelFolder
*folder
= (CamelFolder
*)o
;
471 CamelStore
*store
= folder
->parent_store
;
472 struct _store_info
*si
;
473 struct _folder_info
*mfi
;
475 d(printf("Folder finalised '%s'!\n", ((CamelFolder
*)o
)->full_name
));
478 && (si
= g_hash_table_lookup(stores
, store
)) != NULL
479 && (mfi
= g_hash_table_lookup(si
->folders
, folder
->full_name
)) != NULL
480 && mfi
->folder
== folder
) {
487 folder_renamed(CamelObject
*o
, gpointer event_data
, gpointer user_data
)
489 CamelFolder
*folder
= (CamelFolder
*)o
;
490 char *old
= event_data
;
492 d(printf("Folder renamed from '%s' to '%s'\n", old
, folder
->full_name
));
496 /* Dont do anything, do it from the store rename event? */
499 void mail_note_folder(CamelFolder
*folder
)
501 CamelStore
*store
= folder
->parent_store
;
502 struct _store_info
*si
;
503 struct _folder_info
*mfi
;
505 d(printf("noting folder '%s'\n", folder
->full_name
));
509 || (si
= g_hash_table_lookup(stores
, store
)) == NULL
510 || (mfi
= g_hash_table_lookup(si
->folders
, folder
->full_name
)) == NULL
) {
511 w(g_warning("Noting folder before store initialised"));
516 /* dont do anything if we already have this */
517 if (mfi
->folder
== folder
) {
522 mfi
->folder
= folder
;
524 update_1folder(mfi
, 0, NULL
);
528 camel_object_hook_event(folder
, "folder_changed", folder_changed
, NULL
);
529 camel_object_hook_event(folder
, "renamed", folder_renamed
, NULL
);
530 camel_object_hook_event(folder
, "finalize", folder_finalised
, NULL
);
534 store_folder_subscribed(CamelObject
*o
, void *event_data
, void *data
)
536 struct _store_info
*si
;
537 CamelFolderInfo
*fi
= event_data
;
539 d(printf("Store folder subscribed '%s' store '%s' \n", fi
->full_name
, camel_url_to_string(((CamelService
*)o
)->url
, 0)));
542 si
= g_hash_table_lookup(stores
, o
);
544 setup_folder(fi
, si
);
549 store_folder_created(CamelObject
*o
, void *event_data
, void *data
)
551 /* we only want created events to do more work if we dont support subscriptions */
552 if (!camel_store_supports_subscriptions(CAMEL_STORE(o
)))
553 store_folder_subscribed(o
, event_data
, data
);
557 store_folder_opened(CamelObject
*o
, void *event_data
, void *data
)
559 CamelFolder
*folder
= event_data
;
561 mail_note_folder(folder
);
565 store_folder_unsubscribed(CamelObject
*o
, void *event_data
, void *data
)
567 struct _store_info
*si
;
568 CamelFolderInfo
*fi
= event_data
;
569 struct _folder_info
*mfi
;
570 CamelStore
*store
= (CamelStore
*)o
;
572 d(printf("Store Folder deleted: %s\n", fi
->full_name
));
575 si
= g_hash_table_lookup(stores
, store
);
577 mfi
= g_hash_table_lookup(si
->folders
, fi
->full_name
);
579 g_hash_table_remove(si
->folders
, mfi
->full_name
);
580 g_hash_table_remove(si
->folders_uri
, mfi
->uri
);
581 unset_folder_info(mfi
, TRUE
, TRUE
);
582 free_folder_info(mfi
);
589 store_folder_deleted(CamelObject
*o
, void *event_data
, void *data
)
591 /* we only want deleted events to do more work if we dont support subscriptions */
592 if (!camel_store_supports_subscriptions(CAMEL_STORE(o
)))
593 store_folder_unsubscribed(o
, event_data
, data
);
597 folder_to_url(CamelStore
*store
, const char *full_name
)
602 url
= camel_url_copy(((CamelService
*)store
)->url
);
603 if (((CamelService
*)store
)->provider
->url_flags
& CAMEL_URL_FRAGMENT_IS_PATH
) {
604 camel_url_set_fragment(url
, full_name
);
606 char *name
= g_alloca(strlen(full_name
)+2);
608 sprintf(name
, "/%s", full_name
);
609 camel_url_set_path(url
, name
);
612 out
= camel_url_to_string(url
, CAMEL_URL_HIDE_ALL
);
619 rename_folders(struct _store_info
*si
, const char *oldbase
, const char *newbase
, CamelFolderInfo
*fi
)
621 char *old
, *olduri
, *oldfile
, *newuri
, *newfile
;
622 struct _folder_info
*mfi
;
623 struct _folder_update
*up
;
625 up
= g_malloc0(sizeof(*up
));
627 d(printf("oldbase '%s' newbase '%s' new '%s'\n", oldbase
, newbase
, fi
->full_name
));
629 /* Form what was the old name, and try and look it up */
630 old
= g_strdup_printf("%s%s", oldbase
, fi
->full_name
+ strlen(newbase
));
631 mfi
= g_hash_table_lookup(si
->folders
, old
);
633 d(printf("Found old folder '%s' renaming to '%s'\n", mfi
->full_name
, fi
->full_name
));
635 up
->oldfull
= mfi
->full_name
;
636 up
->olduri
= mfi
->uri
;
638 /* Its a rename op */
639 g_hash_table_remove(si
->folders
, mfi
->full_name
);
640 g_hash_table_remove(si
->folders
, mfi
->uri
);
641 mfi
->full_name
= g_strdup(fi
->full_name
);
642 mfi
->uri
= g_strdup(fi
->uri
);
643 mfi
->flags
= fi
->flags
;
645 g_hash_table_insert(si
->folders
, mfi
->full_name
, mfi
);
646 g_hash_table_insert(si
->folders_uri
, mfi
->uri
, mfi
);
648 d(printf("Rename found a new folder? old '%s' new '%s'\n", old
, fi
->full_name
));
650 mfi
= g_malloc0(sizeof(*mfi
));
651 mfi
->full_name
= g_strdup(fi
->full_name
);
652 mfi
->uri
= g_strdup(fi
->uri
);
653 mfi
->store_info
= si
;
654 mfi
->flags
= fi
->flags
;
656 g_hash_table_insert(si
->folders
, mfi
->full_name
, mfi
);
657 g_hash_table_insert(si
->folders_uri
, mfi
->uri
, mfi
);
660 up
->full_name
= g_strdup(mfi
->full_name
);
661 up
->uri
= g_strdup(mfi
->uri
);
662 up
->unread
= fi
->unread
==-1?0:fi
->unread
;
663 up
->store
= si
->store
;
664 camel_object_ref(up
->store
);
666 if ((fi
->flags
& CAMEL_FOLDER_NOSELECT
) == 0)
669 e_dlist_addtail(&updates
, (EDListNode
*)up
);
673 rename_folders(si
, oldbase
, newbase
, fi
->sibling
, folders
);
675 rename_folders(si
, oldbase
, newbase
, fi
->child
, folders
);
678 /* rename the meta-data we maintain ourselves */
679 olduri
= folder_to_url(si
->store
, old
);
680 e_filename_make_safe(olduri
);
681 newuri
= folder_to_url(si
->store
, fi
->full_name
);
682 e_filename_make_safe(newuri
);
683 oldfile
= g_strdup_printf("%s/mail/config/custom_view-%s.xml", mail_component_peek_base_directory(NULL
), olduri
);
684 newfile
= g_strdup_printf("%s/mail/config/custom_view-%s.xml", mail_component_peek_base_directory(NULL
), newuri
);
685 g_rename(oldfile
, newfile
);
688 oldfile
= g_strdup_printf("%s/mail/config/current_view-%s.xml", mail_component_peek_base_directory(NULL
), olduri
);
689 newfile
= g_strdup_printf("%s/mail/config/current_view-%s.xml", mail_component_peek_base_directory(NULL
), newuri
);
690 g_rename(oldfile
, newfile
);
700 get_folders(CamelFolderInfo
*fi
, GPtrArray
*folders
)
703 g_ptr_array_add(folders
, fi
);
706 get_folders(fi
->child
, folders
);
713 folder_cmp(const void *ap
, const void *bp
)
715 const CamelFolderInfo
*a
= ((CamelFolderInfo
**)ap
)[0];
716 const CamelFolderInfo
*b
= ((CamelFolderInfo
**)bp
)[0];
718 return strcmp(a
->full_name
, b
->full_name
);
722 store_folder_renamed(CamelObject
*o
, void *event_data
, void *data
)
724 CamelStore
*store
= (CamelStore
*)o
;
725 CamelRenameInfo
*info
= event_data
;
726 struct _store_info
*si
;
728 d(printf("Folder renamed: oldbase = '%s' new->full = '%s'\n", info
->old_base
, info
->new->full_name
));
731 si
= g_hash_table_lookup(stores
, store
);
733 GPtrArray
*folders
= g_ptr_array_new();
734 CamelFolderInfo
*top
;
737 /* Ok, so for some reason the folderinfo we have comes in all messed up from
738 imap, should find out why ... this makes it workable */
739 get_folders(info
->new, folders
);
740 qsort(folders
->pdata
, folders
->len
, sizeof(folders
->pdata
[0]), folder_cmp
);
742 top
= folders
->pdata
[0];
743 for (i
=0;i
<folders
->len
;i
++) {
744 rename_folders(si
, info
->old_base
, top
->full_name
, folders
->pdata
[i
]);
747 g_ptr_array_free(folders
, TRUE
);
753 struct _update_data
{
754 struct _update_data
*next
;
755 struct _update_data
*prev
;
757 int id
; /* id for cancellation */
758 guint cancel
:1; /* also tells us we're cancelled */
760 void (*done
)(CamelStore
*store
, CamelFolderInfo
*info
, void *data
);
765 unset_folder_info_hash(char *path
, struct _folder_info
*mfi
, void *data
)
767 unset_folder_info(mfi
, FALSE
, FALSE
);
771 free_folder_info_hash(char *path
, struct _folder_info
*mfi
, void *data
)
773 free_folder_info(mfi
);
777 mail_note_store_remove(CamelStore
*store
)
779 struct _update_data
*ud
;
780 struct _store_info
*si
;
782 g_assert(CAMEL_IS_STORE(store
));
787 d(printf("store removed!!\n"));
789 si
= g_hash_table_lookup(stores
, store
);
791 g_hash_table_remove(stores
, store
);
793 camel_object_unhook_event(store
, "folder_opened", store_folder_opened
, NULL
);
794 camel_object_unhook_event(store
, "folder_created", store_folder_created
, NULL
);
795 camel_object_unhook_event(store
, "folder_deleted", store_folder_deleted
, NULL
);
796 camel_object_unhook_event(store
, "folder_renamed", store_folder_renamed
, NULL
);
797 camel_object_unhook_event(store
, "folder_subscribed", store_folder_subscribed
, NULL
);
798 camel_object_unhook_event(store
, "folder_unsubscribed", store_folder_unsubscribed
, NULL
);
799 g_hash_table_foreach(si
->folders
, (GHFunc
)unset_folder_info_hash
, NULL
);
801 ud
= (struct _update_data
*)si
->folderinfo_updates
.head
;
803 d(printf("Cancelling outstanding folderinfo update %d\n", ud
->id
));
804 mail_msg_cancel(ud
->id
);
809 camel_object_unref(si
->store
);
810 g_hash_table_foreach(si
->folders
, (GHFunc
)free_folder_info_hash
, NULL
);
811 g_hash_table_destroy(si
->folders
);
812 g_hash_table_destroy(si
->folders_uri
);
820 update_folders(CamelStore
*store
, CamelFolderInfo
*fi
, void *data
)
822 struct _update_data
*ud
= data
;
823 struct _store_info
*si
;
825 d(printf("Got folderinfo for store %s\n", store
->parent_object
.provider
->protocol
));
828 si
= g_hash_table_lookup(stores
, store
);
829 if (si
&& !ud
->cancel
) {
830 /* the 'si' is still there, so we can remove ourselves from its list */
831 /* otherwise its not, and we're on our own and free anyway */
832 e_dlist_remove((EDListNode
*)ud
);
835 create_folders(fi
, si
);
840 ud
->done(store
, fi
, ud
->data
);
845 struct _ping_store_msg
{
846 struct _mail_msg msg
;
852 ping_store_desc (struct _mail_msg
*mm
, int done
)
854 struct _ping_store_msg
*m
= (struct _ping_store_msg
*) mm
;
855 char *service_name
= camel_service_get_name (CAMEL_SERVICE (m
->store
), TRUE
);
858 msg
= g_strdup_printf (_("Pinging %s"), service_name
);
859 g_free (service_name
);
865 ping_store_ping (struct _mail_msg
*mm
)
867 gboolean online
= FALSE
;
868 struct _ping_store_msg
*m
= (struct _ping_store_msg
*) mm
;
870 if (CAMEL_SERVICE (m
->store
)->status
== CAMEL_SERVICE_CONNECTED
) {
871 if (CAMEL_IS_DISCO_STORE (m
->store
) &&
872 camel_disco_store_status (CAMEL_DISCO_STORE (m
->store
)) != CAMEL_DISCO_STORE_OFFLINE
)
874 else if (CAMEL_IS_OFFLINE_STORE (m
->store
) &&
875 CAMEL_OFFLINE_STORE (m
->store
)->state
!= CAMEL_OFFLINE_STORE_NETWORK_UNAVAIL
)
879 camel_store_noop (m
->store
, &mm
->ex
);
883 ping_store_free (struct _mail_msg
*mm
)
885 struct _ping_store_msg
*m
= (struct _ping_store_msg
*) mm
;
887 camel_object_unref (m
->store
);
890 static struct _mail_msg_op ping_store_op
= {
898 ping_store (gpointer key
, gpointer val
, gpointer user_data
)
900 CamelStore
*store
= (CamelStore
*) key
;
901 struct _ping_store_msg
*m
;
903 if (CAMEL_SERVICE (store
)->status
!= CAMEL_SERVICE_CONNECTED
)
906 m
= mail_msg_new (&ping_store_op
, NULL
, sizeof (struct _ping_store_msg
));
908 camel_object_ref (store
);
910 e_thread_put (mail_thread_queued_slow
, (EMsg
*) m
);
914 ping_cb (gpointer user_data
)
918 g_hash_table_foreach (stores
, ping_store
, NULL
);
926 store_online_cb (CamelStore
*store
, void *data
)
928 struct _update_data
*ud
= data
;
932 if (g_hash_table_lookup(stores
, store
) != NULL
&& !ud
->cancel
) {
933 /* re-use the cancel id. we're already in the store update list too */
934 ud
->id
= mail_get_folderinfo(store
, NULL
, update_folders
, ud
);
936 /* the store vanished, that means we were probably cancelled, or at any rate,
937 need to clean ourselves up */
945 mail_note_store(CamelStore
*store
, CamelOperation
*op
,
946 void (*done
)(CamelStore
*store
, CamelFolderInfo
*info
, void *data
), void *data
)
948 struct _store_info
*si
;
949 struct _update_data
*ud
;
954 g_assert(CAMEL_IS_STORE(store
));
955 g_assert(pthread_equal(pthread_self(), mail_gui_thread
));
959 if (stores
== NULL
) {
960 stores
= g_hash_table_new(NULL
, NULL
);
961 count_sent
= getenv("EVOLUTION_COUNT_SENT") != NULL
;
962 count_trash
= getenv("EVOLUTION_COUNT_TRASH") != NULL
;
963 buf
= getenv ("EVOLUTION_PING_TIMEOUT");
964 timeout
= buf
? strtoul (buf
, NULL
, 10) * 1000 : 600000;
965 ping_id
= g_timeout_add (timeout
, ping_cb
, NULL
);
968 si
= g_hash_table_lookup(stores
, store
);
970 d(printf("Noting a new store: %p: %s\n", store
, camel_url_to_string(((CamelService
*)store
)->url
, 0)));
972 si
= g_malloc0(sizeof(*si
));
973 si
->folders
= g_hash_table_new(g_str_hash
, g_str_equal
);
974 si
->folders_uri
= g_hash_table_new(CAMEL_STORE_CLASS(CAMEL_OBJECT_GET_CLASS(store
))->hash_folder_name
,
975 CAMEL_STORE_CLASS(CAMEL_OBJECT_GET_CLASS(store
))->compare_folder_name
);
977 camel_object_ref((CamelObject
*)store
);
978 g_hash_table_insert(stores
, store
, si
);
979 e_dlist_init(&si
->folderinfo_updates
);
983 ud
= g_malloc(sizeof(*ud
));
988 /* We might get a race when setting up a store, such that it is still left in offline mode,
989 after we've gone online. This catches and fixes it up when the shell opens us */
990 if (CAMEL_IS_DISCO_STORE (store
)) {
991 if (camel_session_is_online (session
)
992 && camel_disco_store_status (CAMEL_DISCO_STORE (store
)) == CAMEL_DISCO_STORE_OFFLINE
) {
993 /* Note: we use the 'id' here, even though its not the right id, its still ok */
994 ud
->id
= mail_store_set_offline (store
, FALSE
, store_online_cb
, ud
);
998 } else if (CAMEL_IS_OFFLINE_STORE (store
)) {
999 if (camel_session_is_online (session
) && CAMEL_OFFLINE_STORE (store
)->state
== CAMEL_OFFLINE_STORE_NETWORK_UNAVAIL
) {
1000 /* Note: we use the 'id' here, even though its not the right id, its still ok */
1001 ud
->id
= mail_store_set_offline (store
, FALSE
, store_online_cb
, ud
);
1007 ud
->id
= mail_get_folderinfo (store
, op
, update_folders
, ud
);
1010 e_dlist_addtail (&si
->folderinfo_updates
, (EDListNode
*) ud
);
1014 /* there is potential for race here, but it is safe as we check for the store anyway */
1016 camel_object_hook_event(store
, "folder_opened", store_folder_opened
, NULL
);
1017 camel_object_hook_event(store
, "folder_created", store_folder_created
, NULL
);
1018 camel_object_hook_event(store
, "folder_deleted", store_folder_deleted
, NULL
);
1019 camel_object_hook_event(store
, "folder_renamed", store_folder_renamed
, NULL
);
1020 camel_object_hook_event(store
, "folder_subscribed", store_folder_subscribed
, NULL
);
1021 camel_object_hook_event(store
, "folder_unsubscribed", store_folder_unsubscribed
, NULL
);
1027 struct _folder_info
*fi
;
1031 /* look up on each storeinfo using proper hash function for that stores uri's */
1032 static void storeinfo_find_folder_info(CamelStore
*store
, struct _store_info
*si
, struct _find_info
*fi
)
1034 if (fi
->fi
== NULL
) {
1035 if (((CamelService
*)store
)->provider
->url_equal(fi
->url
, ((CamelService
*)store
)->url
)) {
1036 char *path
= fi
->url
->fragment
?fi
->url
->fragment
:fi
->url
->path
;
1040 fi
->fi
= g_hash_table_lookup(si
->folders
, path
);
1045 /* returns TRUE if the uri is available, folderp is set to a
1046 reffed folder if the folder has also already been opened */
1047 int mail_note_get_folder_from_uri(const char *uri
, CamelFolder
**folderp
)
1049 struct _find_info fi
= { uri
, NULL
, NULL
};
1054 fi
.url
= camel_url_new(uri
, NULL
);
1057 g_hash_table_foreach(stores
, (GHFunc
)storeinfo_find_folder_info
, &fi
);
1059 if (fi
.fi
&& fi
.fi
->folder
) {
1060 *folderp
= fi
.fi
->folder
;
1061 camel_object_ref(*folderp
);
1068 camel_url_free(fi
.url
);
1070 return fi
.fi
!= NULL
;