diff options
author | Meilof Veeningen <meilof@wanadoo.nl> | 2004-01-13 06:20:40 +0800 |
---|---|---|
committer | Jeffrey Stedfast <fejj@src.gnome.org> | 2004-01-13 06:20:40 +0800 |
commit | 70f49bc0d7ce40521c4c99b7c9dbe6255db2c788 (patch) | |
tree | 6a1cd1a9114c3cb5d64dfe10c6fd08f39e853b03 /camel/providers/nntp | |
parent | fb0dddb43d9b8a502c6c32dc10e6e485f35bec74 (diff) | |
download | gsoc2013-evolution-70f49bc0d7ce40521c4c99b7c9dbe6255db2c788.tar.gz gsoc2013-evolution-70f49bc0d7ce40521c4c99b7c9dbe6255db2c788.tar.zst gsoc2013-evolution-70f49bc0d7ce40521c4c99b7c9dbe6255db2c788.zip |
now based on discofolder, cache_message and append_message implemented,
2004-01-12 Meilof Veeningen <meilof@wanadoo.nl>
* providers/nntp/camel-nntp-folder.[ch]: now based on discofolder,
cache_message and append_message implemented, only retrieve messages
when we are subscribed, some stubs
* providers/nntp/camel-nntp-provider.c: newsgroup name display
settings, password authentication, fix for check_equal where the
protocols wouldn't be checked
* providers/nntp/camel-nntp-store.[ch]: base on discostore with
online/offline support, subscriptions, downloading changed parts of
the newsgroup list, some stubs, authentication, automatic reconnect
* providers/nntp/camel-nntp-store-summary.[ch]: NNTP store
summary based on IMAP code
* providers/nntp/camel-nntp-summary.c: save summary after xover
* providers/nntp/camel-nntp-grouplist.h: added CamelNNTPGroupList
structs
* providers/nntp/Makefile.am: added store summary
svn path=/trunk/; revision=24178
Diffstat (limited to 'camel/providers/nntp')
-rw-r--r-- | camel/providers/nntp/Makefile.am | 7 | ||||
-rw-r--r-- | camel/providers/nntp/camel-nntp-folder.c | 540 | ||||
-rw-r--r-- | camel/providers/nntp/camel-nntp-folder.h | 5 | ||||
-rw-r--r-- | camel/providers/nntp/camel-nntp-grouplist.h | 11 | ||||
-rw-r--r-- | camel/providers/nntp/camel-nntp-provider.c | 27 | ||||
-rw-r--r-- | camel/providers/nntp/camel-nntp-store-summary.c | 430 | ||||
-rw-r--r-- | camel/providers/nntp/camel-nntp-store-summary.h | 100 | ||||
-rw-r--r-- | camel/providers/nntp/camel-nntp-store.c | 840 | ||||
-rw-r--r-- | camel/providers/nntp/camel-nntp-store.h | 15 | ||||
-rw-r--r-- | camel/providers/nntp/camel-nntp-summary.c | 24 | ||||
-rw-r--r-- | camel/providers/nntp/camel-nntp-summary.h | 1 |
11 files changed, 1686 insertions, 314 deletions
diff --git a/camel/providers/nntp/Makefile.am b/camel/providers/nntp/Makefile.am index 464624b82d..3e177226dd 100644 --- a/camel/providers/nntp/Makefile.am +++ b/camel/providers/nntp/Makefile.am @@ -6,6 +6,7 @@ camel_provider_LTLIBRARIES = libcamelnntp.la camel_provider_DATA = libcamelnntp.urls INCLUDES = -I../.. \ + -I$(top_srcdir) \ -I$(top_srcdir)/camel \ -I$(top_srcdir)/intl \ -I$(top_srcdir)/e-util \ @@ -20,13 +21,15 @@ libcamelnntp_la_SOURCES = \ camel-nntp-store.c \ camel-nntp-folder.c \ camel-nntp-stream.c \ - camel-nntp-summary.c + camel-nntp-summary.c \ + camel-nntp-store-summary.c libcamelnntpinclude_HEADERS = \ camel-nntp-store.h \ camel-nntp-folder.h \ camel-nntp-stream.h \ - camel-nntp-summary.h + camel-nntp-summary.h \ + camel-nntp-store-summary.h noinst_HEADERS = \ camel-nntp-private.h diff --git a/camel/providers/nntp/camel-nntp-folder.c b/camel/providers/nntp/camel-nntp-folder.c index 9f0832bc33..c0d7093a1c 100644 --- a/camel/providers/nntp/camel-nntp-folder.c +++ b/camel/providers/nntp/camel-nntp-folder.c @@ -44,13 +44,21 @@ #include "camel/camel-session.h" #include "camel/camel-data-cache.h" +#include "camel/camel-mime-filter-crlf.h" +#include "camel/camel-stream-filter.h" +#include "camel/camel-mime-message.h" +#include "camel/camel-multipart.h" +#include "camel/camel-mime-part.h" +#include "camel/camel-stream-buffer.h" + #include "camel-nntp-summary.h" #include "camel-nntp-store.h" #include "camel-nntp-folder.h" #include "camel-nntp-store.h" #include "camel-nntp-private.h" -static CamelFolderClass *parent_class = NULL; +static CamelFolderClass *folder_class = NULL; +static CamelDiscoFolderClass *parent_class = NULL; /* Returns the class for a CamelNNTPFolder */ #define CNNTPF_CLASS(so) CAMEL_NNTP_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so)) @@ -58,37 +66,111 @@ static CamelFolderClass *parent_class = NULL; #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_online (CamelFolder *folder, CamelException *ex) { CamelNNTPStore *nntp_store; CamelFolderChangeInfo *changes = NULL; CamelNNTPFolder *nntp_folder; - - nntp_store = (CamelNNTPStore *)folder->parent_store; - nntp_folder = (CamelNNTPFolder *)folder; - + + nntp_store = (CamelNNTPStore *) folder->parent_store; + nntp_folder = (CamelNNTPFolder *) folder; + CAMEL_NNTP_STORE_LOCK(nntp_store, command_lock); - - if (camel_nntp_summary_check((CamelNNTPSummary *)folder->summary, nntp_folder->changes, ex) != -1) + + 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); + camel_object_trigger_event ((CamelObject *) folder, "folder_changed", changes); + camel_folder_change_info_free (changes); + } +} + +static void +nntp_folder_sync_offline (CamelFolder *folder, CamelException *ex) +{ + camel_folder_summary_save (folder->summary); +} + +static void +nntp_folder_set_message_flags (CamelFolder *folder, const char *uid, guint32 flags, guint32 set) +{ + ((CamelFolderClass *) folder_class)->set_message_flags (folder, uid, flags, set); +} + +static CamelStream * +nntp_folder_download_message (CamelNNTPFolder *nntp_folder, const char *msgid, CamelException *ex) +{ + CamelNNTPStore *nntp_store = (CamelNNTPStore *) ((CamelFolder *) nntp_folder)->parent_store; + CamelStream *stream = NULL; + int ret; + char *line; + + if (camel_nntp_store_set_folder (nntp_store, (CamelFolder *) nntp_folder, nntp_folder->changes, ex) == -1) + return NULL; + + ret = camel_nntp_command (nntp_store, &line, "article %s", msgid); + if (ret == -1) + goto fail; + + 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 fail; + if (camel_stream_reset (stream) == -1) + goto fail; + } else { + stream = (CamelStream *) nntp_store->stream; + camel_object_ref (stream); + } } + + return stream; + + fail: + 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"), msgid, g_strerror (errno)); + + return NULL; } + static void -nntp_folder_set_message_flags(CamelFolder *folder, const char *uid, guint32 flags, guint32 set) +nntp_folder_cache_message (CamelDiscoFolder *disco_folder, const char *uid, CamelException *ex) { - ((CamelFolderClass *)parent_class)->set_message_flags(folder, uid, flags, set); + CamelNNTPStore *nntp_store = (CamelNNTPStore *)((CamelFolder *) disco_folder)->parent_store; + CamelStream *stream; + const char *msgid; + + if (!(msgid = strchr (uid, ','))) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Internal error: uid in invalid format: %s"), uid); + return; + } + msgid++; + + CAMEL_NNTP_STORE_LOCK(nntp_store, command_lock); + + stream = nntp_folder_download_message ((CamelNNTPFolder *) disco_folder, msgid, ex); + if (stream) { + camel_object_unref (stream); + } else { + /* failed to download message! */ + camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, + _("Could not get article %s from NNTP server"), uid); + } + + CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock); } static CamelMimeMessage * @@ -99,88 +181,77 @@ nntp_folder_get_message (CamelFolder *folder, const char *uid, CamelException *e CamelFolderChangeInfo *changes; CamelNNTPFolder *nntp_folder; CamelStream *stream = NULL; - int ret; - char *line; + char *line = NULL; const char *msgid; - - nntp_store = (CamelNNTPStore *)folder->parent_store; - nntp_folder = (CamelNNTPFolder *)folder; - + + 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); + + msgid = strchr (uid, ','); + if (msgid == NULL) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Internal error: uid in invalid format: %s"), uid); goto fail; } 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); + 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) + if (camel_disco_store_status ((CamelDiscoStore *) nntp_store) == CAMEL_DISCO_STORE_OFFLINE) { + camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, + _("This message is not currently available")); 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); - } } + + stream = nntp_folder_download_message (nntp_folder, msgid, ex); + if (stream == NULL) + goto fail; } - + if (stream) { - message = camel_mime_message_new(); - if (camel_data_wrapper_construct_from_stream((CamelDataWrapper *)message, stream) == -1) + message = camel_mime_message_new (); + if (camel_data_wrapper_construct_from_stream ((CamelDataWrapper *) message, stream) == -1) goto error; - + CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock); - - camel_object_unref((CamelObject *)stream); + + camel_object_unref (stream); + return message; } - - camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot get message %s: %s"), uid, line); - -error: + + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot get message %s: %s"), uid, line); + + error: if (errno == EINTR) - camel_exception_setv(ex, CAMEL_EXCEPTION_USER_CANCEL, _("User cancelled")); + 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)); - -fail: + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot get message %s: %s"), uid, g_strerror (errno)); + + fail: if (message) - camel_object_unref((CamelObject *)message); - + camel_object_unref (message); + if (stream) - camel_object_unref((CamelObject *)stream); - - if (camel_folder_change_info_changed(nntp_folder->changes)) { + camel_object_unref (stream); + + if (camel_folder_change_info_changed (nntp_folder->changes)) { changes = nntp_folder->changes; - nntp_folder->changes = camel_folder_change_info_new(); + nntp_folder->changes = camel_folder_change_info_new (); } else { changes = NULL; } - + 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); + camel_object_trigger_event ((CamelObject *) folder, "folder_changed", changes); + camel_folder_change_info_free (changes); } - + return NULL; } @@ -189,110 +260,252 @@ 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(); - camel_folder_search_set_folder(nntp_folder->search, folder); - summary = camel_folder_get_summary(folder); - camel_folder_search_set_summary(nntp_folder->search, summary); - - matches = camel_folder_search_execute_expression(nntp_folder->search, expression, ex); - + if (nntp_folder->search == NULL) + nntp_folder->search = camel_folder_search_new (); + + camel_folder_search_set_folder (nntp_folder->search, folder); + summary = camel_folder_get_summary (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(nntp_folder, search_lock); - - camel_folder_free_summary(folder, summary); - + + camel_folder_free_summary (folder, summary); + return matches; } static GPtrArray * -nntp_folder_search_by_uids(CamelFolder *folder, const char *expression, GPtrArray *uids, CamelException *ex) +nntp_folder_search_by_uids (CamelFolder *folder, const char *expression, GPtrArray *uids, CamelException *ex) { - CamelNNTPFolder *nntp_folder = CAMEL_NNTP_FOLDER(folder); + CamelNNTPFolder *nntp_folder = (CamelNNTPFolder *) 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++) { + + 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 ((info = camel_folder_get_message_info (folder, uids->pdata[i]))) + 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); - + 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); - + + 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) +nntp_folder_search_free (CamelFolder *folder, GPtrArray *result) { CamelNNTPFolder *nntp_folder = CAMEL_NNTP_FOLDER (folder); + + camel_folder_search_free_result (nntp_folder->search, result); +} + +static void +nntp_folder_append_message_online (CamelFolder *folder, CamelMimeMessage *mime_message, + const CamelMessageInfo *info, char **appended_uid, + CamelException *ex) +{ + CamelNNTPStore *nntp_store = (CamelNNTPStore *) folder->parent_store; + CamelStream *stream = (CamelStream*)nntp_store->stream; + CamelStreamFilter *filtered_stream; + CamelMimeFilter *crlffilter; + int ret; + unsigned int u; + struct _camel_header_raw *header, *savedhdrs, *n, *tail; + unsigned char *line; + char *cmdbuf = NULL, *respbuf = NULL; + + CAMEL_NNTP_STORE_LOCK(nntp_store, command_lock); + + /* send 'POST' command */ + ret = camel_nntp_command (nntp_store, (char **) &line, "post"); + + if (ret != 340) { + camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION, + _("Posting not allowed by news server")); + CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock); + return; + } + + /* send the 'Newsgroups: ' header */ + cmdbuf = g_strdup_printf ("Newsgroups: %s\r\n", folder->full_name); + + if (camel_stream_write (stream, cmdbuf, strlen (cmdbuf)) == -1) { + g_free (cmdbuf); + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Failed to send newsgroups header: %s: message not posted"), + g_strerror (errno)); + CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock); + return; + } + g_free (cmdbuf); + + /* setup stream filtering */ + crlffilter = camel_mime_filter_crlf_new (CAMEL_MIME_FILTER_CRLF_ENCODE, CAMEL_MIME_FILTER_CRLF_MODE_CRLF_DOTS); + filtered_stream = camel_stream_filter_new_with_stream (stream); + camel_stream_filter_add (filtered_stream, crlffilter); + camel_object_unref (crlffilter); + + /* remove mail 'To', 'CC', and 'BCC' headers */ + savedhdrs = NULL; + tail = (struct _camel_header_raw *) &savedhdrs; + + header = (struct _camel_header_raw *) &CAMEL_MIME_PART (mime_message)->headers; + n = header->next; + while (n != NULL) { + if (!g_ascii_strcasecmp (n->name, "To") || !g_ascii_strcasecmp (n->name, "Cc") || !g_ascii_strcasecmp (n->name, "Bcc")) { + header->next = n->next; + tail->next = n; + n->next = NULL; + tail = n; + } else { + header = n; + } + + n = header->next; + } + + /* write the message */ + ret = camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (mime_message), CAMEL_STREAM (filtered_stream)); + + /* restore the mail headers */ + header->next = savedhdrs; + + if (ret == -1) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Error posting to newsgroup: %s: message not posted"), + g_strerror (errno)); + camel_object_unref (filtered_stream); + CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock); + return; + } + + camel_stream_flush (CAMEL_STREAM (filtered_stream)); + camel_object_unref (filtered_stream); + + /* terminate the message body */ + if (camel_stream_write (stream, "\r\n.\r\n", 5) == -1) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Error posting to newsgroup: %s: message not posted"), + g_strerror (errno)); + CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock); + return; + } + + if (camel_nntp_stream_line (nntp_store->stream, (unsigned char **) &respbuf, &u) == -1) + respbuf = NULL; + + if (!respbuf || strncmp (respbuf, "240", 3)) { + if (!respbuf) + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Error reading response to posted message: message not posted")); + else + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Error posting message: %s: message not posted"), respbuf); + CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock); + return; + } + + CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock); + + return; +} - camel_folder_search_free_result(nntp_folder->search, result); +static void +nntp_folder_append_message_offline (CamelFolder *folder, CamelMimeMessage *mime_message, + const CamelMessageInfo *info, char **appended_uid, + CamelException *ex) +{ + camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, + _("You cannot post NNTP messages while working offline!")); +} +/* I do not know what to do this exactly. Looking at the IMAP implementation for this, it + seems to assume the message is copied to a folder on the same store. In that case, an + NNTP implementation doesn't seem to make any sense. */ +static void +nntp_folder_transfer_message (CamelFolder *source, GPtrArray *uids, CamelFolder *dest, + GPtrArray **transferred_uids, gboolean delete_orig, CamelException *ex) +{ + camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, + _("You cannot copy messages from a NNTP folder!")); } static void -nntp_folder_init(CamelNNTPFolder *nntp_folder, CamelNNTPFolderClass *klass) +nntp_folder_init (CamelNNTPFolder *nntp_folder, CamelNNTPFolderClass *klass) { struct _CamelNNTPFolderPrivate *p; - - nntp_folder->changes = camel_folder_change_info_new(); - p = nntp_folder->priv = g_malloc0(sizeof(*nntp_folder->priv)); - p->search_lock = g_mutex_new(); - p->cache_lock = g_mutex_new(); + + nntp_folder->changes = camel_folder_change_info_new (); + p = nntp_folder->priv = g_malloc0 (sizeof (*nntp_folder->priv)); + p->search_lock = g_mutex_new (); + p->cache_lock = g_mutex_new (); } static void nntp_folder_finalise (CamelNNTPFolder *nntp_folder) { struct _CamelNNTPFolderPrivate *p; - - g_free(nntp_folder->storage_path); + + camel_folder_summary_save (((CamelFolder*) nntp_folder)->summary); p = nntp_folder->priv; - g_mutex_free(p->search_lock); - g_mutex_free(p->cache_lock); - g_free(p); + g_mutex_free (p->search_lock); + g_mutex_free (p->cache_lock); + g_free (p); } static void nntp_folder_class_init (CamelNNTPFolderClass *camel_nntp_folder_class) { + CamelDiscoFolderClass *camel_disco_folder_class = CAMEL_DISCO_FOLDER_CLASS (camel_nntp_folder_class); CamelFolderClass *camel_folder_class = CAMEL_FOLDER_CLASS (camel_nntp_folder_class); - - parent_class = CAMEL_FOLDER_CLASS (camel_type_get_global_classfuncs (camel_folder_get_type ())); - + + parent_class = CAMEL_DISCO_FOLDER_CLASS (camel_type_get_global_classfuncs (camel_disco_folder_get_type ())); + folder_class = CAMEL_FOLDER_CLASS (camel_type_get_global_classfuncs (camel_folder_get_type ())); + /* virtual method definition */ - + /* virtual method overload */ - camel_folder_class->sync = nntp_folder_sync; + camel_disco_folder_class->sync_online = nntp_folder_sync_online; + camel_disco_folder_class->sync_resyncing = nntp_folder_sync_offline; + camel_disco_folder_class->sync_offline = nntp_folder_sync_offline; + camel_disco_folder_class->cache_message = nntp_folder_cache_message; + camel_disco_folder_class->append_online = nntp_folder_append_message_online; + camel_disco_folder_class->append_resyncing = nntp_folder_append_message_online; + camel_disco_folder_class->append_offline = nntp_folder_append_message_offline; + camel_disco_folder_class->transfer_online = nntp_folder_transfer_message; + camel_disco_folder_class->transfer_resyncing = nntp_folder_transfer_message; + camel_disco_folder_class->transfer_offline = nntp_folder_transfer_message; + 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; @@ -306,7 +519,7 @@ camel_nntp_folder_get_type (void) static CamelType camel_nntp_folder_type = CAMEL_INVALID_TYPE; if (camel_nntp_folder_type == CAMEL_INVALID_TYPE) { - camel_nntp_folder_type = camel_type_register (CAMEL_FOLDER_TYPE, "CamelNNTPFolder", + camel_nntp_folder_type = camel_type_register (CAMEL_DISCO_FOLDER_TYPE, "CamelNNTPFolder", sizeof (CamelNNTPFolder), sizeof (CamelNNTPFolderClass), (CamelObjectClassInitFunc) nntp_folder_class_init, @@ -333,15 +546,15 @@ 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; - + + 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); - + + 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); } @@ -349,8 +562,8 @@ 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); + + camel_object_unref (m->folder); } static CamelSessionThreadOps folder_check_ops = { @@ -369,37 +582,48 @@ camel_nntp_folder_new (CamelStore *parent, const char *folder_name, CamelExcepti #ifdef ASYNC_SUMMARY struct _folder_check_msg *m; #endif - - service = (CamelService *)parent; - root = camel_session_get_storage_path(service->session, service, ex); + CamelStoreInfo *si; + gboolean subscribed = TRUE; + + 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 */ camel_mkdir (root, 0777); - + folder = (CamelFolder *) camel_object_new (CAMEL_NNTP_FOLDER_TYPE); nntp_folder = (CamelNNTPFolder *)folder; - + camel_folder_construct (folder, parent, folder_name, folder_name); folder->folder_flags |= CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY|CAMEL_FOLDER_HAS_SEARCH_CAPABILITY; - - 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); + + nntp_folder->storage_path = g_build_filename (root, folder->full_name, NULL); + g_free (root); + + folder->summary = (CamelFolderSummary *) camel_nntp_summary_new (nntp_folder); camel_folder_summary_load (folder->summary); + + si = camel_store_summary_path ((CamelStoreSummary *) ((CamelNNTPStore*) parent)->summary, folder_name); + if (si) { + subscribed = (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) != 0; + camel_store_summary_info_free ((CamelStoreSummary *) ((CamelNNTPStore*) parent)->summary, si); + } + + if (subscribed) { #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); + m = camel_session_thread_msg_new (service->session, &folder_check_ops, sizeof(*m)); + m->folder = nntp_folder; + camel_object_ref (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; - } + if (camel_nntp_summary_check ((CamelNNTPSummary *) folder->summary, nntp_folder->changes, ex) == -1) { + camel_object_unref (folder); + folder = NULL; + } #endif - + } + return folder; } diff --git a/camel/providers/nntp/camel-nntp-folder.h b/camel/providers/nntp/camel-nntp-folder.h index 300d61762d..378fee69cc 100644 --- a/camel/providers/nntp/camel-nntp-folder.h +++ b/camel/providers/nntp/camel-nntp-folder.h @@ -33,6 +33,7 @@ extern "C" { #endif /* __cplusplus }*/ #include "camel/camel-folder.h" +#include "camel/camel-disco-folder.h" /* #include "camel-store.h" */ @@ -42,7 +43,7 @@ extern "C" { #define CAMEL_IS_NNTP_FOLDER(o) (CAMEL_CHECK_TYPE((o), CAMEL_NNTP_FOLDER_TYPE)) typedef struct _CamelNNTPFolder { - CamelFolder parent; + CamelDiscoFolder parent; struct _CamelNNTPFolderPrivate *priv; @@ -52,7 +53,7 @@ typedef struct _CamelNNTPFolder { } CamelNNTPFolder; typedef struct _CamelNNTPFolderClass { - CamelFolderClass parent; + CamelDiscoFolderClass parent; /* Virtual methods */ diff --git a/camel/providers/nntp/camel-nntp-grouplist.h b/camel/providers/nntp/camel-nntp-grouplist.h index edd1e64cb9..b45d352cd8 100644 --- a/camel/providers/nntp/camel-nntp-grouplist.h +++ b/camel/providers/nntp/camel-nntp-grouplist.h @@ -40,9 +40,12 @@ struct CamelNNTPGroupList { GList *group_list; }; -CamelNNTPGroupList* camel_nntp_grouplist_fetch (CamelNNTPStore *store, CamelException *ex); -gint camel_nntp_grouplist_update (CamelNNTPGroupList *group_list, CamelException *ex); -void camel_nntp_grouplist_save (CamelNNTPGroupList *group_list, CamelException *ex); -void camel_nntp_grouplist_free (CamelNNTPGroupList *group_list); +typedef struct CamelNNTPGroupList _CamelNNTPGroupList; +typedef struct CamelNNTPGroupListEntry _CamelNNTPGroupListEntry; + +struct CamelNNTPGroupList* camel_nntp_grouplist_fetch (CamelNNTPStore *store, CamelException *ex); +gint camel_nntp_grouplist_update (struct CamelNNTPGroupList *group_list, CamelException *ex); +void camel_nntp_grouplist_save (struct CamelNNTPGroupList *group_list, CamelException *ex); +void camel_nntp_grouplist_free (struct CamelNNTPGroupList *group_list); #endif /* CAMEL_NNTP_GROUPLIST_H */ diff --git a/camel/providers/nntp/camel-nntp-provider.c b/camel/providers/nntp/camel-nntp-provider.c index 8f54b3ee98..886c09983e 100644 --- a/camel/providers/nntp/camel-nntp-provider.c +++ b/camel/providers/nntp/camel-nntp-provider.c @@ -36,6 +36,17 @@ static guint nntp_url_hash (gconstpointer key); static gint check_equal (char *s1, char *s2); static gint nntp_url_equal (gconstpointer a, gconstpointer b); +CamelProviderConfEntry nntp_conf_entries[] = { + { CAMEL_PROVIDER_CONF_SECTION_START, "folders", NULL, + N_("Folders") }, + { CAMEL_PROVIDER_CONF_CHECKBOX, "show_short_notation", NULL, + N_("Show folders in short notation (e.g. c.o.linux rather than comp.os.linux)"), "1" }, + { CAMEL_PROVIDER_CONF_CHECKBOX, "folder_hierarchy_relative", NULL, + N_("In the subscription dialog, show relative folder names"), "1" }, + { CAMEL_PROVIDER_CONF_SECTION_END }, + { CAMEL_PROVIDER_CONF_END } +}; + static CamelProvider news_provider = { "nntp", N_("USENET news"), @@ -51,9 +62,21 @@ static CamelProvider news_provider = { CAMEL_URL_NEED_HOST | CAMEL_URL_ALLOW_USER | CAMEL_URL_ALLOW_PASSWORD | CAMEL_URL_ALLOW_AUTH, + nntp_conf_entries + /* ... */ }; +CamelServiceAuthType camel_nntp_password_authtype = { + N_("Password"), + + N_("This option will authenticate with the NNTP server using a " + "plaintext password."), + + "", + TRUE +}; + void camel_provider_module_init (CamelSession *session) { @@ -62,6 +85,7 @@ camel_provider_module_init (CamelSession *session) news_provider.url_hash = nntp_url_hash; news_provider.url_equal = nntp_url_equal; + news_provider.authtypes = g_list_append (NULL, &camel_nntp_password_authtype); camel_session_register_provider (session, &news_provider); } @@ -107,7 +131,8 @@ nntp_url_equal (gconstpointer a, gconstpointer b) { const CamelURL *u1 = a, *u2 = b; - return check_equal (u1->user, u2->user) + return check_equal(u1->protocol, u2->protocol) + && check_equal (u1->user, u2->user) && check_equal (u1->host, u2->host) && u1->port == u2->port; } diff --git a/camel/providers/nntp/camel-nntp-store-summary.c b/camel/providers/nntp/camel-nntp-store-summary.c new file mode 100644 index 0000000000..8cade2a5eb --- /dev/null +++ b/camel/providers/nntp/camel-nntp-store-summary.c @@ -0,0 +1,430 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2002 Ximian Inc. + * + * Authors: Michael Zucchi <notzed@ximian.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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. + */ + +/* currently, this is just a straigt s/imap/nntp from the IMAP file*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <unistd.h> +#include <ctype.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> + +#include "camel-nntp-store-summary.h" + +#include "camel-file-utils.h" + +#include "e-util/md5-utils.h" +#include "e-util/e-memory.h" + +#include "camel-private.h" +#include "camel-utf8.h" + +#define d(x) +#define io(x) /* io debug */ + +#define CAMEL_NNTP_STORE_SUMMARY_VERSION_0 (0) + +#define CAMEL_NNTP_STORE_SUMMARY_VERSION (0) + +#define _PRIVATE(o) (((CamelNNTPStoreSummary *)(o))->priv) + +static int summary_header_load(CamelStoreSummary *, FILE *); +static int summary_header_save(CamelStoreSummary *, FILE *); + +/*static CamelStoreInfo * store_info_new(CamelStoreSummary *, const char *);*/ +static CamelStoreInfo * store_info_load(CamelStoreSummary *, FILE *); +static int store_info_save(CamelStoreSummary *, FILE *, CamelStoreInfo *); +static void store_info_free(CamelStoreSummary *, CamelStoreInfo *); + +static const char *store_info_string(CamelStoreSummary *, const CamelStoreInfo *, int); +static void store_info_set_string(CamelStoreSummary *, CamelStoreInfo *, int, const char *); + +static void camel_nntp_store_summary_class_init (CamelNNTPStoreSummaryClass *klass); +static void camel_nntp_store_summary_init (CamelNNTPStoreSummary *obj); +static void camel_nntp_store_summary_finalise (CamelObject *obj); + +static CamelStoreSummaryClass *camel_nntp_store_summary_parent; + +static void +camel_nntp_store_summary_class_init (CamelNNTPStoreSummaryClass *klass) +{ + CamelStoreSummaryClass *ssklass = (CamelStoreSummaryClass *)klass; + + ssklass->summary_header_load = summary_header_load; + ssklass->summary_header_save = summary_header_save; + + /*ssklass->store_info_new = store_info_new;*/ + ssklass->store_info_load = store_info_load; + ssklass->store_info_save = store_info_save; + ssklass->store_info_free = store_info_free; + + ssklass->store_info_string = store_info_string; + ssklass->store_info_set_string = store_info_set_string; +} + +static void +camel_nntp_store_summary_init (CamelNNTPStoreSummary *s) +{ + /*struct _CamelNNTPStoreSummaryPrivate *p; + + p = _PRIVATE(s) = g_malloc0(sizeof(*p));*/ + + ((CamelStoreSummary *) s)->store_info_size = sizeof (CamelNNTPStoreInfo); + s->version = CAMEL_NNTP_STORE_SUMMARY_VERSION; + memset (&s->last_newslist, 0, sizeof (s->last_newslist)); +} + +static void +camel_nntp_store_summary_finalise (CamelObject *obj) +{ + /*struct _CamelNNTPStoreSummaryPrivate *p;*/ + /*CamelNNTPStoreSummary *s = (CamelNNTPStoreSummary *)obj;*/ + + /*p = _PRIVATE(obj); + g_free(p);*/ +} + +CamelType +camel_nntp_store_summary_get_type (void) +{ + static CamelType type = CAMEL_INVALID_TYPE; + + if (type == CAMEL_INVALID_TYPE) { + camel_nntp_store_summary_parent = (CamelStoreSummaryClass *)camel_store_summary_get_type(); + type = camel_type_register((CamelType)camel_nntp_store_summary_parent, "CamelNNTPStoreSummary", + sizeof (CamelNNTPStoreSummary), + sizeof (CamelNNTPStoreSummaryClass), + (CamelObjectClassInitFunc) camel_nntp_store_summary_class_init, + NULL, + (CamelObjectInitFunc) camel_nntp_store_summary_init, + (CamelObjectFinalizeFunc) camel_nntp_store_summary_finalise); + } + + return type; +} + +/** + * camel_nntp_store_summary_new: + * + * Create a new CamelNNTPStoreSummary object. + * + * Return value: A new CamelNNTPStoreSummary widget. + **/ +CamelNNTPStoreSummary * +camel_nntp_store_summary_new (void) +{ + return (CamelNNTPStoreSummary *) camel_object_new (camel_nntp_store_summary_get_type ()); +} + +/** + * camel_nntp_store_summary_full_name: + * @s: + * @path: + * + * Retrieve a summary item by full name. + * + * 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 @full_name name + * is not available. + * It must be freed using camel_store_summary_info_free(). + **/ +CamelNNTPStoreInfo * +camel_nntp_store_summary_full_name(CamelNNTPStoreSummary *s, const char *full_name) +{ + int count, i; + CamelNNTPStoreInfo *info; + + count = camel_store_summary_count ((CamelStoreSummary *) s); + for (i = 0; i < count; i++) { + info = (CamelNNTPStoreInfo *)camel_store_summary_index ((CamelStoreSummary *) s, i); + if (info) { + if (strcmp (info->full_name, full_name) == 0) + return info; + camel_store_summary_info_free ((CamelStoreSummary *) s, (CamelStoreInfo *)info); + } + } + + return NULL; +} + +char * +camel_nntp_store_summary_full_to_path (CamelNNTPStoreSummary *s, const char *full_name, char dir_sep) +{ + char *path, *p; + int c; + const char *f; + + if (dir_sep != '/') { + p = path = g_alloca (strlen (full_name) * 3 + 1); + f = full_name; + while ((c = *f++ & 0xff)) { + if (c == dir_sep) + *p++ = '/'; + else if (c == '/' || c == '%') + p += sprintf (p, "%%%02X", c); + else + *p++ = c; + } + *p = 0; + } else + path = (char *) full_name; + + return camel_utf7_utf8 (path); +} + +static guint32 +hexnib (guint32 c) +{ + if (c >= '0' && c <= '9') + return c-'0'; + else if (c >= 'A' && c <= 'Z') + return c - 'A' + 10; + else + return 0; +} + +char * +camel_nntp_store_summary_path_to_full (CamelNNTPStoreSummary *s, const char *path, char dir_sep) +{ + unsigned char *full, *f; + guint32 c, v = 0; + const char *p; + int state=0; + char *subpath, *last = NULL; + CamelStoreInfo *si; + + /* check to see if we have a subpath of path already defined */ + subpath = g_alloca (strlen (path) + 1); + strcpy (subpath, path); + do { + si = camel_store_summary_path ((CamelStoreSummary *) s, subpath); + if (si == NULL) { + last = strrchr (subpath, '/'); + if (last) + *last = 0; + } + } while (si == NULL && last); + + /* path is already present, use the raw version we have */ + if (si && strlen (subpath) == strlen (path)) { + f = g_strdup (camel_nntp_store_info_full_name (s, si)); + camel_store_summary_info_free ((CamelStoreSummary *) s, si); + return f; + } + + f = full = g_alloca (strlen (path)*2+1); + if (si) + p = path + strlen (subpath); + else + p = path; + + while ((c = camel_utf8_getc ((const unsigned char **) &p))) { + switch (state) { + case 0: + if (c == '%') { + state = 1; + } else { + if (c == '/') + c = dir_sep; + camel_utf8_putc(&f, c); + } + break; + case 1: + state = 2; + v = hexnib (c) << 4; + break; + case 2: + state = 0; + v |= hexnib (c); + camel_utf8_putc (&f, v); + break; + } + } + camel_utf8_putc (&f, c); + + /* merge old path part if required */ + f = camel_utf8_utf7 (full); + if (si) { + full = g_strdup_printf ("%s%s", camel_nntp_store_info_full_name (s, si), f); + g_free (f); + camel_store_summary_info_free ((CamelStoreSummary *) s, si); + f = full; + } + + return f; +} + +CamelNNTPStoreInfo * +camel_nntp_store_summary_add_from_full (CamelNNTPStoreSummary *s, const char *full, char dir_sep) +{ + CamelNNTPStoreInfo *info; + char *pathu8; + int len; + char *full_name; + + d(printf("adding full name '%s' '%c'\n", full, dir_sep)); + + len = strlen (full); + full_name = g_alloca (len+1); + strcpy(full_name, full); + if (full_name[len-1] == dir_sep) + full_name[len-1] = 0; + + info = camel_nntp_store_summary_full_name (s, full_name); + if (info) { + camel_store_summary_info_free ((CamelStoreSummary *) s, (CamelStoreInfo *) info); + d(printf(" already there\n")); + return info; + } + + pathu8 = camel_nntp_store_summary_full_to_path (s, full_name, dir_sep); + + info = (CamelNNTPStoreInfo *) camel_store_summary_add_from_path ((CamelStoreSummary *) s, pathu8); + if (info) { + d(printf(" '%s' -> '%s'\n", pathu8, full_name)); + camel_store_info_set_string((CamelStoreSummary *)s, (CamelStoreInfo *)info, CAMEL_NNTP_STORE_INFO_FULL_NAME, full_name); + } else + d(printf(" failed\n")); + + return info; +} + +static int +summary_header_load (CamelStoreSummary *s, FILE *in) +{ + CamelNNTPStoreSummary *is = (CamelNNTPStoreSummary *) s; + gint32 version, nil; + + if (camel_nntp_store_summary_parent->summary_header_load ((CamelStoreSummary *) s, in) == -1 + || camel_file_util_decode_fixed_int32 (in, &version) == -1) + return -1; + + is->version = version; + + if (version < CAMEL_NNTP_STORE_SUMMARY_VERSION_0) { + g_warning("Store summary header version too low"); + return -1; + } + + if (fread (is->last_newslist, 1, NNTP_DATE_SIZE, in) < NNTP_DATE_SIZE) + return -1; + + camel_file_util_decode_fixed_int32 (in, &nil); + + return 0; +} + +static int +summary_header_save (CamelStoreSummary *s, FILE *out) +{ + CamelNNTPStoreSummary *is = (CamelNNTPStoreSummary *) s; + + /* always write as latest version */ + if (camel_nntp_store_summary_parent->summary_header_save ((CamelStoreSummary *) s, out) == -1 + || camel_file_util_encode_fixed_int32 (out, CAMEL_NNTP_STORE_SUMMARY_VERSION) == -1 + || fwrite (is->last_newslist, 1, NNTP_DATE_SIZE, out) < NNTP_DATE_SIZE + || camel_file_util_encode_fixed_int32 (out, 0) == -1) + return -1; + + return 0; +} + +static CamelStoreInfo * +store_info_load (CamelStoreSummary *s, FILE *in) +{ + CamelNNTPStoreInfo *ni; + + ni = (CamelNNTPStoreInfo *) camel_nntp_store_summary_parent->store_info_load (s, in); + if (ni) { + if (camel_file_util_decode_string (in, &ni->full_name) == -1) { + camel_store_summary_info_free (s, (CamelStoreInfo *) ni); + ni = NULL; + } + /* set the URL */ + } + + return (CamelStoreInfo *) ni; +} + +static int +store_info_save (CamelStoreSummary *s, FILE *out, CamelStoreInfo *mi) +{ + CamelNNTPStoreInfo *isi = (CamelNNTPStoreInfo *)mi; + + if (camel_nntp_store_summary_parent->store_info_save (s, out, mi) == -1 + || camel_file_util_encode_string (out, isi->full_name) == -1) + return -1; + + return 0; +} + +static void +store_info_free (CamelStoreSummary *s, CamelStoreInfo *mi) +{ + CamelNNTPStoreInfo *nsi = (CamelNNTPStoreInfo *) mi; + + g_free (nsi->full_name); + camel_nntp_store_summary_parent->store_info_free (s, mi); +} + +static const char * +store_info_string(CamelStoreSummary *s, const CamelStoreInfo *mi, int type) +{ + CamelNNTPStoreInfo *nsi = (CamelNNTPStoreInfo *)mi; + + /* FIXME: Locks? */ + + g_assert (mi != NULL); + + switch (type) { + case CAMEL_NNTP_STORE_INFO_FULL_NAME: + return nsi->full_name; + default: + return camel_nntp_store_summary_parent->store_info_string(s, mi, type); + } +} + +static void +store_info_set_string(CamelStoreSummary *s, CamelStoreInfo *mi, int type, const char *str) +{ + CamelNNTPStoreInfo *nsi = (CamelNNTPStoreInfo *)mi; + + g_assert(mi != NULL); + + switch (type) { + case CAMEL_NNTP_STORE_INFO_FULL_NAME: + d(printf("Set full name %s -> %s\n", nsi->full_name, str)); + CAMEL_STORE_SUMMARY_LOCK(s, summary_lock); + g_free (nsi->full_name); + nsi->full_name = g_strdup (str); + CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock); + break; + default: + camel_nntp_store_summary_parent->store_info_set_string (s, mi, type, str); + break; + } +} diff --git a/camel/providers/nntp/camel-nntp-store-summary.h b/camel/providers/nntp/camel-nntp-store-summary.h new file mode 100644 index 0000000000..cf0401f809 --- /dev/null +++ b/camel/providers/nntp/camel-nntp-store-summary.h @@ -0,0 +1,100 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2002 Ximian Inc. + * + * Authors: Michael Zucchi <notzed@ximian.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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. + */ + +/* currently, this is just a straigt s/imap/nntp from the IMAP file*/ + + +#ifndef _CAMEL_NNTP_STORE_SUMMARY_H +#define _CAMEL_NNTP_STORE_SUMMARY_H + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus */ + +#include <camel/camel-object.h> +#include <camel/camel-store-summary.h> + +#define CAMEL_NNTP_STORE_SUMMARY(obj) CAMEL_CHECK_CAST (obj, camel_nntp_store_summary_get_type (), CamelNNTPStoreSummary) +#define CAMEL_NNTP_STORE_SUMMARY_CLASS(klass) CAMEL_CHECK_CLASS_CAST (klass, camel_nntp_store_summary_get_type (), CamelNNTPStoreSummaryClass) +#define CAMEL_IS_NNTP_STORE_SUMMARY(obj) CAMEL_CHECK_TYPE (obj, camel_nntp_store_summary_get_type ()) + +typedef struct _CamelNNTPStoreSummary CamelNNTPStoreSummary; +typedef struct _CamelNNTPStoreSummaryClass CamelNNTPStoreSummaryClass; + +typedef struct _CamelNNTPStoreInfo CamelNNTPStoreInfo; + +enum { + CAMEL_NNTP_STORE_INFO_FULL_NAME = CAMEL_STORE_INFO_LAST, + CAMEL_NNTP_STORE_INFO_LAST, +}; + +struct _CamelNNTPStoreInfo { + CamelStoreInfo info; + char *full_name; +}; + +#define NNTP_DATE_SIZE 14 + +struct _CamelNNTPStoreSummary { + CamelStoreSummary summary; + + struct _CamelNNTPStoreSummaryPrivate *priv; + + /* header info */ + guint32 version; /* version of base part of file */ + char last_newslist[NNTP_DATE_SIZE]; +}; + +struct _CamelNNTPStoreSummaryClass { + CamelStoreSummaryClass summary_class; +}; + +CamelType camel_nntp_store_summary_get_type (void); +CamelNNTPStoreSummary *camel_nntp_store_summary_new (void); + +/* TODO: this api needs some more work, needs to support lists */ +/*CamelNNTPStoreNamespace *camel_nntp_store_summary_namespace_new(CamelNNTPStoreSummary *s, const char *full_name, char dir_sep);*/ +/*void camel_nntp_store_summary_namespace_set(CamelNNTPStoreSummary *s, CamelNNTPStoreNamespace *ns);*/ +/*CamelNNTPStoreNamespace *camel_nntp_store_summary_namespace_find_path(CamelNNTPStoreSummary *s, const char *path);*/ +/*CamelNNTPStoreNamespace *camel_nntp_store_summary_namespace_find_full(CamelNNTPStoreSummary *s, const char *full_name);*/ + +/* helper macro's */ +#define camel_nntp_store_info_full_name(s, i) (camel_store_info_string((CamelStoreSummary *)s, (const CamelStoreInfo *)i, CAMEL_NNTP_STORE_INFO_FULL_NAME)) + +/* converts to/from utf8 canonical nasmes */ +char *camel_nntp_store_summary_full_to_path(CamelNNTPStoreSummary *s, const char *full_name, char dir_sep); + +char *camel_nntp_store_summary_path_to_full(CamelNNTPStoreSummary *s, const char *path, char dir_sep); +char *camel_nntp_store_summary_dotted_to_full(CamelNNTPStoreSummary *s, const char *dotted, char dir_sep); + +CamelNNTPStoreInfo *camel_nntp_store_summary_full_name(CamelNNTPStoreSummary *s, const char *full_name); +CamelNNTPStoreInfo *camel_nntp_store_summary_add_from_full(CamelNNTPStoreSummary *s, const char *full_name, char dir_sep); + +/* a convenience lookup function. always use this if path known */ +char *camel_nntp_store_summary_full_from_path(CamelNNTPStoreSummary *s, const char *path); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* ! _CAMEL_NNTP_STORE_SUMMARY_H */ diff --git a/camel/providers/nntp/camel-nntp-store.c b/camel/providers/nntp/camel-nntp-store.c index 531d4aefdf..fc067062ba 100644 --- a/camel/providers/nntp/camel-nntp-store.c +++ b/camel/providers/nntp/camel-nntp-store.c @@ -40,10 +40,15 @@ #include <camel/camel-tcp-stream-raw.h> #include <camel/camel-tcp-stream-ssl.h> +#include <camel/camel-disco-store.h> +#include <camel/camel-disco-diary.h> + #include "camel-nntp-summary.h" #include "camel-nntp-store.h" +#include "camel-nntp-store-summary.h" #include "camel-nntp-folder.h" #include "camel-nntp-private.h" +#include "camel-nntp-resp-codes.h" #define w(x) extern int camel_verbose_debug; @@ -54,10 +59,7 @@ extern int camel_verbose_debug; #define DUMP_EXTENSIONS -/* define if you want the subscribe ui to show folders in tree form */ -/* #define INFO_AS_TREE */ - -static CamelStoreClass *parent_class = NULL; +static CamelDiscoStoreClass *parent_class = NULL; static CamelServiceClass *service_class = NULL; /* Returns the class for a CamelNNTPStore */ @@ -65,6 +67,16 @@ static CamelServiceClass *service_class = NULL; #define CF_CLASS(so) CAMEL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so)) #define CNNTPF_CLASS(so) CAMEL_NNTP_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so)) +static void nntp_construct (CamelService *service, CamelSession *session, + CamelProvider *provider, CamelURL *url, + CamelException *ex); + + +static gboolean +nntp_can_work_offline(CamelDiscoStore *store) +{ + return TRUE; +} enum { USE_SSL_NEVER, @@ -76,25 +88,23 @@ static gboolean connect_to_server (CamelService *service, int ssl_mode, CamelException *ex) { CamelNNTPStore *store = (CamelNNTPStore *) service; + CamelDiscoStore *disco_store = (CamelDiscoStore*) service; CamelStream *tcp_stream; gboolean retval = FALSE; unsigned char *buf; unsigned int len; struct hostent *h; int port, ret; + char *path; CAMEL_NNTP_STORE_LOCK(store, command_lock); - + /* setup store-wide cache */ if (store->cache == NULL) { - char *root; - - root = camel_session_get_storage_path (service->session, service, ex); - if (root == NULL) + if (store->storage_path == NULL) goto fail; - store->cache = camel_data_cache_new (root, 0, ex); - g_free (root); + store->cache = camel_data_cache_new (store->storage_path, 0, ex); if (store->cache == NULL) goto fail; @@ -103,8 +113,7 @@ connect_to_server (CamelService *service, int ssl_mode, CamelException *ex) camel_data_cache_set_expire_access (store->cache, 60*60*24*5); } - h = camel_service_gethost (service, ex); - if (!h) + if (!(h = camel_service_gethost (service, ex))) goto fail; port = service->url->port ? service->url->port : NNTP_PORT; @@ -131,13 +140,13 @@ connect_to_server (CamelService *service, int ssl_mode, CamelException *ex) _("Could not connect to %s (port %d): %s"), service->url->host, port, g_strerror (errno)); - camel_object_unref (CAMEL_OBJECT (tcp_stream)); + camel_object_unref (tcp_stream); goto fail; } store->stream = (CamelNNTPStream *) camel_nntp_stream_new (tcp_stream); - camel_object_unref (CAMEL_OBJECT (tcp_stream)); + camel_object_unref (tcp_stream); /* Read the greeting, if any. */ if (camel_nntp_stream_line (store->stream, &buf, &len) == -1) { @@ -149,7 +158,7 @@ connect_to_server (CamelService *service, int ssl_mode, CamelException *ex) _("Could not read greeting from %s: %s"), service->url->host, g_strerror (errno)); - camel_object_unref (CAMEL_OBJECT (store->stream)); + camel_object_unref (store->stream); store->stream = NULL; goto fail; @@ -161,19 +170,27 @@ connect_to_server (CamelService *service, int ssl_mode, CamelException *ex) _("NNTP server %s returned error code %d: %s"), service->url->host, len, buf); - camel_object_unref (CAMEL_OBJECT (store->stream)); + camel_object_unref (store->stream); store->stream = NULL; goto fail; } /* set 'reader' mode & ignore return code */ - camel_nntp_command (store, (char **) &buf, "mode reader"); + if (camel_nntp_command (store, (char **) &buf, "mode reader") < 0 || + /* hack: inn seems to close connections if nothing is done within + the first ten seconds. a non-existent command is enough though */ + camel_nntp_command (store, (char **) &buf, "date") < 0) + goto fail; + + path = g_build_filename (store->storage_path, ".ev-journal", NULL); + disco_store->diary = camel_disco_diary_new (disco_store, path, ex); + g_free (path); + retval = TRUE; fail: CAMEL_NNTP_STORE_UNLOCK(store, command_lock); - return retval; } @@ -189,7 +206,7 @@ static struct { }; static gboolean -nntp_connect (CamelService *service, CamelException *ex) +nntp_connect_online (CamelService *service, CamelException *ex) { #ifdef HAVE_SSL const char *use_ssl; @@ -230,24 +247,73 @@ nntp_connect (CamelService *service, CamelException *ex) } static gboolean -nntp_disconnect (CamelService *service, gboolean clean, CamelException *ex) +nntp_connect_offline (CamelService *service, CamelException *ex) +{ + CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE(service); + CamelDiscoStore *disco_store = (CamelDiscoStore *) nntp_store; + char *path; + + if (nntp_store->storage_path == NULL) + return FALSE; + + /* setup store-wide cache */ + if (nntp_store->cache == NULL) { + nntp_store->cache = camel_data_cache_new (nntp_store->storage_path, 0, ex); + if (nntp_store->cache == NULL) + return FALSE; + + /* Default cache expiry - 2 weeks old, or not visited in 5 days */ + camel_data_cache_set_expire_age (nntp_store->cache, 60*60*24*14); + camel_data_cache_set_expire_access (nntp_store->cache, 60*60*24*5); + } + + path = g_build_filename (nntp_store->storage_path, ".ev-journal", NULL); + disco_store->diary = camel_disco_diary_new (disco_store, path, ex); + g_free (path); + + if (!disco_store->diary) + return FALSE; + + return TRUE; +} + +static gboolean +nntp_disconnect_online (CamelService *service, gboolean clean, CamelException *ex) { CamelNNTPStore *store = CAMEL_NNTP_STORE (service); char *line; - + CAMEL_NNTP_STORE_LOCK(store, command_lock); - + if (clean) camel_nntp_command (store, &line, "quit"); - - if (!service_class->disconnect (service, clean, ex)) + + if (!service_class->disconnect (service, clean, ex)) { + CAMEL_NNTP_STORE_UNLOCK(store, command_lock); return FALSE; - - camel_object_unref((CamelObject *)store->stream); + } + + camel_object_unref (store->stream); store->stream = NULL; - + CAMEL_NNTP_STORE_UNLOCK(store, command_lock); + + return TRUE; +} +static gboolean +nntp_disconnect_offline (CamelService *service, gboolean clean, CamelException *ex) +{ + CamelDiscoStore *disco = CAMEL_DISCO_STORE(service); + + if (!service_class->disconnect (service, clean, ex)) + return FALSE; + + if (disco->diary) { + camel_object_unref (disco->diary); + disco->diary = NULL; + } + return TRUE; } @@ -261,143 +327,512 @@ nntp_store_get_name (CamelService *service, gboolean brief) } -static CamelServiceAuthType password_authtype = { - N_("Password"), - - N_("This option will authenticate with the NNTP server using a " - "plaintext password."), - - "", - TRUE -}; +extern CamelServiceAuthType camel_nntp_password_authtype; static GList * nntp_store_query_auth_types (CamelService *service, CamelException *ex) { - g_warning ("nntp::query_auth_types: not implemented. Defaulting."); - - return g_list_append (NULL, &password_authtype); + return g_list_append (NULL, &camel_nntp_password_authtype); } static CamelFolder * -nntp_store_get_folder(CamelStore *store, const char *folder_name, guint32 flags, CamelException *ex) +nntp_get_folder(CamelStore *store, const char *folder_name, guint32 flags, CamelException *ex) { CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE (store); CamelFolder *folder; - + CAMEL_NNTP_STORE_LOCK(nntp_store, command_lock); - + folder = camel_nntp_folder_new(store, folder_name, ex); - + CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock); - + return folder; } +/* + * Converts a fully-fledged newsgroup name to a name in short dotted notation, + * e.g. nl.comp.os.linux.programmeren becomes n.c.o.l.programmeren + */ + +static char * +nntp_newsgroup_name_short (const char *name) +{ + char *resptr, *tmp; + const char *ptr2; + + resptr = tmp = g_malloc0 (strlen (name) + 1); + + while ((ptr2 = strchr (name, '.'))) { + if (ptr2 == name) { + name++; + continue; + } + + *resptr++ = *name; + *resptr++ = '.'; + name = ptr2 + 1; + } + + strcpy (resptr, name); + return tmp; +} + +/* + * This function converts a NNTPStoreSummary item to a FolderInfo item that + * can be returned by the get_folders() call to the store. Both structs have + * essentially the same fields. + */ + static CamelFolderInfo * -nntp_store_get_folder_info(CamelStore *store, const char *top, guint32 flags, CamelException *ex) +nntp_folder_info_from_store_info (CamelNNTPStore *store, gboolean short_notation, CamelStoreInfo *si) { - CamelURL *url = CAMEL_SERVICE (store)->url; - CamelNNTPStore *nntp_store = (CamelNNTPStore *)store; - CamelFolderInfo *groups = NULL, *last = NULL, *fi; - unsigned int len; - unsigned char *line, *space; - int ret = -1; + CamelURL *base_url = ((CamelService *) store)->url; + CamelFolderInfo *fi = g_malloc0(sizeof(*fi)); + CamelURL *url; + + fi->full_name = g_strdup (si->path); + + if (short_notation) + fi->name = nntp_newsgroup_name_short (si->path); + else + fi->name = g_strdup (si->path); + + fi->unread_message_count = -1; + /* fi->path is the 'canonicalised' path used by the UI (folder-tree). Not + * as important these days, but folders used to get added to the tree based + * on its path rather than the structure of the CamelFolderInfo's. + * + * must be delimited by '/' which also means that if the user doesn't want + * a flat list of newsgroups, you'll have to replace '.' with '/' for + * full_name too. */ + /*camel_folder_info_build_path(fi, '/');*/ + fi->path = g_strdup_printf ("/%s", si->path); + url = camel_url_new_with_base (base_url, fi->path); + fi->url = camel_url_to_string (url, CAMEL_URL_HIDE_ALL); + camel_url_free (url); + + return fi; +} - CAMEL_NNTP_STORE_LOCK(nntp_store, command_lock); +static CamelFolderInfo * +nntp_folder_info_from_name (CamelNNTPStore *store, gboolean short_notation, const char *name) +{ + CamelFolderInfo *fi = g_malloc0(sizeof(*fi)); + CamelURL *base_url = ((CamelService *)store)->url; + CamelURL *url; + + fi->full_name = g_strdup (name); + + if (short_notation) + fi->name = nntp_newsgroup_name_short (name); + else + fi->name = g_strdup (name); + + fi->unread_message_count = -1; + + fi->path = g_strdup_printf ("/%s", name); + + url = camel_url_new_with_base (base_url, fi->path); + fi->url = camel_url_to_string (url, CAMEL_URL_HIDE_ALL); + camel_url_free (url); + + return fi; +} - ret = camel_nntp_command(nntp_store, (char **)&line, "list"); - if (ret != 215) { - ret = -1; - goto error; - } +static CamelStoreInfo * +nntp_store_info_from_line (CamelNNTPStore *store, char *line) +{ + CamelStoreSummary *summ = (CamelStoreSummary *)store->summary; + CamelURL *base_url = ((CamelService *)store)->url; + CamelNNTPStoreInfo *nsi = (CamelNNTPStoreInfo*)camel_store_summary_info_new(summ); + CamelStoreInfo *si = (CamelStoreInfo*)nsi; + CamelURL *url; + char *relpath; + + relpath = g_strdup_printf ("/%s", line); + url = camel_url_new_with_base (base_url, relpath); + g_free (relpath); + si->uri = camel_url_to_string (url, CAMEL_URL_HIDE_ALL); + camel_url_free (url); + + si->path = g_strdup (line); + nsi->full_name = g_strdup (line); + return (CamelStoreInfo*) si; +} - while ( (ret = camel_nntp_stream_line(nntp_store->stream, &line, &len)) > 0) { - space = strchr(line, ' '); - if (space) - *space = 0; - - if (top == NULL || top[0] == 0 || strcmp(top, line) == 0) { - fi = g_malloc0(sizeof(*fi)); - fi->name = g_strdup(line); - fi->full_name = g_strdup(line); - if (url->user) - fi->url = g_strdup_printf ("nntp://%s@%s/%s", url->user, url->host, line); +static CamelFolderInfo * +nntp_store_get_subscribed_folder_info (CamelNNTPStore *store, const char *top, guint flags, CamelException *ex) +{ + int i; + CamelStoreInfo *si; + CamelFolderInfo *first = NULL, *last = NULL, *fi = NULL; + + /* since we do not do a tree, any request that is not for root is sure to give no results */ + if (top != NULL && top[0] != 0) + return NULL; + + for (i=0;(si = camel_store_summary_index ((CamelStoreSummary *) store->summary, i));i++) { + if (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) { + fi = nntp_folder_info_from_store_info (store, store->do_short_folder_notation, si); + if (!fi) + continue; + fi->flags |= CAMEL_FOLDER_NOINFERIORS | CAMEL_FOLDER_NOCHILDREN; + if (last) + last->sibling = fi; else - fi->url = g_strdup_printf ("nntp://%s/%s", url->host, line); - fi->unread_message_count = -1; - camel_folder_info_build_path(fi, '/'); + first = fi; + last = fi; + } + camel_store_summary_info_free ((CamelStoreSummary *) store->summary, si); + } + + return first; +} +/* + * get folder info, using the information in our StoreSummary + */ +static CamelFolderInfo * +nntp_store_get_cached_folder_info (CamelNNTPStore *store, const char *orig_top, guint flags, CamelException *ex) +{ + int len, i; + int subscribed_or_flag = (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED) ? 0 : 1, + root_or_flag = (orig_top == NULL || orig_top[0] == '\0') ? 1 : 0, + recursive_flag = flags & CAMEL_STORE_FOLDER_INFO_RECURSIVE; + CamelStoreInfo *si; + CamelFolderInfo *first = NULL, *last = NULL, *fi = NULL; + char *tmpname; + char *top = g_strconcat(orig_top?orig_top:"", ".", NULL); + int toplen = strlen(top); + + for (i = 0; (si = camel_store_summary_index ((CamelStoreSummary *) store->summary, i)); i++) { + if ((subscribed_or_flag || (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED)) && + (root_or_flag || g_ascii_strncasecmp (si->path, top, toplen) == 0)) { + if (recursive_flag || strchr (si->path + toplen, '.') == NULL) { + /* add the item */ + fi = nntp_folder_info_from_store_info(store, FALSE, si); + if (!fi) + continue; + if (store->folder_hierarchy_relative) { + g_free (fi->name); + fi->name = g_strdup (si->path + ((toplen == 1) ? 0 : toplen)); + } + } else { + /* apparently, this is an indirect subitem. if it's not a subitem of + the item we added last, we need to add a portion of this item to + the list as a placeholder */ + len = strlen (last->full_name); + if (!last || + g_ascii_strncasecmp(si->path, last->full_name, len) != 0 || + si->path[len] != '.') { + tmpname = g_strdup(si->path); + *(strchr(tmpname + toplen, '.')) = '\0'; + fi = nntp_folder_info_from_name(store, FALSE, tmpname); + fi->flags |= CAMEL_FOLDER_NOSELECT; + if (store->folder_hierarchy_relative) { + g_free(fi->name); + fi->name = g_strdup(tmpname + ((toplen==1) ? 0 : toplen)); + } + g_free(tmpname); + } else { + continue; + } + } if (last) last->sibling = fi; else - groups = fi; + first = fi; last = fi; + } else if (subscribed_or_flag && first) { + /* we have already added subitems, but this item is no longer a subitem */ + camel_store_summary_info_free((CamelStoreSummary *)store->summary, si); + break; } + camel_store_summary_info_free((CamelStoreSummary *)store->summary, si); } + + g_free(top); + return first; +} - if (ret < 0) - goto error; +/* retrieves the date from the NNTP server */ +static gboolean +nntp_get_date(CamelNNTPStore *nntp_store) +{ + unsigned char *line; + int ret = camel_nntp_command(nntp_store, (char **)&line, "date"); + char *ptr; + + nntp_store->summary->last_newslist[0] = 0; + + if (ret == 111) { + ptr = line + 3; + while (*ptr == ' ' || *ptr == '\t') + ptr++; + + if (strlen (ptr) == NNTP_DATE_SIZE) { + memcpy (nntp_store->summary->last_newslist, ptr, NNTP_DATE_SIZE); + return TRUE; + } + } + return FALSE; +} - CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock); +static gint +store_info_sort (gconstpointer a, gconstpointer b) +{ + return strcmp ((*(CamelNNTPStoreInfo**) a)->full_name, (*(CamelNNTPStoreInfo**) b)->full_name); +} - return groups; +static CamelFolderInfo * +nntp_store_get_folder_info_all(CamelNNTPStore *nntp_store, const char *top, guint32 flags, gboolean online, CamelException *ex) +{ + CamelNNTPStoreSummary *summary = nntp_store->summary; + CamelStoreInfo *si; + unsigned int len; + unsigned char *line, *space; + int ret = -1; + + if (top == NULL) + top = ""; + + if (online && (top == NULL || top[0] == 0)) { + /* we may need to update */ + if (summary->last_newslist[0] != 0) { + char date[14]; + memcpy(date, summary->last_newslist + 2, 6); /* YYMMDDD */ + date[6] = ' '; + memcpy(date + 7, summary->last_newslist + 8, 6); /* HHMMSS */ + date[13] = '\0'; + + nntp_get_date (nntp_store); + + ret = camel_nntp_command (nntp_store, (char **) &line, "newgroups %s", date); + if (ret != 231) { + /* newgroups not supported :S so reload the complete list */ + ret = -1; + camel_store_summary_clear ((CamelStoreSummary*) summary); + summary->last_newslist[0] = 0; + goto do_complete_list; + } + + while ((ret = camel_nntp_stream_line (nntp_store->stream, &line, &len)) > 0) { + if ((space = strchr(line, ' '))) + *space = '\0'; + + si = camel_store_summary_path ((CamelStoreSummary *) nntp_store->summary, line); + if (si) { + camel_store_summary_info_free ((CamelStoreSummary *) nntp_store->summary, si); + } else { + si = nntp_store_info_from_line (nntp_store, line); + camel_store_summary_add ((CamelStoreSummary*) nntp_store->summary, si); + } + } + } else { + do_complete_list: + /* seems we do need a complete list */ + /* at first, we do a DATE to find out the last load occasion */ + nntp_get_date (nntp_store); + + ret = camel_nntp_command (nntp_store, (char **)&line, "list"); + if (ret != 215) { + if (ret < 0) + line = _("Stream error"); + ret = -1; + camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_INVALID, + _("Error retrieving newsgroups:\n\n%s"), line); + goto error; + } + + while ((ret = camel_nntp_stream_line(nntp_store->stream, &line, &len)) > 0) { + if ((space = strchr(line, ' '))) + *space = '\0'; + + si = nntp_store_info_from_line (nntp_store, line); + camel_store_summary_add ((CamelStoreSummary*) nntp_store->summary, si); + /* check to see if it answers our current query */ + } + } + + /* sort the list */ + g_ptr_array_sort (CAMEL_STORE_SUMMARY (nntp_store->summary)->folders, store_info_sort); + if (ret < 0) + goto error; + + camel_store_summary_save ((CamelStoreSummary *) nntp_store->summary); + } + + return nntp_store_get_cached_folder_info (nntp_store, top, flags, ex); + error: + return NULL; +} -error: +static CamelFolderInfo * +nntp_get_folder_info (CamelStore *store, const char *top, guint32 flags, gboolean online, CamelException *ex) +{ + CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE(store); + CamelFolderInfo *first = NULL; + + dd(printf("g_f_i: fast %d subscr %d recursive %d online %d top \"%s\"\n", + flags & CAMEL_STORE_FOLDER_INFO_FAST, + flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED, + flags & CAMEL_STORE_FOLDER_INFO_RECURSIVE, + online, + top?top:"")); + + CAMEL_NNTP_STORE_LOCK(nntp_store, command_lock); + + if (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED) + first = nntp_store_get_subscribed_folder_info (nntp_store, top, flags, ex); + else + first = nntp_store_get_folder_info_all (nntp_store, top, flags, online, ex); + CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock); + return first; +} - if (groups) - camel_store_free_folder_info(store, groups); +static CamelFolderInfo * +nntp_get_folder_info_online (CamelStore *store, const char *top, guint32 flags, CamelException *ex) +{ + return nntp_get_folder_info (store, top, flags, TRUE, ex); +} - return NULL; +static CamelFolderInfo * +nntp_get_folder_info_offline(CamelStore *store, const char *top, guint32 flags, CamelException *ex) +{ + return nntp_get_folder_info (store, top, flags, FALSE, ex); } static gboolean nntp_store_folder_subscribed (CamelStore *store, const char *folder_name) { CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE (store); + CamelStoreInfo *si; + int truth = FALSE; + + si = camel_store_summary_path ((CamelStoreSummary *) nntp_store->summary, folder_name); + if (si) { + truth = (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) != 0; + camel_store_summary_info_free ((CamelStoreSummary *) nntp_store->summary, si); + } - nntp_store = nntp_store; - - /* FIXME: implement */ - - return TRUE; + return truth; } static void nntp_store_subscribe_folder (CamelStore *store, const char *folder_name, CamelException *ex) { - CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE (store); - - nntp_store = nntp_store; - - /* FIXME: implement */ + CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE(store); + CamelStoreInfo *si; + CamelFolderInfo *fi; + + CAMEL_NNTP_STORE_LOCK(nntp_store, command_lock); + + si = camel_store_summary_path(CAMEL_STORE_SUMMARY(nntp_store->summary), folder_name); + if (!si) { + camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID, + _("You cannot subscribe to this newsgroup:\n\n" + "No such newsgroup. The selected item is a probably a parent folder.")); + } else { + if (!(si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED)) { + si->flags |= CAMEL_STORE_INFO_FOLDER_SUBSCRIBED; + fi = nntp_folder_info_from_store_info(nntp_store, nntp_store->do_short_folder_notation, si); + fi->flags |= CAMEL_FOLDER_NOINFERIORS | CAMEL_FOLDER_NOCHILDREN; + camel_store_summary_touch ((CamelStoreSummary *) nntp_store->summary); + camel_store_summary_save ((CamelStoreSummary *) nntp_store->summary); + CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock); + camel_object_trigger_event ((CamelObject *) nntp_store, "folder_subscribed", fi); + camel_folder_info_free (fi); + return; + } + } + + CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock); } static void nntp_store_unsubscribe_folder (CamelStore *store, const char *folder_name, CamelException *ex) { - CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE (store); + CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE(store); + CamelFolderInfo *fi; + CamelStoreInfo *fitem; + CAMEL_NNTP_STORE_LOCK(nntp_store, command_lock); + + fitem = camel_store_summary_path(CAMEL_STORE_SUMMARY(nntp_store->summary), folder_name); + + if (!fitem) { + camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID, + _("You cannot unsubscribe to this newsgroup:\n\n" + "newsgroup does not exist!")); + } else { + if (fitem->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) { + fitem->flags &= ~CAMEL_STORE_INFO_FOLDER_SUBSCRIBED; + fi = nntp_folder_info_from_store_info (nntp_store, nntp_store->do_short_folder_notation, fitem); + camel_store_summary_touch ((CamelStoreSummary *) nntp_store->summary); + camel_store_summary_save ((CamelStoreSummary *) nntp_store->summary); + CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock); + camel_object_trigger_event ((CamelObject *) nntp_store, "folder_unsubscribed", fi); + camel_folder_info_free (fi); + return; + } + } + + CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock); +} - nntp_store = nntp_store; +/* stubs for various folder operations we're not implementing */ - /* FIXME: implement */ +static CamelFolderInfo * +nntp_create_folder (CamelStore *store, const char *parent_name, + const char *folder_name, CamelException *ex) +{ + camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID, + _("You cannot create a folder in a News store: subscribe instead.")); + return NULL; +} + +static void +nntp_rename_folder (CamelStore *store, const char *old_name, const char *new_name_in, CamelException *ex) +{ + camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID, + _("You cannot rename a folder in a News store.")); +} + +static void +nntp_delete_folder (CamelStore *store, const char *folder_name, CamelException *ex) +{ + nntp_store_subscribe_folder (store, folder_name, ex); + camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID, + _("You cannot remove a folder in a News store: unsubscribe instead.")); + return; } static void -nntp_store_finalise (CamelObject *object) +nntp_store_finalize (CamelObject *object) { + /* call base finalize */ CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE (object); struct _CamelNNTPStorePrivate *p = nntp_store->priv; + + camel_service_disconnect ((CamelService *)object, TRUE, NULL); + + if (nntp_store->summary) { + camel_store_summary_save ((CamelStoreSummary *) nntp_store->summary); + camel_object_unref (nntp_store->summary); + } - camel_service_disconnect((CamelService *)object, TRUE, NULL); - - camel_object_unref((CamelObject *)nntp_store->mem); + camel_object_unref (nntp_store->mem); nntp_store->mem = NULL; if (nntp_store->stream) - camel_object_unref((CamelObject *)nntp_store->stream); + camel_object_unref (nntp_store->stream); + + if (nntp_store->base_url) + g_free (nntp_store->base_url); + if (nntp_store->storage_path) + g_free (nntp_store->storage_path); e_mutex_destroy(p->command_lock); @@ -407,39 +842,101 @@ nntp_store_finalise (CamelObject *object) static void nntp_store_class_init (CamelNNTPStoreClass *camel_nntp_store_class) { + CamelDiscoStoreClass *camel_disco_store_class = CAMEL_DISCO_STORE_CLASS (camel_nntp_store_class); CamelStoreClass *camel_store_class = CAMEL_STORE_CLASS (camel_nntp_store_class); CamelServiceClass *camel_service_class = CAMEL_SERVICE_CLASS (camel_nntp_store_class); - parent_class = CAMEL_STORE_CLASS (camel_type_get_global_classfuncs (camel_store_get_type ())); - + parent_class = CAMEL_DISCO_STORE_CLASS (camel_type_get_global_classfuncs (camel_disco_store_get_type ())); service_class = CAMEL_SERVICE_CLASS (camel_type_get_global_classfuncs (camel_service_get_type ())); /* virtual method overload */ - camel_service_class->connect = nntp_connect; - camel_service_class->disconnect = nntp_disconnect; + camel_service_class->construct = nntp_construct; camel_service_class->query_auth_types = nntp_store_query_auth_types; camel_service_class->get_name = nntp_store_get_name; - camel_store_class->get_folder = nntp_store_get_folder; - camel_store_class->get_folder_info = nntp_store_get_folder_info; + camel_disco_store_class->can_work_offline = nntp_can_work_offline; + camel_disco_store_class->connect_online = nntp_connect_online; + camel_disco_store_class->connect_offline = nntp_connect_offline; + camel_disco_store_class->disconnect_online = nntp_disconnect_online; + camel_disco_store_class->disconnect_offline = nntp_disconnect_offline; + camel_disco_store_class->get_folder_online = nntp_get_folder; + camel_disco_store_class->get_folder_resyncing = nntp_get_folder; + camel_disco_store_class->get_folder_offline = nntp_get_folder; + + camel_disco_store_class->get_folder_info_online = nntp_get_folder_info_online; + camel_disco_store_class->get_folder_info_resyncing = nntp_get_folder_info_online; + camel_disco_store_class->get_folder_info_offline = nntp_get_folder_info_offline; + camel_store_class->free_folder_info = camel_store_free_folder_info_full; camel_store_class->folder_subscribed = nntp_store_folder_subscribed; camel_store_class->subscribe_folder = nntp_store_subscribe_folder; camel_store_class->unsubscribe_folder = nntp_store_unsubscribe_folder; + + camel_store_class->create_folder = nntp_create_folder; + camel_store_class->delete_folder = nntp_delete_folder; + camel_store_class->rename_folder = nntp_rename_folder; } +/* construction function in which we set some basic store properties */ +static void +nntp_construct (CamelService *service, CamelSession *session, + CamelProvider *provider, CamelURL *url, + CamelException *ex) +{ + CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE(service); + CamelURL *summary_url; + char *tmp; + + /* construct the parent first */ + CAMEL_SERVICE_CLASS (parent_class)->construct (service, session, provider, url, ex); + if (camel_exception_is_set (ex)) + return; + + /* find out the storage path, base url */ + nntp_store->storage_path = camel_session_get_storage_path (session, service, ex); + if (!nntp_store->storage_path) + return; + + /* FIXME */ + nntp_store->base_url = camel_url_to_string (service->url, (CAMEL_URL_HIDE_PASSWORD | + CAMEL_URL_HIDE_PARAMS | + CAMEL_URL_HIDE_AUTH)); + + tmp = g_build_filename (nntp_store->storage_path, ".ev-store-summary", NULL); + nntp_store->summary = camel_nntp_store_summary_new (); + camel_store_summary_set_filename ((CamelStoreSummary *) nntp_store->summary, tmp); + summary_url = camel_url_new (nntp_store->base_url, NULL); + camel_store_summary_set_uri_base ((CamelStoreSummary *) nntp_store->summary, summary_url); + g_free (tmp); + + camel_url_free (summary_url); + if (camel_store_summary_load ((CamelStoreSummary *)nntp_store->summary) == 0) + ; + + /* get options */ + if (camel_url_get_param (url, "show_short_notation")) + nntp_store->do_short_folder_notation = TRUE; + else + nntp_store->do_short_folder_notation = FALSE; + if (camel_url_get_param (url, "folder_hierarchy_relative")) + nntp_store->folder_hierarchy_relative = TRUE; + else + nntp_store->folder_hierarchy_relative = FALSE; +} + + static void nntp_store_init (gpointer object, gpointer klass) { CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE(object); CamelStore *store = CAMEL_STORE (object); struct _CamelNNTPStorePrivate *p; - + store->flags = CAMEL_STORE_SUBSCRIPTIONS; - + nntp_store->mem = (CamelStreamMem *)camel_stream_mem_new(); - + p = nntp_store->priv = g_malloc0(sizeof(*p)); p->command_lock = e_mutex_new(E_MUTEX_REC); } @@ -451,14 +948,14 @@ camel_nntp_store_get_type (void) if (camel_nntp_store_type == CAMEL_INVALID_TYPE) { camel_nntp_store_type = - camel_type_register (CAMEL_STORE_TYPE, + camel_type_register (CAMEL_DISCO_STORE_TYPE, "CamelNNTPStore", sizeof (CamelNNTPStore), sizeof (CamelNNTPStoreClass), (CamelObjectClassInitFunc) nntp_store_class_init, NULL, (CamelObjectInitFunc) nntp_store_init, - (CamelObjectFinalizeFunc) nntp_store_finalise); + (CamelObjectFinalizeFunc) nntp_store_finalize); } return camel_nntp_store_type; @@ -469,17 +966,63 @@ int camel_nntp_store_set_folder (CamelNNTPStore *store, CamelFolder *folder, CamelFolderChangeInfo *changes, CamelException *ex) { int ret; - - if (store->current_folder && strcmp(folder->full_name, store->current_folder) == 0) + + if (store->current_folder && strcmp (folder->full_name, store->current_folder) == 0) return 0; - + /* FIXME: Do something with changeinfo */ - ret = camel_nntp_summary_check((CamelNNTPSummary *)folder->summary, changes, ex); + ret = camel_nntp_summary_check ((CamelNNTPSummary *) folder->summary, changes, ex); + + g_free (store->current_folder); + store->current_folder = g_strdup (folder->full_name); + + return ret; +} - g_free(store->current_folder); - store->current_folder = g_strdup(folder->full_name); +static gboolean +camel_nntp_try_authenticate (CamelNNTPStore *store) +{ + CamelService *service = (CamelService *) store; + CamelSession *session = camel_service_get_session (service); + int ret; + char *line; + + if (!service->url->user) + return FALSE; + + /* if nessecary, prompt for the password */ + if (!service->url->passwd) { + CamelException ex; + char *prompt; + + prompt = g_strdup_printf (_("Please enter the NNTP password for %s@%s"), + service->url->user, + service->url->host); + + camel_exception_init (&ex); + + service->url->passwd = + camel_session_get_password (session, prompt, FALSE, TRUE, + service, "password", &ex); + camel_exception_clear (&ex); + g_free (prompt); + + if (!service->url->passwd) + return FALSE; + } - return ret; + /* now, send auth info (currently, only authinfo user/pass is supported) */ + ret = camel_nntp_command(store, &line, "authinfo user %s", service->url->user); + if (ret == NNTP_AUTH_ACCEPTED) { + return TRUE; + } else if (ret == NNTP_AUTH_CONTINUE) { + ret = camel_nntp_command (store, &line, "authinfo pass %s", service->url->passwd); + if (ret == NNTP_AUTH_ACCEPTED) + return TRUE; + else + return FALSE; + } else + return FALSE; } static gboolean @@ -487,12 +1030,13 @@ nntp_connected (CamelNNTPStore *store, CamelException *ex) { if (store->stream == NULL) return camel_service_connect (CAMEL_SERVICE (store), ex); + return TRUE; } /* Enter owning lock */ int -camel_nntp_command(CamelNNTPStore *store, char **line, const char *fmt, ...) +camel_nntp_command (CamelNNTPStore *store, char **line, const char *fmt, ...) { const unsigned char *p, *ps; unsigned char c; @@ -500,9 +1044,9 @@ camel_nntp_command(CamelNNTPStore *store, char **line, const char *fmt, ...) char *s; int d; unsigned int u, u2; - + e_mutex_assert_locked(store->priv->command_lock); - + if (!nntp_connected (store, NULL)) return -1; @@ -513,14 +1057,15 @@ camel_nntp_command(CamelNNTPStore *store, char **line, const char *fmt, ...) ; } camel_nntp_stream_set_mode(store->stream, CAMEL_NNTP_STREAM_LINE); - + + command_begin_send: va_start(ap, fmt); ps = p = fmt; - while ( (c = *p++) ) { + while ((c = *p++)) { switch (c) { case '%': c = *p++; - camel_stream_write((CamelStream *)store->mem, ps, p-ps-(c=='%'?1:2)); + camel_stream_write ((CamelStream *) store->mem, ps, p - ps - (c == '%' ? 1 : 2)); ps = p; switch (c) { case 's': @@ -553,24 +1098,47 @@ camel_nntp_command(CamelNNTPStore *store, char **line, const char *fmt, ...) } } } - - camel_stream_write((CamelStream *)store->mem, ps, p-ps-1); + + camel_stream_write ((CamelStream *) store->mem, ps, p-ps-1); dd(printf("NNTP_COMMAND: '%.*s'\n", (int)store->mem->buffer->len, store->mem->buffer->data)); - camel_stream_write((CamelStream *)store->mem, "\r\n", 2); - camel_stream_write((CamelStream *)store->stream, store->mem->buffer->data, store->mem->buffer->len); - camel_stream_reset((CamelStream *)store->mem); + camel_stream_write ((CamelStream *) store->mem, "\r\n", 2); + + if (camel_stream_write ((CamelStream *) store->stream, store->mem->buffer->data, store->mem->buffer->len) == -1 && errno != EINTR) { + camel_stream_reset ((CamelStream *) store->mem); + /* FIXME: hack */ + g_byte_array_set_size (store->mem->buffer, 0); + + reconnect: + /* some error, re-connect */ + camel_service_disconnect (CAMEL_SERVICE (store), FALSE, NULL); + + if (!nntp_connected (store, NULL)) + return -1; + + goto command_begin_send; + } + + camel_stream_reset ((CamelStream *) store->mem); /* FIXME: hack */ - g_byte_array_set_size(store->mem->buffer, 0); - - if (camel_nntp_stream_line(store->stream, (unsigned char **)line, &u) == -1) + g_byte_array_set_size (store->mem->buffer, 0); + + if (camel_nntp_stream_line (store->stream, (unsigned char **) line, &u) == -1) return -1; - - u = strtoul(*line, NULL, 10); - + + u = strtoul (*line, NULL, 10); + + /* Check for 'authentication required' codes */ + if (u == NNTP_AUTH_REQUIRED && + camel_nntp_try_authenticate(store)) + goto command_begin_send; + + /* the server doesn't like us anymore, but we still like her! */ + if (u == 401 || u == 503) + goto reconnect; + /* Handle all switching to data mode here, to make callers job easier */ if (u == 215 || (u >= 220 && u <=224) || (u >= 230 && u <= 231)) camel_nntp_stream_set_mode(store->stream, CAMEL_NNTP_STREAM_DATA); - + return u; } - diff --git a/camel/providers/nntp/camel-nntp-store.h b/camel/providers/nntp/camel-nntp-store.h index a201c2cea2..53ebb82edf 100644 --- a/camel/providers/nntp/camel-nntp-store.h +++ b/camel/providers/nntp/camel-nntp-store.h @@ -36,7 +36,11 @@ extern "C" { #include <camel/camel-exception.h> #include <camel/camel-folder.h> +#include <camel/camel-disco-store.h> +#include <camel/camel-disco-folder.h> + #include "camel-nntp-stream.h" +#include "camel-nntp-store-summary.h" #define CAMEL_NNTP_STORE_TYPE (camel_nntp_store_get_type ()) #define CAMEL_NNTP_STORE(obj) (CAMEL_CHECK_CAST((obj), CAMEL_NNTP_STORE_TYPE, CamelNNTPStore)) @@ -55,25 +59,30 @@ extern "C" { typedef struct _CamelNNTPStore CamelNNTPStore; typedef struct _CamelNNTPStoreClass CamelNNTPStoreClass; +#include "camel-nntp-grouplist.h" + struct _CamelNNTPStore { - CamelStore parent_object; + CamelDiscoStore parent_object; struct _CamelNNTPStorePrivate *priv; guint32 extensions; gboolean posting_allowed; + gboolean do_short_folder_notation, folder_hierarchy_relative; + + CamelNNTPStoreSummary *summary; CamelNNTPStream *stream; CamelStreamMem *mem; CamelDataCache *cache; - char *current_folder; + char *current_folder, *storage_path, *base_url; }; struct _CamelNNTPStoreClass { - CamelStoreClass parent_class; + CamelDiscoStoreClass parent_class; }; diff --git a/camel/providers/nntp/camel-nntp-summary.c b/camel/providers/nntp/camel-nntp-summary.c index 2a9669d697..2fa753bef1 100644 --- a/camel/providers/nntp/camel-nntp-summary.c +++ b/camel/providers/nntp/camel-nntp-summary.c @@ -155,12 +155,12 @@ camel_nntp_summary_new(CamelNNTPFolder *folder) char *path; cns->folder = folder; - path = g_strdup_printf("%s.ev-summary", folder->storage_path); + path = g_strdup_printf ("%s%s", folder->storage_path, ".ev-summary"); camel_folder_summary_set_filename((CamelFolderSummary *)cns, path); g_free(path); camel_folder_summary_set_build_content((CamelFolderSummary *)cns, FALSE); - + return cns; } @@ -224,8 +224,11 @@ camel_nntp_summary_check(CamelNNTPSummary *cns, CamelFolderChangeInfo *changes, unsigned int n, f, l; int count; - if (xover_setup(cns, ex) == -1) + if (xover_setup (cns, ex) == -1) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, + _("Connection error: %s"), strerror(errno)); return -1; + } folder = (CamelFolder *)cns->folder; store = (CamelNNTPStore *)folder->parent_store; @@ -237,6 +240,8 @@ camel_nntp_summary_check(CamelNNTPSummary *cns, CamelFolderChangeInfo *changes, _("No such folder: %s"), line); return -1; } else if (ret != 211) { + if (ret < 0) + line = ""; camel_exception_setv(ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, _("Could not get group: %s"), line); return -1; @@ -247,9 +252,6 @@ camel_nntp_summary_check(CamelNNTPSummary *cns, CamelFolderChangeInfo *changes, f = strtoul(line, &line, 10); l = strtoul(line, &line, 10); - dd(printf("nntp_summary: got last '%u' first '%u'\n" - "nntp_summary: high '%u' low '%u'\n", l, f, cns->high, cns->low)); - if (cns->low == f && cns->high == l) { dd(printf("nntp_summary: no work to do!\n")); return 0; @@ -299,7 +301,14 @@ camel_nntp_summary_check(CamelNNTPSummary *cns, CamelFolderChangeInfo *changes, } } + + /* TODO: not from here */ camel_folder_summary_touch(s); + camel_folder_summary_save(s); + + if (ret < 0) + camel_exception_setv(ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, + _("Could not get messages: unspecificed error")); return ret; } @@ -527,7 +536,8 @@ add_range_head(CamelNNTPSummary *cns, unsigned int high, unsigned int low, Camel n = strtoul(line, &line, 10); if (n != i) g_warning("retrieved message '%d' when i expected '%d'?\n", n, i); - + + /* FIXME: use camel-mime-utils.c function for parsing msgid? */ if ((msgid = strchr(line, '<')) && (line = strchr(msgid+1, '>'))){ line[1] = 0; cns->priv->uid = g_strdup_printf("%u,%s\n", n, msgid); diff --git a/camel/providers/nntp/camel-nntp-summary.h b/camel/providers/nntp/camel-nntp-summary.h index 82070cdc31..9db3297da7 100644 --- a/camel/providers/nntp/camel-nntp-summary.h +++ b/camel/providers/nntp/camel-nntp-summary.h @@ -24,7 +24,6 @@ #include <camel/camel-folder-summary.h> #include <camel/camel-folder.h> #include <camel/camel-exception.h> -#include <libibex/ibex.h> #define CAMEL_NNTP_SUMMARY(obj) CAMEL_CHECK_CAST (obj, camel_nntp_summary_get_type (), CamelNNTPSummary) #define CAMEL_NNTP_SUMMARY_CLASS(klass) CAMEL_CHECK_CLASS_CAST (klass, camel_nntp_summary_get_type (), CamelNNTPSummaryClass) |