diff options
author | Not Zed <NotZed@HelixCode.com> | 2000-12-24 08:46:20 +0800 |
---|---|---|
committer | Michael Zucci <zucchi@src.gnome.org> | 2000-12-24 08:46:20 +0800 |
commit | 6de256c2a2b23f30d35e4a2213ad5839bf141d06 (patch) | |
tree | a34d8be64c0718070c4e1ea9548282912f37b387 /camel | |
parent | 6183d89039ba67a7f3869f460c13aff09a548471 (diff) | |
download | gsoc2013-evolution-6de256c2a2b23f30d35e4a2213ad5839bf141d06.tar gsoc2013-evolution-6de256c2a2b23f30d35e4a2213ad5839bf141d06.tar.gz gsoc2013-evolution-6de256c2a2b23f30d35e4a2213ad5839bf141d06.tar.bz2 gsoc2013-evolution-6de256c2a2b23f30d35e4a2213ad5839bf141d06.tar.lz gsoc2013-evolution-6de256c2a2b23f30d35e4a2213ad5839bf141d06.tar.xz gsoc2013-evolution-6de256c2a2b23f30d35e4a2213ad5839bf141d06.tar.zst gsoc2013-evolution-6de256c2a2b23f30d35e4a2213ad5839bf141d06.zip |
Lock the command channel while searching. (imap_body_contains): If
2000-12-24 Not Zed <NotZed@HelixCode.com>
* providers/imap/camel-imap-search.c (imap_body_contains): Lock
the command channel while searching.
(imap_body_contains): If performing a whole uid search, then add
references to our own summary items, dont look it up in the
folder. This way they can't vanish unexpectedly.
* providers/imap/camel-imap-folder.h (CamelImapFolder): Added a
private field.
* providers/imap/camel-imap-private.h: Added lock for imap
searches.
* Merge from camel-mt-branch.
* providers/imap/camel-imap-folder.c (imap_update_summary): Merge
fix, use the folder->summary.
(imap_get_message_flags, imap_set_message_flags,
imap_get_message_user_flag, imap_set_message_user_flag): Removed
again.
(camel_imap_folder_init): Setup private data/lock.
(imap_finalize): Free private data/search lock.
(imap_search_free): Lock the search_lock.
(imap_search_by_expression): Lock the search lock when using the
search object. Also copy/ref hte summary, rather than getting it
directly.
(imap_refresh_info): Free any info lookups. Use folder->summary
not imap_folder->summary. And lock around commands.
svn path=/trunk/; revision=7150
Diffstat (limited to 'camel')
52 files changed, 2979 insertions, 1275 deletions
diff --git a/camel/ChangeLog b/camel/ChangeLog index 3a2563fefc..833282f2dd 100644 --- a/camel/ChangeLog +++ b/camel/ChangeLog @@ -1,3 +1,33 @@ +2000-12-24 Not Zed <NotZed@HelixCode.com> + + * providers/imap/camel-imap-search.c (imap_body_contains): Lock + the command channel while searching. + (imap_body_contains): If performing a whole uid search, then add + references to our own summary items, dont look it up in the + folder. This way they can't vanish unexpectedly. + + * providers/imap/camel-imap-folder.h (CamelImapFolder): Added a + private field. + + * providers/imap/camel-imap-private.h: Added lock for imap + searches. + + * Merge from camel-mt-branch. + + * providers/imap/camel-imap-folder.c (imap_update_summary): Merge + fix, use the folder->summary. + (imap_get_message_flags, imap_set_message_flags, + imap_get_message_user_flag, imap_set_message_user_flag): Removed + again. + (camel_imap_folder_init): Setup private data/lock. + (imap_finalize): Free private data/search lock. + (imap_search_free): Lock the search_lock. + (imap_search_by_expression): Lock the search lock when using the + search object. Also copy/ref hte summary, rather than getting it + directly. + (imap_refresh_info): Free any info lookups. Use folder->summary + not imap_folder->summary. And lock around commands. + 2000-12-22 Dan Winship <danw@helixcode.com> * providers/imap/camel-imap-store.c (get_folder_info): When @@ -76,6 +106,570 @@ * providers/imap/camel-imap-folder.c: Use %S instead of "%s" where appropriate. +2000-12-22 Not Zed <NotZed@HelixCode.com> + + * camel-folder-summary.c (GLOBAL_INFO_UNLOCK): Doh, cut and paste + victim again, call unlock instead of lock! + (summary_assign_uid): Unlock around trying to get a new uid, if we + have a clash. + +2000-12-21 Not Zed <NotZed@HelixCode.com> + + * tests/folder/Makefile.am (TESTS): reenabled the tests here. + + * providers/nntp/camel-nntp-newsrc.c (CamelNNTPNewsrc): Add a + lock. Made completely mt-safe. Sigh, this is all so i can + support snooping of the set_flags stuff inside camel-nntp-folder, + since i've removed the global folder lock from all of the other + folder summary operations. + (camel_nntp_newsrc_read_for_server): Setup lock. + (camel_nntp_newsrc_get_highest_article_read): Lock internal access. + (camel_nntp_newsrc_get_num_articles_read): + (camel_nntp_newsrc_mark_article_read): + (camel_nntp_newsrc_mark_range_read): + (camel_nntp_newsrc_article_is_read): + (camel_nntp_newsrc_group_is_subscribed): + (camel_nntp_newsrc_unsubscribe_group): + (camel_nntp_newsrc_subscribe_group): + (camel_nntp_newsrc_get_subscribed_group_names): + (camel_nntp_newsrc_get_all_group_names): " + (camel_nntp_newsrc_write_to_file): Lock internal accesses. + (camel_nntp_newsrc_write): Lock around diry stuff. + + * providers/local/camel-maildir-summary.c (maildir_summary_check): + Lock around our internal poking of the summary hashtable/array. + (maildir_summary_sync): And the same here too. + + * camel-folder-summary.c: Moved the summaryprivate data into + camel-private.h. Only needed for the locks really. + (camel_folder_summary_init): Setup locks. + (camel_folder_summary_set_filename): Lock. + (camel_folder_summary_index): lock + (camel_folder_summary_uid): Lock + (camel_folder_summary_next_uid): " + (camel_folder_summary_set_uid): " + (camel_folder_summary_load): Lock around whole load. Hrm, + probably not required. + (camel_folder_summary_save): Lock around whole save, this is + reqwuired, unless we ref/copy the whole list first, and i couldn't + be bothreed. + (camel_folder_summary_add): lock + (summary_assign_uid): Lock around internal accesses. + (camel_folder_summary_info_new_from_parser): Lock around filtr stuff. + (camel_folder_summary_info_ref): Add locking. Could probably use + its own lock? + (camel_folder_summary_touch): Add locking, likewise. + (camel_folder_summary_clear): Lock. If the preiovus two are + changed, then so this will need mroe locking. + (camel_folder_summary_info_free): Lock around refcounting. + (camel_folder_summary_remove): Locking. + (camel_folder_summary_remove_uid): Lock ref, unlock, unref. Also + fixed a bug that would have made this never work, freeing the key, + which isn't allocated. + (camel_folder_summary_remove_index): lock, ref, unlock unref also. + (camel_message_info_ref): Lock using a separate global lock, since + we have no context here. + (camel_message_info_free): Here too. + (content_info_alloc): Use an alocation lock here. avoids races + with setting up content_info_chunks, etc. + (camel_folder_summary_info_new): And here too. + (camel_folder_summary_load): Changed to use a differnet lock for + loading/saving. + (camel_folder_summary_init): Doh, i do need a reflock afterall, + set one up. + (camel_folder_summary_finalize): Free it. + (camel_folder_summary_index): Add a reflock. + (camel_folder_summary_uid): And here too. + (camel_folder_summary_info_free): Use reflock instead of + summary_lock. + (camel_folder_summary_info_ref): Likewise. + (camel_folder_summary_remove_uid): Add reflocking. + (camel_folder_summary_remove_index): And here too. + + * providers/vee/camel-vee-folder.c (camel_vee_folder_new): Setup a + summary object. + (vee_folder_build): Build each item into the real summary object. + (vee_folder_add, vee_folder_add_uid, vee_folder_add_change): New + functions to add a single record to the vfolder summary. + (vee_folder_build_folder): Use a real summary. + (vee_get_uids): Removed, use default impl. + (vee_free_message_info): " + (vee_get_message_info): " + (vee_get_summary): " + (vee_get_unread_message_count): " + (vee_get_message_count): " + (vee_sync): Make vee-folder-sync sync all subordinate folders. + Well i dont think this is right, but enough peopl ehave aksed for + it. + (vee_expunge): Same for expunge. + (vee_set_message_flags): Call parent class, then mirror flags to + subfolder. + (vee_get_message_user_flag): Removed, we just use the default + impl, and our flags should always match. + (vee_set_message_user_flag): Snoop the set and set on subfolder. + (vee_get_message_flags): Removed, jsut use parent impl, assume our + copy matches the real ones. + (get_real_message): Removed. We sort ofneed to keep the + summaryitem refed while we use it, so this doesn't make as much + sense as it did once. + (camel_vee_folder_finalise): Unhook listening events as we unref + folders. + (camel_vee_folder_new): Set the summary messageinfo data size + properly. + (vfolder_remove_match): Fixed for summary change. + (folder_changed): ditto. Also stopped changed items from being + re-searched and possibly removed. Might have to resort to the old + whole-search idea again. + (message_changed): Similarly. Might be easier just not to bother + with a whole search. + + * providers/vee/camel-vee-folder.h: Removes summary + hashtable/array, and use a real summary object. Sigh, more work. + + * providers/nntp/camel-nntp-folder.c (nntp_folder_sync): Move + summary to folder object. + (nntp_folder_get_message_count): Removed, use default impl. + (nntp_folder_set_message_flags): Call parent impl to do the work, + but snoop for newsrc. + (nntp_folder_get_uids): Removed, use default impl. + (nntp_folder_get_summary): " + (nntp_folder_get_message_info): " + (nntp_folder_free_message_info): " + (camel_nntp_folder_new): moved summary to folder class + + * providers/imap/camel-imap-folder.c (camel_imap_folder_init): + Move summary to folder object. + (camel_imap_folder_new): " + (imap_finalize): No longer unref the summary (or infact, do + anything else either). + (imap_refresh_info): move summary to folder object. + (imap_sync): " + (imap_get_message_count): Removed, use default impl. + (imap_get_unread_message_count): " + (imap_get_uids): " + (imap_get_summary): " + (imap_free_summary): " + (imap_get_message_info): " + (imap_free_message_info): " + (imap_get_message_flags): " + (imap_set_message_flags): " + (imap_get_message_user_flag): ", this changes functionality, but + only by providing non-permanent flags. + (imap_set_message_user_flag): " + (imap_update_summary): move summary to folder object, and use + camel_folder_summary_info_new_from_header, instead of calling the + virtual function directly. + + * providers/local/camel-maildir-folder.c (maildir_append_message): + move summary to folder object, again. + (maildir_get_message): " + + * providers/local/camel-mh-folder.c (mh_append_message): move + summary to folder object. + (mh_get_message): " + + * providers/local/camel-mbox-folder.c (mbox_append_message): Move + summary to folder object + (mbox_get_message): " + (mbox_set_message_user_flag): We need our own copy of this, sigh, + so we can track if the xev needs a rewrite (probably, its only a + guide anyway). + (mbox_set_message_user_tag): Same same with this one. + (camel_mbox_folder_class_init): Link in these new virtual functions. + + * providers/local/camel-local-folder.h (CamelLocalFolder): removed + summary. + + * providers/local/camel-local-folder.c (local_get_message_count): + (local_get_unread_message_count): + (local_get_uids): + (local_get_summary): + (local_free_summary): + (local_get_message_info): + (local_free_message_info): + (local_get_message_flags): + (local_set_message_flags): + (local_get_message_user_flag): + (local_set_message_user_flag): + (local_get_message_user_tag): + (local_set_message_user_tag): Removed, all now use default + implementation. + (camel_local_folder_class_init): Removed overrides. + (local_init): Clear folder->summary. + (local_finalize): move summary to folder. + (camel_local_folder_construct): " + (local_sync): " + + * camel-folder.c (free_summary): Provide a real impl. + (get_summary): " + (free_uids): " + (get_uids): " + (free_message_info): " + (get_message_info): " + (set_message_user_tag): " + (get_message_user_tag): " + (set_message_user_flag): " + (get_message_user_flag): " Sigh, the last so far. + (get_message_flags): Sigh, 1 more afterall. + (get_unread_message_count): And and this. + (get_message_count): and this! + (set_message_flags): Sigh, and this. + (camel_folder_finalize): Unref the summary if we have it. + (camel_folder_free_uids): Remove locking. + (camel_folder_get_message_flags): Remove locks, since the summary + is now mt-safe. + (camel_folder_set_message_flags): " + (camel_folder_get_message_user_flag): " + (camel_folder_set_message_user_flag): " + (camel_folder_get_message_user_tag): " + (camel_folder_set_message_user_tag): " + (camel_folder_get_message_info): " + (camel_folder_free_message_info): " + (camel_folder_get_uids): " + (camel_folder_free_summary): " + (camel_folder_get_unread_message_count): " + (get_unread_message_count): Check we got info, no big deal, but + the summary might change while we're counting, and upset the count + slightly. + (camel_folder_get_message_count): Remove locks. + + * camel-folder.h (struct _CamelFolder): Added CamelFolderSummary + to the base folder object. Sigh, this is going to be a lot of + work ... + + * camel-service.c (camel_service_init, finalise): changed + connect_lock to be recursive e_mutex. + + * camel-remote-store.c (camel_remote_store_init, finalise): Changed + stream_lock to recursive e_mutex. + + * camel-private.h (RemoteStorePrivate, ServicePrivate): Use + recursive mutexes for connect_lock & stream_lock. Ick. Because + of the imap code. + + * providers/imap/camel-imap-private.h: Change the command_lock to + be an EMutex. + + * providers/imap/camel-imap-store.c (connect_to_server): Removed + the command_lock stuff. If we are just connected again, it should + be safe to assume we have exclusive access at this point without + it (and yes, removes a deadlock). + (imap_create): Remove a lock that wasn't even supposed to be + there. + (camel_imap_store_finalize, init): g_mutex->e_mutex(E_MUTEX_REC). + Use a recursive mutex since the imap code is multiply recursivly + calling some functions (sigh, it would be nice to fix this). + +2000-12-20 Not Zed <NotZed@HelixCode.com> + + * camel-folder.c (folder_changed): Change the locks to inside the + if. Stops a deadloc,k and we dont need a lock to read a value. + (message_changed): Likewise. + + * camel.c (camel_init): Blah, comment out g_thread_init, leave it + to the caller, since it can only ever be done once. + + * camel-folder-thread.c (camel_folder_thread_messages_new): Ref + and save the folder in the thread return. Also get the summary + once, and access that for the messageinfo's. Added bonus that now + threads should come out in a similar order. We need to do this so + that the messageinfo's dont get unref'd under us, and using the + summary directly simplifies memory management. + (camel_folder_thread_messages_destroy): Free the summary/unref the + folder. + + * camel-folder-thread.h: Add a folder to CamelFolderThread struct. + +2000-12-19 Not Zed <NotZed@HelixCode.com> + + * providers/imap/camel-imap-utils.c (imap_translate_sexp): Unref + using e_sexp_unref, since it is no longer an object. + + * tests/lib/camel-test.c: Added a global lock, and made it + thread-aware. + + * camel-exception.c: Add a global lock. Not sure its really + needed here, but it doesn't really hurt. + (camel_exception_clear): Lock around access. + (camel_exception_set): Same. + (camel_exception_setv): " + (camel_exception_xfer): " + + * providers/local/Makefile.am (noinst_HEADERS): Added, for + camel-local-private.h + + * camel-folder.c (camel_folder_search_by_expression): Removed + locking here, now upto the implementing class to lock. + (camel_folder_search_free): Removed the folder lock here too, for + consistency really, and the locking probably wasn't needed. + + * providers/local/camel-local-folder.c (local_get_summary): + Changed to copy/ref the summary items. + (local_free_summary): Unref the summary items/free the array. + (local_get_unread_message_count): Free info lookups. + (local_search_by_expression): Perform more fine-grained locking, + and dont lock the whole folder while searching, just the search + object. Dum de dum, it *would* be possible to not even have to + lock the search, by using our own copy of the search object each + time. Could be investigated. + (local_init): Init priaate data/lock. + (local_finalize): Free private data/lock. + (local_search_free): Also lock around the search object, because + it accesses global data inside. + + * README.mt: Some info about the mt code development. + + * camel-folder-search.c (match_message): Put in a warning, this + plain deadlocks so far, but i'm going to bed now. + (camel_folder_search_finalize): Use e_sexp_unref() since its no + longer an object. + + * camel-session.c (camel_session_init): Init private data/lock. + (camel_session_finalise): Free private data/lock. + (camel_session_register_provider): Lock hash insert. + (service_cache_remove): Lock. + (camel_session_list_providers): " + (camel_session_get_service): Lock the whole function. + + * camel-session.h (struct _CamelSession): Added private data. + + * providers/imap/camel-imap-store.c (camel_imap_store_init): Setup + private data. + (camel_imap_store_finalize): Free private data. + (connect_to_server): Lock around command calls. I dont think its + needed, but it shouldn't hurt either. + (imap_connect): Lock around command calls. Again, I don think it + is needed, but wont hurt. + (imap_disconnect): ditto for this one. + (imap_create): Lock around command. + (get_folder): " + (get_folder_info): " + (subscribe_folder): " + (unsubscribe_folder): " + (imap_keepalive): " + + * providers/imap/camel-imap-folder.c (camel_imap_folder_new): Lock + around calling a command. + (imap_refresh_info): Likewise. + (imap_sync): " + (imap_append_message): " + (imap_copy_message_to): " + (imap_move_message_to): " + (imap_get_message): " + (imap_update_summary): " + (imap_search_by_expression): ", also minor logic cleanup. + (imap_get_summary): Copy/ref the summary items. + (imap_free_summary): And free it now too. + + * providers/imap/camel-imap-auth.c (imap_try_kerberos_v4_auth): + Lock the command stream for the command session. + + * providers/imap/camel-imap-private.h: Private (locking info) + stuff for imap provider. + (CAMEL_IMAP_STORE_UNLOCK): Fix to call unlock, rather than lock! + + * camel-service.c (camel_service_query_auth_types): Lock also for + the connection here, incase the service needs to connect. + + * camel-remote-store.c (camel_remote_store_init): Init private + data/lock. + (camel_remote_store_finalise): Readded, free private data/lock. + (remote_send_stream): Changed to return the number of bytes + written, like a good little stream function should. + (camel_remote_store_send_string): Lock for stream access. + (camel_remote_store_send_stream): Same here. + (camel_remote_store_recv_line): And here. + (camel_remote_store_refresh_folders): Lock the store for cache + access. + (timeout_cb): Lock for stream access. + +2000-12-18 Not Zed <NotZed@HelixCode.com> + + * camel-service.c (camel_service_init): Init private data. + (camel_service_finalise): Free private data. + (camel_service_connect): Lock around connection access. + (camel_service_disconnect): " + + * camel-service.h (struct _CamelService): Added private field. + + * camel-store.c (camel_store_init): Init private data/lock. + (camel_store_finalize): Free private data/lock. + (camel_store_get_folder): Lock internal calls. + (camel_store_delete_folder): " + (camel_store_rename_folder): " + (camel_store_get_root_folder): " + (camel_store_get_default_folder): " + (camel_store_get_folder_info): " + (camel_store_folder_subscribed): " + (camel_store_subscribe_folder): " + (camel_store_unsubscribe_folder): " + (lookup_folder): Lock accesses to the folder cache. + (cache_folder): " + (uncache_folder): And here too. + + * camel-store.h (struct _CamelStore): Added a private field. + + * Makefile.am (noinst_HEADERS): Add camel-private.h. There is a + comment in that header as to why it ins't installed. + + * camel-private.h: New header for private data, that subclasses + can get to. + + * tests/lib/camel-test.c (camel_test_init): If we do have threads + enabled already, dont call g_thread_init(). + + * tests/lib/folders.c (test_folder_counts): Add free of info. + (test_folder_message): And here too. + + * providers/local/camel-maildir-summary.c (remove_summary): Free + info. + (maildir_summary_check): Free info lookups. + + * providers/local/camel-mh-summary.c (message_info_new): Removed, + didn't add value. + (remove_summary): free info after being used. + (mh_summary_check): Free info lookups. + (mh_summary_sync): and here too. + + * providers/local/camel-mbox-summary.c (summary_update): Free info + lookups. + (mbox_summary_sync_full): Likewise. + (mbox_summary_sync_quick): And here. + (mbox_summary_sync): ... and here. + + * providers/local/camel-maildir-folder.c (maildir_get_message): + Free messageinfo lookups. + + * providers/local/camel-mh-folder.c (mh_get_message): Free + messageinfo lookups. + + * providers/local/camel-mbox-folder.c (mbox_get_message): Free + messageinfo lookups. + + * providers/local/camel-local-summary.c + (message_info_new_from_message): Removed, since it wasn't doing + anything useful. + (message_info_new_from_parser): Same. And some other dead code. + + * providers/local/camel-local-folder.c (local_get_message_info): + deconstify. + (local_free_message_info):new func to free info. + (local_get_uids): + (local_get_message_flags): + (local_set_message_flags): + (local_get_message_user_flag): + (local_set_message_user_flag): + (local_get_message_user_tag): + (local_set_message_user_tag): Free all info lookups. + (local_expunge): Dont call camel_folder_* functions, to avoid a deadlock. + + * providers/nntp/camel-nntp-folder.c + (nntp_folder_get_message_info): deconstify. + (nntp_folder_free_message_info): Free summary item. + (nntp_folder_get_message_flags): Free summary lookup. + (nntp_folder_set_message_flags): and here. + (nntp_folder_get_uids): And here. + + * providers/imap/camel-imap-auth.c: Make base64_*_simple optional + with the HAVE_KRB4 flag. + + * providers/imap/camel-imap-folder.c (imap_free_message_info): + Added a free of the message info. + (imap_refresh_info): + (imap_sync): + (imap_refresh_info): + (imap_get_unread_message_count): + (imap_get_uids): + (imap_get_message_flags): + (imap_set_message_flags): Free any summary lookups. + (imap_get_message_flags): Get the message info directly from the + summary, not via the folder interface. + (imap_move_message_to): Dont call folder functions directly + (delete_message), as it will deadlock since we already have the + lock. + + * providers/vee/camel-vee-folder.c (vee_free_message_info): Free/unref + the messageinfo. + (vee_get_message_info): First ref the internal copy, then return it. + (folder_changed): Free all got message info's. + (message_changed): And here. + (get_real_message): And here. + (vee_folder_build): and here. + (vee_folder_build_folder): ... and here. + + * camel-folder-summary.c (camel_folder_summary_info_new): + Initialise the messageinfo refcount to 1. + (camel_folder_summary_info_free): Down the refcount till we need + to free. + (camel_folder_summary_info_ref): New function to up the refcount. + (camel_message_info_dup_to): Sewt the refcount of the dest to 1. + (camel_message_info_new): Set refcount to 1. + (camel_message_info_new_from_header): Dont allocate the mi + ourselves. + (camel_message_info_free): Handle refcounting. + (camel_message_info_ref): New function. + (camel_folder_summary_index): Ref the messageinfo before returning + it. + (camel_folder_summary_uid): Likewise. + (camel_folder_summary_save): Access the messageinfo array + directly, rather than through accessors (saves ref/unref). + (camel_folder_summary_clear): Likewise. + (camel_folder_summary_remove_index): Likewise. + (main): Free lookups. + + * camel-folder-summary.h (CamelMessageInfo): Added a refcount + value. + + * camel-folder.c (camel_folder_free_message_info): New function to + 'free' a message info. + (camel_folder_get_message_info): Deconstify return. + (camel_folder_lock): New (internal) function to thread-lock the + folder. + (camel_folder_unlock): Likewise for unlocking. + (freeze): + (thaw): Lock around access to folder->frozen/changed_frozen. + (folder_changed): Likewise. + (message_changed): Likewise. + (camel_folder_init): Init private data, locks and moved frozen + info. + (camel_folder_finalize): Free new private data, locks and moved + frozen info. + (copy_message_to): Free the messageinfo after we've used it. + (move_message_to): And here too. + (camel_folder_sync): Lock around virtual method invocation. Just + locking around any virtual method that is known to need it. This + is the first cut at locking. + (camel_folder_refresh_info): " + (camel_folder_expunge): " + (camel_folder_get_message_count): " + (camel_folder_get_unread_message_count): " + (camel_folder_append_message): " + (camel_folder_set_message_flags): " + (camel_folder_get_message_flags): " + (camel_folder_get_message_user_flag): " + (camel_folder_set_message_user_flag): " + (camel_folder_get_message_user_tag): " + (camel_folder_set_message_user_tag): " + (camel_folder_get_message): " + (camel_folder_get_uids): " + (camel_folder_free_uids): " + (camel_folder_get_summary): " + (camel_folder_search_by_expression): " + (camel_folder_free_summary): " + (camel_folder_search_free): " + (camel_folder_move_message_to): " + (camel_folder_copy_message_to): " + (copy_message_to): Dont call any of our own folder functions + directly, otherwise we will deadlock. + (move_message_to): Likewise. + + * camel-folder.h (CamelFolder): Added free_message_info() function + & deconstify get_message_info()'s return. + (CamelFolder): Add a private field (for locks). + (struct _CamelFolder): Moved changed_changed/frozen into private + data, since it really is private and now needs locked access. + +>>>>>>> 1.641.2.8 2000-12-15 Jeffrey Stedfast <fejj@helixcode.com> * camel-mime-utils.c (header_fold): When checking to see if we diff --git a/camel/Makefile.am b/camel/Makefile.am index bff90e3d89..318b1596f4 100644 --- a/camel/Makefile.am +++ b/camel/Makefile.am @@ -127,7 +127,8 @@ libcamel_la_LIBADD = $(top_builddir)/e-util/libeutil.la $(UNICODE_LIBS) noinst_HEADERS = \ - camel-charset-map-private.h + camel-charset-map-private.h \ + camel-private.h EXTRA_DIST = \ README diff --git a/camel/README.mt b/camel/README.mt new file mode 100644 index 0000000000..aeece1b0bb --- /dev/null +++ b/camel/README.mt @@ -0,0 +1,171 @@ + +This version of camel is working towards being multi-thread safe +(MT-SAFE). At least, for the important api's. + +This code has now been merged into the main head, but this file +will remain here as a log of how it was done, incase any issues +arise. The ChangeLog of course has a much more detailed list +of changes. + +Intended method +=============== + +I intend working on it in several stages: + +1. Making the api multi-threadable. Basically removing some const-returns, +and copying some data where it wasn't before. The api should +still continue to work if not being used in a multithreaded +application. There is not a significant amount of work here since +this was more or less the intention all along. + +Some functions where references to objects are returned may have to be +changed slightly, so that refcounts are incremented before return. +This doesn't affect much though. + +camel_folder::get_message_info done +camel_folder_summary::uid done +camel_folder_summary::index done +camel_folder::get_summary + Needs to ref each summary item it points to. done +camel_folder::free_summary + Needs to unref each summary item it points to. done +camel_folder_get_message_tag + needs to copy the tag return +camel_maildir_summary filename string + should not be able to modify the string + array contents after it has been added to + the summary. +camel_folder done + Make every camel-folder use a camel-folder-summary. + This just reduces some of the code duplication, + since everything but vee-folder does this already. + +2. Adding high level locks for proof of concept. The locks will +be stored in private or global data, so the api should remain the same for +non-threaded applications. + +A per-folder lock which governs access to the folder + summary, the folder file or + communications socket, etc. done +Locking for exceptions. done +Per store locks for internal stuff. done +Per-service locks for various internal lists and + caches done + +3. Further fine-grained locking where it can be done/is worthwhile. + +A per-index lock for libibex done +Locking for the search object half done +Internal lock for the folder_summary itself + So that searching can be detatched from other + folder operations, etc. done +Possibly a lock for access to parts of a mime-part + or message + +4. A method to cancel operations. + +Individual outstanding operations must be cancellable, and not just +'all current operations'. This will probably not use pthread_cancel +type of cancelling. + +This will however, probably use a method for starting a new thread, +through camel, that can then be cancelled, and/or some method of +registering that a thread can be cancelled. Blocking states within +camel, within that thread, will then act as checkpoints for if the +operation, and if it is cancelled, the operation will abort +(i.e. fail, with an appropriate exception code). + +Operation cancelling should also function when the application is not +multi-threaded. Not sure of the api for this yet, probably a callback +system. Hopefully the api for both scenarios can be made the same. + +Other thoughts +============== + +Basically much of the code in camel that does the actual work does NOT +need to be thread safe to make it safely usable in an mt context. + +camel-folder, camel-summary, camel-imap-search, and the camel-service +classes (at least) are the important ones to be made multithreaded. + +For other things, they are either resources that are created +one-off (for example, camel-mime-message, and its associated +parts, like camel-internet-address), or multithreadedness +doesn't make a lot of sense - e.g. camel-stream, or camel-mime-parser. + +So basically the approach is a low-risk one. Adding the minimum +number of locks to start with, and providing further fine-grained +locks as required. The locks should not need to be particularly +fine-grained in order to get reasonable results. + +Log of changes +============== + +Changed CamelFolder:get_message_info() to return a ref'd copy, requiring +all get_message_info()'s to have a matching free_message_info(). + +Moved the CamelFolder frozen changelog data to a private structure. + +Added a mutex for CamelFolder frozen changelog stuff (it was just easy +to do, although it isn't needed yet). + +Added a single mutex around all other CamelFolder functions that need +it, this is just the first cut at mt'edness. + +Fixed all camel-folder implementations that call any other +camel-folder functions to call via virtual methods, to bypass the locks. + +Added camel-store private data. + +Added a single mutex lock for camel-store's folder functions. + +Added camel-service private data. + +Added a single mutex lock for camel-service's connect stuff. + +Added a mutex for remote-store stream io stuff. + +Added a mutex for imap, so it can bracket a compound command +exclusively. Pop doesn't need this since you can only have a single +folder per store, and the folder interface is already forced +single-threaded. + +Added mutex for camel-session, most operations. + +Running the tests finds at least 1 deadlock so far. Need to +work on that. + +Fixed get_summary to ref/unref its items. + +Removed the global folder lock from the toplevel +camel_folder_search(), each implementation must now handle locking. + +Fixed the local-folder implementation of searching. imap-folder +searching should already be mt-safe through the command lock. + +Fixed imap summary to ref/unref too. + +Built some test cases, and expanded the test framework library to +handle multiple threads. It works! + +Next, added a recursive mutex class, so that locking inside imap had +any chance of working. Got imap working. + +Moved the camel folder summary into the base folder class, and fixed +everything to use it that way. + +Made the vfolder use a real camel-folder-summary rather than a +hashtable + array that it was using, and probably fixed some problems +which caused evolution-mail not to always catch flag updates. Oh, and +made it sync/expunge all its subfolders when sync/expungeing. + +Made the camel-folder summary completely mt-safe. + +Removed all of the locks on the folder functions dealing directly with +the summary, so now for example all summary lookups will not be +interupted by long operations. + +Made the nntp newsrc thing mt-safe, because of some unfortunate +sideeffect of it being called from the summary interaction code in +nntp-folder. + diff --git a/camel/camel-exception.c b/camel/camel-exception.c index ef90136859..6f07183f2b 100644 --- a/camel/camel-exception.c +++ b/camel/camel-exception.c @@ -27,7 +27,21 @@ #include <config.h> #include "camel-exception.h" +/* i dont know why gthread_mutex stuff even exists, this is easier */ +/* also, i'm not convinced mutexes are needed here. But it + doesn't really hurt either */ +#ifdef ENABLE_THREADS +#include <pthread.h> + +static pthread_mutex_t exception_mutex = PTHREAD_MUTEX_INITIALIZER; + +#define CAMEL_EXCEPTION_LOCK(e) (pthread_mutex_lock(&exception_mutex)) +#define CAMEL_EXCEPTION_UNLOCK(e) (pthread_mutex_unlock(&exception_mutex)) +#else +#define CAMEL_EXCEPTION_LOCK(e) +#define CAMEL_EXCEPTION_UNLOCK(e) +#endif /** * camel_exception_new: allocate a new exception object. @@ -83,19 +97,18 @@ camel_exception_init (CamelException *ex) void camel_exception_clear (CamelException *exception) { - if (!exception) return; - - /* free the description text */ + if (!exception) + return; + + CAMEL_EXCEPTION_LOCK(exception); + if (exception->desc) g_free (exception->desc); exception->desc = NULL; - - /* set the Exception Id to NULL */ exception->id = CAMEL_EXCEPTION_NONE; -} - - + CAMEL_EXCEPTION_UNLOCK(exception); +} /** * camel_exception_free: Free an exception @@ -108,12 +121,11 @@ camel_exception_clear (CamelException *exception) void camel_exception_free (CamelException *exception) { - if (!exception) return; + if (!exception) + return; - /* free the description text */ if (exception->desc) g_free (exception->desc); - /* free the exeption itself */ g_free (exception); } @@ -137,17 +149,19 @@ camel_exception_set (CamelException *ex, ExceptionId id, const char *desc) { - /* if no exception is given, do nothing */ - if (!ex) return; + if (!ex) + return; + + CAMEL_EXCEPTION_LOCK(exception); ex->id = id; - /* remove the previous exception description */ if (ex->desc) - g_free (ex->desc); - ex->desc = g_strdup (desc); -} + g_free(ex->desc); + ex->desc = g_strdup(desc); + CAMEL_EXCEPTION_UNLOCK(exception); +} /** * camel_exception_setv: set an exception @@ -178,25 +192,22 @@ camel_exception_setv (CamelException *ex, { va_list args; - /* if no exception is given, do nothing */ - if (!ex) return; + if (!ex) + return; + + CAMEL_EXCEPTION_LOCK(exception); if (ex->desc) g_free (ex->desc); - /* create the temporary exception string */ va_start(args, format); ex->desc = g_strdup_vprintf (format, args); va_end (args); ex->id = id; -} - - - - - + CAMEL_EXCEPTION_UNLOCK(exception); +} /** * camel_exception_xfer: transfer an exception @@ -212,6 +223,8 @@ void camel_exception_xfer (CamelException *ex_dst, CamelException *ex_src) { + CAMEL_EXCEPTION_LOCK(exception); + if (ex_dst->desc) g_free (ex_dst->desc); @@ -220,13 +233,9 @@ camel_exception_xfer (CamelException *ex_dst, ex_src->desc = NULL; ex_src->id = CAMEL_EXCEPTION_NONE; -} - - - - - + CAMEL_EXCEPTION_UNLOCK(exception); +} /** * camel_exception_get_id: get the exception id @@ -246,9 +255,6 @@ camel_exception_get_id (CamelException *ex) return CAMEL_EXCEPTION_NONE; } - - - /** * camel_exception_get_description: get the description of an exception. * @ex: The exception object @@ -262,8 +268,10 @@ camel_exception_get_id (CamelException *ex) const gchar * camel_exception_get_description (CamelException *ex) { + char *ret = NULL; + if (ex) - return ex->desc; - else - return NULL; + ret = ex->desc; + + return ret; } diff --git a/camel/camel-folder-search.c b/camel/camel-folder-search.c index f887986e3b..365580ffa8 100644 --- a/camel/camel-folder-search.c +++ b/camel/camel-folder-search.c @@ -118,9 +118,8 @@ camel_folder_search_finalize (CamelObject *obj) CamelFolderSearch *search = (CamelFolderSearch *)obj; struct _CamelFolderSearchPrivate *p = _PRIVATE(obj); - /* yeah, this is a gtk object */ if (search->sexp) - gtk_object_unref((GtkObject *)search->sexp); + e_sexp_unref(search->sexp); g_free(search->last_search); g_hash_table_foreach(p->mempool_hash, free_mempool, obj); @@ -404,6 +403,9 @@ void camel_folder_search_free_result(CamelFolderSearch *search, GPtrArray *resul g_ptr_array_free(result, TRUE); } + + + /* dummy function, returns false always, or an empty match array */ static ESExpResult * search_dummy(struct _ESExp *f, int argc, struct _ESExpResult **argv, CamelFolderSearch *search) diff --git a/camel/camel-folder-summary.c b/camel/camel-folder-summary.c index 6ee83348a4..b6d8fc910e 100644 --- a/camel/camel-folder-summary.c +++ b/camel/camel-folder-summary.c @@ -43,6 +43,21 @@ #include "e-util/md5-utils.h" #include "e-util/e-memory.h" +#include "camel-private.h" + +#ifdef ENABLE_THREADS +#include <pthread.h> + +static pthread_mutex_t info_lock = PTHREAD_MUTEX_INITIALIZER; + +/* this lock is ONLY for the standalone messageinfo stuff */ +#define GLOBAL_INFO_LOCK(i) pthread_mutex_lock(&info_lock) +#define GLOBAL_INFO_UNLOCK(i) pthread_mutex_unlock(&info_lock) +#else +#define GLOBAL_INFO_LOCK(i) +#define GLOBAL_INFO_UNLOCK(i) +#endif + /* this should probably be conditional on it existing */ #define USE_BSEARCH @@ -55,17 +70,6 @@ extern int strdup_count, malloc_count, free_count; #define CAMEL_FOLDER_SUMMARY_VERSION (11) -struct _CamelFolderSummaryPrivate { - GHashTable *filter_charset; /* CamelMimeFilterCharset's indexed by source charset */ - - CamelMimeFilterIndex *filter_index; - CamelMimeFilterBasic *filter_64; - CamelMimeFilterBasic *filter_qp; - CamelMimeFilterSave *filter_save; - - ibex *index; -}; - #define _PRIVATE(o) (((CamelFolderSummary *)(o))->priv) /* trivial lists, just because ... */ @@ -155,6 +159,14 @@ camel_folder_summary_init (CamelFolderSummary *s) s->messages = g_ptr_array_new(); s->messages_uid = g_hash_table_new(g_str_hash, g_str_equal); + +#ifdef ENABLE_THREADS + p->summary_lock = g_mutex_new(); + p->io_lock = g_mutex_new(); + p->filter_lock = g_mutex_new(); + p->alloc_lock = g_mutex_new(); + p->ref_lock = g_mutex_new(); +#endif } static void free_o_name(void *key, void *value, void *data) @@ -194,6 +206,14 @@ camel_folder_summary_finalize (CamelObject *obj) if (p->filter_save) camel_object_unref ((CamelObject *)p->filter_save); +#ifdef ENABLE_THREADS + g_mutex_free(p->summary_lock); + g_mutex_free(p->io_lock); + g_mutex_free(p->filter_lock); + g_mutex_free(p->alloc_lock); + g_mutex_free(p->ref_lock); +#endif + g_free(p); } @@ -225,8 +245,7 @@ camel_folder_summary_get_type (void) CamelFolderSummary * camel_folder_summary_new (void) { - CamelFolderSummary *new = CAMEL_FOLDER_SUMMARY ( camel_object_new (camel_folder_summary_get_type ())); - return new; + CamelFolderSummary *new = CAMEL_FOLDER_SUMMARY ( camel_object_new (camel_folder_summary_get_type ())); return new; } @@ -239,8 +258,12 @@ camel_folder_summary_new (void) **/ void camel_folder_summary_set_filename(CamelFolderSummary *s, const char *name) { + CAMEL_SUMMARY_LOCK(s, summary_lock); + g_free(s->summary_path); s->summary_path = g_strdup(name); + + CAMEL_SUMMARY_UNLOCK(s, summary_lock); } /** @@ -294,16 +317,33 @@ camel_folder_summary_count(CamelFolderSummary *s) * @i: * * Retrieve a summary item by index number. + * + * A referenced to the summary item is returned, which may be + * ref'd or free'd as appropriate. * * Return value: The summary item, or NULL if the index @i is out * of range. + * It must be freed using camel_folder_summary_info_free(). **/ CamelMessageInfo * camel_folder_summary_index(CamelFolderSummary *s, int i) { + CamelMessageInfo *info = NULL; + + CAMEL_SUMMARY_LOCK(s, ref_lock); + CAMEL_SUMMARY_LOCK(s, summary_lock); + if (i<s->messages->len) - return g_ptr_array_index(s->messages, i); - return NULL; + info = g_ptr_array_index(s->messages, i); + + CAMEL_SUMMARY_UNLOCK(s, summary_lock); + + if (info) + info->refcount++; + + CAMEL_SUMMARY_UNLOCK(s, ref_lock); + + return info; } /** @@ -312,14 +352,32 @@ camel_folder_summary_index(CamelFolderSummary *s, int i) * @uid: * * Retrieve a summary item by uid. + * + * A referenced to the summary item is returned, which may be + * ref'd or free'd as appropriate. * * Return value: The summary item, or NULL if the uid @uid * is not available. + * It must be freed using camel_folder_summary_info_free(). **/ CamelMessageInfo * camel_folder_summary_uid(CamelFolderSummary *s, const char *uid) { - return g_hash_table_lookup(s->messages_uid, uid); + CamelMessageInfo *info; + + CAMEL_SUMMARY_LOCK(s, ref_lock); + CAMEL_SUMMARY_LOCK(s, summary_lock); + + info = g_hash_table_lookup(s->messages_uid, uid); + + CAMEL_SUMMARY_UNLOCK(s, summary_lock); + + if (info) + info->refcount++; + + CAMEL_SUMMARY_UNLOCK(s, ref_lock); + + return info; } /** @@ -333,7 +391,14 @@ camel_folder_summary_uid(CamelFolderSummary *s, const char *uid) **/ guint32 camel_folder_summary_next_uid(CamelFolderSummary *s) { - guint32 uid = s->nextuid++; + guint32 uid; + + + CAMEL_SUMMARY_LOCK(s, summary_lock); + + uid = s->nextuid++; + + CAMEL_SUMMARY_UNLOCK(s, summary_lock); /* FIXME: sync this to disk */ /* summary_header_save(s);*/ @@ -352,7 +417,11 @@ guint32 camel_folder_summary_next_uid(CamelFolderSummary *s) void camel_folder_summary_set_uid(CamelFolderSummary *s, guint32 uid) { /* TODO: sync to disk? */ + CAMEL_SUMMARY_LOCK(s, summary_lock); + s->nextuid = MAX(s->nextuid, uid); + + CAMEL_SUMMARY_UNLOCK(s, summary_lock); } /** @@ -406,8 +475,10 @@ camel_folder_summary_load(CamelFolderSummary *s) return -1; } + CAMEL_SUMMARY_LOCK(s, io_lock); if ( ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->summary_header_load(s, in) == -1) { fclose(in); + CAMEL_SUMMARY_UNLOCK(s, io_lock); return -1; } @@ -422,6 +493,8 @@ camel_folder_summary_load(CamelFolderSummary *s) camel_folder_summary_add(s, mi); } + + CAMEL_SUMMARY_UNLOCK(s, io_lock); if (fclose(in) == -1) return -1; @@ -481,22 +554,29 @@ camel_folder_summary_save(CamelFolderSummary *s) io(printf("saving header\n")); + CAMEL_SUMMARY_LOCK(s, io_lock); + if ( ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->summary_header_save(s, out) == -1) { fclose(out); + CAMEL_SUMMARY_UNLOCK(s, io_lock); return -1; } /* now write out each message ... */ /* FIXME: check returns */ - count = camel_folder_summary_count(s); + + count = s->messages->len; for (i=0;i<count;i++) { - mi = camel_folder_summary_index(s, i); + mi = s->messages->pdata[i]; ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->message_info_save(s, out, mi); if (s->build_content) { perform_content_info_save(s, out, mi->content); } } + + CAMEL_SUMMARY_UNLOCK(s, io_lock); + if (fclose(out) == -1) return -1; @@ -515,12 +595,18 @@ summary_assign_uid(CamelFolderSummary *s, CamelMessageInfo *info) uid = camel_message_info_uid(info); } + CAMEL_SUMMARY_LOCK(s, summary_lock); + while (g_hash_table_lookup(s->messages_uid, uid)) { g_warning("Trying to insert message with clashing uid (%s). new uid re-assigned", camel_message_info_uid(info)); + CAMEL_SUMMARY_UNLOCK(s, summary_lock); camel_message_info_set_uid(info, camel_folder_summary_next_uid_string(s)); uid = camel_message_info_uid(info); info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED; + CAMEL_SUMMARY_LOCK(s, summary_lock); } + + CAMEL_SUMMARY_UNLOCK(s, summary_lock); } /** @@ -542,6 +628,8 @@ void camel_folder_summary_add(CamelFolderSummary *s, CamelMessageInfo *info) summary_assign_uid(s, info); + CAMEL_SUMMARY_LOCK(s, summary_lock); + #ifdef DOESTRV /* this is vitally important, and also if this is ever modified, then the hash table needs to be resynced */ @@ -551,6 +639,8 @@ void camel_folder_summary_add(CamelFolderSummary *s, CamelMessageInfo *info) g_ptr_array_add(s->messages, info); g_hash_table_insert(s->messages_uid, (char *)camel_message_info_uid(info), info); s->flags |= CAMEL_SUMMARY_DIRTY; + + CAMEL_SUMMARY_UNLOCK(s, summary_lock); } /** @@ -668,6 +758,8 @@ CamelMessageInfo *camel_folder_summary_info_new_from_parser(CamelFolderSummary * * know if we are going to store this in the summary, but no matter */ summary_assign_uid(s, info); + CAMEL_SUMMARY_LOCK(s, filter_lock); + if (p->index) { if (p->filter_index == NULL) p->filter_index = camel_mime_filter_index_new_ibex(p->index); @@ -677,6 +769,9 @@ CamelMessageInfo *camel_folder_summary_info_new_from_parser(CamelFolderSummary * /* always scan the content info, even if we dont save it */ info->content = summary_build_content_info(s, info, mp); + + CAMEL_SUMMARY_UNLOCK(s, filter_lock); + info->size = camel_mime_parser_tell(mp) - start; } return info; @@ -731,7 +826,7 @@ perform_content_info_free(CamelFolderSummary *s, CamelMessageContentInfo *ci) * @s: * @mi: * - * Free the message info @mi, and all associated memory. + * Unref and potentially free the message info @mi, and all associated memory. **/ void camel_folder_summary_info_free(CamelFolderSummary *s, CamelMessageInfo *mi) { @@ -740,6 +835,18 @@ void camel_folder_summary_info_free(CamelFolderSummary *s, CamelMessageInfo *mi) g_assert(mi); g_assert(s); + CAMEL_SUMMARY_LOCK(s, ref_lock); + + g_assert(mi->refcount >= 1); + + mi->refcount--; + if (mi->refcount > 0) { + CAMEL_SUMMARY_UNLOCK(s, ref_lock); + return; + } + + CAMEL_SUMMARY_UNLOCK(s, ref_lock); + ci = mi->content; ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->message_info_free(s, mi); @@ -749,6 +856,24 @@ void camel_folder_summary_info_free(CamelFolderSummary *s, CamelMessageInfo *mi) } /** + * camel_folder_summary_info_ref: + * @s: + * @mi: + * + * Add an extra reference to @mi. + **/ +void camel_folder_summary_info_ref(CamelFolderSummary *s, CamelMessageInfo *mi) +{ + g_assert(mi); + g_assert(s); + + CAMEL_SUMMARY_LOCK(s, ref_lock); + g_assert(mi->refcount >= 1); + mi->refcount++; + CAMEL_SUMMARY_UNLOCK(s, ref_lock); +} + +/** * camel_folder_summary_touch: * @s: * @@ -757,7 +882,9 @@ void camel_folder_summary_info_free(CamelFolderSummary *s, CamelMessageInfo *mi) void camel_folder_summary_touch(CamelFolderSummary *s) { + CAMEL_SUMMARY_LOCK(s, summary_lock); s->flags |= CAMEL_SUMMARY_DIRTY; + CAMEL_SUMMARY_UNLOCK(s, summary_lock); } /** @@ -771,16 +898,20 @@ camel_folder_summary_clear(CamelFolderSummary *s) { int i; - if (camel_folder_summary_count(s) == 0) + CAMEL_SUMMARY_LOCK(s, summary_lock); + if (camel_folder_summary_count(s) == 0) { + CAMEL_SUMMARY_UNLOCK(s, summary_lock); return; + } - for (i=0;i<camel_folder_summary_count(s);i++) - camel_folder_summary_info_free(s, camel_folder_summary_index(s, i)); + for (i=0;i<s->messages->len;i++) + camel_folder_summary_info_free(s, s->messages->pdata[i]); g_ptr_array_set_size(s->messages, 0); g_hash_table_destroy(s->messages_uid); s->messages_uid = g_hash_table_new(g_str_hash, g_str_equal); s->flags |= CAMEL_SUMMARY_DIRTY; + CAMEL_SUMMARY_UNLOCK(s, summary_lock); } /** @@ -792,10 +923,13 @@ camel_folder_summary_clear(CamelFolderSummary *s) **/ void camel_folder_summary_remove(CamelFolderSummary *s, CamelMessageInfo *info) { + CAMEL_SUMMARY_LOCK(s, summary_lock); g_hash_table_remove(s->messages_uid, camel_message_info_uid(info)); g_ptr_array_remove(s->messages, info); - camel_folder_summary_info_free(s, info); s->flags |= CAMEL_SUMMARY_DIRTY; + CAMEL_SUMMARY_UNLOCK(s, summary_lock); + + camel_folder_summary_info_free(s, info); } /** @@ -810,10 +944,19 @@ void camel_folder_summary_remove_uid(CamelFolderSummary *s, const char *uid) CamelMessageInfo *oldinfo; char *olduid; + CAMEL_SUMMARY_LOCK(s, ref_lock); + CAMEL_SUMMARY_LOCK(s, summary_lock); if (g_hash_table_lookup_extended(s->messages_uid, uid, (void *)&olduid, (void *)&oldinfo)) { + /* make sure it doesn't vanish while we're removing it */ + oldinfo->refcount++; + CAMEL_SUMMARY_UNLOCK(s, summary_lock); + CAMEL_SUMMARY_UNLOCK(s, ref_lock); camel_folder_summary_remove(s, oldinfo); - g_free(olduid); - } + camel_folder_summary_info_free(s, oldinfo); + } else { + CAMEL_SUMMARY_UNLOCK(s, summary_lock); + CAMEL_SUMMARY_UNLOCK(s, ref_lock); + } } /** @@ -825,11 +968,20 @@ void camel_folder_summary_remove_uid(CamelFolderSummary *s, const char *uid) **/ void camel_folder_summary_remove_index(CamelFolderSummary *s, int index) { - CamelMessageInfo *oldinfo; - - oldinfo = camel_folder_summary_index (s, index); - if (oldinfo) - camel_folder_summary_remove(s, oldinfo); + CAMEL_SUMMARY_LOCK(s, ref_lock); + CAMEL_SUMMARY_LOCK(s, summary_lock); + if (index < s->messages->len) { + CamelMessageInfo *info = s->messages->pdata[index]; + /* make sure it doesn't vanish while we're not looking */ + info->refcount++; + CAMEL_SUMMARY_UNLOCK(s, summary_lock); + CAMEL_SUMMARY_UNLOCK(s, ref_lock); + camel_folder_summary_remove(s, info); + camel_folder_summary_info_free(s, info); + } else { + CAMEL_SUMMARY_UNLOCK(s, summary_lock); + CAMEL_SUMMARY_UNLOCK(s, ref_lock); + } } /** @@ -1430,13 +1582,17 @@ camel_folder_summary_info_new(CamelFolderSummary *s) { CamelMessageInfo *mi; + CAMEL_SUMMARY_LOCK(s, alloc_lock); if (s->message_info_chunks == NULL) s->message_info_chunks = e_memchunk_new(32, s->message_info_size); mi = e_memchunk_alloc(s->message_info_chunks); + CAMEL_SUMMARY_UNLOCK(s, alloc_lock); + memset(mi, 0, s->message_info_size); #ifdef DOESTRV mi->strings = e_strv_new(s->message_info_strings); #endif + mi->refcount = 1; return mi; } @@ -1445,9 +1601,12 @@ content_info_alloc(CamelFolderSummary *s) { CamelMessageContentInfo *ci; + CAMEL_SUMMARY_LOCK(s, alloc_lock); if (s->content_info_chunks == NULL) s->content_info_chunks = e_memchunk_new(32, s->content_info_size); ci = e_memchunk_alloc(s->content_info_chunks); + CAMEL_SUMMARY_UNLOCK(s, alloc_lock); + memset(ci, 0, s->content_info_size); return ci; } @@ -1769,6 +1928,7 @@ next_uid_string(CamelFolderSummary *s) and any indexing and what not is performed */ +/* must have filter_lock before calling this function */ static CamelMessageContentInfo * summary_build_content_info(CamelFolderSummary *s, CamelMessageInfo *msginfo, CamelMimeParser *mp) { @@ -1892,6 +2052,7 @@ summary_build_content_info(CamelFolderSummary *s, CamelMessageInfo *msginfo, Cam } /* build the content-info, from a message */ +/* this needs no lock, as we copy all data, and ibex is threadsafe */ static CamelMessageContentInfo * summary_build_content_info_message(CamelFolderSummary *s, CamelMessageInfo *msginfo, CamelMimePart *object) { @@ -2212,14 +2373,29 @@ camel_message_info_new (void) { CamelMessageInfo *info; - info = g_new0 (CamelMessageInfo, 1); + info = g_malloc0(sizeof(*info)); #ifdef DOESTRV info->strings = e_strv_new (CAMEL_MESSAGE_INFO_LAST); #endif - + info->refcount = 1; + return info; } +/** + * camel_message_info_ref: + * @info: + * + * Reference an info. + * + * NOTE: This interface is not MT-SAFE, like the others. + **/ +void camel_message_info_ref(CamelMessageInfo *info) +{ + GLOBAL_INFO_LOCK(info); + info->refcount++; + GLOBAL_INFO_UNLOCK(info); +} /** * camel_message_info_new_from_header: @@ -2238,20 +2414,13 @@ camel_message_info_new_from_header (struct _header_raw *header) to = camel_folder_summary_format_address (header, "to"); cc = camel_folder_summary_format_address (header, "cc"); - info = g_new0 (CamelMessageInfo, 1); -#ifdef DOESTRV - info->strings = e_strv_new (CAMEL_MESSAGE_INFO_LAST); + info = camel_message_info_new(); + camel_message_info_set_subject (info, subject); camel_message_info_set_from (info, from); camel_message_info_set_to (info, to); camel_message_info_set_cc (info, cc); -#else - info->subject = subject; - info->from = from; - info->to = to; - info->cc = cc; -#endif - + return info; } @@ -2276,6 +2445,7 @@ camel_message_info_dup_to(const CamelMessageInfo *from, CamelMessageInfo *to) to->size = from->size; to->date_sent = from->date_sent; to->date_received = from->date_received; + to->refcount = 1; /* Copy strings */ #ifdef DOESTRV @@ -2325,15 +2495,27 @@ camel_message_info_dup_to(const CamelMessageInfo *from, CamelMessageInfo *to) * camel_message_info_free: * @mi: the message info * - * Frees a CamelMessageInfo and its contents. + * Unref's and potentially frees a CamelMessageInfo and its contents. * * Can only be used to free CamelMessageInfo's created with * camel_message_info_dup_to. * + * NOTE: This interface is not MT-SAFE, like the others. + * **/ void camel_message_info_free(CamelMessageInfo *mi) { + g_return_if_fail(mi != NULL); + + GLOBAL_INFO_LOCK(info); + mi->refcount--; + if (mi->refcount > 0) { + GLOBAL_INFO_UNLOCK(info); + return; + } + GLOBAL_INFO_UNLOCK(info); + #ifdef DOESTRV e_strv_destroy(mi->strings); #else @@ -2468,7 +2650,9 @@ int main(int argc, char **argv) printf("Printing summary\n"); for (i=0;i<camel_folder_summary_count(s);i++) { - message_info_dump(camel_folder_summary_index(s, i)); + CamelMessageInfo *info = camel_folder_summary_index(s, i); + message_info_dump(info); + camel_folder_summary_info_free(info); } printf("Saivng summary\n"); @@ -2486,7 +2670,9 @@ int main(int argc, char **argv) printf("Printing summary\n"); for (i=0;i<camel_folder_summary_count(n);i++) { - message_info_dump(camel_folder_summary_index(n, i)); + CamelMessageInfo *info = camel_folder_summary_index(s, i); + message_info_dump(info); + camel_folder_summary_info_free(info); } camel_object_unref(n); } diff --git a/camel/camel-folder-summary.h b/camel/camel-folder-summary.h index b948ef4412..989268c187 100644 --- a/camel/camel-folder-summary.h +++ b/camel/camel-folder-summary.h @@ -121,6 +121,7 @@ typedef struct { #endif guint32 flags; guint32 size; + guint32 refcount; time_t date_sent; time_t date_received; @@ -228,6 +229,7 @@ CamelMessageInfo *camel_folder_summary_info_new_from_header(CamelFolderSummary * CamelMessageInfo *camel_folder_summary_info_new_from_parser(CamelFolderSummary *, CamelMimeParser *); CamelMessageInfo *camel_folder_summary_info_new_from_message(CamelFolderSummary *, CamelMimeMessage *); +void camel_folder_summary_info_ref(CamelFolderSummary *, CamelMessageInfo *); void camel_folder_summary_info_free(CamelFolderSummary *, CamelMessageInfo *); /* removes a summary item, doesn't fix content offsets */ @@ -280,8 +282,9 @@ void camel_tag_list_free(CamelTag **list); /* message info utils for working with pseudo-messageinfo structures NOTE: These cannot be added to a real summary object, but suffice for all other external interfaces that use message info's */ -CamelMessageInfo *camel_message_info_new (void); -CamelMessageInfo *camel_message_info_new_from_header (struct _header_raw *header); +CamelMessageInfo *camel_message_info_new(void); +void camel_message_info_ref(CamelMessageInfo *info); +CamelMessageInfo *camel_message_info_new_from_header(struct _header_raw *header); void camel_message_info_dup_to(const CamelMessageInfo *from, CamelMessageInfo *to); void camel_message_info_free(CamelMessageInfo *mi); diff --git a/camel/camel-folder-thread.c b/camel/camel-folder-thread.c index 4b6a72053e..c83c33687d 100644 --- a/camel/camel-folder-thread.c +++ b/camel/camel-folder-thread.c @@ -433,7 +433,8 @@ camel_folder_thread_messages_new(CamelFolder *folder, GPtrArray *uids) int i; CamelFolderThreadNode *c, *child, *head; CamelFolderThread *thread; - GPtrArray *myuids; + GHashTable *wanted = NULL; + GPtrArray *summary; #ifdef TIMEIT struct timeval start, end; @@ -445,22 +446,26 @@ camel_folder_thread_messages_new(CamelFolder *folder, GPtrArray *uids) thread = g_malloc(sizeof(*thread)); thread->tree = NULL; thread->node_chunks = e_memchunk_new(32, sizeof(CamelFolderThreadNode)); + thread->folder = folder; + camel_object_ref((CamelObject *)folder); + + /* wanted is the list of what we want, we put it in a hash for quick lookup */ + if (uids) { + wanted = g_hash_table_new(g_str_hash, g_str_equal); + for (i=0;i<uids->len;i++) + g_hash_table_insert(wanted, uids->pdata[i], uids->pdata[i]); + } - if (uids == NULL) - uids = myuids = camel_folder_get_uids(folder); - else - myuids = NULL; - + thread->summary = summary = camel_folder_get_summary(folder); + id_table = g_hash_table_new((GHashFunc)id_hash, (GCompareFunc)id_equal); no_id_table = g_hash_table_new(NULL, NULL); - for (i=0;i<uids->len;i++) { - const CamelMessageInfo *mi; - mi = camel_folder_get_message_info(folder, uids->pdata[i]); + for (i=0;i<summary->len;i++) { + CamelMessageInfo *mi = summary->pdata[i]; + const char *uid = camel_message_info_uid(mi); - if (mi == NULL) { - g_warning("Folder doesn't contain uid %s", (char *)uids->pdata[i]); + if (wanted && g_hash_table_lookup(wanted, uid) == 0) continue; - } if (mi->message_id.id.id) { c = g_hash_table_lookup(id_table, &mi->message_id); @@ -540,9 +545,6 @@ camel_folder_thread_messages_new(CamelFolder *folder, GPtrArray *uids) uids->len, diff / 1000, diff % 1000); #endif - if (myuids) - camel_folder_free_uids(folder, myuids); - return thread; } @@ -555,6 +557,8 @@ camel_folder_thread_messages_new(CamelFolder *folder, GPtrArray *uids) void camel_folder_thread_messages_destroy(CamelFolderThread *thread) { + camel_folder_free_summary(thread->folder, thread->summary); + camel_object_unref((CamelObject *)thread->folder); e_memchunk_destroy(thread->node_chunks); g_free(thread); } diff --git a/camel/camel-folder-thread.h b/camel/camel-folder-thread.h index 7e710544c3..843ade0b56 100644 --- a/camel/camel-folder-thread.h +++ b/camel/camel-folder-thread.h @@ -38,6 +38,8 @@ typedef struct _CamelFolderThreadNode { typedef struct CamelFolderThread { struct _CamelFolderThreadNode *tree; struct _EMemChunk *node_chunks; + CamelFolder *folder; + GPtrArray *summary; } CamelFolderThread; CamelFolderThread *camel_folder_thread_messages_new(CamelFolder *folder, GPtrArray *uids); diff --git a/camel/camel-folder.c b/camel/camel-folder.c index 126b3e56c0..5be28274c6 100644 --- a/camel/camel-folder.c +++ b/camel/camel-folder.c @@ -32,6 +32,8 @@ #include "string-utils.h" #include "e-util/e-memory.h" +#include "camel-private.h" + static CamelObjectClass *parent_class = NULL; /* Returns the class for a CamelFolder */ @@ -80,8 +82,8 @@ static CamelMimeMessage *get_message (CamelFolder *folder, const gchar *uid, CamelException *ex); -static const CamelMessageInfo *get_message_info (CamelFolder *folder, - const char *uid); +static CamelMessageInfo *get_message_info (CamelFolder *folder, const char *uid); +static void free_message_info (CamelFolder *folder, CamelMessageInfo *info); static GPtrArray *search_by_expression (CamelFolder *folder, const char *exp, @@ -140,6 +142,7 @@ camel_folder_class_init (CamelFolderClass *camel_folder_class) camel_folder_class->search_by_expression = search_by_expression; camel_folder_class->search_free = search_free; camel_folder_class->get_message_info = get_message_info; + camel_folder_class->free_message_info = free_message_info; camel_folder_class->copy_message_to = copy_message_to; camel_folder_class->move_message_to = move_message_to; camel_folder_class->freeze = freeze; @@ -157,8 +160,13 @@ camel_folder_init (gpointer object, gpointer klass) { CamelFolder *folder = object; - folder->frozen = 0; - folder->changed_frozen = camel_folder_change_info_new(); + folder->priv = g_malloc0(sizeof(*folder->priv)); + folder->priv->frozen = 0; + folder->priv->changed_frozen = camel_folder_change_info_new(); +#ifdef ENABLE_THREADS + folder->priv->lock = g_mutex_new(); + folder->priv->change_lock = g_mutex_new(); +#endif } static void @@ -172,7 +180,15 @@ camel_folder_finalize (CamelObject *object) if (camel_folder->parent_store) camel_object_unref (CAMEL_OBJECT (camel_folder->parent_store)); - camel_folder_change_info_free(camel_folder->changed_frozen); + if (camel_folder->summary) + camel_object_unref((CamelObject *)camel_folder->summary); + + camel_folder_change_info_free(camel_folder->priv->changed_frozen); +#ifdef ENABLE_THREADS + g_mutex_free(camel_folder->priv->lock); + g_mutex_free(camel_folder->priv->change_lock); +#endif + g_free(camel_folder->priv); } CamelType @@ -241,7 +257,11 @@ camel_folder_sync (CamelFolder *folder, gboolean expunge, CamelException *ex) { g_return_if_fail (CAMEL_IS_FOLDER (folder)); + CAMEL_FOLDER_LOCK(folder, lock); + CF_CLASS (folder)->sync (folder, expunge, ex); + + CAMEL_FOLDER_UNLOCK(folder, lock); } @@ -263,7 +283,11 @@ camel_folder_refresh_info (CamelFolder *folder, CamelException *ex) { g_return_if_fail (CAMEL_IS_FOLDER (folder)); + CAMEL_FOLDER_LOCK(folder, lock); + CF_CLASS (folder)->refresh_info (folder, ex); + + CAMEL_FOLDER_UNLOCK(folder, lock); } @@ -355,16 +379,19 @@ camel_folder_expunge (CamelFolder *folder, CamelException *ex) { g_return_if_fail (CAMEL_IS_FOLDER (folder)); + CAMEL_FOLDER_LOCK(folder, lock); + CF_CLASS (folder)->expunge (folder, ex); -} + CAMEL_FOLDER_UNLOCK(folder, lock); +} static int get_message_count (CamelFolder *folder) { - g_warning ("CamelFolder::get_message_count not implemented " - "for `%s'", camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))); - return -1; + g_return_val_if_fail(folder->summary != NULL, -1); + + return camel_folder_summary_count(folder->summary); } /** @@ -376,18 +403,33 @@ get_message_count (CamelFolder *folder) int camel_folder_get_message_count (CamelFolder *folder) { + int ret; + g_return_val_if_fail (CAMEL_IS_FOLDER (folder), -1); - return CF_CLASS (folder)->get_message_count (folder); -} + ret = CF_CLASS (folder)->get_message_count (folder); + return ret; +} static int -get_unread_message_count (CamelFolder *folder) +get_unread_message_count(CamelFolder *folder) { - g_warning ("CamelFolder::get_unread_message_count not implemented " - "for `%s'", camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))); - return -1; + int i, count, unread=0; + + g_return_val_if_fail(folder->summary != NULL, -1); + + count = camel_folder_summary_count(folder->summary); + for (i=0; i<count; i++) { + CamelMessageInfo *info = camel_folder_summary_index(folder->summary, i); + + if (info && !(info->flags & CAMEL_MESSAGE_SEEN)) + unread++; + + camel_folder_summary_info_free(folder->summary, info); + } + + return unread; } /** @@ -399,12 +441,15 @@ get_unread_message_count (CamelFolder *folder) int camel_folder_get_unread_message_count (CamelFolder *folder) { + int ret; + g_return_val_if_fail (CAMEL_IS_FOLDER (folder), -1); + + ret = CF_CLASS (folder)->get_unread_message_count (folder); - return CF_CLASS (folder)->get_unread_message_count (folder); + return ret; } - static void append_message (CamelFolder *folder, CamelMimeMessage *message, const CamelMessageInfo *info, CamelException *ex) @@ -432,7 +477,11 @@ camel_folder_append_message (CamelFolder *folder, CamelMimeMessage *message, { g_return_if_fail (CAMEL_IS_FOLDER (folder)); + CAMEL_FOLDER_LOCK(folder, lock); + CF_CLASS (folder)->append_message (folder, message, info, ex); + + CAMEL_FOLDER_UNLOCK(folder, lock); } @@ -458,13 +507,21 @@ camel_folder_get_permanent_flags (CamelFolder *folder) return CF_CLASS (folder)->get_permanent_flags (folder); } - static guint32 -get_message_flags (CamelFolder *folder, const char *uid) +get_message_flags(CamelFolder *folder, const char *uid) { - g_warning ("CamelFolder::get_message_flags not implemented for `%s'", - camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))); - return 0; + CamelMessageInfo *info; + guint32 flags; + + g_return_val_if_fail(folder->summary != NULL, 0); + + info = camel_folder_summary_uid(folder->summary, uid); + g_return_val_if_fail(info != NULL, 0); + + flags = info->flags; + camel_folder_summary_info_free(folder->summary, info); + + return flags; } /** @@ -478,18 +535,37 @@ get_message_flags (CamelFolder *folder, const char *uid) guint32 camel_folder_get_message_flags (CamelFolder *folder, const char *uid) { + guint32 ret; + g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0); - return CF_CLASS (folder)->get_message_flags (folder, uid); -} + ret = CF_CLASS (folder)->get_message_flags (folder, uid); + return ret; +} static void -set_message_flags (CamelFolder *folder, const char *uid, - guint32 flags, guint32 set) +set_message_flags(CamelFolder *folder, const char *uid, guint32 flags, guint32 set) { - g_warning ("CamelFolder::set_message_flags not implemented for `%s'", - camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))); + CamelMessageInfo *info; + guint32 new; + + g_return_if_fail(folder->summary != NULL); + + info = camel_folder_summary_uid(folder->summary, uid); + g_return_if_fail(info != NULL); + + new = (info->flags & ~flags) | (set & flags); + if (new == info->flags) { + camel_folder_summary_info_free(folder->summary, info); + return; + } + + info->flags = new | CAMEL_MESSAGE_FOLDER_FLAGGED; + camel_folder_summary_touch(folder->summary); + camel_folder_summary_info_free(folder->summary, info); + + camel_object_trigger_event(CAMEL_OBJECT(folder), "message_changed", (char *) uid); } /** @@ -514,12 +590,20 @@ camel_folder_set_message_flags (CamelFolder *folder, const char *uid, static gboolean -get_message_user_flag (CamelFolder *folder, const char *uid, - const char *name) +get_message_user_flag(CamelFolder *folder, const char *uid, const char *name) { - g_warning ("CamelFolder::get_message_user_flag not implemented " - "for `%s'", camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))); - return FALSE; + CamelMessageInfo *info; + gboolean ret; + + g_return_val_if_fail(folder->summary != NULL, FALSE); + + info = camel_folder_summary_uid(folder->summary, uid); + g_return_val_if_fail(info != NULL, FALSE); + + ret = camel_flag_get(&info->user_flags, name); + camel_folder_summary_info_free(folder->summary, info); + + return ret; } /** @@ -534,18 +618,31 @@ gboolean camel_folder_get_message_user_flag (CamelFolder *folder, const char *uid, const char *name) { + gboolean ret; + g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0); - return CF_CLASS (folder)->get_message_user_flag (folder, uid, name); -} + ret = CF_CLASS (folder)->get_message_user_flag (folder, uid, name); + return ret; +} static void -set_message_user_flag (CamelFolder *folder, const char *uid, - const char *name, gboolean value) +set_message_user_flag(CamelFolder *folder, const char *uid, const char *name, gboolean value) { - g_warning ("CamelFolder::set_message_user_flag not implemented " - "for `%s'", camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))); + CamelMessageInfo *info; + + g_return_if_fail(folder->summary != NULL); + + info = camel_folder_summary_uid(folder->summary, uid); + g_return_if_fail(info != NULL); + + if (camel_flag_set(&info->user_flags, name, value)) { + info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED; + camel_folder_summary_touch(folder->summary); + camel_object_trigger_event(CAMEL_OBJECT(folder), "message_changed", (char *) uid); + } + camel_folder_summary_info_free(folder->summary, info); } /** @@ -568,11 +665,23 @@ camel_folder_set_message_user_flag (CamelFolder *folder, const char *uid, CF_CLASS (folder)->set_message_user_flag (folder, uid, name, value); } -static const char *get_message_user_tag(CamelFolder *folder, const char *uid, const char *name) +static const char * +get_message_user_tag(CamelFolder *folder, const char *uid, const char *name) { - g_warning ("CamelFolder::get_message_user_tag not implemented " - "for `%s'", camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))); - return NULL; + CamelMessageInfo *info; + const char *ret; + + g_return_val_if_fail(folder->summary != NULL, NULL); + + info = camel_folder_summary_uid(folder->summary, uid); + g_return_val_if_fail(info != NULL, FALSE); + +#warning "Need to duplicate tag string" + + ret = camel_tag_get(&info->user_tags, name); + camel_folder_summary_info_free(folder->summary, info); + + return ret; } /** @@ -586,17 +695,32 @@ static const char *get_message_user_tag(CamelFolder *folder, const char *uid, co const char * camel_folder_get_message_user_tag (CamelFolder *folder, const char *uid, const char *name) { + const char *ret; + g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0); - return CF_CLASS (folder)->get_message_user_tag (folder, uid, name); -} +#warning "get_message_user_tag() needs to copy the tag contents" + ret = CF_CLASS (folder)->get_message_user_tag (folder, uid, name); + return ret; +} static void set_message_user_tag(CamelFolder *folder, const char *uid, const char *name, const char *value) { - g_warning ("CamelFolder::set_message_user_tag not implemented " - "for `%s'", camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))); + CamelMessageInfo *info; + + g_return_if_fail(folder->summary != NULL); + + info = camel_folder_summary_uid(folder->summary, uid); + g_return_if_fail(info != NULL); + + if (camel_tag_set(&info->user_tags, name, value)) { + info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED; + camel_folder_summary_touch(folder->summary); + camel_object_trigger_event(CAMEL_OBJECT(folder), "message_changed", (char *) uid); + } + camel_folder_summary_info_free(folder->summary, info); } /** @@ -618,13 +742,12 @@ camel_folder_set_message_user_tag (CamelFolder *folder, const char *uid, const c CF_CLASS (folder)->set_message_user_tag (folder, uid, name, value); } - -static const CamelMessageInfo * +static CamelMessageInfo * get_message_info (CamelFolder *folder, const char *uid) { - g_warning ("CamelFolder::get_message_info not implemented for `%s'", - camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))); - return NULL; + g_return_val_if_fail(folder->summary != NULL, NULL); + + return camel_folder_summary_uid(folder->summary, uid); } /** @@ -632,17 +755,48 @@ get_message_info (CamelFolder *folder, const char *uid) * @folder: a CamelFolder * @uid: the uid of a message * - * Return value: the summary information for the indicated message + * Retrieve the CamelMessageInfo for the specified @uid. This return + * must be freed using free_message_info(). + * + * Return value: the summary information for the indicated message, or NULL + * if the uid does not exist. **/ -const CamelMessageInfo * +CamelMessageInfo * camel_folder_get_message_info (CamelFolder *folder, const char *uid) { + CamelMessageInfo *ret; + g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL); g_return_val_if_fail (uid != NULL, NULL); - return CF_CLASS (folder)->get_message_info (folder, uid); + ret = CF_CLASS (folder)->get_message_info (folder, uid); + + return ret; } +static void +free_message_info (CamelFolder *folder, CamelMessageInfo *info) +{ + g_return_if_fail(folder->summary != NULL); + + camel_folder_summary_info_free(folder->summary, info); +} + +/** + * camel_folder_free_message_info: + * @folder: + * @info: + * + * Free (unref) a CamelMessageInfo, previously obtained with get_message_info(). + **/ +void +camel_folder_free_message_info(CamelFolder *folder, CamelMessageInfo *info) +{ + g_return_if_fail(CAMEL_IS_FOLDER (folder)); + g_return_if_fail(info != NULL); + + CF_CLASS (folder)->free_message_info(folder, info); +} /* TODO: is this function required anyway? */ gboolean @@ -677,21 +831,45 @@ get_message (CamelFolder *folder, const gchar *uid, CamelException *ex) * Return value: Message corresponding to the UID **/ CamelMimeMessage * -camel_folder_get_message (CamelFolder *folder, const gchar *uid, - CamelException *ex) +camel_folder_get_message (CamelFolder *folder, const gchar *uid, CamelException *ex) { + CamelMimeMessage *ret; + g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL); - return CF_CLASS (folder)->get_message (folder, uid, ex); -} + CAMEL_FOLDER_LOCK(folder, lock); + + ret = CF_CLASS (folder)->get_message (folder, uid, ex); + CAMEL_FOLDER_UNLOCK(folder, lock); + + return ret; +} static GPtrArray * -get_uids (CamelFolder *folder) +get_uids(CamelFolder *folder) { - g_warning ("CamelFolder::get_uids not implemented for `%s'", - camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))); - return NULL; + GPtrArray *array; + int i, count; + + array = g_ptr_array_new(); + + g_return_val_if_fail(folder->summary != NULL, array); + + count = camel_folder_summary_count(folder->summary); + g_ptr_array_set_size(array, count); + for (i=0; i<count; i++) { + CamelMessageInfo *info = camel_folder_summary_index(folder->summary, i); + + if (info) { + array->pdata[i] = g_strdup(camel_message_info_uid(info)); + camel_folder_summary_info_free(folder->summary, info); + } else { + array->pdata[i] = g_strdup("xx unknown uid xx"); + } + } + + return array; } /** @@ -709,17 +887,23 @@ get_uids (CamelFolder *folder) GPtrArray * camel_folder_get_uids (CamelFolder *folder) { + GPtrArray *ret; + g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL); - return CF_CLASS (folder)->get_uids (folder); -} + ret = CF_CLASS (folder)->get_uids (folder); + return ret; +} static void free_uids (CamelFolder *folder, GPtrArray *array) { - g_warning ("CamelFolder::free_uids not implemented for `%s'", - camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))); + int i; + + for (i=0; i<array->len; i++) + g_free(array->pdata[i]); + g_ptr_array_free(array, TRUE); } /** @@ -737,13 +921,20 @@ camel_folder_free_uids (CamelFolder *folder, GPtrArray *array) CF_CLASS (folder)->free_uids (folder, array); } - static GPtrArray * -get_summary (CamelFolder *folder) +get_summary(CamelFolder *folder) { - g_warning ("CamelFolder::get_summary not implemented for `%s'", - camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))); - return NULL; + GPtrArray *res = g_ptr_array_new(); + int i, count; + + g_return_val_if_fail(folder->summary != NULL, res); + + count = camel_folder_summary_count(folder->summary); + g_ptr_array_set_size(res, count); + for (i=0;i<count;i++) + res->pdata[i] = camel_folder_summary_index(folder->summary, i); + + return res; } /** @@ -759,17 +950,26 @@ get_summary (CamelFolder *folder) GPtrArray * camel_folder_get_summary (CamelFolder *folder) { + GPtrArray *ret; + g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL); - return CF_CLASS (folder)->get_summary (folder); -} + ret = CF_CLASS (folder)->get_summary (folder); + return ret; +} static void -free_summary (CamelFolder *folder, GPtrArray *array) +free_summary(CamelFolder *folder, GPtrArray *summary) { - g_warning ("CamelFolder::free_summary not implemented for `%s'", - camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))); + int i; + + g_return_if_fail(folder->summary != NULL); + + for (i=0;i<summary->len;i++) + camel_folder_summary_info_free(folder->summary, summary->pdata[i]); + + g_ptr_array_free(summary, TRUE); } /** @@ -826,10 +1026,16 @@ GPtrArray * camel_folder_search_by_expression (CamelFolder *folder, const char *expression, CamelException *ex) { + GPtrArray *ret; + g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL); g_return_val_if_fail (folder->has_search_capability, NULL); - return CF_CLASS (folder)->search_by_expression (folder, expression, ex); + /* NOTE: that it is upto the callee to lock */ + + ret = CF_CLASS (folder)->search_by_expression (folder, expression, ex); + + return ret; } static void @@ -855,25 +1061,28 @@ camel_folder_search_free (CamelFolder *folder, GPtrArray *result) g_return_if_fail (CAMEL_IS_FOLDER (folder)); g_return_if_fail (folder->has_search_capability); - return CF_CLASS (folder)->search_free (folder, result); + /* NOTE: upto the callee to lock */ + CF_CLASS (folder)->search_free (folder, result); } static void -copy_message_to (CamelFolder *source, const char *uid, CamelFolder *dest, - CamelException *ex) +copy_message_to (CamelFolder *source, const char *uid, CamelFolder *dest, CamelException *ex) { CamelMimeMessage *msg; - const CamelMessageInfo *info; + CamelMessageInfo *info; /* Default implementation. */ - msg = camel_folder_get_message (source, uid, ex); + /* we alredy have the lock, dont deadlock */ + msg = CF_CLASS(source)->get_message(source, uid, ex); if (!msg) return; - info = camel_folder_get_message_info (source, uid); + info = CF_CLASS(source)->get_message_info (source, uid); camel_folder_append_message (dest, msg, info, ex); camel_object_unref (CAMEL_OBJECT (msg)); + if (info) + CF_CLASS(source)->free_message_info(source, info); } /** @@ -900,11 +1109,14 @@ camel_folder_copy_message_to (CamelFolder *source, const char *uid, g_warning("CamelFolder.copy_message_to() is a deprecated api"); - if (source->parent_store == dest->parent_store) { - return CF_CLASS (source)->copy_message_to (source, uid, - dest, ex); - } else - return copy_message_to (source, uid, dest, ex); + CAMEL_FOLDER_LOCK(source, lock); + + if (source->parent_store == dest->parent_store) + return CF_CLASS (source)->copy_message_to (source, uid, dest, ex); + else + copy_message_to (source, uid, dest, ex); + + CAMEL_FOLDER_UNLOCK(source, lock); } @@ -913,19 +1125,21 @@ move_message_to (CamelFolder *source, const char *uid, CamelFolder *dest, CamelException *ex) { CamelMimeMessage *msg; - const CamelMessageInfo *info; + CamelMessageInfo *info; /* Default implementation. */ - msg = camel_folder_get_message (source, uid, ex); + msg = CF_CLASS(source)->get_message (source, uid, ex); if (!msg) return; - info = camel_folder_get_message_info (source, uid); + info = CF_CLASS(source)->get_message_info (source, uid); camel_folder_append_message (dest, msg, info, ex); camel_object_unref (CAMEL_OBJECT (msg)); - if (camel_exception_is_set(ex)) - return; - camel_folder_delete_message (source, uid); + if (!camel_exception_is_set(ex)) + CF_CLASS(source)->set_message_flags(source, uid, CAMEL_MESSAGE_DELETED, CAMEL_MESSAGE_DELETED); + + if (info) + CF_CLASS(source)->free_message_info(source, info); } /** @@ -939,9 +1153,6 @@ move_message_to (CamelFolder *source, const char *uid, * @dest folders have the same parent_store, this may be more efficient * than a camel_folder_append_message() followed by * camel_folder_delete_message(). - * - * FIXME: This call should be depracated, since append_message() can - * determine this from the message itself. **/ void camel_folder_move_message_to (CamelFolder *source, const char *uid, @@ -953,17 +1164,24 @@ camel_folder_move_message_to (CamelFolder *source, const char *uid, g_warning("CamelFolder.move_message_to() is a deprecated api"); - if (source->parent_store == dest->parent_store) { - return CF_CLASS (source)->move_message_to (source, uid, - dest, ex); - } else - return move_message_to (source, uid, dest, ex); + CAMEL_FOLDER_LOCK(source, lock); + + if (source->parent_store == dest->parent_store) + CF_CLASS (source)->move_message_to (source, uid, dest, ex); + else + move_message_to (source, uid, dest, ex); + + CAMEL_FOLDER_UNLOCK(source, lock); } static void freeze (CamelFolder *folder) { - folder->frozen++; + CAMEL_FOLDER_LOCK(folder, change_lock); + + folder->priv->frozen++; + + CAMEL_FOLDER_UNLOCK(folder, change_lock); } /** @@ -989,23 +1207,25 @@ thaw (CamelFolder * folder) int i; CamelFolderChangeInfo *info; - folder->frozen--; - if (folder->frozen != 0) - return; - - /* If we have more or less messages, do a folder changed, otherwise just - do a message changed for each one. - TODO: message_changed is now probably irrelevant and not required */ - info = folder->changed_frozen; - if (info->uid_added->len > 0 || info->uid_removed->len > 0) { - camel_object_trigger_event(CAMEL_OBJECT(folder), "folder_changed", info); - } else if (info->uid_changed->len > 0) { - for (i=0;i<info->uid_changed->len;i++) { - camel_object_trigger_event(CAMEL_OBJECT(folder), "message_changed", info->uid_changed->pdata[i]); + CAMEL_FOLDER_LOCK(folder, change_lock); + + folder->priv->frozen--; + if (folder->priv->frozen == 0) { + /* If we have more or less messages, do a folder changed, otherwise just + do a message changed for each one. + TODO: message_changed is now probably irrelevant and not required */ + info = folder->priv->changed_frozen; + if (info->uid_added->len > 0 || info->uid_removed->len > 0) { + camel_object_trigger_event(CAMEL_OBJECT(folder), "folder_changed", info); + } else if (info->uid_changed->len > 0) { + for (i=0;i<info->uid_changed->len;i++) { + camel_object_trigger_event(CAMEL_OBJECT(folder), "message_changed", info->uid_changed->pdata[i]); + } } + camel_folder_change_info_clear(info); } - camel_folder_change_info_clear(info); + CAMEL_FOLDER_UNLOCK(folder, change_lock); } /** @@ -1019,7 +1239,7 @@ void camel_folder_thaw (CamelFolder *folder) { g_return_if_fail (CAMEL_IS_FOLDER (folder)); - g_return_if_fail (folder->frozen != 0); + g_return_if_fail (folder->priv->frozen != 0); CF_CLASS (folder)->thaw (folder); } @@ -1031,29 +1251,40 @@ folder_changed (CamelObject *obj, gpointer event_data) { CamelFolder *folder = CAMEL_FOLDER (obj); CamelFolderChangeInfo *changed = event_data; + gboolean ret = TRUE; + + if (folder->priv->frozen) { + CAMEL_FOLDER_LOCK(folder, change_lock); - if (folder->frozen) { if (changed != NULL) - camel_folder_change_info_cat(folder->changed_frozen, changed); + camel_folder_change_info_cat(folder->priv->changed_frozen, changed); else g_warning("Class %s is passing NULL to folder_changed event", camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder))); - return FALSE; + ret = FALSE; + + CAMEL_FOLDER_UNLOCK(folder, change_lock); } - return TRUE; + + return ret; } static gboolean message_changed (CamelObject *obj, /*const char *uid*/gpointer event_data) { CamelFolder *folder = CAMEL_FOLDER (obj); + gboolean ret = TRUE; + + if (folder->priv->frozen) { + CAMEL_FOLDER_LOCK(folder, change_lock); + + camel_folder_change_info_change_uid(folder->priv->changed_frozen, (char *)event_data); + ret = FALSE; - if (folder->frozen) { - camel_folder_change_info_change_uid(folder->changed_frozen, (char *)event_data); - return FALSE; + CAMEL_FOLDER_UNLOCK(folder, change_lock); } - return TRUE; + return ret; } @@ -1112,6 +1343,9 @@ camel_folder_free_deep (CamelFolder *folder, GPtrArray *array) * @void: * * Create a new folder change info structure. + * + * Change info structures are not MT-SAFE and must be + * locked for exclusive access externally. * * Return value: **/ diff --git a/camel/camel-folder.h b/camel/camel-folder.h index 4060d36731..615ecb53a0 100644 --- a/camel/camel-folder.h +++ b/camel/camel-folder.h @@ -1,9 +1,9 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* camel-folder.h: Abstract class for an email folder */ - -/* - * Author: - * Bertrand Guiheneuf <bertrand@helixcode.com> +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * camel-folder.h: Abstract class for an email folder + * + * Authors: Bertrand Guiheneuf <bertrand@helixcode.com> + * Michael Zucchi <notzed@helixcode.com> * * Copyright 1999, 2000 Helix Code, Inc. (http://www.helixcode.com) * @@ -23,7 +23,6 @@ * USA */ - #ifndef CAMEL_FOLDER_H #define CAMEL_FOLDER_H 1 @@ -50,22 +49,24 @@ struct _CamelFolderChangeInfo { GHashTable *uid_source; /* used to create unique lists */ struct _EMemPool *uid_pool; /* pool used to store copies of uid strings */ + struct _CamelFolderChangeInfoPrivate *priv; }; struct _CamelFolder { CamelObject parent_object; - int frozen; - CamelFolderChangeInfo *changed_frozen; /* queues changed events */ + struct _CamelFolderPrivate *priv; char *name; char *full_name; CamelStore *parent_store; + CamelFolderSummary *summary; guint32 permanent_flags; gboolean has_summary_capability:1; gboolean has_search_capability:1; + }; typedef struct { @@ -137,7 +138,8 @@ typedef struct { void (*search_free) (CamelFolder *folder, GPtrArray *result); - const CamelMessageInfo * (*get_message_info) (CamelFolder *, const char *uid); + CamelMessageInfo * (*get_message_info) (CamelFolder *, const char *uid); + void (*free_message_info) (CamelFolder *, CamelMessageInfo *); void (*copy_message_to) (CamelFolder *source, const char *uid, @@ -250,9 +252,10 @@ GPtrArray * camel_folder_search_by_expression (CamelFolder *folder, void camel_folder_search_free (CamelFolder *folder, GPtrArray *); /* summary info */ -const CamelMessageInfo *camel_folder_get_message_info (CamelFolder *summary, - const char *uid); +CamelMessageInfo *camel_folder_get_message_info (CamelFolder *folder, const char *uid); +void camel_folder_free_message_info (CamelFolder *folder, CamelMessageInfo *info); +/* FIXME: copy-message-to is not required */ void camel_folder_copy_message_to (CamelFolder *source, const char *uid, CamelFolder *dest, @@ -263,9 +266,15 @@ void camel_folder_move_message_to (CamelFolder *source, CamelFolder *dest, CamelException *ex); +/* stop/restart getting events */ void camel_folder_freeze (CamelFolder *folder); void camel_folder_thaw (CamelFolder *folder); +#if 0 +/* lock/unlock at the thread level, NOTE: only used internally */ +void camel_folder_lock (CamelFolder *folder); +void camel_folder_unlock (CamelFolder *folder); +#endif /* For use by subclasses (for free_{uids,summary,subfolder_names}) */ void camel_folder_free_nop (CamelFolder *folder, GPtrArray *array); diff --git a/camel/camel-private.h b/camel/camel-private.h new file mode 100644 index 0000000000..cdfe878edd --- /dev/null +++ b/camel/camel-private.h @@ -0,0 +1,153 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * camel-private.h: Private info for class implementers. + * + * Authors: Michael Zucchi <notzed@helixcode.com> + * + * Copyright 1999, 2000 Helix Code, Inc. (http://www.helixcode.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#ifndef CAMEL_PRIVATE_H +#define CAMEL_PRIVATE_H 1 + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus }*/ + +/* need a way to configure and save this data, if this header is to + be installed. For now, dont install it */ + +#include "config.h" + +#ifdef ENABLE_THREADS +#include <pthread.h> +#include "e-util/e-msgport.h" +#endif + +struct _CamelFolderPrivate { +#ifdef ENABLE_THREADS + GMutex *lock; + GMutex *change_lock; +#endif + + /* must require the 'change_lock' to access this */ + int frozen; + struct _CamelFolderChangeInfo *changed_frozen; /* queues changed events */ +}; + +#ifdef ENABLE_THREADS +#define CAMEL_FOLDER_LOCK(f, l) (g_mutex_lock(((CamelFolder *)f)->priv->l)) +#define CAMEL_FOLDER_UNLOCK(f, l) (g_mutex_unlock(((CamelFolder *)f)->priv->l)) +#else +#define CAMEL_FOLDER_LOCK(f, l) +#define CAMEL_FOLDER_UNLOCK(f, l) +#endif + +struct _CamelStorePrivate { +#ifdef ENABLE_THREADS + GMutex *folder_lock; /* for locking folder operations */ + GMutex *cache_lock; /* for locking access to the cache */ +#endif +}; + +#ifdef ENABLE_THREADS +#define CAMEL_STORE_LOCK(f, l) (g_mutex_lock(((CamelStore *)f)->priv->l)) +#define CAMEL_STORE_UNLOCK(f, l) (g_mutex_unlock(((CamelStore *)f)->priv->l)) +#else +#define CAMEL_STORE_LOCK(f, l) +#define CAMEL_STORE_UNLOCK(f, l) +#endif + +struct _CamelServicePrivate { +#ifdef ENABLE_THREADS + EMutex *connect_lock; /* for locking connection operations */ +#endif +}; + +#ifdef ENABLE_THREADS +#define CAMEL_SERVICE_LOCK(f, l) (e_mutex_lock(((CamelService *)f)->priv->l)) +#define CAMEL_SERVICE_UNLOCK(f, l) (e_mutex_unlock(((CamelService *)f)->priv->l)) +#else +#define CAMEL_SERVICE_LOCK(f, l) +#define CAMEL_SERVICE_UNLOCK(f, l) +#endif + +struct _CamelSessionPrivate { +#ifdef ENABLE_THREADS + GMutex *lock; /* for locking everything basically */ +#endif +}; + +#ifdef ENABLE_THREADS +#define CAMEL_SESSION_LOCK(f, l) (g_mutex_lock(((CamelSession *)f)->priv->l)) +#define CAMEL_SESSION_UNLOCK(f, l) (g_mutex_unlock(((CamelSession *)f)->priv->l)) +#else +#define CAMEL_SESSION_LOCK(f, l) +#define CAMEL_SESSION_UNLOCK(f, l) +#endif + + +struct _CamelRemoteStorePrivate { +#ifdef ENABLE_THREADS + EMutex *stream_lock; /* for locking stream operations */ +#endif +}; + +#ifdef ENABLE_THREADS +#define CAMEL_REMOTE_STORE_LOCK(f, l) (e_mutex_lock(((CamelRemoteStore *)f)->priv->l)) +#define CAMEL_REMOTE_STORE_UNLOCK(f, l) (e_mutex_unlock(((CamelRemoteStore *)f)->priv->l)) +#else +#define CAMEL_REMOTE_STORE_LOCK(f, l) +#define CAMEL_REMOTE_STORE_UNLOCK(f, l) +#endif + +/* most of this stuff really is private, but the lock can be used by subordinate classes */ +struct _CamelFolderSummaryPrivate { + GHashTable *filter_charset; /* CamelMimeFilterCharset's indexed by source charset */ + + CamelMimeFilterIndex *filter_index; + CamelMimeFilterBasic *filter_64; + CamelMimeFilterBasic *filter_qp; + CamelMimeFilterSave *filter_save; + + struct ibex *index; + +#ifdef ENABLE_THREADS + GMutex *summary_lock; /* for the summary hashtable/array */ + GMutex *io_lock; /* load/save lock, for access to saved_count, etc */ + GMutex *filter_lock; /* for accessing any of the filtering/indexing stuff, since we share them */ + GMutex *alloc_lock; /* for setting up and using allocators */ + GMutex *ref_lock; /* for reffing/unreffing messageinfo's ALWAYS obtain before summary_lock */ +#endif +}; + +#ifdef ENABLE_THREADS +#define CAMEL_SUMMARY_LOCK(f, l) (g_mutex_lock(((CamelFolderSummary *)f)->priv->l)) +#define CAMEL_SUMMARY_UNLOCK(f, l) (g_mutex_unlock(((CamelFolderSummary *)f)->priv->l)) +#else +#define CAMEL_SUMMARY_LOCK(f, l) +#define CAMEL_SUMMARY_UNLOCK(f, l) +#endif + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CAMEL_H */ + diff --git a/camel/camel-remote-store.c b/camel/camel-remote-store.c index 3a64f59ce6..3436e41733 100644 --- a/camel/camel-remote-store.c +++ b/camel/camel-remote-store.c @@ -47,6 +47,8 @@ #include "camel-url.h" #include "string-utils.h" +#include "camel-private.h" + #define d(x) x #if d(!)0 extern gboolean camel_verbose_debug; @@ -112,17 +114,24 @@ camel_remote_store_init (CamelObject *object) remote_store->istream = NULL; remote_store->ostream = NULL; remote_store->timeout_id = 0; + + remote_store->priv = g_malloc0(sizeof(*remote_store->priv)); +#ifdef ENABLE_THREADS + remote_store->priv->stream_lock = e_mutex_new(E_MUTEX_REC); +#endif +} + +static void +camel_remote_store_finalise(CamelObject *object) +{ + CamelRemoteStore *remote_store = CAMEL_REMOTE_STORE (object); + +#ifdef ENABLE_THREADS + e_mutex_destroy(remote_store->priv->stream_lock); +#endif + g_free(remote_store->priv); } -/* - *static void - *camel_remote_store_finalize (CamelObject *object) - *{ - * CamelRemoteStore *remote_store = CAMEL_REMOTE_STORE (object); - * - * g_free (remote_store->nice_name); - *} - */ CamelType camel_remote_store_get_type (void) @@ -137,7 +146,7 @@ camel_remote_store_get_type (void) (CamelObjectClassInitFunc) camel_remote_store_class_init, NULL, (CamelObjectInitFunc) camel_remote_store_init, - (CamelObjectFinalizeFunc) NULL); + (CamelObjectFinalizeFunc) camel_remote_store_finalise); } return camel_remote_store_type; @@ -193,7 +202,14 @@ remote_get_name (CamelService *service, gboolean brief) static gboolean timeout_cb (gpointer data) { - CRSC (data)->keepalive (CAMEL_REMOTE_STORE (data)); + CamelRemoteStore *store = CAMEL_REMOTE_STORE(data); + + CAMEL_REMOTE_STORE_LOCK(store, stream_lock); + + CRSC (data)->keepalive(store); + + CAMEL_REMOTE_STORE_UNLOCK(store, stream_lock); + return TRUE; } @@ -331,6 +347,9 @@ remote_send_string (CamelRemoteStore *store, CamelException *ex, char *fmt, va_l return 0; } +/* FIXME: All of these functions need an api overhaul, they're not like + any other functions, anywhere in the world ... */ + /** * camel_remote_store_send_string: Writes a string to the server * @store: a CamelRemoteStore @@ -353,7 +372,9 @@ camel_remote_store_send_string (CamelRemoteStore *store, CamelException *ex, g_return_val_if_fail (fmt, -1); va_start (ap, fmt); + CAMEL_REMOTE_STORE_LOCK(store, stream_lock); ret = CRSC (store)->send_string (store, ex, fmt, ap); + CAMEL_REMOTE_STORE_UNLOCK(store, stream_lock); va_end (ap); return ret; @@ -362,9 +383,11 @@ camel_remote_store_send_string (CamelRemoteStore *store, CamelException *ex, static gint remote_send_stream (CamelRemoteStore *store, CamelStream *stream, CamelException *ex) { + int ret; + /* Check for connectedness. Failed (or cancelled) operations will * close the connection. */ - + if (store->ostream == NULL) { d(g_message ("remote: (sendstream) disconnected, reconnecting.")); @@ -374,15 +397,15 @@ remote_send_stream (CamelRemoteStore *store, CamelStream *stream, CamelException d(fprintf (stderr, "(sending stream)\n")); - if (camel_stream_write_to_stream (stream, store->ostream) < 0) { + ret = camel_stream_write_to_stream (stream, store->ostream); + if (ret < 0) { camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, g_strerror (errno)); camel_service_disconnect (CAMEL_SERVICE (store), FALSE, NULL); - return -1; } - - return 0; + + return ret; } /** @@ -398,10 +421,18 @@ remote_send_stream (CamelRemoteStore *store, CamelStream *stream, CamelException gint camel_remote_store_send_stream (CamelRemoteStore *store, CamelStream *stream, CamelException *ex) { + int ret; + g_return_val_if_fail (CAMEL_IS_REMOTE_STORE (store), -1); g_return_val_if_fail (CAMEL_IS_STREAM (stream), -1); + + CAMEL_REMOTE_STORE_LOCK(store, stream_lock); - return CRSC (store)->send_stream (store, stream, ex); + ret = CRSC (store)->send_stream (store, stream, ex); + + CAMEL_REMOTE_STORE_UNLOCK(store, stream_lock); + + return ret; } static int @@ -476,7 +507,7 @@ remote_recv_line (CamelRemoteStore *store, char **dest, CamelException *ex) * @dest: a pointer that will be set to the location of a buffer * holding the server's response * @ex: a CamelException - * Return value: 0 on success, -1 on error + * Return value: -1 on error, otherwise the length read. * * Reads a line from the server (terminated by \n or \r\n). **/ @@ -485,10 +516,18 @@ gint camel_remote_store_recv_line (CamelRemoteStore *store, char **dest, CamelException *ex) { + int ret; + g_return_val_if_fail (CAMEL_IS_REMOTE_STORE (store), -1); g_return_val_if_fail (dest, -1); + + CAMEL_REMOTE_STORE_LOCK(store, stream_lock); - return CRSC (store)->recv_line (store, dest, ex); + ret = CRSC (store)->recv_line (store, dest, ex); + + CAMEL_REMOTE_STORE_UNLOCK(store, stream_lock); + + return ret; } static void @@ -507,9 +546,12 @@ refresh_folder_info (gpointer key, gpointer value, gpointer data) * * Refreshes the folders listed in the folders hashtable. **/ - void camel_remote_store_refresh_folders (CamelRemoteStore *store, CamelException *ex) { + CAMEL_STORE_LOCK(store, cache_lock); + g_hash_table_foreach (CAMEL_STORE (store)->folders, refresh_folder_info, ex); + + CAMEL_STORE_UNLOCK(store, cache_lock); } diff --git a/camel/camel-remote-store.h b/camel/camel-remote-store.h index 2d6bc829c2..910db53e92 100644 --- a/camel/camel-remote-store.h +++ b/camel/camel-remote-store.h @@ -40,7 +40,8 @@ extern "C" { typedef struct { CamelStore parent_object; - + struct _CamelRemoteStorePrivate *priv; + CamelStream *istream, *ostream; guint timeout_id, default_port; } CamelRemoteStore; @@ -62,6 +63,9 @@ typedef struct { /* Standard Camel function */ CamelType camel_remote_store_get_type (void); +/* FIXME: All of these i/o functions need an api overhaul, they're not like + any other functions, anywhere in the world ... */ + /* Extra public functions */ gint camel_remote_store_send_string (CamelRemoteStore *store, CamelException *ex, char *fmt, ...); diff --git a/camel/camel-service.c b/camel/camel-service.c index 0fcb61fa52..6a14327b92 100644 --- a/camel/camel-service.c +++ b/camel/camel-service.c @@ -31,6 +31,8 @@ #include <ctype.h> #include <stdlib.h> +#include "camel-private.h" + static CamelObjectClass *parent_class = NULL; /* Returns the class for a CamelService */ @@ -64,6 +66,17 @@ camel_service_class_init (CamelServiceClass *camel_service_class) } static void +camel_service_init (void *o, void *k) +{ + CamelService *service = o; + + service->priv = g_malloc0(sizeof(*service->priv)); +#ifdef ENABLE_THREADS + service->priv->connect_lock = e_mutex_new(E_MUTEX_REC); +#endif +} + +static void camel_service_finalize (CamelObject *object) { CamelService *camel_service = CAMEL_SERVICE (object); @@ -85,6 +98,11 @@ camel_service_finalize (CamelObject *object) camel_url_free (camel_service->url); if (camel_service->session) camel_object_unref (CAMEL_OBJECT (camel_service->session)); + +#ifdef ENABLE_THREADS + e_mutex_destroy(camel_service->priv->connect_lock); +#endif + g_free(camel_service->priv); } @@ -100,7 +118,7 @@ camel_service_get_type (void) sizeof (CamelServiceClass), (CamelObjectClassInitFunc) camel_service_class_init, NULL, - NULL, + (CamelObjectInitFunc) camel_service_init, camel_service_finalize ); } @@ -209,24 +227,28 @@ service_connect (CamelService *service, CamelException *ex) gboolean camel_service_connect (CamelService *service, CamelException *ex) { + gboolean ret = FALSE; + g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE); g_return_val_if_fail (service->session != NULL, FALSE); g_return_val_if_fail (service->url != NULL, FALSE); + CAMEL_SERVICE_LOCK(service, connect_lock); + if (service->connected) { /* But we're still connected, so no exception * and return true. */ g_warning ("camel_service_connect: trying to connect to an already connected service"); - return TRUE; - } - - if (CSERV_CLASS (service)->connect (service, ex)) { + ret = TRUE; + } else if (CSERV_CLASS (service)->connect (service, ex)) { service->connected = TRUE; - return TRUE; + ret = TRUE; } - return FALSE; + CAMEL_SERVICE_UNLOCK(service, connect_lock); + + return ret; } static gboolean @@ -257,13 +279,17 @@ gboolean camel_service_disconnect (CamelService *service, gboolean clean, CamelException *ex) { - gboolean res; + gboolean res = TRUE; - if (!service->connected) - return TRUE; + CAMEL_SERVICE_LOCK(service, connect_lock); + + if (service->connected) { + res = CSERV_CLASS (service)->disconnect (service, clean, ex); + service->connected = FALSE; + } + + CAMEL_SERVICE_UNLOCK(service, connect_lock); - res = CSERV_CLASS (service)->disconnect (service, clean, ex); - service->connected = FALSE; return res; } @@ -431,10 +457,20 @@ query_auth_types_func (CamelService *service, CamelException *ex) GList * camel_service_query_auth_types (CamelService *service, CamelException *ex) { + GList *ret; + + /* note that we get the connect lock here, which means the callee + must not call the connect functions itself */ + CAMEL_SERVICE_LOCK(service, connect_lock); + if (service->url->empty) - return CSERV_CLASS (service)->query_auth_types_generic (service, ex); + ret = CSERV_CLASS (service)->query_auth_types_generic (service, ex); else - return CSERV_CLASS (service)->query_auth_types_connected (service, ex); + ret = CSERV_CLASS (service)->query_auth_types_connected (service, ex); + + CAMEL_SERVICE_UNLOCK(service, connect_lock); + + return ret; } static void @@ -475,6 +511,8 @@ camel_service_gethost (CamelService *service, CamelException *ex) struct hostent *h; char *hostname; +#warning "This needs to use gethostbyname_r()" + if (service->url->host) hostname = service->url->host; else diff --git a/camel/camel-service.h b/camel/camel-service.h index 3f5acabbb5..7ba07a66e9 100644 --- a/camel/camel-service.h +++ b/camel/camel-service.h @@ -47,6 +47,7 @@ extern "C" { struct _CamelService { CamelObject parent_object; + struct _CamelServicePrivate *priv; CamelSession *session; CamelProvider *provider; diff --git a/camel/camel-session.c b/camel/camel-session.c index 3ca63ca9e9..b1cc489c71 100644 --- a/camel/camel-session.c +++ b/camel/camel-session.c @@ -41,6 +41,8 @@ #include "camel-url.h" #include "hash-table-utils.h" +#include "camel-private.h" + static CamelObjectClass *parent_class; static void @@ -48,6 +50,10 @@ camel_session_init (CamelSession *session) { session->modules = camel_provider_init (); session->providers = g_hash_table_new (g_strcase_hash, g_strcase_equal); + session->priv = g_malloc0(sizeof(*session->priv)); +#ifdef ENABLE_THREADS + session->priv->lock = g_mutex_new(); +#endif } static gboolean @@ -69,6 +75,12 @@ camel_session_finalise (CamelObject *o) g_hash_table_foreach_remove (session->providers, camel_session_destroy_provider, NULL); g_hash_table_destroy (session->providers); + +#ifdef ENABLE_THREADS + g_mutex_free(session->priv->lock); +#endif + + g_free(session->priv); } static void @@ -131,6 +143,9 @@ camel_session_new (const char *storage_path, * @provider: provider object * * Registers a protocol to provider mapping for the session. + * + * Assumes the session lock has already been obtained, + * which is the case for automatically loaded provider modules. **/ void camel_session_register_provider (CamelSession *session, @@ -194,12 +209,16 @@ camel_session_list_providers (CamelSession *session, gboolean load) g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL); - if (load) { + CAMEL_SESSION_LOCK(session, lock); + + if (load) g_hash_table_foreach (session->modules, ensure_loaded, session); - } list = NULL; g_hash_table_foreach (session->providers, add_to_list, &list); + + CAMEL_SESSION_UNLOCK(session, lock); + return list; } @@ -213,8 +232,12 @@ service_cache_remove (CamelService *service, gpointer event_data, gpointer user_ g_return_if_fail (service != NULL); g_return_if_fail (service->url != NULL); + CAMEL_SESSION_LOCK(session, lock); + provider = g_hash_table_lookup (session->providers, service->url->protocol); g_hash_table_remove (provider->service_cache, service->url); + + CAMEL_SESSION_UNLOCK(session, lock); } /** @@ -250,6 +273,7 @@ camel_session_get_service (CamelSession *session, const char *url_string, /* We need to look up the provider so we can then lookup the service in the provider's cache */ + CAMEL_SESSION_LOCK(session, lock); provider = g_hash_table_lookup (session->providers, url->protocol); if (!provider) { /* See if there's one we can load. */ @@ -261,6 +285,7 @@ camel_session_get_service (CamelSession *session, const char *url_string, if (camel_exception_get_id (ex) != CAMEL_EXCEPTION_NONE) { camel_url_free (url); + CAMEL_SESSION_UNLOCK(session, lock); return NULL; } } @@ -272,6 +297,7 @@ camel_session_get_service (CamelSession *session, const char *url_string, _("No provider available for protocol `%s'"), url->protocol); camel_url_free (url); + CAMEL_SESSION_UNLOCK(session, lock); return NULL; } @@ -280,6 +306,7 @@ camel_session_get_service (CamelSession *session, const char *url_string, if (service != NULL) { camel_url_free (url); camel_object_ref (CAMEL_OBJECT (service)); + CAMEL_SESSION_UNLOCK(session, lock); return service; } @@ -288,6 +315,7 @@ camel_session_get_service (CamelSession *session, const char *url_string, g_hash_table_insert (provider->service_cache, url, service); camel_object_hook_event (CAMEL_OBJECT (service), "finalize", (CamelObjectEventHookFunc) service_cache_remove, session); } + CAMEL_SESSION_UNLOCK(session, lock); return service; } diff --git a/camel/camel-session.h b/camel/camel-session.h index fed21be6f1..78b4b817e5 100644 --- a/camel/camel-session.h +++ b/camel/camel-session.h @@ -60,6 +60,7 @@ typedef gboolean (*CamelTimeoutRemoveCallback) (guint id); struct _CamelSession { CamelObject parent_object; + struct _CamelSessionPrivate *priv; char *storage_path; CamelAuthCallback authenticator; diff --git a/camel/camel-store.c b/camel/camel-store.c index 6ee4692ff4..48ec13918b 100644 --- a/camel/camel-store.c +++ b/camel/camel-store.c @@ -31,6 +31,8 @@ #include "camel-folder.h" #include "camel-exception.h" +#include "camel-private.h" + static CamelServiceClass *parent_class = NULL; /* Returns the class for a CamelStore */ @@ -93,6 +95,12 @@ camel_store_init (void *o, void *k) if (store->folders == NULL) store->folders = g_hash_table_new (g_str_hash, g_str_equal); store->flags = 0; + + store->priv = g_malloc0(sizeof(*store->priv)); +#ifdef ENABLE_THREADS + store->priv->folder_lock = g_mutex_new(); + store->priv->cache_lock = g_mutex_new(); +#endif } static void @@ -108,6 +116,12 @@ camel_store_finalize (CamelObject *object) } g_hash_table_destroy (store->folders); } + +#ifdef ENABLE_THREADS + g_mutex_free(store->priv->folder_lock); + g_mutex_free(store->priv->cache_lock); +#endif + g_free(store->priv); } @@ -185,13 +199,19 @@ get_default_folder_name (CamelStore *store, CamelException *ex) static CamelFolder * lookup_folder (CamelStore *store, const char *folder_name) { + CamelFolder *folder = NULL; + + CAMEL_STORE_LOCK(store, cache_lock); + if (store->folders) { - CamelFolder *folder = g_hash_table_lookup (store->folders, folder_name); + folder = g_hash_table_lookup (store->folders, folder_name); if (folder) camel_object_ref(CAMEL_OBJECT(folder)); - return folder; } - return NULL; + + CAMEL_STORE_UNLOCK(store, cache_lock); + + return folder; } static void folder_finalize (CamelObject *folder, gpointer event_data, gpointer user_data) @@ -202,16 +222,18 @@ static void folder_finalize (CamelObject *folder, gpointer event_data, gpointer static void cache_folder (CamelStore *store, const char *folder_name, CamelFolder *folder) { - if (!store->folders) - return; + CAMEL_STORE_LOCK(store, cache_lock); - if (g_hash_table_lookup (store->folders, folder_name)) { - g_warning ("Caching folder %s that already exists.", - folder_name); + if (store->folders) { + if (g_hash_table_lookup (store->folders, folder_name)) { + g_warning ("Caching folder %s that already exists.", folder_name); + } + g_hash_table_insert (store->folders, g_strdup (folder_name), folder); + + camel_object_hook_event (CAMEL_OBJECT (folder), "finalize", folder_finalize, store); } - g_hash_table_insert (store->folders, g_strdup (folder_name), folder); - camel_object_hook_event (CAMEL_OBJECT (folder), "finalize", folder_finalize, store); + CAMEL_STORE_UNLOCK(store, cache_lock); /* * gt_k so as not to get caught by my little gt_k cleanliness detector. @@ -235,7 +257,11 @@ folder_matches (gpointer key, gpointer value, gpointer user_data) static void uncache_folder (CamelStore *store, CamelFolder *folder) { + CAMEL_STORE_LOCK(store, cache_lock); + g_hash_table_foreach_remove (store->folders, folder_matches, folder); + + CAMEL_STORE_UNLOCK(store, cache_lock); } @@ -244,6 +270,8 @@ get_folder_internal(CamelStore *store, const char *folder_name, guint32 flags, C { CamelFolder *folder = NULL; + /* NB: we already have folder_lock */ + /* Try cache first. */ folder = CS_CLASS(store)->lookup_folder(store, folder_name); @@ -279,11 +307,16 @@ camel_store_get_folder(CamelStore *store, const char *folder_name, guint32 flags char *name; CamelFolder *folder = NULL; + CAMEL_STORE_LOCK(store, folder_lock); + name = CS_CLASS(store)->get_folder_name(store, folder_name, ex); if (name) { folder = get_folder_internal(store, name, flags, ex); g_free (name); } + + CAMEL_STORE_UNLOCK(store, folder_lock); + return folder; } @@ -302,11 +335,15 @@ camel_store_delete_folder (CamelStore *store, const char *folder_name, { char *name; + CAMEL_STORE_LOCK(store, folder_lock); + name = CS_CLASS (store)->get_folder_name (store, folder_name, ex); if (name) { CS_CLASS (store)->delete_folder (store, name, ex); g_free (name); } + + CAMEL_STORE_UNLOCK(store, folder_lock); } /** @@ -325,6 +362,8 @@ void camel_store_rename_folder (CamelStore *store, { char *old, *new; + CAMEL_STORE_LOCK(store, folder_lock); + old = CS_CLASS (store)->get_folder_name(store, old_name, ex); if (old) { new = CS_CLASS (store)->get_folder_name(store, new_name, ex); @@ -334,6 +373,8 @@ void camel_store_rename_folder (CamelStore *store, } g_free(old); } + + CAMEL_STORE_UNLOCK(store, folder_lock); } @@ -351,11 +392,16 @@ camel_store_get_root_folder (CamelStore *store, CamelException *ex) char *name; CamelFolder *folder = NULL; + CAMEL_STORE_LOCK(store, folder_lock); + name = CS_CLASS (store)->get_root_folder_name (store, ex); if (name) { folder = get_folder_internal (store, name, CAMEL_STORE_FOLDER_CREATE, ex); g_free (name); } + + CAMEL_STORE_UNLOCK(store, folder_lock); + return folder; } @@ -374,11 +420,16 @@ camel_store_get_default_folder (CamelStore *store, CamelException *ex) char *name; CamelFolder *folder = NULL; + CAMEL_STORE_LOCK(store, folder_lock); + name = CS_CLASS (store)->get_default_folder_name (store, ex); if (name) { folder = get_folder_internal (store, name, CAMEL_STORE_FOLDER_CREATE, ex); g_free (name); } + + CAMEL_STORE_UNLOCK(store, folder_lock); + return folder; } @@ -420,11 +471,17 @@ camel_store_get_folder_info (CamelStore *store, const char *top, gboolean subscribed_only, CamelException *ex) { + CamelFolderInfo *ret; + g_return_val_if_fail (CAMEL_IS_STORE (store), NULL); - return CS_CLASS (store)->get_folder_info (store, top, fast, - recursive, subscribed_only, - ex); + CAMEL_STORE_LOCK(store, folder_lock); + + ret = CS_CLASS (store)->get_folder_info (store, top, fast, recursive, subscribed_only, ex); + + CAMEL_STORE_UNLOCK(store, folder_lock); + + return ret; } @@ -596,10 +653,18 @@ gboolean camel_store_folder_subscribed (CamelStore *store, const char *folder_name) { + gboolean ret; + g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE); g_return_val_if_fail (store->flags & CAMEL_STORE_SUBSCRIPTIONS, FALSE); - return CS_CLASS (store)->folder_subscribed (store, folder_name); + CAMEL_STORE_LOCK(store, folder_lock); + + ret = CS_CLASS (store)->folder_subscribed (store, folder_name); + + CAMEL_STORE_UNLOCK(store, folder_lock); + + return ret; } static void @@ -622,7 +687,11 @@ camel_store_subscribe_folder (CamelStore *store, g_return_if_fail (CAMEL_IS_STORE (store)); g_return_if_fail (store->flags & CAMEL_STORE_SUBSCRIPTIONS); + CAMEL_STORE_LOCK(store, folder_lock); + CS_CLASS (store)->subscribe_folder (store, folder_name, ex); + + CAMEL_STORE_UNLOCK(store, folder_lock); } static void @@ -646,6 +715,10 @@ camel_store_unsubscribe_folder (CamelStore *store, g_return_if_fail (CAMEL_IS_STORE (store)); g_return_if_fail (store->flags & CAMEL_STORE_SUBSCRIPTIONS); + CAMEL_STORE_LOCK(store, folder_lock); + CS_CLASS (store)->unsubscribe_folder (store, folder_name, ex); + + CAMEL_STORE_UNLOCK(store, folder_lock); } diff --git a/camel/camel-store.h b/camel/camel-store.h index 7558c0eb5a..0ad6e0cbed 100644 --- a/camel/camel-store.h +++ b/camel/camel-store.h @@ -57,7 +57,9 @@ typedef struct _CamelFolderInfo { struct _CamelStore { CamelService parent_object; + struct _CamelStorePrivate *priv; + /* should have cache_lock when accessing this (priv->cache_lock) */ GHashTable *folders; int flags; @@ -99,6 +101,7 @@ typedef struct { void (*uncache_folder) (CamelStore *store, CamelFolder *folder); + /* this should take flags instead, so its more futureproof */ CamelFolderInfo *(*get_folder_info) (CamelStore *store, const char *top, gboolean fast, diff --git a/camel/camel-transport.h b/camel/camel-transport.h index 48ab0ea1ff..87d3628264 100644 --- a/camel/camel-transport.h +++ b/camel/camel-transport.h @@ -71,6 +71,7 @@ gboolean camel_transport_send (CamelTransport *transport, CamelMedium *message, CamelException *ex); +/* FIXME: This should use a camel-address */ gboolean camel_transport_send_to (CamelTransport *transport, CamelMedium *message, GList *recipients, diff --git a/camel/camel.c b/camel/camel.c index 846d946b9a..2d2272057a 100644 --- a/camel/camel.c +++ b/camel/camel.c @@ -34,7 +34,7 @@ camel_init(void) { #ifdef ENABLE_THREADS #ifdef G_THREADS_ENABLED - g_thread_init (NULL); + /*g_thread_init (NULL);*/ #else /* G_THREADS_ENABLED */ printf ("Threads are not supported by your version of glib\n"); #endif /* G_THREADS_ENABLED */ diff --git a/camel/providers/imap/Makefile.am b/camel/providers/imap/Makefile.am index 03dec12608..35a4a97e77 100644 --- a/camel/providers/imap/Makefile.am +++ b/camel/providers/imap/Makefile.am @@ -42,6 +42,9 @@ libcamelimapinclude_HEADERS = \ libcamelimap_la_LDFLAGS = $(KRB4_LDFLAGS) -version-info 0:0:0 +noinst_HEADERS = \ + camel-imap-private.h + EXTRA_DIST = libcamelimap.urls diff --git a/camel/providers/imap/camel-imap-auth.c b/camel/providers/imap/camel-imap-auth.c index c510eaf01c..42abd1fa55 100644 --- a/camel/providers/imap/camel-imap-auth.c +++ b/camel/providers/imap/camel-imap-auth.c @@ -42,6 +42,10 @@ #include "camel-imap-command.h" #include "camel-imap-utils.h" +#include "camel-imap-private.h" + +#ifdef HAVE_KRB4 + static char * base64_encode_simple (const char *data, int len) { @@ -66,7 +70,6 @@ base64_decode_simple (char *data, int len) (unsigned char *)data, &state, &save); } -#ifdef HAVE_KRB4 #define IMAP_KERBEROS_V4_PROTECTION_NONE 1 #define IMAP_KERBEROS_V4_PROTECTION_INTEGRITY 2 #define IMAP_KERBEROS_V4_PROTECTION_PRIVACY 4 @@ -85,14 +88,17 @@ imap_try_kerberos_v4_auth (CamelImapStore *store, CamelException *ex) des_cblock session; des_key_schedule schedule; + CAMEL_IMAP_STORE_LOCK(store, command_lock); + /* The kickoff. */ response = camel_imap_command (store, NULL, ex, "AUTHENTICATE KERBEROS_V4"); if (!response) - return FALSE; + goto fail; resp = camel_imap_response_extract_continuation (response, ex); if (!resp) - return FALSE; + goto fail; + data = imap_next_word (resp); /* First server response is a base64-encoded 32-bit random number @@ -182,6 +188,7 @@ imap_try_kerberos_v4_auth (CamelImapStore *store, CamelException *ex) if (!response) goto lose; camel_imap_response_free (response); + CAMEL_IMAP_STORE_UNLOCK(store, command_lock); return TRUE; break_and_lose: @@ -197,6 +204,8 @@ imap_try_kerberos_v4_auth (CamelImapStore *store, CamelException *ex) camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, _("Bad authentication response from server.")); } +fail: + CAMEL_IMAP_STORE_UNLOCK(store, command_lock); return FALSE; } #endif /* HAVE_KRB4 */ diff --git a/camel/providers/imap/camel-imap-command.c b/camel/providers/imap/camel-imap-command.c index b61c9f8cd3..df27fba0fb 100644 --- a/camel/providers/imap/camel-imap-command.c +++ b/camel/providers/imap/camel-imap-command.c @@ -67,6 +67,9 @@ static char *imap_command_strdup_vprintf (CamelImapStore *store, * and quoted strings otherwise. (%S does not support strings that * contain newlines.) * + * This function assumes you have an exclusive lock on the command + * channel/stream. + * * Return value: %NULL if an error occurred (in which case @ex will * be set). Otherwise, a CamelImapResponse describing the server's * response, which the caller must free with camel_imap_response_free(). @@ -120,6 +123,8 @@ camel_imap_command (CamelImapStore *store, CamelFolder *folder, * This method is for sending continuing responses to the IMAP server * after camel_imap_command returns a CAMEL_IMAP_PLUS response. * + * This function assumes you have an exclusive lock on the remote stream. + * * Return value: as for camel_imap_command() **/ CamelImapResponse * diff --git a/camel/providers/imap/camel-imap-folder.c b/camel/providers/imap/camel-imap-folder.c index 9da5103b68..952e28faa7 100644 --- a/camel/providers/imap/camel-imap-folder.c +++ b/camel/providers/imap/camel-imap-folder.c @@ -56,6 +56,7 @@ #include "camel-mime-filter-crlf.h" #include "camel-exception.h" #include "camel-mime-utils.h" +#include "camel-imap-private.h" #define d(x) x @@ -69,10 +70,6 @@ static void imap_sync (CamelFolder *folder, gboolean expunge, CamelException *ex static const char *imap_get_full_name (CamelFolder *folder); static void imap_expunge (CamelFolder *folder, CamelException *ex); -/* message counts */ -static gint imap_get_message_count (CamelFolder *folder); -static gint imap_get_unread_message_count (CamelFolder *folder); - /* message manipulation */ static CamelMimeMessage *imap_get_message (CamelFolder *folder, const gchar *uid, CamelException *ex); @@ -84,10 +81,6 @@ static void imap_move_message_to (CamelFolder *source, const char *uid, CamelFolder *destination, CamelException *ex); /* summary info */ -static GPtrArray *imap_get_uids (CamelFolder *folder); -static GPtrArray *imap_get_summary (CamelFolder *folder); -static const CamelMessageInfo *imap_get_message_info (CamelFolder *folder, const char *uid); - static void imap_update_summary (CamelFolder *folder, int first, int last, CamelFolderChangeInfo *changes, CamelException *ex); @@ -119,27 +112,13 @@ camel_imap_folder_class_init (CamelImapFolderClass *camel_imap_folder_class) camel_folder_class->expunge = imap_expunge; camel_folder_class->get_full_name = imap_get_full_name; - camel_folder_class->get_uids = imap_get_uids; - camel_folder_class->free_uids = camel_folder_free_nop; - - camel_folder_class->get_message_count = imap_get_message_count; - camel_folder_class->get_unread_message_count = imap_get_unread_message_count; camel_folder_class->get_message = imap_get_message; camel_folder_class->append_message = imap_append_message; camel_folder_class->copy_message_to = imap_copy_message_to; camel_folder_class->move_message_to = imap_move_message_to; - camel_folder_class->get_summary = imap_get_summary; - camel_folder_class->get_message_info = imap_get_message_info; - camel_folder_class->free_summary = camel_folder_free_nop; - camel_folder_class->search_by_expression = imap_search_by_expression; camel_folder_class->search_free = imap_search_free; - - camel_folder_class->get_message_flags = imap_get_message_flags; - camel_folder_class->set_message_flags = imap_set_message_flags; - camel_folder_class->get_message_user_flag = imap_get_message_user_flag; - camel_folder_class->set_message_user_flag = imap_set_message_user_flag; } static void @@ -151,7 +130,11 @@ camel_imap_folder_init (gpointer object, gpointer klass) folder->has_summary_capability = TRUE; folder->has_search_capability = TRUE; - imap_folder->summary = NULL; + folder->summary = NULL; + imap_folder->priv = g_malloc0(sizeof(*imap_folder->priv)); +#ifdef ENABLE_THREADS + imap_folder->priv->search_lock = g_mutex_new(); +#endif } CamelType @@ -188,7 +171,10 @@ camel_imap_folder_new (CamelStore *parent, const char *folder_name, camel_folder_construct (folder, parent, folder_name, short_name); + CAMEL_IMAP_STORE_LOCK(imap_store, command_lock); response = camel_imap_command (imap_store, folder, ex, NULL); + CAMEL_IMAP_STORE_UNLOCK(imap_store, command_lock); + if (!response) { camel_object_unref ((CamelObject *)folder); return NULL; @@ -213,8 +199,8 @@ camel_imap_folder_new (CamelStore *parent, const char *folder_name, } camel_imap_response_free (response); - imap_folder->summary = camel_imap_summary_new (summary_file, validity); - if (!imap_folder->summary) { + folder->summary = camel_imap_summary_new (summary_file, validity); + if (!folder->summary) { camel_object_unref (CAMEL_OBJECT (folder)); camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Could not load summary for %s"), @@ -236,9 +222,13 @@ imap_finalize (CamelObject *object) { CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (object); - camel_object_unref ((CamelObject *)imap_folder->summary); if (imap_folder->search) camel_object_unref ((CamelObject *)imap_folder->search); + +#ifdef ENABLE_THREADS + g_mutex_free(imap_folder->priv->search_lock); +#endif + g_free(imap_folder->priv); } static void @@ -262,9 +252,11 @@ imap_refresh_info (CamelFolder *folder, CamelException *ex) /* Get UIDs and flags of all messages. */ if (imap_folder->exists) { + CAMEL_IMAP_STORE_LOCK(store, command_lock); response = camel_imap_command (store, folder, ex, "FETCH 1:%d (UID FLAGS)", imap_folder->exists); + CAMEL_IMAP_STORE_UNLOCK(store, command_lock); if (!response) { camel_folder_change_info_free (changes); return; @@ -298,18 +290,21 @@ imap_refresh_info (CamelFolder *folder, CamelException *ex) * the UID in the folder, that it means the message was * deleted on the server, so we remove it from the summary. */ - summary_len = camel_folder_summary_count (imap_folder->summary); + summary_len = camel_folder_summary_count (folder->summary); for (i = 0; i < summary_len && i < imap_folder->exists; i++) { - info = camel_folder_summary_index (imap_folder->summary, i); - iinfo = (CamelImapMessageInfo *)info; - /* Shouldn't happen, but... */ if (!new[i].uid) continue; + info = camel_folder_summary_index (imap_folder->summary, i); + iinfo = (CamelImapMessageInfo *)info; + if (strcmp (camel_message_info_uid (info), new[i].uid) != 0) { camel_folder_change_info_remove_uid (changes, camel_message_info_uid (info)); camel_folder_summary_remove (imap_folder->summary, info); + camel_folder_summary_info_free(folder->summary, info); + folder_changed = TRUE; + g_free (new[i].uid); i--; summary_len--; continue; @@ -328,14 +323,17 @@ imap_refresh_info (CamelFolder *folder, CamelException *ex) camel_folder_change_info_change_uid (changes, new[i].uid); } + camel_folder_summary_info_free(folder->summary, info); + g_free (new[i].uid); } /* Remove any leftover cached summary messages. */ while (summary_len > i + 1) { - info = camel_folder_summary_index (imap_folder->summary, --summary_len); + info = camel_folder_summary_index (folder->summary, --summary_len); camel_folder_change_info_remove_uid (changes, camel_message_info_uid (info)); - camel_folder_summary_remove (imap_folder->summary, info); + camel_folder_summary_remove (folder->summary, info); + camel_folder_summary_info_free(folder->summary, info); } /* Add any new folder messages. */ @@ -360,40 +358,48 @@ static void imap_sync (CamelFolder *folder, gboolean expunge, CamelException *ex) { CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store); - CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder); + /*CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);*/ CamelImapResponse *response; int i, max; /* Set the flags on any messages that have changed this session */ - max = camel_folder_summary_count (imap_folder->summary); + max = camel_folder_summary_count (folder->summary); for (i = 0; i < max; i++) { CamelMessageInfo *info; - info = camel_folder_summary_index (imap_folder->summary, i); - if (info->flags & CAMEL_MESSAGE_FOLDER_FLAGGED) { + info = camel_folder_summary_index (folder->summary, i); + if (info && (info->flags & CAMEL_MESSAGE_FOLDER_FLAGGED)) { char *flags; flags = imap_create_flag_list (info->flags); if (flags) { + CAMEL_IMAP_STORE_LOCK(store, command_lock); response = camel_imap_command ( store, folder, ex, "UID STORE %s FLAGS.SILENT %s", camel_message_info_uid(info), flags); + CAMEL_IMAP_STORE_UNLOCK(store, command_lock); + g_free (flags); - if (!response) + if (!response) { + camel_folder_summary_info_free(folder->summary, info); return; + } camel_imap_response_free (response); } info->flags &= ~CAMEL_MESSAGE_FOLDER_FLAGGED; } + camel_folder_summary_info_free(folder->summary, info); } if (expunge) { + CAMEL_IMAP_STORE_LOCK(store, command_lock); response = camel_imap_command (store, folder, ex, "EXPUNGE"); + CAMEL_IMAP_STORE_UNLOCK(store, command_lock); camel_imap_response_free (response); } - camel_folder_summary_save (imap_folder->summary); + camel_folder_summary_save (folder->summary); } static void @@ -417,31 +423,6 @@ imap_get_full_name (CamelFolder *folder) return folder->full_name; } -static gint -imap_get_message_count (CamelFolder *folder) -{ - CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder); - - return camel_folder_summary_count (imap_folder->summary); -} - -static gint -imap_get_unread_message_count (CamelFolder *folder) -{ - CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder); - CamelMessageInfo *info; - int i, max, count = 0; - - max = camel_folder_summary_count (imap_folder->summary); - for (i = 0; i < max; i++) { - info = camel_folder_summary_index (imap_folder->summary, i); - if (!(info->flags & CAMEL_MESSAGE_SEEN)) - count++; - } - - return count; -} - static void imap_append_message (CamelFolder *folder, CamelMimeMessage *message, const CamelMessageInfo *info, CamelException *ex) @@ -476,6 +457,7 @@ imap_append_message (CamelFolder *folder, CamelMimeMessage *message, camel_object_unref (CAMEL_OBJECT (crlf_filter)); camel_object_unref (CAMEL_OBJECT (memstream)); + CAMEL_IMAP_STORE_LOCK(store, command_lock); response = camel_imap_command (store, NULL, ex, "APPEND %S%s%s {%d}", folder->full_name, flagstr ? " " : "", flagstr ? flagstr : "", ba->len); @@ -483,11 +465,13 @@ imap_append_message (CamelFolder *folder, CamelMimeMessage *message, if (!response) { g_byte_array_free (ba, TRUE); + CAMEL_IMAP_STORE_UNLOCK(store, command_lock); return; } result = camel_imap_response_extract_continuation (response, ex); if (!result) { g_byte_array_free (ba, TRUE); + CAMEL_IMAP_STORE_UNLOCK(store, command_lock); return; } g_free (result); @@ -496,6 +480,7 @@ imap_append_message (CamelFolder *folder, CamelMimeMessage *message, g_byte_array_append (ba, "\0", 3); response = camel_imap_command_continuation (store, ex, ba->data); g_byte_array_free (ba, TRUE); + CAMEL_IMAP_STORE_UNLOCK(store, command_lock); if (!response) return; camel_imap_response_free (response); @@ -508,8 +493,11 @@ imap_copy_message_to (CamelFolder *source, const char *uid, CamelImapStore *store = CAMEL_IMAP_STORE (source->parent_store); CamelImapResponse *response; + CAMEL_IMAP_STORE_LOCK(store, command_lock); response = camel_imap_command (store, source, ex, "UID COPY %s %S", uid, destination->full_name); + CAMEL_IMAP_STORE_UNLOCK(store, command_lock); + camel_imap_response_free (response); } @@ -520,8 +508,10 @@ imap_move_message_to (CamelFolder *source, const char *uid, CamelImapStore *store = CAMEL_IMAP_STORE (source->parent_store); CamelImapResponse *response; + CAMEL_IMAP_STORE_LOCK(store, command_lock); response = camel_imap_command (store, source, ex, "UID COPY %s %S", uid, destination->full_name); + CAMEL_IMAP_STORE_UNLOCK(store, command_lock); camel_imap_response_free (response); if (camel_exception_is_set (ex)) @@ -530,27 +520,6 @@ imap_move_message_to (CamelFolder *source, const char *uid, camel_folder_delete_message (source, uid); } -static GPtrArray * -imap_get_uids (CamelFolder *folder) -{ - CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder); - const CamelMessageInfo *info; - GPtrArray *array; - int i, count; - - count = camel_folder_summary_count (imap_folder->summary); - - array = g_ptr_array_new (); - g_ptr_array_set_size (array, count); - - for (i = 0; i < count; i++) { - info = camel_folder_summary_index (imap_folder->summary, i); - array->pdata[i] = g_strdup (camel_message_info_uid(info)); - } - - return array; -} - static CamelMimeMessage * imap_get_message (CamelFolder *folder, const gchar *uid, CamelException *ex) { @@ -561,8 +530,11 @@ imap_get_message (CamelFolder *folder, const gchar *uid, CamelException *ex) char *result, *mesg, *p; int len; + CAMEL_IMAP_STORE_LOCK(store, command_lock); response = camel_imap_command (store, folder, ex, "UID FETCH %s BODY.PEEK[]", uid); + CAMEL_IMAP_STORE_UNLOCK(store, command_lock); + if (!response) return NULL; result = camel_imap_response_extract (response, "FETCH", ex); @@ -622,7 +594,7 @@ imap_update_summary (CamelFolder *folder, int first, int last, CamelFolderChangeInfo *changes, CamelException *ex) { CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store); - CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder); + /*CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);*/ CamelImapResponse *response; GPtrArray *headers = NULL; char *q, *summary_specifier; @@ -630,6 +602,7 @@ imap_update_summary (CamelFolder *folder, int first, int last, int i; summary_specifier = imap_protocol_get_summary_specifier (store); + CAMEL_IMAP_STORE_LOCK(store, command_lock); if (first == last) { response = camel_imap_command (store, folder, ex, "FETCH %d (%s)", first, @@ -639,6 +612,7 @@ imap_update_summary (CamelFolder *folder, int first, int last, "FETCH %d:%d (%s)", first, last, summary_specifier); } + CAMEL_IMAP_STORE_UNLOCK(store, command_lock); g_free (summary_specifier); if (!response) @@ -683,9 +657,10 @@ imap_update_summary (CamelFolder *folder, int first, int last, /* We can't just call camel_folder_summary_add_from_parser * because it will assign the wrong UID, and thus get the * uid hash table wrong and all that. FIXME some day. + * Well you can actually now, because you can override next_uid_string(), but + * it hasn't been done yet. */ - info = camel_folder_summary_info_new_from_header ( - imap_folder->summary, h); + info = camel_folder_summary_info_new_from_header(folder->summary, h); iinfo = (CamelImapMessageInfo *)info; header_raw_clear (&h); uid = g_strndup (uid, q - uid); @@ -708,42 +683,33 @@ imap_update_summary (CamelFolder *folder, int first, int last, } else info->size = strtoul (size + 12, NULL, 10); - camel_folder_summary_add (imap_folder->summary, info); + camel_folder_summary_add (folder->summary, info); } camel_imap_response_free (response); } static GPtrArray * -imap_get_summary (CamelFolder *folder) -{ - CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder); - - return imap_folder->summary->messages; -} - -/* get a single message info, by uid */ -static const CamelMessageInfo * -imap_get_message_info (CamelFolder *folder, const char *uid) -{ - CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder); - - return camel_folder_summary_uid (imap_folder->summary, uid); -} - -static GPtrArray * imap_search_by_expression (CamelFolder *folder, const char *expression, CamelException *ex) { CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder); + GPtrArray *matches, *summary; + + /* we could get around this by creating a new search object each time, + but i doubt its worth it since any long operation would lock the + command channel too */ + CAMEL_IMAP_FOLDER_LOCK(folder, search_lock); if (!imap_folder->search) imap_folder->search = camel_imap_search_new (); camel_folder_search_set_folder (imap_folder->search, folder); - camel_folder_search_set_summary ( - imap_folder->search, imap_folder->summary->messages); + summary = camel_folder_get_summary(folder); + camel_folder_search_set_summary(imap_folder->search, summary); + uids = camel_folder_search_execute_expression (imap_folder->search, expression, ex); + + CAMEL_IMAP_FOLDER_UNLOCK(folder, search_lock); - return camel_folder_search_execute_expression (imap_folder->search, - expression, ex); + camel_folder_free_summary(folder, summary); } static void @@ -753,54 +719,11 @@ imap_search_free (CamelFolder *folder, GPtrArray *uids) g_return_if_fail (imap_folder->search); - camel_folder_search_free_result (imap_folder->search, uids); -} + CAMEL_IMAP_FOLDER_LOCK(folder, search_lock); -static guint32 -imap_get_message_flags (CamelFolder *folder, const char *uid) -{ - const CamelMessageInfo *info; - - info = imap_get_message_info (folder, uid); - g_return_val_if_fail (info != NULL, 0); - - return info->flags; -} - -static void -imap_set_message_flags (CamelFolder *folder, const char *uid, guint32 flags, guint32 set) -{ - CamelImapFolder *imap_folder = (CamelImapFolder *)folder; - CamelMessageInfo *info; - guint32 new; - - info = camel_folder_summary_uid (imap_folder->summary, uid); - g_return_if_fail (info != NULL); - - new = (info->flags & ~flags) | (set & flags); - if (new == info->flags) - return; - - info->flags = new | CAMEL_MESSAGE_FOLDER_FLAGGED; - camel_folder_summary_touch (imap_folder->summary); - - camel_object_trigger_event (CAMEL_OBJECT (folder), "message_changed", - (gpointer)uid); -} - -static gboolean -imap_get_message_user_flag (CamelFolder *folder, const char *uid, const char *name) -{ - /* FIXME */ - return FALSE; -} + camel_folder_search_free_result (imap_folder->search, uids); -static void -imap_set_message_user_flag (CamelFolder *folder, const char *uid, const char *name, gboolean value) -{ - /* FIXME */ - camel_object_trigger_event (CAMEL_OBJECT (folder), "message_changed", - (gpointer)uid); + CAMEL_IMAP_FOLDER_UNLOCK(folder, search_lock); } void diff --git a/camel/providers/imap/camel-imap-folder.h b/camel/providers/imap/camel-imap-folder.h index 3183a8816d..cff11255c2 100644 --- a/camel/providers/imap/camel-imap-folder.h +++ b/camel/providers/imap/camel-imap-folder.h @@ -44,6 +44,8 @@ extern "C" { typedef struct { CamelFolder parent_object; + struct _CamelImapFolderPrivate *priv; + CamelFolderSearch *search; CamelFolderSummary *summary; int exists; diff --git a/camel/providers/imap/camel-imap-private.h b/camel/providers/imap/camel-imap-private.h new file mode 100644 index 0000000000..95ec3a5a0b --- /dev/null +++ b/camel/providers/imap/camel-imap-private.h @@ -0,0 +1,74 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * camel-imap-private.h: Private info for imap. + * + * Authors: Michael Zucchi <notzed@helixcode.com> + * + * Copyright 1999, 2000 Helix Code, Inc. (http://www.helixcode.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#ifndef CAMEL_PRIVATE_H +#define CAMEL_PRIVATE_H 1 + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus }*/ + +/* need a way to configure and save this data, if this header is to + be installed. For now, dont install it */ + +#include "config.h" + +#ifdef ENABLE_THREADS +#include "e-util/e-msgport.h" +#endif + +struct _CamelImapStorePrivate { +#ifdef ENABLE_THREADS + EMutex *command_lock; /* for locking the command stream for a complete operation */ +#endif +}; + +#ifdef ENABLE_THREADS +#define CAMEL_IMAP_STORE_LOCK(f, l) (e_mutex_lock(((CamelImapStore *)f)->priv->l)) +#define CAMEL_IMAP_STORE_UNLOCK(f, l) (e_mutex_unlock(((CamelImapStore *)f)->priv->l)) +#else +#define CAMEL_IMAP_STORE_LOCK(f, l) +#define CAMEL_IMAP_STORE_UNLOCK(f, l) +#endif + +struct _CamelImapFolderPrivate { +#ifdef ENABLE_THREADS + GMutex *search_lock; /* for locking the search object */ +#endif +}; + +#ifdef ENABLE_THREADS +#define CAMEL_IMAP_FOLDER_LOCK(f, l) (g_mutex_lock(((CamelImapFolder *)f)->priv->l)) +#define CAMEL_IMAP_FOLDER_UNLOCK(f, l) (g_mutex_unlock(((CamelImapFolder *)f)->priv->l)) +#else +#define CAMEL_IMAP_FOLDER_LOCK(f, l) +#define CAMEL_IMAP_FOLDER_UNLOCK(f, l) +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CAMEL_H */ + diff --git a/camel/providers/imap/camel-imap-search.c b/camel/providers/imap/camel-imap-search.c index 1aa6b55bc5..cec0ee1f89 100644 --- a/camel/providers/imap/camel-imap-search.c +++ b/camel/providers/imap/camel-imap-search.c @@ -31,6 +31,7 @@ #include "camel-imap-command.h" #include "camel-imap-folder.h" #include "camel-imap-search.h" +#include "camel-imap-private.h" static ESExpResult * imap_body_contains (struct _ESExp *f, int argc, struct _ESExpResult **argv, @@ -72,10 +73,13 @@ imap_body_contains (struct _ESExp *f, int argc, struct _ESExpResult **argv, CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (s->folder); char *value = argv[0]->value.string; CamelImapResponse *response; - char *result, *p, *lasts = NULL; + char *result, *p, *lasts = NULL, *real_uid; const char *uid; ESExpResult *r; CamelMessageInfo *info; + GHashTable *uid_hash = NULL; + + CAMEL_IMAP_STORE_LOCK(store, command_lock); if (s->current) { uid = camel_message_info_uid (s->current); @@ -91,6 +95,9 @@ imap_body_contains (struct _ESExp *f, int argc, struct _ESExpResult **argv, "UID SEARCH BODY \"%s\"", value); } + + CAMEL_IMAP_STORE_UNLOCK(store, command_lock); + if (!response) return r; result = camel_imap_response_extract (response, "SEARCH", NULL); @@ -105,14 +112,27 @@ imap_body_contains (struct _ESExp *f, int argc, struct _ESExpResult **argv, break; } } else { - /* FIXME: The strings added to the array must be - * static... - */ - info = camel_folder_summary_uid (imap_folder->summary, p); - g_ptr_array_add (r->value.ptrarray, (char *)camel_message_info_uid (info)); + /* if we need to setup a hash of summary items, this way we get + access to the summary memory which is locked for the duration of + the search, and wont vanish on us */ + if (uid_hash == NULL) { + int i; + + uid_hash = g_hash_table_new(g_str_hash, g_str_equal); + for (i=0;i<s->summary->len;i++) { + info = s->summary->pdata[i]; + g_hash_table_insert(uid_hash, camel_message_info_uid(info), info); + } + } + if (g_hash_table_lookup_extended(uid_hash, p, &real_uid, &info)) + g_ptr_array_add (r->value.ptrarray, real_uid); } } + /* we could probably cache this globally, but its probably not worth it */ + if (uid_hash) + g_hash_table_destroy(uid_hash); + return r; } diff --git a/camel/providers/imap/camel-imap-store.c b/camel/providers/imap/camel-imap-store.c index e3bd61af27..8774d783bd 100644 --- a/camel/providers/imap/camel-imap-store.c +++ b/camel/providers/imap/camel-imap-store.c @@ -47,6 +47,8 @@ #include "camel-url.h" #include "string-utils.h" +#include "camel-imap-private.h" + #define d(x) x /* Specified in RFC 2060 */ @@ -121,6 +123,10 @@ camel_imap_store_finalize (CamelObject *object) g_hash_table_foreach_remove (imap_store->subscribed_folders, free_sub, NULL); g_hash_table_destroy (imap_store->subscribed_folders); +#ifdef ENABLE_THREADS + e_mutex_destroy(imap_store->priv->command_lock); +#endif + g_free(imap_store->priv); } static void @@ -139,6 +145,11 @@ camel_imap_store_init (gpointer object, gpointer klass) imap_store->connected = FALSE; imap_store->subscribed_folders = g_hash_table_new (g_str_hash, g_str_equal); + + imap_store->priv = g_malloc0(sizeof(*imap_store->priv)); +#ifdef ENABLE_THREADS + imap_store->priv->command_lock = e_mutex_new(E_MUTEX_REC); +#endif } CamelType @@ -175,6 +186,7 @@ static struct { { NULL, 0 } }; +/* we have remote-store:connect_lock by now */ static gboolean connect_to_server (CamelService *service, CamelException *ex) { @@ -351,10 +363,12 @@ imap_connect (CamelService *service, CamelException *ex) } } + CAMEL_IMAP_STORE_LOCK(store, command_lock); response = camel_imap_command (store, NULL, ex, "LOGIN %S %S", service->url->user, service->url->passwd); + CAMEL_IMAP_STORE_UNLOCK(store, command_lock); if (!response) { errbuf = g_strdup_printf (_("Unable to authenticate " "to IMAP server.\n%s\n\n"), @@ -380,6 +394,8 @@ imap_connect (CamelService *service, CamelException *ex) namespace++; else namespace = ""; + + CAMEL_IMAP_STORE_LOCK(store, command_lock); if (store->server_level >= IMAP_LEVEL_IMAP4REV1) { /* This idiom means "tell me the hierarchy separator * for the given path, even if that path doesn't exist. @@ -396,6 +412,8 @@ imap_connect (CamelService *service, CamelException *ex) "LIST \"\" %S", namespace); } + CAMEL_IMAP_STORE_UNLOCK(store, command_lock); + if (!response) return FALSE; @@ -431,7 +449,11 @@ imap_disconnect (CamelService *service, gboolean clean, CamelException *ex) if (store->connected && clean) { /* send the logout command */ + + /* NB: this lock probably isn't required */ + CAMEL_IMAP_STORE_LOCK(store, command_lock); response = camel_imap_command (store, NULL, ex, "LOGOUT"); + CAMEL_IMAP_STORE_UNLOCK(store, command_lock); camel_imap_response_free (response); } @@ -440,6 +462,7 @@ imap_disconnect (CamelService *service, gboolean clean, CamelException *ex) return CAMEL_SERVICE_CLASS (remote_store_class)->disconnect (service, clean, ex); } +/* NOTE: Must have imap_store::command_lock before calling this */ static gboolean imap_folder_exists (CamelImapStore *store, const char *folder_name, gboolean *selectable, char **short_name, @@ -481,6 +504,7 @@ imap_folder_exists (CamelImapStore *store, const char *folder_name, return TRUE; } +/* NOTE: Must have imap_store::command_lock before calling this */ static gboolean imap_create (CamelImapStore *store, const char *folder_name, CamelException *ex) @@ -503,18 +527,19 @@ get_folder (CamelStore *store, const char *folder_name, guint32 flags, char *short_name, *summary_file, *p; gboolean selectable; + /* lock around the whole lot to check/create atomically */ + CAMEL_IMAP_STORE_LOCK(imap_store, command_lock); if (!imap_folder_exists (imap_store, folder_name, &selectable, &short_name, ex)) { - if ((flags & CAMEL_STORE_FOLDER_CREATE) == 0) - return NULL; - - if (!imap_create (imap_store, folder_name, ex)) - return NULL; - - if (!imap_folder_exists (imap_store, folder_name, - &selectable, &short_name, ex)) + if ((flags & CAMEL_STORE_FOLDER_CREATE) == 0 + || (!imap_create (imap_store, folder_name, ex)) + || (!imap_folder_exists (imap_store, folder_name, + &selectable, &short_name, ex))) { + CAMEL_IMAP_STORE_UNLOCK(imap_store, command_lock); return NULL; + } } + CAMEL_IMAP_STORE_UNLOCK(imap_store, command_lock); if (!selectable) { camel_exception_setv (ex, CAMEL_EXCEPTION_STORE_NO_FOLDER, @@ -621,8 +646,10 @@ get_folder_info (CamelStore *store, const char *top, gboolean fast, else name = ""; } + CAMEL_IMAP_STORE_LOCK(imap_store, command_lock); response = camel_imap_command (imap_store, NULL, ex, "LIST \"\" %S", name); + CAMEL_IMAP_STORE_UNLOCK(imap_store, command_lock); if (!response) return FALSE; list = camel_imap_response_extract (response, "LIST", ex); @@ -642,10 +669,13 @@ get_folder_info (CamelStore *store, const char *top, gboolean fast, else pattern = g_strdup_printf ("%s%c", name, recursive ? '*' : '%'); + CAMEL_IMAP_STORE_LOCK(imap_store, command_lock); response = camel_imap_command (imap_store, NULL, ex, "%s \"\" %S", subscribed_only ? "LSUB" : "LIST", pattern); + CAMEL_IMAP_STORE_UNLOCK(imap_store, command_lock); + g_free (pattern); if (!response) return NULL; @@ -694,10 +724,12 @@ get_folder_info (CamelStore *store, const char *top, gboolean fast, if (!fi->url) continue; + CAMEL_IMAP_STORE_LOCK(imap_store, command_lock); response = camel_imap_command ( imap_store, NULL, NULL, "STATUS %S (MESSAGES UNSEEN)", fi->full_name); + CAMEL_IMAP_STORE_UNLOCK(imap_store, command_lock); if (!response) continue; status = camel_imap_response_extract ( @@ -757,6 +789,7 @@ subscribe_folder (CamelStore *store, const char *folder_name, CamelImapStore *imap_store = CAMEL_IMAP_STORE (store); CamelImapResponse *response; + CAMEL_IMAP_STORE_LOCK(imap_store, command_lock); response = camel_imap_command (imap_store, NULL, ex, "SUBSCRIBE %S", folder_name); if (response) { @@ -774,9 +807,11 @@ unsubscribe_folder (CamelStore *store, const char *folder_name, CamelImapStore *imap_store = CAMEL_IMAP_STORE (store); CamelImapResponse *response; gpointer key, value; - + + CAMEL_IMAP_STORE_LOCK(imap_store, command_lock); response = camel_imap_command (imap_store, NULL, ex, "UNSUBSCRIBE %S", folder_name); + CAMEL_IMAP_STORE_UNLOCK(imap_store, command_lock); if (response) { g_hash_table_lookup_extended (imap_store->subscribed_folders, folder_name, &key, &value); @@ -793,6 +828,8 @@ imap_keepalive (CamelRemoteStore *store) CamelImapStore *imap_store = CAMEL_IMAP_STORE (store); CamelImapResponse *response; + CAMEL_IMAP_STORE_LOCK(imap_store, command_lock); response = camel_imap_command (imap_store, NULL, NULL, "NOOP"); + CAMEL_IMAP_STORE_UNLOCK(imap_store, command_lock); camel_imap_response_free (response); } diff --git a/camel/providers/imap/camel-imap-store.h b/camel/providers/imap/camel-imap-store.h index 025e28256a..cd93d8eddf 100644 --- a/camel/providers/imap/camel-imap-store.h +++ b/camel/providers/imap/camel-imap-store.h @@ -56,7 +56,8 @@ typedef enum { typedef struct { CamelRemoteStore parent_object; - + struct _CamelImapStorePrivate *priv; + CamelFolder *current_folder; guint32 command; diff --git a/camel/providers/local/Makefile.am b/camel/providers/local/Makefile.am index 1f250b04cd..2e95021334 100644 --- a/camel/providers/local/Makefile.am +++ b/camel/providers/local/Makefile.am @@ -47,6 +47,9 @@ libcamellocalinclude_HEADERS = \ camel-maildir-store.h \ camel-maildir-summary.h +noinst_HEADERS = \ + camel-local-private.h + libcamellocal_la_LDFLAGS = -version-info 0:0:0 libcamellocal_la_LIBADD = $(top_builddir)/e-util/libeutil.la $(top_builddir)/libibex/libibex.la $(UNICODE_LIBS) diff --git a/camel/providers/local/camel-local-folder.c b/camel/providers/local/camel-local-folder.c index 931c95a114..116a10ea7d 100644 --- a/camel/providers/local/camel-local-folder.c +++ b/camel/providers/local/camel-local-folder.c @@ -42,6 +42,8 @@ #include "camel-mime-filter-from.h" #include "camel-exception.h" +#include "camel-local-private.h" + #define d(x) /*(printf("%s(%d): ", __FILE__, __LINE__),(x))*/ static CamelFolderClass *parent_class = NULL; @@ -55,30 +57,11 @@ static int local_lock(CamelLocalFolder *lf, CamelLockType type, CamelException * static void local_unlock(CamelLocalFolder *lf); static void local_sync(CamelFolder *folder, gboolean expunge, CamelException *ex); -static gint local_get_message_count(CamelFolder *folder); -static gint local_get_unread_message_count(CamelFolder *folder); - -static GPtrArray *local_get_uids(CamelFolder *folder); -static GPtrArray *local_get_summary(CamelFolder *folder); -#if 0 -static void local_append_message(CamelFolder *folder, CamelMimeMessage * message, const CamelMessageInfo * info, CamelException *ex); -static CamelMimeMessage *local_get_message(CamelFolder *folder, const gchar * uid, CamelException *ex); -#endif static void local_expunge(CamelFolder *folder, CamelException *ex); -static const CamelMessageInfo *local_get_message_info(CamelFolder *folder, const char *uid); - static GPtrArray *local_search_by_expression(CamelFolder *folder, const char *expression, CamelException *ex); static void local_search_free(CamelFolder *folder, GPtrArray * result); -static guint32 local_get_message_flags(CamelFolder *folder, const char *uid); -static void local_set_message_flags(CamelFolder *folder, const char *uid, guint32 flags, guint32 set); -static gboolean local_get_message_user_flag(CamelFolder *folder, const char *uid, const char *name); -static void local_set_message_user_flag(CamelFolder *folder, const char *uid, const char *name, gboolean value); -static const char *local_get_message_user_tag(CamelFolder *folder, const char *uid, const char *name); -static void local_set_message_user_tag(CamelFolder *folder, const char *uid, const char *name, const char *value); - - static void local_finalize(CamelObject * object); static void @@ -92,26 +75,11 @@ camel_local_folder_class_init(CamelLocalFolderClass * camel_local_folder_class) /* virtual method overload */ camel_folder_class->sync = local_sync; - camel_folder_class->get_message_count = local_get_message_count; - camel_folder_class->get_unread_message_count = local_get_unread_message_count; - camel_folder_class->get_uids = local_get_uids; - camel_folder_class->free_uids = camel_folder_free_deep; - camel_folder_class->get_summary = local_get_summary; - camel_folder_class->free_summary = camel_folder_free_nop; camel_folder_class->expunge = local_expunge; camel_folder_class->search_by_expression = local_search_by_expression; camel_folder_class->search_free = local_search_free; - camel_folder_class->get_message_info = local_get_message_info; - - camel_folder_class->get_message_flags = local_get_message_flags; - camel_folder_class->set_message_flags = local_set_message_flags; - camel_folder_class->get_message_user_flag = local_get_message_user_flag; - camel_folder_class->set_message_user_flag = local_set_message_user_flag; - camel_folder_class->get_message_user_tag = local_get_message_user_tag; - camel_folder_class->set_message_user_tag = local_set_message_user_tag; - camel_local_folder_class->lock = local_lock; camel_local_folder_class->unlock = local_unlock; } @@ -129,18 +97,25 @@ local_init(gpointer object, gpointer klass) CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_DRAFT | CAMEL_MESSAGE_FLAGGED | CAMEL_MESSAGE_SEEN | CAMEL_MESSAGE_USER; - local_folder->summary = NULL; + folder->summary = NULL; local_folder->search = NULL; + + local_folder->priv = g_malloc0(sizeof(*local_folder->priv)); +#ifdef ENABLE_THREADS + local_folder->priv->search_lock = g_mutex_new(); +#endif } static void local_finalize(CamelObject * object) { CamelLocalFolder *local_folder = CAMEL_LOCAL_FOLDER(object); + CamelFolder *folder = (CamelFolder *)object; - if (local_folder->summary) { - camel_local_summary_sync(local_folder->summary, FALSE, local_folder->changes, NULL); - camel_object_unref((CamelObject *)local_folder->summary); + if (folder->summary) { + camel_local_summary_sync((CamelLocalSummary *)folder->summary, FALSE, local_folder->changes, NULL); + camel_object_unref((CamelObject *)folder->summary); + folder->summary = NULL; } if (local_folder->search) { @@ -160,6 +135,11 @@ local_finalize(CamelObject * object) g_free(local_folder->index_path); camel_folder_change_info_free(local_folder->changes); + +#ifdef ENABLE_THREADS + g_mutex_free(local_folder->priv->search_lock); +#endif + g_free(local_folder->priv); } CamelType camel_local_folder_get_type(void) @@ -228,8 +208,8 @@ camel_local_folder_construct(CamelLocalFolder *lf, CamelStore *parent_store, con lf->flags = flags; - lf->summary = CLOCALF_CLASS(lf)->create_summary(lf->summary_path, lf->folder_path, lf->index); - if (camel_local_summary_load(lf->summary, forceindex, ex) == -1) { + folder->summary = (CamelFolderSummary *)CLOCALF_CLASS(lf)->create_summary(lf->summary_path, lf->folder_path, lf->index); + if (camel_local_summary_load((CamelLocalSummary *)folder->summary, forceindex, ex) == -1) { camel_object_unref (CAMEL_OBJECT (folder)); return NULL; } @@ -289,7 +269,7 @@ local_sync(CamelFolder *folder, gboolean expunge, CamelException *ex) return; /* if sync fails, we'll pass it up on exit through ex */ - camel_local_summary_sync(lf->summary, expunge, lf->changes, ex); + camel_local_summary_sync((CamelLocalSummary *)folder->summary, expunge, lf->changes, ex); camel_local_folder_unlock(lf); if (camel_folder_change_info_changed(lf->changes)) { @@ -300,8 +280,8 @@ local_sync(CamelFolder *folder, gboolean expunge, CamelException *ex) /* force save of metadata */ if (lf->index) ibex_save(lf->index); - if (lf->summary) - camel_folder_summary_save(CAMEL_FOLDER_SUMMARY(lf->summary)); + if (folder->summary) + camel_folder_summary_save(folder->summary); } static void @@ -310,203 +290,48 @@ local_expunge(CamelFolder *folder, CamelException *ex) d(printf("expunge\n")); /* Just do a sync with expunge, serves the same purpose */ - camel_folder_sync(folder, TRUE, ex); -} - -/* - The following functions all work off the summary, so the default operations provided - in camel-local-folder will suffice for all subclasses. They may want to - snoop various operations to ensure the status remains synced, or just wait - for the sync operation -*/ -static gint -local_get_message_count(CamelFolder *folder) -{ - CamelLocalFolder *local_folder = CAMEL_LOCAL_FOLDER(folder); - - g_return_val_if_fail(local_folder->summary != NULL, -1); - - return camel_folder_summary_count(CAMEL_FOLDER_SUMMARY(local_folder->summary)); -} - -static gint -local_get_unread_message_count(CamelFolder *folder) -{ - CamelLocalFolder *local_folder = CAMEL_LOCAL_FOLDER(folder); - CamelMessageInfo *info; - GPtrArray *infolist; - gint i, max, count = 0; - - g_return_val_if_fail(local_folder->summary != NULL, -1); - - max = camel_folder_summary_count(CAMEL_FOLDER_SUMMARY(local_folder->summary)); - if (max == -1) - return -1; - - infolist = local_get_summary(folder); - - for (i = 0; i < infolist->len; i++) { - info = (CamelMessageInfo *) g_ptr_array_index(infolist, i); - if (!(info->flags & CAMEL_MESSAGE_SEEN)) - count++; - } - - return count; + /* call the callback directly, to avoid locking problems */ + CAMEL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(folder))->sync(folder, TRUE, ex); } static GPtrArray * -local_get_uids(CamelFolder *folder) -{ - GPtrArray *array; - CamelLocalFolder *local_folder = CAMEL_LOCAL_FOLDER(folder); - int i, count; - - count = camel_folder_summary_count(CAMEL_FOLDER_SUMMARY(local_folder->summary)); - array = g_ptr_array_new(); - g_ptr_array_set_size(array, count); - for (i = 0; i < count; i++) { - CamelMessageInfo *info = camel_folder_summary_index(CAMEL_FOLDER_SUMMARY(local_folder->summary), i); - - array->pdata[i] = g_strdup(camel_message_info_uid(info)); - } - - return array; -} - -GPtrArray * -local_get_summary(CamelFolder *folder) +local_search_by_expression(CamelFolder *folder, const char *expression, CamelException *ex) { CamelLocalFolder *local_folder = CAMEL_LOCAL_FOLDER(folder); + GPtrArray *summary, *matches; - return CAMEL_FOLDER_SUMMARY(local_folder->summary)->messages; -} - -/* get a single message info, by uid */ -static const CamelMessageInfo * -local_get_message_info(CamelFolder *folder, const char *uid) -{ - CamelLocalFolder *local_folder = CAMEL_LOCAL_FOLDER(folder); + /* NOTE: could get away without the search lock by creating a new + search object each time */ - return camel_folder_summary_uid(CAMEL_FOLDER_SUMMARY(local_folder->summary), uid); -} - -static GPtrArray * -local_search_by_expression(CamelFolder *folder, const char *expression, CamelException *ex) -{ - CamelLocalFolder *local_folder = CAMEL_LOCAL_FOLDER(folder); + CAMEL_LOCAL_FOLDER_LOCK(folder, search_lock); - if (local_folder->search == NULL) { + if (local_folder->search == NULL) local_folder->search = camel_folder_search_new(); - } camel_folder_search_set_folder(local_folder->search, folder); - if (local_folder->summary) { - /* FIXME: dont access summary array directly? */ - camel_folder_search_set_summary(local_folder->search, - CAMEL_FOLDER_SUMMARY(local_folder->summary)->messages); - } - camel_folder_search_set_body_index(local_folder->search, local_folder->index); + summary = camel_folder_get_summary(folder); + camel_folder_search_set_summary(local_folder->search, summary); - return camel_folder_search_execute_expression(local_folder->search, expression, ex); -} - -static void -local_search_free(CamelFolder *folder, GPtrArray * result) -{ - CamelLocalFolder *local_folder = CAMEL_LOCAL_FOLDER(folder); - - camel_folder_search_free_result(local_folder->search, result); -} - -static guint32 -local_get_message_flags(CamelFolder *folder, const char *uid) -{ - CamelMessageInfo *info; - CamelLocalFolder *mf = CAMEL_LOCAL_FOLDER(folder); - - info = camel_folder_summary_uid(CAMEL_FOLDER_SUMMARY(mf->summary), uid); - g_return_val_if_fail(info != NULL, 0); - - return info->flags; -} - -static void -local_set_message_flags(CamelFolder *folder, const char *uid, guint32 flags, guint32 set) -{ - CamelMessageInfo *info; - CamelLocalFolder *mf = CAMEL_LOCAL_FOLDER(folder); - guint32 new; - - info = camel_folder_summary_uid(CAMEL_FOLDER_SUMMARY(mf->summary), uid); - g_return_if_fail(info != NULL); - - new = (info->flags & ~flags) | (set & flags); - if (new == info->flags) - return; - - info->flags = new | CAMEL_MESSAGE_FOLDER_FLAGGED; - camel_folder_summary_touch(CAMEL_FOLDER_SUMMARY(mf->summary)); - - camel_object_trigger_event(CAMEL_OBJECT(folder), "message_changed", (char *) uid); -} + matches = camel_folder_search_execute_expression(local_folder->search, expression, ex); -static gboolean -local_get_message_user_flag(CamelFolder *folder, const char *uid, const char *name) -{ - CamelMessageInfo *info; - CamelLocalFolder *mf = CAMEL_LOCAL_FOLDER(folder); + CAMEL_LOCAL_FOLDER_UNLOCK(folder, search_lock); - info = camel_folder_summary_uid(CAMEL_FOLDER_SUMMARY(mf->summary), uid); - g_return_val_if_fail(info != NULL, FALSE); + camel_folder_free_summary(folder, summary); - return camel_flag_get(&info->user_flags, name); + return matches; } static void -local_set_message_user_flag(CamelFolder *folder, const char *uid, const char *name, gboolean value) -{ - CamelMessageInfo *info; - CamelLocalFolder *mf = CAMEL_LOCAL_FOLDER(folder); - - info = camel_folder_summary_uid(CAMEL_FOLDER_SUMMARY(mf->summary), uid); - g_return_if_fail(info != NULL); - - if (!camel_flag_set(&info->user_flags, name, value)) - return; - - info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED|CAMEL_MESSAGE_FOLDER_XEVCHANGE; - camel_folder_summary_touch(CAMEL_FOLDER_SUMMARY(mf->summary)); - camel_object_trigger_event(CAMEL_OBJECT(folder), "message_changed", (char *) uid); -} - -static const char * -local_get_message_user_tag(CamelFolder *folder, const char *uid, const char *name) -{ - CamelMessageInfo *info; - CamelLocalFolder *mf = CAMEL_LOCAL_FOLDER(folder); - - info = camel_folder_summary_uid(CAMEL_FOLDER_SUMMARY(mf->summary), uid); - g_return_val_if_fail(info != NULL, FALSE); - - return camel_tag_get(&info->user_tags, name); -} - -static void -local_set_message_user_tag(CamelFolder *folder, const char *uid, const char *name, const char *value) +local_search_free(CamelFolder *folder, GPtrArray * result) { - CamelMessageInfo *info; - CamelLocalFolder *mf = CAMEL_LOCAL_FOLDER(folder); + CamelLocalFolder *local_folder = CAMEL_LOCAL_FOLDER(folder); - info = camel_folder_summary_uid(CAMEL_FOLDER_SUMMARY(mf->summary), uid); - g_return_if_fail(info != NULL); + /* we need to lock this free because of the way search_free_result works */ + /* FIXME: put the lock inside search_free_result */ + CAMEL_LOCAL_FOLDER_LOCK(folder, search_lock); - if (!camel_tag_set(&info->user_tags, name, value)) - return; + camel_folder_search_free_result(local_folder->search, result); - info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED|CAMEL_MESSAGE_FOLDER_XEVCHANGE; - camel_folder_summary_touch(CAMEL_FOLDER_SUMMARY(mf->summary)); - camel_object_trigger_event(CAMEL_OBJECT(folder), "message_changed", (char *) uid); + CAMEL_LOCAL_FOLDER_UNLOCK(folder, search_lock); } - - diff --git a/camel/providers/local/camel-local-folder.h b/camel/providers/local/camel-local-folder.h index 4fa65420d4..7c975c8f7a 100644 --- a/camel/providers/local/camel-local-folder.h +++ b/camel/providers/local/camel-local-folder.h @@ -43,6 +43,7 @@ extern "C" { typedef struct { CamelFolder parent_object; + struct _CamelLocalFolderPrivate *priv; guint32 flags; /* open mode flags */ @@ -55,7 +56,6 @@ typedef struct { char *index_path; /* where the index file lives */ ibex *index; /* index for this folder */ - CamelLocalSummary *summary; CamelFolderSearch *search; /* used to run searches, we just use the real thing (tm) */ CamelFolderChangeInfo *changes; /* used to store changes to the folder during processing */ } CamelLocalFolder; diff --git a/camel/providers/local/camel-local-private.h b/camel/providers/local/camel-local-private.h new file mode 100644 index 0000000000..1d1a89ea27 --- /dev/null +++ b/camel/providers/local/camel-local-private.h @@ -0,0 +1,60 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * camel-local-private.h: Private info for local provider. + * + * Authors: Michael Zucchi <notzed@helixcode.com> + * + * Copyright 1999, 2000 Helix Code, Inc. (http://www.helixcode.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#ifndef CAMEL_PRIVATE_H +#define CAMEL_PRIVATE_H 1 + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus }*/ + +/* need a way to configure and save this data, if this header is to + be installed. For now, dont install it */ + +#include "config.h" + +#ifdef ENABLE_THREADS +#include <pthread.h> +#endif + +struct _CamelLocalFolderPrivate { +#ifdef ENABLE_THREADS + GMutex *search_lock; /* for locking the search object */ +#endif +}; + +#ifdef ENABLE_THREADS +#define CAMEL_LOCAL_FOLDER_LOCK(f, l) (g_mutex_lock(((CamelLocalFolder *)f)->priv->l)) +#define CAMEL_LOCAL_FOLDER_UNLOCK(f, l) (g_mutex_unlock(((CamelLocalFolder *)f)->priv->l)) +#else +#define CAMEL_LOCAL_FOLDER_LOCK(f, l) +#define CAMEL_LOCAL_FOLDER_UNLOCK(f, l) +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CAMEL_H */ + diff --git a/camel/providers/local/camel-local-summary.c b/camel/providers/local/camel-local-summary.c index 1377ff43a8..869b7c712a 100644 --- a/camel/providers/local/camel-local-summary.c +++ b/camel/providers/local/camel-local-summary.c @@ -42,19 +42,7 @@ struct _CamelLocalSummaryPrivate { #define _PRIVATE(o) (((CamelLocalSummary *)(o))->priv) -#if 0 -static int summary_header_load (CamelFolderSummary *, FILE *); -static int summary_header_save (CamelFolderSummary *, FILE *); -#endif - static CamelMessageInfo * message_info_new (CamelFolderSummary *, struct _header_raw *); -static CamelMessageInfo * message_info_new_from_parser (CamelFolderSummary *, CamelMimeParser *); -static CamelMessageInfo * message_info_new_from_message(CamelFolderSummary *s, CamelMimeMessage *msg); -#if 0 -static CamelMessageInfo * message_info_load (CamelFolderSummary *, FILE *); -static int message_info_save (CamelFolderSummary *, FILE *, CamelMessageInfo *); -#endif -/*static void message_info_free (CamelFolderSummary *, CamelMessageInfo *);*/ static int local_summary_decode_x_evolution(CamelLocalSummary *cls, const char *xev, CamelMessageInfo *mi); static char *local_summary_encode_x_evolution(CamelLocalSummary *cls, const CamelMessageInfo *mi); @@ -67,7 +55,6 @@ static CamelMessageInfo *local_summary_add(CamelLocalSummary *cls, CamelMimeMess static void camel_local_summary_class_init (CamelLocalSummaryClass *klass); static void camel_local_summary_init (CamelLocalSummary *obj); static void camel_local_summary_finalise (CamelObject *obj); - static CamelFolderSummaryClass *camel_local_summary_parent; CamelType @@ -95,16 +82,7 @@ camel_local_summary_class_init(CamelLocalSummaryClass *klass) camel_local_summary_parent = CAMEL_FOLDER_SUMMARY_CLASS(camel_type_get_global_classfuncs(camel_folder_summary_get_type())); - /*sklass->summary_header_load = summary_header_load; - sklass->summary_header_save = summary_header_save;*/ - sklass->message_info_new = message_info_new; - sklass->message_info_new_from_parser = message_info_new_from_parser; - sklass->message_info_new_from_message = message_info_new_from_message; - - /*sklass->message_info_load = message_info_load; - sklass->message_info_save = message_info_save;*/ - /*sklass->message_info_free = message_info_free;*/ klass->load = local_summary_load; klass->check = local_summary_check; @@ -288,6 +266,7 @@ camel_local_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changei for (i=0;i<camel_folder_summary_count(s);i++) { CamelMessageInfo *info = camel_folder_summary_index(s, i); do_stat_mi(cls, &stats, info); + camel_folder_summary_info_free(s, info); } printf("\nMemory used by summary:\n\n"); @@ -372,57 +351,10 @@ camel_local_summary_write_headers(int fd, struct _header_raw *header, char *xevl return outlen; } -#if 0 -static int -summary_header_load(CamelFolderSummary *s, FILE *in) -{ - CamelLocalSummary *mbs = CAMEL_LOCAL_SUMMARY(s); - - if (((CamelFolderSummaryClass *)camel_local_summary_parent)->summary_header_load(s, in) == -1) - return -1; - - return camel_folder_summary_decode_uint32(in, &mbs->folder_size); -} - -static int -summary_header_save(CamelFolderSummary *s, FILE *out) -{ - CamelLocalSummary *mbs = CAMEL_LOCAL_SUMMARY(s); - - if (((CamelFolderSummaryClass *)camel_local_summary_parent)->summary_header_save(s, out) == -1) - return -1; - - return camel_folder_summary_encode_uint32(out, mbs->folder_size); -} - -static int -header_evolution_decode(const char *in, guint32 *uid, guint32 *flags) -{ - char *header; - - if (in && (header = header_token_decode(in))) { - if (strlen (header) == strlen ("00000000-0000") - && sscanf (header, "%08x-%04x", uid, flags) == 2) { - g_free(header); - return *uid; - } - g_free(header); - } - - return -1; -} - -static char * -header_evolution_encode(guint32 uid, guint32 flags) -{ - return g_strdup_printf("%08x-%04x", uid, flags & 0xffff); -} -#endif - static int local_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changeinfo, CamelException *ex) { - /* FIXME: sync index here */ + /* FIXME: sync index here ? */ return 0; } @@ -623,90 +555,3 @@ message_info_new(CamelFolderSummary *s, struct _header_raw *h) return mi; } - -static CamelMessageInfo * message_info_new_from_message(CamelFolderSummary *s, CamelMimeMessage *msg) -{ - CamelMessageInfo *mi; - /*CamelLocalSummary *cls = (CamelLocalSummary *)s;*/ - - mi = ((CamelFolderSummaryClass *)camel_local_summary_parent)->message_info_new_from_message(s, msg); -#if 0 - if (mi) { - - if (mi->uid == NULL) { - d(printf("no uid assigned yet, assigning one\n")); - mi->uid = camel_folder_summary_next_uid_string(s); - mi->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED | CAMEL_MESSAGE_FOLDER_NOXEV; - } - - if (cls->index - && (cls->index_force - || !ibex_contains_name(cls->index, mi->uid))) { - d(printf("Am indexing message %s\n", mi->uid)); - camel_folder_summary_set_index(s, cls->index); - } else { - d(printf("Not indexing message %s\n", mi->uid)); - camel_folder_summary_set_index(s, NULL); - } - } -#endif - return mi; -} - -static CamelMessageInfo * -message_info_new_from_parser(CamelFolderSummary *s, CamelMimeParser *mp) -{ - CamelMessageInfo *mi; - /*CamelLocalSummary *mbs = CAMEL_LOCAL_SUMMARY(s);*/ - - mi = ((CamelFolderSummaryClass *)camel_local_summary_parent)->message_info_new_from_parser(s, mp); - if (mi) { -#if 0 - /* do we want to index this message as we add it, as well? */ - if (mbs->index - && (mbs->index_force - || !ibex_contains_name(mbs->index, mi->uid))) { - d(printf("Am indexing message %s\n", mi->uid)); - camel_folder_summary_set_index(s, mbs->index); - } else { - d(printf("Not indexing message %s\n", mi->uid)); - camel_folder_summary_set_index(s, NULL); - } -#endif - } - - return mi; -} - -#if 0 -static CamelMessageInfo * -message_info_load(CamelFolderSummary *s, FILE *in) -{ - CamelMessageInfo *mi; - - io(printf("loading local message info\n")); - - mi = ((CamelFolderSummaryClass *)camel_local_summary_parent)->message_info_load(s, in); - if (mi) { - guint32 position; - CamelLocalMessageInfo *mbi = (CamelLocalMessageInfo *)mi; - - camel_folder_summary_decode_uint32(in, &position); - mbi->frompos = position; - } - - return mi; -} - -static int -message_info_save(CamelFolderSummary *s, FILE *out, CamelMessageInfo *mi) -{ - CamelLocalMessageInfo *mbi = (CamelLocalMessageInfo *)mi; - - io(printf("saving local message info\n")); - - ((CamelFolderSummaryClass *)camel_local_summary_parent)->message_info_save(s, out, mi); - - return camel_folder_summary_encode_uint32(out, mbi->frompos); -} -#endif diff --git a/camel/providers/local/camel-maildir-folder.c b/camel/providers/local/camel-maildir-folder.c index d4771db5b8..c4d347d083 100644 --- a/camel/providers/local/camel-maildir-folder.c +++ b/camel/providers/local/camel-maildir-folder.c @@ -131,7 +131,7 @@ static void maildir_append_message(CamelFolder * folder, CamelMimeMessage * mess d(printf("Appending message\n")); /* add it to the summary/assign the uid, etc */ - mi = camel_local_summary_add(lf->summary, message, info, lf->changes, ex); + mi = camel_local_summary_add((CamelLocalSummary *)folder->summary, message, info, lf->changes, ex); if (camel_exception_is_set(ex)) { return; } @@ -191,7 +191,7 @@ static CamelMimeMessage *maildir_get_message(CamelFolder * folder, const gchar * d(printf("getting message: %s\n", uid)); /* get the message summary info */ - if ((info = camel_folder_summary_uid((CamelFolderSummary *)lf->summary, uid)) == NULL) { + if ((info = camel_folder_summary_uid(folder->summary, uid)) == NULL) { camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID, _("Cannot get message: %s\n %s"), uid, _("No such message")); return NULL; } @@ -200,6 +200,9 @@ static CamelMimeMessage *maildir_get_message(CamelFolder * folder, const gchar * /* what do we do if the message flags (and :info data) changes? filename mismatch - need to recheck I guess */ name = g_strdup_printf("%s/cur/%s", lf->folder_path, camel_maildir_info_filename(mdi)); + + camel_folder_summary_info_free(folder->summary, info); + if ((message_stream = camel_stream_fs_new_with_name(name, O_RDONLY, 0)) == NULL) { camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID, _("Cannot get message: %s\n %s"), name, g_strerror(errno)); diff --git a/camel/providers/local/camel-maildir-summary.c b/camel/providers/local/camel-maildir-summary.c index 8d421d5fdd..a65adafc0f 100644 --- a/camel/providers/local/camel-maildir-summary.c +++ b/camel/providers/local/camel-maildir-summary.c @@ -33,6 +33,7 @@ #include <ctype.h> +#include "camel-private.h" #include "e-util/e-memory.h" #define d(x) /*(printf("%s(%d): ", __FILE__, __LINE__),(x))*/ @@ -474,6 +475,7 @@ remove_summary(char *key, CamelMessageInfo *info, CamelLocalSummary *cls) if (cls->index) ibex_unindex(cls->index, (char *)camel_message_info_uid(info)); camel_folder_summary_remove((CamelFolderSummary *)cls, info); + camel_folder_summary_info_free((CamelFolderSummary *)cls, info); } static int @@ -551,33 +553,47 @@ maildir_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changes, Ca if (info == NULL || (cls->index && (!ibex_contains_name(cls->index, uid)))) { /* need to add this file to the summary */ if (info != NULL) { - g_hash_table_remove(left, uid); + CamelMessageInfo *old = g_hash_table_lookup(left, camel_message_info_uid(info)); + if (old) { + g_hash_table_remove(left, uid); + camel_folder_summary_info_free((CamelFolderSummary *)cls, old); + } camel_folder_summary_remove((CamelFolderSummary *)cls, info); + camel_folder_summary_info_free((CamelFolderSummary *)cls, info); } camel_maildir_summary_add(cls, d->d_name, forceindex); } else { const char *filename; + CamelMessageInfo *old; - g_hash_table_remove(left, camel_message_info_uid(info)); + old = g_hash_table_lookup(left, camel_message_info_uid(info)); + if (old) { + camel_folder_summary_info_free((CamelFolderSummary *)cls, old); + g_hash_table_remove(left, camel_message_info_uid(info)); + } mdi = (CamelMaildirMessageInfo *)info; filename = camel_maildir_info_filename(mdi); /* TODO: only store the extension in the mdi->filename struct, not the whole lot */ if (filename == NULL || strcmp(filename, d->d_name) != 0) { #ifdef DOESTRV +#warning "cannot modify the estrv after its been setup, for mt-safe code" d(printf("filename changed: %s to %s\n", filename, d->d_name)); /* need to update the summary hash string reference since it might (will) change */ + CAMEL_SUMMARY_LOCK(s, summary_lock); g_hash_table_remove(s->messages_uid, uid); info->strings = e_strv_set_ref(info->strings, CAMEL_MAILDIR_INFO_FILENAME, d->d_name); /* we need to re-pack as well */ info->strings = e_strv_pack(info->strings); g_hash_table_insert(s->messages_uid, (char *)camel_message_info_uid(info), info); + CAMEL_SUMMARY_UNLOCK(s, summary_lock); #else g_free(mdi->filename); mdi->filename = g_strdup(d->d_name); #endif } + camel_folder_summary_info_free((CamelFolderSummary *)cls, info); } g_free(uid); } @@ -597,9 +613,10 @@ maildir_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changes, Ca continue; /* already in summary? shouldn't happen, but just incase ... */ - if (camel_folder_summary_uid((CamelFolderSummary *)cls, name)) + if ((info = camel_folder_summary_uid((CamelFolderSummary *)cls, name))) { + camel_folder_summary_info_free((CamelFolderSummary *)cls, info); newname = destname = camel_folder_summary_next_uid_string(s); - else { + } else { newname = NULL; destname = name; } @@ -633,7 +650,9 @@ maildir_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changes, Ca g_free(cur); /* sort the summary based on receive time, since the directory order is not useful */ + CAMEL_SUMMARY_LOCK(s, summary_lock); qsort(s->messages->pdata, s->messages->len, sizeof(CamelMessageInfo *), sort_receive_cmp); + CAMEL_SUMMARY_UNLOCK(s, summary_lock); /* FIXME: move this up a class? */ @@ -699,12 +718,18 @@ maildir_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChange /* we'll assume it didn't work, but dont change anything else */ g_free(newname); } else { + /* TODO: If this is made mt-safe, then this code could be a problem, since + the estrv is being modified. + Sigh, this may mean the maildir name has to be cached another way */ #ifdef DOESTRV +#warning "cannot modify the estrv after its been setup, for mt-safe code" + CAMEL_SUMMARY_LOCK(s, summary_lock); /* need to update the summary hash ref */ g_hash_table_remove(s->messages_uid, camel_message_info_uid(info)); info->strings = e_strv_set_ref_free(info->strings, CAMEL_MAILDIR_INFO_FILENAME, newname); info->strings = e_strv_pack(info->strings); g_hash_table_insert(s->messages_uid, (char *)camel_message_info_uid(info), info); + CAMEL_SUMMARY_UNLOCK(s, summary_lock); #else g_free(mdi->filename); mdi->filename = newname; @@ -719,6 +744,7 @@ maildir_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChange /* strip FOLDER_MESSAGE_FLAGED, etc */ info->flags &= 0xffff; } + camel_folder_summary_info_free((CamelFolderSummary *)cls, info); } return 0; } diff --git a/camel/providers/local/camel-mbox-folder.c b/camel/providers/local/camel-mbox-folder.c index 41fb5865b5..14dca0049e 100644 --- a/camel/providers/local/camel-mbox-folder.c +++ b/camel/providers/local/camel-mbox-folder.c @@ -54,6 +54,9 @@ static CamelLocalFolderClass *parent_class = NULL; static int mbox_lock(CamelLocalFolder *lf, CamelLockType type, CamelException *ex); static void mbox_unlock(CamelLocalFolder *lf); +static void mbox_set_message_user_flag(CamelFolder *folder, const char *uid, const char *name, gboolean value); +static void mbox_set_message_user_tag(CamelFolder *folder, const char *uid, const char *name, const char *value); + static void mbox_append_message(CamelFolder *folder, CamelMimeMessage * message, const CamelMessageInfo * info, CamelException *ex); static CamelMimeMessage *mbox_get_message(CamelFolder *folder, const gchar * uid, CamelException *ex); static CamelLocalSummary *mbox_create_summary(const char *path, const char *folder, ibex *index); @@ -74,6 +77,9 @@ camel_mbox_folder_class_init(CamelMboxFolderClass * camel_mbox_folder_class) camel_folder_class->append_message = mbox_append_message; camel_folder_class->get_message = mbox_get_message; + camel_folder_class->set_message_user_flag = mbox_set_message_user_flag; + camel_folder_class->set_message_user_tag = mbox_set_message_user_tag; + lclass->create_summary = mbox_create_summary; lclass->lock = mbox_lock; lclass->unlock = mbox_unlock; @@ -164,7 +170,7 @@ mbox_append_message(CamelFolder *folder, CamelMimeMessage * message, const Camel CamelLocalFolder *lf = (CamelLocalFolder *)folder; CamelStream *output_stream = NULL, *filter_stream = NULL; CamelMimeFilter *filter_from = NULL; - CamelMboxSummary *mbs = (CamelMboxSummary *)lf->summary; + CamelMboxSummary *mbs = (CamelMboxSummary *)folder->summary; CamelMessageInfo *mi; char *fromline = NULL; int fd; @@ -179,12 +185,12 @@ mbox_append_message(CamelFolder *folder, CamelMimeMessage * message, const Camel d(printf("Appending message\n")); /* first, check the summary is correct (updates folder_size too) */ - camel_local_summary_check(lf->summary, lf->changes, ex); + camel_local_summary_check((CamelLocalSummary *)folder->summary, lf->changes, ex); if (camel_exception_is_set(ex)) goto fail; /* add it to the summary/assign the uid, etc */ - mi = camel_local_summary_add(lf->summary, message, info, lf->changes, ex); + mi = camel_local_summary_add((CamelLocalSummary *)folder->summary, message, info, lf->changes, ex); if (camel_exception_is_set(ex)) goto fail; @@ -199,7 +205,7 @@ mbox_append_message(CamelFolder *folder, CamelMimeMessage * message, const Camel /* and we need to set the frompos/XEV explicitly */ ((CamelMboxMessageInfo *)mi)->frompos = mbs->folder_size?mbs->folder_size+1:0; #if 0 - xev = camel_local_summary_encode_x_evolution(lf->summary, mi); + xev = camel_local_summary_encode_x_evolution((CamelLocalSummary *)folder->summary, mi); if (xev) { /* the x-ev header should match the 'current' flags, no problem, so store as much */ camel_medium_set_header((CamelMedium *)message, "X-Evolution", xev); @@ -304,7 +310,7 @@ mbox_get_message(CamelFolder *folder, const gchar * uid, CamelException *ex) retry: /* get the message summary info */ - info = (CamelMboxMessageInfo *) camel_folder_summary_uid((CamelFolderSummary *)lf->summary, uid); + info = (CamelMboxMessageInfo *) camel_folder_summary_uid(folder->summary, uid); if (info == NULL) { camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID, @@ -327,6 +333,7 @@ retry: _("Cannot get message: %s from folder %s\n %s"), uid, lf->folder_path, strerror(errno)); camel_local_folder_unlock(lf); + camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)info); return NULL; } @@ -345,10 +352,11 @@ retry: camel_mime_parser_state(parser)); camel_object_unref((CamelObject *)parser); + camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)info); if (!retried) { retried = TRUE; - camel_local_summary_check(lf->summary, lf->changes, ex); + camel_local_summary_check((CamelLocalSummary *)folder->summary, lf->changes, ex); if (!camel_exception_is_set(ex)) goto retry; } @@ -360,6 +368,8 @@ retry: camel_local_folder_unlock(lf); return NULL; } + + camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)info); message = camel_mime_message_new(); if (camel_mime_part_construct_from_parser((CamelMimePart *)message, parser) == -1) { @@ -386,3 +396,39 @@ retry: return message; } + +static void +mbox_set_message_user_flag(CamelFolder *folder, const char *uid, const char *name, gboolean value) +{ + CamelMessageInfo *info; + + g_return_if_fail(folder->summary != NULL); + + info = camel_folder_summary_uid(folder->summary, uid); + g_return_if_fail(info != NULL); + + if (camel_flag_set(&info->user_flags, name, value)) { + info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED|CAMEL_MESSAGE_FOLDER_XEVCHANGE; + camel_folder_summary_touch(folder->summary); + camel_object_trigger_event(CAMEL_OBJECT(folder), "message_changed", (char *) uid); + } + camel_folder_summary_info_free(folder->summary, info); +} + +static void +mbox_set_message_user_tag(CamelFolder *folder, const char *uid, const char *name, const char *value) +{ + CamelMessageInfo *info; + + g_return_if_fail(folder->summary != NULL); + + info = camel_folder_summary_uid(folder->summary, uid); + g_return_if_fail(info != NULL); + + if (camel_tag_set(&info->user_tags, name, value)) { + info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED|CAMEL_MESSAGE_FOLDER_XEVCHANGE; + camel_folder_summary_touch(folder->summary); + camel_object_trigger_event(CAMEL_OBJECT(folder), "message_changed", (char *) uid); + } + camel_folder_summary_info_free(folder->summary, info); +} diff --git a/camel/providers/local/camel-mbox-summary.c b/camel/providers/local/camel-mbox-summary.c index 2dde67b70c..5b62743857 100644 --- a/camel/providers/local/camel-mbox-summary.c +++ b/camel/providers/local/camel-mbox-summary.c @@ -306,6 +306,7 @@ summary_update(CamelLocalSummary *cls, off_t offset, CamelFolderChangeInfo *chan CamelMessageInfo *mi = camel_folder_summary_index(s, i); camel_folder_change_info_add_source(changeinfo, camel_message_info_uid(mi)); + camel_folder_summary_info_free(s, mi); } } @@ -318,6 +319,7 @@ summary_update(CamelLocalSummary *cls, off_t offset, CamelFolderChangeInfo *chan for (i = 0; i < count; i++) { CamelMessageInfo *mi = camel_folder_summary_index(s, i); camel_folder_change_info_add_update(changeinfo, camel_message_info_uid(mi)); + camel_folder_summary_info_free(s, mi); } camel_folder_change_info_build_diff(changeinfo); } @@ -459,7 +461,7 @@ mbox_summary_sync_full(CamelLocalSummary *cls, gboolean expunge, CamelFolderChan CamelFolderSummary *s = (CamelFolderSummary *)mbs; CamelMimeParser *mp = NULL; int i, count; - CamelMboxMessageInfo *info; + CamelMboxMessageInfo *info = NULL; int fd = -1, fdout = -1; char *tmpname = NULL; char *buffer, *xevnew = NULL; @@ -533,6 +535,7 @@ mbox_summary_sync_full(CamelLocalSummary *cls, gboolean expunge, CamelFolderChan /* remove it from the change list */ camel_folder_change_info_remove_uid(changeinfo, uid); camel_folder_summary_remove(s, (CamelMessageInfo *)info); + camel_folder_summary_info_free(s, (CamelMessageInfo *)info); count--; i--; info = NULL; @@ -584,6 +587,8 @@ mbox_summary_sync_full(CamelLocalSummary *cls, gboolean expunge, CamelFolderChan d(printf("we are now at %d, from = %d\n", (int)camel_mime_parser_tell(mp), (int)camel_mime_parser_tell_start_from(mp))); camel_mime_parser_unstep(mp); + camel_folder_summary_info_free(s, (CamelMessageInfo *)info); + info = NULL; } } @@ -634,6 +639,8 @@ mbox_summary_sync_full(CamelLocalSummary *cls, gboolean expunge, CamelFolderChan unlink(tmpname); if (mp) camel_object_unref((CamelObject *)mp); + if (info) + camel_folder_summary_info_free(s, (CamelMessageInfo *)info); return -1; } @@ -646,7 +653,7 @@ mbox_summary_sync_quick(CamelLocalSummary *cls, gboolean expunge, CamelFolderCha CamelFolderSummary *s = (CamelFolderSummary *)mbs; CamelMimeParser *mp = NULL; int i, count; - CamelMboxMessageInfo *info; + CamelMboxMessageInfo *info = NULL; int fd = -1; char *xevnew, *xevtmp; const char *xev; @@ -678,8 +685,11 @@ mbox_summary_sync_quick(CamelLocalSummary *cls, gboolean expunge, CamelFolderCha d(printf("Checking message %s %08x\n", info->info.uid, info->info.flags)); - if ((info->info.flags & CAMEL_MESSAGE_FOLDER_FLAGGED) == 0) + if ((info->info.flags & CAMEL_MESSAGE_FOLDER_FLAGGED) == 0) { + camel_folder_summary_info_free(s, (CamelMessageInfo *)info); + info = NULL; continue; + } d(printf("Updating message %s\n", info->info.uid)); @@ -738,6 +748,7 @@ mbox_summary_sync_quick(CamelLocalSummary *cls, gboolean expunge, CamelFolderCha camel_mime_parser_drop_step(mp); info->info.flags &= 0xffff; + camel_folder_summary_info_free(s, (CamelMessageInfo *)info); } d(printf("Closing folders\n")); @@ -757,9 +768,10 @@ mbox_summary_sync_quick(CamelLocalSummary *cls, gboolean expunge, CamelFolderCha error: if (fd != -1) close(fd); - if (mp) camel_object_unref((CamelObject *)mp); + if (info) + camel_folder_summary_info_free(s, (CamelMessageInfo *)info); return -1; } @@ -792,6 +804,7 @@ mbox_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInf quick = FALSE; else work |= (info->flags & CAMEL_MESSAGE_FOLDER_FLAGGED) != 0; + camel_folder_summary_info_free(s, info); } /* yuck i hate this logic, but its to simplify the 'all ok, update summary' and failover cases */ diff --git a/camel/providers/local/camel-mh-folder.c b/camel/providers/local/camel-mh-folder.c index 063e7e939e..fec620edea 100644 --- a/camel/providers/local/camel-mh-folder.c +++ b/camel/providers/local/camel-mh-folder.c @@ -132,7 +132,7 @@ static void mh_append_message(CamelFolder * folder, CamelMimeMessage * message, d(printf("Appending message\n")); /* add it to the summary/assign the uid, etc */ - mi = camel_local_summary_add(lf->summary, message, info, lf->changes, ex); + mi = camel_local_summary_add((CamelLocalSummary *)folder->summary, message, info, lf->changes, ex); if (camel_exception_is_set(ex)) { return; } @@ -179,11 +179,14 @@ static CamelMimeMessage *mh_get_message(CamelFolder * folder, const gchar * uid, d(printf("getting message: %s\n", uid)); /* get the message summary info */ - if ((info = camel_folder_summary_uid((CamelFolderSummary *)lf->summary, uid)) == NULL) { + if ((info = camel_folder_summary_uid(folder->summary, uid)) == NULL) { camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID, _("Cannot get message: %s\n %s"), uid, _("No such message")); return NULL; } + /* we only need it to check the message exists */ + camel_folder_summary_info_free(folder->summary, info); + name = g_strdup_printf("%s/%s", lf->folder_path, uid); if ((message_stream = camel_stream_fs_new_with_name(name, O_RDONLY, 0)) == NULL) { camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID, _("Cannot get message: %s\n %s"), diff --git a/camel/providers/local/camel-mh-summary.c b/camel/providers/local/camel-mh-summary.c index e5f37d879a..c933626653 100644 --- a/camel/providers/local/camel-mh-summary.c +++ b/camel/providers/local/camel-mh-summary.c @@ -37,8 +37,6 @@ #define CAMEL_MH_SUMMARY_VERSION (0x2000) -static CamelMessageInfo *message_info_new(CamelFolderSummary *, struct _header_raw *); - static int mh_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changeinfo, CamelException *ex); static int mh_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex); /*static int mh_summary_add(CamelLocalSummary *cls, CamelMimeMessage *msg, CamelMessageInfo *info, CamelFolderChangeInfo *, CamelException *ex);*/ @@ -84,7 +82,6 @@ camel_mh_summary_class_init (CamelMhSummaryClass *class) parent_class = (CamelLocalSummaryClass *)camel_type_get_global_classfuncs(camel_local_summary_get_type ()); /* override methods */ - sklass->message_info_new = message_info_new; sklass->next_uid_string = mh_summary_next_uid_string; lklass->check = mh_summary_check; @@ -125,20 +122,6 @@ CamelMhSummary *camel_mh_summary_new (const char *filename, const char *mhdir, i return o; } -static CamelMessageInfo *message_info_new(CamelFolderSummary * s, struct _header_raw *h) -{ - CamelMessageInfo *mi; - /*CamelMhSummary *mhs = (CamelMhSummary *)s;*/ - - mi = ((CamelFolderSummaryClass *) parent_class)->message_info_new(s, h); - /* hmm, this isn't quite right */ - if (mi) { - /*mi->uid = mh_summary_next_uid_string(s);*/ - } - - return mi; -} - static char *mh_summary_next_uid_string(CamelFolderSummary *s) { CamelMhSummary *mhs = (CamelMhSummary *)s; @@ -206,6 +189,7 @@ remove_summary(char *key, CamelMessageInfo *info, CamelLocalSummary *cls) if (cls->index) ibex_unindex(cls->index, (char *)camel_message_info_uid(info)); camel_folder_summary_remove((CamelFolderSummary *)cls, info); + camel_folder_summary_info_free((CamelFolderSummary *)cls, info); } static int @@ -256,11 +240,19 @@ mh_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changeinfo, Came if (info != NULL) { g_hash_table_remove(left, camel_message_info_uid(info)); camel_folder_summary_remove((CamelFolderSummary *)cls, info); + camel_folder_summary_info_free((CamelFolderSummary *)cls, info); } camel_mh_summary_add(cls, d->d_name, forceindex); } else { - g_hash_table_remove(left, camel_message_info_uid(info)); - } + const char *uid = camel_message_info_uid(info); + CamelMessageInfo *old = g_hash_table_lookup(left, uid); + + if (old) { + camel_folder_summary_info_free((CamelFolderSummary *)cls, old); + g_hash_table_remove(left, uid); + } + camel_folder_summary_info_free((CamelFolderSummary *)cls, info); + } } } closedir(dir); @@ -394,6 +386,7 @@ mh_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInfo g_warning("Problem occured when trying to expunge, ignored"); } } + camel_folder_summary_info_free((CamelFolderSummary *)cls, info); } return 0; diff --git a/camel/providers/nntp/camel-nntp-folder.c b/camel/providers/nntp/camel-nntp-folder.c index f918317502..7edc4a98fd 100644 --- a/camel/providers/nntp/camel-nntp-folder.c +++ b/camel/providers/nntp/camel-nntp-folder.c @@ -63,7 +63,7 @@ nntp_folder_sync (CamelFolder *folder, gboolean expunge, { CamelNNTPStore *store; - camel_folder_summary_save (CAMEL_NNTP_FOLDER(folder)->summary); + camel_folder_summary_save (folder->summary); store = CAMEL_NNTP_STORE (camel_folder_get_parent_store (folder)); @@ -71,39 +71,12 @@ nntp_folder_sync (CamelFolder *folder, gboolean expunge, camel_nntp_newsrc_write (store->newsrc); } -static gint -nntp_folder_get_message_count (CamelFolder *folder) -{ - CamelNNTPFolder *nntp_folder = CAMEL_NNTP_FOLDER(folder); - - g_assert (folder); - g_assert (nntp_folder->summary); - - return camel_folder_summary_count(nntp_folder->summary); -} - -static guint32 -nntp_folder_get_message_flags (CamelFolder *folder, const char *uid) -{ - CamelNNTPFolder *nntp_folder = CAMEL_NNTP_FOLDER (folder); - CamelMessageInfo *info = camel_folder_summary_uid (nntp_folder->summary, uid); - - return info->flags; -} - static void nntp_folder_set_message_flags (CamelFolder *folder, const char *uid, guint32 flags, guint32 set) { - CamelNNTPFolder *nntp_folder = CAMEL_NNTP_FOLDER (folder); - CamelMessageInfo *info = camel_folder_summary_uid (nntp_folder->summary, uid); - guint32 new; - - new = (info->flags & ~flags) | (set & flags); - if (new == info->flags) - return; + ((CamelFolderClass *)parent_class)->set_message_flags(folder, uid, flags, set); - info->flags = new; if (flags & set & CAMEL_MESSAGE_SEEN) { int article_num; CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE (camel_folder_get_parent_store (folder)); @@ -114,8 +87,6 @@ nntp_folder_set_message_flags (CamelFolder *folder, const char *uid, folder->name, article_num); } - - camel_folder_summary_touch (nntp_folder->summary); } static CamelMimeMessage * @@ -151,6 +122,9 @@ nntp_folder_get_message (CamelFolder *folder, const gchar *uid, CamelException * return NULL; } + /* this could probably done fairly easily with an nntp stream that + returns eof after '.' */ + /* XXX ick ick ick. read the entire message into a buffer and then create a stream_mem for it. */ buf_alloc = 2048; @@ -206,34 +180,6 @@ nntp_folder_get_message (CamelFolder *folder, const gchar *uid, CamelException * return message; } -static GPtrArray * -nntp_folder_get_uids (CamelFolder *folder) -{ - CamelNNTPFolder *nntp_folder = CAMEL_NNTP_FOLDER (folder); - GPtrArray *out; - CamelMessageInfo *message_info; - int i; - int count = camel_folder_summary_count (nntp_folder->summary); - - out = g_ptr_array_new (); - g_ptr_array_set_size (out, count); - - for (i = 0; i < count; i++) { - message_info = camel_folder_summary_index (nntp_folder->summary, i); - out->pdata[i] = g_strdup (camel_message_info_uid(message_info)); - } - - return out; -} - -static GPtrArray * -nntp_folder_get_summary (CamelFolder *folder) -{ - CamelNNTPFolder *nntp_folder = CAMEL_NNTP_FOLDER (folder); - - return nntp_folder->summary->messages; -} - static GPtrArray* nntp_folder_search_by_expression (CamelFolder *folder, const char *expression, CamelException *ex) { @@ -241,14 +187,6 @@ nntp_folder_search_by_expression (CamelFolder *folder, const char *expression, C return NULL; } -static const CamelMessageInfo* -nntp_folder_get_message_info (CamelFolder *folder, const char *uid) -{ - CamelNNTPFolder *nntp_folder = CAMEL_NNTP_FOLDER (folder); - - return camel_folder_summary_uid (nntp_folder->summary, uid); -} - static void nntp_folder_finalize (CamelObject *object) { @@ -268,16 +206,9 @@ camel_nntp_folder_class_init (CamelNNTPFolderClass *camel_nntp_folder_class) /* virtual method overload */ camel_folder_class->sync = nntp_folder_sync; - camel_folder_class->get_message_count = nntp_folder_get_message_count; camel_folder_class->set_message_flags = nntp_folder_set_message_flags; - camel_folder_class->get_message_flags = nntp_folder_get_message_flags; camel_folder_class->get_message = nntp_folder_get_message; - camel_folder_class->get_uids = nntp_folder_get_uids; - camel_folder_class->free_uids = camel_folder_free_deep; - camel_folder_class->get_summary = nntp_folder_get_summary; - camel_folder_class->free_summary = camel_folder_free_nop; camel_folder_class->search_by_expression = nntp_folder_search_by_expression; - camel_folder_class->get_message_info = nntp_folder_get_message_info; } CamelType @@ -313,11 +244,11 @@ camel_nntp_folder_new (CamelStore *parent, const char *folder_name, CamelExcepti root_dir_path, folder->name); - nntp_folder->summary = camel_folder_summary_new (); - camel_folder_summary_set_filename (nntp_folder->summary, + folder->summary = camel_folder_summary_new (); + camel_folder_summary_set_filename (folder->summary, nntp_folder->summary_file_path); - if (-1 == camel_folder_summary_load (nntp_folder->summary)) { + if (-1 == camel_folder_summary_load (folder->summary)) { /* Bad or nonexistant summary file */ camel_nntp_get_headers (CAMEL_FOLDER( folder )->parent_store, nntp_folder, ex); @@ -327,7 +258,7 @@ camel_nntp_folder_new (CamelStore *parent, const char *folder_name, CamelExcepti } /* XXX check return value */ - camel_folder_summary_save (nntp_folder->summary); + camel_folder_summary_save (folder->summary); } return folder; diff --git a/camel/providers/nntp/camel-nntp-newsrc.c b/camel/providers/nntp/camel-nntp-newsrc.c index b474dd3a40..d0b56222cf 100644 --- a/camel/providers/nntp/camel-nntp-newsrc.c +++ b/camel/providers/nntp/camel-nntp-newsrc.c @@ -32,6 +32,16 @@ #include "camel-nntp-newsrc.h" #include <camel/camel-folder-summary.h> +#ifdef ENABLE_THREADS +#include <pthread.h> + +#define NEWSRC_LOCK(f, l) (g_mutex_lock(((CamelNNTPNewsrc *)f)->l)) +#define NEWSRC_UNLOCK(f, l) (g_mutex_unlock(((CamelNNTPNewsrc *)f)->l)) +#else +#define NEWSRC_LOCK(f, l) +#define NEWSRC_UNLOCK(f, l) +#endif + typedef struct { guint low; guint high; @@ -47,8 +57,12 @@ struct CamelNNTPNewsrc { gchar *filename; GHashTable *groups; gboolean dirty; +#ifdef ENABLE_THREADS + GMutex *lock; +#endif } ; + static NewsrcGroup * camel_nntp_newsrc_group_add (CamelNNTPNewsrc *newsrc, const char *group_name, gboolean subscribed) { @@ -180,26 +194,40 @@ int camel_nntp_newsrc_get_highest_article_read (CamelNNTPNewsrc *newsrc, const char *group_name) { NewsrcGroup *group; + int ret; + + NEWSRC_LOCK(newsrc, lock); group = g_hash_table_lookup (newsrc->groups, group_name); + ret = camel_nntp_newsrc_group_get_highest_article_read (newsrc, group); + + NEWSRC_UNLOCK(newsrc, lock); - return camel_nntp_newsrc_group_get_highest_article_read (newsrc, group); + return ret; } int camel_nntp_newsrc_get_num_articles_read (CamelNNTPNewsrc *newsrc, const char *group_name) { NewsrcGroup *group; + int ret; + + NEWSRC_LOCK(newsrc, lock); group = g_hash_table_lookup (newsrc->groups, group_name); + ret = camel_nntp_newsrc_group_get_num_articles_read (newsrc, group); + + NEWSRC_UNLOCK(newsrc, lock); - return camel_nntp_newsrc_group_get_num_articles_read (newsrc, group); + return ret; } void camel_nntp_newsrc_mark_article_read (CamelNNTPNewsrc *newsrc, const char *group_name, int num) { + NEWSRC_LOCK(newsrc, lock); camel_nntp_newsrc_mark_range_read (newsrc, group_name, num, num); + NEWSRC_UNLOCK(newsrc, lock); } void @@ -216,9 +244,11 @@ camel_nntp_newsrc_mark_range_read(CamelNNTPNewsrc *newsrc, const char *group_nam low = tmp; } + NEWSRC_LOCK(newsrc, lock); group = g_hash_table_lookup (newsrc->groups, group_name); camel_nntp_newsrc_group_mark_range_read (newsrc, group, low, high); + NEWSRC_UNLOCK(newsrc, lock); } gboolean @@ -226,36 +256,51 @@ camel_nntp_newsrc_article_is_read (CamelNNTPNewsrc *newsrc, const char *group_na { int i; NewsrcGroup *group; + int ret = FALSE; + NEWSRC_LOCK(newsrc, lock); group = g_hash_table_lookup (newsrc->groups, group_name); for (i = 0; i < group->ranges->len; i++) { if (num >= g_array_index (group->ranges, ArticleRange, i).low && num <= g_array_index (group->ranges, ArticleRange, i).high) { - return TRUE; + ret = TRUE; + break; } } + NEWSRC_UNLOCK(newsrc, lock); + return FALSE; } gboolean camel_nntp_newsrc_group_is_subscribed (CamelNNTPNewsrc *newsrc, const char *group_name) { - NewsrcGroup *group = g_hash_table_lookup (newsrc->groups, group_name); + NewsrcGroup *group; + int ret = FALSE; + + NEWSRC_LOCK(newsrc, lock); + + group = g_hash_table_lookup (newsrc->groups, group_name); if (group) { - return group->subscribed; - } - else { - return FALSE; + ret = group->subscribed; } + + NEWSRC_UNLOCK(newsrc, lock); + + return ret; } void camel_nntp_newsrc_subscribe_group (CamelNNTPNewsrc *newsrc, const char *group_name) { - NewsrcGroup *group = g_hash_table_lookup (newsrc->groups, group_name); + NewsrcGroup *group; + + NEWSRC_LOCK(newsrc, lock); + + group = g_hash_table_lookup (newsrc->groups, group_name); if (group) { if (!group->subscribed) @@ -265,13 +310,18 @@ camel_nntp_newsrc_subscribe_group (CamelNNTPNewsrc *newsrc, const char *group_na else { camel_nntp_newsrc_group_add (newsrc, group_name, TRUE); } + + NEWSRC_UNLOCK(newsrc, lock); } void camel_nntp_newsrc_unsubscribe_group (CamelNNTPNewsrc *newsrc, const char *group_name) { - NewsrcGroup *group = g_hash_table_lookup (newsrc->groups, group_name); + NewsrcGroup *group; + + NEWSRC_LOCK(newsrc, lock); + group = g_hash_table_lookup (newsrc->groups, group_name); if (group) { if (group->subscribed) newsrc->dirty = TRUE; @@ -280,6 +330,8 @@ camel_nntp_newsrc_unsubscribe_group (CamelNNTPNewsrc *newsrc, const char *group_ else { camel_nntp_newsrc_group_add (newsrc, group_name, FALSE); } + + NEWSRC_UNLOCK(newsrc, lock); } struct newsrc_ptr_array { @@ -287,6 +339,7 @@ struct newsrc_ptr_array { gboolean subscribed_only; }; +/* this needs to strdup the grup_name, if the group array is likely to change */ static void get_group_foreach (char *group_name, NewsrcGroup *group, struct newsrc_ptr_array *npa) { @@ -302,12 +355,16 @@ camel_nntp_newsrc_get_subscribed_group_names (CamelNNTPNewsrc *newsrc) g_return_val_if_fail (newsrc, NULL); + NEWSRC_LOCK(newsrc, lock); + npa.ptr_array = g_ptr_array_new(); npa.subscribed_only = TRUE; g_hash_table_foreach (newsrc->groups, (GHFunc)get_group_foreach, &npa); + NEWSRC_UNLOCK(newsrc, lock); + return npa.ptr_array; } @@ -318,12 +375,16 @@ camel_nntp_newsrc_get_all_group_names (CamelNNTPNewsrc *newsrc) g_return_val_if_fail (newsrc, NULL); + NEWSRC_LOCK(newsrc, lock); + npa.ptr_array = g_ptr_array_new(); npa.subscribed_only = FALSE; g_hash_table_foreach (newsrc->groups, (GHFunc)get_group_foreach, &npa); + NEWSRC_UNLOCK(newsrc, lock); + return npa.ptr_array; } @@ -395,9 +456,13 @@ camel_nntp_newsrc_write_to_file(CamelNNTPNewsrc *newsrc, FILE *fp) newsrc_fp.newsrc = newsrc; newsrc_fp.fp = fp; + NEWSRC_LOCK(newsrc, lock); + g_hash_table_foreach (newsrc->groups, (GHFunc)camel_nntp_newsrc_write_group_line, &newsrc_fp); + + NEWSRC_UNLOCK(newsrc, lock); } void @@ -407,17 +472,21 @@ camel_nntp_newsrc_write(CamelNNTPNewsrc *newsrc) g_return_if_fail (newsrc); + NEWSRC_LOCK(newsrc, lock); + if (!newsrc->dirty) return; if ((fp = fopen(newsrc->filename, "w")) == NULL) { g_warning ("Couldn't open newsrc file '%s'.\n", newsrc->filename); + NEWSRC_UNLOCK(newsrc, lock); return; } - camel_nntp_newsrc_write_to_file(newsrc, fp); - newsrc->dirty = FALSE; + NEWSRC_UNLOCK(newsrc, lock); + + camel_nntp_newsrc_write_to_file(newsrc, fp); fclose(fp); } @@ -535,6 +604,9 @@ camel_nntp_newsrc_read_for_server (const char *server) newsrc = g_new0(CamelNNTPNewsrc, 1); newsrc->filename = filename; newsrc->groups = g_hash_table_new (g_str_hash, g_str_equal); +#ifdef ENABLE_THREADS + newsrc->lock = g_mutex_new(); +#endif if ((fd = open(filename, O_RDONLY)) == -1) { g_warning ("~/.newsrc-%s not present.\n", server); diff --git a/camel/providers/pop3/camel-pop3-folder.c b/camel/providers/pop3/camel-pop3-folder.c index 91760c80c5..cf55a95661 100644 --- a/camel/providers/pop3/camel-pop3-folder.c +++ b/camel/providers/pop3/camel-pop3-folder.c @@ -122,7 +122,8 @@ camel_pop3_folder_new (CamelStore *parent, CamelException *ex) folder = CAMEL_FOLDER (camel_object_new (CAMEL_POP3_FOLDER_TYPE)); camel_folder_construct (folder, parent, "inbox", "inbox"); - camel_folder_refresh_info (folder, ex); + /* mt-ok, since we dont have the folder-lock for new() */ + camel_folder_refresh_info (folder, ex);/* mt-ok */ if (camel_exception_is_set (ex)) { camel_object_unref (CAMEL_OBJECT (folder)); folder = NULL; diff --git a/camel/providers/vee/camel-vee-folder.c b/camel/providers/vee/camel-vee-folder.c index c8ac7f2059..7bfbd6d052 100644 --- a/camel/providers/vee/camel-vee-folder.c +++ b/camel/providers/vee/camel-vee-folder.c @@ -34,6 +34,8 @@ #include <string.h> +#define d(x) + /* our message info includes the parent folder */ typedef struct _CamelVeeMessageInfo { CamelMessageInfo info; @@ -47,27 +49,22 @@ struct _CamelVeeFolderPrivate { #define _PRIVATE(o) (((CamelVeeFolder *)(o))->priv) static void vee_sync (CamelFolder *folder, gboolean expunge, CamelException *ex); +static void vee_expunge (CamelFolder *folder, CamelException *ex); -static GPtrArray *vee_get_uids (CamelFolder *folder); -GPtrArray *vee_get_summary (CamelFolder *folder); - -static gint vee_get_message_count (CamelFolder *folder); -static gint vee_get_unread_message_count (CamelFolder *folder); static CamelMimeMessage *vee_get_message (CamelFolder *folder, const gchar *uid, CamelException *ex); -static const CamelMessageInfo *vee_get_message_info (CamelFolder *folder, const char *uid); static GPtrArray *vee_search_by_expression(CamelFolder *folder, const char *expression, CamelException *ex); -static guint32 vee_get_message_flags (CamelFolder *folder, const char *uid); static void vee_set_message_flags (CamelFolder *folder, const char *uid, guint32 flags, guint32 set); -static gboolean vee_get_message_user_flag (CamelFolder *folder, const char *uid, const char *name); static void vee_set_message_user_flag (CamelFolder *folder, const char *uid, const char *name, gboolean value); - static void camel_vee_folder_class_init (CamelVeeFolderClass *klass); static void camel_vee_folder_init (CamelVeeFolder *obj); static void camel_vee_folder_finalise (CamelObject *obj); +static void folder_changed(CamelFolder *sub, gpointer type, CamelVeeFolder *vf); +static void message_changed(CamelFolder *f, const char *uid, CamelVeeFolder *mf); + static void vee_folder_build(CamelVeeFolder *vf, CamelException *ex); static void vee_folder_build_folder(CamelVeeFolder *vf, CamelFolder *source, CamelException *ex); @@ -99,22 +96,13 @@ camel_vee_folder_class_init (CamelVeeFolderClass *klass) camel_vee_folder_parent = CAMEL_FOLDER_CLASS(camel_type_get_global_classfuncs (camel_folder_get_type ())); folder_class->sync = vee_sync; + folder_class->expunge = vee_expunge; - folder_class->get_uids = vee_get_uids; - folder_class->free_uids = camel_folder_free_deep; - folder_class->get_summary = vee_get_summary; - folder_class->free_summary = camel_folder_free_nop; folder_class->get_message = vee_get_message; - folder_class->get_message_info = vee_get_message_info; - - folder_class->get_message_count = vee_get_message_count; - folder_class->get_unread_message_count = vee_get_unread_message_count; folder_class->search_by_expression = vee_search_by_expression; - folder_class->get_message_flags = vee_get_message_flags; folder_class->set_message_flags = vee_set_message_flags; - folder_class->get_message_user_flag = vee_get_message_user_flag; folder_class->set_message_user_flag = vee_set_message_user_flag; } @@ -154,10 +142,15 @@ camel_vee_folder_finalise (CamelObject *obj) node = p->folders; while (node) { CamelFolder *f = node->data; + camel_object_unhook_event ((CamelObject *)f, "folder_changed", (CamelObjectEventHookFunc) folder_changed, vf); + camel_object_unhook_event ((CamelObject *)f, "message_changed", (CamelObjectEventHookFunc) message_changed, vf); camel_object_unref((CamelObject *)f); node = g_list_next(node); } + g_free(vf->expression); + g_free(vf->vname); + camel_folder_change_info_free(vf->changes); #ifdef DYNAMIC camel_object_unref((CamelObject *)vf->search); @@ -195,8 +188,8 @@ camel_vee_folder_new (CamelStore *parent_store, const char *name, CamelException *searchpart++ = 0; } - vf->messages = g_ptr_array_new(); - vf->messages_uid = g_hash_table_new(g_str_hash, g_str_equal); + folder->summary = camel_folder_summary_new(); + folder->summary->message_info_size = sizeof(CamelVeeMessageInfo); vf->expression = g_strdup(searchpart); vf->vname = namepart; @@ -210,6 +203,43 @@ camel_vee_folder_new (CamelStore *parent_store, const char *name, CamelException return folder; } +static CamelVeeMessageInfo * +vee_folder_add(CamelVeeFolder *vf, CamelFolder *f, CamelMessageInfo *info) +{ + CamelVeeMessageInfo *mi; + char *uid; + CamelFolder *folder = (CamelFolder *)vf; + + mi = (CamelVeeMessageInfo *)camel_folder_summary_info_new(folder->summary); + camel_message_info_dup_to(info, (CamelMessageInfo *)mi); + uid = g_strdup_printf("%p:%s", f, camel_message_info_uid(info)); +#ifdef DOESTRV + mi->info.strings = e_strv_set_ref_free(mi->info.strings, CAMEL_MESSAGE_INFO_UID, uid); + mi->info.strings = e_strv_pack(mi->info.strings); +#else + g_free(mi->info.uid); + mi->info.uid = uid; +#endif + mi->folder = f; + camel_folder_summary_add(folder->summary, (CamelMessageInfo *)mi); + + return mi; +} + +static CamelVeeMessageInfo * +vee_folder_add_uid(CamelVeeFolder *vf, CamelFolder *f, const char *inuid) +{ + CamelMessageInfo *info; + CamelVeeMessageInfo *mi = NULL; + + info = camel_folder_get_message_info(f, inuid); + if (info) { + mi = vee_folder_add(vf, f, info); + camel_folder_free_message_info(f, info); + } + return mi; +} + #ifdef DYNAMIC static void vfolder_remove_match(CamelVeeFolder *vf, CamelVeeMessageInfo *vinfo) @@ -218,38 +248,21 @@ vfolder_remove_match(CamelVeeFolder *vf, CamelVeeMessageInfo *vinfo) printf("removing match %s\n", uid); - g_hash_table_remove(vf->messages_uid, uid); - g_ptr_array_remove_fast(vf->messages, vinfo); + camel_folder_summary_remove(((CamelFolder *)vf)->summary, (CamelMessageInfo *)vinfo); camel_folder_change_info_remove_uid(vf->changes, uid); - camel_message_info_free((CamelMessageInfo *)vinfo); } static CamelVeeMessageInfo * -vfolder_add_match(CamelVeeFolder *vf, CamelFolder *f, const CamelMessageInfo *info) +vee_folder_add_change(CamelVeeFolder *vf, CamelFolder *f, CamelMessageInfo *info) { - CamelVeeMessageInfo *mi; - char *uid; + CamelVeeMessageInfo *mi = NULL; - mi = g_malloc0(sizeof(*mi)); - camel_message_info_dup_to(info, (CamelMessageInfo*)mi); - uid = g_strdup_printf("%p:%s", f, camel_message_info_uid(info)); -#ifdef DOESTRV - mi->info.strings = e_strv_set_ref_free(mi->info.strings, CAMEL_MESSAGE_INFO_UID, uid); - mi->info.strings = e_strv_pack(mi->info.strings); -#else - g_free (mi->info.uid); - mi->info.uid = uid; -#endif - mi->folder = f; - g_ptr_array_add(vf->messages, mi); - uid = (char *)camel_message_info_uid(mi); - g_hash_table_insert(vf->messages_uid, uid, mi); - - printf("adding match %s\n", uid); - - camel_folder_change_info_add_uid(vf->changes, uid); + mi = vee_folder_add(vf, f, info); + camel_folder_change_info_add_uid(vf->changes, camel_message_info_uid(mi)); + return mi; } + #endif static void @@ -258,7 +271,7 @@ vfolder_change_match(CamelVeeFolder *vf, CamelVeeMessageInfo *vinfo, const Camel CamelFlag *flag; CamelTag *tag; - printf("changing match %s\n", camel_message_info_uid(vinfo)); + d(printf("changing match %s\n", camel_message_info_uid(vinfo))); vinfo->info.flags = info->flags; camel_flag_list_free(&vinfo->info.user_flags); @@ -276,6 +289,8 @@ vfolder_change_match(CamelVeeFolder *vf, CamelVeeMessageInfo *vinfo, const Camel camel_folder_change_info_change_uid(vf->changes, camel_message_info_uid(vinfo)); } +/* FIXME: This code is a big race, as it is never called locked ... */ + static void folder_changed(CamelFolder *sub, gpointer type, CamelVeeFolder *vf) { @@ -283,6 +298,7 @@ folder_changed(CamelFolder *sub, gpointer type, CamelVeeFolder *vf) #ifdef DYNAMIC CamelFolderChangeInfo *changes = type; + CamelFolder *folder = (CamelFolder *)vf; /* assume its faster to search a long list in whole, than by part */ if (changes && (changes->uid_added->len + changes->uid_changed->len) < 500) { @@ -290,10 +306,18 @@ folder_changed(CamelFolder *sub, gpointer type, CamelVeeFolder *vf) char *vuid; CamelVeeMessageInfo *vinfo; gboolean match; - const CamelMessageInfo *info; + CamelMessageInfo *info; ex = camel_exception_new(); + /* FIXME: We dont search body contents with this search, so, it isn't as + useful as it might be. + We shold probably just perform a whole search if we need to, i.e. there + are added items. Changed items we are unlikely to want to remove immediately + anyway, although I guess it might be useful. + Removed items can always just be removed. + */ + /* see if added ones now match us */ for (i=0;i<changes->uid_added->len;i++) { info = camel_folder_get_message_info(sub, changes->uid_added->pdata[i]); @@ -301,7 +325,8 @@ folder_changed(CamelFolder *sub, gpointer type, CamelVeeFolder *vf) camel_folder_search_set_folder(vf->search, sub); match = camel_folder_search_match_expression(vf->search, vf->expression, info, ex); if (match) - vinfo = vfolder_add_match(vf, sub, info); + vinfo = vee_folder_add_change(vf, sub, info); + camel_folder_free_message_info(sub, info); } } @@ -309,20 +334,31 @@ folder_changed(CamelFolder *sub, gpointer type, CamelVeeFolder *vf) for (i=0;i<changes->uid_changed->len;i++) { info = camel_folder_get_message_info(sub, changes->uid_changed->pdata[i]); vuid = g_strdup_printf("%p:%s", sub, (char *)changes->uid_changed->pdata[i]); - vinfo = (CamelVeeMessageInfo *)vee_get_message_info((CamelFolder *)vf, vuid); + vinfo = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, vuid); if (info) { camel_folder_search_set_folder(vf->search, sub); +#if 0 match = camel_folder_search_match_expression(vf->search, vf->expression, info, ex); +#endif if (vinfo) { - if (match) - vfolder_change_match(vf, vinfo, info); - else +#if 0 + if (!match) vfolder_remove_match(vf, vinfo); - } else if (match) - vfolder_add_match(vf, sub, info); + else +#endif + vfolder_change_match(vf, vinfo, info); + } +#if 0 + else if (match) + vee_folder_add_change(vf, sub, info); +#endif + camel_folder_free_message_info(sub, info); } else if (vinfo) vfolder_remove_match(vf, vinfo); + if (vinfo) + camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)vinfo); + g_free(vuid); } @@ -331,9 +367,11 @@ folder_changed(CamelFolder *sub, gpointer type, CamelVeeFolder *vf) /* mirror removes directly, if they used to match */ for (i=0;i<changes->uid_removed->len;i++) { vuid = g_strdup_printf("%p:%s", sub, (char *)changes->uid_removed->pdata[i]); - vinfo = (CamelVeeMessageInfo *)vee_get_message_info((CamelFolder *)vf, vuid); - if (vinfo) + vinfo = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, vuid); + if (vinfo) { vfolder_remove_match(vf, vinfo); + camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)vinfo); + } g_free(vuid); } } else { @@ -352,21 +390,24 @@ folder_changed(CamelFolder *sub, gpointer type, CamelVeeFolder *vf) } } +/* FIXME: This code is a race, as it is never called locked */ + /* track flag changes in the summary */ static void message_changed(CamelFolder *f, const char *uid, CamelVeeFolder *mf) { - const CamelMessageInfo *info; + CamelMessageInfo *info; CamelVeeMessageInfo *vinfo; char *vuid; + CamelFolder *folder = (CamelFolder *)mf; #ifdef DYNAMIC - gboolean match; + /*gboolean match;*/ CamelException *ex; #endif info = camel_folder_get_message_info(f, uid); vuid = g_strdup_printf("%p:%s", f, uid); - vinfo = (CamelVeeMessageInfo *)vee_get_message_info((CamelFolder *)mf, vuid); + vinfo = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, vuid); /* see if this message now matches/doesn't match anymore */ @@ -376,16 +417,23 @@ message_changed(CamelFolder *f, const char *uid, CamelVeeFolder *mf) #ifdef DYNAMIC camel_folder_search_set_folder(mf->search, f); ex = camel_exception_new(); +#if 0 match = camel_folder_search_match_expression(mf->search, mf->expression, info, ex); +#endif camel_exception_free(ex); if (info) { if (vinfo) { - if (match) - vfolder_change_match(mf, vinfo, info); - else +#if 0 + if (!match) vfolder_remove_match(mf, vinfo); - } else if (match) - vfolder_add_match(mf, f, info); + else +#endif + vfolder_change_match(mf, vinfo, info); + } +#if 0 + else if (match) + vee_folder_add_change(mf, f, info); +#endif } else if (vinfo) vfolder_remove_match(mf, vinfo); #else @@ -393,6 +441,11 @@ message_changed(CamelFolder *f, const char *uid, CamelVeeFolder *mf) vfolder_change_match(mf, vinfo, info); #endif + if (info) + camel_folder_free_message_info(f, info); + if (vinfo) + camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)vinfo); + /* cascade up, if required. This could probably be delayed, but doesn't matter really, that is what freeze is for. */ if (camel_folder_change_info_changed(mf->changes)) { @@ -432,97 +485,53 @@ camel_vee_folder_add_folder(CamelVeeFolder *vf, CamelFolder *sub) } - static void vee_sync (CamelFolder *folder, gboolean expunge, CamelException *ex) { - ; -} - -static gint vee_get_message_count (CamelFolder *folder) -{ CamelVeeFolder *vf = (CamelVeeFolder *)folder; + struct _CamelVeeFolderPrivate *p = _PRIVATE(vf); + GList *node; - return vf->messages->len; -} - -static gint -vee_get_unread_message_count (CamelFolder *folder) -{ - CamelVeeFolder *vee_folder = CAMEL_VEE_FOLDER (folder); - CamelMessageInfo *info; - GPtrArray *infolist; - gint i, count = 0; - - g_return_val_if_fail (folder != NULL, -1); + node = p->folders; + while (node) { + CamelFolder *f = node->data; - infolist = vee_folder->messages; - - for (i = 0; i < infolist->len; i++) { - info = (CamelMessageInfo *) g_ptr_array_index (infolist, i); - if (!(info->flags & CAMEL_MESSAGE_SEEN)) - count++; + camel_folder_sync(f, expunge, ex); + node = node->next; } - - return count; } -static gboolean -get_real_message(CamelFolder *folder, const char *uid, CamelFolder **out_folder, const char **out_uid) +static void +vee_expunge (CamelFolder *folder, CamelException *ex) { - CamelVeeMessageInfo *mi; + CamelVeeFolder *vf = (CamelVeeFolder *)folder; + struct _CamelVeeFolderPrivate *p = _PRIVATE(vf); + GList *node; - mi = (CamelVeeMessageInfo *)vee_get_message_info(folder, uid); - g_return_val_if_fail(mi != NULL, FALSE); + node = p->folders; + while (node) { + CamelFolder *f = node->data; - *out_folder = mi->folder; - *out_uid = strchr(camel_message_info_uid(mi), ':')+1; - return TRUE; + camel_folder_expunge(f, ex); + node = node->next; + } } static CamelMimeMessage *vee_get_message(CamelFolder *folder, const gchar *uid, CamelException *ex) { - const char *real_uid; - CamelFolder *real_folder; + CamelVeeMessageInfo *mi; + CamelMimeMessage *msg = NULL; - if (!get_real_message(folder, uid, &real_folder, &real_uid)) { + mi = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, uid); + if (mi == NULL) camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID, "No such message %s in %s", uid, folder->name); - return NULL; - } - - return camel_folder_get_message (real_folder, real_uid, ex); -} - -GPtrArray *vee_get_summary(CamelFolder *folder) -{ - CamelVeeFolder *vf = (CamelVeeFolder *)folder; + else + msg = camel_folder_get_message(mi->folder, strchr(camel_message_info_uid(mi), ':') + 1, ex); + camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)mi); - return vf->messages; -} - -static const CamelMessageInfo *vee_get_message_info(CamelFolder *f, const char *uid) -{ - CamelVeeFolder *vf = (CamelVeeFolder *)f; - - return g_hash_table_lookup(vf->messages_uid, uid); -} - -static GPtrArray *vee_get_uids (CamelFolder *folder) -{ - GPtrArray *result; - int i; - CamelVeeFolder *vf = (CamelVeeFolder *)folder; - - result = g_ptr_array_new (); - g_ptr_array_set_size (result, vf->messages->len); - for (i=0;i<vf->messages->len;i++) { - CamelMessageInfo *mi = g_ptr_array_index(vf->messages, i); - result->pdata[i] = g_strdup(camel_message_info_uid(mi)); - } - - return result; + return msg; } static GPtrArray * @@ -551,55 +560,32 @@ vee_search_by_expression(CamelFolder *folder, const char *expression, CamelExcep return result; } -static guint32 -vee_get_message_flags(CamelFolder *folder, const char *uid) -{ - const char *real_uid; - CamelFolder *real_folder; - - if (!get_real_message (folder, uid, &real_folder, &real_uid)) - return 0; - - return camel_folder_get_message_flags(real_folder, real_uid); -} - static void vee_set_message_flags(CamelFolder *folder, const char *uid, guint32 flags, guint32 set) { - const char *real_uid; - CamelFolder *real_folder; - - if (!get_real_message(folder, uid, &real_folder, &real_uid)) - return; - - camel_folder_set_message_flags(real_folder, real_uid, flags, set); -} - -static gboolean -vee_get_message_user_flag(CamelFolder *folder, const char *uid, const char *name) -{ - const char *real_uid; - CamelFolder *real_folder; - - if (!get_real_message(folder, uid, &real_folder, &real_uid)) - return FALSE; + CamelVeeMessageInfo *mi; - return camel_folder_get_message_user_flag(real_folder, real_uid, name); + mi = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, uid); + if (mi) { + ((CamelFolderClass *)camel_vee_folder_parent)->set_message_flags(folder, uid, flags, set); + camel_folder_set_message_flags(mi->folder, strchr(camel_message_info_uid(mi), ':') + 1, flags, set); + camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)mi); + } } static void vee_set_message_user_flag(CamelFolder *folder, const char *uid, const char *name, gboolean value) { - const char *real_uid; - CamelFolder *real_folder; - - if (!get_real_message(folder, uid, &real_folder, &real_uid)) - return; + CamelVeeMessageInfo *mi; - return camel_folder_set_message_user_flag(real_folder, real_uid, name, value); + mi = (CamelVeeMessageInfo *)camel_folder_summary_uid(folder->summary, uid); + if (mi) { + ((CamelFolderClass *)camel_vee_folder_parent)->set_message_user_flag(folder, uid, name, value); + camel_folder_set_message_user_flag(mi->folder, strchr(camel_message_info_uid(mi), ':') + 1, name, value); + camel_folder_summary_info_free(folder->summary, (CamelMessageInfo *)mi); + } } - /* need incremental update, based on folder. Need to watch folders for changes and update accordingly. @@ -609,54 +595,22 @@ vee_set_message_user_flag(CamelFolder *folder, const char *uid, const char *name static void vee_folder_build(CamelVeeFolder *vf, CamelException *ex) { + CamelFolder *folder = (CamelFolder *)vf; struct _CamelVeeFolderPrivate *p = _PRIVATE(vf); GList *node; - int i; - GPtrArray *messages; - GHashTable *messages_uid; - for (i=0;i<vf->messages->len;i++) { - CamelMessageInfo *mi = g_ptr_array_index(vf->messages, i); - camel_message_info_free(mi); - } - - messages = g_ptr_array_new(); - messages_uid = g_hash_table_new(g_str_hash, g_str_equal); - - g_ptr_array_free(vf->messages, TRUE); - vf->messages = messages; - g_hash_table_destroy(vf->messages_uid); - vf->messages_uid = messages_uid; + camel_folder_summary_clear(folder->summary); node = p->folders; while (node) { GPtrArray *matches; CamelFolder *f = node->data; - CamelVeeMessageInfo *mi; - const CamelMessageInfo *info; int i; matches = camel_folder_search_by_expression(f, vf->expression, ex); - for (i = 0; i < matches->len; i++) { - info = camel_folder_get_message_info(f, matches->pdata[i]); - if (info) { - char *uid; + for (i = 0; i < matches->len; i++) + vee_folder_add_uid(vf, f, matches->pdata[i]); - mi = g_malloc0(sizeof(*mi)); - camel_message_info_dup_to(info, (CamelMessageInfo *)mi); - uid = g_strdup_printf("%p:%s", f, camel_message_info_uid(info)); -#ifdef DOESTRV - mi->info.strings = e_strv_set_ref_free(mi->info.strings, CAMEL_MESSAGE_INFO_UID, uid); - mi->info.strings = e_strv_pack(mi->info.strings); -#else - g_free(mi->info.uid); - mi->info.uid = uid; -#endif - mi->folder = f; - g_ptr_array_add(messages, mi); - g_hash_table_insert(messages_uid, (char *)camel_message_info_uid(mi), mi); - } - } camel_folder_search_free(f, matches); node = g_list_next(node); } @@ -670,50 +624,27 @@ vee_folder_build_folder(CamelVeeFolder *vf, CamelFolder *source, CamelException GPtrArray *matches; CamelFolder *f = source; CamelVeeMessageInfo *mi; - const CamelMessageInfo *info; - - GPtrArray *messages; - GHashTable *messages_uid; + CamelFolder *folder = (CamelFolder *)vf; int i; + int count; - for (i=0;i<vf->messages->len;i++) { - CamelVeeMessageInfo *mi = g_ptr_array_index(vf->messages, i); - if (mi->folder == source) { + count = camel_folder_summary_count(folder->summary); + for (i=0;i<count;i++) { + CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *)camel_folder_summary_index(folder->summary, i); + if (mi && mi->folder == source) { const char *uid = camel_message_info_uid(mi); camel_folder_change_info_add_source(vf->changes, uid); - g_hash_table_remove(vf->messages_uid, uid); - g_ptr_array_remove_index_fast(vf->messages, i); - - camel_message_info_free((CamelMessageInfo *)mi); + camel_folder_summary_remove(folder->summary, (CamelMessageInfo *)mi); i--; } + camel_message_info_free((CamelMessageInfo *)mi); } - messages = vf->messages; - messages_uid = vf->messages_uid; - matches = camel_folder_search_by_expression(f, vf->expression, ex); for (i = 0; i < matches->len; i++) { - info = camel_folder_get_message_info(f, matches->pdata[i]); - if (info) { - char *uid; - - mi = g_malloc0(sizeof(*mi)); - camel_message_info_dup_to(info, (CamelMessageInfo*)mi); - uid = g_strdup_printf("%p:%s", f, camel_message_info_uid(info)); -#ifdef DOESTRV - mi->info.strings = e_strv_set_ref_free(mi->info.strings, CAMEL_MESSAGE_INFO_UID, uid); - mi->info.strings = e_strv_pack(mi->info.strings); -#else - g_free (mi->info.uid); - mi->info.uid = uid; -#endif - mi->folder = f; - g_ptr_array_add(messages, mi); - uid = (char *)camel_message_info_uid(mi); - g_hash_table_insert(messages_uid, uid, mi); - camel_folder_change_info_add_update(vf->changes, uid); - } + mi = vee_folder_add_uid(vf, f, matches->pdata[i]); + if (mi) + camel_folder_change_info_add_update(vf->changes, camel_message_info_uid(mi)); } camel_folder_search_free(f, matches); diff --git a/camel/providers/vee/camel-vee-folder.h b/camel/providers/vee/camel-vee-folder.h index 3b6676d281..65c718a8d6 100644 --- a/camel/providers/vee/camel-vee-folder.h +++ b/camel/providers/vee/camel-vee-folder.h @@ -43,9 +43,6 @@ struct _CamelVeeFolder { char *vname; /* local name */ CamelFolder *local; /* local storage for folder */ - /* FIXME: Move this to a summary object??? */ - GPtrArray *messages; /* message info's */ - GHashTable *messages_uid; CamelFolderChangeInfo *changes; #ifdef DYNAMIC CamelFolderSearch *search; diff --git a/camel/tests/folder/Makefile.am b/camel/tests/folder/Makefile.am index 45f2e9895e..4401d310f6 100644 --- a/camel/tests/folder/Makefile.am +++ b/camel/tests/folder/Makefile.am @@ -15,11 +15,13 @@ LDADD = \ check_PROGRAMS = \ test1 test4 test5 \ test2 test6 test7 \ - test3 + test3 \ + test8 TESTS = test1 test4 test5 \ test2 test6 test7 \ - test3 + test3 \ + test8 diff --git a/camel/tests/folder/README b/camel/tests/folder/README index fab1358198..e02308d2d7 100644 --- a/camel/tests/folder/README +++ b/camel/tests/folder/README @@ -7,3 +7,5 @@ test5 camel store folder operations, NNTP test6 basic folder operations, IMAP test7 basic folder operations, NNTP +test8 multithreaded folder torture test, local + diff --git a/camel/tests/folder/test8.c b/camel/tests/folder/test8.c new file mode 100644 index 0000000000..30552cf782 --- /dev/null +++ b/camel/tests/folder/test8.c @@ -0,0 +1,222 @@ +/* threaded folder testing */ + +#include "camel-test.h" +#include "folders.h" +#include "messages.h" + +#include <camel/camel-exception.h> +#include <camel/camel-service.h> +#include <camel/camel-session.h> +#include <camel/camel-store.h> + +#define MAX_MESSAGES (100) +#define MAX_THREADS (10) + +#define d(x) + +#ifndef ENABLE_THREADS +int main(int argc, char **argv) +{ + printf("Test %s is only compiled with threads enabled\n", argv[0]); + return 77; +} +#else + +#include <pthread.h> + +/* god, who designed this horrid interface */ +static char *auth_callback(CamelAuthCallbackMode mode, + char *data, gboolean secret, + CamelService *service, char *item, + CamelException *ex) +{ + return NULL; +} + +#define ARRAY_LEN(x) (sizeof(x)/sizeof(x[0])) + +static char *local_providers[] = { + "mbox", + "mh", + "maildir" +}; + +static void +test_add_message(CamelFolder *folder, int j) +{ + CamelMimeMessage *msg; + char *content; + char *subject; + CamelException ex; + + camel_exception_init(&ex); + + push("creating message %d\n", j); + msg = test_message_create_simple(); + content = g_strdup_printf("Test message %d contents\n\n", j); + test_message_set_content_simple((CamelMimePart *)msg, 0, "text/plain", + content, strlen(content)); + test_free(content); + subject = g_strdup_printf("Test message %d subject", j); + camel_mime_message_set_subject(msg, subject); + pull(); + + push("appending simple message %d", j); + camel_folder_append_message(folder, msg, NULL, &ex); + check_msg(!camel_exception_is_set(&ex), "%s", camel_exception_get_description(&ex)); + pull(); + + check_unref(msg, 1); +} + +struct _threadinfo { + int id; + CamelFolder *folder; +}; + +static void * +worker(void *d) +{ + struct _threadinfo *info = d; + int i, j, id = info->id; + char *sub, *content; + GPtrArray *res; + CamelException *ex = camel_exception_new(); + CamelMimeMessage *msg; + + /* we add a message, search for it, twiddle some flags, delete it */ + /* and flat out */ + for (i=0;i<MAX_MESSAGES;i++) { + d(printf("Thread %ld message %i\n", pthread_self(), i)); + test_add_message(info->folder, id+i); + + sub = g_strdup_printf("(match-all (header-contains \"subject\" \"message %d subject\"))", id+i); + + push("searching for message %d\n\tusing: %s", id+i, sub); + res = camel_folder_search_by_expression(info->folder, sub, ex); + check_msg(!camel_exception_is_set(ex), "%s", camel_exception_get_description(ex)); + check_msg(res->len == 1, "res->len = %d", res->len); + pull(); + + push("getting message '%s'", res->pdata[0]); + msg = camel_folder_get_message(info->folder, (char *)res->pdata[0], ex); + check_msg(!camel_exception_is_set(ex), "%s", camel_exception_get_description(ex)); + pull(); + + push("comparing content"); + content = g_strdup_printf("Test message %d contents\n\n", id+i); + test_message_compare_content(camel_medium_get_content_object((CamelMedium *)msg), content, strlen(content)); + test_free(content); + + push("deleting message, cleanup"); + j=(100.0*rand()/(RAND_MAX+1.0)); + if (j<=70) { + camel_folder_delete_message(info->folder, res->pdata[0]); + } + + camel_folder_search_free(info->folder, res); + res = NULL; + test_free(sub); + + check_unref(msg, 1); + pull(); + + /* about 1-in 100 calls will expunge */ + j=(200.0*rand()/(RAND_MAX+1.0)); + if (j<=2) { + d(printf("Forcing an expuge\n")); + push("expunging folder"); + camel_folder_expunge(info->folder, ex); + check_msg(!camel_exception_is_set(ex), "%s", camel_exception_get_description(ex)); + pull(); + } + } + + camel_exception_free(ex); + + return info; +} + +int main(int argc, char **argv) +{ + CamelSession *session; + CamelException *ex; + int i, j, index; + char *path; + CamelStore *store; + pthread_t threads[MAX_THREADS]; + struct _threadinfo *info; + CamelFolder *folder; + GPtrArray *uids; + + camel_test_init(argc, argv); + + ex = camel_exception_new(); + + /* clear out any camel-test data */ + system("/bin/rm -rf /tmp/camel-test"); + + session = camel_session_new("/tmp/camel-test", auth_callback, NULL, NULL); + + for (j=0;j<ARRAY_LEN(local_providers);j++) { + for (index=0;index<2;index++) { + path = g_strdup_printf("method %s %s", local_providers[j], index?"indexed":"nonindexed"); + camel_test_start(path); + test_free(path); + + push("trying %s index %d", local_providers[j], index); + path = g_strdup_printf("%s:///tmp/camel-test/%s", local_providers[j], local_providers[j]); + store = camel_session_get_store(session, path, ex); + check_msg(!camel_exception_is_set(ex), "%s", camel_exception_get_description(ex)); + test_free(path); + + if (index == 0) + folder = camel_store_get_folder(store, "testbox", CAMEL_STORE_FOLDER_CREATE, ex); + else + folder = camel_store_get_folder(store, "testbox", + CAMEL_STORE_FOLDER_CREATE|CAMEL_STORE_FOLDER_BODY_INDEX, ex); + check_msg(!camel_exception_is_set(ex), "%s", camel_exception_get_description(ex)); + + for (i=0;i<MAX_THREADS;i++) { + info = g_malloc(sizeof(*info)); + info->id = i*MAX_MESSAGES; + info->folder = folder; + pthread_create(&threads[i], 0, worker, info); + } + + for (i=0;i<MAX_THREADS;i++) { + pthread_join(threads[i], (void **)&info); + g_free(info); + } + pull(); + + push("deleting remaining messages"); + uids = camel_folder_get_uids(folder); + for (i=0;i<uids->len;i++) { + camel_folder_delete_message(folder, uids->pdata[i]); + } + camel_folder_free_uids(folder, uids); + + camel_folder_expunge(folder, ex); + check_msg(!camel_exception_is_set(ex), "%s", camel_exception_get_description(ex)); + + check_unref(folder, 1); + + camel_store_delete_folder(store, "testbox", ex); + check_msg(!camel_exception_is_set(ex), "%s", camel_exception_get_description(ex)); + + check_unref(store, 1); + + pull(); + + camel_test_end(); + } + } + + camel_object_unref((CamelObject *)session); + camel_exception_free(ex); + + return 0; +} + +#endif /* ENABLE_THREADS */ diff --git a/camel/tests/lib/camel-test.c b/camel/tests/lib/camel-test.c index 6a1e00addb..09014e4b07 100644 --- a/camel/tests/lib/camel-test.c +++ b/camel/tests/lib/camel-test.c @@ -4,39 +4,97 @@ #include <stdio.h> #include <signal.h> +#ifdef ENABLE_THREADS +#include <pthread.h> +#endif + +#ifdef ENABLE_THREADS +/* well i dunno, doesn't seem to be in the headers but hte manpage mentions it */ +/* a nonportable checking mutex for glibc, not really needed, just validates + the test harness really */ +# ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP +static pthread_mutex_t lock = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; +# else +static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; +# endif +#define CAMEL_TEST_LOCK pthread_mutex_lock(&lock) +#define CAMEL_TEST_UNLOCK pthread_mutex_unlock(&lock) +#define CAMEL_TEST_ID (pthread_self()) +#else +#define CAMEL_TEST_LOCK +#define CAMEL_TEST_UNLOCK +#define CAMEL_TEST_ID (0) +#endif + +static int setup; +static int ok; + struct _stack { struct _stack *next; + int fatal; char *what; }; -static int setup; -static struct _stack *state; -static struct _stack *nonfatal; -static int ok; +/* per-thread state */ +struct _state { + char *test; + int nonfatal; + struct _stack *state; +}; + +static GHashTable *info_table; int camel_test_verbose; +static void +dump_action(int id, struct _state *s, void *d) +{ + struct _stack *node; + +#ifdef ENABLE_THREADS + printf("\nThread %d:\n", id); +#endif + node = s->state; + if (node) { + printf("Current action:\n"); + while (node) { + printf("\t%s%s\n", node->fatal?"":"[nonfatal]", node->what); + node = node->next; + } + } + printf("\tTest: %s\n", s->test); +} + static void die(int sig) { static int indie = 0; - struct _stack *node; if (!indie) { indie = 1; printf("\n\nReceived fatal signal %d\n", sig); - node = state; - if (node) { - printf("Current action:\n"); - while (node) { - printf("\t%s\n", node->what); - node = node->next; - } - } + g_hash_table_foreach(info_table, (GHFunc)dump_action, 0); } _exit(1); } +static struct _state * +current_state(void) +{ + struct _state *info; + + if (info_table == NULL) + info_table = g_hash_table_new(0, 0); + + info = g_hash_table_lookup(info_table, (void *)CAMEL_TEST_ID); + if (info == NULL) { + info = g_malloc0(sizeof(*info)); + g_hash_table_insert(info_table, (void *)CAMEL_TEST_ID, info); + } + return info; +} + + void camel_test_init(int argc, char **argv) { void camel_init(void); @@ -44,7 +102,11 @@ void camel_test_init(int argc, char **argv) setup = 1; +#ifndef ENABLE_THREADS camel_init(); +#endif + + info_table = g_hash_table_new(0, 0); /* yeah, we do need ot thread init, even though camel isn't compiled with enable threads */ g_thread_init(NULL); @@ -71,15 +133,25 @@ void camel_test_init(int argc, char **argv) void camel_test_start(const char *what) { + struct _state *s; + + CAMEL_TEST_LOCK; + + s = current_state(); + if (!setup) camel_test_init(0, 0); ok = 1; + s->test = g_strdup(what); + if (camel_test_verbose > 0) { printf("Test: %s ... ", what); fflush(stdout); } + + CAMEL_TEST_UNLOCK; } void camel_test_push(const char *what, ...) @@ -87,6 +159,11 @@ void camel_test_push(const char *what, ...) struct _stack *node; va_list ap; char *text; + struct _state *s; + + CAMEL_TEST_LOCK; + + s = current_state(); va_start(ap, what); text = g_strdup_vprintf(what, ap); @@ -97,26 +174,40 @@ void camel_test_push(const char *what, ...) node = g_malloc(sizeof(*node)); node->what = text; - node->next = state; - state = node; + node->next = s->state; + node->fatal = 1; + s->state = node; + + CAMEL_TEST_UNLOCK; } void camel_test_pull(void) { struct _stack *node; + struct _state *s; - g_assert(state); + CAMEL_TEST_LOCK; + + s = current_state(); + + g_assert(s->state); if (camel_test_verbose > 3) - printf("Finish step: %s\n", state->what); + printf("Finish step: %s\n", s->state->what); - node = state; - state = node->next; + node = s->state; + s->state = node->next; + if (!node->fatal) + s->nonfatal--; g_free(node->what); g_free(node); + + CAMEL_TEST_UNLOCK; } /* where to set breakpoints */ +void camel_test_break(void); + void camel_test_break(void) { } @@ -130,75 +221,75 @@ void camel_test_fail(const char *why, ...) va_end(ap); } + void camel_test_failv(const char *why, va_list ap) { - struct _stack *node; char *text; + struct _state *s; + + CAMEL_TEST_LOCK; + + s = current_state(); text = g_strdup_vprintf(why, ap); - if ((nonfatal == NULL && camel_test_verbose > 0) - || (nonfatal && camel_test_verbose > 1)) { + if ((s->nonfatal == 0 && camel_test_verbose > 0) + || (s->nonfatal && camel_test_verbose > 1)) { printf("Failed.\n%s\n", text); camel_test_break(); } g_free(text); - if ((nonfatal == NULL && camel_test_verbose > 0) - || (nonfatal && camel_test_verbose > 2)) { - node = state; - if (node) { - printf("Current action:\n"); - while (node) { - printf("\t%s\n", node->what); - node = node->next; - } - } + if ((s->nonfatal == 0 && camel_test_verbose > 0) + || (s->nonfatal && camel_test_verbose > 2)) { + g_hash_table_foreach(info_table, (GHFunc)dump_action, 0); } - if (nonfatal == NULL) { + if (s->nonfatal == 0) { exit(1); } else { ok=0; if (camel_test_verbose > 1) { - printf("Known problem (ignored): %s\n", nonfatal->what); + printf("Known problem (ignored):\n"); + dump_action(CAMEL_TEST_ID, s, 0); } } + + CAMEL_TEST_UNLOCK; } -void camel_test_nonfatal(const char *why, ...) +void camel_test_nonfatal(const char *what, ...) { struct _stack *node; va_list ap; char *text; + struct _state *s; - va_start(ap, why); - text = g_strdup_vprintf(why, ap); + CAMEL_TEST_LOCK; + + s = current_state(); + + va_start(ap, what); + text = g_strdup_vprintf(what, ap); va_end(ap); - if (camel_test_verbose>3) + if (camel_test_verbose > 3) printf("Start nonfatal: %s\n", text); node = g_malloc(sizeof(*node)); node->what = text; - node->next = nonfatal; - nonfatal = node; + node->next = s->state; + node->fatal = 0; + s->nonfatal++; + s->state = node; + + CAMEL_TEST_UNLOCK; } void camel_test_fatal(void) { - struct _stack *node; - - g_assert(nonfatal); - - if (camel_test_verbose>3) - printf("Finish nonfatal: %s\n", nonfatal->what); - - node = nonfatal; - nonfatal = node->next; - g_free(node->what); - g_free(node); + camel_test_pull(); } void camel_test_end(void) diff --git a/camel/tests/lib/folders.c b/camel/tests/lib/folders.c index 6f24cd483a..12136b0cc7 100644 --- a/camel/tests/lib/folders.c +++ b/camel/tests/lib/folders.c @@ -11,7 +11,7 @@ test_folder_counts(CamelFolder *folder, int total, int unread) { GPtrArray *s; int i, myunread; - const CamelMessageInfo *info; + CamelMessageInfo *info; push("test folder counts %d total %d unread", total, unread); @@ -41,6 +41,7 @@ test_folder_counts(CamelFolder *folder, int total, int unread) info = camel_folder_get_message_info(folder, s->pdata[i]); if (info->flags & CAMEL_MESSAGE_SEEN) myunread--; + camel_folder_free_message_info(folder, info); } check(unread == myunread); camel_folder_free_uids(folder, s); @@ -81,7 +82,7 @@ void test_folder_message(CamelFolder *folder, const char *uid) { CamelMimeMessage *msg; - const CamelMessageInfo *info; + CamelMessageInfo *info; GPtrArray *s; int i; CamelException *ex = camel_exception_new(); @@ -93,6 +94,7 @@ test_folder_message(CamelFolder *folder, const char *uid) info = camel_folder_get_message_info(folder, uid); check(info != NULL); check(strcmp(camel_message_info_uid(info), uid) == 0); + camel_folder_free_message_info(folder, info); /* then, getting message */ msg = camel_folder_get_message(folder, uid, ex); @@ -137,7 +139,7 @@ void test_folder_not_message(CamelFolder *folder, const char *uid) { CamelMimeMessage *msg; - const CamelMessageInfo *info; + CamelMessageInfo *info; GPtrArray *s; int i; CamelException *ex = camel_exception_new(); @@ -314,7 +316,7 @@ test_folder_message_ops(CamelSession *session, const char *name, int local) int j; int indexed, max; GPtrArray *uids; - const CamelMessageInfo *info; + CamelMessageInfo *info; max=local?2:1; @@ -390,6 +392,7 @@ test_folder_message_ops(CamelSession *session, const char *name, int local) check_msg(strcmp(camel_message_info_subject(info), subject)==0, "info->subject %s", camel_message_info_subject(info)); camel_folder_free_uids(folder, uids); + camel_folder_free_message_info(folder, info); pull(); test_free(subject); @@ -429,6 +432,7 @@ test_folder_message_ops(CamelSession *session, const char *name, int local) check_msg(strcmp(camel_message_info_subject(info), subject)==0, "info->subject %s", camel_message_info_subject(info)); test_free(subject); + camel_folder_free_message_info(folder, info); pull(); } @@ -455,6 +459,7 @@ test_folder_message_ops(CamelSession *session, const char *name, int local) check_msg(strcmp(camel_message_info_subject(info), subject)==0, "info->subject %s", camel_message_info_subject(info)); test_free(subject); + camel_folder_free_message_info(folder, info); pull(); } pull(); @@ -483,6 +488,7 @@ test_folder_message_ops(CamelSession *session, const char *name, int local) check_msg(strcmp(camel_message_info_subject(info), subject)==0, "info->subject %s", camel_message_info_subject(info)); test_free(subject); + camel_folder_free_message_info(folder, info); pull(); } pull(); |