NeoMutt  2025-12-11-1039-g550ac6
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
nntp.c File Reference

Usenet network mailbox type; talk to an NNTP server. More...

#include "config.h"
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <time.h>
#include <unistd.h>
#include "private.h"
#include "mutt/lib.h"
#include "config/lib.h"
#include "email/lib.h"
#include "core/lib.h"
#include "conn/lib.h"
#include "lib.h"
#include "attach/lib.h"
#include "bcache/lib.h"
#include "hcache/lib.h"
#include "hooks/lib.h"
#include "ncrypt/lib.h"
#include "progress/lib.h"
#include "question/lib.h"
#include "adata.h"
#include "edata.h"
#include "mdata.h"
#include "module_data.h"
#include "mutt_logging.h"
#include "muttlib.h"
#include "mx.h"
#include "mutt.h"
Include dependency graph for nntp.c:

Go to the source code of this file.

Data Structures

struct  FetchCtx
 Keep track when getting data from a server. More...
struct  ChildCtx
 Keep track of the children of an article. More...

Functions

void nntp_hashelem_free (int type, void *obj, intptr_t data)
 Free our hash table data - Implements hash_hdata_free_t -.
static int nntp_connect_error (struct NntpAccountData *adata)
 Signal a failed connection.
static int nntp_capabilities (struct NntpAccountData *adata)
 Get capabilities.
static int nntp_attempt_features (struct NntpAccountData *adata)
 Detect supported commands.
static int nntp_auth (struct NntpAccountData *adata)
 Get login, password and authenticate.
static int nntp_query (struct NntpMboxData *mdata, char *line, size_t linelen)
 Send data from buffer and receive answer to same buffer.
static int nntp_fetch_lines (struct NntpMboxData *mdata, char *query, size_t qlen, const char *msg, int(*func)(char *, void *), void *data)
 Read lines, calling a callback function for each.
static int fetch_description (char *line, void *data)
 Parse newsgroup description.
static int get_description (struct NntpMboxData *mdata, const char *wildmat, const char *msg)
 Fetch newsgroups descriptions.
static void nntp_parse_xref (struct Mailbox *m, struct Email *e)
 Parse cross-reference.
static int fetch_tempfile (char *line, void *data)
 Write line to temporary file.
static int fetch_numbers (char *line, void *data)
 Parse article number.
static int parse_overview_line (char *line, void *data)
 Parse overview line.
static int nntp_fetch_headers (struct Mailbox *m, void *hc, anum_t first, anum_t last, bool restore)
 Fetch headers.
static int nntp_group_poll (struct NntpMboxData *mdata, bool update_stat)
 Check newsgroup for new articles.
static enum MxStatus check_mailbox (struct Mailbox *m)
 Check current newsgroup for new articles.
static int nntp_date (struct NntpAccountData *adata, time_t *now)
 Get date and time from server.
static int fetch_children (char *line, void *data)
 Parse XPAT line.
int nntp_open_connection (struct NntpAccountData *adata)
 Connect to server, authenticate and get capabilities.
int nntp_post (struct Mailbox *m, const char *msg)
 Post article.
int nntp_active_fetch (struct NntpAccountData *adata, bool mark_new)
 Fetch list of all newsgroups from server.
int nntp_check_new_groups (struct Mailbox *m, struct NntpAccountData *adata)
 Check for new groups/articles in subscribed groups.
int nntp_check_msgid (struct Mailbox *m, const char *msgid)
 Fetch article by Message-ID.
int nntp_check_children (struct Mailbox *m, const char *msgid)
 Fetch children of article with the Message-ID.
int nntp_sort_unsorted (const struct Email *a, const struct Email *b, bool reverse)
 Restore the 'unsorted' order of emails - Implements sort_email_t -.
static bool nntp_ac_owns_path (struct Account *a, const char *path)
 Check whether an Account owns a Mailbox path - Implements MxOps::ac_owns_path() -.
static bool nntp_ac_add (struct Account *a, struct Mailbox *m)
 Add a Mailbox to an Account - Implements MxOps::ac_add() -.
static enum MxOpenReturns nntp_mbox_open (struct Mailbox *m)
 Open a Mailbox - Implements MxOps::mbox_open() -.
static enum MxStatus nntp_mbox_check (struct Mailbox *m)
 Check for new mail - Implements MxOps::mbox_check() -.
static enum MxStatus nntp_mbox_sync (struct Mailbox *m)
 Save changes to the Mailbox - Implements MxOps::mbox_sync() -.
static enum MxStatus nntp_mbox_close (struct Mailbox *m)
 Close a Mailbox - Implements MxOps::mbox_close() -.
static bool nntp_msg_open (struct Mailbox *m, struct Message *msg, struct Email *e)
 Open an email message in a Mailbox - Implements MxOps::msg_open() -.
static int nntp_msg_close (struct Mailbox *m, struct Message *msg)
 Close an email - Implements MxOps::msg_close() -.
enum MailboxType nntp_path_probe (const char *path, const struct stat *st)
 Is this an NNTP Mailbox?
static int nntp_path_canon (struct Buffer *path)
 Canonicalise a Mailbox path - Implements MxOps::path_canon() -.

Variables

static const char * OverviewFmt
 Fields to get from server, if it supports the LIST OVERVIEW.FMT feature.
const struct MxOps MxNntpOps
 NNTP Mailbox - Implements MxOps -.

Detailed Description

Usenet network mailbox type; talk to an NNTP server.

Authors
  • Richard Russon
  • Pietro Cerutti
  • Ian Zimmerman
  • Dennis Schön
  • Thomas Klausner

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, see http://www.gnu.org/licenses/.

Definition in file nntp.c.

Function Documentation

◆ nntp_connect_error()

int nntp_connect_error ( struct NntpAccountData * adata)
static

Signal a failed connection.

Parameters
adataNNTP server
Return values
-1Always

Definition at line 122 of file nntp.c.

123{
124 adata->status = NNTP_NONE;
125 mutt_error(_("Server closed connection"));
126 return -1;
127}
#define mutt_error(...)
Definition logging2.h:94
#define _(a)
Definition message.h:28
@ NNTP_NONE
No connection to server.
Definition private.h:44
unsigned int status
Connection status.
Definition adata.h:47
Here is the caller graph for this function:

◆ nntp_capabilities()

int nntp_capabilities ( struct NntpAccountData * adata)
static

Get capabilities.

Parameters
adataNNTP server
Return values
-1Error, connection is closed
0Mode is reader, capabilities set up
1Need to switch to reader mode

Definition at line 136 of file nntp.c.

137{
138 struct Connection *conn = adata->conn;
139 bool mode_reader = false;
140 char authinfo[1024] = { 0 };
141
142 adata->hasCAPABILITIES = false;
143 adata->hasSTARTTLS = false;
144 adata->hasDATE = false;
145 adata->hasLIST_NEWSGROUPS = false;
146 adata->hasLISTGROUP = false;
147 adata->hasLISTGROUPrange = false;
148 adata->hasOVER = false;
149 FREE(&adata->authenticators);
150
151 struct Buffer *buf = buf_pool_get();
152
153 if ((mutt_socket_send(conn, "CAPABILITIES\r\n") < 0) ||
154 (mutt_socket_buffer_readln(buf, conn) < 0))
155 {
156 buf_pool_release(&buf);
157 return nntp_connect_error(adata);
158 }
159
160 /* no capabilities */
161 if (!mutt_str_startswith(buf_string(buf), "101"))
162 {
163 buf_pool_release(&buf);
164 return 1;
165 }
166 adata->hasCAPABILITIES = true;
167
168 /* parse capabilities */
169 do
170 {
171 size_t plen = 0;
172 if (mutt_socket_buffer_readln(buf, conn) < 0)
173 {
174 buf_pool_release(&buf);
175 return nntp_connect_error(adata);
176 }
177 if (mutt_str_equal("STARTTLS", buf_string(buf)))
178 {
179 adata->hasSTARTTLS = true;
180 }
181 else if (mutt_str_equal("MODE-READER", buf_string(buf)))
182 {
183 mode_reader = true;
184 }
185 else if (mutt_str_equal("READER", buf_string(buf)))
186 {
187 adata->hasDATE = true;
188 adata->hasLISTGROUP = true;
189 adata->hasLISTGROUPrange = true;
190 }
191 else if ((plen = mutt_str_startswith(buf_string(buf), "AUTHINFO ")))
192 {
193 buf_addch(buf, ' ');
194 mutt_str_copy(authinfo, buf->data + plen - 1, sizeof(authinfo));
195 }
196#ifdef USE_SASL_CYRUS
197 else if ((plen = mutt_str_startswith(buf_string(buf), "SASL ")))
198 {
199 char *p = buf->data + plen;
200 while (*p == ' ')
201 p++;
202 adata->authenticators = mutt_str_dup(p);
203 }
204#endif
205 else if (mutt_str_equal("OVER", buf_string(buf)))
206 {
207 adata->hasOVER = true;
208 }
209 else if (mutt_str_startswith(buf_string(buf), "LIST "))
210 {
211 const char *p = buf_find_string(buf, " NEWSGROUPS");
212 if (p)
213 {
214 p += 11;
215 if ((*p == '\0') || (*p == ' '))
216 adata->hasLIST_NEWSGROUPS = true;
217 }
218 }
219 } while (!mutt_str_equal(".", buf_string(buf)));
220 buf_reset(buf);
221
222#ifdef USE_SASL_CYRUS
223 if (adata->authenticators && mutt_istr_find(authinfo, " SASL "))
224 buf_strcpy(buf, adata->authenticators);
225#endif
226 if (mutt_istr_find(authinfo, " USER "))
227 {
228 if (!buf_is_empty(buf))
229 buf_addch(buf, ' ');
230 buf_addstr(buf, "USER");
231 }
233 buf_pool_release(&buf);
234
235 /* current mode is reader */
236 if (adata->hasDATE)
237 return 0;
238
239 /* server is mode-switching, need to switch to reader mode */
240 if (mode_reader)
241 return 1;
242
243 mutt_socket_close(conn);
244 adata->status = NNTP_BYE;
245 mutt_error(_("Server doesn't support reader mode"));
246 return -1;
247}
const char * buf_find_string(const struct Buffer *buf, const char *s)
Return a pointer to a substring found in the buffer.
Definition buffer.c:644
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:89
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:298
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition buffer.c:248
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:233
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:401
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:666
const char * mutt_istr_find(const char *haystack, const char *needle)
Find first occurrence of string (ignoring case).
Definition string.c:528
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition string.c:234
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination).
Definition string.c:587
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition string.c:284
@ NNTP_BYE
Disconnected from server.
Definition private.h:46
static int nntp_connect_error(struct NntpAccountData *adata)
Signal a failed connection.
Definition nntp.c:122
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
int mutt_socket_close(struct Connection *conn)
Close a socket.
Definition socket.c:100
#define mutt_socket_send(conn, buf)
Definition socket.h:56
#define mutt_socket_buffer_readln(buf, conn)
Definition socket.h:60
String manipulation buffer.
Definition buffer.h:36
char * data
Pointer to data.
Definition buffer.h:37
struct Connection * conn
Connection to NNTP Server.
Definition adata.h:63
char * authenticators
Authenticators list.
Definition adata.h:52
bool hasCAPABILITIES
Server supports CAPABILITIES command.
Definition adata.h:37
bool hasSTARTTLS
Server supports STARTTLS command.
Definition adata.h:38
bool hasLISTGROUPrange
Server supports LISTGROUPrange command.
Definition adata.h:43
bool hasLISTGROUP
Server supports LISTGROUP command.
Definition adata.h:42
bool hasOVER
Server supports OVER command.
Definition adata.h:44
bool hasDATE
Server supports DATE command.
Definition adata.h:39
bool hasLIST_NEWSGROUPS
Server supports LIST_NEWSGROUPS command.
Definition adata.h:40
Here is the call graph for this function:
Here is the caller graph for this function:

◆ nntp_attempt_features()

int nntp_attempt_features ( struct NntpAccountData * adata)
static

Detect supported commands.

