diff options
Diffstat (limited to 'camel/providers/nntp/camel-nntp-folder.c')
-rw-r--r-- | camel/providers/nntp/camel-nntp-folder.c | 386 |
1 files changed, 243 insertions, 143 deletions
diff --git a/camel/providers/nntp/camel-nntp-folder.c b/camel/providers/nntp/camel-nntp-folder.c index e3b4da6b40..47d3111a01 100644 --- a/camel/providers/nntp/camel-nntp-folder.c +++ b/camel/providers/nntp/camel-nntp-folder.c @@ -1,10 +1,10 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* camel-nntp-folder.c : Abstract class for an email folder */ - -/* - * Author : Chris Toshok <toshok@ximian.com> +/* camel-nntp-folder.c : Class for a news folder + * + * Authors : Chris Toshok <toshok@ximian.com> + * Michael Zucchi <notzed@ximian.com> * - * Copyright (C) 2000 Ximian . + * Copyright (C) 2001 Ximian . * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public @@ -33,21 +33,20 @@ #include <string.h> #include <fcntl.h> -#include "camel-folder-summary.h" -#include "camel-nntp-resp-codes.h" +#include "camel/string-utils.h" +#include "camel/camel-stream-mem.h" +#include "camel/camel-data-wrapper.h" +#include "camel/camel-mime-message.h" +#include "camel/camel-folder-search.h" +#include "camel/camel-exception.h" +#include "camel/camel-session.h" +#include "camel/camel-data-cache.h" + +#include "camel-nntp-summary.h" #include "camel-nntp-store.h" #include "camel-nntp-folder.h" #include "camel-nntp-store.h" -#include "camel-nntp-utils.h" - -#include "string-utils.h" -#include "camel-stream-mem.h" -#include "camel-data-wrapper.h" -#include "camel-mime-message.h" -#include "camel-folder-summary.h" -#include "camel-folder-search.h" - -#include "camel-exception.h" +#include "camel-nntp-private.h" static CamelFolderClass *parent_class=NULL; @@ -56,131 +55,131 @@ static CamelFolderClass *parent_class=NULL; #define CF_CLASS(so) CAMEL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so)) #define CNNTPS_CLASS(so) CAMEL_STORE_CLASS (CAMEL_OBJECT_GET_CLASS(so)) - static void -nntp_folder_sync (CamelFolder *folder, gboolean expunge, - CamelException *ex) +nntp_folder_sync (CamelFolder *folder, gboolean expunge, CamelException *ex) { - CamelNNTPStore *store; + CamelNNTPStore *nntp_store; + CamelFolderChangeInfo *changes = NULL; + CamelNNTPFolder *nntp_folder; - camel_folder_summary_save (folder->summary); + nntp_store = (CamelNNTPStore *)folder->parent_store; + nntp_folder = (CamelNNTPFolder *)folder; - store = CAMEL_NNTP_STORE (camel_folder_get_parent_store (folder)); + CAMEL_NNTP_STORE_LOCK(nntp_store, command_lock); - if (store->newsrc) - camel_nntp_newsrc_write (store->newsrc); + if (camel_nntp_summary_check((CamelNNTPSummary *)folder->summary, nntp_folder->changes, ex) != -1) + camel_folder_summary_save (folder->summary); + + if (camel_folder_change_info_changed(nntp_folder->changes)) { + changes = nntp_folder->changes; + nntp_folder->changes = camel_folder_change_info_new(); + } + + CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock); + + if (changes) { + camel_object_trigger_event((CamelObject *)folder, "folder_changed", changes); + camel_folder_change_info_free(changes); + } } static void -nntp_folder_set_message_flags (CamelFolder *folder, const char *uid, - guint32 flags, guint32 set) +nntp_folder_set_message_flags(CamelFolder *folder, const char *uid, guint32 flags, guint32 set) { ((CamelFolderClass *)parent_class)->set_message_flags(folder, uid, flags, set); - - if (flags & set & CAMEL_MESSAGE_SEEN) { - int article_num; - CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE (camel_folder_get_parent_store (folder)); - - sscanf (uid, "%d", &article_num); - - camel_nntp_newsrc_mark_article_read (nntp_store->newsrc, - folder->name, - article_num); - } } static CamelMimeMessage * -nntp_folder_get_message (CamelFolder *folder, const gchar *uid, CamelException *ex) +nntp_folder_get_message (CamelFolder *folder, const char *uid, CamelException *ex) { - CamelStream *message_stream = NULL; CamelMimeMessage *message = NULL; - CamelStore *parent_store; - char *buf; - int buf_len; - int buf_alloc; - int status; - gboolean done; - char *message_id; - - /* get the parent store */ - parent_store = camel_folder_get_parent_store (folder); - - message_id = strchr (uid, ','); - if (message_id) { - message_id++; - status = camel_nntp_command (CAMEL_NNTP_STORE( parent_store ), ex, NULL, "ARTICLE %s", message_id); + CamelNNTPStore *nntp_store; + CamelFolderChangeInfo *changes; + CamelNNTPFolder *nntp_folder; + CamelStream *stream = NULL; + int ret; + char *line; + const char *msgid; + + nntp_store = (CamelNNTPStore *)folder->parent_store; + nntp_folder = (CamelNNTPFolder *)folder; + + CAMEL_NNTP_STORE_LOCK(nntp_store, command_lock); + + msgid = strchr(uid, ','); + if (msgid == 0) { + camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, + _("Internal error: uid in invalid format: %s"), uid); + goto fail; } - - /* if the message_id was not found, raise an exception and return */ - if (message_id == NULL || status == NNTP_NO_SUCH_ARTICLE) { - camel_exception_setv (ex, - CAMEL_EXCEPTION_FOLDER_INVALID_UID, - _("Message %s not found."), - uid); - return NULL; - } - else if (status != NNTP_ARTICLE_FOLLOWS) { - /* XXX */ - g_warning ("weird nntp error %d\n", status); - return NULL; + msgid++; + + /* Lookup in cache, NEWS is global messageid's so use a global cache path */ + stream = camel_data_cache_get(nntp_store->cache, "cache", msgid, NULL); + if (stream == NULL) { + /* Not in cache, retrieve and put in cache */ + if (camel_nntp_store_set_folder(nntp_store, folder, nntp_folder->changes, ex) == -1) + goto fail; + + ret = camel_nntp_command(nntp_store, &line, "article %s", msgid); + if (ret == -1) + goto error; + + if (ret == 220) { + stream = camel_data_cache_add(nntp_store->cache, "cache", msgid, NULL); + if (stream) { + if (camel_stream_write_to_stream((CamelStream *)nntp_store->stream, stream) == -1) + goto error; + if (camel_stream_reset(stream) == -1) + goto error; + } else { + stream = (CamelStream *)nntp_store->stream; + camel_object_ref((CamelObject *)stream); + } + } } - /* this could probably done fairly easily with an nntp stream that - returns eof after '.' */ + if (stream) { + message = camel_mime_message_new(); + if (camel_data_wrapper_construct_from_stream((CamelDataWrapper *)message, stream) == -1) + goto error; - /* XXX ick ick ick. read the entire message into a buffer and - then create a stream_mem for it. */ - buf_alloc = 2048; - buf_len = 0; - buf = g_malloc(buf_alloc); - done = FALSE; + CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock); - buf[0] = 0; + camel_object_unref((CamelObject *)stream); + return message; + } - while (!done) { - int line_length; - char *line; + camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot get message %s: %s"), uid, line); - if (camel_remote_store_recv_line (CAMEL_REMOTE_STORE (parent_store), &line, ex) < 0) { - g_warning ("recv_line failed while building message\n"); - break; - } +error: + if (errno == EINTR) + camel_exception_setv(ex, CAMEL_EXCEPTION_USER_CANCEL, _("User cancelled")); + else + camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot get message %s: %s"), uid, strerror(errno)); - /* XXX check exception */ +fail: + if (message) + camel_object_unref((CamelObject *)message); - line_length = strlen ( line ); + if (stream) + camel_object_unref((CamelObject *)stream); - if (!strcmp(line, ".")) { - done = TRUE; - g_free (line); - } - else { - if (buf_len + line_length > buf_alloc) { - buf_alloc *= 2; - buf = g_realloc (buf, buf_alloc); - } - strcat(buf, line); - strcat(buf, "\n"); - buf_len += strlen(line) + 1; - g_free (line); - } + if (camel_folder_change_info_changed(nntp_folder->changes)) { + changes = nntp_folder->changes; + nntp_folder->changes = camel_folder_change_info_new(); + } else { + changes = NULL; } - /* create a stream bound to the message */ - message_stream = camel_stream_mem_new_with_buffer(buf, buf_len); - - message = camel_mime_message_new (); - camel_data_wrapper_construct_from_stream (CAMEL_DATA_WRAPPER(message), message_stream); - - camel_object_unref (CAMEL_OBJECT (message_stream)); + CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock); -#if 0 - gtk_signal_connect (CAMEL_OBJECT (message), "message_changed", message_changed, folder); -#endif - - g_free (buf); + if (changes) { + camel_object_trigger_event((CamelObject *)folder, "folder_changed", changes); + camel_folder_change_info_free(changes); + } - return message; + return NULL; } static GPtrArray* @@ -188,7 +187,9 @@ nntp_folder_search_by_expression (CamelFolder *folder, const char *expression, C { CamelNNTPFolder *nntp_folder = CAMEL_NNTP_FOLDER (folder); GPtrArray *matches, *summary; - + + CAMEL_NNTP_FOLDER_LOCK(nntp_folder, search_lock); + if(nntp_folder->search == NULL) nntp_folder->search = camel_folder_search_new(); @@ -198,11 +199,54 @@ nntp_folder_search_by_expression (CamelFolder *folder, const char *expression, C matches = camel_folder_search_execute_expression(nntp_folder->search, expression, ex); + CAMEL_NNTP_FOLDER_UNLOCK(nntp_folder, search_lock); + camel_folder_free_summary(folder, summary); return matches; } +static GPtrArray * +nntp_folder_search_by_uids(CamelFolder *folder, const char *expression, GPtrArray *uids, CamelException *ex) +{ + CamelNNTPFolder *nntp_folder = CAMEL_NNTP_FOLDER(folder); + GPtrArray *summary, *matches; + int i; + + /* NOTE: could get away without the search lock by creating a new + search object each time */ + + summary = g_ptr_array_new(); + for (i=0;i<uids->len;i++) { + CamelMessageInfo *info; + + info = camel_folder_get_message_info(folder, uids->pdata[i]); + if (info) + g_ptr_array_add(summary, info); + } + + if (summary->len == 0) + return summary; + + CAMEL_NNTP_FOLDER_LOCK(folder, search_lock); + + if (nntp_folder->search == NULL) + nntp_folder->search = camel_folder_search_new(); + + camel_folder_search_set_folder(nntp_folder->search, folder); + camel_folder_search_set_summary(nntp_folder->search, summary); + + matches = camel_folder_search_execute_expression(nntp_folder->search, expression, ex); + + CAMEL_NNTP_FOLDER_UNLOCK(folder, search_lock); + + for (i=0;i<summary->len;i++) + camel_folder_free_message_info(folder, summary->pdata[i]); + g_ptr_array_free(summary, TRUE); + + return matches; +} + static void nntp_folder_search_free(CamelFolder *folder, GPtrArray *result) { @@ -213,15 +257,19 @@ nntp_folder_search_free(CamelFolder *folder, GPtrArray *result) } static void -nntp_folder_finalize (CamelObject *object) +nntp_folder_init(CamelNNTPFolder *nntp_folder, CamelNNTPFolderClass *klass) { - CamelNNTPFolder *nntp_folder = CAMEL_NNTP_FOLDER (object); + nntp_folder->changes = camel_folder_change_info_new(); +} - g_free (nntp_folder->summary_file_path); +static void +nntp_folder_finalise (CamelNNTPFolder *nntp_folder) +{ + g_free(nntp_folder->storage_path); } static void -camel_nntp_folder_class_init (CamelNNTPFolderClass *camel_nntp_folder_class) +nntp_folder_class_init (CamelNNTPFolderClass *camel_nntp_folder_class) { CamelFolderClass *camel_folder_class = CAMEL_FOLDER_CLASS (camel_nntp_folder_class); @@ -234,6 +282,7 @@ camel_nntp_folder_class_init (CamelNNTPFolderClass *camel_nntp_folder_class) camel_folder_class->set_message_flags = nntp_folder_set_message_flags; camel_folder_class->get_message = nntp_folder_get_message; camel_folder_class->search_by_expression = nntp_folder_search_by_expression; + camel_folder_class->search_by_uids = nntp_folder_search_by_uids; camel_folder_class->search_free = nntp_folder_search_free; } @@ -246,46 +295,97 @@ camel_nntp_folder_get_type (void) camel_nntp_folder_type = camel_type_register (CAMEL_FOLDER_TYPE, "CamelNNTPFolder", sizeof (CamelNNTPFolder), sizeof (CamelNNTPFolderClass), - (CamelObjectClassInitFunc) camel_nntp_folder_class_init, + (CamelObjectClassInitFunc) nntp_folder_class_init, NULL, - (CamelObjectInitFunc) NULL, - (CamelObjectFinalizeFunc) nntp_folder_finalize); + (CamelObjectInitFunc) nntp_folder_init, + (CamelObjectFinalizeFunc) nntp_folder_finalise); } return camel_nntp_folder_type; } + +/* not yet */ +/* Idea is we update in stages, but this requires a different xover command, etc */ +#ifdef ASYNC_SUMMARY +struct _folder_check_msg { + CamelSessionThreadMsg msg; + CamelNNTPFolder *folder; +}; + +static void +folder_check(CamelSession *session, CamelSessionThreadMsg *msg) +{ + struct _folder_check_msg *m = (struct _folder_check_msg *)msg; + CamelException *ex; + CamelNNTPStore *nntp_store; + + nntp_store = (CamelNNTPStore *)m->folder->parent.parent_store; + + CAMEL_NNTP_STORE_LOCK(nntp_store, command_lock); + + ex = camel_exception_new(); + camel_nntp_summary_check((CamelNNTPSummary *)m->folder->parent.summary, m->folder->changes, ex); + camel_exception_free(ex); + + CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock); +} + +static void +folder_check_free(CamelSession *session, CamelSessionThreadMsg *msg) +{ + struct _folder_check_msg *m = (struct _folder_check_msg *)msg; + + camel_object_unref((CamelObject *)m->folder); +} + +static CamelSessionThreadOps folder_check_ops = { + folder_check, + folder_check_free, +}; +#endif + CamelFolder * camel_nntp_folder_new (CamelStore *parent, const char *folder_name, CamelException *ex) { - CamelFolder *folder = CAMEL_FOLDER (camel_object_new (CAMEL_NNTP_FOLDER_TYPE)); - CamelNNTPFolder *nntp_folder = CAMEL_NNTP_FOLDER (folder); - const gchar *root_dir_path; + CamelFolder *folder; + CamelNNTPFolder *nntp_folder; + char *root; + CamelService *service; +#ifdef ASYNC_SUMMARY + struct _folder_check_msg *m; +#endif - camel_folder_construct (folder, parent, folder_name, folder_name); - folder->folder_flags |= (CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY | - CAMEL_FOLDER_HAS_SEARCH_CAPABILITY); + service = (CamelService *)parent; + root = camel_session_get_storage_path(service->session, service, ex); + if (root == NULL) + return NULL; + + /* If this doesn't work, stuff wont save, but let it continue anyway */ + (void) camel_mkdir_hier(root, 0777); + + folder = (CamelFolder *) camel_object_new (CAMEL_NNTP_FOLDER_TYPE); + nntp_folder = (CamelNNTPFolder *)folder; - root_dir_path = camel_nntp_store_get_toplevel_dir (CAMEL_NNTP_STORE(folder->parent_store)); - nntp_folder->summary_file_path = g_strdup_printf ("%s/%s-ev-summary", - root_dir_path, - folder->name); + camel_folder_construct (folder, parent, folder_name, folder_name); + folder->folder_flags |= CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY|CAMEL_FOLDER_HAS_SEARCH_CAPABILITY; - folder->summary = camel_folder_summary_new (); - camel_folder_summary_set_filename (folder->summary, - nntp_folder->summary_file_path); + nntp_folder->storage_path = g_strdup_printf("%s/%s", root, folder->full_name); + g_free(root); + folder->summary = (CamelFolderSummary *)camel_nntp_summary_new(nntp_folder); camel_folder_summary_load (folder->summary); - - camel_nntp_get_headers (CAMEL_FOLDER( folder )->parent_store, - nntp_folder, ex); - if (camel_exception_get_id (ex)) { - camel_object_unref (CAMEL_OBJECT (folder)); - return NULL; +#ifdef ASYNC_SUMMARY + m = camel_session_thread_msg_new(service->session, &folder_check_ops, sizeof(*m)); + m->folder = nntp_folder; + camel_object_ref((CamelObject *)folder); + camel_session_thread_queue(service->session, &m->msg, 0); +#else + if (camel_nntp_summary_check((CamelNNTPSummary *)folder->summary, nntp_folder->changes, ex) == -1) { + camel_object_unref((CamelObject *)folder); + folder = NULL; } +#endif - /* XXX check return value */ - camel_folder_summary_save (folder->summary); - return folder; } |