aboutsummaryrefslogtreecommitdiffstats
path: root/camel/providers/nntp
diff options
context:
space:
mode:
authorMeilof Veeningen <meilof@wanadoo.nl>2004-01-13 06:20:40 +0800
committerJeffrey Stedfast <fejj@src.gnome.org>2004-01-13 06:20:40 +0800
commit70f49bc0d7ce40521c4c99b7c9dbe6255db2c788 (patch)
tree6a1cd1a9114c3cb5d64dfe10c6fd08f39e853b03 /camel/providers/nntp
parentfb0dddb43d9b8a502c6c32dc10e6e485f35bec74 (diff)
downloadgsoc2013-evolution-70f49bc0d7ce40521c4c99b7c9dbe6255db2c788.tar
gsoc2013-evolution-70f49bc0d7ce40521c4c99b7c9dbe6255db2c788.tar.gz
gsoc2013-evolution-70f49bc0d7ce40521c4c99b7c9dbe6255db2c788.tar.bz2
gsoc2013-evolution-70f49bc0d7ce40521c4c99b7c9dbe6255db2c788.tar.lz
gsoc2013-evolution-70f49bc0d7ce40521c4c99b7c9dbe6255db2c788.tar.xz
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.am7
-rw-r--r--camel/providers/nntp/camel-nntp-folder.c540
-rw-r--r--camel/providers/nntp/camel-nntp-folder.h5
-rw-r--r--camel/providers/nntp/camel-nntp-grouplist.h11
-rw-r--r--camel/providers/nntp/camel-nntp-provider.c27
-rw-r--r--camel/providers/nntp/camel-nntp-store-summary.c430
-rw-r--r--camel/providers/nntp/camel-nntp-store-summary.h100
-rw-r--r--camel/providers/nntp/camel-nntp-store.c840
-rw-r--r--camel/providers/nntp/camel-nntp-store.h15
-rw-r--r--camel/providers/nntp/camel-nntp-summary.c24
-rw-r--r--camel/providers/nntp/camel-nntp-summary.h1
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)