diff options
Diffstat (limited to 'camel/providers/nntp/camel-nntp-store.c')
-rw-r--r-- | camel/providers/nntp/camel-nntp-store.c | 860 |
1 files changed, 212 insertions, 648 deletions
diff --git a/camel/providers/nntp/camel-nntp-store.c b/camel/providers/nntp/camel-nntp-store.c index 7ac3fcdc82..feca770e37 100644 --- a/camel/providers/nntp/camel-nntp-store.c +++ b/camel/providers/nntp/camel-nntp-store.c @@ -3,7 +3,10 @@ /* * - * Copyright (C) 2000 Ximian, Inc. <toshok@ximian.com> + * Copyright (C) 2001 Ximian, Inc. <www.ximain.com> + * + * Authors: Christopher Toshok <toshok@ximian.com> + * 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 @@ -29,17 +32,22 @@ #include <string.h> #include <unistd.h> -#include "libgnome/libgnome.h" +#include "camel/camel-exception.h" +#include "camel/camel-url.h" +#include "camel/string-utils.h" +#include "camel/camel-stream-mem.h" +#include "camel/camel-session.h" +#include "camel/camel-data-cache.h" -#include "camel-nntp-resp-codes.h" -#include "camel-folder-summary.h" +#include "camel-nntp-stream.h" +#include "camel-nntp-summary.h" #include "camel-nntp-store.h" -#include "camel-nntp-grouplist.h" #include "camel-nntp-folder.h" -#include "camel-nntp-auth.h" -#include "camel-exception.h" -#include "camel-url.h" -#include "string-utils.h" +#include "camel-nntp-private.h" + +#define w(x) +extern int camel_verbose_debug; +#define dd(x) (camel_verbose_debug?(x):0) #define NNTP_PORT 119 @@ -57,249 +65,73 @@ 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 gboolean ensure_news_dir_exists (CamelNNTPStore *store); - -static int -camel_nntp_store_set_mode (CamelNNTPStore *store, CamelException *ex) -{ - int rc; - - CAMEL_NNTP_STORE_LOCK(store); - rc = camel_nntp_command(store, ex, NULL, "MODE READER"); - CAMEL_NNTP_STORE_UNLOCK(store); - - return rc; -} - -static void -camel_nntp_store_get_extensions (CamelNNTPStore *store, CamelException *ex) -{ - int rc; - - store->extensions = 0; - - CAMEL_NNTP_STORE_LOCK(store); - rc = camel_nntp_command(store, ex, NULL, "LIST EXTENSIONS"); - if (rc == NNTP_LIST_FOLLOWS || rc == NNTP_EXTENSIONS_SUPPORTED) { - gboolean done = FALSE; - CamelException ex; - - camel_exception_init (&ex); - - while (!done) { - char *line; - - if (camel_remote_store_recv_line (CAMEL_REMOTE_STORE (store), &line, &ex) < 0) - break; /* XXX */ - - if (*line == '.') { - done = TRUE; - } - else { -#define CHECK_EXT(name,val) if (!strcasecmp (line, (name))) store->extensions |= (val) - - CHECK_EXT ("SEARCH", CAMEL_NNTP_EXT_SEARCH); - CHECK_EXT ("SETGET", CAMEL_NNTP_EXT_SETGET); - CHECK_EXT ("OVER", CAMEL_NNTP_EXT_OVER); - CHECK_EXT ("XPATTEXT", CAMEL_NNTP_EXT_XPATTEXT); - CHECK_EXT ("XACTIVE", CAMEL_NNTP_EXT_XACTIVE); - CHECK_EXT ("LISTMOTD", CAMEL_NNTP_EXT_LISTMOTD); - CHECK_EXT ("LISTSUBSCR", CAMEL_NNTP_EXT_LISTSUBSCR); - CHECK_EXT ("LISTPNAMES", CAMEL_NNTP_EXT_LISTPNAMES); - -#undef CHECK_EXT - } - - g_free (line); - } - } - CAMEL_NNTP_STORE_UNLOCK(store); - -#ifdef DUMP_EXTENSIONS - g_print ("NNTP Extensions:"); -#define DUMP_EXT(name,val) if (store->extensions & (val)) g_print (" %s", name); - DUMP_EXT ("SEARCH", CAMEL_NNTP_EXT_SEARCH); - DUMP_EXT ("SETGET", CAMEL_NNTP_EXT_SETGET); - DUMP_EXT ("OVER", CAMEL_NNTP_EXT_OVER); - DUMP_EXT ("XPATTEXT", CAMEL_NNTP_EXT_XPATTEXT); - DUMP_EXT ("XACTIVE", CAMEL_NNTP_EXT_XACTIVE); - DUMP_EXT ("LISTMOTD", CAMEL_NNTP_EXT_LISTMOTD); - DUMP_EXT ("LISTSUBSCR", CAMEL_NNTP_EXT_LISTSUBSCR); - DUMP_EXT ("LISTPNAMES", CAMEL_NNTP_EXT_LISTPNAMES); - g_print ("\n"); -#undef DUMP_EXT -#endif -} - -static void -camel_nntp_store_get_overview_fmt (CamelNNTPStore *store, CamelException *ex) -{ - int status; - int i; - gboolean done = FALSE; - - CAMEL_NNTP_STORE_LOCK(store); - status = camel_nntp_command (store, ex, NULL, - "LIST OVERVIEW.FMT"); - - if (status != NNTP_LIST_FOLLOWS) { - if (store->extensions & CAMEL_NNTP_EXT_OVER) { - /* if we can't get the overview format, we should - disable OVER support */ - g_warning ("server reported support of OVER but LIST OVERVIEW.FMT failed." - " disabling OVER.\n"); - store->extensions &= ~CAMEL_NNTP_EXT_OVER; - } - CAMEL_NNTP_STORE_UNLOCK(store); - return; - } - else { - if (!(store->extensions & CAMEL_NNTP_EXT_OVER)) { - g_warning ("server didn't report support of OVER but LIST OVERVIEW.FMT worked." - " enabling OVER.\n"); - store->extensions |= CAMEL_NNTP_EXT_OVER; - } - } - - /* start at 1 because the article number is always first */ - store->num_overview_fields = 1; - - for (i = 0; i < CAMEL_NNTP_OVER_LAST; i ++) { - store->overview_field [i].index = -1; - } - - while (!done) { - char *line; - - if (camel_remote_store_recv_line (CAMEL_REMOTE_STORE (store), &line, ex) < 0) - break; /* XXX */ - - if (*line == '.') { - done = TRUE; - } - else { - CamelNNTPOverField *over_field = NULL; - char *colon = NULL;; - - if (!strncasecmp (line, "From:", 5)) { - over_field = &store->overview_field [ CAMEL_NNTP_OVER_FROM ]; - over_field->index = store->num_overview_fields; - colon = line + 5; - } - else if (!strncasecmp (line, "Subject:", 7)) { - over_field = &store->overview_field [ CAMEL_NNTP_OVER_SUBJECT ]; - over_field->index = store->num_overview_fields; - colon = line + 7; - } - else if (!strncasecmp (line, "Date:", 5)) { - over_field = &store->overview_field [ CAMEL_NNTP_OVER_DATE ]; - over_field->index = store->num_overview_fields; - colon = line + 5; - } - else if (!strncasecmp (line, "Message-ID:", 11)) { - over_field = &store->overview_field [ CAMEL_NNTP_OVER_MESSAGE_ID ]; - over_field->index = store->num_overview_fields; - colon = line + 11; - } - else if (!strncasecmp (line, "References:", 11)) { - over_field = &store->overview_field [ CAMEL_NNTP_OVER_REFERENCES ]; - over_field->index = store->num_overview_fields; - colon = line + 11; - } - else if (!strncasecmp (line, "Bytes:", 6)) { - over_field = &store->overview_field [ CAMEL_NNTP_OVER_BYTES ]; - over_field->index = store->num_overview_fields; - colon = line + 11; - } - - if (colon && !strncmp (colon + 1, "full", 4)) - over_field->full = TRUE; - - store->num_overview_fields ++; - } - - g_free (line); - } - - for (i = 0; i < CAMEL_NNTP_OVER_LAST; i ++) { - if (store->overview_field [i].index == -1) { - g_warning ("server's OVERVIEW.FMT doesn't support minimum set we require," - " disabling OVER support.\n"); - store->extensions &= ~CAMEL_NNTP_EXT_OVER; - } - } - CAMEL_NNTP_STORE_UNLOCK(store); -} - static gboolean nntp_store_connect (CamelService *service, CamelException *ex) { - char *buf; - int resp_code; + unsigned char *line; + unsigned int len; + int ret = FALSE; CamelNNTPStore *store = CAMEL_NNTP_STORE (service); - if (!ensure_news_dir_exists(store)) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Could not open directory for news server: %s"), - strerror (errno)); - return FALSE; - } + CAMEL_NNTP_STORE_LOCK(store, command_lock); - if (CAMEL_SERVICE_CLASS (remote_store_class)->connect (service, ex) == FALSE) - return FALSE; + /* setup store-wide cache */ + if (store->cache == NULL) { + char *root; - /* Read the greeting */ - if (camel_remote_store_recv_line (CAMEL_REMOTE_STORE (service), &buf, ex) < 0) { - return FALSE; - } + root = camel_session_get_storage_path(service->session, service, ex); + if (root == NULL) + goto fail; - resp_code = atoi (buf); - /* Check to see if we have any permissions on this server. */ - if (resp_code == NNTP_NO_PERMISSION) - return FALSE; + store->cache = camel_data_cache_new(root, 0, ex); + g_free(root); + if (store->cache == NULL) + goto fail; - /* check if posting is allowed. */ - if (resp_code == NNTP_GREETING_POSTING_OK) { - g_print ("posting allowed\n"); - store->posting_allowed = TRUE; - } - else if (resp_code == NNTP_GREETING_NO_POSTING) { - g_print ("no posting allowed\n"); - store->posting_allowed = FALSE; - } - else { - g_warning ("unexpected server greeting code %d, no posting allowed\n", resp_code); - store->posting_allowed = FALSE; + /* Default cache expiry - 2 weeks old, or not visited in 5 days */ + camel_data_cache_set_expire_age(store->cache, 60*60*24*14); + camel_data_cache_set_expire_access(store->cache, 60*60*24*5); } - g_free (buf); + if (CAMEL_SERVICE_CLASS (remote_store_class)->connect (service, ex) == FALSE) + goto fail; - /* set 'reader' mode */ - camel_nntp_store_set_mode(store, ex); + store->stream = (CamelNNTPStream *)camel_nntp_stream_new(((CamelRemoteStore *)service)->ostream); + if (camel_nntp_stream_line(store->stream, &line, &len) == -1) + goto fail; - /* get a list of extensions that the server supports */ - camel_nntp_store_get_extensions (store, ex); + len = strtoul(line, (char **)&line, 10); + if (len != 200 && len != 201) + goto fail; - /* try to get the overview.fmt */ - camel_nntp_store_get_overview_fmt (store, ex); + /* set 'reader' mode & ignore return code */ + camel_nntp_command(store, (char **)&line, "mode reader"); + ret = TRUE; +fail: + CAMEL_NNTP_STORE_UNLOCK(store, command_lock); - return TRUE; + return ret; } static gboolean nntp_store_disconnect (CamelService *service, gboolean clean, CamelException *ex) { CamelNNTPStore *store = CAMEL_NNTP_STORE (service); + char *line; - if (clean) - camel_nntp_command (store, ex, NULL, "QUIT"); + CAMEL_NNTP_STORE_LOCK(store, command_lock); - if (store->newsrc) - camel_nntp_newsrc_write (store->newsrc); + if (clean) + camel_nntp_command (store, &line, "quit"); if (!service_class->disconnect (service, clean, ex)) return FALSE; + camel_object_unref((CamelObject *)store->stream); + store->stream = NULL; + + CAMEL_NNTP_STORE_UNLOCK(store, command_lock); + return TRUE; } @@ -334,211 +166,51 @@ nntp_store_query_auth_types (CamelService *service, CamelException *ex) } static CamelFolder * -nntp_store_get_folder (CamelStore *store, const gchar *folder_name, - guint32 flags, CamelException *ex) +nntp_store_get_folder(CamelStore *store, const char *folder_name, guint32 flags, CamelException *ex) { CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE (store); + CamelFolder *folder; - /* if we haven't already read our .newsrc, read it now */ - if (!nntp_store->newsrc) - nntp_store->newsrc = - camel_nntp_newsrc_read_for_server (CAMEL_SERVICE(store)->url->host); - - if (!nntp_store->newsrc) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, - _("Unable to open or create .newsrc file for %s: %s"), - CAMEL_SERVICE(store)->url->host, - strerror(errno)); - return NULL; - } - - return camel_nntp_folder_new (store, folder_name, ex); -} - -#ifdef INFO_AS_TREE -static void -build_folder_info (CamelNNTPStore *nntp_store, CamelFolderInfo **root, - CamelFolderInfo *parent, CamelNNTPGroupListEntry *entry, - char *prefix, char *suffix, - GHashTable *name_to_info) -{ - CamelURL *url = CAMEL_SERVICE (nntp_store)->url; - char *dot; - if ((dot = strchr (suffix, '.'))) { - /* it's an internal node, figure out the next node in - the chain */ - CamelFolderInfo *node; - char *node_name, *node_full_name; - - node_name = g_malloc0 (dot - suffix + 1); - strncpy (node_name, suffix, dot - suffix); - node_full_name = g_strdup_printf ("%s.%s", prefix, node_name); - - node = g_hash_table_lookup (name_to_info, node_full_name); - if (!node) { - /* we need to add one */ - node = g_new0 (CamelFolderInfo, 1); - node->name = g_strdup (node_name); - node->full_name = g_strdup (node_full_name); - node->url = NULL; - node->unread_message_count = -1; - - if (parent) { - if (parent->child) { - node->sibling = parent->child; - parent->child = node; - } - else { - parent->child = node; - } - } - else { - if (*root) { - *root = node; - } - else { - node->sibling = *root; - *root = node; - } - } - - g_hash_table_insert (name_to_info, node_full_name, node); - } - - build_folder_info (nntp_store, root, node, entry, node_full_name, dot + 1, name_to_info); - } - else { - /* it's a leaf node, make the CamelFolderInfo and - append it to @parent's list of children. */ - CamelFolderInfo *new_group; - - new_group = g_new0 (CamelFolderInfo, 1); - new_group->name = g_strdup (entry->group_name); - new_group->full_name = g_strdup (entry->group_name); - new_group->url = g_strdup_printf ("nntp://%s%s%s/%s", - url->user ? url->user : "", - url->user ? "@" : "", - url->host, (char *)entry->group_name); - - new_group->unread_message_count = (entry->high - entry->low - - camel_nntp_newsrc_get_num_articles_read (nntp_store->newsrc, entry->group_name)); - - if (parent) { - if (parent->child) { - new_group->sibling = parent->child; - parent->child = new_group; - } - else { - parent->child = new_group; - } - } - else { - if (*root) { - *root = new_group; - } - else { - new_group->sibling = *root; - *root = new_group; - } - } - } -} -#endif - -static CamelFolderInfo * -build_folder_info_from_grouplist (CamelNNTPStore *nntp_store, const char *top) -{ - GList *g; - CamelFolderInfo *groups = NULL; -#ifdef INFO_AS_TREE - GHashTable *hash = g_hash_table_new (g_str_hash, g_str_equal); -#else - CamelFolderInfo *last = NULL, *fi; - CamelURL *url = CAMEL_SERVICE (nntp_store)->url; -#endif + CAMEL_NNTP_STORE_LOCK(nntp_store, command_lock); - for (g = nntp_store->group_list->group_list; g; g = g_list_next (g)) { - CamelNNTPGroupListEntry *entry = g->data; - - if (!top || !strncmp (top, entry->group_name, strlen (top))) { -#ifdef INFO_AS_TREE - build_folder_info (nntp_store, &groups, NULL, entry, - "", entry->group_name, hash); -#else - - fi = g_new0 (CamelFolderInfo, 1); - fi->name = g_strdup (entry->group_name); - fi->full_name = g_strdup (entry->group_name); - fi->url = g_strdup_printf ("nntp://%s%s%s/%s", - url->user ? url->user : "", - url->user ? "@" : "", - url->host, (char *)entry->group_name); - - fi->unread_message_count = (entry->high - entry->low - - camel_nntp_newsrc_get_num_articles_read ( - nntp_store->newsrc, entry->group_name)); - camel_folder_info_build_path(fi, '/'); + folder = camel_nntp_folder_new(store, folder_name, ex); - if (last) - last->sibling = fi; - else - groups = fi; - last = fi; -#endif - } - } + CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock); - return groups; + return folder; } static CamelFolderInfo * -nntp_store_get_folder_info (CamelStore *store, const char *top, - guint32 flags, - CamelException *ex) +nntp_store_get_folder_info(CamelStore *store, const char *top, guint32 flags, CamelException *ex) { CamelURL *url = CAMEL_SERVICE (store)->url; CamelNNTPStore *nntp_store = (CamelNNTPStore *)store; - GPtrArray *names; CamelFolderInfo *groups = NULL, *last = NULL, *fi; - int i; - - /* if we haven't already read our .newsrc, read it now */ - if (!nntp_store->newsrc) - nntp_store->newsrc = - camel_nntp_newsrc_read_for_server (CAMEL_SERVICE(store)->url->host); - - if (!nntp_store->newsrc) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, - _("Unable to open or create .newsrc file for %s: %s"), - CAMEL_SERVICE(store)->url->host, - strerror(errno)); - return NULL; - } + unsigned int len; + unsigned char *line, *space; + int ret = -1; - if (!(flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED)) { - if (!nntp_store->group_list) - nntp_store->group_list = camel_nntp_grouplist_fetch (nntp_store, ex); - if (camel_exception_is_set (ex)) { - return NULL; - } - else { - fi = build_folder_info_from_grouplist (nntp_store, top); - return fi; - } + CAMEL_NNTP_STORE_LOCK(nntp_store, command_lock); + + ret = camel_nntp_command(nntp_store, (char **)&line, "list"); + if (ret != 215) { + ret = -1; + goto error; } - if (top == NULL) { - /* return the list of groups */ - names = camel_nntp_newsrc_get_subscribed_group_names (nntp_store->newsrc); - for (i = 0; i < names->len; i++) { - fi = g_new0 (CamelFolderInfo, 1); - fi->name = g_strdup (names->pdata[i]); - fi->full_name = g_strdup (names->pdata[i]); - fi->url = g_strdup_printf ("nntp://%s%s%s/%s", - url->user ? url->user : "", - url->user ? "@" : "", - url->host, (char *)names->pdata[i]); - /* FIXME */ + 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); + else + fi->url = g_strdup_printf ("nntp://%s/%s", url->host, line); fi->unread_message_count = -1; camel_folder_info_build_path(fi, '/'); @@ -548,23 +220,22 @@ nntp_store_get_folder_info (CamelStore *store, const char *top, groups = fi; last = fi; } - camel_nntp_newsrc_free_group_names (nntp_store->newsrc, names); - - return groups; - } - else { - /* getting a specific group */ - - fi = g_new0 (CamelFolderInfo, 1); - fi->name = g_strdup (top); - fi->full_name = g_strdup (top); - fi->url = g_strdup_printf ("nntp://%s/%s", url->host, top); - /* FIXME */ - fi->unread_message_count = -1; - camel_folder_info_build_path(fi, '/'); - - return fi; } + + if (ret < 0) + goto error; + + CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock); + + return groups; + +error: + CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock); + + if (groups) + camel_store_free_folder_info(store, groups); + + return NULL; } static gboolean @@ -572,7 +243,11 @@ nntp_store_folder_subscribed (CamelStore *store, const char *folder_name) { CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE (store); - return camel_nntp_newsrc_group_is_subscribed (nntp_store->newsrc, folder_name); + nntp_store = nntp_store; + + /* FIXME: implement */ + + return TRUE; } static void @@ -581,7 +256,9 @@ nntp_store_subscribe_folder (CamelStore *store, const char *folder_name, { CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE (store); - camel_nntp_newsrc_subscribe_group (nntp_store->newsrc, folder_name); + nntp_store = nntp_store; + + /* FIXME: implement */ } static void @@ -590,22 +267,32 @@ nntp_store_unsubscribe_folder (CamelStore *store, const char *folder_name, { CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE (store); - camel_nntp_newsrc_unsubscribe_group (nntp_store->newsrc, folder_name); + nntp_store = nntp_store; + + /* FIXME: implement */ } static void -finalize (CamelObject *object) +nntp_store_finalise (CamelObject *object) { CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE (object); - if (nntp_store->newsrc) - camel_nntp_newsrc_write (nntp_store->newsrc); + struct _CamelNNTPStorePrivate *p = nntp_store->priv; + + camel_service_disconnect((CamelService *)object, TRUE, NULL); + + camel_object_unref((CamelObject *)nntp_store->mem); + nntp_store->mem = NULL; + if (nntp_store->stream) + camel_object_unref((CamelObject *)nntp_store->stream); + #ifdef ENABLE_THREADS - e_mutex_destroy(nntp_store->command_lock); + e_mutex_destroy(p->command_lock); #endif + g_free(p); } static void -camel_nntp_store_class_init (CamelNNTPStoreClass *camel_nntp_store_class) +nntp_store_class_init (CamelNNTPStoreClass *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); @@ -630,21 +317,23 @@ camel_nntp_store_class_init (CamelNNTPStoreClass *camel_nntp_store_class) camel_store_class->unsubscribe_folder = nntp_store_unsubscribe_folder; } - - static void -camel_nntp_store_init (gpointer object, gpointer klass) +nntp_store_init (gpointer object, gpointer klass) { CamelRemoteStore *remote_store = CAMEL_REMOTE_STORE (object); CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE(object); CamelStore *store = CAMEL_STORE (object); + struct _CamelNNTPStorePrivate *p; remote_store->default_port = NNTP_PORT; store->flags = CAMEL_STORE_SUBSCRIPTIONS; + nntp_store->mem = (CamelStreamMem *)camel_stream_mem_new(); + + p = nntp_store->priv = g_malloc0(sizeof(*p)); #ifdef ENABLE_THREADS - nntp_store->command_lock = e_mutex_new(E_MUTEX_REC); + p->command_lock = e_mutex_new(E_MUTEX_REC); #endif } @@ -657,238 +346,113 @@ camel_nntp_store_get_type (void) camel_nntp_store_type = camel_type_register (CAMEL_REMOTE_STORE_TYPE, "CamelNNTPStore", sizeof (CamelNNTPStore), sizeof (CamelNNTPStoreClass), - (CamelObjectClassInitFunc) camel_nntp_store_class_init, + (CamelObjectClassInitFunc) nntp_store_class_init, NULL, - (CamelObjectInitFunc) camel_nntp_store_init, - (CamelObjectFinalizeFunc) finalize); + (CamelObjectInitFunc) nntp_store_init, + (CamelObjectFinalizeFunc) nntp_store_finalise); } return camel_nntp_store_type; } - -/** - * camel_nntp_command: Send a command to a NNTP server. - * @store: the NNTP store - * @ret: a pointer to return the full server response in - * @fmt: a printf-style format string, followed by arguments - * - * This command sends the command specified by @fmt and the following - * arguments to the connected NNTP store specified by @store. It then - * reads the server's response and parses out the status code. If - * the caller passed a non-NULL pointer for @ret, camel_nntp_command - * will set it to point to an buffer containing the rest of the - * response from the NNTP server. (If @ret was passed but there was - * no extended response, @ret will be set to NULL.) The caller must - * free this buffer when it is done with it. - * - * Return value: the response code of the nntp command. - **/ -static int -camel_nntp_command_send_recv (CamelNNTPStore *store, CamelException *ex, char **ret, char *cmd) +/* enter owning lock */ +int camel_nntp_store_set_folder(CamelNNTPStore *store, CamelFolder *folder, CamelFolderChangeInfo *changes, CamelException *ex) { - char *respbuf; - int resp_code; - gboolean again; + int ret; - do { - again = FALSE; + if (store->current_folder && strcmp(folder->full_name, store->current_folder) == 0) + return 0; - /* Send the command */ - if (camel_remote_store_send_string (CAMEL_REMOTE_STORE (store), ex, cmd) < 0) { - return NNTP_PROTOCOL_ERROR; - } - - /* Read the response */ - if (camel_remote_store_recv_line (CAMEL_REMOTE_STORE (store), &respbuf, ex) < 0) { - if (ret) - *ret = g_strdup (g_strerror (errno)); - return NNTP_PROTOCOL_ERROR; - } + /* FIXME: Do something with changeinfo */ + ret = camel_nntp_summary_check((CamelNNTPSummary *)folder->summary, changes, ex); - resp_code = atoi (respbuf); + g_free(store->current_folder); + store->current_folder = g_strdup(folder->full_name); - /* this is kind of a gross hack, but since an auth challenge - can pop up at any time, and we want to shield this from our - callers, we handle authentication here. */ - if (resp_code == NNTP_AUTH_REQUIRED) { - resp_code = camel_nntp_auth_authenticate (store, ex); - if (resp_code != NNTP_AUTH_ACCEPTED) { - return resp_code; - } - - /* need to resend our command here */ - again = TRUE; - } - } while (again); - - if (ret) { - *ret = strchr (respbuf, ' '); - if (*ret) - *ret = g_strdup (*ret + 1); - } - g_free (respbuf); - - return resp_code; + return ret; } +/* Enter owning lock */ int -camel_nntp_command (CamelNNTPStore *store, CamelException *ex, char **ret, char *fmt, ...) +camel_nntp_command(CamelNNTPStore *store, char **line, const char *fmt, ...) { - char *cmdbuf; + const unsigned char *p, *ps; + unsigned char c; va_list ap; - int resp_code; - char *real_fmt; - - real_fmt = g_strdup_printf ("%s\r\n", fmt); - - va_start (ap, fmt); - cmdbuf = g_strdup_vprintf (real_fmt, ap); - va_end (ap); + char *s; + int d; + unsigned int u, u2; - g_free (real_fmt); + e_mutex_assert_locked(store->priv->command_lock); - resp_code = camel_nntp_command_send_recv (store, ex, ret, cmdbuf); + if (!camel_remote_store_connected((CamelRemoteStore *)store, NULL)) + return -1; - if(camel_exception_get_id(ex) == - CAMEL_EXCEPTION_SERVICE_NOT_CONNECTED) { - /* the connect might have timed out, give it another shot.. */ - camel_exception_clear(ex); - if(nntp_store_connect(CAMEL_SERVICE(store), ex)) - resp_code = - camel_nntp_command_send_recv (store, ex, ret, cmdbuf); - /* that's it, no more tries */ + /* Check for unprocessed data, ! */ + if (store->stream->mode == CAMEL_NNTP_STREAM_DATA) { + g_warning("Unprocessed data left in stream, flushing"); + while (camel_nntp_stream_getd(store->stream, (unsigned char **)&p, &u) > 0) + ; } - - g_free (cmdbuf); - - return resp_code; -} - -void -camel_nntp_store_subscribe_group (CamelStore *store, - const gchar *group_name) -{ - gchar *root_dir = camel_nntp_store_get_toplevel_dir(CAMEL_NNTP_STORE(store)); - char *ret = NULL; - CamelException *ex = camel_exception_new(); - - if (camel_exception_get_id (ex)) { - g_free (root_dir); - camel_exception_free (ex); - return; - } - - if (camel_nntp_command ( CAMEL_NNTP_STORE (store), - ex, &ret, "GROUP %s", group_name) == NNTP_GROUP_SELECTED) { - /* we create an empty summary file here, so that when - the group is opened we'll know we need to build it. */ - gchar *summary_file; - int fd; - summary_file = g_strdup_printf ("%s/%s-ev-summary", root_dir, group_name); - - fd = open (summary_file, O_CREAT | O_RDWR, 0666); - close (fd); - - g_free (summary_file); - } - if (ret) g_free (ret); - - g_free (root_dir); - camel_exception_free (ex); -} - -void -camel_nntp_store_unsubscribe_group (CamelStore *store, - const gchar *group_name) -{ - gchar *root_dir = camel_nntp_store_get_toplevel_dir(CAMEL_NNTP_STORE(store)); - gchar *summary_file; - - summary_file = g_strdup_printf ("%s/%s-ev-summary", root_dir, group_name); - if (g_file_exists (summary_file)) - unlink (summary_file); - g_free (summary_file); - - g_free (root_dir); -} - -GList * -camel_nntp_store_list_subscribed_groups(CamelStore *store) -{ - GList *group_name_list = NULL; - struct stat stat_buf; - gint stat_error = 0; - gchar *entry_name; - gchar *full_entry_name; - gchar *real_group_name; - struct dirent *dir_entry; - DIR *dir_handle; - gchar *root_dir = camel_nntp_store_get_toplevel_dir(CAMEL_NNTP_STORE(store)); - - dir_handle = opendir (root_dir); - g_return_val_if_fail (dir_handle, NULL); - - /* read the first entry in the directory */ - dir_entry = readdir (dir_handle); - while ((stat_error != -1) && (dir_entry != NULL)) { - - /* get the name of the next entry in the dir */ - entry_name = dir_entry->d_name; - full_entry_name = g_strdup_printf ("%s/%s", root_dir, entry_name); - stat_error = stat (full_entry_name, &stat_buf); - g_free (full_entry_name); - - /* is it a normal file ending in -ev-summary ? */ - if ((stat_error != -1) && S_ISREG (stat_buf.st_mode)) { - gboolean summary_suffix_found; - - real_group_name = string_prefix (entry_name, "-ev-summary", - &summary_suffix_found); - - if (summary_suffix_found) - /* add the folder name to the list */ - group_name_list = g_list_append (group_name_list, - real_group_name); + camel_nntp_stream_set_mode(store->stream, CAMEL_NNTP_STREAM_LINE); + + va_start(ap, fmt); + ps = p = fmt; + while ( (c = *p++) ) { + switch (c) { + case '%': + c = *p++; + camel_stream_write((CamelStream *)store->mem, ps, p-ps-(c=='%'?1:2)); + ps = p; + switch (c) { + case 's': + s = va_arg(ap, char *); + camel_stream_write((CamelStream *)store->mem, s, strlen(s)); + break; + case 'd': + d = va_arg(ap, int); + camel_stream_printf((CamelStream *)store->mem, "%d", d); + break; + case 'u': + u = va_arg(ap, unsigned int); + camel_stream_printf((CamelStream *)store->mem, "%u", u); + break; + case 'm': + s = va_arg(ap, char *); + camel_stream_printf((CamelStream *)store->mem, "<%s>", s); + break; + case 'r': + u = va_arg(ap, unsigned int); + u2 = va_arg(ap, unsigned int); + if (u == u2) + camel_stream_printf((CamelStream *)store->mem, "%u", u); + else + camel_stream_printf((CamelStream *)store->mem, "%u-%u", u, u2); + break; + default: + g_warning("Passing unknown format to nntp_command: %c\n", c); + g_assert(0); + } } - /* read next entry */ - dir_entry = readdir (dir_handle); } - closedir (dir_handle); - - return group_name_list; -} + 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); + /* FIXME: hack */ + g_byte_array_set_size(store->mem->buffer, 0); -gchar * -camel_nntp_store_get_toplevel_dir (CamelNNTPStore *store) -{ - CamelURL *url = CAMEL_SERVICE (store)->url; - char *top_dir; + if (camel_nntp_stream_line(store->stream, (unsigned char **)line, &u) == -1) + return -1; - g_assert(url != NULL); + u = strtoul(*line, NULL, 10); - top_dir = g_strdup_printf( "%s/evolution/news/%s", - g_get_home_dir (), - url->host ); + /* 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 top_dir; + return u; } -static gboolean -ensure_news_dir_exists (CamelNNTPStore *store) -{ - gchar *dir = camel_nntp_store_get_toplevel_dir (store); - - if (access (dir, F_OK) == 0) { - g_free (dir); - return TRUE; - } - - if (camel_mkdir_hier (dir, S_IRWXU) == -1) { - g_free (dir); - return FALSE; - } - - return TRUE; -} |