X-Git-Url: http://git.madism.org/?p=apps%2Fmadmutt.git;a=blobdiff_plain;f=nntp.c;h=914df3968bb66cf50f407054d1b79598550761b7;hp=9c9a591232797ca808f72a5845fb440253f042e9;hb=1cd799f26007d33d10b1ae04aa443f572d84ab20;hpb=c7ceecd31e97891dd5cf48536d4bddf2c1d7d9dd diff --git a/nntp.c b/nntp.c index 9c9a591..914df39 100644 --- a/nntp.c +++ b/nntp.c @@ -1,68 +1,1056 @@ /* + * Copyright notice from original mutt: * Copyright (C) 1998 Brandon Long * Copyright (C) 1999 Andrej Gritsenko * Copyright (C) 2000-2002 Vsevolod Volkov - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifdef HAVE_CONFIG_H -#include -#endif + * + * This file is part of mutt-ng, see http://www.muttng.org/. + * It's licensed under the GNU General Public License, + * please see the file GPL in the top level source directory. + */ + +#include + +#include + +#include +#include +#include +#include #include "mutt.h" -#include "mutt_curses.h" #include "sort.h" -#include "mx.h" -#include "mime.h" -#include "rfc1524.h" -#include "rfc2047.h" -#include "mailbox.h" #include "nntp.h" +#include "buffy.h" +#include "crypt.h" + +#define NNTP_PORT 119 +#define NNTP_SSL_PORT 563 + +static struct { + unsigned use_cache : 1; + + int checked; +} nntp = { true, 0 }; + +static int nntp_check_newgroups (nntp_server_t *, int); + +/* newsrc {{{ */ + +static void mutt_newsgroup_stat(nntp_data_t *data) +{ + data->unread = 0; + if (data->lastMessage == 0 || data->firstMessage > data->lastMessage) + return; + + data->unread = data->lastMessage - data->firstMessage + 1; + for (int i = 0; i < data->num; i++) { + int first = MAX(data->entries[i].first, data->firstMessage); + int last = MIN(data->entries[i].last, data->lastMessage); + data->unread -= MAX(0, last - first + 1); + } +} + +static int nntp_parse_newsrc_line(nntp_server_t *news, const char *line) +{ + nntp_data_t *data; + char group[LONG_STRING]; + const char *p; + int x = 1; + + for (p = line; *p; p++) { + x += *p == ','; + } + + p = strpbrk(line, ":!"); + if (!p) + return -1; + + m_strncpy(group, ssizeof(group), line, p - line); + data = hash_find(news->newsgroups, group); + if (!data) { + data = nntp_data_new(); + data->group = p_dupstr(line, p - line); + data->nserv = news; + data->deleted = 1; + hash_insert(news->newsgroups, data->group, data); + news->tail = nntp_data_list_append(news->tail, data); + } else { + p_delete(&data->entries); + } + + data->rc = 1; + data->entries = p_new(NEWSRC_ENTRY, x * 2); + data->max = x * 2; + data->subscribed = (*p++ == ':'); + p = skipspaces(p); + + for (x = 0; *p; p++) { + data->entries[x].first = strtol(p, (char **)&p, 10); + p += strcspn(p, "-,"); + if (*p == '-') { + if (!*p) + break; + data->entries[x].last = strtol(p + 1, (char **)&p, 10); + } else { + data->entries[x].last = data->entries[x].first; + p = strchrnul(p, ','); + } + x += data->entries[x].last != 0; + } + + if (x && !data->lastMessage) + data->lastMessage = data->entries[x - 1].last; + data->num = x; + mutt_newsgroup_stat(data); + return 0; +} + +static int slurp_newsrc (nntp_server_t * news) +{ + FILE *fp; + char *buf = NULL; + size_t n = 0; + struct stat sb; + + news->stat = stat(news->newsrc, &sb); + news->size = sb.st_size; + news->mtime = sb.st_mtime; + + if ((fp = safe_fopen(news->newsrc, "r")) == NULL) + return -1; + + /* hmm, should we use dotlock? */ + if (mx_lock_file(news->newsrc, fileno (fp), 0, 0, 1)) { + m_fclose(&fp); + return -1; + } + + while (getline(&buf, &n, fp) >= 0) { + nntp_parse_newsrc_line(news, buf); + } + p_delete(&buf); + + mx_unlock_file (news->newsrc, fileno (fp), 0); + m_fclose(&fp); + return 0; +} + +#define nntp_cache_expand(dst, dlen, fmt, ...) \ + do { \ + snprintf((dst), (dlen), "%s/" fmt, mod_core.cachedir, ##__VA_ARGS__);\ + mutt_expand_path((dst), (dlen)); \ + } while (0) + +/* Loads $news_cache_dir/.index into memory, loads newsserver data + * and newsgroup cache names */ +static int nntp_parse_cacheindex(nntp_server_t *news) +{ + char buf[HUGE_STRING]; + FILE *idx; + + p_delete(&news->cache); + if (m_strisempty(mod_core.cachedir)) + return 0; + + nntp_cache_expand(buf, sizeof(buf), "%s.index", news->conn->account.host); + if (!(idx = safe_fopen(buf, "a+"))) + return 0; + rewind(idx); + + while (fgets(buf, sizeof(buf), idx)) { + buf[m_strlen(buf) - 1] = 0; /* strip ending '\n' */ + if (!m_strncmp(buf, "#: ", 3) + && !m_strcasecmp(buf + 3, news->conn->account.host)) { + break; + } + } + + while (fgets(buf, sizeof(buf), idx)) { + char *p, *q; + int l, m, t; + + if (m_strstart(buf, "#:", NULL)) + break; + + p = strchrnul(buf, ' '); + if (!*p) + continue; + *p++ = '\0'; + p = vskipspaces(p); + q = strchrnul(p, ' '); + if (!*q) + continue; + *q++ = '\0'; + l = strtol(q, &q, 10); + m = strtol(q, &q, 10); + + if (!m_strcmp(buf, "ALL")) { + m_strreplace(&news->cache, m_strdup(p)); + news->newgroups_time = m; + continue; + } + + if (news->newsgroups) { + nntp_data_t *data = hash_find(news->newsgroups, buf); + + if (!data) { + data = nntp_data_new(); + data->group = m_strdup(buf); + data->nserv = news; + data->deleted = 1; + hash_insert (news->newsgroups, data->group, data); + news->tail = nntp_data_list_append(news->tail, data); + } + m_strreplace(&data->cache, p); + + t = !data->firstMessage || data->lastMessage < m; + if (!data->firstMessage) + data->firstMessage = l; + if (data->lastMessage < m) + data->lastMessage = m; + data->lastCached = m; + if (t || !data->unread) + mutt_newsgroup_stat(data); + } + } + + m_fclose(&idx); + return 0; +} + +/* nntp_parse_url: given an NNPT URL, return host, port, + * username, password and newsgroup will recognise. */ +static int nntp_parse_url(const char *server, ACCOUNT *act, char *group, + ssize_t group_len) +{ + ciss_url_t url; + char s[STRING]; + + act->flags = 0; + act->port = 0; + act->type = M_ACCT_TYPE_NNTP; + + m_strcpy(s, sizeof(s), server); + url_parse_ciss(&url, s); + + if (url.scheme != U_NNTP || url.scheme != U_NNTPS) + return -1; + + act->has_ssl = (url.scheme == U_NNTPS); + if (!act->port) { + act->port = act->has_ssl ? NNTP_PORT : NNTP_SSL_PORT; + } + m_strcpy(group, group_len, url.path); + + return mutt_account_fromurl(act, &url); +} + +void nntp_expand_path (char *line, ssize_t len, ACCOUNT * act) +{ + ciss_url_t url; + + url.path = m_strdup(line); + mutt_account_tourl (act, &url); + url_ciss_tostring (&url, line, len, 0); + p_delete(&url.path); +} + +static int add_group (char *buf, void *serv) +{ + nntp_server_t *s = serv; + char group[LONG_STRING], mod, desc[HUGE_STRING]; + int first, last; + nntp_data_t *nntp_data; + static int n = 0; + + nntp.checked = n; /* nntp.checked have N, where N = number of groups */ + if (!buf) /* at EOF must be zerouth */ + n = 0; + + if (!s || !buf) + return 0; + + *desc = 0; + sscanf (buf, "%s %d %d %c %[^\n]", group, &last, &first, &mod, desc); + if (!group) + return 0; + if ((nntp_data = hash_find(s->newsgroups, group)) == NULL) { + n++; + nntp_data = nntp_data_new(); + nntp_data->group = m_strdup(group); + nntp_data->nserv = s; + hash_insert(s->newsgroups, nntp_data->group, nntp_data); + s->tail = nntp_data_list_append(s->tail, nntp_data); + } + nntp_data->deleted = 0; + nntp_data->firstMessage = first; + nntp_data->lastMessage = last; + if (mod == 'y') + nntp_data->allowed = 1; + else + nntp_data->allowed = 0; + if (nntp_data->desc) + p_delete(&nntp_data->desc); + if (*desc) + nntp_data->desc = m_strdup(desc); + if (nntp_data->rc || nntp_data->lastCached) + mutt_newsgroup_stat (nntp_data); + else if (nntp_data->lastMessage && + nntp_data->firstMessage <= nntp_data->lastMessage) + nntp_data->unread = nntp_data->lastMessage - nntp_data->firstMessage + 1; + else + nntp_data->unread = 0; + + return 0; +} + +/* Load list of all newsgroups from cache ALL */ +static int nntp_get_cache_all (nntp_server_t * serv) +{ + char buf[HUGE_STRING]; + FILE *f; + + nntp_cache_expand(buf, ssizeof(buf), "%s", serv->cache); + if ((f = safe_fopen (buf, "r"))) { + int i = 0; + + while (fgets (buf, sizeof (buf), f) != NULL) { + if (ReadInc && (i % ReadInc == 0)) + mutt_message (_("Loading list from cache... %d"), i); + add_group (buf, serv); + i++; + } + add_group (NULL, NULL); + m_fclose(&f); + mutt_clear_error (); + return 0; + } + else { + p_delete(&serv->cache); + return -1; + } +} + +/* + * Automatically loads a newsrc into memory, if necessary. + * Checks the size/mtime of a newsrc file, if it doesn't match, load + * again. Hmm, if a system has broken mtimes, this might mean the file + * is reloaded every time, which we'd have to fix. + * + * a newsrc file is a line per newsgroup, with the newsgroup, then a + * ':' denoting subscribed or '!' denoting unsubscribed, then a + * comma separated list of article numbers and ranges. + */ +nntp_server_t *mutt_select_newsserver (char *server) +{ + char file[_POSIX_PATH_MAX]; + char *buf, *p; + nntp_data_t *list; + ACCOUNT act; + nntp_server_t *serv; + CONNECTION *conn; + + p_clear(&act, 1); + + if (!server || !*server) { + mutt_error _("No newsserver defined!"); + + return NULL; + } + + buf = p = p_new(char, m_strlen(server) + 10); + if (url_check_scheme (server) == U_UNKNOWN) { + strcpy (buf, "nntp://"); + p = strchr (buf, '\0'); + } + strcpy (p, server); + + if ((nntp_parse_url (buf, &act, file, sizeof (file))) < 0 || *file) { + p_delete(&buf); + mutt_error (_("%s is an invalid newsserver specification!"), server); + return NULL; + } + p_delete(&buf); + + conn = mutt_conn_find (NULL, &act); + if (!conn) + return NULL; + + nntp_cache_expand(file, sizeof(file), "%s.newsrc", conn->account.host); + serv = (nntp_server_t *) conn->data; + if (serv) { + struct stat sb; + + /* externally modified? */ + if (serv->stat != stat(file, &sb) || (!serv->stat && + (serv->size != sb.st_size + || serv->mtime != sb.st_mtime))) + { + for (list = serv->list; list; list = list->next) { + list->subscribed = list->rc = list->num = 0; + } + slurp_newsrc (serv); + } + + if (serv->status == NNTP_BYE) + serv->status = NNTP_NONE; + nntp_check_newgroups (serv, 0); + return serv; + } + + /* New newsserver */ + serv = p_new(nntp_server_t, 1); + serv->tail = &serv->list; + serv->conn = conn; + serv->newsrc = m_strdup(file); + serv->newsgroups = hash_new(SHRT_MAX, false); + slurp_newsrc (serv); /* load .newsrc */ + nntp_parse_cacheindex (serv); /* load .index */ + if (nntp.use_cache && serv->cache && nntp_get_cache_all (serv) >= 0) + nntp_check_newgroups (serv, 1); + else if (nntp_get_active (serv) < 0) { + hash_delete(&serv->newsgroups, NULL); + nntp_data_list_wipe(&serv->list); + p_delete(&serv->newsrc); + p_delete(&serv->cache); + p_delete(&serv); + return NULL; + } + conn->data = (void *) serv; + + return serv; +} + +/* + * full status flags are not supported by nntp, but we can fake some + * of them. This is how: + * Read = a read message number is in the .newsrc + * New = a message is new since we last read this newsgroup + * Old = anything else + * So, Read is marked as such in the newsrc, old is anything that is + * "skipped" in the newsrc, and new is anything not in the newsrc nor + * in the cache. By skipped, I mean before the last unread message + */ +static void +nntp_get_status(CONTEXT *ctx, HEADER *h, const char *group, int article) +{ + nntp_data_t *data = ctx->data; + + if (group) + data = hash_find(data->nserv->newsgroups, group); + if (!data) + return; + + for (int i = 0; i < data->num; i++) { + if ((article >= data->entries[i].first) && + (article <= data->entries[i].last)) + { + /* we cannot use mutt_set_flag() because mx_update_context() + didn't called yet */ + h->read = 1; + return; + } + } + + /* If article was not cached yet, it is new! :) */ + if (!data->cache || article > data->lastCached) + return; + + /* Old articles are articles which aren't read but an article after them + * has been cached */ + if (option(OPTMARKOLD)) + h->old = 1; +} + +static void newsrc_gen_entries (CONTEXT * ctx) +{ + nntp_data_t *data = (nntp_data_t *) ctx->data; + int series, x; + int last = 0, first = 1; + int save_sort = SORT_ORDER; + + if (Sort != SORT_ORDER) { + save_sort = Sort; + Sort = SORT_ORDER; + mutt_sort_headers (ctx, 0); + } + + if (!data->max) { + data->entries = p_new(NEWSRC_ENTRY, 5); + data->max = 5; + } + + /* + * Set up to fake initial sequence from 1 to the article before the + * first article in our list + */ + data->num = 0; + series = 1; + + for (x = 0; x < ctx->msgcount; x++) { + if (series) { /* search for first unread */ + /* + * We don't actually check sequential order, since we mark + * "missing" entries as read/deleted + */ + last = ctx->hdrs[x]->article_num; + if (last >= data->firstMessage && !ctx->hdrs[x]->deleted && + !ctx->hdrs[x]->read) { + if (data->num >= data->max) { + data->max = data->max * 2; + p_realloc(&data->entries, data->max); + } + data->entries[data->num].first = first; + data->entries[data->num].last = last - 1; + data->num++; + series = 0; + } + } + else { /* search for first read */ + + if (ctx->hdrs[x]->deleted || ctx->hdrs[x]->read) { + first = last + 1; + series = 1; + } + last = ctx->hdrs[x]->article_num; + } + } + if (series && first <= data->lastLoaded) { + if (data->num >= data->max) { + data->max = data->max * 2; + p_realloc(&data->entries, data->max); + } + data->entries[data->num].first = first; + data->entries[data->num].last = data->lastLoaded; + data->num++; + } + + if (save_sort != Sort) { + Sort = save_sort; + mutt_sort_headers (ctx, 0); + } +} + +static int mutt_update_list_file (char *filename, char *section, + const char *key, const char *line) +{ + FILE *ifp; + FILE *ofp; + char buf[HUGE_STRING]; + char tmpf[_POSIX_PATH_MAX], lnk[_POSIX_PATH_MAX]; + char *c; + int ext = 0, done = 0, r = 0, l = 0; + + /* if file not exist, create it */ + if ((ifp = safe_fopen (filename, "a"))) + m_fclose(&ifp); + if (!(ifp = safe_fopen (filename, "r"))) { + mutt_error (_("Unable to open %s for reading"), filename); + return -1; + } + if (mx_lock_file (filename, fileno (ifp), 0, 0, 1)) { + m_fclose(&ifp); + mutt_error (_("Unable to lock %s"), filename); + return -1; + } + + /* use m_tempfile() to get a tempfile in the same + * directory as filename is so that we can follow symlinks + * via rename(2); as dirname(2) may modify its argument, + * temporarily use buf as copy of it + */ + m_strcpy(buf, sizeof(buf), filename); + ofp = m_tempfile(tmpf, sizeof(tmpf), dirname(buf), filename); + if (!ofp) { + m_fclose(&ifp); + mutt_error (_("Unable to open %s for writing"), tmpf); + return -1; + } + + if (section) { + while (r != EOF && !done && fgets (buf, sizeof (buf), ifp)) { + r = fputs (buf, ofp); + c = buf; + while (*c && *c != '\n') c++; + c[0] = 0; /* strip EOL */ + if (!strncmp (buf, "#: ", 3) && !m_strcasecmp(buf+3, section)) + done++; + } + if (r != EOF && !done) { + snprintf (buf, sizeof(buf), "#: %s\n", section); + r = fputs (buf, ofp); + } + done = 0; + } + + while (r != EOF && fgets (buf, sizeof (buf), ifp)) { + if (ext) { + c = buf; + while (*c && (*c != '\r') && (*c != '\n')) c++; + c--; + if (*c != '\\') ext = 0; + } else if ((section && !strncmp (buf, "#: ", 3))) { + if (!done && line) { + fputs (line, ofp); + fputc ('\n', ofp); + } + r = fputs (buf, ofp); + done++; + break; + } else if (key && !strncmp (buf, key, strlen(key)) && + (!*key || buf[strlen(key)] == ' ')) { + c = buf; + ext = 0; + while (*c && (*c != '\r') && (*c != '\n')) c++; + c--; + if (*c == '\\') ext = 1; + if (!done && line) { + r = fputs (line, ofp); + if (*key) + r = fputc ('\n', ofp); + done++; + } + } else { + r = fputs (buf, ofp); + } + } + + while (r != EOF && fgets (buf, sizeof (buf), ifp)) + r = fputs (buf, ofp); + + /* If there wasn't a line to replace, put it on the end of the file */ + if (r != EOF && !done && line) { + fputs (line, ofp); + r = fputc ('\n', ofp); + } + mx_unlock_file (filename, fileno (ifp), 0); + m_fclose(&ofp); + m_fclose(&ifp); + if (r == EOF) { + unlink (tmpf); + mutt_error (_("Can't write %s"), tmpf); + return -1; + } + lnk[0] = '\0'; + if ((l = readlink (filename, lnk, sizeof(lnk)-1)) > 0) + lnk[l] = '\0'; + if (rename (tmpf, l > 0 ? lnk : filename) < 0) { + unlink (tmpf); + mutt_error (_("Can't rename %s to %s"), tmpf, l > 0 ? lnk : filename); + return -1; + } + return 0; +} + +int mutt_newsrc_update (nntp_server_t * news) +{ + buffer_t buf; + nntp_data_t *data; + int r = -1; + + if (!news) + return -1; + + buffer_init(&buf); + + /* we will generate full newsrc here */ + for (data = news->list; data; data = data->next) { + if (!data || !data->rc) + continue; + + buffer_addstr(&buf, data->group); + buffer_addch(&buf, data->subscribed ? ':' : '!'); + + for (int x = 0; x < data->num; x++) { + buffer_addch(&buf, x ? ',' : ' '); + buffer_addf(&buf, "%d-%d", data->entries[x].first, + data->entries[x].last); + } + buffer_addch(&buf, '\n'); + } + + /* newrc being fully rewritten */ + if (news->newsrc + && (r = mutt_update_list_file(news->newsrc, NULL, "", buf.data)) == 0) + { + struct stat st; + + stat (news->newsrc, &st); + news->size = st.st_size; + news->mtime = st.st_mtime; + } + + buffer_wipe(&buf); + return r; +} + +static FILE *mutt_mkname (char *s) +{ + char buf[_POSIX_PATH_MAX], *pc; + int fd; + FILE *fp; + + nntp_cache_expand(buf, ssizeof(buf), "%s", s); + if ((fp = safe_fopen (buf, "w"))) + return fp; + + nntp_cache_expand(buf, ssizeof(buf), "cache-XXXXXX"); + pc = buf + m_strlen(buf) - 12; /* positioning to "cache-XXXXXX" */ + if ((fd = mkstemp (buf)) == -1) + return NULL; + strcpy (s, pc); /* generated name */ + return fdopen (fd, "w"); +} + +/* Updates info into .index file: ALL or about selected newsgroup */ +static int nntp_update_cacheindex (nntp_server_t * serv, nntp_data_t * data) +{ + char buf[LONG_STRING]; + char file[_POSIX_PATH_MAX]; + const char *key = "ALL"; + + if (!serv || !serv->conn || !serv->conn->account.host) + return -1; + + if (data && data->group) { + key = data->group; + snprintf(buf, sizeof(buf), "%s %s %d %d", key, data->cache, + data->firstMessage, data->lastLoaded); + } else { + m_strcpy(file, sizeof(file), serv->cache); + snprintf(buf, sizeof (buf), "ALL %s 0 %d", file, + (int)serv->newgroups_time); + } + nntp_cache_expand(file, ssizeof(file), "%s.index", serv->conn->account.host); + return mutt_update_list_file(file, serv->conn->account.host, key, buf); +} + +static void nntp_delete_cache (nntp_data_t * data) +{ + char buf[_POSIX_PATH_MAX]; + + if (!nntp.use_cache || !data || !data->cache || !data->nserv) + return; + + nntp_cache_expand(buf, ssizeof(buf), "%s", data->cache); + unlink (buf); + p_delete(&data->cache); + data->lastCached = 0; + nntp_cache_expand(buf, ssizeof(buf), "%s.index", + data->nserv->conn->account.host); + mutt_update_list_file(buf, data->nserv->conn->account.host, data->group, + NULL); +} + +static int nntp_save_cache_index (nntp_server_t * news) +{ + char buf[HUGE_STRING]; + char file[_POSIX_PATH_MAX]; + nntp_data_t *d; + FILE *f; + + if (!news || !news->newsgroups) + return -1; + if (!nntp.use_cache) + return 0; + + if (news->cache) { + nntp_cache_expand(file, ssizeof(file), "%s", news->cache); + unlink(file); + f = safe_fopen(file, "w"); + } else { + m_strcpy(buf, ssizeof(buf), news->conn->account.host); + f = mutt_mkname(buf); + news->cache = m_strdup(buf); + nntp_cache_expand(file, ssizeof(file), "%s", buf); + } + if (!f) + return -1; + + for (d = news->list; d; d = d->next) { + if (!d->deleted) { + if (d->desc) + snprintf (buf, sizeof (buf), "%s %d %d %c %s\n", d->group, + d->lastMessage, d->firstMessage, d->allowed ? 'y' : 'n', + d->desc); + else + snprintf (buf, sizeof (buf), "%s %d %d %c\n", d->group, + d->lastMessage, d->firstMessage, d->allowed ? 'y' : 'n'); + if (fputs (buf, f) == EOF) { + m_fclose(&f); + unlink (file); + return -1; + } + } + } + m_fclose(&f); + + if (nntp_update_cacheindex (news, NULL)) { + unlink (file); + return -1; + } + return 0; +} + +static int nntp_save_cache_group (CONTEXT * ctx) +{ + char buf[HUGE_STRING], addr[STRING]; + char file[_POSIX_PATH_MAX]; + FILE *f; + HEADER *h; + struct tm *tm; + int i = 0, save = SORT_ORDER; + int prev = 0; + + if (!nntp.use_cache) + return 0; + if (!ctx || !ctx->data || ctx->magic != M_NNTP) + return -1; + + if (((nntp_data_t *) ctx->data)->cache) { + nntp_cache_expand(file, ssizeof(file), "%s", + ((nntp_data_t *)ctx->data)->cache); + unlink (file); + f = safe_fopen (file, "w"); + } + else { + snprintf(buf, ssizeof(buf), "%s-%s", + ((nntp_data_t *)ctx->data)->nserv->conn->account.host, + ((nntp_data_t *)ctx->data)->group); + f = mutt_mkname (buf); + ((nntp_data_t *)ctx->data)->cache = m_strdup(buf); + nntp_cache_expand(file, ssizeof(file), "%s", buf); + } + if (!f) + return -1; + + if (Sort != SORT_ORDER) { + save = Sort; + Sort = SORT_ORDER; + mutt_sort_headers (ctx, 0); + } + + /* Save only $nntp_context messages... */ + ((nntp_data_t *) ctx->data)->lastCached = 0; + if (NntpContext && ctx->msgcount > NntpContext) + i = ctx->msgcount - NntpContext; + for (; i < ctx->msgcount; i++) { + if (!ctx->hdrs[i]->deleted && ctx->hdrs[i]->article_num != prev) { + h = ctx->hdrs[i]; + addr[0] = 0; + rfc822_addrcat(addr, sizeof(addr), h->env->from, 0); + tm = gmtime (&h->date_sent); + snprintf (buf, sizeof (buf), + "%d\t%s\t%s\t%d %s %d %02d:%02d:%02d GMT\t%s\t", + h->article_num, h->env->subject, addr, tm->tm_mday, + Months[tm->tm_mon], tm->tm_year + 1900, tm->tm_hour, + tm->tm_min, tm->tm_sec, h->env->message_id); + fputs (buf, f); + if (h->env->references) + mutt_write_references (h->env->references, f); + snprintf (buf, sizeof (buf), "\t%zd\t%d\tXref: %s\n", + h->content->length, h->lines, NONULL (h->env->xref)); + if (fputs (buf, f) == EOF) { + m_fclose(&f); + unlink (file); + return -1; + } + } + prev = ctx->hdrs[i]->article_num; + } + + if (save != Sort) { + Sort = save; + mutt_sort_headers (ctx, 0); + } + m_fclose(&f); + + if (nntp_update_cacheindex (((nntp_data_t *) ctx->data)->nserv, + (nntp_data_t *) ctx->data)) { + unlink (file); + return -1; + } + ((nntp_data_t *) ctx->data)->lastCached = + ((nntp_data_t *) ctx->data)->lastLoaded; + return 0; +} -#ifdef HAVE_PGP -#include "pgp.h" -#endif +nntp_data_t *mutt_newsgroup_subscribe (nntp_server_t * news, char *group) +{ + nntp_data_t *data; + + if (!news || !news->newsgroups || !group || !*group) + return NULL; + if (!(data = (nntp_data_t *) hash_find (news->newsgroups, group))) { + data = nntp_data_new(); + data->group = m_strdup(group); + data->nserv = news; + data->deleted = 1; + hash_insert (news->newsgroups, data->group, data); + news->tail = nntp_data_list_append(news->tail, data); + } + if (!data->subscribed) { + data->subscribed = 1; + data->rc = 1; + } + return data; +} -#ifdef HAVE_SMIME -#include "smime.h" -#endif +nntp_data_t *mutt_newsgroup_unsubscribe (nntp_server_t * news, char *group) +{ + nntp_data_t *data; -#include -#include -#include -#include + if (!news || !news->newsgroups || !group || !*group || + !(data = (nntp_data_t *) hash_find (news->newsgroups, group))) + return NULL; + if (data->subscribed) { + data->subscribed = 0; + } + return data; +} -static unsigned int _checked = 0; +nntp_data_t *mutt_newsgroup_catchup (nntp_server_t * news, char *group) +{ + nntp_data_t *data; + + if (!news || !news->newsgroups || !group || !*group || + !(data = (nntp_data_t *) hash_find (news->newsgroups, group))) + return NULL; + if (!data->max) { + data->entries = p_new(NEWSRC_ENTRY, 5); + data->max = 5; + } + data->num = 1; + data->entries[0].first = 1; + data->unread = 0; + data->entries[0].last = data->lastMessage; + if (Context && Context->data == data) { + int x; + + for (x = 0; x < Context->msgcount; x++) + mutt_set_flag (Context, Context->hdrs[x], M_READ, 1); + } + return data; +} -#ifdef DEBUG -static void nntp_error (const char *where, const char *msg) +nntp_data_t *mutt_newsgroup_uncatchup (nntp_server_t * news, char *group) { - dprint (1, (debugfile, "nntp_error(): unexpected response in %s: %s\n", where, msg)); + nntp_data_t *data; + + if (!news || !news->newsgroups || !group || !*group || + !(data = (nntp_data_t *) hash_find (news->newsgroups, group))) + return NULL; + if (!data->max) { + data->entries = p_new(NEWSRC_ENTRY, 5); + data->max = 5; + } + data->num = 1; + data->entries[0].first = 1; + data->entries[0].last = data->firstMessage - 1; + if (Context && Context->data == data) { + int x; + + data->unread = Context->msgcount; + for (x = 0; x < Context->msgcount; x++) + mutt_set_flag (Context, Context->hdrs[x], M_READ, 0); + } + else + data->unread = data->lastMessage - data->entries[0].last; + return data; +} + +/* this routine gives the first newsgroup with new messages */ +void nntp_buffy (char* dst, ssize_t dstlen) { + nntp_data_t *list; + int count = 0; + + /* forward to current group */ + for (list = CurrentNewsSrv->list; list; list = list->next) { + if (list->subscribed && list->unread + && Context && Context->magic == M_NNTP + && m_strcmp(list->group, ((nntp_data_t *)Context->data)->group) == 0) + { + list = list->next; + break; + } + } + + *dst = '\0'; + + while (count < 2) { + + if (!list) + list = CurrentNewsSrv->list; + + for (; list; list = list->next) { + if (list->subscribed && list->unread) { + if (Context && Context->magic == M_NNTP && + !m_strcmp(list->group, ((nntp_data_t *)Context->data)->group)) { + int i, unread = 0; + + for (i = 0; i < Context->msgcount; i++) + if (!Context->hdrs[i]->read && !Context->hdrs[i]->deleted) + unread++; + if (!unread) + continue; + } + m_strcpy(dst, dstlen, list->group); + break; + } + } + /* done if found */ + if (dst && *dst) + return; + count++; + } + *dst = '\0'; +} + +/* }}} */ +/* nntp protocol {{{ */ + +void nntp_sync_sidebar (nntp_data_t* data) { + int i = 0; + BUFFY* tmp = NULL; + char buf[STRING]; + + if (!Incoming.len) + return; + + snprintf(buf, sizeof (buf), "nntp%s://%s%s%s%s/%s", + data->nserv->conn->account.has_ssl ? "s" : "", + NONULL(data->nserv->conn->account.user), + *data->nserv->conn->account.pass ? ":" : "", + *data->nserv->conn->account.pass ? data->nserv->conn->account.pass : "", + data->nserv->conn->account.host, + data->group); + + /* bail out if group not found via mailboxes */ + if ((i = buffy_lookup (buf)) < 0) + return; + + tmp = Incoming.arr[i]; + /* copied from browser.c */ + if (option (OPTMARKOLD) && + data->lastCached >= data->firstMessage && + data->lastCached <= data->lastMessage) + tmp->msg_unread = data->lastMessage - data->lastCached; + else + tmp->msg_unread = data->unread; + tmp->new = data->unread > 0; + /* this is closest to a "total" count we can get */ + tmp->msgcount = data->lastMessage - data->firstMessage; } -#endif /* DEBUG */ -static int nntp_auth (NNTP_SERVER *serv) +static int nntp_auth (nntp_server_t * serv) { CONNECTION *conn = serv->conn; char buf[STRING]; unsigned char flags = conn->account.flags; - if (mutt_account_getuser (&conn->account) || !conn->account.user[0] || - mutt_account_getpass (&conn->account) || !conn->account.pass[0]) - { + if (mutt_account_getuser(&conn->account) || !conn->account.user[0] || + mutt_account_getpass(&conn->account) || !conn->account.pass[0]) { conn->account.flags = flags; return -2; } @@ -71,29 +1059,22 @@ static int nntp_auth (NNTP_SERVER *serv) snprintf (buf, sizeof (buf), "AUTHINFO USER %s\r\n", conn->account.user); mutt_socket_write (conn, buf); - if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) - { + if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) { conn->account.flags = flags; return -1; } -#ifdef DEBUG - /* don't print the password unless we're at the ungodly debugging level */ - if (debuglevel < M_SOCK_LOG_FULL) - dprint (M_SOCK_LOG_CMD, (debugfile, "> AUTHINFO PASS *\n")); -#endif snprintf (buf, sizeof (buf), "AUTHINFO PASS %s\r\n", conn->account.pass); - mutt_socket_write_d (conn, buf, M_SOCK_LOG_FULL); - if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) - { + mutt_socket_write(conn, buf); + if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) { conn->account.flags = flags; return -1; } - if (mutt_strncmp ("281", buf, 3)) - { + if (m_strncmp("281", buf, 3)) { conn->account.flags = flags; mutt_error _("Login failed."); + sleep (2); return -3; } @@ -101,16 +1082,17 @@ static int nntp_auth (NNTP_SERVER *serv) return 0; } -static int nntp_connect_error (NNTP_SERVER *serv) +static int nntp_connect_error (nntp_server_t * serv) { serv->status = NNTP_NONE; mutt_socket_close (serv->conn); mutt_error _("Server closed connection!"); + sleep (2); return -1; } -static int nntp_connect_and_auth (NNTP_SERVER *serv) +static int nntp_connect_and_auth (nntp_server_t * serv) { CONNECTION *conn = serv->conn; char buf[STRING]; @@ -124,23 +1106,20 @@ static int nntp_connect_and_auth (NNTP_SERVER *serv) if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) return nntp_connect_error (serv); - if (!mutt_strncmp ("200", buf, 3)) + if (!m_strncmp("200", buf, 3)) mutt_message (_("Connected to %s. Posting ok."), conn->account.host); - else if (!mutt_strncmp ("201", buf, 3)) + else if (!m_strncmp("201", buf, 3)) mutt_message (_("Connected to %s. Posting NOT ok."), conn->account.host); - else - { - mutt_socket_close (conn); - mutt_remove_trailing_ws (buf); - mutt_error ("%s", buf); + else { + mutt_socket_close(conn); + m_strrtrim(buf); + mutt_error("%s", buf); sleep (2); return -1; } - sleep (1); - /* Tell INN to switch to mode reader if it isn't so. Ignore all - returned codes and messages. */ + returned codes and messages. */ mutt_socket_write (conn, "MODE READER\r\n"); if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) return nntp_connect_error (serv); @@ -149,8 +1128,7 @@ static int nntp_connect_and_auth (NNTP_SERVER *serv) if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) return nntp_connect_error (serv); - if (!(conn->account.flags & M_ACCT_USER) && mutt_strncmp ("480", buf, 3)) - { + if (!conn->account.has_user && m_strncmp("480", buf, 3)) { serv->status = NNTP_OK; return 0; } @@ -158,16 +1136,15 @@ static int nntp_connect_and_auth (NNTP_SERVER *serv) rc = nntp_auth (serv); if (rc == -1) return nntp_connect_error (serv); - if (rc == -2) - { + if (rc == -2) { mutt_socket_close (conn); serv->status = NNTP_BYE; return -1; } - if (rc < 0) - { + if (rc < 0) { mutt_socket_close (conn); mutt_error _("Login failed."); + sleep (2); return -1; } @@ -175,48 +1152,50 @@ static int nntp_connect_and_auth (NNTP_SERVER *serv) return 0; } -static int nntp_attempt_features (NNTP_SERVER *serv) +static int nntp_attempt_features (nntp_server_t * serv) { char buf[LONG_STRING]; CONNECTION *conn = serv->conn; - mutt_socket_write (conn, "LISTGROUP\r\n"); - if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) - return (nntp_connect_error (serv)); - if (mutt_strncmp ("500", buf, 3)) - serv->hasLISTGROUP = 1; + if (serv->feat_known) + return 0; mutt_socket_write (conn, "XOVER\r\n"); if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) return nntp_connect_error (serv); - if (mutt_strncmp ("500", buf, 3)) + if (m_strncmp("500", buf, 3)) serv->hasXOVER = 1; mutt_socket_write (conn, "XPAT\r\n"); if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) return nntp_connect_error (serv); - if (mutt_strncmp ("500", buf, 3)) + if (m_strncmp("500", buf, 3)) serv->hasXPAT = 1; + mutt_socket_write (conn, "LISTGROUP\r\n"); + if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) + return (nntp_connect_error (serv)); + if (m_strncmp("500", buf, 3)) + serv->hasLISTGROUP = 1; + mutt_socket_write (conn, "XGTITLE +\r\n"); if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) return nntp_connect_error (serv); - if (mutt_strncmp ("500", buf, 3)) + if (m_strncmp("500", buf, 3)) serv->hasXGTITLE = 1; - if (!mutt_strncmp ("282", buf, 3)) - { - do - { + if (!m_strncmp("282", buf, 3)) { + do { if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) - return nntp_connect_error (serv); + return nntp_connect_error (serv); } while (!(buf[0] == '.' && buf[1] == '\0')); } + serv->feat_known = 1; return 0; } -static int nntp_open_connection (NNTP_SERVER *serv) +static int nntp_open_connection (nntp_server_t * serv) { if (serv->status == NNTP_OK) return 0; @@ -229,21 +1208,19 @@ static int nntp_open_connection (NNTP_SERVER *serv) return 0; } -static int nntp_reconnect (NNTP_SERVER *serv) +static int nntp_reconnect (nntp_server_t * serv) { - char buf[SHORT_STRING]; + char buf[STRING]; mutt_socket_close (serv->conn); - FOREVER - { + for (;;) { if (nntp_connect_and_auth (serv) == 0) return 0; snprintf (buf, sizeof (buf), _("Connection to %s lost. Reconnect?"), - serv->conn->account.host); - if (query_quadoption (OPT_NNTPRECONNECT, buf) != M_YES) - { + serv->conn->account.host); + if (query_quadoption (OPT_NNTPRECONNECT, buf) != M_YES) { serv->status = NNTP_BYE; return -1; } @@ -251,7 +1228,7 @@ static int nntp_reconnect (NNTP_SERVER *serv) } /* Send data from line[LONG_STRING] and receive answer to same line */ -static int mutt_nntp_query (NNTP_DATA *data, char *line, size_t linelen) +static int mutt_nntp_query (nntp_data_t * data, char *line, size_t linelen) { char buf[LONG_STRING]; int done = TRUE; @@ -259,39 +1236,34 @@ static int mutt_nntp_query (NNTP_DATA *data, char *line, size_t linelen) if (data->nserv->status == NNTP_BYE) return -1; - do - { - if (*line) - { + do { + if (*line) { mutt_socket_write (data->nserv->conn, line); } - else if (data->group) - { + else if (data->group) { snprintf (buf, sizeof (buf), "GROUP %s\r\n", data->group); mutt_socket_write (data->nserv->conn, buf); } done = TRUE; - if (mutt_socket_readln (buf, sizeof (buf), data->nserv->conn) < 0) - { + if (mutt_socket_readln (buf, sizeof (buf), data->nserv->conn) < 0) { if (nntp_reconnect (data->nserv) < 0) - return -1; + return -1; - if (data->group) - { - snprintf (buf, sizeof (buf), "GROUP %s\r\n", data->group); - mutt_socket_write (data->nserv->conn, buf); - if (mutt_socket_readln (buf, sizeof (buf), data->nserv->conn) < 0) - return -1; + if (data->group) { + snprintf (buf, sizeof (buf), "GROUP %s\r\n", data->group); + mutt_socket_write (data->nserv->conn, buf); + if (mutt_socket_readln (buf, sizeof (buf), data->nserv->conn) < 0) + return -1; } if (*line) - done = FALSE; + done = FALSE; } - else if ((!mutt_strncmp ("480", buf, 3)) && nntp_auth (data->nserv) < 0) + else if ((!m_strncmp("480", buf, 3)) && nntp_auth (data->nserv) < 0) return -1; } while (!done); - strfcpy (line, buf, linelen); + m_strcpy(line, linelen, buf); return 0; } @@ -305,19 +1277,21 @@ static int mutt_nntp_query (NNTP_DATA *data, char *line, size_t linelen) * -2 - invalid command or execution error, * -3 - error in funct(*line, *data). */ -static int mutt_nntp_fetch (NNTP_DATA *nntp_data, char *query, char *msg, - int (*funct) (char *, void *), void *data, int tagged) +static int mutt_nntp_fetch (nntp_data_t * nntp_data, const char *query, + const char *msg, progress_t* bar, + int (*funct) (char *, void *), + void *data, int tagged) { char buf[LONG_STRING]; char *inbuf, *p; int done = FALSE; int chunk, line; + long pos = 0; size_t lenbuf = 0; int ret; - do - { - strfcpy (buf, query, sizeof (buf)); + do { + m_strcpy(buf, sizeof(buf), query); if (mutt_nntp_query (nntp_data, buf, sizeof (buf)) < 0) return -1; if (buf[0] == '5') @@ -327,51 +1301,50 @@ static int mutt_nntp_fetch (NNTP_DATA *nntp_data, char *query, char *msg, ret = 0; line = 0; - inbuf = safe_malloc (sizeof (buf)); + inbuf = p_new(char, sizeof(buf)); - FOREVER - { - chunk = mutt_socket_readln_d (buf, sizeof (buf), nntp_data->nserv->conn, - M_SOCK_LOG_HDR); + for (;;) { + chunk = mutt_socket_readln(buf, sizeof (buf), nntp_data->nserv->conn); if (chunk < 0) - break; + break; p = buf; - if (!lenbuf && buf[0] == '.') - { - if (buf[1] == '\0') - { - done = TRUE; - break; - } - if (buf[1] == '.') - p++; + if (!lenbuf && buf[0] == '.') { + if (buf[1] == '\0') { + done = TRUE; + break; + } + if (buf[1] == '.') + p++; } - strfcpy (inbuf + lenbuf, p, sizeof (buf)); + m_strcpy(inbuf + lenbuf, sizeof(buf), p); + pos += chunk; - if (chunk >= sizeof (buf)) - { - lenbuf += strlen (p); + if (chunk >= ssizeof (buf)) { + lenbuf += m_strlen(p); } - else - { - line++; - if (msg && ReadInc && (line % ReadInc == 0)) { - if (tagged) - mutt_message (_("%s (tagged: %d) %d"), msg, tagged, line); - else - mutt_message ("%s %d", msg, line); - } - - if (ret == 0 && funct (inbuf, data) < 0) - ret = -3; - lenbuf = 0; + else { + if (bar) { + mutt_progress_bar (bar, pos); + } else if (msg) { + line++; + if (ReadInc && (line % ReadInc == 0)) { + if (tagged) + mutt_message (_("%s (tagged: %d) %d"), msg, tagged, line); + else + mutt_message ("%s %d", msg, line); + } + } + + if (ret == 0 && funct (inbuf, data) < 0) + ret = -3; + lenbuf = 0; } - safe_realloc (&inbuf, lenbuf + sizeof (buf)); + p_realloc(&inbuf, lenbuf + sizeof (buf)); } - FREE (&inbuf); + p_delete(&inbuf); funct (NULL, data); } while (!done); @@ -380,12 +1353,11 @@ static int mutt_nntp_fetch (NNTP_DATA *nntp_data, char *query, char *msg, static int nntp_read_tempfile (char *line, void *file) { - FILE *f = (FILE *)file; + FILE *f = (FILE *) file; if (!line) rewind (f); - else - { + else { fputs (line, f); if (fputc ('\n', f) == EOF) return -1; @@ -393,38 +1365,36 @@ static int nntp_read_tempfile (char *line, void *file) return 0; } -static void nntp_parse_xref (CONTEXT *ctx, char *group, char *xref, HEADER *h) +static void nntp_parse_xref (CONTEXT * ctx, char *group, char *xref, + HEADER * h) { register char *p, *b; register char *colon = NULL; b = p = xref; - while (*p) - { + while (*p) { /* skip to next word */ b = p; - while (*b && ((*b == ' ') || (*b == '\t'))) b++; + while (*b && ((*b == ' ') || (*b == '\t'))) + b++; p = b; colon = NULL; /* skip to end of word */ - while (*p && (*p != ' ') && (*p != '\t')) - { + while (*p && (*p != ' ') && (*p != '\t')) { if (*p == ':') - colon = p; + colon = p; p++; } - if (*p) - { + if (*p) { *p = '\0'; p++; } - if (colon) - { + if (colon) { *colon = '\0'; colon++; - nntp_get_status (ctx, h, p, atoi(colon)); - if (h && h->article_num == 0 && mutt_strcmp (group, b) == 0) - h->article_num = atoi(colon); + nntp_get_status (ctx, h, b, atoi (colon)); + if (h && h->article_num == 0 && m_strcmp(group, b) == 0) + h->article_num = atoi (colon); } } } @@ -435,17 +1405,18 @@ static void nntp_parse_xref (CONTEXT *ctx, char *group, char *xref, HEADER *h) * 1 if article not found * -1 if read or write error on tempfile or socket */ -static int nntp_read_header (CONTEXT *ctx, const char *msgid, int article_num) +static int nntp_read_header (CONTEXT * ctx, const char *msgid, + int article_num) { - NNTP_DATA *nntp_data = ((NNTP_DATA *)ctx->data); + nntp_data_t *nntp_data = ((nntp_data_t *) ctx->data); FILE *f; char buf[LONG_STRING]; char tempfile[_POSIX_PATH_MAX]; int ret; HEADER *h = ctx->hdrs[ctx->msgcount]; - mutt_mktemp (tempfile); - if (!(f = safe_fopen (tempfile, "w+"))) + f = m_tempfile(tempfile, sizeof(tempfile), NONULL(mod_core.tmpdir), NULL); + if (!f) return -1; if (!msgid) @@ -453,27 +1424,21 @@ static int nntp_read_header (CONTEXT *ctx, const char *msgid, int article_num) else snprintf (buf, sizeof (buf), "HEAD %s\r\n", msgid); - ret = mutt_nntp_fetch (nntp_data, buf, NULL, nntp_read_tempfile, f, 0); - if (ret) - { -#ifdef DEBUG - if (ret != -1) - dprint(1, (debugfile, "nntp_read_header: %s\n", buf)); -#endif - fclose (f); + ret = mutt_nntp_fetch (nntp_data, buf, NULL, NULL, nntp_read_tempfile, f, 0); + if (ret) { + m_fclose(&f); unlink (tempfile); return (ret == -1 ? -1 : 1); } h->article_num = article_num; h->env = mutt_read_rfc822_header (f, h, 0, 0); - fclose (f); + m_fclose(&f); unlink (tempfile); if (h->env->xref != NULL) nntp_parse_xref (ctx, nntp_data->group, h->env->xref, h); - else if (h->article_num == 0 && msgid) - { + else if (h->article_num == 0 && msgid) { snprintf (buf, sizeof (buf), "STAT %s\r\n", msgid); if (mutt_nntp_query (nntp_data, buf, sizeof (buf)) == 0) h->article_num = atoi (buf + 4); @@ -484,28 +1449,27 @@ static int nntp_read_header (CONTEXT *ctx, const char *msgid, int article_num) static int parse_description (char *line, void *n) { -#define news ((NNTP_SERVER *) n) register char *d = line; - NNTP_DATA *data; + nntp_data_t *data; + nntp_server_t *news = n; if (!line) return 0; - while (*d && *d != '\t' && *d != ' ') d++; - *d = 0; + while (*d && *d != '\t' && *d != ' ') + d++; + *d = 0; d++; - while (*d && (*d == '\t' || *d == ' ')) d++; - dprint (2, (debugfile, "group: %s, desc: %s\n", line, d)); - if ((data = (NNTP_DATA *) hash_find (news->newsgroups, line)) != NULL && - mutt_strcmp (d, data->desc)) - { - FREE (&data->desc); - data->desc = safe_strdup (d); + while (*d && (*d == '\t' || *d == ' ')) + d++; + if ((data = (nntp_data_t *) hash_find (news->newsgroups, line)) != NULL && + m_strcmp(d, data->desc)) { + p_delete(&data->desc); + data->desc = m_strdup(d); } return 0; -#undef news } -static void nntp_get_desc (NNTP_DATA *data, char *mask, char *msg) +static void nntp_get_desc (nntp_data_t * data, const char *mask, char *msg, progress_t* bar) { char buf[STRING]; @@ -517,12 +1481,7 @@ static void nntp_get_desc (NNTP_DATA *data, char *mask, char *msg) snprintf (buf, sizeof (buf), "XGTITLE %s\r\n", mask); else snprintf (buf, sizeof (buf), "LIST NEWSGROUPS %s\r\n", mask); - if (mutt_nntp_fetch (data, buf, msg, parse_description, data->nserv, 0) != 0) - { -#ifdef DEBUG - nntp_error ("nntp_get_desc()", buf); -#endif - } + mutt_nntp_fetch (data, buf, msg, bar, parse_description, data->nserv, 0); } /* @@ -533,82 +1492,77 @@ static void nntp_get_desc (NNTP_DATA *data, char *mask, char *msg) * mutt_read_rfc822_header(), since it replaces the call to that (albeit with * a limited number of headers which are "parsed" by placement in the list) */ -static int nntp_parse_xover (CONTEXT *ctx, char *buf, HEADER *hdr) +static int nntp_parse_xover (CONTEXT * ctx, char *buf, HEADER * hdr) { - NNTP_DATA *nntp_data = (NNTP_DATA *) ctx->data; + nntp_data_t *nntp_data = (nntp_data_t *) ctx->data; char *p, *b; int x, done = 0; - hdr->env = mutt_new_envelope(); - hdr->env->newsgroups = safe_strdup (nntp_data->group); - hdr->content = mutt_new_body(); + hdr->env = envelope_new(); + hdr->env->newsgroups = m_strdup(nntp_data->group); + hdr->content = body_new(); hdr->content->type = TYPETEXT; - hdr->content->subtype = safe_strdup ("plain"); + hdr->content->subtype = m_strdup("plain"); hdr->content->encoding = ENC7BIT; hdr->content->disposition = DISPINLINE; hdr->content->length = -1; b = p = buf; - for (x = 0; !done && x < 9; x++) - { + for (x = 0; !done && x < 9; x++) { /* if from file, need to skip newline character */ - while (*p && *p != '\n' && *p != '\t') p++; - if (!*p) done++; + while (*p && *p != '\n' && *p != '\t') + p++; + if (!*p) + done++; *p = '\0'; p++; - switch (x) - { - case 0: - - hdr->article_num = atoi (b); - nntp_get_status (ctx, hdr, NULL, hdr->article_num); - break; - case 1: - hdr->env->subject = safe_strdup (b); - /* Now we need to do the things which would normally be done in - * mutt_read_rfc822_header() */ - if (hdr->env->subject) - { - regmatch_t pmatch[1]; - - rfc2047_decode (&hdr->env->subject); - - if (regexec (ReplyRegexp.rx, hdr->env->subject, 1, pmatch, 0) == 0) - hdr->env->real_subj = hdr->env->subject + pmatch[0].rm_eo; - else - hdr->env->real_subj = hdr->env->subject; - } - break; - case 2: - rfc822_free_address (&hdr->env->from); - hdr->env->from = rfc822_parse_adrlist (hdr->env->from, b); - rfc2047_decode_adrlist (hdr->env->from); - break; - case 3: - hdr->date_sent = mutt_parse_date (b, hdr); - hdr->received = hdr->date_sent; - break; - case 4: - FREE (&hdr->env->message_id); - hdr->env->message_id = safe_strdup (b); - break; - case 5: - mutt_free_list (&hdr->env->references); - hdr->env->references = mutt_parse_references (b, 0); - break; - case 6: - hdr->content->length = atoi (b); - break; - case 7: - hdr->lines = atoi (b); - break; - case 8: - if (!hdr->read) - FREE (&hdr->env->xref); - b = b + 6; /* skips the "Xref: " */ - hdr->env->xref = safe_strdup (b); - nntp_parse_xref (ctx, nntp_data->group, b, hdr); + switch (x) { + case 0: + hdr->article_num = atoi (b); + nntp_get_status (ctx, hdr, NULL, hdr->article_num); + break; + case 1: + hdr->env->subject = m_strdup(b); + break; + case 2: + address_list_wipe(&hdr->env->from); + hdr->env->from = rfc822_parse_adrlist (hdr->env->from, b); + /* same as for mutt_parse_rfc822_line(): + * don't leave from info NULL if there's an invalid address (or + * whatever) in From: field; mutt would just display it as empty + * and mark mail/(esp.) news article as your own. aaargh! this + * bothered me for _years_ */ + if (!hdr->env->from) { + hdr->env->from = address_new (); + hdr->env->from->personal = m_strdup(b); + } + break; + case 3: + hdr->date_sent = mutt_parse_date (b, hdr); + hdr->received = hdr->date_sent; + break; + case 4: + p_delete(&hdr->env->message_id); + hdr->env->message_id = m_strdup(b); + break; + case 5: + string_list_wipe(&hdr->env->references); + hdr->env->references = mutt_parse_references (b, 0); + break; + case 6: + hdr->content->length = atoi (b); + break; + case 7: + hdr->lines = atoi (b); + break; + case 8: + if (!hdr->read) + p_delete(&hdr->env->xref); + b = b + 6; /* skips the "Xref: " */ + hdr->env->xref = m_strdup(b); + nntp_parse_xref (ctx, nntp_data->group, b, hdr); } + rfc2047_decode_envelope(hdr->env); if (!*p) return -1; b = p; @@ -616,49 +1570,47 @@ static int nntp_parse_xover (CONTEXT *ctx, char *buf, HEADER *hdr) return 0; } -typedef struct -{ +typedef struct { CONTEXT *ctx; - unsigned int base; - unsigned int first; - unsigned int last; - unsigned short *messages; - char* msg; + int first; + int last; + bits_t messages; + const char *msg; } FETCH_CONTEXT; -#define fc ((FETCH_CONTEXT *) c) -static int _nntp_fetch_numbers (unsigned int num ,void* c) { - if (num < fc->base || num > fc->last) - return 0; - fc->messages[num - fc->base] = 1; - return 0; -} static int nntp_fetch_numbers (char *line, void *c) { - if (!line) + FETCH_CONTEXT *fc = c; + + if (line) { + int num = atoi(line); + if (num < fc->first || num > fc->last) + return 0; + + bit_set(&fc->messages, num); + } return 0; - return (_nntp_fetch_numbers ((unsigned int) atoi (line), c)); } static int add_xover_line (char *line, void *c) { - unsigned int num, total; + int num, total; + FETCH_CONTEXT *fc = c; CONTEXT *ctx = fc->ctx; - NNTP_DATA *data = (NNTP_DATA *)ctx->data; + nntp_data_t *data = (nntp_data_t *) ctx->data; if (!line) return 0; if (ctx->msgcount >= ctx->hdrmax) mx_alloc_memory (ctx); - ctx->hdrs[ctx->msgcount] = mutt_new_header (); + ctx->hdrs[ctx->msgcount] = header_new(); ctx->hdrs[ctx->msgcount]->index = ctx->msgcount; nntp_parse_xover (ctx, line, ctx->hdrs[ctx->msgcount]); num = ctx->hdrs[ctx->msgcount]->article_num; - if (num >= fc->first && num <= fc->last && fc->messages[num - fc->base]) - { + if (num >= fc->first && num <= fc->last) { ctx->msgcount++; if (num > data->lastLoaded) data->lastLoaded = num; @@ -666,24 +1618,21 @@ static int add_xover_line (char *line, void *c) total = fc->last - fc->first + 1; if (!ctx->quiet && fc->msg && ReadInc && (num % ReadInc == 0)) mutt_message ("%s %d/%d", fc->msg, num, total); + } else { + header_delete(&ctx->hdrs[ctx->msgcount]); /* skip it */ } - else - mutt_free_header (&ctx->hdrs[ctx->msgcount]); /* skip it */ return 0; } -#undef fc -static int nntp_fetch_headers (CONTEXT *ctx, unsigned int first, - unsigned int last) + +static int nntp_fetch_headers(CONTEXT * ctx, int first, int last) { char buf[HUGE_STRING]; - char *msg = _("Fetching message headers..."); - NNTP_DATA *nntp_data = ((NNTP_DATA *)ctx->data); - int ret; - int num; - int oldmsgcount; - unsigned int current; + const char *msg = _("Fetching message headers..."); + const char *msg2 = _("Fetching headers from cache..."); + nntp_data_t *nntp_data = ((nntp_data_t *) ctx->data); + int ret, num, oldmsgcount, current; FILE *f; FETCH_CONTEXT fc; @@ -693,223 +1642,202 @@ static int nntp_fetch_headers (CONTEXT *ctx, unsigned int first, /* fetch list of articles */ mutt_message _("Fetching list of articles..."); - fc.ctx = ctx; - fc.base = first; - fc.last = last; - fc.messages = safe_calloc (last - first + 1, sizeof (unsigned short)); - if (nntp_data->nserv->hasLISTGROUP) { - snprintf (buf, sizeof (buf), "LISTGROUP %s\r\n", nntp_data->group); - if (mutt_nntp_fetch (nntp_data, buf, NULL, nntp_fetch_numbers, &fc, 0) != 0) - { - mutt_error (_("LISTGROUP command failed: %s"), buf); - sleep (2); -#ifdef DEBUG - nntp_error ("nntp_fetch_headers()", buf); -#endif - FREE (&fc.messages); - return -1; - } - } else { - /* mutt_nntp_query() issues a 'GROUP nntp_data->group' - * command on its own if !*buf */ - buf[0] = '\0'; - mutt_nntp_query (nntp_data, buf, sizeof (buf)); - if (sscanf (buf + 4, "%d %u %u %s", &num, &fc.first, &fc.last, buf) != 4) { - mutt_error (_("GROUP command failed: %s"), buf); - FREE (&fc.messages); - return (-1); - } else { - for (num = fc.first; num < fc.last; num++) - _nntp_fetch_numbers (num, &fc); - } - } + + fc.ctx = ctx; + fc.first = first; + fc.last = last; /* CACHE: must be loaded xover cache here */ num = nntp_data->lastCached - first + 1; - if (option (OPTNEWSCACHE) && nntp_data->cache && num > 0) - { - nntp_cache_expand (buf, nntp_data->cache); - mutt_message _("Fetching headers from cache..."); - if ((f = safe_fopen (buf, "r"))) - { - int r = 0; - - /* counting number of lines */ - while (fgets (buf, sizeof (buf), f) != NULL) - r++; - rewind (f); - while (r > num && fgets (buf, sizeof (buf), f) != NULL) - r--; - oldmsgcount = ctx->msgcount; - fc.first = first; - fc.last = first + num - 1; - fc.msg = NULL; - while (fgets (buf, sizeof (buf), f) != NULL) - add_xover_line (buf, &fc); - fclose (f); - nntp_data->lastLoaded = fc.last; - first = fc.last + 1; - if (ctx->msgcount > oldmsgcount) - mx_update_context (ctx, ctx->msgcount - oldmsgcount); + if (nntp.use_cache && nntp_data->cache && num > 0) { + nntp_cache_expand(buf, ssizeof(buf), "%s", nntp_data->cache); + mutt_message (msg2); + + if ((f = safe_fopen (buf, "r"))) { + int r = 0, c = 0; + + /* counting number of lines */ + while (fgets (buf, sizeof (buf), f) != NULL) + r++; + rewind (f); + while (r > num && fgets (buf, sizeof (buf), f) != NULL) + r--; + oldmsgcount = ctx->msgcount; + fc.first = first; + fc.last = first + num - 1; + fc.msg = NULL; + while (fgets (buf, sizeof (buf), f) != NULL) { + if (ReadInc && ((++c) % ReadInc == 0)) + mutt_message ("%s %d/%d", msg2, c, r); + add_xover_line (buf, &fc); } - else - nntp_delete_cache (nntp_data); + m_fclose(&f); + nntp_data->lastLoaded = fc.last; + first = fc.last + 1; + if (ctx->msgcount > oldmsgcount) + mx_update_context (ctx, ctx->msgcount - oldmsgcount); + } + else + nntp_delete_cache (nntp_data); } num = last - first + 1; - if (num <= 0) - { - FREE (&fc.messages); + if (num <= 0) { return 0; } /* - * Without XOVER, we have to fetch each article header and parse - * it. With XOVER, we ask for all of them - */ + * Without XOVER, we have to fetch each article header and parse + * it. With XOVER, we ask for all of them + */ mutt_message (msg); - if (nntp_data->nserv->hasXOVER) - { + if (nntp_data->nserv->hasXOVER) { oldmsgcount = ctx->msgcount; fc.first = first; fc.last = last; fc.msg = msg; snprintf (buf, sizeof (buf), "XOVER %d-%d\r\n", first, last); - ret = mutt_nntp_fetch (nntp_data, buf, NULL, add_xover_line, &fc, 0); + ret = mutt_nntp_fetch (nntp_data, buf, NULL, NULL, add_xover_line, &fc, 0); if (ctx->msgcount > oldmsgcount) mx_update_context (ctx, ctx->msgcount - oldmsgcount); - if (ret != 0) - { + if (ret != 0) { mutt_error (_("XOVER command failed: %s"), buf); -#ifdef DEBUG - nntp_error ("nntp_fetch_headers()", buf); -#endif - FREE (&fc.messages); return -1; } - /* fetched OK */ - } - else - for (current = first; current <= last; current++) - { - HEADER *h; + } else { + bits_init(&fc.messages); - ret = current - first + 1; - mutt_message ("%s %d/%d", msg, ret, num); + if (nntp_data->nserv->hasLISTGROUP) { + snprintf (buf, sizeof (buf), "LISTGROUP %s\r\n", nntp_data->group); + if (mutt_nntp_fetch(nntp_data, buf, NULL, NULL, + nntp_fetch_numbers, &fc, 0)) + { + mutt_error (_("LISTGROUP command failed: %s"), buf); + sleep (2); + bits_wipe(&fc.messages); + return -1; + } + } else { + for (num = first; num <= last; num++) + bit_set(&fc.messages, num); + } - if (!fc.messages[current - fc.base]) - continue; + for (current = first; current <= last; current++) { + HEADER *h; - if (ctx->msgcount >= ctx->hdrmax) - mx_alloc_memory (ctx); - h = ctx->hdrs[ctx->msgcount] = mutt_new_header (); - h->index = ctx->msgcount; + ret = current - first + 1; + mutt_message ("%s %d/%d", msg, ret, num); - ret = nntp_read_header (ctx, NULL, current); - if (ret == 0) /* Got article. Fetch next header */ - { - nntp_get_status (ctx, h, NULL, h->article_num); - ctx->msgcount++; - mx_update_context (ctx, 1); - } - else - mutt_free_header (&h); /* skip it */ - if (ret == -1) - { - FREE (&fc.messages); - return -1; - } + if (!bit_isset(&fc.messages, current)) + continue; + + if (ctx->msgcount >= ctx->hdrmax) + mx_alloc_memory (ctx); + h = ctx->hdrs[ctx->msgcount] = header_new(); + h->index = ctx->msgcount; + + ret = nntp_read_header (ctx, NULL, current); + if (ret == 0) { /* Got article. Fetch next header */ + nntp_get_status (ctx, h, NULL, h->article_num); + ctx->msgcount++; + mx_update_context (ctx, 1); + } + else + header_delete(&h); /* skip it */ + if (ret == -1) { + bits_wipe(&fc.messages); + return -1; + } - if (current > nntp_data->lastLoaded) - nntp_data->lastLoaded = current; + if (current > nntp_data->lastLoaded) + nntp_data->lastLoaded = current; + } + bits_wipe(&fc.messages); } - FREE (&fc.messages); + nntp_data->lastLoaded = last; mutt_clear_error (); return 0; } -/* +/* * currently, nntp "mailbox" is "newsgroup" */ -int nntp_open_mailbox (CONTEXT *ctx) +static int nntp_open_mailbox (CONTEXT * ctx) { - NNTP_DATA *nntp_data; - NNTP_SERVER *serv; + nntp_data_t *nntp_data; + nntp_server_t *serv; char buf[HUGE_STRING]; char server[LONG_STRING]; - int count = 0; - unsigned int first; - ACCOUNT acct; + int count = 0, first; + ACCOUNT act; - if (nntp_parse_url (ctx->path, &acct, buf, sizeof (buf)) < 0 || !*buf) - { + p_clear(&act, 1); + + if (nntp_parse_url (ctx->path, &act, buf, sizeof (buf)) < 0 || !*buf) { mutt_error (_("%s is an invalid newsgroup specification!"), ctx->path); mutt_sleep (2); return -1; } server[0] = '\0'; - nntp_expand_path (server, sizeof (server), &acct); + nntp_expand_path (server, sizeof (server), &act); if (!(serv = mutt_select_newsserver (server)) || serv->status != NNTP_OK) return -1; CurrentNewsSrv = serv; /* create NNTP-specific state struct if nof found in list */ - if ((nntp_data = (NNTP_DATA *) hash_find (serv->newsgroups, buf)) == NULL) - { - nntp_data = safe_calloc (1, sizeof (NNTP_DATA) + strlen (buf) + 1); - nntp_data->group = (char *) nntp_data + sizeof (NNTP_DATA); - strcpy (nntp_data->group, buf); - hash_insert (serv->newsgroups, nntp_data->group, nntp_data, 0); - nntp_add_to_list (serv, nntp_data); + if ((nntp_data = hash_find(serv->newsgroups, buf)) == NULL) { + nntp_data = nntp_data_new(); + nntp_data->group = m_strdup(buf); + hash_insert(serv->newsgroups, nntp_data->group, nntp_data); + serv->tail = nntp_data_list_append(serv->tail, nntp_data); } ctx->data = nntp_data; nntp_data->nserv = serv; mutt_message (_("Selecting %s..."), nntp_data->group); - if (!nntp_data->desc) - { - nntp_get_desc (nntp_data, nntp_data->group, NULL); + if (!nntp_data->desc) { + nntp_get_desc (nntp_data, nntp_data->group, NULL, NULL); if (nntp_data->desc) nntp_save_cache_index (serv); } buf[0] = 0; - if (mutt_nntp_query (nntp_data, buf, sizeof(buf)) < 0) - { -#ifdef DEBUG - nntp_error ("nntp_open_mailbox()", buf); -#endif + if (mutt_nntp_query (nntp_data, buf, sizeof (buf)) < 0) { return -1; } - if (mutt_strncmp ("211", buf, 3)) - { - LIST *l = serv->list; + if (m_strncmp("211", buf, 3)) { + nntp_data_t **l; /* GROUP command failed */ - if (!mutt_strncmp ("411", buf, 3)) - { + if (!m_strncmp("411", buf, 3)) { mutt_error (_("Newsgroup %s not found on server %s"), - nntp_data->group, serv->conn->account.host); + nntp_data->group, serv->conn->account.host); /* CACHE: delete cache and line from .index */ nntp_delete_cache (nntp_data); - hash_delete (serv->newsgroups, nntp_data->group, NULL, nntp_delete_data); - while (l && l->data != (void *) nntp_data) l = l->next; - if (l) - l->data = NULL; - + hash_remove(serv->newsgroups, nntp_data->group, NULL, NULL); + for (l = &serv->list; *l; l = &(*l)->next) { + if ((*l) == nntp_data) { + nntp_data_list_pop(l); + nntp_data_delete(&nntp_data); + if (!*l) + serv->tail = l; + break; + } + } + nntp_data_delete(&nntp_data); sleep (2); } return -1; } - sscanf (buf + 4, "%d %u %u %s", &count, &nntp_data->firstMessage, - &nntp_data->lastMessage, buf); + sscanf (buf + 4, "%d %u %u %s", &count, &nntp_data->firstMessage, + &nntp_data->lastMessage, buf); nntp_data->deleted = 0; @@ -927,16 +1855,18 @@ int nntp_open_mailbox (CONTEXT *ctx) return nntp_fetch_headers (ctx, first, nntp_data->lastMessage); } -int nntp_fetch_message (MESSAGE *msg, CONTEXT *ctx, int msgno) +int nntp_fetch_message (MESSAGE * msg, CONTEXT * ctx, int msgno) { char buf[LONG_STRING]; char path[_POSIX_PATH_MAX]; NNTP_CACHE *cache; - char *m = _("Fetching message..."); int ret; + progress_t bar; /* see if we already have the message in our cache */ - cache = &((NNTP_DATA *) ctx->data)->acache[ctx->hdrs[msgno]->index % NNTP_CACHE_LEN]; + cache = + &((nntp_data_t *) ctx->data)->acache[ctx->hdrs[msgno]->index % + NNTP_CACHE_LEN]; /* if everything is fine, assign msg->fp and return */ if (cache->path && cache->index == ctx->hdrs[msgno]->index && @@ -945,49 +1875,47 @@ int nntp_fetch_message (MESSAGE *msg, CONTEXT *ctx, int msgno) /* clear the previous entry */ unlink (cache->path); - free (cache->path); - - mutt_message (m); + p_delete(&cache->path); cache->index = ctx->hdrs[msgno]->index; - mutt_mktemp (path); - cache->path = safe_strdup (path); - if (!(msg->fp = safe_fopen (path, "w+"))) - { - FREE (&cache->path); + msg->fp = m_tempfile(path, sizeof(path), NONULL(mod_core.tmpdir), NULL); + if (!msg->fp) { return -1; } + cache->path = m_strdup(path); if (ctx->hdrs[msgno]->article_num == 0) snprintf (buf, sizeof (buf), "ARTICLE %s\r\n", - ctx->hdrs[msgno]->env->message_id); + ctx->hdrs[msgno]->env->message_id); else snprintf (buf, sizeof (buf), "ARTICLE %d\r\n", - ctx->hdrs[msgno]->article_num); + ctx->hdrs[msgno]->article_num); + + bar.msg = _("Fetching message..."); + bar.size = 0; + mutt_progress_bar (&bar, 0); - ret = mutt_nntp_fetch ((NNTP_DATA *)ctx->data, buf, m, nntp_read_tempfile, - msg->fp, ctx->tagged); - if (ret == 1) - { - mutt_error (_("Article %d not found on server"), - ctx->hdrs[msgno]->article_num); - dprint (1, (debugfile, "nntp_fetch_message: %s\n", buf)); + ret = mutt_nntp_fetch ((nntp_data_t *) ctx->data, buf, NULL, &bar, nntp_read_tempfile, + msg->fp, ctx->tagged); + if (ret == 1) { + mutt_error (_("Article %d not found on server"), + ctx->hdrs[msgno]->article_num); } - if (ret) - { - fclose (msg->fp); + if (ret) { + m_fclose(&msg->fp); unlink (path); - FREE (&cache->path); + p_delete(&cache->path); return -1; } - mutt_free_envelope (&ctx->hdrs[msgno]->env); - ctx->hdrs[msgno]->env = mutt_read_rfc822_header (msg->fp, ctx->hdrs[msgno], 0, 0); + envelope_delete(&ctx->hdrs[msgno]->env); + ctx->hdrs[msgno]->env = + mutt_read_rfc822_header (msg->fp, ctx->hdrs[msgno], 0, 0); /* fix content length */ - fseek(msg->fp, 0, SEEK_END); - ctx->hdrs[msgno]->content->length = ftell (msg->fp) - - ctx->hdrs[msgno]->content->offset; + fseeko (msg->fp, 0, SEEK_END); + ctx->hdrs[msgno]->content->length = ftello (msg->fp) - + ctx->hdrs[msgno]->content->offset; /* this is called in mutt before the open which fetches the message, * which is probably wrong, but we just call it again here to handle @@ -999,85 +1927,76 @@ int nntp_fetch_message (MESSAGE *msg, CONTEXT *ctx, int msgno) * full headers aren't parsed with XOVER, so the information wasn't * available then. */ -#if defined(HAVE_PGP) || defined(HAVE_SMIME) ctx->hdrs[msgno]->security = crypt_query (ctx->hdrs[msgno]->content); -#endif /* HAVE_PGP || HAVE_SMIME */ - mutt_clear_error(); + mutt_clear_error (); rewind (msg->fp); return 0; } /* Post article */ -int nntp_post (const char *msg) { +int nntp_post (const char *msg) +{ char buf[LONG_STRING]; size_t len; FILE *f; - NNTP_DATA *nntp_data; + nntp_data_t *nntp_data; if (Context && Context->magic == M_NNTP) - nntp_data = (NNTP_DATA *)Context->data; - else - { - if (!(CurrentNewsSrv = mutt_select_newsserver (NewsServer)) || - !CurrentNewsSrv->list || !CurrentNewsSrv->list->data) + nntp_data = (nntp_data_t *)Context->data; + else { + if (!(CurrentNewsSrv = mutt_select_newsserver(NewsServer)) || + !CurrentNewsSrv->list) { mutt_error (_("Can't post article. No connection to news server.")); return -1; } - nntp_data = (NNTP_DATA *)CurrentNewsSrv->list->data; + nntp_data = CurrentNewsSrv->list; } - if (!(f = safe_fopen (msg, "r"))) - { + if (!(f = safe_fopen (msg, "r"))) { mutt_error (_("Can't post article. Unable to open %s"), msg); return -1; } - strfcpy (buf, "POST\r\n", sizeof (buf)); - if (mutt_nntp_query (nntp_data, buf, sizeof (buf)) < 0) - { + m_strcpy(buf, sizeof(buf), "POST\r\n"); + if (mutt_nntp_query (nntp_data, buf, sizeof (buf)) < 0) { mutt_error (_("Can't post article. Connection to %s lost."), - nntp_data->nserv->conn->account.host); + nntp_data->nserv->conn->account.host); return -1; } - if (buf[0] != '3') - { + if (buf[0] != '3') { mutt_error (_("Can't post article: %s"), buf); return -1; } buf[0] = '.'; buf[1] = '\0'; - while (fgets (buf + 1, sizeof (buf) - 2, f) != NULL) - { - len = strlen (buf); - if (buf[len - 1] == '\n') - { + while (fgets (buf + 1, sizeof (buf) - 2, f) != NULL) { + len = m_strlen(buf); + if (buf[len - 1] == '\n') { buf[len - 1] = '\r'; buf[len] = '\n'; len++; buf[len] = '\0'; } if (buf[1] == '.') - mutt_socket_write_d (nntp_data->nserv->conn, buf, M_SOCK_LOG_HDR); + mutt_socket_write(nntp_data->nserv->conn, buf); else - mutt_socket_write_d (nntp_data->nserv->conn, buf + 1, M_SOCK_LOG_HDR); + mutt_socket_write(nntp_data->nserv->conn, buf + 1); } - fclose (f); + m_fclose(&f); - if (buf[strlen (buf) - 1] != '\n') - mutt_socket_write_d (nntp_data->nserv->conn, "\r\n", M_SOCK_LOG_HDR); - mutt_socket_write_d (nntp_data->nserv->conn, ".\r\n", M_SOCK_LOG_HDR); - if (mutt_socket_readln (buf, sizeof (buf), nntp_data->nserv->conn) < 0) - { + if (buf[m_strlen(buf) - 1] != '\n') + mutt_socket_write(nntp_data->nserv->conn, "\r\n"); + mutt_socket_write(nntp_data->nserv->conn, ".\r\n"); + if (mutt_socket_readln (buf, sizeof (buf), nntp_data->nserv->conn) < 0) { mutt_error (_("Can't post article. Connection to %s lost."), - nntp_data->nserv->conn->account.host); + nntp_data->nserv->conn->account.host); return -1; } - if (buf[0] != '2') - { + if (buf[0] != '2') { mutt_error (_("Can't post article: %s"), buf); return -1; } @@ -1089,73 +2008,59 @@ int nntp_post (const char *msg) { void nntp_logout_all (void) { char buf[LONG_STRING]; - CONNECTION* conn; - CONNECTION* tmp; + CONNECTION *conn; conn = mutt_socket_head (); - while (conn) - { - tmp = conn; - - if (conn->account.type == M_ACCT_TYPE_NNTP) - { + while (conn) { + CONNECTION* next = conn->next; + if (conn->account.type == M_ACCT_TYPE_NNTP) { mutt_message (_("Closing connection to %s..."), conn->account.host); mutt_socket_write (conn, "QUIT\r\n"); mutt_socket_readln (buf, sizeof (buf), conn); mutt_clear_error (); mutt_socket_close (conn); - - mutt_socket_free (tmp); + mutt_socket_free (conn); } - - conn = conn->next; + conn = next; } } -static void nntp_free_acache (NNTP_DATA *data) +static void nntp_free_acache (nntp_data_t * data) { int i; - for (i = 0; i < NNTP_CACHE_LEN; i++) - { - if (data->acache[i].path) - { + for (i = 0; i < NNTP_CACHE_LEN; i++) { + if (data->acache[i].path) { unlink (data->acache[i].path); - FREE (&data->acache[i].path); + p_delete(&data->acache[i].path); } } } -void nntp_delete_data (void *p) +void nntp_data_wipe(nntp_data_t *data) { - NNTP_DATA *data = (NNTP_DATA *)p; - - if (!p) - return; - FREE (&data->entries); - FREE (&data->desc); - FREE (&data->cache); - nntp_free_acache (data); - FREE (p); + p_delete(&data->entries); + p_delete(&data->desc); + p_delete(&data->cache); + p_delete(&data->group); + nntp_free_acache(data); } -int nntp_sync_mailbox (CONTEXT *ctx) +static int nntp_sync_mailbox (CONTEXT * ctx, int unused1, int* unused2) { - NNTP_DATA *data = ctx->data; + nntp_data_t *data = ctx->data; - /* CACHE: update cache and .index files */ - if ((option (OPTSAVEUNSUB) || data->subscribed)) - nntp_save_cache_group (ctx); - nntp_free_acache (data); + nntp_save_cache_group(ctx); + nntp_free_acache(data); - data->nserv->check_time = 0; /* next nntp_check_mailbox() will really check */ + data->nserv->check_time = 0; /* next nntp_check_mailbox() will really check */ return 0; } -void nntp_fastclose_mailbox (CONTEXT *ctx) +static void nntp_fastclose_mailbox (CONTEXT * ctx) { - NNTP_DATA *data = (NNTP_DATA *) ctx->data, *tmp; + nntp_data_t *data = (nntp_data_t *) ctx->data, *tmp; if (!data) return; @@ -1164,46 +2069,46 @@ void nntp_fastclose_mailbox (CONTEXT *ctx) return; nntp_save_cache_index (data->nserv); if ((tmp = hash_find (data->nserv->newsgroups, data->group)) == NULL - || tmp != data) - nntp_delete_data (data); + || tmp != data) + nntp_data_delete(&data); + else + nntp_sync_sidebar (data); } /* commit changes and terminate connection */ -int nntp_close_mailbox (CONTEXT *ctx) +int nntp_close_mailbox (CONTEXT * ctx) { if (!ctx) return -1; mutt_message _("Quitting newsgroup..."); - if (ctx->data) - { - NNTP_DATA *data = (NNTP_DATA *) ctx->data; + + if (ctx->data) { + nntp_data_t *data = (nntp_data_t *) ctx->data; int ret; - if (data->nserv && data->nserv->conn && ctx->unread) - { + if (data->nserv && data->nserv->conn && ctx->unread) { ret = query_quadoption (OPT_CATCHUP, _("Mark all articles read?")); if (ret == M_YES) - mutt_newsgroup_catchup (data->nserv, data->group); + mutt_newsgroup_catchup (data->nserv, data->group); else if (ret < 0) - return -1; + return -1; } } - nntp_sync_mailbox (ctx); - if (ctx->data && ((NNTP_DATA *)ctx->data)->nserv) - { - NNTP_SERVER *news; + nntp_sync_mailbox (ctx, 0, NULL); + if (ctx->data && ((nntp_data_t *) ctx->data)->nserv) { + nntp_server_t *news; - news = ((NNTP_DATA *)ctx->data)->nserv; + news = ((nntp_data_t *) ctx->data)->nserv; newsrc_gen_entries (ctx); - ((NNTP_DATA *)ctx->data)->unread = ctx->unread; + ((nntp_data_t *) ctx->data)->unread = ctx->unread; mutt_newsrc_update (news); } - mutt_clear_error(); + mutt_clear_error (); return 0; } /* use the GROUP command to poll for new mail */ -static int _nntp_check_mailbox (CONTEXT *ctx, NNTP_DATA *nntp_data) +static int _nntp_check_mailbox (CONTEXT * ctx, nntp_data_t * nntp_data) { char buf[LONG_STRING]; int count = 0; @@ -1212,34 +2117,23 @@ static int _nntp_check_mailbox (CONTEXT *ctx, NNTP_DATA *nntp_data) return 0; buf[0] = 0; - if (mutt_nntp_query (nntp_data, buf, sizeof (buf)) < 0) - { -#ifdef DEBUG - nntp_error ("nntp_check_mailbox()", buf); -#endif + if (mutt_nntp_query (nntp_data, buf, sizeof (buf)) < 0) { return -1; } - if (mutt_strncmp ("211", buf, 3)) - { + if (m_strncmp("211", buf, 3)) { buf[0] = 0; - if (mutt_nntp_query (nntp_data, buf, sizeof (buf)) < 0) - { -#ifdef DEBUG - nntp_error ("nntp_check_mailbox()", buf); -#endif + if (mutt_nntp_query (nntp_data, buf, sizeof (buf)) < 0) { return -1; } } - if (!mutt_strncmp ("211", buf, 3)) - { + if (!m_strncmp("211", buf, 3)) { int first; int last; sscanf (buf + 4, "%d %d %d", &count, &first, &last); nntp_data->firstMessage = first; nntp_data->lastMessage = last; - if (ctx && last > nntp_data->lastLoaded) - { + if (ctx && last > nntp_data->lastLoaded) { nntp_fetch_headers (ctx, nntp_data->lastLoaded + 1, last); time (&nntp_data->nserv->check_time); return 1; @@ -1249,89 +2143,33 @@ static int _nntp_check_mailbox (CONTEXT *ctx, NNTP_DATA *nntp_data) else mutt_newsgroup_stat (nntp_data); /* active was renumbered? */ - if (last < nntp_data->lastLoaded) - { - if (!nntp_data->max) - { - nntp_data->entries = safe_calloc (5, sizeof (NEWSRC_ENTRY)); - nntp_data->max = 5; + if (last < nntp_data->lastLoaded) { + if (!nntp_data->max) { + nntp_data->entries = p_new(NEWSRC_ENTRY, 5); + nntp_data->max = 5; } nntp_data->lastCached = 0; nntp_data->num = 1; nntp_data->entries[0].first = 1; nntp_data->entries[0].last = 0; } + nntp_sync_sidebar (nntp_data); } time (&nntp_data->nserv->check_time); return 0; } -int nntp_check_mailbox (CONTEXT *ctx) -{ - return _nntp_check_mailbox (ctx, (NNTP_DATA *)ctx->data); -} - -static int add_group (char *buf, void *serv) +static int nntp_check_mailbox (CONTEXT * ctx, int* unused1, int unused2) { -#define s ((NNTP_SERVER *) serv) - char group[LONG_STRING], mod, desc[HUGE_STRING]; - int first, last; - NNTP_DATA *nntp_data; - static int n = 0; - - _checked = n; /* _checked have N, where N = number of groups */ - if (!buf) /* at EOF must be zerouth */ - n = 0; - - if (!s || !buf) - return 0; - - *desc = 0; - sscanf (buf, "%s %d %d %c %[^\n]", group, &last, &first, &mod, desc); - if (!group) - return 0; - if ((nntp_data = (NNTP_DATA *) hash_find (s->newsgroups, group)) == NULL) - { - n++; - nntp_data = safe_calloc (1, sizeof (NNTP_DATA) + strlen (group) + 1); - nntp_data->group = (char *) nntp_data + sizeof (NNTP_DATA); - strcpy (nntp_data->group, group); - nntp_data->nserv = s; - if (s->newsgroups->nelem < s->newsgroups->curnelem * 2) - s->newsgroups = hash_resize (s->newsgroups, s->newsgroups->nelem * 2); - hash_insert (s->newsgroups, nntp_data->group, nntp_data, 0); - nntp_add_to_list (s, nntp_data); - } - nntp_data->deleted = 0; - nntp_data->firstMessage = first; - nntp_data->lastMessage = last; - if (mod == 'y') - nntp_data->allowed = 1; - else - nntp_data->allowed = 0; - if (nntp_data->desc) - FREE (&nntp_data->desc); - if (*desc) - nntp_data->desc = safe_strdup (desc); - if (nntp_data->rc || nntp_data->lastCached) - mutt_newsgroup_stat (nntp_data); - else if (nntp_data->lastMessage && - nntp_data->firstMessage <= nntp_data->lastMessage) - nntp_data->unread = nntp_data->lastMessage - nntp_data->firstMessage + 1; - else - nntp_data->unread = 0; - - return 0; -#undef s + return _nntp_check_mailbox (ctx, (nntp_data_t *) ctx->data); } -int nntp_check_newgroups (NNTP_SERVER *serv, int force) +static int nntp_check_newgroups (nntp_server_t * serv, int force) { char buf[LONG_STRING]; - NNTP_DATA nntp_data; - LIST *l; - LIST emp; + nntp_data_t nntp_data; + nntp_data_t *l, **emp; time_t now; struct tm *t; @@ -1342,140 +2180,98 @@ int nntp_check_newgroups (NNTP_SERVER *serv, int force) return -1; /* check subscribed groups for new news */ - if (option (OPTSHOWNEWNEWS)) - { + if (option (OPTSHOWNEWNEWS)) { mutt_message _("Checking for new messages..."); - for (l = serv->list; l; l = l->next) - { - serv->check_time = 0; /* really check! */ - if (l->data && ((NNTP_DATA *) l->data)->subscribed) - _nntp_check_mailbox (NULL, (NNTP_DATA *) l->data); + + for (l = serv->list; l; l = l->next) { + serv->check_time = 0; /* really check! */ + if (l->subscribed) + _nntp_check_mailbox (NULL, l); } + sidebar_draw (); } else if (!force) return 0; mutt_message _("Checking for new newsgroups..."); + now = serv->newgroups_time; time (&serv->newgroups_time); t = gmtime (&now); snprintf (buf, sizeof (buf), "NEWGROUPS %02d%02d%02d %02d%02d%02d GMT\r\n", - (t->tm_year % 100), t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, - t->tm_sec); + (t->tm_year % 100), t->tm_mon + 1, t->tm_mday, t->tm_hour, + t->tm_min, t->tm_sec); nntp_data.nserv = serv; if (Context && Context->magic == M_NNTP) - nntp_data.group = ((NNTP_DATA *)Context->data)->group; + nntp_data.group = ((nntp_data_t *) Context->data)->group; else nntp_data.group = NULL; - l = serv->tail; - if (mutt_nntp_fetch (&nntp_data, buf, _("Adding new newsgroups..."), - add_group, serv, 0) != 0) - { -#ifdef DEBUG - nntp_error ("nntp_check_newgroups()", buf); -#endif + + emp = nntp_data_list_last(&serv->list); + if (mutt_nntp_fetch (&nntp_data, buf, _("Adding new newsgroups..."), NULL, + add_group, serv, 0) != 0) { return -1; } mutt_message _("Loading descriptions..."); - if (l) - emp.next = l->next; - else - emp.next = serv->list; - l = &emp; - while (l->next) - { - l = l->next; - ((NNTP_DATA *) l->data)->new = 1; - nntp_get_desc ((NNTP_DATA *) l->data, ((NNTP_DATA *) l->data)->group, NULL); - } - if (emp.next) - nntp_save_cache_index (serv); - mutt_clear_error (); - return _checked; -} - -/* Load list of all newsgroups from cache ALL */ -int nntp_get_cache_all (NNTP_SERVER *serv) -{ - char buf[HUGE_STRING]; - FILE *f; - - nntp_cache_expand (buf, serv->cache); - if ((f = safe_fopen (buf, "r"))) - { - int i = 0; - while (fgets (buf, sizeof(buf), f) != NULL) - { - if (ReadInc && (i % ReadInc == 0)) - mutt_message (_("Loading list from cache... %d"), i); - add_group (buf, serv); - i++; - } - add_group (NULL, NULL); - fclose (f); - mutt_clear_error (); - return 0; - } - else - { - FREE (&serv->cache); - return -1; + for (l = *emp; l; l = l->next) { + l->new = 1; + nntp_get_desc(l, l->group, NULL, NULL); } + if (*emp) + nntp_save_cache_index(serv); + mutt_clear_error(); + return nntp.checked; } /* Load list of all newsgroups from active */ -int nntp_get_active (NNTP_SERVER *serv) +int nntp_get_active (nntp_server_t * serv) { - char msg[SHORT_STRING]; - NNTP_DATA nntp_data; - LIST *tmp; + char msg[STRING]; + nntp_data_t nntp_data, **tmp = &serv->list; if (nntp_open_connection (serv) < 0) return -1; - snprintf (msg, sizeof(msg), _("Loading list of all newsgroups on server %s..."), - serv->conn->account.host); + snprintf (msg, sizeof (msg), + _("Loading list of all newsgroups on server %s..."), + serv->conn->account.host); mutt_message (msg); time (&serv->newgroups_time); nntp_data.nserv = serv; nntp_data.group = NULL; - if (mutt_nntp_fetch (&nntp_data, "LIST\r\n", msg, add_group, serv, 0) < 0) - { -#ifdef DEBUG - nntp_error ("nntp_get_active()", "LIST\r\n"); -#endif + if (mutt_nntp_fetch (&nntp_data, "LIST\r\n", msg, NULL, add_group, serv, 0) < 0) { return -1; } - strfcpy (msg, _("Loading descriptions..."), sizeof (msg)); + m_strcpy(msg, sizeof(msg), _("Loading descriptions...")); mutt_message (msg); - nntp_get_desc (&nntp_data, "*", msg); - - for (tmp = serv->list; tmp; tmp = tmp->next) - { - NNTP_DATA *data = (NNTP_DATA *)tmp->data; - - if (data && data->deleted && !data->rc) - { - nntp_delete_cache (data); - hash_delete (serv->newsgroups, data->group, NULL, nntp_delete_data); - tmp->data = NULL; + nntp_get_desc (&nntp_data, "*", msg, NULL); + + while (*tmp) { + if ((*tmp)->deleted && !(*tmp)->rc) { + nntp_data_t *d = nntp_data_list_pop(tmp); + nntp_delete_cache(d); + hash_remove(serv->newsgroups, d->group, NULL, NULL); + nntp_data_delete(&d); + } else { + tmp = &(*tmp)->next; } } - nntp_save_cache_index (serv); + serv->tail = tmp; + nntp_save_cache_index(serv); mutt_clear_error (); - return _checked; + return nntp.checked; } /* * returns -1 if error ocurred while retrieving header, * number of articles which ones exist in context on success. */ -int nntp_check_msgid (CONTEXT *ctx, const char *msgid) +int nntp_check_msgid (CONTEXT * ctx, const char *msgid) { int ret; @@ -1484,17 +2280,16 @@ int nntp_check_msgid (CONTEXT *ctx, const char *msgid) return 1; if (ctx->msgcount == ctx->hdrmax) mx_alloc_memory (ctx); - ctx->hdrs[ctx->msgcount] = mutt_new_header (); + ctx->hdrs[ctx->msgcount] = header_new(); ctx->hdrs[ctx->msgcount]->index = ctx->msgcount; - + mutt_message (_("Fetching %s from server..."), msgid); ret = nntp_read_header (ctx, msgid, 0); /* since nntp_read_header() may set read flag, we must reset it */ ctx->hdrs[ctx->msgcount]->read = 0; if (ret != 0) - mutt_free_header (&ctx->hdrs[ctx->msgcount]); - else - { + header_delete(&ctx->hdrs[ctx->msgcount]); + else { ctx->msgcount++; mx_update_context (ctx, 1); ctx->changed = 1; @@ -1502,18 +2297,17 @@ int nntp_check_msgid (CONTEXT *ctx, const char *msgid) return ret; } -typedef struct -{ +typedef struct { CONTEXT *ctx; - unsigned int num; - unsigned int max; - unsigned int *child; + int num; + int max; + int *child; } CHILD_CONTEXT; static int check_children (char *s, void *c) { -#define cc ((CHILD_CONTEXT *) c) - unsigned int i, n; + CHILD_CONTEXT *cc = c; + int i, n; if (!s || (n = atoi (s)) == 0) return 0; @@ -1521,63 +2315,90 @@ static int check_children (char *s, void *c) if (cc->ctx->hdrs[i]->article_num == n) return 0; if (cc->num >= cc->max) - safe_realloc (&cc->child, sizeof (unsigned int) * (cc->max += 25)); + p_realloc(&cc->child, cc->max += 25); cc->child[cc->num++] = n; return 0; -#undef cc } -int nntp_check_children (CONTEXT *ctx, const char *msgid) +int nntp_check_children (CONTEXT * ctx, const char *msgid) { - NNTP_DATA *nntp_data = (NNTP_DATA *)ctx->data; + nntp_data_t *nntp_data = (nntp_data_t *) ctx->data; char buf[STRING]; int i, ret = 0, tmp = 0; - CHILD_CONTEXT cc; + CHILD_CONTEXT cc = { ctx, 0, 0, NULL }; if (!nntp_data || !nntp_data->nserv || !nntp_data->nserv->conn || - !nntp_data->nserv->conn->account.host) + !nntp_data->nserv->conn->account.host) return -1; if (nntp_data->firstMessage > nntp_data->lastLoaded) return 0; - if (!nntp_data->nserv->hasXPAT) - { + if (!nntp_data->nserv->hasXPAT) { mutt_error (_("Server %s does not support this operation!"), - nntp_data->nserv->conn->account.host); + nntp_data->nserv->conn->account.host); return -1; } - snprintf (buf, sizeof (buf), "XPAT References %d-%d *%s*\r\n", - nntp_data->firstMessage, nntp_data->lastLoaded, msgid); + snprintf (buf, sizeof (buf), "XPAT References %d-%d *%s*\r\n", + nntp_data->firstMessage, nntp_data->lastLoaded, msgid); - cc.ctx = ctx; - cc.num = 0; - cc.max = 25; - cc.child = safe_malloc (sizeof (unsigned int) * 25); - if (mutt_nntp_fetch (nntp_data, buf, NULL, check_children, &cc, 0)) - { - FREE (&cc.child); + if (mutt_nntp_fetch (nntp_data, buf, NULL, NULL, check_children, &cc, 0)) { + p_delete(&cc.child); return -1; } + /* dont try to read the xover cache. check_children() already * made sure that we dont have the article, so we need to visit * the server. Reading the cache at this point is also bad * because it would duplicate messages */ - if (option (OPTNEWSCACHE)) - { + if (nntp.use_cache) { tmp++; - unset_option (OPTNEWSCACHE); + nntp.use_cache = false; } - for (i = 0; i < cc.num; i++) - { + for (i = 0; i < cc.num; i++) { if ((ret = nntp_fetch_headers (ctx, cc.child[i], cc.child[i]))) break; if (ctx->msgcount && - ctx->hdrs[ctx->msgcount - 1]->article_num == cc.child[i]) + ctx->hdrs[ctx->msgcount - 1]->article_num == cc.child[i]) ctx->hdrs[ctx->msgcount - 1]->read = 0; } if (tmp) - set_option (OPTNEWSCACHE); - FREE (&cc.child); + nntp.use_cache = true; + p_delete(&cc.child); return ret; } + +static int nntp_is_magic (const char* path, struct stat* st) { + url_scheme_t s = url_check_scheme(NONULL(path)); + return s == U_NNTP || s == U_NNTPS ? M_NNTP : -1; +} + +static int acl_check_nntp (CONTEXT* ctx, int bit) { + switch (bit) { + case ACL_INSERT: /* editing messages */ + case ACL_WRITE: /* change importance */ + return (0); + case ACL_DELETE: /* (un)deletion */ + case ACL_SEEN: /* mark as read */ + return (1); + default: + return (0); + } +} + +mx_t const nntp_mx = { + M_NNTP, + 0, + nntp_is_magic, + NULL, + NULL, + nntp_open_mailbox, + NULL, + acl_check_nntp, + nntp_check_mailbox, + nntp_fastclose_mailbox, + nntp_sync_mailbox, + NULL, +}; + +/* }}} */