diff options
-rw-r--r-- | camel/ChangeLog | 28 | ||||
-rw-r--r-- | camel/providers/nntp/camel-nntp-newsrc.c | 32 | ||||
-rw-r--r-- | camel/providers/nntp/camel-nntp-store.c | 184 | ||||
-rw-r--r-- | camel/providers/nntp/camel-nntp-store.h | 31 | ||||
-rw-r--r-- | camel/providers/nntp/camel-nntp-utils.c | 56 |
5 files changed, 294 insertions, 37 deletions
diff --git a/camel/ChangeLog b/camel/ChangeLog index 4c3a60d500..410a5dfaf8 100644 --- a/camel/ChangeLog +++ b/camel/ChangeLog @@ -1,3 +1,31 @@ +2000-08-31 Chris Toshok <toshok@helixcode.com> + + * providers/nntp/camel-nntp-newsrc.c + (camel_nntp_newsrc_read_for_server): make this a bit more robust. + try to create an empty .newsrc file for the server if we can't + open it for reading. also, don't allocate everything until we've + opened the file. + + * providers/nntp/camel-nntp-utils.c (get_OVER_headers): make use + of our overview field indices. + (camel_nntp_get_headers): only call get_OVER_headers if the + extension is present. warn if it's not - since get_HEAD_headers + needs work before it works. + + * providers/nntp/camel-nntp-store.c + (camel_nntp_store_get_extensions): new function - query the server + for it's extensions. + (camel_nntp_store_get_overview_fmt): new function - query the + server for the overview format and build our table of the indices + we care about. support the "full" suffix on fields. + (nntp_store_connect): call camel_nntp_store_get_extensions and + camel_nntp_store_get_overview_fmt. + + * providers/nntp/camel-nntp-store.h: add codes for extensions + found on news.mozilla.org. only one that we care about is OVER. + also, add CamelNNTPOverField and an enum of the overview fields + that we care about. + 2000-08-31 Jeffrey Stedfast <fejj@helixcode.com> * providers/imap/camel-imap-utils.c (imap_translate_sexp): diff --git a/camel/providers/nntp/camel-nntp-newsrc.c b/camel/providers/nntp/camel-nntp-newsrc.c index 81594d3dcd..9510fe5bfa 100644 --- a/camel/providers/nntp/camel-nntp-newsrc.c +++ b/camel/providers/nntp/camel-nntp-newsrc.c @@ -26,6 +26,8 @@ #include <string.h> #include <stdlib.h> #include <glib.h> +#include <fcntl.h> +#include <unistd.h> #include "camel-nntp-newsrc.h" typedef struct { @@ -440,17 +442,33 @@ camel_nntp_newsrc_read_for_server (const char *server) { FILE *fp; char buf[BUFFER_LENGTH]; - CamelNNTPNewsrc *newsrc = g_new0(CamelNNTPNewsrc, 1); + char *filename = g_strdup_printf ("%s/.newsrc-%s", g_get_home_dir(), server); + CamelNNTPNewsrc *newsrc; + + if ((fp = fopen(filename, "r")) == NULL) { + int fd; + + g_warning ("~/.newsrc-%s not present. creating empty file\n", server); + + if ((fd = open (filename, O_CREAT, O_TRUNC, O_WRONLY, 0777)) < 0) { + g_warning ("unable to create ~/.newsrc-%s file\n", server); + g_free (filename); + return NULL; + } + close (fd); - newsrc->filename = g_strdup_printf ("%s/.newsrc-%s", g_get_home_dir(), server); + if ((fp = fopen(filename, "r")) == NULL) { + g_warning ("unable to open ~/.newsrc-%s file on second try.\n", server); + g_free (filename); + return NULL; + } + } + + newsrc = g_new0(CamelNNTPNewsrc, 1); + newsrc->filename = filename; newsrc->groups = g_hash_table_new (g_str_hash, g_str_equal); newsrc->subscribed_groups = g_hash_table_new (g_str_hash, g_str_equal); - if ((fp = fopen(newsrc->filename, "r")) == NULL) { - g_free (newsrc->filename); - g_free (newsrc); - return NULL; - } while (fgets(buf, MAX_LINE_LENGTH, fp) != NULL) { /* we silently ignore (and lose!) lines longer than 20 * 1500 chars. diff --git a/camel/providers/nntp/camel-nntp-store.c b/camel/providers/nntp/camel-nntp-store.c index 5c9475ed14..37cb184f28 100644 --- a/camel/providers/nntp/camel-nntp-store.c +++ b/camel/providers/nntp/camel-nntp-store.c @@ -48,6 +48,8 @@ #define NNTP_PORT 119 +#define DUMP_EXTENSIONS + static CamelServiceClass *service_class = NULL; /* Returns the class for a CamelNNTPStore */ @@ -57,6 +59,143 @@ static CamelServiceClass *service_class = NULL; static gboolean ensure_news_dir_exists (CamelNNTPStore *store); +static void +camel_nntp_store_get_extensions (CamelNNTPStore *store) +{ + store->extensions = 0; + + if (CAMEL_NNTP_OK == camel_nntp_command (store, NULL, "LIST EXTENSIONS")) { + gboolean done = FALSE; + + while (!done) { + char *line; + + line = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER(store->istream)); + + 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); + } + } + +#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) +{ + int status; + char *result; + char *field; + int i; + + status = camel_nntp_command (store, NULL, + "LIST OVERVIEW.FMT"); + + if (status != CAMEL_NNTP_OK) { + /* 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; + return; + } + + result = camel_nntp_command_get_additional_data (store); + + /* count the number of fields the server returns in the + overview. 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 ((field = strsep (&result, "\n"))) { + CamelNNTPOverField *over_field = NULL; + char *colon = NULL;; + + if (field[0] == '\0') + break; + + if (!strncasecmp (field, "From:", 5)) { + over_field = &store->overview_field [ CAMEL_NNTP_OVER_FROM ]; + over_field->index = store->num_overview_fields; + colon = field + 5; + } + else if (!strncasecmp (field, "Subject:", 7)) { + over_field = &store->overview_field [ CAMEL_NNTP_OVER_SUBJECT ]; + over_field->index = store->num_overview_fields; + colon = field + 7; + } + else if (!strncasecmp (field, "Date:", 5)) { + over_field = &store->overview_field [ CAMEL_NNTP_OVER_DATE ]; + over_field->index = store->num_overview_fields; + colon = field + 5; + } + else if (!strncasecmp (field, "Message-ID:", 11)) { + over_field = &store->overview_field [ CAMEL_NNTP_OVER_MESSAGE_ID ]; + over_field->index = store->num_overview_fields; + colon = field + 11; + } + else if (!strncasecmp (field, "References:", 11)) { + over_field = &store->overview_field [ CAMEL_NNTP_OVER_REFERENCES ]; + over_field->index = store->num_overview_fields; + colon = field + 11; + } + else if (!strncasecmp (field, "Bytes:", 6)) { + over_field = &store->overview_field [ CAMEL_NNTP_OVER_BYTES ]; + over_field->index = store->num_overview_fields; + colon = field + 11; + } + + if (colon && !strcmp (colon + 1, "full")) + over_field->full = TRUE; + + store->num_overview_fields ++; + } + + g_free (result); + + 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; + } + } +} + static gboolean nntp_store_connect (CamelService *service, CamelException *ex) { @@ -64,6 +203,7 @@ nntp_store_connect (CamelService *service, CamelException *ex) struct sockaddr_in sin; int fd; char *buf; + int resp_code; CamelNNTPStore *store = CAMEL_NNTP_STORE (service); if (!ensure_news_dir_exists(store)) { @@ -106,14 +246,29 @@ nntp_store_connect (CamelService *service, CamelException *ex) return -1; } + /* check if posting is allowed. */ + resp_code = atoi (buf); + if (resp_code == 200) { + g_print ("posting allowed\n"); + store->posting_allowed = TRUE; + } + else if (resp_code == 201) { + 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; + } + g_free (buf); /* get a list of extensions that the server supports */ - if (CAMEL_NNTP_OK == camel_nntp_command (store, NULL, "LIST EXTENSIONS")) { - char *ext_response = camel_nntp_command_get_additional_data(store); + camel_nntp_store_get_extensions (store); - g_free (ext_response); - } + /* if the server supports the OVER extension, get the overview.fmt */ + if (store->extensions & CAMEL_NNTP_EXT_OVER) + camel_nntp_store_get_overview_fmt (store); return TRUE; } @@ -162,6 +317,14 @@ nntp_store_get_folder (CamelStore *store, const gchar *folder_name, 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; + } + /* check if folder has already been created */ /* call the standard routine for that when */ /* it is done ... */ @@ -329,13 +492,12 @@ camel_nntp_command (CamelNNTPStore *store, char **ret, char *fmt, ...) * a NNTP command. * @store: the NNTP store * - * This command gets the additional data returned by - * This command gets the additional data returned by "multi-line" POP - * commands, such as LIST, RETR, TOP, and UIDL. This command _must_ - * be called after a successful (CAMEL_NNTP_OK) call to - * camel_nntp_command for a command that has a multi-line response. - * The returned data is un-byte-stuffed, and has lines termined by - * newlines rather than CR/LF pairs. + * This command gets the additional data returned by This command gets + * the additional data returned by "multi-line" NNTP commands, such as + * LIST. This command must only be called after a successful + * (CAMEL_NNTP_OK) call to camel_nntp_command for a command that has a + * multi-line response. The returned data is un-byte-stuffed, and has + * lines termined by newlines rather than CR/LF pairs. * * Return value: the data, which the caller must free. **/ diff --git a/camel/providers/nntp/camel-nntp-store.h b/camel/providers/nntp/camel-nntp-store.h index 3099f84962..a74ea84f5e 100644 --- a/camel/providers/nntp/camel-nntp-store.h +++ b/camel/providers/nntp/camel-nntp-store.h @@ -40,12 +40,41 @@ extern "C" { #define IS_CAMEL_NNTP_STORE(o) (CAMEL_CHECK_TYPE((o), CAMEL_NNTP_STORE_TYPE)) +enum { + CAMEL_NNTP_OVER_FROM, + CAMEL_NNTP_OVER_SUBJECT, + CAMEL_NNTP_OVER_DATE, + CAMEL_NNTP_OVER_MESSAGE_ID, + CAMEL_NNTP_OVER_REFERENCES, + CAMEL_NNTP_OVER_BYTES, + + CAMEL_NNTP_OVER_LAST +}; + +typedef struct { + int index; + gboolean full; /* full in the OVER sense - the field name + precedes the ':' in the XOVER list. */ +} CamelNNTPOverField; + typedef struct { CamelStore parent_object; -#define CAMEL_NNTP_EXT_XOVER 0x01 +#define CAMEL_NNTP_EXT_SEARCH (1<<0) +#define CAMEL_NNTP_EXT_SETGET (1<<1) +#define CAMEL_NNTP_EXT_OVER (1<<2) +#define CAMEL_NNTP_EXT_XPATTEXT (1<<3) +#define CAMEL_NNTP_EXT_XACTIVE (1<<4) +#define CAMEL_NNTP_EXT_LISTMOTD (1<<5) +#define CAMEL_NNTP_EXT_LISTSUBSCR (1<<6) +#define CAMEL_NNTP_EXT_LISTPNAMES (1<<7) guint32 extensions; + gboolean posting_allowed; + + int num_overview_fields; + CamelNNTPOverField overview_field[ CAMEL_NNTP_OVER_LAST ]; + CamelNNTPNewsrc *newsrc; CamelStream *istream, *ostream; diff --git a/camel/providers/nntp/camel-nntp-utils.c b/camel/providers/nntp/camel-nntp-utils.c index e0a331f2a7..01b7876134 100644 --- a/camel/providers/nntp/camel-nntp-utils.c +++ b/camel/providers/nntp/camel-nntp-utils.c @@ -33,7 +33,7 @@ #include <string.h> static void -get_XOVER_headers(CamelNNTPStore *nntp_store, CamelFolder *folder, +get_OVER_headers(CamelNNTPStore *nntp_store, CamelFolder *folder, int first_message, int last_message, CamelException *ex) { int status; @@ -43,7 +43,7 @@ get_XOVER_headers(CamelNNTPStore *nntp_store, CamelFolder *folder, "XOVER %d-%d", first_message, last_message); - + if (status == CAMEL_NNTP_OK) { CamelStream *nntp_istream = nntp_store->istream; gboolean done = FALSE; @@ -55,23 +55,44 @@ get_XOVER_headers(CamelNNTPStore *nntp_store, CamelFolder *folder, if (*line == '.') { done = TRUE; + g_print ("done\n"); } else { CamelMessageInfo *new_info = g_new0(CamelMessageInfo, 1); char **split_line = g_strsplit (line, "\t", 7); - - new_info->subject = g_strdup(split_line[1]); - new_info->from = g_strdup(split_line[2]); + char *subject, *from, *date, *message_id, *bytes; + + subject = split_line [nntp_store->overview_field [CAMEL_NNTP_OVER_SUBJECT].index]; + from = split_line [nntp_store->overview_field [CAMEL_NNTP_OVER_FROM].index]; + date = split_line [nntp_store->overview_field [CAMEL_NNTP_OVER_DATE].index]; + message_id = split_line [nntp_store->overview_field [CAMEL_NNTP_OVER_MESSAGE_ID].index]; + bytes = split_line [nntp_store->overview_field [CAMEL_NNTP_OVER_BYTES].index]; + + /* if the overview format flagged this + field as "full", skip over the + preceding field name and colon */ + if (nntp_store->overview_field [ CAMEL_NNTP_OVER_SUBJECT ].full) + subject += strlen ("Subject:"); + if (nntp_store->overview_field [ CAMEL_NNTP_OVER_FROM ].full) + from += strlen ("From:"); + if (nntp_store->overview_field [ CAMEL_NNTP_OVER_DATE ].full) + date += strlen ("Date:"); + if (nntp_store->overview_field [ CAMEL_NNTP_OVER_MESSAGE_ID ].full) + message_id += strlen ("Message-ID:"); + if (nntp_store->overview_field [ CAMEL_NNTP_OVER_BYTES ].full) + bytes += strlen ("Bytes:"); + + new_info->subject = g_strdup(subject); + new_info->from = g_strdup(from); new_info->to = g_strdup(nntp_folder->group_name); - new_info->date_sent = header_decode_date(split_line[3], NULL); + new_info->date_sent = header_decode_date(date, NULL); #if 0 /* XXX do we need to fill in both dates? */ - new_info->headers.date_received = g_strdup(split_line[3]); + new_info->headers.date_received = g_strdup(date); #endif - new_info->size = atoi(split_line[5]); - new_info->uid = g_strdup_printf ("%s,%s", split_line[0], split_line[4]); - new_info->message_id = g_strdup(split_line[4]); - g_strfreev (split_line); + new_info->size = atoi(bytes); + new_info->uid = g_strdup_printf ("%s,%s", split_line[0], message_id); + new_info->message_id = g_strdup(message_id); if (camel_nntp_newsrc_article_is_read (nntp_store->newsrc, nntp_folder->group_name, @@ -79,6 +100,7 @@ get_XOVER_headers(CamelNNTPStore *nntp_store, CamelFolder *folder, new_info->flags |= CAMEL_MESSAGE_SEEN; camel_folder_summary_add (nntp_folder->summary, new_info); + g_strfreev (split_line); } g_free (line); } @@ -204,15 +226,13 @@ camel_nntp_get_headers (CamelStore *store, return; } -#if 0 - if (nntp_store->extensions & CAMEL_NNTP_EXT_XOVER) { -#endif - get_XOVER_headers (nntp_store, folder, first_message, last_message, ex); -#if 0 + if (nntp_store->extensions & CAMEL_NNTP_EXT_OVER) { + get_OVER_headers (nntp_store, folder, first_message, last_message, ex); } else { + g_warning ("need to fix get_HEAD_headers\n"); +#if 0 get_HEAD_headers (nntp_store, folder, first_message, last_message, ex); - } #endif + } } - |