struct NntpServer *CurrentNewsSrv;
+const char *OverviewFmt = "Subject:\0"
+ "From:\0"
+ "Date:\0"
+ "Message-ID:\0"
+ "References:\0"
+ "Content-Length:\0"
+ "Lines:\0"
+ "\0";
+
+/**
+ * struct FetchCtx - Keep track when getting data from a server
+ */
+struct FetchCtx
+{
+ struct Context *ctx;
+ anum_t first;
+ anum_t last;
+ int restore;
+ unsigned char *messages;
+ struct Progress progress;
+#ifdef USE_HCACHE
+ header_cache_t *hc;
+#endif
+};
+
+/**
+ * struct ChildCtx - Keep track of the children of an article
+ */
+struct ChildCtx
+{
+ struct Mailbox *mailbox;
+ unsigned int num;
+ unsigned int max;
+ anum_t *child;
+};
+
/**
* nntp_connect_error - Signal a failed connection
* @param nserv NNTP server
return -1;
}
-const char *OverviewFmt = "Subject:\0"
- "From:\0"
- "Date:\0"
- "Message-ID:\0"
- "References:\0"
- "Content-Length:\0"
- "Lines:\0"
- "\0";
-
/**
* nntp_attempt_features - Detect supported commands
* @param nserv NNTP server
return -1;
}
-/**
- * nntp_open_connection - Connect to server, authenticate and get capabilities
- * @param nserv NNTP server
- * @retval 0 Success
- * @retval -1 Failure
- */
-int nntp_open_connection(struct NntpServer *nserv)
-{
- struct Connection *conn = nserv->conn;
- char buf[STRING];
- int cap;
- bool posting = false, auth = true;
-
- if (nserv->status == NNTP_OK)
- return 0;
- if (nserv->status == NNTP_BYE)
- return -1;
- nserv->status = NNTP_NONE;
-
- if (mutt_socket_open(conn) < 0)
- return -1;
-
- if (mutt_socket_readln(buf, sizeof(buf), conn) < 0)
- return nntp_connect_error(nserv);
-
- if (mutt_str_strncmp("200", buf, 3) == 0)
- posting = true;
- else if (mutt_str_strncmp("201", buf, 3) != 0)
- {
- mutt_socket_close(conn);
- mutt_str_remove_trailing_ws(buf);
- mutt_error("%s", buf);
- return -1;
- }
-
- /* get initial capabilities */
- cap = nntp_capabilities(nserv);
- if (cap < 0)
- return -1;
-
- /* tell news server to switch to mode reader if it isn't so */
- if (cap > 0)
- {
- if (mutt_socket_send(conn, "MODE READER\r\n") < 0 ||
- mutt_socket_readln(buf, sizeof(buf), conn) < 0)
- {
- return nntp_connect_error(nserv);
- }
-
- if (mutt_str_strncmp("200", buf, 3) == 0)
- posting = true;
- else if (mutt_str_strncmp("201", buf, 3) == 0)
- posting = false;
- /* error if has capabilities, ignore result if no capabilities */
- else if (nserv->hasCAPABILITIES)
- {
- mutt_socket_close(conn);
- mutt_error(_("Could not switch to reader mode"));
- return -1;
- }
-
- /* recheck capabilities after MODE READER */
- if (nserv->hasCAPABILITIES)
- {
- cap = nntp_capabilities(nserv);
- if (cap < 0)
- return -1;
- }
- }
-
- mutt_message(_("Connected to %s. %s"), conn->account.host,
- posting ? _("Posting is ok") : _("Posting is NOT ok"));
- mutt_sleep(1);
-
-#ifdef USE_SSL
- /* Attempt STARTTLS if available and desired. */
- if (nserv->use_tls != 1 && (nserv->hasSTARTTLS || SslForceTls))
- {
- if (nserv->use_tls == 0)
- {
- nserv->use_tls =
- SslForceTls || query_quadoption(SslStarttls,
- _("Secure connection with TLS?")) == MUTT_YES ?
- 2 :
- 1;
- }
- if (nserv->use_tls == 2)
- {
- if (mutt_socket_send(conn, "STARTTLS\r\n") < 0 ||
- mutt_socket_readln(buf, sizeof(buf), conn) < 0)
- {
- return nntp_connect_error(nserv);
- }
- if (mutt_str_strncmp("382", buf, 3) != 0)
- {
- nserv->use_tls = 0;
- mutt_error("STARTTLS: %s", buf);
- }
- else if (mutt_ssl_starttls(conn))
- {
- nserv->use_tls = 0;
- nserv->status = NNTP_NONE;
- mutt_socket_close(nserv->conn);
- mutt_error(_("Could not negotiate TLS connection"));
- return -1;
- }
- else
- {
- /* recheck capabilities after STARTTLS */
- cap = nntp_capabilities(nserv);
- if (cap < 0)
- return -1;
- }
- }
- }
-#endif
-
- /* authentication required? */
- if (conn->account.flags & MUTT_ACCT_USER)
- {
- if (!conn->account.user[0])
- auth = false;
- }
- else
- {
- if (mutt_socket_send(conn, "STAT\r\n") < 0 ||
- mutt_socket_readln(buf, sizeof(buf), conn) < 0)
- {
- return nntp_connect_error(nserv);
- }
- if (mutt_str_strncmp("480", buf, 3) != 0)
- auth = false;
- }
-
- /* authenticate */
- if (auth && nntp_auth(nserv) < 0)
- return -1;
-
- /* get final capabilities after authentication */
- if (nserv->hasCAPABILITIES && (auth || cap > 0))
- {
- cap = nntp_capabilities(nserv);
- if (cap < 0)
- return -1;
- if (cap > 0)
- {
- mutt_socket_close(conn);
- mutt_error(_("Could not switch to reader mode"));
- return -1;
- }
- }
-
- /* attempt features */
- if (nntp_attempt_features(nserv) < 0)
- return -1;
-
- nserv->status = NNTP_OK;
- return 0;
-}
-
/**
* nntp_query - Send data from buffer and receive answer to same buffer
* @param nntp_data NNTP server data
return 0;
}
-/**
- * struct FetchCtx - Keep track when getting data from a server
- */
-struct FetchCtx
-{
- struct Context *ctx;
- anum_t first;
- anum_t last;
- int restore;
- unsigned char *messages;
- struct Progress progress;
-#ifdef USE_HCACHE
- header_cache_t *hc;
-#endif
-};
-
/**
* fetch_numbers - Parse article number
* @param line Article number
}
/**
- * nntp_mbox_open - Implements MxOps::mbox_open()
+ * nntp_group_poll - Check newsgroup for new articles
+ * @param nntp_data NNTP server data
+ * @param update_stat Update the stats?
+ * @retval 1 New articles found
+ * @retval 0 No change
+ * @retval -1 Lost connection
*/
-static int nntp_mbox_open(struct Context *ctx)
+static int nntp_group_poll(struct NntpData *nntp_data, int update_stat)
{
- struct NntpServer *nserv = NULL;
- struct NntpData *nntp_data = NULL;
- char buf[HUGE_STRING];
- char server[LONG_STRING];
- char *group = NULL;
- int rc;
- void *hc = NULL;
- anum_t first, last, count = 0;
- struct Url url;
+ char buf[LONG_STRING] = "";
+ anum_t count, first, last;
- mutt_str_strfcpy(buf, ctx->mailbox->path, sizeof(buf));
- if (url_parse(&url, buf) < 0 || !url.host || !url.path ||
- !(url.scheme == U_NNTP || url.scheme == U_NNTPS))
- {
- url_free(&url);
- mutt_error(_("%s is an invalid newsgroup specification"), ctx->mailbox->path);
+ /* use GROUP command to poll newsgroup */
+ if (nntp_query(nntp_data, buf, sizeof(buf)) < 0)
return -1;
- }
+ if (sscanf(buf, "211 " ANUM " " ANUM " " ANUM, &count, &first, &last) != 3)
+ return 0;
+ if (first == nntp_data->first_message && last == nntp_data->last_message)
+ return 0;
- group = url.path;
- url.path = strchr(url.path, '\0');
- url_tostring(&url, server, sizeof(server), 0);
- nserv = nntp_select_server(ctx->mailbox, server, true);
- url_free(&url);
- if (!nserv)
- return -1;
- CurrentNewsSrv = nserv;
-
- /* find news group data structure */
- nntp_data = mutt_hash_find(nserv->groups_hash, group);
- if (!nntp_data)
+ /* articles have been renumbered */
+ if (last < nntp_data->last_message)
{
- nntp_newsrc_close(nserv);
- mutt_error(_("Newsgroup %s not found on the server"), group);
- return -1;
+ nntp_data->last_cached = 0;
+ if (nntp_data->newsrc_len)
+ {
+ mutt_mem_realloc(&nntp_data->newsrc_ent, sizeof(struct NewsrcEntry));
+ nntp_data->newsrc_len = 1;
+ nntp_data->newsrc_ent[0].first = 1;
+ nntp_data->newsrc_ent[0].last = 0;
+ }
}
+ nntp_data->first_message = first;
+ nntp_data->last_message = last;
+ if (!update_stat)
+ return 1;
- mutt_bit_unset(ctx->mailbox->rights, MUTT_ACL_INSERT);
- if (!nntp_data->newsrc_ent && !nntp_data->subscribed && !SaveUnsubscribed)
- ctx->mailbox->readonly = true;
+ /* update counters */
+ else if (!last || (!nntp_data->newsrc_ent && !nntp_data->last_cached))
+ nntp_data->unread = count;
+ else
+ nntp_group_unread_stat(nntp_data);
+ return 1;
+}
- /* select newsgroup */
- mutt_message(_("Selecting %s..."), group);
- buf[0] = '\0';
- if (nntp_query(nntp_data, buf, sizeof(buf)) < 0)
+/**
+ * check_mailbox - Check current newsgroup for new articles
+ * @param ctx Mailbox
+ * @retval #MUTT_REOPENED Articles have been renumbered or removed from server
+ * @retval #MUTT_NEW_MAIL New articles found
+ * @retval 0 No change
+ * @retval -1 Lost connection
+ *
+ * Leave newsrc locked
+ */
+static int check_mailbox(struct Context *ctx)
+{
+ struct NntpData *nntp_data = ctx->mailbox->data;
+ struct NntpServer *nserv = nntp_data->nserv;
+ time_t now = time(NULL);
+ int rc, ret = 0;
+ void *hc = NULL;
+
+ if (nserv->check_time + NntpPoll > now)
+ return 0;
+
+ mutt_message(_("Checking for new messages..."));
+ if (nntp_newsrc_parse(nserv) < 0)
+ return -1;
+
+ nserv->check_time = now;
+ rc = nntp_group_poll(nntp_data, 0);
+ if (rc < 0)
{
nntp_newsrc_close(nserv);
return -1;
}
+ if (rc)
+ nntp_active_save_cache(nserv);
- /* newsgroup not found, remove it */
- if (mutt_str_strncmp("411", buf, 3) == 0)
+ /* articles have been renumbered, remove all headers */
+ if (nntp_data->last_message < nntp_data->last_loaded)
{
- mutt_error(_("Newsgroup %s has been removed from the server"), nntp_data->group);
- if (!nntp_data->deleted)
- {
- nntp_data->deleted = true;
- nntp_active_save_cache(nserv);
- }
- if (nntp_data->newsrc_ent && !nntp_data->subscribed && !SaveUnsubscribed)
+ for (int i = 0; i < ctx->mailbox->msg_count; i++)
+ mutt_header_free(&ctx->mailbox->hdrs[i]);
+ ctx->mailbox->msg_count = 0;
+ ctx->tagged = 0;
+
+ if (nntp_data->last_message < nntp_data->last_loaded)
{
- FREE(&nntp_data->newsrc_ent);
- nntp_data->newsrc_len = 0;
- nntp_delete_group_cache(nntp_data);
- nntp_newsrc_update(nserv);
+ nntp_data->last_loaded = nntp_data->first_message - 1;
+ if (NntpContext && nntp_data->last_message - nntp_data->last_loaded > NntpContext)
+ nntp_data->last_loaded = nntp_data->last_message - NntpContext;
}
+ ret = MUTT_REOPENED;
}
- /* parse newsgroup info */
- else
+ /* .newsrc has been externally modified */
+ if (nserv->newsrc_modified)
{
- if (sscanf(buf, "211 " ANUM " " ANUM " " ANUM, &count, &first, &last) != 3)
+#ifdef USE_HCACHE
+ unsigned char *messages = NULL;
+ char buf[16];
+ void *hdata = NULL;
+ struct Header *hdr = NULL;
+ anum_t first = nntp_data->first_message;
+
+ if (NntpContext && nntp_data->last_message - first + 1 > NntpContext)
+ first = nntp_data->last_message - NntpContext + 1;
+ messages = mutt_mem_calloc(nntp_data->last_loaded - first + 1, sizeof(unsigned char));
+ hc = nntp_hcache_open(nntp_data);
+ nntp_hcache_update(nntp_data, hc);
+#endif
+
+ /* update flags according to .newsrc */
+ int j = 0;
+ anum_t anum;
+ for (int i = 0; i < ctx->mailbox->msg_count; i++)
{
- nntp_newsrc_close(nserv);
- mutt_error("GROUP: %s", buf);
- return -1;
+ bool flagged = false;
+ anum = NHDR(ctx->mailbox->hdrs[i])->article_num;
+
+#ifdef USE_HCACHE
+ /* check hcache for flagged and deleted flags */
+ if (hc)
+ {
+ if (anum >= first && anum <= nntp_data->last_loaded)
+ messages[anum - first] = 1;
+
+ snprintf(buf, sizeof(buf), "%u", anum);
+ hdata = mutt_hcache_fetch(hc, buf, strlen(buf));
+ if (hdata)
+ {
+ bool deleted;
+
+ mutt_debug(2, "#1 mutt_hcache_fetch %s\n", buf);
+ hdr = mutt_hcache_restore(hdata);
+ mutt_hcache_free(hc, &hdata);
+ hdr->data = NULL;
+ deleted = hdr->deleted;
+ flagged = hdr->flagged;
+ mutt_header_free(&hdr);
+
+ /* header marked as deleted, removing from context */
+ if (deleted)
+ {
+ mutt_set_flag(ctx, ctx->mailbox->hdrs[i], MUTT_TAG, 0);
+ mutt_header_free(&ctx->mailbox->hdrs[i]);
+ continue;
+ }
+ }
+ }
+#endif
+
+ if (!ctx->mailbox->hdrs[i]->changed)
+ {
+ ctx->mailbox->hdrs[i]->flagged = flagged;
+ ctx->mailbox->hdrs[i]->read = false;
+ ctx->mailbox->hdrs[i]->old = false;
+ nntp_article_status(ctx->mailbox, ctx->mailbox->hdrs[i], NULL, anum);
+ if (!ctx->mailbox->hdrs[i]->read)
+ nntp_parse_xref(ctx->mailbox, ctx->mailbox->hdrs[i]);
+ }
+ ctx->mailbox->hdrs[j++] = ctx->mailbox->hdrs[i];
}
- nntp_data->first_message = first;
- nntp_data->last_message = last;
- nntp_data->deleted = false;
- /* get description if empty */
- if (NntpLoadDescription && !nntp_data->desc)
+#ifdef USE_HCACHE
+ ctx->mailbox->msg_count = j;
+
+ /* restore headers without "deleted" flag */
+ for (anum = first; anum <= nntp_data->last_loaded; anum++)
{
- if (get_description(nntp_data, NULL, NULL) < 0)
+ if (messages[anum - first])
+ continue;
+
+ snprintf(buf, sizeof(buf), "%u", anum);
+ hdata = mutt_hcache_fetch(hc, buf, strlen(buf));
+ if (hdata)
{
- nntp_newsrc_close(nserv);
- return -1;
+ mutt_debug(2, "#2 mutt_hcache_fetch %s\n", buf);
+ if (ctx->mailbox->msg_count >= ctx->mailbox->hdrmax)
+ mx_alloc_memory(ctx->mailbox);
+
+ hdr = mutt_hcache_restore(hdata);
+ ctx->mailbox->hdrs[ctx->mailbox->msg_count] = hdr;
+ mutt_hcache_free(hc, &hdata);
+ hdr->data = NULL;
+ if (hdr->deleted)
+ {
+ mutt_header_free(&hdr);
+ if (nntp_data->bcache)
+ {
+ mutt_debug(2, "mutt_bcache_del %s\n", buf);
+ mutt_bcache_del(nntp_data->bcache, buf);
+ }
+ continue;
+ }
+
+ ctx->mailbox->msg_count++;
+ hdr->read = false;
+ hdr->old = false;
+ hdr->data = mutt_mem_calloc(1, sizeof(struct NntpHeaderData));
+ NHDR(hdr)->article_num = anum;
+ nntp_article_status(ctx->mailbox, hdr, NULL, anum);
+ if (!hdr->read)
+ nntp_parse_xref(ctx->mailbox, hdr);
}
- if (nntp_data->desc)
- nntp_active_save_cache(nserv);
}
+ FREE(&messages);
+#endif
+
+ nserv->newsrc_modified = false;
+ ret = MUTT_REOPENED;
}
- time(&nserv->check_time);
- ctx->mailbox->data = nntp_data;
- if (!nntp_data->bcache && (nntp_data->newsrc_ent || nntp_data->subscribed || SaveUnsubscribed))
- nntp_data->bcache = mutt_bcache_open(&nserv->conn->account, nntp_data->group);
+ /* some headers were removed, context must be updated */
+ if (ret == MUTT_REOPENED)
+ {
+ if (ctx->mailbox->subj_hash)
+ mutt_hash_destroy(&ctx->mailbox->subj_hash);
+ if (ctx->mailbox->id_hash)
+ mutt_hash_destroy(&ctx->mailbox->id_hash);
+ mutt_clear_threads(ctx);
- /* strip off extra articles if adding context is greater than $nntp_context */
- first = nntp_data->first_message;
- if (NntpContext && nntp_data->last_message - first + 1 > NntpContext)
- first = nntp_data->last_message - NntpContext + 1;
- nntp_data->last_loaded = first ? first - 1 : 0;
- count = nntp_data->first_message;
- nntp_data->first_message = first;
- nntp_bcache_update(nntp_data);
- nntp_data->first_message = count;
+ ctx->mailbox->vcount = 0;
+ ctx->deleted = 0;
+ ctx->new = 0;
+ ctx->mailbox->msg_unread = 0;
+ ctx->mailbox->msg_flagged = 0;
+ ctx->mailbox->changed = false;
+ ctx->mailbox->id_hash = NULL;
+ ctx->mailbox->subj_hash = NULL;
+ mx_update_context(ctx, ctx->mailbox->msg_count);
+ }
+
+ /* fetch headers of new articles */
+ if (nntp_data->last_message > nntp_data->last_loaded)
+ {
+ int oldmsgcount = ctx->mailbox->msg_count;
+ bool quiet = ctx->mailbox->quiet;
+ ctx->mailbox->quiet = true;
#ifdef USE_HCACHE
- hc = nntp_hcache_open(nntp_data);
- nntp_hcache_update(nntp_data, hc);
+ if (!hc)
+ {
+ hc = nntp_hcache_open(nntp_data);
+ nntp_hcache_update(nntp_data, hc);
+ }
#endif
- if (!hc)
- {
- mutt_bit_unset(ctx->mailbox->rights, MUTT_ACL_WRITE);
- mutt_bit_unset(ctx->mailbox->rights, MUTT_ACL_DELETE);
+ rc = nntp_fetch_headers(ctx, hc, nntp_data->last_loaded + 1, nntp_data->last_message, 0);
+ ctx->mailbox->quiet = quiet;
+ if (rc >= 0)
+ nntp_data->last_loaded = nntp_data->last_message;
+ if (ret == 0 && ctx->mailbox->msg_count > oldmsgcount)
+ ret = MUTT_NEW_MAIL;
}
- nntp_newsrc_close(nserv);
- rc = nntp_fetch_headers(ctx, hc, first, nntp_data->last_message, 0);
+
#ifdef USE_HCACHE
mutt_hcache_close(hc);
#endif
- if (rc < 0)
- return -1;
- nntp_data->last_loaded = nntp_data->last_message;
- nserv->newsrc_modified = false;
- return 0;
+ if (ret)
+ nntp_newsrc_close(nserv);
+ mutt_clear_error();
+ return ret;
}
/**
- * nntp_msg_open - Implements MxOps::msg_open()
+ * nntp_date - Get date and time from server
+ * @param nserv NNTP server
+ * @param now Server time
+ * @retval 0 Success
+ * @retval -1 Failure
*/
-static int nntp_msg_open(struct Context *ctx, struct Message *msg, int msgno)
+static int nntp_date(struct NntpServer *nserv, time_t *now)
{
- struct NntpData *nntp_data = ctx->mailbox->data;
- struct Header *hdr = ctx->mailbox->hdrs[msgno];
- char article[16];
-
- /* try to get article from cache */
- struct NntpAcache *acache = &nntp_data->acache[hdr->index % NNTP_ACACHE_LEN];
- if (acache->path)
+ if (nserv->hasDATE)
{
- if (acache->index == hdr->index)
+ struct NntpData nntp_data;
+ char buf[LONG_STRING];
+ struct tm tm;
+ memset(&tm, 0, sizeof(tm));
+
+ nntp_data.nserv = nserv;
+ nntp_data.group = NULL;
+ mutt_str_strfcpy(buf, "DATE\r\n", sizeof(buf));
+ if (nntp_query(&nntp_data, buf, sizeof(buf)) < 0)
+ return -1;
+
+ if (sscanf(buf, "111 %4d%2d%2d%2d%2d%2d%*s", &tm.tm_year, &tm.tm_mon,
+ &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6)
{
- msg->fp = mutt_file_fopen(acache->path, "r");
- if (msg->fp)
+ tm.tm_year -= 1900;
+ tm.tm_mon--;
+ *now = timegm(&tm);
+ if (*now >= 0)
+ {
+ mutt_debug(1, "server time is %lu\n", *now);
return 0;
- }
- /* clear previous entry */
- else
- {
- unlink(acache->path);
- FREE(&acache->path);
+ }
}
}
- snprintf(article, sizeof(article), "%d", NHDR(hdr)->article_num);
- msg->fp = mutt_bcache_get(nntp_data->bcache, article);
- if (msg->fp)
- {
- if (NHDR(hdr)->parsed)
+ time(now);
+ return 0;
+}
+
+/**
+ * fetch_children - Parse XPAT line
+ * @param line String to parse
+ * @param data ChildCtx
+ * @retval 0 Always
+ */
+static int fetch_children(char *line, void *data)
+{
+ struct ChildCtx *cc = data;
+ anum_t anum;
+
+ if (!line || sscanf(line, ANUM, &anum) != 1)
+ return 0;
+ for (unsigned int i = 0; i < cc->mailbox->msg_count; i++)
+ if (NHDR(cc->mailbox->hdrs[i])->article_num == anum)
return 0;
+ if (cc->num >= cc->max)
+ {
+ cc->max *= 2;
+ mutt_mem_realloc(&cc->child, sizeof(anum_t) * cc->max);
}
- else
+ cc->child[cc->num++] = anum;
+ return 0;
+}
+
+/**
+ * nntp_open_connection - Connect to server, authenticate and get capabilities
+ * @param nserv NNTP server
+ * @retval 0 Success
+ * @retval -1 Failure
+ */
+int nntp_open_connection(struct NntpServer *nserv)
+{
+ struct Connection *conn = nserv->conn;
+ char buf[STRING];
+ int cap;
+ bool posting = false, auth = true;
+
+ if (nserv->status == NNTP_OK)
+ return 0;
+ if (nserv->status == NNTP_BYE)
+ return -1;
+ nserv->status = NNTP_NONE;
+
+ if (mutt_socket_open(conn) < 0)
+ return -1;
+
+ if (mutt_socket_readln(buf, sizeof(buf), conn) < 0)
+ return nntp_connect_error(nserv);
+
+ if (mutt_str_strncmp("200", buf, 3) == 0)
+ posting = true;
+ else if (mutt_str_strncmp("201", buf, 3) != 0)
{
- char buf[PATH_MAX];
- /* don't try to fetch article from removed newsgroup */
- if (nntp_data->deleted)
+ mutt_socket_close(conn);
+ mutt_str_remove_trailing_ws(buf);
+ mutt_error("%s", buf);
+ return -1;
+ }
+
+ /* get initial capabilities */
+ cap = nntp_capabilities(nserv);
+ if (cap < 0)
+ return -1;
+
+ /* tell news server to switch to mode reader if it isn't so */
+ if (cap > 0)
+ {
+ if (mutt_socket_send(conn, "MODE READER\r\n") < 0 ||
+ mutt_socket_readln(buf, sizeof(buf), conn) < 0)
+ {
+ return nntp_connect_error(nserv);
+ }
+
+ if (mutt_str_strncmp("200", buf, 3) == 0)
+ posting = true;
+ else if (mutt_str_strncmp("201", buf, 3) == 0)
+ posting = false;
+ /* error if has capabilities, ignore result if no capabilities */
+ else if (nserv->hasCAPABILITIES)
+ {
+ mutt_socket_close(conn);
+ mutt_error(_("Could not switch to reader mode"));
return -1;
+ }
- /* create new cache file */
- const char *fetch_msg = _("Fetching message...");
- mutt_message(fetch_msg);
- msg->fp = mutt_bcache_put(nntp_data->bcache, article);
- if (!msg->fp)
+ /* recheck capabilities after MODE READER */
+ if (nserv->hasCAPABILITIES)
{
- mutt_mktemp(buf, sizeof(buf));
- acache->path = mutt_str_strdup(buf);
- acache->index = hdr->index;
- msg->fp = mutt_file_fopen(acache->path, "w+");
- if (!msg->fp)
- {
- mutt_perror(acache->path);
- unlink(acache->path);
- FREE(&acache->path);
+ cap = nntp_capabilities(nserv);
+ if (cap < 0)
return -1;
+ }
+ }
+
+ mutt_message(_("Connected to %s. %s"), conn->account.host,
+ posting ? _("Posting is ok") : _("Posting is NOT ok"));
+ mutt_sleep(1);
+
+#ifdef USE_SSL
+ /* Attempt STARTTLS if available and desired. */
+ if (nserv->use_tls != 1 && (nserv->hasSTARTTLS || SslForceTls))
+ {
+ if (nserv->use_tls == 0)
+ {
+ nserv->use_tls =
+ SslForceTls || query_quadoption(SslStarttls,
+ _("Secure connection with TLS?")) == MUTT_YES ?
+ 2 :
+ 1;
+ }
+ if (nserv->use_tls == 2)
+ {
+ if (mutt_socket_send(conn, "STARTTLS\r\n") < 0 ||
+ mutt_socket_readln(buf, sizeof(buf), conn) < 0)
+ {
+ return nntp_connect_error(nserv);
+ }
+ if (mutt_str_strncmp("382", buf, 3) != 0)
+ {
+ nserv->use_tls = 0;
+ mutt_error("STARTTLS: %s", buf);
}
- }
-
- /* fetch message to cache file */
- snprintf(buf, sizeof(buf), "ARTICLE %s\r\n",
- NHDR(hdr)->article_num ? article : hdr->env->message_id);
- const int rc = nntp_fetch_lines(nntp_data, buf, sizeof(buf), fetch_msg,
- fetch_tempfile, msg->fp);
- if (rc)
- {
- mutt_file_fclose(&msg->fp);
- if (acache->path)
+ else if (mutt_ssl_starttls(conn))
{
- unlink(acache->path);
- FREE(&acache->path);
+ nserv->use_tls = 0;
+ nserv->status = NNTP_NONE;
+ mutt_socket_close(nserv->conn);
+ mutt_error(_("Could not negotiate TLS connection"));
+ return -1;
}
- if (rc > 0)
+ else
{
- if (mutt_str_strncmp(NHDR(hdr)->article_num ? "423" : "430", buf, 3) == 0)
- {
- mutt_error(_("Article %d not found on the server"),
- NHDR(hdr)->article_num ? article : hdr->env->message_id);
- }
- else
- mutt_error("ARTICLE: %s", buf);
+ /* recheck capabilities after STARTTLS */
+ cap = nntp_capabilities(nserv);
+ if (cap < 0)
+ return -1;
}
- return -1;
}
-
- if (!acache->path)
- mutt_bcache_commit(nntp_data->bcache, article);
}
+#endif
- /* replace envelope with new one
- * hash elements must be updated because pointers will be changed */
- if (ctx->mailbox->id_hash && hdr->env->message_id)
- mutt_hash_delete(ctx->mailbox->id_hash, hdr->env->message_id, hdr);
- if (ctx->mailbox->subj_hash && hdr->env->real_subj)
- mutt_hash_delete(ctx->mailbox->subj_hash, hdr->env->real_subj, hdr);
-
- mutt_env_free(&hdr->env);
- hdr->env = mutt_rfc822_read_header(msg->fp, hdr, false, false);
-
- if (ctx->mailbox->id_hash && hdr->env->message_id)
- mutt_hash_insert(ctx->mailbox->id_hash, hdr->env->message_id, hdr);
- if (ctx->mailbox->subj_hash && hdr->env->real_subj)
- mutt_hash_insert(ctx->mailbox->subj_hash, hdr->env->real_subj, hdr);
+ /* authentication required? */
+ if (conn->account.flags & MUTT_ACCT_USER)
+ {
+ if (!conn->account.user[0])
+ auth = false;
+ }
+ else
+ {
+ if (mutt_socket_send(conn, "STAT\r\n") < 0 ||
+ mutt_socket_readln(buf, sizeof(buf), conn) < 0)
+ {
+ return nntp_connect_error(nserv);
+ }
+ if (mutt_str_strncmp("480", buf, 3) != 0)
+ auth = false;
+ }
- /* fix content length */
- fseek(msg->fp, 0, SEEK_END);
- hdr->content->length = ftell(msg->fp) - hdr->content->offset;
+ /* authenticate */
+ if (auth && nntp_auth(nserv) < 0)
+ return -1;
- /* this is called in neomutt before the open which fetches the message,
- * which is probably wrong, but we just call it again here to handle
- * the problem instead of fixing it */
- NHDR(hdr)->parsed = true;
- mutt_parse_mime_message(ctx, hdr);
+ /* get final capabilities after authentication */
+ if (nserv->hasCAPABILITIES && (auth || cap > 0))
+ {
+ cap = nntp_capabilities(nserv);
+ if (cap < 0)
+ return -1;
+ if (cap > 0)
+ {
+ mutt_socket_close(conn);
+ mutt_error(_("Could not switch to reader mode"));
+ return -1;
+ }
+ }
- /* these would normally be updated in mx_update_context(), but the
- * full headers aren't parsed with overview, so the information wasn't
- * available then */
- if (WithCrypto)
- hdr->security = crypt_query(hdr->content);
+ /* attempt features */
+ if (nntp_attempt_features(nserv) < 0)
+ return -1;
- rewind(msg->fp);
- mutt_clear_error();
+ nserv->status = NNTP_OK;
return 0;
}
-/**
- * nntp_msg_close - Implements MxOps::msg_close()
- *
- * @note May also return EOF Failure, see errno
- */
-static int nntp_msg_close(struct Context *ctx, struct Message *msg)
-{
- return mutt_file_fclose(&msg->fp);
-}
-
/**
* nntp_post - Post article
* @param mailbox Mailbox
}
/**
- * nntp_group_poll - Check newsgroup for new articles
- * @param nntp_data NNTP server data
- * @param update_stat Update the stats?
- * @retval 1 New articles found
- * @retval 0 No change
- * @retval -1 Lost connection
+ * nntp_active_fetch - Fetch list of all newsgroups from server
+ * @param nserv NNTP server
+ * @param new Mark the groups as new
+ * @retval 0 Success
+ * @retval -1 Failure
*/
-static int nntp_group_poll(struct NntpData *nntp_data, int update_stat)
+int nntp_active_fetch(struct NntpServer *nserv, bool new)
{
- char buf[LONG_STRING] = "";
- anum_t count, first, last;
+ struct NntpData nntp_data;
+ char msg[STRING];
+ char buf[LONG_STRING];
+ unsigned int i;
+ int rc;
- /* use GROUP command to poll newsgroup */
- if (nntp_query(nntp_data, buf, sizeof(buf)) < 0)
+ snprintf(msg, sizeof(msg), _("Loading list of groups from server %s..."),
+ nserv->conn->account.host);
+ mutt_message(msg);
+ if (nntp_date(nserv, &nserv->newgroups_time) < 0)
return -1;
- if (sscanf(buf, "211 " ANUM " " ANUM " " ANUM, &count, &first, &last) != 3)
- return 0;
- if (first == nntp_data->first_message && last == nntp_data->last_message)
- return 0;
- /* articles have been renumbered */
- if (last < nntp_data->last_message)
+ nntp_data.nserv = nserv;
+ nntp_data.group = NULL;
+ i = nserv->groups_num;
+ mutt_str_strfcpy(buf, "LIST\r\n", sizeof(buf));
+ rc = nntp_fetch_lines(&nntp_data, buf, sizeof(buf), msg, nntp_add_group, nserv);
+ if (rc)
{
- nntp_data->last_cached = 0;
- if (nntp_data->newsrc_len)
+ if (rc > 0)
{
- mutt_mem_realloc(&nntp_data->newsrc_ent, sizeof(struct NewsrcEntry));
- nntp_data->newsrc_len = 1;
- nntp_data->newsrc_ent[0].first = 1;
- nntp_data->newsrc_ent[0].last = 0;
+ mutt_error("LIST: %s", buf);
}
+ return -1;
}
- nntp_data->first_message = first;
- nntp_data->last_message = last;
- if (!update_stat)
- return 1;
- /* update counters */
- else if (!last || (!nntp_data->newsrc_ent && !nntp_data->last_cached))
- nntp_data->unread = count;
- else
- nntp_group_unread_stat(nntp_data);
- return 1;
+ if (new)
+ {
+ for (; i < nserv->groups_num; i++)
+ {
+ struct NntpData *data = nserv->groups_list[i];
+ data->new = true;
+ }
+ }
+
+ for (i = 0; i < nserv->groups_num; i++)
+ {
+ struct NntpData *data = nserv->groups_list[i];
+
+ if (data && data->deleted && !data->newsrc_ent)
+ {
+ nntp_delete_group_cache(data);
+ mutt_hash_delete(nserv->groups_hash, data->group, NULL);
+ nserv->groups_list[i] = NULL;
+ }
+ }
+
+ if (NntpLoadDescription)
+ rc = get_description(&nntp_data, "*", _("Loading descriptions..."));
+
+ nntp_active_save_cache(nserv);
+ if (rc < 0)
+ return -1;
+ mutt_clear_error();
+ return 0;
}
/**
- * check_mailbox - Check current newsgroup for new articles
- * @param ctx Mailbox
- * @retval #MUTT_REOPENED Articles have been renumbered or removed from server
- * @retval #MUTT_NEW_MAIL New articles found
- * @retval 0 No change
- * @retval -1 Lost connection
- *
- * Leave newsrc locked
+ * nntp_check_new_groups - Check for new groups/articles in subscribed groups
+ * @param mailbox Mailbox
+ * @param nserv NNTP server
+ * @retval 1 New groups found
+ * @retval 0 No new groups
+ * @retval -1 Error
*/
-static int check_mailbox(struct Context *ctx)
+int nntp_check_new_groups(struct Mailbox *mailbox, struct NntpServer *nserv)
{
- struct NntpData *nntp_data = ctx->mailbox->data;
- struct NntpServer *nserv = nntp_data->nserv;
- time_t now = time(NULL);
- int rc, ret = 0;
- void *hc = NULL;
+ struct NntpData nntp_data;
+ time_t now;
+ struct tm *tm = NULL;
+ char buf[LONG_STRING];
+ char *msg = _("Checking for new newsgroups...");
+ unsigned int i;
+ int rc, update_active = false;
- if (nserv->check_time + NntpPoll > now)
+ if (!nserv || !nserv->newgroups_time)
+ return -1;
+
+ /* check subscribed newsgroups for new articles */
+ if (ShowNewNews)
+ {
+ mutt_message(_("Checking for new messages..."));
+ for (i = 0; i < nserv->groups_num; i++)
+ {
+ struct NntpData *data = nserv->groups_list[i];
+
+ if (data && data->subscribed)
+ {
+ rc = nntp_group_poll(data, 1);
+ if (rc < 0)
+ return -1;
+ if (rc > 0)
+ update_active = true;
+ }
+ }
+ /* select current newsgroup */
+ if (mailbox && (mailbox->magic == MUTT_NNTP))
+ {
+ buf[0] = '\0';
+ if (nntp_query(mailbox->data, buf, sizeof(buf)) < 0)
+ return -1;
+ }
+ }
+ else if (nserv->newgroups_time)
return 0;
- mutt_message(_("Checking for new messages..."));
- if (nntp_newsrc_parse(nserv) < 0)
- return -1;
-
- nserv->check_time = now;
- rc = nntp_group_poll(nntp_data, 0);
- if (rc < 0)
- {
- nntp_newsrc_close(nserv);
+ /* get list of new groups */
+ mutt_message(msg);
+ if (nntp_date(nserv, &now) < 0)
return -1;
- }
+ nntp_data.nserv = nserv;
+ if (mailbox && (mailbox->magic == MUTT_NNTP))
+ nntp_data.group = ((struct NntpData *) mailbox->data)->group;
+ else
+ nntp_data.group = NULL;
+ i = nserv->groups_num;
+ tm = gmtime(&nserv->newgroups_time);
+ snprintf(buf, sizeof(buf), "NEWGROUPS %02d%02d%02d %02d%02d%02d GMT\r\n",
+ tm->tm_year % 100, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour,
+ tm->tm_min, tm->tm_sec);
+ rc = nntp_fetch_lines(&nntp_data, buf, sizeof(buf), msg, nntp_add_group, nserv);
if (rc)
- nntp_active_save_cache(nserv);
-
- /* articles have been renumbered, remove all headers */
- if (nntp_data->last_message < nntp_data->last_loaded)
{
- for (int i = 0; i < ctx->mailbox->msg_count; i++)
- mutt_header_free(&ctx->mailbox->hdrs[i]);
- ctx->mailbox->msg_count = 0;
- ctx->tagged = 0;
-
- if (nntp_data->last_message < nntp_data->last_loaded)
+ if (rc > 0)
{
- nntp_data->last_loaded = nntp_data->first_message - 1;
- if (NntpContext && nntp_data->last_message - nntp_data->last_loaded > NntpContext)
- nntp_data->last_loaded = nntp_data->last_message - NntpContext;
+ mutt_error("NEWGROUPS: %s", buf);
}
- ret = MUTT_REOPENED;
+ return -1;
}
- /* .newsrc has been externally modified */
- if (nserv->newsrc_modified)
+ /* new groups found */
+ rc = 0;
+ if (nserv->groups_num != i)
{
-#ifdef USE_HCACHE
- unsigned char *messages = NULL;
- char buf[16];
- void *hdata = NULL;
- struct Header *hdr = NULL;
- anum_t first = nntp_data->first_message;
-
- if (NntpContext && nntp_data->last_message - first + 1 > NntpContext)
- first = nntp_data->last_message - NntpContext + 1;
- messages = mutt_mem_calloc(nntp_data->last_loaded - first + 1, sizeof(unsigned char));
- hc = nntp_hcache_open(nntp_data);
- nntp_hcache_update(nntp_data, hc);
-#endif
+ int groups_num = i;
- /* update flags according to .newsrc */
- int j = 0;
- anum_t anum;
- for (int i = 0; i < ctx->mailbox->msg_count; i++)
+ nserv->newgroups_time = now;
+ for (; i < nserv->groups_num; i++)
{
- bool flagged = false;
- anum = NHDR(ctx->mailbox->hdrs[i])->article_num;
-
-#ifdef USE_HCACHE
- /* check hcache for flagged and deleted flags */
- if (hc)
- {
- if (anum >= first && anum <= nntp_data->last_loaded)
- messages[anum - first] = 1;
-
- snprintf(buf, sizeof(buf), "%u", anum);
- hdata = mutt_hcache_fetch(hc, buf, strlen(buf));
- if (hdata)
- {
- bool deleted;
-
- mutt_debug(2, "#1 mutt_hcache_fetch %s\n", buf);
- hdr = mutt_hcache_restore(hdata);
- mutt_hcache_free(hc, &hdata);
- hdr->data = NULL;
- deleted = hdr->deleted;
- flagged = hdr->flagged;
- mutt_header_free(&hdr);
-
- /* header marked as deleted, removing from context */
- if (deleted)
- {
- mutt_set_flag(ctx, ctx->mailbox->hdrs[i], MUTT_TAG, 0);
- mutt_header_free(&ctx->mailbox->hdrs[i]);
- continue;
- }
- }
- }
-#endif
-
- if (!ctx->mailbox->hdrs[i]->changed)
- {
- ctx->mailbox->hdrs[i]->flagged = flagged;
- ctx->mailbox->hdrs[i]->read = false;
- ctx->mailbox->hdrs[i]->old = false;
- nntp_article_status(ctx->mailbox, ctx->mailbox->hdrs[i], NULL, anum);
- if (!ctx->mailbox->hdrs[i]->read)
- nntp_parse_xref(ctx->mailbox, ctx->mailbox->hdrs[i]);
- }
- ctx->mailbox->hdrs[j++] = ctx->mailbox->hdrs[i];
+ struct NntpData *data = nserv->groups_list[i];
+ data->new = true;
}
-#ifdef USE_HCACHE
- ctx->mailbox->msg_count = j;
-
- /* restore headers without "deleted" flag */
- for (anum = first; anum <= nntp_data->last_loaded; anum++)
+ /* loading descriptions */
+ if (NntpLoadDescription)
{
- if (messages[anum - first])
- continue;
+ unsigned int count = 0;
+ struct Progress progress;
- snprintf(buf, sizeof(buf), "%u", anum);
- hdata = mutt_hcache_fetch(hc, buf, strlen(buf));
- if (hdata)
+ mutt_progress_init(&progress, _("Loading descriptions..."),
+ MUTT_PROGRESS_MSG, ReadInc, nserv->groups_num - i);
+ for (i = groups_num; i < nserv->groups_num; i++)
{
- mutt_debug(2, "#2 mutt_hcache_fetch %s\n", buf);
- if (ctx->mailbox->msg_count >= ctx->mailbox->hdrmax)
- mx_alloc_memory(ctx->mailbox);
-
- hdr = mutt_hcache_restore(hdata);
- ctx->mailbox->hdrs[ctx->mailbox->msg_count] = hdr;
- mutt_hcache_free(hc, &hdata);
- hdr->data = NULL;
- if (hdr->deleted)
- {
- mutt_header_free(&hdr);
- if (nntp_data->bcache)
- {
- mutt_debug(2, "mutt_bcache_del %s\n", buf);
- mutt_bcache_del(nntp_data->bcache, buf);
- }
- continue;
- }
+ struct NntpData *data = nserv->groups_list[i];
- ctx->mailbox->msg_count++;
- hdr->read = false;
- hdr->old = false;
- hdr->data = mutt_mem_calloc(1, sizeof(struct NntpHeaderData));
- NHDR(hdr)->article_num = anum;
- nntp_article_status(ctx->mailbox, hdr, NULL, anum);
- if (!hdr->read)
- nntp_parse_xref(ctx->mailbox, hdr);
+ if (get_description(data, NULL, NULL) < 0)
+ return -1;
+ mutt_progress_update(&progress, ++count, -1);
}
}
- FREE(&messages);
-#endif
-
- nserv->newsrc_modified = false;
- ret = MUTT_REOPENED;
- }
-
- /* some headers were removed, context must be updated */
- if (ret == MUTT_REOPENED)
- {
- if (ctx->mailbox->subj_hash)
- mutt_hash_destroy(&ctx->mailbox->subj_hash);
- if (ctx->mailbox->id_hash)
- mutt_hash_destroy(&ctx->mailbox->id_hash);
- mutt_clear_threads(ctx);
-
- ctx->mailbox->vcount = 0;
- ctx->deleted = 0;
- ctx->new = 0;
- ctx->mailbox->msg_unread = 0;
- ctx->mailbox->msg_flagged = 0;
- ctx->mailbox->changed = false;
- ctx->mailbox->id_hash = NULL;
- ctx->mailbox->subj_hash = NULL;
- mx_update_context(ctx, ctx->mailbox->msg_count);
- }
-
- /* fetch headers of new articles */
- if (nntp_data->last_message > nntp_data->last_loaded)
- {
- int oldmsgcount = ctx->mailbox->msg_count;
- bool quiet = ctx->mailbox->quiet;
- ctx->mailbox->quiet = true;
-#ifdef USE_HCACHE
- if (!hc)
- {
- hc = nntp_hcache_open(nntp_data);
- nntp_hcache_update(nntp_data, hc);
- }
-#endif
- rc = nntp_fetch_headers(ctx, hc, nntp_data->last_loaded + 1, nntp_data->last_message, 0);
- ctx->mailbox->quiet = quiet;
- if (rc >= 0)
- nntp_data->last_loaded = nntp_data->last_message;
- if (ret == 0 && ctx->mailbox->msg_count > oldmsgcount)
- ret = MUTT_NEW_MAIL;
+ update_active = true;
+ rc = 1;
}
-
-#ifdef USE_HCACHE
- mutt_hcache_close(hc);
-#endif
- if (ret)
- nntp_newsrc_close(nserv);
+ if (update_active)
+ nntp_active_save_cache(nserv);
mutt_clear_error();
- return ret;
+ return rc;
}
/**
- * nntp_mbox_check - Implements MxOps::mbox_check()
- * @param ctx Mailbox
- * @param index_hint Current message (UNUSED)
- * @retval #MUTT_REOPENED Articles have been renumbered or removed from server
- * @retval #MUTT_NEW_MAIL New articles found
- * @retval 0 No change
- * @retval -1 Lost connection
+ * nntp_check_msgid - Fetch article by Message-ID
+ * @param ctx Mailbox
+ * @param msgid Message ID
+ * @retval 0 Success
+ * @retval 1 No such article
+ * @retval -1 Error
*/
-static int nntp_mbox_check(struct Context *ctx, int *index_hint)
+int nntp_check_msgid(struct Context *ctx, const char *msgid)
{
- int ret = check_mailbox(ctx);
- if (ret == 0)
+ struct NntpData *nntp_data = ctx->mailbox->data;
+ char buf[LONG_STRING];
+
+ FILE *fp = mutt_file_mkstemp();
+ if (!fp)
+ {
+ mutt_perror(_("Can't create temporary file"));
+ return -1;
+ }
+
+ snprintf(buf, sizeof(buf), "HEAD %s\r\n", msgid);
+ int rc = nntp_fetch_lines(nntp_data, buf, sizeof(buf), NULL, fetch_tempfile, fp);
+ if (rc)
+ {
+ mutt_file_fclose(&fp);
+ if (rc < 0)
+ return -1;
+ if (mutt_str_strncmp("430", buf, 3) == 0)
+ return 1;
+ mutt_error("HEAD: %s", buf);
+ return -1;
+ }
+
+ /* parse header */
+ if (ctx->mailbox->msg_count == ctx->mailbox->hdrmax)
+ mx_alloc_memory(ctx->mailbox);
+ ctx->mailbox->hdrs[ctx->mailbox->msg_count] = mutt_header_new();
+ struct Header *hdr = ctx->mailbox->hdrs[ctx->mailbox->msg_count];
+ hdr->data = mutt_mem_calloc(1, sizeof(struct NntpHeaderData));
+ hdr->env = mutt_rfc822_read_header(fp, hdr, false, false);
+ mutt_file_fclose(&fp);
+
+ /* get article number */
+ if (hdr->env->xref)
+ nntp_parse_xref(ctx->mailbox, hdr);
+ else
{
- struct NntpData *nntp_data = ctx->mailbox->data;
- struct NntpServer *nserv = nntp_data->nserv;
- nntp_newsrc_close(nserv);
+ snprintf(buf, sizeof(buf), "STAT %s\r\n", msgid);
+ if (nntp_query(nntp_data, buf, sizeof(buf)) < 0)
+ {
+ mutt_header_free(&hdr);
+ return -1;
+ }
+ sscanf(buf + 4, ANUM, &NHDR(hdr)->article_num);
}
- return ret;
+
+ /* reset flags */
+ hdr->read = false;
+ hdr->old = false;
+ hdr->deleted = false;
+ hdr->changed = true;
+ hdr->received = hdr->date_sent;
+ hdr->index = ctx->mailbox->msg_count++;
+ mx_update_context(ctx, 1);
+ return 0;
}
/**
- * nntp_mbox_sync - Implements MxOps::mbox_sync()
- *
- * @note May also return values from check_mailbox()
+ * nntp_check_children - Fetch children of article with the Message-ID
+ * @param ctx Mailbox
+ * @param msgid Message ID to find
+ * @retval 0 Success
+ * @retval -1 Failure
*/
-static int nntp_mbox_sync(struct Context *ctx, int *index_hint)
+int nntp_check_children(struct Context *ctx, const char *msgid)
{
struct NntpData *nntp_data = ctx->mailbox->data;
+ struct ChildCtx cc;
+ char buf[STRING];
int rc;
-#ifdef USE_HCACHE
- header_cache_t *hc = NULL;
-#endif
+ bool quiet;
+ void *hc = NULL;
- /* check for new articles */
- nntp_data->nserv->check_time = 0;
- rc = check_mailbox(ctx);
- if (rc)
- return rc;
+ if (!nntp_data || !nntp_data->nserv)
+ return -1;
+ if (nntp_data->first_message > nntp_data->last_loaded)
+ return 0;
-#ifdef USE_HCACHE
- nntp_data->last_cached = 0;
- hc = nntp_hcache_open(nntp_data);
-#endif
+ /* init context */
+ cc.mailbox = ctx->mailbox;
+ cc.num = 0;
+ cc.max = 10;
+ cc.child = mutt_mem_malloc(sizeof(anum_t) * cc.max);
- for (int i = 0; i < ctx->mailbox->msg_count; i++)
+ /* fetch numbers of child messages */
+ snprintf(buf, sizeof(buf), "XPAT References %u-%u *%s*\r\n",
+ nntp_data->first_message, nntp_data->last_loaded, msgid);
+ rc = nntp_fetch_lines(nntp_data, buf, sizeof(buf), NULL, fetch_children, &cc);
+ if (rc)
{
- struct Header *hdr = ctx->mailbox->hdrs[i];
- char buf[16];
-
- snprintf(buf, sizeof(buf), "%d", NHDR(hdr)->article_num);
- if (nntp_data->bcache && hdr->deleted)
- {
- mutt_debug(2, "mutt_bcache_del %s\n", buf);
- mutt_bcache_del(nntp_data->bcache, buf);
- }
-
-#ifdef USE_HCACHE
- if (hc && (hdr->changed || hdr->deleted))
+ FREE(&cc.child);
+ if (rc > 0)
{
- if (hdr->deleted && !hdr->read)
- nntp_data->unread--;
- mutt_debug(2, "mutt_hcache_store %s\n", buf);
- mutt_hcache_store(hc, buf, strlen(buf), hdr, 0);
+ if (mutt_str_strncmp("500", buf, 3) != 0)
+ mutt_error("XPAT: %s", buf);
+ else
+ {
+ mutt_error(_("Unable to find child articles because server does not "
+ "support XPAT command"));
+ }
}
-#endif
+ return -1;
}
+ /* fetch all found messages */
+ quiet = ctx->mailbox->quiet;
+ ctx->mailbox->quiet = true;
#ifdef USE_HCACHE
- if (hc)
+ hc = nntp_hcache_open(nntp_data);
+#endif
+ for (int i = 0; i < cc.num; i++)
{
- mutt_hcache_close(hc);
- nntp_data->last_cached = nntp_data->last_loaded;
+ rc = nntp_fetch_headers(ctx, hc, cc.child[i], cc.child[i], 1);
+ if (rc < 0)
+ break;
}
+#ifdef USE_HCACHE
+ mutt_hcache_close(hc);
#endif
-
- /* save .newsrc entries */
- nntp_newsrc_gen_entries(ctx);
- nntp_newsrc_update(nntp_data->nserv);
- nntp_newsrc_close(nntp_data->nserv);
- return 0;
+ ctx->mailbox->quiet = quiet;
+ FREE(&cc.child);
+ return (rc < 0) ? -1 : 0;
}
/**
- * nntp_mbox_close - Implements MxOps::mbox_close()
- * @retval 0 Always
+ * nntp_compare_order - Sort to mailbox order - Implements ::sort_t
*/
-static int nntp_mbox_close(struct Context *ctx)
+int nntp_compare_order(const void *a, const void *b)
{
- struct NntpData *nntp_data = ctx->mailbox->data, *nntp_tmp = NULL;
-
- if (!nntp_data)
- return 0;
-
- nntp_data->unread = ctx->mailbox->msg_unread;
-
- nntp_acache_free(nntp_data);
- if (!nntp_data->nserv || !nntp_data->nserv->groups_hash || !nntp_data->group)
- return 0;
+ struct Header **ha = (struct Header **) a;
+ struct Header **hb = (struct Header **) b;
- nntp_tmp = mutt_hash_find(nntp_data->nserv->groups_hash, nntp_data->group);
- if (!nntp_tmp || nntp_tmp != nntp_data)
- nntp_data_free(nntp_data);
- return 0;
+ anum_t na = NHDR(*ha)->article_num;
+ anum_t nb = NHDR(*hb)->article_num;
+ int result = (na == nb) ? 0 : (na > nb) ? 1 : -1;
+ result = perform_auxsort(result, a, b);
+ return SORTCODE(result);
}
/**
- * nntp_date - Get date and time from server
- * @param nserv NNTP server
- * @param now Server time
- * @retval 0 Success
- * @retval -1 Failure
+ * nntp_mbox_open - Implements MxOps::mbox_open()
*/
-static int nntp_date(struct NntpServer *nserv, time_t *now)
+static int nntp_mbox_open(struct Context *ctx)
{
- if (nserv->hasDATE)
- {
- struct NntpData nntp_data;
- char buf[LONG_STRING];
- struct tm tm;
- memset(&tm, 0, sizeof(tm));
-
- nntp_data.nserv = nserv;
- nntp_data.group = NULL;
- mutt_str_strfcpy(buf, "DATE\r\n", sizeof(buf));
- if (nntp_query(&nntp_data, buf, sizeof(buf)) < 0)
- return -1;
+ struct NntpServer *nserv = NULL;
+ struct NntpData *nntp_data = NULL;
+ char buf[HUGE_STRING];
+ char server[LONG_STRING];
+ char *group = NULL;
+ int rc;
+ void *hc = NULL;
+ anum_t first, last, count = 0;
+ struct Url url;
- if (sscanf(buf, "111 %4d%2d%2d%2d%2d%2d%*s", &tm.tm_year, &tm.tm_mon,
- &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6)
- {
- tm.tm_year -= 1900;
- tm.tm_mon--;
- *now = timegm(&tm);
- if (*now >= 0)
- {
- mutt_debug(1, "server time is %lu\n", *now);
- return 0;
- }
- }
+ mutt_str_strfcpy(buf, ctx->mailbox->path, sizeof(buf));
+ if (url_parse(&url, buf) < 0 || !url.host || !url.path ||
+ !(url.scheme == U_NNTP || url.scheme == U_NNTPS))
+ {
+ url_free(&url);
+ mutt_error(_("%s is an invalid newsgroup specification"), ctx->mailbox->path);
+ return -1;
}
- time(now);
- return 0;
-}
-
-/**
- * nntp_active_fetch - Fetch list of all newsgroups from server
- * @param nserv NNTP server
- * @param new Mark the groups as new
- * @retval 0 Success
- * @retval -1 Failure
- */
-int nntp_active_fetch(struct NntpServer *nserv, bool new)
-{
- struct NntpData nntp_data;
- char msg[STRING];
- char buf[LONG_STRING];
- unsigned int i;
- int rc;
- snprintf(msg, sizeof(msg), _("Loading list of groups from server %s..."),
- nserv->conn->account.host);
- mutt_message(msg);
- if (nntp_date(nserv, &nserv->newgroups_time) < 0)
+ group = url.path;
+ url.path = strchr(url.path, '\0');
+ url_tostring(&url, server, sizeof(server), 0);
+ nserv = nntp_select_server(ctx->mailbox, server, true);
+ url_free(&url);
+ if (!nserv)
return -1;
+ CurrentNewsSrv = nserv;
- nntp_data.nserv = nserv;
- nntp_data.group = NULL;
- i = nserv->groups_num;
- mutt_str_strfcpy(buf, "LIST\r\n", sizeof(buf));
- rc = nntp_fetch_lines(&nntp_data, buf, sizeof(buf), msg, nntp_add_group, nserv);
- if (rc)
+ /* find news group data structure */
+ nntp_data = mutt_hash_find(nserv->groups_hash, group);
+ if (!nntp_data)
{
- if (rc > 0)
- {
- mutt_error("LIST: %s", buf);
- }
+ nntp_newsrc_close(nserv);
+ mutt_error(_("Newsgroup %s not found on the server"), group);
+ return -1;
+ }
+
+ mutt_bit_unset(ctx->mailbox->rights, MUTT_ACL_INSERT);
+ if (!nntp_data->newsrc_ent && !nntp_data->subscribed && !SaveUnsubscribed)
+ ctx->mailbox->readonly = true;
+
+ /* select newsgroup */
+ mutt_message(_("Selecting %s..."), group);
+ buf[0] = '\0';
+ if (nntp_query(nntp_data, buf, sizeof(buf)) < 0)
+ {
+ nntp_newsrc_close(nserv);
return -1;
}
- if (new)
+ /* newsgroup not found, remove it */
+ if (mutt_str_strncmp("411", buf, 3) == 0)
{
- for (; i < nserv->groups_num; i++)
+ mutt_error(_("Newsgroup %s has been removed from the server"), nntp_data->group);
+ if (!nntp_data->deleted)
{
- struct NntpData *data = nserv->groups_list[i];
- data->new = true;
+ nntp_data->deleted = true;
+ nntp_active_save_cache(nserv);
+ }
+ if (nntp_data->newsrc_ent && !nntp_data->subscribed && !SaveUnsubscribed)
+ {
+ FREE(&nntp_data->newsrc_ent);
+ nntp_data->newsrc_len = 0;
+ nntp_delete_group_cache(nntp_data);
+ nntp_newsrc_update(nserv);
}
}
- for (i = 0; i < nserv->groups_num; i++)
+ /* parse newsgroup info */
+ else
{
- struct NntpData *data = nserv->groups_list[i];
+ if (sscanf(buf, "211 " ANUM " " ANUM " " ANUM, &count, &first, &last) != 3)
+ {
+ nntp_newsrc_close(nserv);
+ mutt_error("GROUP: %s", buf);
+ return -1;
+ }
+ nntp_data->first_message = first;
+ nntp_data->last_message = last;
+ nntp_data->deleted = false;
- if (data && data->deleted && !data->newsrc_ent)
+ /* get description if empty */
+ if (NntpLoadDescription && !nntp_data->desc)
{
- nntp_delete_group_cache(data);
- mutt_hash_delete(nserv->groups_hash, data->group, NULL);
- nserv->groups_list[i] = NULL;
+ if (get_description(nntp_data, NULL, NULL) < 0)
+ {
+ nntp_newsrc_close(nserv);
+ return -1;
+ }
+ if (nntp_data->desc)
+ nntp_active_save_cache(nserv);
}
}
- if (NntpLoadDescription)
- rc = get_description(&nntp_data, "*", _("Loading descriptions..."));
+ time(&nserv->check_time);
+ ctx->mailbox->data = nntp_data;
+ if (!nntp_data->bcache && (nntp_data->newsrc_ent || nntp_data->subscribed || SaveUnsubscribed))
+ nntp_data->bcache = mutt_bcache_open(&nserv->conn->account, nntp_data->group);
- nntp_active_save_cache(nserv);
+ /* strip off extra articles if adding context is greater than $nntp_context */
+ first = nntp_data->first_message;
+ if (NntpContext && nntp_data->last_message - first + 1 > NntpContext)
+ first = nntp_data->last_message - NntpContext + 1;
+ nntp_data->last_loaded = first ? first - 1 : 0;
+ count = nntp_data->first_message;
+ nntp_data->first_message = first;
+ nntp_bcache_update(nntp_data);
+ nntp_data->first_message = count;
+#ifdef USE_HCACHE
+ hc = nntp_hcache_open(nntp_data);
+ nntp_hcache_update(nntp_data, hc);
+#endif
+ if (!hc)
+ {
+ mutt_bit_unset(ctx->mailbox->rights, MUTT_ACL_WRITE);
+ mutt_bit_unset(ctx->mailbox->rights, MUTT_ACL_DELETE);
+ }
+ nntp_newsrc_close(nserv);
+ rc = nntp_fetch_headers(ctx, hc, first, nntp_data->last_message, 0);
+#ifdef USE_HCACHE
+ mutt_hcache_close(hc);
+#endif
if (rc < 0)
return -1;
- mutt_clear_error();
+ nntp_data->last_loaded = nntp_data->last_message;
+ nserv->newsrc_modified = false;
return 0;
}
/**
- * nntp_check_new_groups - Check for new groups/articles in subscribed groups
- * @param mailbox Mailbox
- * @param nserv NNTP server
- * @retval 1 New groups found
- * @retval 0 No new groups
- * @retval -1 Error
+ * nntp_mbox_check - Implements MxOps::mbox_check()
+ * @param ctx Mailbox
+ * @param index_hint Current message (UNUSED)
+ * @retval #MUTT_REOPENED Articles have been renumbered or removed from server
+ * @retval #MUTT_NEW_MAIL New articles found
+ * @retval 0 No change
+ * @retval -1 Lost connection
*/
-int nntp_check_new_groups(struct Mailbox *mailbox, struct NntpServer *nserv)
+static int nntp_mbox_check(struct Context *ctx, int *index_hint)
{
- struct NntpData nntp_data;
- time_t now;
- struct tm *tm = NULL;
- char buf[LONG_STRING];
- char *msg = _("Checking for new newsgroups...");
- unsigned int i;
- int rc, update_active = false;
-
- if (!nserv || !nserv->newgroups_time)
- return -1;
-
- /* check subscribed newsgroups for new articles */
- if (ShowNewNews)
- {
- mutt_message(_("Checking for new messages..."));
- for (i = 0; i < nserv->groups_num; i++)
- {
- struct NntpData *data = nserv->groups_list[i];
-
- if (data && data->subscribed)
- {
- rc = nntp_group_poll(data, 1);
- if (rc < 0)
- return -1;
- if (rc > 0)
- update_active = true;
- }
- }
- /* select current newsgroup */
- if (mailbox && (mailbox->magic == MUTT_NNTP))
- {
- buf[0] = '\0';
- if (nntp_query(mailbox->data, buf, sizeof(buf)) < 0)
- return -1;
- }
- }
- else if (nserv->newgroups_time)
- return 0;
-
- /* get list of new groups */
- mutt_message(msg);
- if (nntp_date(nserv, &now) < 0)
- return -1;
- nntp_data.nserv = nserv;
- if (mailbox && (mailbox->magic == MUTT_NNTP))
- nntp_data.group = ((struct NntpData *) mailbox->data)->group;
- else
- nntp_data.group = NULL;
- i = nserv->groups_num;
- tm = gmtime(&nserv->newgroups_time);
- snprintf(buf, sizeof(buf), "NEWGROUPS %02d%02d%02d %02d%02d%02d GMT\r\n",
- tm->tm_year % 100, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour,
- tm->tm_min, tm->tm_sec);
- rc = nntp_fetch_lines(&nntp_data, buf, sizeof(buf), msg, nntp_add_group, nserv);
- if (rc)
- {
- if (rc > 0)
- {
- mutt_error("NEWGROUPS: %s", buf);
- }
- return -1;
- }
-
- /* new groups found */
- rc = 0;
- if (nserv->groups_num != i)
+ int ret = check_mailbox(ctx);
+ if (ret == 0)
{
- int groups_num = i;
-
- nserv->newgroups_time = now;
- for (; i < nserv->groups_num; i++)
- {
- struct NntpData *data = nserv->groups_list[i];
- data->new = true;
- }
-
- /* loading descriptions */
- if (NntpLoadDescription)
- {
- unsigned int count = 0;
- struct Progress progress;
-
- mutt_progress_init(&progress, _("Loading descriptions..."),
- MUTT_PROGRESS_MSG, ReadInc, nserv->groups_num - i);
- for (i = groups_num; i < nserv->groups_num; i++)
- {
- struct NntpData *data = nserv->groups_list[i];
-
- if (get_description(data, NULL, NULL) < 0)
- return -1;
- mutt_progress_update(&progress, ++count, -1);
- }
- }
- update_active = true;
- rc = 1;
+ struct NntpData *nntp_data = ctx->mailbox->data;
+ struct NntpServer *nserv = nntp_data->nserv;
+ nntp_newsrc_close(nserv);
}
- if (update_active)
- nntp_active_save_cache(nserv);
- mutt_clear_error();
- return rc;
+ return ret;
}
/**
- * nntp_check_msgid - Fetch article by Message-ID
- * @param ctx Mailbox
- * @param msgid Message ID
- * @retval 0 Success
- * @retval 1 No such article
- * @retval -1 Error
+ * nntp_mbox_sync - Implements MxOps::mbox_sync()
+ *
+ * @note May also return values from check_mailbox()
*/
-int nntp_check_msgid(struct Context *ctx, const char *msgid)
+static int nntp_mbox_sync(struct Context *ctx, int *index_hint)
{
struct NntpData *nntp_data = ctx->mailbox->data;
- char buf[LONG_STRING];
-
- FILE *fp = mutt_file_mkstemp();
- if (!fp)
- {
- mutt_perror(_("Can't create temporary file"));
- return -1;
- }
+ int rc;
+#ifdef USE_HCACHE
+ header_cache_t *hc = NULL;
+#endif
- snprintf(buf, sizeof(buf), "HEAD %s\r\n", msgid);
- int rc = nntp_fetch_lines(nntp_data, buf, sizeof(buf), NULL, fetch_tempfile, fp);
+ /* check for new articles */
+ nntp_data->nserv->check_time = 0;
+ rc = check_mailbox(ctx);
if (rc)
+ return rc;
+
+#ifdef USE_HCACHE
+ nntp_data->last_cached = 0;
+ hc = nntp_hcache_open(nntp_data);
+#endif
+
+ for (int i = 0; i < ctx->mailbox->msg_count; i++)
{
- mutt_file_fclose(&fp);
- if (rc < 0)
- return -1;
- if (mutt_str_strncmp("430", buf, 3) == 0)
- return 1;
- mutt_error("HEAD: %s", buf);
- return -1;
- }
+ struct Header *hdr = ctx->mailbox->hdrs[i];
+ char buf[16];
- /* parse header */
- if (ctx->mailbox->msg_count == ctx->mailbox->hdrmax)
- mx_alloc_memory(ctx->mailbox);
- ctx->mailbox->hdrs[ctx->mailbox->msg_count] = mutt_header_new();
- struct Header *hdr = ctx->mailbox->hdrs[ctx->mailbox->msg_count];
- hdr->data = mutt_mem_calloc(1, sizeof(struct NntpHeaderData));
- hdr->env = mutt_rfc822_read_header(fp, hdr, false, false);
- mutt_file_fclose(&fp);
+ snprintf(buf, sizeof(buf), "%d", NHDR(hdr)->article_num);
+ if (nntp_data->bcache && hdr->deleted)
+ {
+ mutt_debug(2, "mutt_bcache_del %s\n", buf);
+ mutt_bcache_del(nntp_data->bcache, buf);
+ }
- /* get article number */
- if (hdr->env->xref)
- nntp_parse_xref(ctx->mailbox, hdr);
- else
- {
- snprintf(buf, sizeof(buf), "STAT %s\r\n", msgid);
- if (nntp_query(nntp_data, buf, sizeof(buf)) < 0)
+#ifdef USE_HCACHE
+ if (hc && (hdr->changed || hdr->deleted))
{
- mutt_header_free(&hdr);
- return -1;
+ if (hdr->deleted && !hdr->read)
+ nntp_data->unread--;
+ mutt_debug(2, "mutt_hcache_store %s\n", buf);
+ mutt_hcache_store(hc, buf, strlen(buf), hdr, 0);
}
- sscanf(buf + 4, ANUM, &NHDR(hdr)->article_num);
+#endif
}
- /* reset flags */
- hdr->read = false;
- hdr->old = false;
- hdr->deleted = false;
- hdr->changed = true;
- hdr->received = hdr->date_sent;
- hdr->index = ctx->mailbox->msg_count++;
- mx_update_context(ctx, 1);
+#ifdef USE_HCACHE
+ if (hc)
+ {
+ mutt_hcache_close(hc);
+ nntp_data->last_cached = nntp_data->last_loaded;
+ }
+#endif
+
+ /* save .newsrc entries */
+ nntp_newsrc_gen_entries(ctx);
+ nntp_newsrc_update(nntp_data->nserv);
+ nntp_newsrc_close(nntp_data->nserv);
return 0;
}
/**
- * struct ChildCtx - Keep track of the children of an article
- */
-struct ChildCtx
-{
- struct Mailbox *mailbox;
- unsigned int num;
- unsigned int max;
- anum_t *child;
-};
-
-/**
- * fetch_children - Parse XPAT line
- * @param line String to parse
- * @param data ChildCtx
+ * nntp_mbox_close - Implements MxOps::mbox_close()
* @retval 0 Always
*/
-static int fetch_children(char *line, void *data)
+static int nntp_mbox_close(struct Context *ctx)
{
- struct ChildCtx *cc = data;
- anum_t anum;
+ struct NntpData *nntp_data = ctx->mailbox->data, *nntp_tmp = NULL;
- if (!line || sscanf(line, ANUM, &anum) != 1)
+ if (!nntp_data)
return 0;
- for (unsigned int i = 0; i < cc->mailbox->msg_count; i++)
- if (NHDR(cc->mailbox->hdrs[i])->article_num == anum)
- return 0;
- if (cc->num >= cc->max)
- {
- cc->max *= 2;
- mutt_mem_realloc(&cc->child, sizeof(anum_t) * cc->max);
- }
- cc->child[cc->num++] = anum;
+
+ nntp_data->unread = ctx->mailbox->msg_unread;
+
+ nntp_acache_free(nntp_data);
+ if (!nntp_data->nserv || !nntp_data->nserv->groups_hash || !nntp_data->group)
+ return 0;
+
+ nntp_tmp = mutt_hash_find(nntp_data->nserv->groups_hash, nntp_data->group);
+ if (!nntp_tmp || nntp_tmp != nntp_data)
+ nntp_data_free(nntp_data);
return 0;
}
/**
- * nntp_check_children - Fetch children of article with the Message-ID
- * @param ctx Mailbox
- * @param msgid Message ID to find
- * @retval 0 Success
- * @retval -1 Failure
+ * nntp_msg_open - Implements MxOps::msg_open()
*/
-int nntp_check_children(struct Context *ctx, const char *msgid)
+static int nntp_msg_open(struct Context *ctx, struct Message *msg, int msgno)
{
struct NntpData *nntp_data = ctx->mailbox->data;
- struct ChildCtx cc;
- char buf[STRING];
- int rc;
- bool quiet;
- void *hc = NULL;
+ struct Header *hdr = ctx->mailbox->hdrs[msgno];
+ char article[16];
- if (!nntp_data || !nntp_data->nserv)
- return -1;
- if (nntp_data->first_message > nntp_data->last_loaded)
- return 0;
+ /* try to get article from cache */
+ struct NntpAcache *acache = &nntp_data->acache[hdr->index % NNTP_ACACHE_LEN];
+ if (acache->path)
+ {
+ if (acache->index == hdr->index)
+ {
+ msg->fp = mutt_file_fopen(acache->path, "r");
+ if (msg->fp)
+ return 0;
+ }
+ /* clear previous entry */
+ else
+ {
+ unlink(acache->path);
+ FREE(&acache->path);
+ }
+ }
+ snprintf(article, sizeof(article), "%d", NHDR(hdr)->article_num);
+ msg->fp = mutt_bcache_get(nntp_data->bcache, article);
+ if (msg->fp)
+ {
+ if (NHDR(hdr)->parsed)
+ return 0;
+ }
+ else
+ {
+ char buf[PATH_MAX];
+ /* don't try to fetch article from removed newsgroup */
+ if (nntp_data->deleted)
+ return -1;
- /* init context */
- cc.mailbox = ctx->mailbox;
- cc.num = 0;
- cc.max = 10;
- cc.child = mutt_mem_malloc(sizeof(anum_t) * cc.max);
+ /* create new cache file */
+ const char *fetch_msg = _("Fetching message...");
+ mutt_message(fetch_msg);
+ msg->fp = mutt_bcache_put(nntp_data->bcache, article);
+ if (!msg->fp)
+ {
+ mutt_mktemp(buf, sizeof(buf));
+ acache->path = mutt_str_strdup(buf);
+ acache->index = hdr->index;
+ msg->fp = mutt_file_fopen(acache->path, "w+");
+ if (!msg->fp)
+ {
+ mutt_perror(acache->path);
+ unlink(acache->path);
+ FREE(&acache->path);
+ return -1;
+ }
+ }
- /* fetch numbers of child messages */
- snprintf(buf, sizeof(buf), "XPAT References %u-%u *%s*\r\n",
- nntp_data->first_message, nntp_data->last_loaded, msgid);
- rc = nntp_fetch_lines(nntp_data, buf, sizeof(buf), NULL, fetch_children, &cc);
- if (rc)
- {
- FREE(&cc.child);
- if (rc > 0)
+ /* fetch message to cache file */
+ snprintf(buf, sizeof(buf), "ARTICLE %s\r\n",
+ NHDR(hdr)->article_num ? article : hdr->env->message_id);
+ const int rc = nntp_fetch_lines(nntp_data, buf, sizeof(buf), fetch_msg,
+ fetch_tempfile, msg->fp);
+ if (rc)
{
- if (mutt_str_strncmp("500", buf, 3) != 0)
- mutt_error("XPAT: %s", buf);
- else
+ mutt_file_fclose(&msg->fp);
+ if (acache->path)
{
- mutt_error(_("Unable to find child articles because server does not "
- "support XPAT command"));
+ unlink(acache->path);
+ FREE(&acache->path);
+ }
+ if (rc > 0)
+ {
+ if (mutt_str_strncmp(NHDR(hdr)->article_num ? "423" : "430", buf, 3) == 0)
+ {
+ mutt_error(_("Article %d not found on the server"),
+ NHDR(hdr)->article_num ? article : hdr->env->message_id);
+ }
+ else
+ mutt_error("ARTICLE: %s", buf);
}
+ return -1;
}
- return -1;
- }
- /* fetch all found messages */
- quiet = ctx->mailbox->quiet;
- ctx->mailbox->quiet = true;
-#ifdef USE_HCACHE
- hc = nntp_hcache_open(nntp_data);
-#endif
- for (int i = 0; i < cc.num; i++)
- {
- rc = nntp_fetch_headers(ctx, hc, cc.child[i], cc.child[i], 1);
- if (rc < 0)
- break;
+ if (!acache->path)
+ mutt_bcache_commit(nntp_data->bcache, article);
}
-#ifdef USE_HCACHE
- mutt_hcache_close(hc);
-#endif
- ctx->mailbox->quiet = quiet;
- FREE(&cc.child);
- return (rc < 0) ? -1 : 0;
+
+ /* replace envelope with new one
+ * hash elements must be updated because pointers will be changed */
+ if (ctx->mailbox->id_hash && hdr->env->message_id)
+ mutt_hash_delete(ctx->mailbox->id_hash, hdr->env->message_id, hdr);
+ if (ctx->mailbox->subj_hash && hdr->env->real_subj)
+ mutt_hash_delete(ctx->mailbox->subj_hash, hdr->env->real_subj, hdr);
+
+ mutt_env_free(&hdr->env);
+ hdr->env = mutt_rfc822_read_header(msg->fp, hdr, false, false);
+
+ if (ctx->mailbox->id_hash && hdr->env->message_id)
+ mutt_hash_insert(ctx->mailbox->id_hash, hdr->env->message_id, hdr);
+ if (ctx->mailbox->subj_hash && hdr->env->real_subj)
+ mutt_hash_insert(ctx->mailbox->subj_hash, hdr->env->real_subj, hdr);
+
+ /* fix content length */
+ fseek(msg->fp, 0, SEEK_END);
+ hdr->content->length = ftell(msg->fp) - hdr->content->offset;
+
+ /* this is called in neomutt before the open which fetches the message,
+ * which is probably wrong, but we just call it again here to handle
+ * the problem instead of fixing it */
+ NHDR(hdr)->parsed = true;
+ mutt_parse_mime_message(ctx, hdr);
+
+ /* these would normally be updated in mx_update_context(), but the
+ * full headers aren't parsed with overview, so the information wasn't
+ * available then */
+ if (WithCrypto)
+ hdr->security = crypt_query(hdr->content);
+
+ rewind(msg->fp);
+ mutt_clear_error();
+ return 0;
}
/**
- * nntp_compare_order - Sort to mailbox order - Implements ::sort_t
+ * nntp_msg_close - Implements MxOps::msg_close()
+ *
+ * @note May also return EOF Failure, see errno
*/
-int nntp_compare_order(const void *a, const void *b)
+static int nntp_msg_close(struct Context *ctx, struct Message *msg)
{
- struct Header **ha = (struct Header **) a;
- struct Header **hb = (struct Header **) b;
-
- anum_t na = NHDR(*ha)->article_num;
- anum_t nb = NHDR(*hb)->article_num;
- int result = (na == nb) ? 0 : (na > nb) ? 1 : -1;
- result = perform_auxsort(result, a, b);
- return SORTCODE(result);
+ return mutt_file_fclose(&msg->fp);
}
/**