Parameters
adataNNTP server
Return values
0Success
-1Failure

Definition at line 255 of file nntp.c.

256{
257 struct Connection *conn = adata->conn;
258 char buf[1024] = { 0 };
259 int rc = -1;
260
261 /* no CAPABILITIES, trying DATE, LISTGROUP, LIST NEWSGROUPS */
262 if (!adata->hasCAPABILITIES)
263 {
264 if ((mutt_socket_send(conn, "DATE\r\n") < 0) ||
265 (mutt_socket_readln(buf, sizeof(buf), conn) < 0))
266 {
267 goto fail;
268 }
269 if (!mutt_str_startswith(buf, "500"))
270 adata->hasDATE = true;
271
272 if ((mutt_socket_send(conn, "LISTGROUP\r\n") < 0) ||
273 (mutt_socket_readln(buf, sizeof(buf), conn) < 0))
274 {
275 goto fail;
276 }
277 if (!mutt_str_startswith(buf, "500"))
278 adata->hasLISTGROUP = true;
279
280 if ((mutt_socket_send(conn, "LIST NEWSGROUPS +\r\n") < 0) ||
281 (mutt_socket_readln(buf, sizeof(buf), conn) < 0))
282 {
283 goto fail;
284 }
285 if (!mutt_str_startswith(buf, "500"))
286 adata->hasLIST_NEWSGROUPS = true;
287 if (mutt_str_startswith(buf, "215"))
288 {
289 do
290 {
291 if (mutt_socket_readln(buf, sizeof(buf), conn) < 0)
292 goto fail;
293 } while (!mutt_str_equal(".", buf));
294 }
295 }
296
297 /* no LIST NEWSGROUPS, trying XGTITLE */
298 if (!adata->hasLIST_NEWSGROUPS)
299 {
300 if ((mutt_socket_send(conn, "XGTITLE\r\n") < 0) ||
301 (mutt_socket_readln(buf, sizeof(buf), conn) < 0))
302 {
303 goto fail;
304 }
305 if (!mutt_str_startswith(buf, "500"))
306 adata->hasXGTITLE = true;
307 }
308
309 /* no OVER, trying XOVER */
310 if (!adata->hasOVER)
311 {
312 if ((mutt_socket_send(conn, "XOVER\r\n") < 0) ||
313 (mutt_socket_readln(buf, sizeof(buf), conn) < 0))
314 {
315 goto fail;
316 }
317 if (!mutt_str_startswith(buf, "500"))
318 adata->hasXOVER = true;
319 }
320
321 /* trying LIST OVERVIEW.FMT */
322 if (adata->hasOVER || adata->hasXOVER)
323 {
324 if ((mutt_socket_send(conn, "LIST OVERVIEW.FMT\r\n") < 0) ||
325 (mutt_socket_readln(buf, sizeof(buf), conn) < 0))
326 {
327 goto fail;
328 }
329 if (!mutt_str_startswith(buf, "215"))
330 {
332 }
333 else
334 {
335 bool cont = false;
336 size_t buflen = 2048;
337 size_t off = 0;
338 size_t b = 0;
339
340 FREE(&adata->overview_fmt);
341 adata->overview_fmt = MUTT_MEM_MALLOC(buflen, char);
342
343 while (true)
344 {
345 if ((buflen - off) < 1024)
346 {
347 buflen *= 2;
348 MUTT_MEM_REALLOC(&adata->overview_fmt, buflen, char);
349 }
350
351 const int chunk = mutt_socket_readln_d(adata->overview_fmt + off,
352 buflen - off, conn, MUTT_SOCK_LOG_HDR);
353 if (chunk < 0)
354 {
355 FREE(&adata->overview_fmt);
356 goto fail;
357 }
358
359 if (!cont && mutt_str_equal(".", adata->overview_fmt + off))
360 break;
361
362 cont = (chunk >= (buflen - off));
363 off += strlen(adata->overview_fmt + off);
364 if (!cont)
365 {
366 if (adata->overview_fmt[b] == ':')
367 {
368 memmove(adata->overview_fmt + b, adata->overview_fmt + b + 1, off - b - 1);
369 adata->overview_fmt[off - 1] = ':';
370 }
371 char *colon = strchr(adata->overview_fmt + b, ':');
372 if (!colon)
373 adata->overview_fmt[off++] = ':';
374 else if (!mutt_str_equal(colon + 1, "full"))
375 off = colon + 1 - adata->overview_fmt;
376 if (strcasecmp(adata->overview_fmt + b, "Bytes:") == 0)
377 {
378 size_t len = strlen(adata->overview_fmt + b);
379 mutt_str_copy(adata->overview_fmt + b, "Content-Length:", len + 1);
380 off = b + len;
381 }
382 adata->overview_fmt[off++] = '\0';
383 b = off;
384 }
385 }
386 adata->overview_fmt[off++] = '\0';
387 MUTT_MEM_REALLOC(&adata->overview_fmt, off, char);
388 }
389 }
390 rc = 0; // Success
391
392fail:
393 if (rc < 0)
394 nntp_connect_error(adata);
395
396 return rc;
397}
#define MUTT_MEM_REALLOC(pptr, n, type)
Definition memory.h:55
#define MUTT_MEM_MALLOC(n, type)
Definition memory.h:53
static const char * OverviewFmt
Fields to get from server, if it supports the LIST OVERVIEW.FMT feature.
Definition nntp.c:75
int mutt_socket_readln_d(char *buf, size_t buflen, struct Connection *conn, int dbg)
Read a line from a socket.
Definition socket.c:238
#define MUTT_SOCK_LOG_HDR
Log commands and headers.
Definition socket.h:52
#define mutt_socket_readln(buf, buflen, conn)
Definition socket.h:55
bool hasXOVER
Server supports XOVER command.
Definition adata.h:45
char * overview_fmt
Overview format.
Definition adata.h:53
bool hasXGTITLE
Server supports XGTITLE command.
Definition adata.h:41
Here is the call graph for this function:
Here is the caller graph for this function:

◆ nntp_auth()

int nntp_auth ( struct NntpAccountData * adata)
static

Get login, password and authenticate.

Parameters
adataNNTP server
Return values
0Success
-1Failure

Definition at line 453 of file nntp.c.

