aboutsummaryrefslogtreecommitdiffstats
path: root/camel/providers/nntp/camel-nntp-store.c
diff options
context:
space:
mode:
Diffstat (limited to 'camel/providers/nntp/camel-nntp-store.c')
-rw-r--r--camel/providers/nntp/camel-nntp-store.c840
1 files changed, 704 insertions, 136 deletions
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;
}
-