454{
455 struct Connection *conn = adata->conn;
456 char authenticators[1024] = "USER";
457 char *method = NULL;
458 char *a = NULL;
459 char *p = NULL;
460 unsigned char flags = conn->account.flags;
461 struct Buffer *buf = buf_pool_get();
462
463 const char *const c_nntp_authenticators = cs_subset_string(NeoMutt->sub, "nntp_authenticators");
464 while (true)
465 {
466 /* get login and password */
467 if ((mutt_account_getuser(&conn->account) < 0) || (conn->account.user[0] == '\0') ||
468 (mutt_account_getpass(&conn->account) < 0) || (conn->account.pass[0] == '\0'))
469 {
470 break;
471 }
472
473 /* get list of authenticators */
474 if (c_nntp_authenticators)
475 {
476 mutt_str_copy(authenticators, c_nntp_authenticators, sizeof(authenticators));
477 }
478 else if (adata->hasCAPABILITIES)
479 {
480 mutt_str_copy(authenticators, adata->authenticators, sizeof(authenticators));
481 p = authenticators;
482 while (*p)
483 {
484 if (*p == ' ')
485 *p = ':';
486 p++;
487 }
488 }
489 p = authenticators;
490 while (*p)
491 {
492 *p = mutt_toupper(*p);
493 p++;
494 }
495
496 mutt_debug(LL_DEBUG1, "available methods: %s\n", adata->authenticators);
497 a = authenticators;
498 while (true)
499 {
500 if (!a)
501 {
502 mutt_error(_("No authenticators available"));
503 break;
504 }
505
506 method = a;
507 a = strchr(a, ':');
508 if (a)
509 *a++ = '\0';
510
511 /* check authenticator */
512 if (adata->hasCAPABILITIES)
513 {
514 if (!adata->authenticators)
515 continue;
516 const char *m = mutt_istr_find(adata->authenticators, method);
517 if (!m)
518 continue;
519 if ((m > adata->authenticators) && (*(m - 1) != ' '))
520 continue;
521 m += strlen(method);
522 if ((*m != '\0') && (*m != ' '))
523 continue;
524 }
525 mutt_debug(LL_DEBUG1, "trying method %s\n", method);
526
527 /* AUTHINFO USER authentication */
528 if (mutt_str_equal(method, "USER"))
529 {
530 // L10N: (%s) is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
531 mutt_message(_("Authenticating (%s)..."), method);
532 buf_printf(buf, "AUTHINFO USER %s\r\n", conn->account.user);
533 if ((mutt_socket_send(conn, buf_string(buf)) < 0) ||
535 {
536 break;
537 }
538
539 /* authenticated, password is not required */
540 if (mutt_str_startswith(buf_string(buf), "281"))
541 {
542 buf_pool_release(&buf);
543 return 0;
544 }
545
546 /* username accepted, sending password */
547 if (mutt_str_startswith(buf_string(buf), "381"))
548 {
549 mutt_debug(MUTT_SOCK_LOG_FULL, "%d> AUTHINFO PASS *\n", conn->fd);
550 buf_printf(buf, "AUTHINFO PASS %s\r\n", conn->account.pass);
551 if ((mutt_socket_send_d(conn, buf_string(buf), MUTT_SOCK_LOG_FULL) < 0) ||
553 {
554 break;
555 }
556
557 /* authenticated */
558 if (mutt_str_startswith(buf_string(buf), "281"))
559 {
560 buf_pool_release(&buf);
561 return 0;
562 }
563 }
564
565 /* server doesn't support AUTHINFO USER, trying next method */
566 if (buf_at(buf, 0) == '5')
567 continue;
568 }
569 else
570 {
571#ifdef USE_SASL_CYRUS
572 sasl_conn_t *saslconn = NULL;
573 sasl_interact_t *interaction = NULL;
574 int rc;
575 char inbuf[1024] = { 0 };
576 const char *mech = NULL;
577 const char *client_out = NULL;
578 unsigned int client_len, len;
579
580 if (mutt_sasl_client_new(conn, &saslconn) < 0)
581 {
582 mutt_debug(LL_DEBUG1, "error allocating SASL connection\n");
583 continue;
584 }
585
586 while (true)
587 {
588 rc = sasl_client_start(saslconn, method, &interaction, &client_out,
589 &client_len, &mech);
590 if (rc != SASL_INTERACT)
591 break;
592 mutt_sasl_interact(interaction);
593 }
594 if ((rc != SASL_OK) && (rc != SASL_CONTINUE))
595 {
596 sasl_dispose(&saslconn);
597 mutt_debug(LL_DEBUG1, "error starting SASL authentication exchange\n");
598 continue;
599 }
600
601 // L10N: (%s) is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
602 mutt_message(_("Authenticating (%s)..."), method);
603 buf_printf(buf, "AUTHINFO SASL %s", method);
604
605 /* looping protocol */
606 while ((rc == SASL_CONTINUE) || ((rc == SASL_OK) && client_len))
607 {
608 /* send out client response */
609 if (client_len)
610 {
611 nntp_log_binbuf(client_out, client_len, "SASL", MUTT_SOCK_LOG_FULL);
612 if (!buf_is_empty(buf))
613 buf_addch(buf, ' ');
614 len = buf_len(buf);
615 if (sasl_encode64(client_out, client_len, buf->data + len,
616 buf->dsize - len, &len) != SASL_OK)
617 {
618 mutt_debug(LL_DEBUG1, "error base64-encoding client response\n");
619 break;
620 }
621 }
622
623 buf_addstr(buf, "\r\n");
624 if (buf_find_char(buf, ' '))
625 {
626 mutt_debug(MUTT_SOCK_LOG_CMD, "%d> AUTHINFO SASL %s%s\n", conn->fd,
627 method, client_len ? " sasl_data" : "");
628 }
629 else
630 {
631 mutt_debug(MUTT_SOCK_LOG_CMD, "%d> sasl_data\n", conn->fd);
632 }
633 client_len = 0;
634 if ((mutt_socket_send_d(conn, buf_string(buf), MUTT_SOCK_LOG_FULL) < 0) ||
635 (mutt_socket_readln_d(inbuf, sizeof(inbuf), conn, MUTT_SOCK_LOG_FULL) < 0))
636 {
637 break;
638 }
639 if (!mutt_str_startswith(inbuf, "283 ") && !mutt_str_startswith(inbuf, "383 "))
640 {
641 mutt_debug(MUTT_SOCK_LOG_FULL, "%d< %s\n", conn->fd, inbuf);
642 break;
643 }
644 inbuf[3] = '\0';
645 mutt_debug(MUTT_SOCK_LOG_FULL, "%d< %s sasl_data\n", conn->fd, inbuf);
646
647 if (mutt_str_equal("=", inbuf + 4))
648 len = 0;
649 else if (sasl_decode64(inbuf + 4, strlen(inbuf + 4), buf->data,
650 buf->dsize - 1, &len) != SASL_OK)
651 {
652 mutt_debug(LL_DEBUG1, "error base64-decoding server response\n");
653 break;
654 }
655 else
656 {
657 nntp_log_binbuf(buf_string(buf), len, "SASL", MUTT_SOCK_LOG_FULL);
658 }
659
660 while (true)
661 {
662 rc = sasl_client_step(saslconn, buf_string(buf), len, &interaction,
663 &client_out, &client_len);
664 if (rc != SASL_INTERACT)
665 break;
666 mutt_sasl_interact(interaction);
667 }
668 if (*inbuf != '3')
669 break;
670
671 buf_reset(buf);
672 } /* looping protocol */
673
674 if ((rc == SASL_OK) && (client_len == 0) && (*inbuf == '2'))
675 {
676 mutt_sasl_setup_conn(conn, saslconn);
677 buf_pool_release(&buf);
678 return 0;
679 }
680
681 /* terminate SASL session */
682 sasl_dispose(&saslconn);
683 if (conn->fd < 0)
684 break;
685 if (mutt_str_startswith(inbuf, "383 "))
686 {
687 if ((mutt_socket_send(conn, "*\r\n") < 0) ||
688 (mutt_socket_readln(inbuf, sizeof(inbuf), conn) < 0))
689 {
690 break;
691 }
692 }
693
694 /* server doesn't support AUTHINFO SASL, trying next method */
695 if (*inbuf == '5')
696 continue;
697#else
698 continue;
699#endif /* USE_SASL_CYRUS */
700 }
701
702 // L10N: %s is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
703 mutt_error(_("%s authentication failed"), method);
704 break;
705 }
706 break;
707 }
708
709 /* error */
710 adata->status = NNTP_BYE;
711 conn->account.flags = flags;
712 if (conn->fd < 0)
713 {
714 mutt_error(_("Server closed connection"));
715 }
716 else
717 {
718 mutt_socket_close(conn);
719 }
720
721 buf_pool_release(&buf);
722 return -1;
723}
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition buffer.c:168
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition buffer.c:497
char buf_at(const struct Buffer *buf, size_t offset)
Return the character at the given offset.
Definition buffer.c:674
const char * buf_find_char(const struct Buffer *buf, const char c)
Return a pointer to a char found in the buffer.
Definition buffer.c:659
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition helpers.c:291
int mutt_account_getpass(struct ConnAccount *cac)
Fetch password into ConnAccount, if necessary.
int mutt_account_getuser(struct ConnAccount *cac)
Retrieve username into ConnAccount, if necessary.
Definition connaccount.c:51
int mutt_toupper(int arg)
Wrapper for toupper(3).
Definition ctype.c:139
#define mutt_message(...)
Definition logging2.h:93
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
int mutt_sasl_interact(sasl_interact_t *interaction)
Perform an SASL interaction with the user.
Definition sasl.c:710
int mutt_sasl_client_new(struct Connection *conn, sasl_conn_t **saslconn)
Wrapper for sasl_client_new().
Definition sasl.c:612
void mutt_sasl_setup_conn(struct Connection *conn, sasl_conn_t *saslconn)
Set up an SASL connection.
Definition sasl.c:746
int mutt_socket_buffer_readln_d(struct Buffer *buf, struct Connection *conn, int dbg)
Read a line from a socket into a Buffer.
Definition socket.c:328
#define MUTT_SOCK_LOG_FULL
Log everything including full protocol.
Definition socket.h:53
#define MUTT_SOCK_LOG_CMD
Log commands only.
Definition socket.h:51
#define mutt_socket_send_d(conn, buf, dbg)
Definition socket.h:57
size_t dsize
Length of data.
Definition buffer.h:39
char user[128]
Username.
Definition connaccount.h:62
char pass[256]
Password.
Definition connaccount.h:63
MuttAccountFlags flags
Which fields are initialised, e.g. MUTT_ACCT_USER.
Definition connaccount.h:66
struct ConnAccount account
Account details: username, password, etc.
Definition connection.h:49
int fd
Socket file descriptor.
Definition connection.h:53
Container for Accounts, Notifications.
Definition neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
Here is the call graph for this function:
Here is the caller graph for this function:

◆ nntp_query()

int nntp_query ( struct NntpMboxData * mdata,
char * line,
size_t linelen )
static

Send data from buffer and receive answer to same buffer.

Parameters
mdataNNTP Mailbox data
lineBuffer containing data
linelenLength of buffer
Return values
0Success
-1Failure

Definition at line 733 of file nntp.c.

734{
735 struct NntpAccountData *adata = mdata->adata;
736 if (adata->status == NNTP_BYE)
737 return -1;
738
739 char buf[1024] = { 0 };
740 int rc = -1;
741
742 while (true)
743 {
744 if (adata->status == NNTP_OK)
745 {
746 int rc_send = 0;
747
748 if (*line)
749 {
750 rc_send = mutt_socket_send(adata->conn, line);
751 }
752 else if (mdata->group)
753 {
754 snprintf(buf, sizeof(buf), "GROUP %s\r\n", mdata->group);
755 rc_send = mutt_socket_send(adata->conn, buf);
756 }
757 if (rc_send >= 0)
758 rc_send = mutt_socket_readln(buf, sizeof(buf), adata->conn);
759 if (rc_send >= 0)
760 break;
761 }
762
763 /* reconnect */
764 while (true)
765 {
766 adata->status = NNTP_NONE;
767 if (nntp_open_connection(adata) == 0)
768 break;
769
770 snprintf(buf, sizeof(buf), _("Connection to %s lost. Reconnect?"),
771 adata->conn->account.host);
772 if (query_yesorno(buf, MUTT_YES) != MUTT_YES)
773 {
774 adata->status = NNTP_BYE;
775 goto done;
776 }
777 }
778
779 /* select newsgroup after reconnection */
780 if (mdata->group)
781 {
782 snprintf(buf, sizeof(buf), "GROUP %s\r\n", mdata->group);
783 if ((mutt_socket_send(adata->conn, buf) < 0) ||
784 (mutt_socket_readln(buf, sizeof(buf), adata->conn) < 0))
785 {
787 goto done;
788 }
789 }
790 if (*line == '\0')
791 break;
792 }
793
794 mutt_str_copy(line, buf, linelen);
795 rc = 0;
796
797done:
798 return rc;
799}
@ NNTP_OK
Connected to server.
Definition private.h:45
int nntp_open_connection(struct NntpAccountData *adata)
Connect to server, authenticate and get capabilities.
Definition nntp.c:1776
@ MUTT_YES
User answered 'Yes', or assume 'Yes'.
Definition quad.h:39
enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
Ask the user a Yes/No question.
Definition question.c:329
void * adata
Private data (for Mailbox backends).
Definition account.h:42
NNTP-specific Account data -.
Definition adata.h:36
char * group
Name of newsgroup.
Definition mdata.h:35
struct NntpAccountData * adata
Account data.
Definition mdata.h:48
Here is the call graph for this function:
Here is the caller graph for this function:

◆ nntp_fetch_lines()

int nntp_fetch_lines ( struct NntpMboxData * mdata,
char * query,
size_t qlen,
const char * msg,
int(* func )(char *, void *),
void * data )
static

Read lines, calling a callback function for each.

Parameters
mdataNNTP Mailbox data
queryQuery to match
qlenLength of query
msgProgress message (OPTIONAL)
funcCallback function
dataData for callback function
Return values
0Success
1Bad response (answer in query buffer)
-1Connection lost
-2Error in func(*line, *data)

This function calls func(*line, *data) for each received line, func(NULL, *data) if rewind(*data) needs, exits when fail or done:

Definition at line 817 of file nntp.c.

819{
820 bool done = false;
821 int rc;
822
823 while (!done)
824 {
825 char buf[1024] = { 0 };
826 char *line = NULL;
827 unsigned int lines = 0;
828 size_t off = 0;
829 struct Progress *progress = NULL;
830
831 mutt_str_copy(buf, query, sizeof(buf));
832 if (nntp_query(mdata, buf, sizeof(buf)) < 0)
833 return -1;
834 if (buf[0] != '2')
835 {
836 mutt_str_copy(query, buf, qlen);
837 return 1;
838 }
839
840 line = MUTT_MEM_MALLOC(sizeof(buf), char);
841 rc = 0;
842
843 if (msg)
844 {
845 progress = progress_new(MUTT_PROGRESS_READ, 0);
846 progress_set_message(progress, "%s", msg);
847 }
848
849 while (true)
850 {
851 char *p = NULL;
852 int chunk = mutt_socket_readln_d(buf, sizeof(buf), mdata->adata->conn, MUTT_SOCK_LOG_FULL);
853 if (chunk < 0)
854 {
855 mdata->adata->status = NNTP_NONE;
856 break;
857 }
858
859 p = buf;
860 if (!off && (buf[0] == '.'))
861 {
862 if (buf[1] == '\0')
863 {
864 done = true;
865 break;
866 }
867 if (buf[1] == '.')
868 p++;
869 }
870
871 mutt_str_copy(line + off, p, sizeof(buf));
872
873 if (chunk >= sizeof(buf))
874 {
875 off += strlen(p);
876 }
877 else
878 {
879 progress_update(progress, ++lines, -1);
880
881 if ((rc == 0) && (func(line, data) < 0))
882 rc = -2;
883 off = 0;
884 }
885
886 MUTT_MEM_REALLOC(&line, off + sizeof(buf), char);
887 }
888 FREE(&line);
889 func(NULL, data);
890 progress_free(&progress);
891 }
892
893 return rc;
894}
static int nntp_query(struct NntpMboxData *mdata, char *line, size_t linelen)
Send data from buffer and receive answer to same buffer.
Definition nntp.c:733
@ MUTT_PROGRESS_READ
Progress tracks elements, according to $read_inc.
Definition lib.h:84
struct Progress * progress_new(enum ProgressType type, size_t size)
Create a new Progress Bar.
Definition progress.c:139
void progress_free(struct Progress **ptr)
Free a Progress Bar.
Definition progress.c:110
void progress_set_message(struct Progress *progress, const char *fmt,...) __attribute__((__format__(__printf__
bool progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition progress.c:80
Here is the call graph for this function:
Here is the caller graph for this function:

◆ fetch_description()

int fetch_description ( char * line,
void * data )
static

Parse newsgroup description.

Parameters
lineString to parse
dataNNTP Server
Return values
0Always

Definition at line 902 of file nntp.c.

903{
904 if (!line)
905 return 0;
906
907 struct NntpAccountData *adata = data;
908
909 char *desc = strpbrk(line, " \t");
910 if (desc)
911 {
912 *desc++ = '\0';
913 desc += strspn(desc, " \t");
914 }
915 else
916 {
917 desc = strchr(line, '\0');
918 }
919
921 if (mdata && !mutt_str_equal(desc, mdata->desc))
922 {
923 mutt_str_replace(&mdata->desc, desc);
924 mutt_debug(LL_DEBUG2, "group: %s, desc: %s\n", line, desc);
925 }
926 return 0;
927}
void * mutt_hash_find(const struct HashTable *table, const char *strkey)
Find the HashElem data in a Hash Table element using a key.
Definition hash.c:365
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:46
void * mdata
Driver specific data.
Definition mailbox.h:134
struct HashTable * groups_hash
Hash Table: "newsgroup" -> NntpMboxData.
Definition adata.h:62
NNTP-specific Mailbox data -.
Definition mdata.h:34
char * desc
Description of newsgroup.
Definition mdata.h:36
Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_description()

int get_description ( struct NntpMboxData * mdata,
const char * wildmat,
const char * msg )
static

Fetch newsgroups descriptions.

Parameters
mdataNNTP Mailbox data
wildmatString to match
msgProgress message
Return values
0Success
1Bad response (answer in query buffer)
-1Connection lost
-2Error

Definition at line 939 of file nntp.c.

940{
941 char buf[256] = { 0 };
942 const char *cmd = NULL;
943
944 /* get newsgroup description, if possible */
945 struct NntpAccountData *adata = mdata->adata;
946 if (!wildmat)
947 wildmat = mdata->group;
948 if (adata->hasLIST_NEWSGROUPS)
949 cmd = "LIST NEWSGROUPS";
950 else if (adata->hasXGTITLE)
951 cmd = "XGTITLE";
952 else
953 return 0;
954
955 snprintf(buf, sizeof(buf), "%s %s\r\n", cmd, wildmat);
956 int rc = nntp_fetch_lines(mdata, buf, sizeof(buf), msg, fetch_description, adata);
957 if (rc > 0)
958 {
959 mutt_error("%s: %s", cmd, buf);
960 }
961 return rc;
962}
static int nntp_fetch_lines(struct NntpMboxData *mdata, char *query, size_t qlen, const char *msg, int(*func)(char *, void *), void *data)
Read lines, calling a callback function for each.
Definition nntp.c:817
static int fetch_description(char *line, void *data)
Parse newsgroup description.
Definition nntp.c:902
Here is the call graph for this function:
Here is the caller graph for this function:

◆ nntp_parse_xref()

void nntp_parse_xref ( struct Mailbox * m,
struct Email * e )
static

Parse cross-reference.

Parameters
mMailbox
eEmail

Update read flag and set article number if empty

Definition at line 971 of file nntp.c.

972{
973 struct NntpMboxData *mdata = m->mdata;
974
975 char *buf = mutt_str_dup(e->env->xref);
976 char *p = buf;
977 while (p)
978 {
979 anum_t anum = 0;
980
981 /* skip to next word */
982 p += strspn(p, " \t");
983 char *grp = p;
984
985 /* skip to end of word */
986 p = strpbrk(p, " \t");
987 if (p)
988 *p++ = '\0';
989
990 /* find colon */
991 char *colon = strchr(grp, ':');
992 if (!colon)
993 continue;
994 *colon++ = '\0';
995 if (sscanf(colon, ANUM_FMT, &anum) != 1)
996 continue;
997
998 nntp_article_status(m, e, grp, anum);
999 if (!nntp_edata_get(e)->article_num && mutt_str_equal(mdata->group, grp))
1000 nntp_edata_get(e)->article_num = anum;
1001 }
1002 FREE(&buf);
1003}
void nntp_article_status(struct Mailbox *m, struct Email *e, char *group, anum_t anum)
Get status of articles from .newsrc.
Definition newsrc.c:1151
struct NntpEmailData * nntp_edata_get(struct Email *e)
Get the private data for this Email.
Definition edata.c:60
#define ANUM_FMT
Definition lib.h:64
#define anum_t
Definition lib.h:63
struct Envelope * env
Envelope information.
Definition email.h:68
char * xref
List of cross-references.
Definition envelope.h:79
anum_t article_num
NNTP article number.
Definition edata.h:36
Here is the call graph for this function:
Here is the caller graph for this function:

◆ fetch_tempfile()

int fetch_tempfile ( char * line,
void * data )
static

Write line to temporary file.

Parameters
lineText to write
dataFILE pointer
Return values
0Success
-1Failure

Definition at line 1012 of file nntp.c.

1013{
1014 FILE *fp = data;
1015
1016 if (!line)
1017 {
1018 fseek(fp, 0, SEEK_SET);
1019 clearerr(fp);
1020 }
1021 else if ((fputs(line, fp) == EOF) || (fputc('\n', fp) == EOF))
1022 return -1;
1023 return 0;
1024}
Here is the caller graph for this function:

◆ fetch_numbers()

int fetch_numbers ( char * line,
void * data )
static

Parse article number.

Parameters
lineArticle number
dataFetchCtx
Return values
0Always

Definition at line 1032 of file nntp.c.

1033{
1034 struct FetchCtx *fc = data;
1035 anum_t anum = 0;
1036
1037 if (!line)
1038 return 0;
1039 if (sscanf(line, ANUM_FMT, &anum) != 1)
1040 return 0;
1041 if ((anum < fc->first) || (anum > fc->last))
1042 return 0;
1043 fc->messages[anum - fc->first] = 1;
1044 return 0;
1045}
Keep track when getting data from a server.
Definition nntp.c:88
anum_t first
First article number.
Definition nntp.c:90
anum_t last
Last article number.
Definition nntp.c:91
unsigned char * messages
Array of message flags.
Definition nntp.c:93
Here is the caller graph for this function:

◆ parse_overview_line()

int parse_overview_line ( char * line,
void * data )
static

Parse overview line.

Parameters
lineString to parse
dataFetchCtx
Return values
0Success
-1Failure

Definition at line 1054 of file nntp.c.

1055{
1056 if (!line || !data)
1057 return 0;
1058
1059 struct FetchCtx *fc = data;
1060 struct Mailbox *m = fc->mailbox;
1061 if (!m)
1062 return -1;
1063
1064 struct NntpMboxData *mdata = m->mdata;
1065 struct Email *e = NULL;
1066 char *header = NULL;
1067 char *field = NULL;
1068 bool save = true;
1069 anum_t anum = 0;
1070
1071 /* parse article number */
1072 field = strchr(line, '\t');
1073 if (field)
1074 *field++ = '\0';
1075 if (sscanf(line, ANUM_FMT, &anum) != 1)
1076 return 0;
1077 mutt_debug(LL_DEBUG2, "" ANUM_FMT "\n", anum);
1078
1079 /* out of bounds */
1080 if ((anum < fc->first) || (anum > fc->last))
1081 return 0;
1082
1083 /* not in LISTGROUP */
1084 if (!fc->messages[anum - fc->first])
1085 {
1086 progress_update(fc->progress, anum - fc->first + 1, -1);
1087 return 0;
1088 }
1089
1090 /* convert overview line to header */
1091 FILE *fp = mutt_file_mkstemp();
1092 if (!fp)
1093 return -1;
1094
1095 header = mdata->adata->overview_fmt;
1096 while (field)
1097 {
1098 char *b = field;
1099
1100 if (*header)
1101 {
1102 if (!strstr(header, ":full") && (fputs(header, fp) == EOF))
1103 {
1104 mutt_file_fclose(&fp);
1105 return -1;
1106 }
1107 header = strchr(header, '\0') + 1;
1108 }
1109
1110 field = strchr(field, '\t');
1111 if (field)
1112 *field++ = '\0';
1113 if ((fputs(b, fp) == EOF) || (fputc('\n', fp) == EOF))
1114 {
1115 mutt_file_fclose(&fp);
1116 return -1;
1117 }
1118 }
1119 fseek(fp, 0, SEEK_SET);
1120 clearerr(fp);
1121
1122 /* allocate memory for headers */
1124
1125 /* parse header */
1126 m->emails[m->msg_count] = email_new();
1127 e = m->emails[m->msg_count];
1128 e->env = mutt_rfc822_read_header(fp, e, false, false);
1129 e->env->newsgroups = mutt_str_dup(mdata->group);
1130 e->received = e->date_sent;
1131 mutt_file_fclose(&fp);
1132
1133#ifdef USE_HCACHE
1134 if (fc->hc)
1135 {
1136 char buf[16] = { 0 };
1137
1138 /* try to replace with header from cache */
1139 snprintf(buf, sizeof(buf), ANUM_FMT, anum);
1140 struct HCacheEntry hce = hcache_fetch_email(fc->hc, buf, strlen(buf), 0);
1141 if (hce.email)
1142 {
1143 mutt_debug(LL_DEBUG2, "hcache_fetch_email %s\n", buf);
1144 email_free(&e);
1145 e = hce.email;
1146 m->emails[m->msg_count] = e;
1147 e->edata = NULL;
1148 e->read = false;
1149 e->old = false;
1150
1151 /* skip header marked as deleted in cache */
1152 if (e->deleted && !fc->restore)
1153 {
1154 if (mdata->bcache)
1155 {
1156 mutt_debug(LL_DEBUG2, "mutt_bcache_del %s\n", buf);
1157 mutt_bcache_del(mdata->bcache, buf);
1158 }
1159 save = false;
1160 }
1161 }
1162 else
1163 {
1164 /* not cached yet, store header */
1165 mutt_debug(LL_DEBUG2, "hcache_store_email %s\n", buf);
1166 hcache_store_email(fc->hc, buf, strlen(buf), e, 0);
1167 }
1168 }
1169#endif
1170
1171 if (save)
1172 {
1173 e->index = m->msg_count++;
1174 e->read = false;
1175 e->old = false;
1176 e->deleted = false;
1177 e->edata = nntp_edata_new();
1179 nntp_edata_get(e)->article_num = anum;
1180 if (fc->restore)
1181 {
1182 e->changed = true;
1183 }
1184 else
1185 {
1186 nntp_article_status(m, e, NULL, anum);
1187 if (!e->read)
1188 nntp_parse_xref(m, e);
1189 }
1190 if (anum > mdata->last_loaded)
1191 mdata->last_loaded = anum;
1192 }
1193 else
1194 {
1195 email_free(&e);
1196 }
1197
1198 progress_update(fc->progress, anum - fc->first + 1, -1);
1199 return 0;
1200}
int mutt_bcache_del(struct BodyCache *bcache, const char *id)
Delete a file from the Body Cache.
Definition bcache.c:290
struct Email * email_new(void)
Create a new Email.
Definition email.c:77
void email_free(struct Email **ptr)
Free an Email.
Definition email.c:46
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
Parses an RFC822 header.
Definition parse.c:1358
#define mutt_file_fclose(FP)
Definition file.h:144
void nntp_edata_free(void **ptr)
Free the private Email data - Implements Email::edata_free() -.
Definition edata.c:38
struct HCacheEntry hcache_fetch_email(struct HeaderCache *hc, const char *key, size_t keylen, uint32_t uidvalidity)
Multiplexor for StoreOps::fetch.
Definition hcache.c:584
int hcache_store_email(struct HeaderCache *hc, const char *key, size_t keylen, struct Email *e, uint32_t uidvalidity)
Multiplexor for StoreOps::store.
Definition hcache.c:703
void mx_alloc_memory(struct Mailbox *m, int req_size)
Create storage for the emails.
Definition mx.c:1211
struct NntpEmailData * nntp_edata_new(void)
Create a new NntpEmailData for an Email.
Definition edata.c:50
static void nntp_parse_xref(struct Mailbox *m, struct Email *e)
Parse cross-reference.
Definition nntp.c:971
The envelope/body of an email.
Definition email.h:39
bool read
Email is read.
Definition email.h:50
void * edata
Driver-specific data.
Definition email.h:74
bool old
Email is seen, but unread.
Definition email.h:49
void(* edata_free)(void **ptr)
Definition email.h:90
bool changed
Email has been edited.
Definition email.h:77
time_t date_sent
Time when the message was sent (UTC).
Definition email.h:60
bool deleted
Email is deleted.
Definition email.h:78
int index
The absolute (unsorted) message number.
Definition email.h:110
time_t received
Time when the message was placed in the mailbox.
Definition email.h:61
char * newsgroups
List of newsgroups.
Definition envelope.h:78
struct HeaderCache * hc
Header cache.
Definition nntp.c:95
struct Progress * progress
Progress bar.
Definition nntp.c:94
struct Mailbox * mailbox
Mailbox.
Definition nntp.c:89
bool restore
Restore message headers from cache.
Definition nntp.c:92
Wrapper for Email retrieved from the header cache.
Definition lib.h:100
struct Email * email
Retrieved email.
Definition lib.h:103
A mailbox.
Definition mailbox.h:81
int msg_count
Total number of messages.
Definition mailbox.h:90
struct Email ** emails
Array of Emails.
Definition mailbox.h:98
struct BodyCache * bcache
Body cache.
Definition mdata.h:50
anum_t last_loaded
Last loaded article.
Definition mdata.h:39
#define mutt_file_mkstemp()
Definition tmp.h:36
Here is the call graph for this function:
Here is the caller graph for this function:

◆ nntp_fetch_headers()

int nntp_fetch_headers ( struct Mailbox * m,
void * hc,
anum_t first,
anum_t last,
bool restore )
static

Fetch headers.

Parameters
mMailbox
hcHeader cache
firstNumber of first header to fetch
lastNumber of last header to fetch
restoreRestore message listed as deleted
Return values
0Success
-1Failure

Definition at line 1212 of file nntp.c.

1213{
1214 if (!m)
1215 return -1;
1216
1217 struct NntpMboxData *mdata = m->mdata;
1218 struct FetchCtx fc = { 0 };
1219 struct Email *e = NULL;
1220 char buf[8192] = { 0 };
1221 int rc = 0;
1222 anum_t current;
1223 anum_t first_over = first;
1224
1225 /* if empty group or nothing to do */
1226 if (!last || (first > last))
1227 return 0;
1228
1229 /* init fetch context */
1230 fc.mailbox = m;
1231 fc.first = first;
1232 fc.last = last;
1233 fc.restore = restore;
1234 fc.messages = MUTT_MEM_CALLOC(last - first + 1, unsigned char);
1235 if (!fc.messages)
1236 return -1;
1237 fc.hc = hc;
1238
1239 /* fetch list of articles */
1240 const bool c_nntp_listgroup = cs_subset_bool(NeoMutt->sub, "nntp_listgroup");
1241 if (c_nntp_listgroup && mdata->adata->hasLISTGROUP && !mdata->deleted)
1242 {
1243 if (m->verbose)
1244 mutt_message(_("Fetching list of articles..."));
1245 if (mdata->adata->hasLISTGROUPrange)
1246 {
1247 snprintf(buf, sizeof(buf), "LISTGROUP %s " ANUM_FMT "-" ANUM_FMT "\r\n",
1248 mdata->group, first, last);
1249 }
1250 else
1251 {
1252 snprintf(buf, sizeof(buf), "LISTGROUP %s\r\n", mdata->group);
1253 }
1254 rc = nntp_fetch_lines(mdata, buf, sizeof(buf), NULL, fetch_numbers, &fc);
1255 if (rc > 0)
1256 {
1257 mutt_error("LISTGROUP: %s", buf);
1258 }
1259 if (rc == 0)
1260 {
1261 for (current = first; (current <= last); current++)
1262 {
1263 if (fc.messages[current - first])
1264 continue;
1265
1266 snprintf(buf, sizeof(buf), ANUM_FMT, current);
1267 if (mdata->bcache)
1268 {
1269 mutt_debug(LL_DEBUG2, "#1 mutt_bcache_del %s\n", buf);
1270 mutt_bcache_del(mdata->bcache, buf);
1271 }
1272
1273#ifdef USE_HCACHE
1274 if (fc.hc)
1275 {
1276 mutt_debug(LL_DEBUG2, "hcache_delete_email %s\n", buf);
1277 hcache_delete_email(fc.hc, buf, strlen(buf));
1278 }
1279#endif
1280 }
1281 }
1282 }
1283 else
1284 {
1285 for (current = first; current <= last; current++)
1286 fc.messages[current - first] = 1;
1287 }
1288
1289 /* fetching header from cache or server, or fallback to fetch overview */
1290 if (m->verbose)
1291 {
1292 fc.progress = progress_new(MUTT_PROGRESS_READ, last - first + 1);
1293 progress_set_message(fc.progress, _("Fetching message headers..."));
1294 }
1295 for (current = first; (current <= last) && (rc == 0); current++)
1296 {
1297 progress_update(fc.progress, current - first + 1, -1);
1298
1299#ifdef USE_HCACHE
1300 snprintf(buf, sizeof(buf), ANUM_FMT, current);
1301#endif
1302
1303 /* delete header from cache that does not exist on server */
1304 if (!fc.messages[current - first])
1305 continue;
1306
1307 /* allocate memory for headers */
1309
1310#ifdef USE_HCACHE
1311 /* try to fetch header from cache */
1312 struct HCacheEntry hce = hcache_fetch_email(fc.hc, buf, strlen(buf), 0);
1313 if (hce.email)
1314 {
1315 mutt_debug(LL_DEBUG2, "hcache_fetch_email %s\n", buf);
1316 e = hce.email;
1317 m->emails[m->msg_count] = e;
1318 e->edata = NULL;
1319
1320 /* skip header marked as deleted in cache */
1321 if (e->deleted && !restore)
1322 {
1323 email_free(&e);
1324 if (mdata->bcache)
1325 {
1326 mutt_debug(LL_DEBUG2, "#2 mutt_bcache_del %s\n", buf);
1327 mutt_bcache_del(mdata->bcache, buf);
1328 }
1329 continue;
1330 }
1331
1332 e->read = false;
1333 e->old = false;
1334 }
1335 else
1336#endif
1337 if (mdata->deleted)
1338 {
1339 /* don't try to fetch header from removed newsgroup */
1340 continue;
1341 }
1342 else if (mdata->adata->hasOVER || mdata->adata->hasXOVER)
1343 {
1344 /* fallback to fetch overview */
1345 if (c_nntp_listgroup && mdata->adata->hasLISTGROUP)
1346 break;
1347 else
1348 continue;
1349 }
1350 else
1351 {
1352 /* fetch header from server */
1353 FILE *fp = mutt_file_mkstemp();
1354 if (!fp)
1355 {
1356 mutt_perror(_("Can't create temporary file"));
1357 rc = -1;
1358 break;
1359 }
1360
1361 snprintf(buf, sizeof(buf), "HEAD " ANUM_FMT "\r\n", current);
1362 rc = nntp_fetch_lines(mdata, buf, sizeof(buf), NULL, fetch_tempfile, fp);
1363 if (rc)
1364 {
1365 mutt_file_fclose(&fp);
1366 if (rc < 0)
1367 break;
1368
1369 /* invalid response */
1370 if (!mutt_str_startswith(buf, "423"))
1371 {
1372 mutt_error("HEAD: %s", buf);
1373 break;
1374 }
1375
1376 /* no such article */
1377 if (mdata->bcache)
1378 {
1379 snprintf(buf, sizeof(buf), ANUM_FMT, current);
1380 mutt_debug(LL_DEBUG2, "#3 mutt_bcache_del %s\n", buf);
1381 mutt_bcache_del(mdata->bcache, buf);
1382 }
1383 rc = 0;
1384 continue;
1385 }
1386
1387 /* parse header */
1388 m->emails[m->msg_count] = email_new();
1389 e = m->emails[m->msg_count];
1390 e->env = mutt_rfc822_read_header(fp, e, false, false);
1391 e->received = e->date_sent;
1392 mutt_file_fclose(&fp);
1393 }
1394
1395 /* save header in context */
1396 e->index = m->msg_count++;
1397 e->read = false;
1398 e->old = false;
1399 e->deleted = false;
1400 e->edata = nntp_edata_new();
1402 nntp_edata_get(e)->article_num = current;
1403 if (restore)
1404 {
1405 e->changed = true;
1406 }
1407 else
1408 {
1409 nntp_article_status(m, e, NULL, nntp_edata_get(e)->article_num);
1410 if (!e->read)
1411 nntp_parse_xref(m, e);
1412 }
1413 if (current > mdata->last_loaded)
1414 mdata->last_loaded = current;
1415 first_over = current + 1;
1416 }
1417
1418 if (!c_nntp_listgroup || !mdata->adata->hasLISTGROUP)
1419 current = first_over;
1420
1421 /* fetch overview information */
1422 if ((current <= last) && (rc == 0) && !mdata->deleted)
1423 {
1424 char *cmd = mdata->adata->hasOVER ? "OVER" : "XOVER";
1425 snprintf(buf, sizeof(buf), "%s " ANUM_FMT "-" ANUM_FMT "\r\n", cmd, current, last);
1426 rc = nntp_fetch_lines(mdata, buf, sizeof(buf), NULL, parse_overview_line, &fc);
1427 if (rc > 0)
1428 {
1429 mutt_error("%s: %s", cmd, buf);
1430 }
1431 }
1432
1433 FREE(&fc.messages);
1435 if (rc != 0)
1436 return -1;
1438 return 0;
1439}
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
#define mutt_perror(...)
Definition logging2.h:95
int hcache_delete_email(struct HeaderCache *hc, const char *key, size_t keylen)
Multiplexor for StoreOps::delete_record.
Definition hcache.c:772
#define MUTT_MEM_CALLOC(n, type)
Definition memory.h:52
void mutt_clear_error(void)
Clear the message line (bottom line of screen).
static int parse_overview_line(char *line, void *data)
Parse overview line.
Definition nntp.c:1054
static int fetch_tempfile(char *line, void *data)
Write line to temporary file.
Definition nntp.c:1012
static int fetch_numbers(char *line, void *data)
Parse article number.
Definition nntp.c:1032
bool verbose
Display status messages?
Definition mailbox.h:119
bool deleted
Newsgroup is deleted.
Definition mdata.h:45
Here is the call graph for this function:
Here is the caller graph for this function:

◆ nntp_group_poll()

int nntp_group_poll ( struct NntpMboxData * mdata,
bool update_stat )
static

Check newsgroup for new articles.

Parameters
mdataNNTP Mailbox data
update_statUpdate the stats?
Return values
1New articles found
0No change
-1Lost connection

Definition at line 1449 of file nntp.c.

1450{
1451 char buf[1024] = { 0 };
1452 anum_t count = 0;
1453 anum_t first = 0;
1454 anum_t last = 0;
1455
1456 /* use GROUP command to poll newsgroup */
1457 if (nntp_query(mdata, buf, sizeof(buf)) < 0)
1458 return -1;
1459 if (sscanf(buf, "211 " ANUM_FMT " " ANUM_FMT " " ANUM_FMT, &count, &first, &last) != 3)
1460 return 0;
1461 if ((first == mdata->first_message) && (last == mdata->last_message))
1462 return 0;
1463
1464 /* articles have been renumbered */
1465 if (last < mdata->last_message)
1466 {
1467 mdata->last_cached = 0;
1468 if (mdata->newsrc_len)
1469 {
1470 MUTT_MEM_REALLOC(&mdata->newsrc_ent, 1, struct NewsrcEntry);
1471 mdata->newsrc_len = 1;
1472 mdata->newsrc_ent[0].first = 1;
1473 mdata->newsrc_ent[0].last = 0;
1474 }
1475 }
1476 mdata->first_message = first;
1477 mdata->last_message = last;
1478 if (!update_stat)
1479 {
1480 return 1;
1481 }
1482 else if (!last || (!mdata->newsrc_ent && !mdata->last_cached))
1483 {
1484 /* update counters */
1485 mdata->unread = count;
1486 }
1487 else
1488 {
1490 }
1491 return 1;
1492}
void nntp_group_unread_stat(struct NntpMboxData *mdata)
Count number of unread articles using .newsrc data.
Definition newsrc.c:135
An entry in a .newsrc (subscribed newsgroups).
Definition lib.h:79
anum_t last
Last article number in run.
Definition lib.h:81
anum_t first
First article number in run.
Definition lib.h:80
anum_t last_cached
Last cached article.
Definition mdata.h:40
anum_t last_message
Last article number.
Definition mdata.h:38
struct NewsrcEntry * newsrc_ent
Newsrc entries.
Definition mdata.h:47
anum_t unread
Unread articles.
Definition mdata.h:41
unsigned int newsrc_len
Length of newsrc entry.
Definition mdata.h:46
anum_t first_message
First article number.
Definition mdata.h:37
Here is the call graph for this function:
Here is the caller graph for this function:

◆ check_mailbox()

enum MxStatus check_mailbox ( struct Mailbox * m)
static

Check current newsgroup for new articles.

Parameters
mMailbox
Return values
enumMxStatus

Leave newsrc locked

Definition at line 1501 of file nntp.c.

1502{
1503 if (!m || !m->mdata)
1504 return MX_STATUS_ERROR;
1505
1506 struct NntpMboxData *mdata = m->mdata;
1507 struct NntpAccountData *adata = mdata->adata;
1508 time_t now = mutt_date_now();
1509 enum MxStatus rc = MX_STATUS_OK;
1510 struct HeaderCache *hc = NULL;
1511
1512 const short c_nntp_poll = cs_subset_number(NeoMutt->sub, "nntp_poll");
1513 if (adata->check_time + c_nntp_poll > now)
1514 return MX_STATUS_OK;
1515
1516 mutt_message(_("Checking for new messages..."));
1517 if (nntp_newsrc_parse(adata) < 0)
1518 return MX_STATUS_ERROR;
1519
1520 adata->check_time = now;
1521 int rc2 = nntp_group_poll(mdata, false);
1522 if (rc2 < 0)
1523 {
1524 nntp_newsrc_close(adata);
1525 return -1;
1526 }
1527 if (rc2 != 0)
1529
1530 /* articles have been renumbered, remove all emails */
1531 if (mdata->last_message < mdata->last_loaded)
1532 {
1533 for (int i = 0; i < m->msg_count; i++)
1534 email_free(&m->emails[i]);
1535 m->msg_count = 0;
1536 m->msg_tagged = 0;
1537
1538 mdata->last_loaded = mdata->first_message - 1;
1539 const long c_nntp_context = cs_subset_long(NeoMutt->sub, "nntp_context");
1540 if (c_nntp_context && (mdata->last_message - mdata->last_loaded > c_nntp_context))
1541 mdata->last_loaded = mdata->last_message - c_nntp_context;
1542
1543 rc = MX_STATUS_REOPENED;
1544 }
1545
1546 /* .newsrc has been externally modified */
1547 if (adata->newsrc_modified)
1548 {
1549#ifdef USE_HCACHE
1550 unsigned char *messages = NULL;
1551 char buf[16] = { 0 };
1552 struct Email *e = NULL;
1553 anum_t first = mdata->first_message;
1554
1555 const long c_nntp_context = cs_subset_long(NeoMutt->sub, "nntp_context");
1556 if (c_nntp_context && ((mdata->last_message - first + 1) > c_nntp_context))
1557 first = mdata->last_message - c_nntp_context + 1;
1558 messages = MUTT_MEM_CALLOC(mdata->last_loaded - first + 1, unsigned char);
1559 hc = nntp_hcache_open(mdata);
1560 nntp_hcache_update(mdata, hc);
1561#endif
1562
1563 /* update flags according to .newsrc */
1564 int j = 0;
1565 for (int i = 0; i < m->msg_count; i++)
1566 {
1567 if (!m->emails[i])
1568 continue;
1569 bool flagged = false;
1570 anum_t anum = nntp_edata_get(m->emails[i])->article_num;
1571
1572#ifdef USE_HCACHE
1573 /* check hcache for flagged and deleted flags */
1574 if (hc)
1575 {
1576 if ((anum >= first) && (anum <= mdata->last_loaded))
1577 messages[anum - first] = 1;
1578
1579 snprintf(buf, sizeof(buf), ANUM_FMT, anum);
1580 struct HCacheEntry hce = hcache_fetch_email(hc, buf, strlen(buf), 0);
1581 if (hce.email)
1582 {
1583 bool deleted;
1584
1585 mutt_debug(LL_DEBUG2, "#1 hcache_fetch_email %s\n", buf);
1586 e = hce.email;
1587 e->edata = NULL;
1588 deleted = e->deleted;
1589 flagged = e->flagged;
1590 email_free(&e);
1591
1592 /* header marked as deleted, removing from context */
1593 if (deleted)
1594 {
1595 mutt_set_flag(m, m->emails[i], MUTT_TAG, false, true);
1596 email_free(&m->emails[i]);
1597 continue;
1598 }
1599 }
1600 }
1601#endif
1602
1603 if (!m->emails[i]->changed)
1604 {
1605 m->emails[i]->flagged = flagged;
1606 m->emails[i]->read = false;
1607 m->emails[i]->old = false;
1608 nntp_article_status(m, m->emails[i], NULL, anum);
1609 if (!m->emails[i]->read)
1610 nntp_parse_xref(m, m->emails[i]);
1611 }
1612 m->emails[j++] = m->emails[i];
1613 }
1614
1615#ifdef USE_HCACHE
1616 m->msg_count = j;
1617
1618 /* restore headers without "deleted" flag */
1619 for (anum_t anum = first; anum <= mdata->last_loaded; anum++)
1620 {
1621 if (messages[anum - first])
1622 continue;
1623
1624 snprintf(buf, sizeof(buf), ANUM_FMT, anum);
1625 struct HCacheEntry hce = hcache_fetch_email(hc, buf, strlen(buf), 0);
1626 if (hce.email)
1627 {
1628 mutt_debug(LL_DEBUG2, "#2 hcache_fetch_email %s\n", buf);
1630
1631 e = hce.email;
1632 m->emails[m->msg_count] = e;
1633 e->edata = NULL;
1634 if (e->deleted)
1635 {
1636 email_free(&e);
1637 if (mdata->bcache)
1638 {
1639 mutt_debug(LL_DEBUG2, "mutt_bcache_del %s\n", buf);
1640 mutt_bcache_del(mdata->bcache, buf);
1641 }
1642 continue;
1643 }
1644
1645 m->msg_count++;
1646 e->read = false;
1647 e->old = false;
1648 e->edata = nntp_edata_new();
1650 nntp_edata_get(e)->article_num = anum;
1651 nntp_article_status(m, e, NULL, anum);
1652 if (!e->read)
1653 nntp_parse_xref(m, e);
1654 }
1655 }
1656 FREE(&messages);
1657#endif
1658
1659 adata->newsrc_modified = false;
1660 rc = MX_STATUS_REOPENED;
1661 }
1662
1663 /* some emails were removed, mailboxview must be updated */
1664 if (rc == MX_STATUS_REOPENED)
1666
1667 /* fetch headers of new articles */
1668 if (mdata->last_message > mdata->last_loaded)
1669 {
1670 int oldmsgcount = m->msg_count;
1671 bool verbose = m->verbose;
1672 m->verbose = false;
1673#ifdef USE_HCACHE
1674 if (!hc)
1675 {
1676 hc = nntp_hcache_open(mdata);
1677 nntp_hcache_update(mdata, hc);
1678 }
1679#endif
1680 int old_msg_count = m->msg_count;
1681 rc2 = nntp_fetch_headers(m, hc, mdata->last_loaded + 1, mdata->last_message, false);
1682 m->verbose = verbose;
1683 if (rc2 == 0)
1684 {
1685 if (m->msg_count > old_msg_count)
1687 mdata->last_loaded = mdata->last_message;
1688 }
1689 if ((rc == MX_STATUS_OK) && (m->msg_count > oldmsgcount))
1690 rc = MX_STATUS_NEW_MAIL;
1691 }
1692
1693#ifdef USE_HCACHE
1694 hcache_close(&hc);
1695#endif
1696 if (rc != MX_STATUS_OK)
1697 nntp_newsrc_close(adata);
1699 return rc;
1700}
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition helpers.c:143
long cs_subset_long(const struct ConfigSubset *sub, const char *name)
Get a long config item by name.
Definition helpers.c:95
void mailbox_changed(struct Mailbox *m, enum NotifyMailbox action)
Notify observers of a change to a Mailbox.
Definition mailbox.c:232
@ NT_MAILBOX_INVALID
Email list was changed.
Definition mailbox.h:182
void mutt_set_flag(struct Mailbox *m, struct Email *e, enum MessageType flag, bool bf, bool upd_mbox)
Set a flag on an email.
Definition flags.c:54
void hcache_close(struct HeaderCache **ptr)
Multiplexor for StoreOps::close.
Definition hcache.c:564
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition date.c:459
@ MUTT_TAG
Tagged messages.
Definition mutt.h:99
MxStatus
Return values from mbox_check(), mbox_check_stats(), mbox_sync(), and mbox_close().
Definition mxapi.h:70
@ MX_STATUS_ERROR
An error occurred.
Definition mxapi.h:71
@ MX_STATUS_OK
No changes.
Definition mxapi.h:72
@ MX_STATUS_REOPENED
Mailbox was reopened.
Definition mxapi.h:75
@ MX_STATUS_NEW_MAIL
New mail received in Mailbox.
Definition mxapi.h:73
struct HeaderCache * nntp_hcache_open(struct NntpMboxData *mdata)
Open newsgroup hcache.
Definition newsrc.c:712
void nntp_hcache_update(struct NntpMboxData *mdata, struct HeaderCache *hc)
Remove stale cached headers.
Definition newsrc.c:736
int nntp_active_save_cache(struct NntpAccountData *adata)
Save list of all newsgroups to cache.
Definition newsrc.c:651
int nntp_newsrc_parse(struct NntpAccountData *adata)
Parse .newsrc file.
Definition newsrc.c:165
void nntp_newsrc_close(struct NntpAccountData *adata)
Unlock and close .newsrc file.
Definition newsrc.c:121
static int nntp_group_poll(struct NntpMboxData *mdata, bool update_stat)
Check newsgroup for new articles.
Definition nntp.c:1449
static int nntp_fetch_headers(struct Mailbox *m, void *hc, anum_t first, anum_t last, bool restore)
Fetch headers.
Definition nntp.c:1212
bool flagged
Marked important?
Definition email.h:47
Header Cache.
Definition lib.h:87
int msg_tagged
How many messages are tagged?
Definition mailbox.h:96
bool newsrc_modified
Newsrc file was modified.
Definition adata.h:49
time_t check_time
Last check time.
Definition adata.h:57
Here is the call graph for this function:
Here is the caller graph for this function:

◆ nntp_date()

int nntp_date ( struct NntpAccountData * adata,
time_t * now )
static

Get date and time from server.

Parameters
adataNNTP server
nowServer time
Return values
0Success
-1Failure

Definition at line 1709 of file nntp.c.

1710{
1711 if (adata->hasDATE)
1712 {
1713 struct NntpMboxData mdata = { 0 };
1714 char buf[1024] = { 0 };
1715 struct tm tm = { 0 };
1716
1717 mdata.adata = adata;
1718 mdata.group = NULL;
1719 mutt_str_copy(buf, "DATE\r\n", sizeof(buf));
1720 if (nntp_query(&mdata, buf, sizeof(buf)) < 0)
1721 return -1;
1722
1723 if (sscanf(buf, "111 %4d%2d%2d%2d%2d%2d%*s", &tm.tm_year, &tm.tm_mon,
1724 &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6)
1725 {
1726 tm.tm_year -= 1900;
1727 tm.tm_mon--;
1728 *now = timegm(&tm);
1729 if (*now >= 0)
1730 {
1731 mutt_debug(LL_DEBUG1, "server time is %llu\n", (unsigned long long) *now);
1732 return 0;
1733 }
1734 }
1735 }
1736 *now = mutt_date_now();
1737 return 0;
1738}
time_t timegm(struct tm *tm)
Convert struct tm to time_t seconds since epoch.
Definition timegm.c:70
Here is the call graph for this function:
Here is the caller graph for this function:

◆ fetch_children()

int fetch_children ( char * line,
void * data )
static

Parse XPAT line.

Parameters
lineString to parse
dataChildCtx
Return values
0Always

Definition at line 1746 of file nntp.c.

1747{
1748 struct ChildCtx *cc = data;
1749 anum_t anum = 0;
1750
1751 if (!line || (sscanf(line, ANUM_FMT, &anum) != 1))
1752 return 0;
1753 for (unsigned int i = 0; i < cc->mailbox->msg_count; i++)
1754 {
1755 struct Email *e = cc->mailbox->emails[i];
1756 if (!e)
1757 break;
1758 if (nntp_edata_get(e)->article_num == anum)
1759 return 0;
1760 }
1761 if (cc->num >= cc->max)
1762 {
1763 cc->max *= 2;
1764 MUTT_MEM_REALLOC(&cc->child, cc->max, anum_t);
1765 }
1766 cc->child[cc->num++] = anum;
1767 return 0;
1768}
Keep track of the children of an article.
Definition nntp.c:102
anum_t * child
Array of child article numbers.
Definition nntp.c:106
struct Mailbox * mailbox
Mailbox.
Definition nntp.c:103
unsigned int max
Maximum number of children.
Definition nntp.c:105
unsigned int num
Number of children.
Definition nntp.c:104
Here is the call graph for this function:
Here is the caller graph for this function:

◆ nntp_open_connection()

int nntp_open_connection ( struct NntpAccountData * adata)

Connect to server, authenticate and get capabilities.

Parameters
adataNNTP server
Return values
0Success
-1Failure

Definition at line 1776 of file nntp.c.

1777{
1778 if (adata->status == NNTP_OK)
1779 return 0;
1780 if (adata->status == NNTP_BYE)
1781 return -1;
1782 adata->status = NNTP_NONE;
1783
1784 struct Connection *conn = adata->conn;
1785 if (mutt_socket_open(conn) < 0)
1786 return -1;
1787
1788 char buf[256] = { 0 };
1789 int cap;
1790 bool posting = false, auth = true;
1791 int rc = -1;
1792
1793 if (mutt_socket_readln(buf, sizeof(buf), conn) < 0)
1794 {
1795 nntp_connect_error(adata);
1796 goto done;
1797 }
1798
1799 if (mutt_str_startswith(buf, "200"))
1800 {
1801 posting = true;
1802 }
1803 else if (!mutt_str_startswith(buf, "201"))
1804 {
1805 mutt_socket_close(conn);
1807 mutt_error("%s", buf);
1808 goto done;
1809 }
1810
1811 /* get initial capabilities */
1812 cap = nntp_capabilities(adata);
1813 if (cap < 0)
1814 goto done;
1815
1816 /* tell news server to switch to mode reader if it isn't so */
1817 if (cap > 0)
1818 {
1819 if ((mutt_socket_send(conn, "MODE READER\r\n") < 0) ||
1820 (mutt_socket_readln(buf, sizeof(buf), conn) < 0))
1821 {
1822 nntp_connect_error(adata);
1823 goto done;
1824 }
1825
1826 if (mutt_str_startswith(buf, "200"))
1827 {
1828 posting = true;
1829 }
1830 else if (mutt_str_startswith(buf, "201"))
1831 {
1832 posting = false;
1833 }
1834 else if (adata->hasCAPABILITIES)
1835 {
1836 /* error if has capabilities, ignore result if no capabilities */
1837 mutt_socket_close(conn);
1838 mutt_error(_("Could not switch to reader mode"));
1839 goto done;
1840 }
1841
1842 /* recheck capabilities after MODE READER */
1843 if (adata->hasCAPABILITIES)
1844 {
1845 cap = nntp_capabilities(adata);
1846 if (cap < 0)
1847 goto done;
1848 }
1849 }
1850
1851 mutt_message(_("Connected to %s. %s"), conn->account.host,
1852 posting ? _("Posting is ok") : _("Posting is NOT ok"));
1853 mutt_sleep(1);
1854
1855#ifdef USE_SSL
1856 /* Attempt STARTTLS if available and desired. */
1857 const bool c_ssl_force_tls = cs_subset_bool(NeoMutt->sub, "ssl_force_tls");
1858 if ((adata->use_tls != 1) && (adata->hasSTARTTLS || c_ssl_force_tls))
1859 {
1860 if (adata->use_tls == 0)
1861 {
1862 adata->use_tls = c_ssl_force_tls ||
1863 (query_quadoption(_("Secure connection with TLS?"),
1864 NeoMutt->sub, "ssl_starttls") == MUTT_YES) ?
1865 2 :
1866 1;
1867 }
1868 if (adata->use_tls == 2)
1869 {
1870 if ((mutt_socket_send(conn, "STARTTLS\r\n") < 0) ||
1871 (mutt_socket_readln(buf, sizeof(buf), conn) < 0))
1872 {
1873 nntp_connect_error(adata);
1874 goto done;
1875 }
1876 // Clear any data after the STARTTLS acknowledgement
1877 mutt_socket_empty(conn);
1878 if (!mutt_str_startswith(buf, "382"))
1879 {
1880 adata->use_tls = 0;
1881 mutt_error("STARTTLS: %s", buf);
1882 }
1883 else if (mutt_ssl_starttls(conn) != 0)
1884 {
1885 adata->use_tls = 0;
1886 adata->status = NNTP_NONE;
1887 mutt_socket_close(adata->conn);
1888 mutt_error(_("Could not negotiate TLS connection"));
1889 goto done;
1890 }
1891 else
1892 {
1893 /* recheck capabilities after STARTTLS */
1894 cap = nntp_capabilities(adata);
1895 if (cap < 0)
1896 goto done;
1897 }
1898 }
1899 }
1900#endif
1901
1902 /* authentication required? */
1903 if (conn->account.flags & MUTT_ACCT_USER)
1904 {
1905 if (!conn->account.user[0])
1906 auth = false;
1907 }
1908 else
1909 {
1910 if ((mutt_socket_send(conn, "STAT\r\n") < 0) ||
1911 (mutt_socket_readln(buf, sizeof(buf), conn) < 0))
1912 {
1913 nntp_connect_error(adata);
1914 goto done;
1915 }
1916 if (!mutt_str_startswith(buf, "480"))
1917 auth = false;
1918 }
1919
1920 /* authenticate */
1921 if (auth && (nntp_auth(adata) < 0))
1922 goto done;
1923
1924 /* get final capabilities after authentication */
1925 if (adata->hasCAPABILITIES && (auth || (cap > 0)))
1926 {
1927 cap = nntp_capabilities(adata);
1928 if (cap < 0)
1929 goto done;
1930 if (cap > 0)
1931 {
1932 mutt_socket_close(conn);
1933 mutt_error(_("Could not switch to reader mode"));
1934 goto done;
1935 }
1936 }
1937
1938 /* attempt features */
1939 if (nntp_attempt_features(adata) < 0)
1940 goto done;
1941
1942 rc = 0;
1943 adata->status = NNTP_OK;
1944
1945done:
1946 return rc;
1947}
@ MUTT_ACCT_USER
User field has been set.
Definition connaccount.h:48
int mutt_ssl_starttls(struct Connection *conn)
Negotiate TLS over an already opened connection.
Definition gnutls.c:1176
void mutt_str_remove_trailing_ws(char *s)
Trim trailing whitespace from a string.
Definition string.c:571
void mutt_sleep(short s)
Sleep for a while.
Definition muttlib.c:795
static int nntp_auth(struct NntpAccountData *adata)
Get login, password and authenticate.
Definition nntp.c:453
static int nntp_capabilities(struct NntpAccountData *adata)
Get capabilities.
Definition nntp.c:136
static int nntp_attempt_features(struct NntpAccountData *adata)
Detect supported commands.
Definition nntp.c:255
enum QuadOption query_quadoption(const char *prompt, struct ConfigSubset *sub, const char *name)
Ask the user a quad-question.
Definition question.c:384
void mutt_socket_empty(struct Connection *conn)
Clear out any queued data.
Definition socket.c:306
int mutt_socket_open(struct Connection *conn)
Simple wrapper.
Definition socket.c:76
char host[128]
Server to login to.
Definition connaccount.h:60
unsigned int use_tls
Use TLS.
Definition adata.h:46
Here is the call graph for this function:
Here is the caller graph for this function:

◆ nntp_post()

int nntp_post ( struct Mailbox * m,
const char * msg )

Post article.

Parameters
mMailbox
msgMessage to post
Return values
0Success
-1Failure

Definition at line 1956 of file nntp.c.

1957{
1959 struct NntpMboxData *mdata = NULL;
1960 struct NntpMboxData tmp_mdata = { 0 };
1961 char buf[1024] = { 0 };
1962 int rc = -1;
1963
1964 if (m && (m->type == MUTT_NNTP))
1965 {
1966 mdata = m->mdata;
1967 }
1968 else
1969 {
1970 const char *const c_news_server = cs_subset_string(NeoMutt->sub, "news_server");
1971 mod_data->current_news_srv = nntp_select_server(m, c_news_server, false);
1972 if (!mod_data->current_news_srv)
1973 goto done;
1974
1975 mdata = &tmp_mdata;
1976 mdata->adata = mod_data->current_news_srv;
1977 mdata->group = NULL;
1978 }
1979
1980 FILE *fp = mutt_file_fopen(msg, "r");
1981 if (!fp)
1982 {
1983 mutt_perror("%s", msg);
1984 goto done;
1985 }
1986
1987 mutt_str_copy(buf, "POST\r\n", sizeof(buf));
1988 if (nntp_query(mdata, buf, sizeof(buf)) < 0)
1989 {
1990 mutt_file_fclose(&fp);
1991 goto done;
1992 }
1993 if (buf[0] != '3')
1994 {
1995 mutt_error(_("Can't post article: %s"), buf);
1996 mutt_file_fclose(&fp);
1997 goto done;
1998 }
1999
2000 buf[0] = '.';
2001 buf[1] = '\0';
2002 while (fgets(buf + 1, sizeof(buf) - 2, fp))
2003 {
2004 size_t len = strlen(buf);
2005 if (buf[len - 1] == '\n')
2006 {
2007 buf[len - 1] = '\r';
2008 buf[len] = '\n';
2009 len++;
2010 buf[len] = '\0';
2011 }
2012 if (mutt_socket_send_d(mdata->adata->conn, (buf[1] == '.') ? buf : buf + 1,
2013 MUTT_SOCK_LOG_FULL) < 0)
2014 {
2015 mutt_file_fclose(&fp);
2016 nntp_connect_error(mdata->adata);
2017 goto done;
2018 }
2019 }
2020 mutt_file_fclose(&fp);
2021
2022 if (((buf[strlen(buf) - 1] != '\n') &&
2023 (mutt_socket_send_d(mdata->adata->conn, "\r\n", MUTT_SOCK_LOG_FULL) < 0)) ||
2024 (mutt_socket_send_d(mdata->adata->conn, ".\r\n", MUTT_SOCK_LOG_FULL) < 0) ||
2025 (mutt_socket_readln(buf, sizeof(buf), mdata->adata->conn) < 0))
2026 {
2027 nntp_connect_error(mdata->adata);
2028 goto done;
2029 }
2030 if (buf[0] != '2')
2031 {
2032 mutt_error(_("Can't post article: %s"), buf);
2033 goto done;
2034 }
2035 rc = 0;
2036
2037done:
2038 return rc;
2039}
@ MUTT_NNTP
'NNTP' (Usenet) Mailbox type
Definition mailbox.h:48
#define mutt_file_fopen(PATH, MODE)
Definition file.h:143
@ MODULE_ID_NNTP
ModuleNntp, Nntp
Definition module_api.h:83
void * neomutt_get_module_data(struct NeoMutt *n, enum ModuleId id)
Get the private data for a Module.
Definition neomutt.c:666
struct NntpAccountData * nntp_select_server(struct Mailbox *m, const char *server, bool leave_lock)
Open a connection to an NNTP server.
Definition newsrc.c:957
enum MailboxType type
Mailbox type.
Definition mailbox.h:104
Nntp private Module data.
Definition module_data.h:30
struct NntpAccountData * current_news_srv
Current NNTP news server.
Definition module_data.h:32
Here is the call graph for this function:
Here is the caller graph for this function:

◆ nntp_active_fetch()

int nntp_active_fetch ( struct NntpAccountData * adata,
bool mark_new )

Fetch list of all newsgroups from server.

Parameters
adataNNTP server
mark_newMark the groups as new
Return values
0Success
-1Failure

Definition at line 2048 of file nntp.c.

2049{
2050 struct NntpMboxData tmp_mdata = { 0 };
2051 char msg[256] = { 0 };
2052 char buf[1024] = { 0 };
2053 unsigned int i;
2054 int rc;
2055
2056 snprintf(msg, sizeof(msg), _("Loading list of groups from server %s..."),
2058 mutt_message("%s", msg);
2059 if (nntp_date(adata, &adata->newgroups_time) < 0)
2060 return -1;
2061
2062 tmp_mdata.adata = adata;
2063 tmp_mdata.group = NULL;
2064 i = adata->groups_num;
2065 mutt_str_copy(buf, "LIST\r\n", sizeof(buf));
2066 rc = nntp_fetch_lines(&tmp_mdata, buf, sizeof(buf), msg, nntp_add_group, adata);
2067 if (rc)
2068 {
2069 if (rc > 0)
2070 {
2071 mutt_error("LIST: %s", buf);
2072 }
2073 return -1;
2074 }
2075
2076 if (mark_new)
2077 {
2078 for (; i < adata->groups_num; i++)
2079 {
2080 struct NntpMboxData *mdata = adata->groups_list[i];
2081 mdata->has_new_mail = true;
2082 }
2083 }
2084
2085 for (i = 0; i < adata->groups_num; i++)
2086 {
2087 struct NntpMboxData *mdata = adata->groups_list[i];
2088
2089 if (mdata && mdata->deleted && !mdata->newsrc_ent)
2090 {
2092 mutt_hash_delete(adata->groups_hash, mdata->group, NULL);
2093 adata->groups_list[i] = NULL;
2094 }
2095 }
2096
2097 const bool c_nntp_load_description = cs_subset_bool(NeoMutt->sub, "nntp_load_description");
2098 if (c_nntp_load_description)
2099 rc = get_description(&tmp_mdata, "*", _("Loading descriptions..."));
2100
2102 if (rc < 0)
2103 return -1;
2105 return 0;
2106}
void mutt_hash_delete(struct HashTable *table, const char *strkey, const void *data)
Remove an element from a Hash Table.
Definition hash.c:430
void nntp_delete_group_cache(struct NntpMboxData *mdata)
Remove hcache and bcache of newsgroup.
Definition newsrc.c:814
int nntp_add_group(char *line, void *data)
Parse newsgroup.
Definition newsrc.c:575
static int nntp_date(struct NntpAccountData *adata, time_t *now)
Get date and time from server.
Definition nntp.c:1709
static int get_description(struct NntpMboxData *mdata, const char *wildmat, const char *msg)
Fetch newsgroups descriptions.
Definition nntp.c:939
time_t newgroups_time
Last newgroups request time.
Definition adata.h:56
struct NntpMboxData ** groups_list
List of newsgroups.
Definition adata.h:60
unsigned int groups_num
Number of newsgroups.
Definition adata.h:58
bool has_new_mail
Has new articles.
Definition mdata.h:43
Here is the call graph for this function:
Here is the caller graph for this function:

◆ nntp_check_new_groups()

int nntp_check_new_groups ( struct Mailbox * m,
struct NntpAccountData * adata )

Check for new groups/articles in subscribed groups.

Parameters
mMailbox
adataNNTP server
Return values
1New groups found
0No new groups
-1Error

Definition at line 2116 of file nntp.c.

2117{
2118 struct NntpMboxData tmp_mdata = { 0 };
2119 time_t now = 0;
2120 char buf[1024] = { 0 };
2121 char *msg = _("Checking for new newsgroups...");
2122 unsigned int i;
2123 int rc;
2124 int update_active = false;
2125
2126 if (!adata || !adata->newgroups_time)
2127 return -1;
2128
2129 /* check subscribed newsgroups for new articles */
2130 const bool c_show_new_news = cs_subset_bool(NeoMutt->sub, "show_new_news");
2131 if (c_show_new_news)
2132 {
2133 mutt_message(_("Checking for new messages..."));
2134 for (i = 0; i < adata->groups_num; i++)
2135 {
2136 struct NntpMboxData *mdata = adata->groups_list[i];
2137
2138 if (mdata && mdata->subscribed)
2139 {
2140 rc = nntp_group_poll(mdata, true);
2141 if (rc < 0)
2142 return -1;
2143 if (rc > 0)
2144 update_active = true;
2145 }
2146 }
2147 }
2148 else if (adata->newgroups_time)
2149 {
2150 return 0;
2151 }
2152
2153 /* get list of new groups */
2154 mutt_message("%s", msg);
2155 if (nntp_date(adata, &now) < 0)
2156 return -1;
2157 tmp_mdata.adata = adata;
2158 if (m && m->mdata)
2159 tmp_mdata.group = ((struct NntpMboxData *) m->mdata)->group;
2160 else
2161 tmp_mdata.group = NULL;
2162 i = adata->groups_num;
2163 struct tm tm = mutt_date_gmtime(adata->newgroups_time);
2164 snprintf(buf, sizeof(buf), "NEWGROUPS %02d%02d%02d %02d%02d%02d GMT\r\n",
2165 tm.tm_year % 100, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
2166 rc = nntp_fetch_lines(&tmp_mdata, buf, sizeof(buf), msg, nntp_add_group, adata);
2167 if (rc)
2168 {
2169 if (rc > 0)
2170 {
2171 mutt_error("NEWGROUPS: %s", buf);
2172 }
2173 return -1;
2174 }
2175
2176 /* new groups found */
2177 rc = 0;
2178 if (adata->groups_num != i)
2179 {
2180 int groups_num = i;
2181
2182 adata->newgroups_time = now;
2183 for (; i < adata->groups_num; i++)
2184 {
2185 struct NntpMboxData *mdata = adata->groups_list[i];
2186 mdata->has_new_mail = true;
2187 }
2188
2189 /* loading descriptions */
2190 const bool c_nntp_load_description = cs_subset_bool(NeoMutt->sub, "nntp_load_description");
2191 if (c_nntp_load_description)
2192 {
2193 unsigned int count = 0;
2194 struct Progress *progress = progress_new(MUTT_PROGRESS_READ, adata->groups_num - i);
2195 progress_set_message(progress, _("Loading descriptions..."));
2196
2197 for (i = groups_num; i < adata->groups_num; i++)
2198 {
2199 struct NntpMboxData *mdata = adata->groups_list[i];
2200
2201 if (get_description(mdata, NULL, NULL) < 0)
2202 {
2203 progress_free(&progress);
2204 return -1;
2205 }
2206 progress_update(progress, ++count, -1);
2207 }
2208 progress_free(&progress);
2209 }
2210 update_active = true;
2211 rc = 1;
2212 }
2213 if (update_active)
2216 return rc;
2217}
struct tm mutt_date_gmtime(time_t t)
Converts calendar time to a broken-down time structure expressed in UTC timezone.
Definition date.c:933
Here is the call graph for this function:
Here is the caller graph for this function:

◆ nntp_check_msgid()

int nntp_check_msgid ( struct Mailbox * m,
const char * msgid )

Fetch article by Message-ID.

Parameters
mMailbox
msgidMessage ID
Return values
0Success
1No such article
-1Error

Definition at line 2227 of file nntp.c.

2228{
2229 if (!m)
2230 return -1;
2231
2232 struct NntpMboxData *mdata = m->mdata;
2233 char buf[1024] = { 0 };
2234
2235 FILE *fp = mutt_file_mkstemp();
2236 if (!fp)
2237 {
2238 mutt_perror(_("Can't create temporary file"));
2239 return -1;
2240 }
2241
2242 snprintf(buf, sizeof(buf), "HEAD %s\r\n", msgid);
2243 int rc = nntp_fetch_lines(mdata, buf, sizeof(buf), NULL, fetch_tempfile, fp);
2244 if (rc)
2245 {
2246 mutt_file_fclose(&fp);
2247 if (rc < 0)
2248 return -1;
2249 if (mutt_str_startswith(buf, "430"))
2250 return 1;
2251 mutt_error("HEAD: %s", buf);
2252 return -1;
2253 }
2254
2255 /* parse header */
2257 m->emails[m->msg_count] = email_new();
2258 struct Email *e = m->emails[m->msg_count];
2259 e->edata = nntp_edata_new();
2261 e->env = mutt_rfc822_read_header(fp, e, false, false);
2262 mutt_file_fclose(&fp);
2263
2264 /* get article number */
2265 if (e->env->xref)
2266 {
2267 nntp_parse_xref(m, e);
2268 }
2269 else
2270 {
2271 snprintf(buf, sizeof(buf), "STAT %s\r\n", msgid);
2272 if (nntp_query(mdata, buf, sizeof(buf)) < 0)
2273 {
2274 email_free(&e);
2275 return -1;
2276 }
2277 sscanf(buf + 4, ANUM_FMT, &nntp_edata_get(e)->article_num);
2278 }
2279
2280 /* reset flags */
2281 e->read = false;
2282 e->old = false;
2283 e->deleted = false;
2284 e->changed = true;
2285 e->received = e->date_sent;
2286 e->index = m->msg_count++;
2288 return 0;
2289}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ nntp_check_children()

int nntp_check_children ( struct Mailbox * m,
const char * msgid )

Fetch children of article with the Message-ID.

Parameters
mMailbox
msgidMessage ID to find
Return values
0Success
-1Failure

Definition at line 2298 of file nntp.c.

2299{
2300 if (!m)
2301 return -1;
2302
2303 struct NntpMboxData *mdata = m->mdata;
2304 char buf[256] = { 0 };
2305 int rc;
2306 struct HeaderCache *hc = NULL;
2307
2308 if (!mdata || !mdata->adata)
2309 return -1;
2310 if (mdata->first_message > mdata->last_loaded)
2311 return 0;
2312
2313 /* init context */
2314 struct ChildCtx cc = { 0 };
2315 cc.mailbox = m;
2316 cc.num = 0;
2317 cc.max = 10;
2318 cc.child = MUTT_MEM_MALLOC(cc.max, anum_t);
2319
2320 /* fetch numbers of child messages */
2321 snprintf(buf, sizeof(buf), "XPAT References " ANUM_FMT "-" ANUM_FMT " *%s*\r\n",
2322 mdata->first_message, mdata->last_loaded, msgid);
2323 rc = nntp_fetch_lines(mdata, buf, sizeof(buf), NULL, fetch_children, &cc);
2324 if (rc)
2325 {
2326 FREE(&cc.child);
2327 if (rc > 0)
2328 {
2329 if (!mutt_str_startswith(buf, "500"))
2330 {
2331 mutt_error("XPAT: %s", buf);
2332 }
2333 else
2334 {
2335 mutt_error(_("Unable to find child articles because server does not support XPAT command"));
2336 }
2337 }
2338 return -1;
2339 }
2340
2341 /* fetch all found messages */
2342 bool verbose = m->verbose;
2343 m->verbose = false;
2344#ifdef USE_HCACHE
2345 hc = nntp_hcache_open(mdata);
2346#endif
2347 int old_msg_count = m->msg_count;
2348 for (int i = 0; i < cc.num; i++)
2349 {
2350 rc = nntp_fetch_headers(m, hc, cc.child[i], cc.child[i], true);
2351 if (rc < 0)
2352 break;
2353 }
2354 if (m->msg_count > old_msg_count)
2356
2357#ifdef USE_HCACHE
2358 hcache_close(&hc);
2359#endif
2360 m->verbose = verbose;
2361 FREE(&cc.child);
2362 return (rc < 0) ? -1 : 0;
2363}
static int fetch_children(char *line, void *data)
Parse XPAT line.
Definition nntp.c:1746
Here is the call graph for this function:
Here is the caller graph for this function:

Variable Documentation

◆ OverviewFmt

const char* OverviewFmt
static
Initial value:
= "Subject:\0"
"From:\0"
"Date:\0"
"Message-ID:\0"
"References:\0"
"Content-Length:\0"
"Lines:\0"
"\0"

Fields to get from server, if it supports the LIST OVERVIEW.FMT feature.

Definition at line 75 of file nntp.c.