NeoMutt  2025-12-11-694-ga89709
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 "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

struct NntpAccountDataCurrentNewsSrv = NULL
 Current news server.
 
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()

static int nntp_connect_error ( struct NntpAccountData * adata)
static

Signal a failed connection.

Parameters
adataNNTP server
Return values
-1Always

Definition at line 124 of file nntp.c.

125{
126 adata->status = NNTP_NONE;
127 mutt_error(_("Server closed connection"));
128 return -1;
129}
#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()

static 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 138 of file nntp.c.

139{
140 struct Connection *conn = adata->conn;
141 bool mode_reader = false;
142 char authinfo[1024] = { 0 };
143
144 adata->hasCAPABILITIES = false;
145 adata->hasSTARTTLS = false;
146 adata->hasDATE = false;
147 adata->hasLIST_NEWSGROUPS = false;
148 adata->hasLISTGROUP = false;
149 adata->hasLISTGROUPrange = false;
150 adata->hasOVER = false;
151 FREE(&adata->authenticators);
152
153 struct Buffer *buf = buf_pool_get();
154
155 if ((mutt_socket_send(conn, "CAPABILITIES\r\n") < 0) ||
156 (mutt_socket_buffer_readln(buf, conn) < 0))
157 {
158 buf_pool_release(&buf);
159 return nntp_connect_error(adata);
160 }
161
162 /* no capabilities */
163 if (!mutt_str_startswith(buf_string(buf), "101"))
164 {
165 buf_pool_release(&buf);
166 return 1;
167 }
168 adata->hasCAPABILITIES = true;
169
170 /* parse capabilities */
171 do
172 {
173 size_t plen = 0;
174 if (mutt_socket_buffer_readln(buf, conn) < 0)
175 {
176 buf_pool_release(&buf);
177 return nntp_connect_error(adata);
178 }
179 if (mutt_str_equal("STARTTLS", buf_string(buf)))
180 {
181 adata->hasSTARTTLS = true;
182 }
183 else if (mutt_str_equal("MODE-READER", buf_string(buf)))
184 {
185 mode_reader = true;
186 }
187 else if (mutt_str_equal("READER", buf_string(buf)))
188 {
189 adata->hasDATE = true;
190 adata->hasLISTGROUP = true;
191 adata->hasLISTGROUPrange = true;
192 }
193 else if ((plen = mutt_str_startswith(buf_string(buf), "AUTHINFO ")))
194 {
195 buf_addch(buf, ' ');
196 mutt_str_copy(authinfo, buf->data + plen - 1, sizeof(authinfo));
197 }
198#ifdef USE_SASL_CYRUS
199 else if ((plen = mutt_str_startswith(buf_string(buf), "SASL ")))
200 {
201 char *p = buf->data + plen;
202 while (*p == ' ')
203 p++;
204 adata->authenticators = mutt_str_dup(p);
205 }
206#endif
207 else if (mutt_str_equal("OVER", buf_string(buf)))
208 {
209 adata->hasOVER = true;
210 }
211 else if (mutt_str_startswith(buf_string(buf), "LIST "))
212 {
213 const char *p = buf_find_string(buf, " NEWSGROUPS");
214 if (p)
215 {
216 p += 11;
217 if ((*p == '\0') || (*p == ' '))
218 adata->hasLIST_NEWSGROUPS = true;
219 }
220 }
221 } while (!mutt_str_equal(".", buf_string(buf)));
222 buf_reset(buf);
223
224#ifdef USE_SASL_CYRUS
225 if (adata->authenticators && mutt_istr_find(authinfo, " SASL "))
226 buf_strcpy(buf, adata->authenticators);
227#endif
228 if (mutt_istr_find(authinfo, " USER "))
229 {
230 if (!buf_is_empty(buf))
231 buf_addch(buf, ' ');
232 buf_addstr(buf, "USER");
233 }
235 buf_pool_release(&buf);
236
237 /* current mode is reader */
238 if (adata->hasDATE)
239 return 0;
240
241 /* server is mode-switching, need to switch to reader mode */
242 if (mode_reader)
243 return 1;
244
245 mutt_socket_close(conn);
246 adata->status = NNTP_BYE;
247 mutt_error(_("Server doesn't support reader mode"));
248 return -1;
249}
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:638
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:76
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:291
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition buffer.c:241
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:226
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:395
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:665
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:586
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:124
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()

static int nntp_attempt_features ( struct NntpAccountData * adata)
static

Detect supported commands.

Parameters
adataNNTP server
Return values
0Success
-1Failure

Definition at line 257 of file nntp.c.

258{
259 struct Connection *conn = adata->conn;
260 char buf[1024] = { 0 };
261 int rc = -1;
262
263 /* no CAPABILITIES, trying DATE, LISTGROUP, LIST NEWSGROUPS */
264 if (!adata->hasCAPABILITIES)
265 {
266 if ((mutt_socket_send(conn, "DATE\r\n") < 0) ||
267 (mutt_socket_readln(buf, sizeof(buf), conn) < 0))
268 {
269 goto fail;
270 }
271 if (!mutt_str_startswith(buf, "500"))
272 adata->hasDATE = true;
273
274 if ((mutt_socket_send(conn, "LISTGROUP\r\n") < 0) ||
275 (mutt_socket_readln(buf, sizeof(buf), conn) < 0))
276 {
277 goto fail;
278 }
279 if (!mutt_str_startswith(buf, "500"))
280 adata->hasLISTGROUP = true;
281
282 if ((mutt_socket_send(conn, "LIST NEWSGROUPS +\r\n") < 0) ||
283 (mutt_socket_readln(buf, sizeof(buf), conn) < 0))
284 {
285 goto fail;
286 }
287 if (!mutt_str_startswith(buf, "500"))
288 adata->hasLIST_NEWSGROUPS = true;
289 if (mutt_str_startswith(buf, "215"))
290 {
291 do
292 {
293 if (mutt_socket_readln(buf, sizeof(buf), conn) < 0)
294 goto fail;
295 } while (!mutt_str_equal(".", buf));
296 }
297 }
298
299 /* no LIST NEWSGROUPS, trying XGTITLE */
300 if (!adata->hasLIST_NEWSGROUPS)
301 {
302 if ((mutt_socket_send(conn, "XGTITLE\r\n") < 0) ||
303 (mutt_socket_readln(buf, sizeof(buf), conn) < 0))
304 {
305 goto fail;
306 }
307 if (!mutt_str_startswith(buf, "500"))
308 adata->hasXGTITLE = true;
309 }
310
311 /* no OVER, trying XOVER */
312 if (!adata->hasOVER)
313 {
314 if ((mutt_socket_send(conn, "XOVER\r\n") < 0) ||
315 (mutt_socket_readln(buf, sizeof(buf), conn) < 0))
316 {
317 goto fail;
318 }
319 if (!mutt_str_startswith(buf, "500"))
320 adata->hasXOVER = true;
321 }
322
323 /* trying LIST OVERVIEW.FMT */
324 if (adata->hasOVER || adata->hasXOVER)
325 {
326 if ((mutt_socket_send(conn, "LIST OVERVIEW.FMT\r\n") < 0) ||
327 (mutt_socket_readln(buf, sizeof(buf), conn) < 0))
328 {
329 goto fail;
330 }
331 if (!mutt_str_startswith(buf, "215"))
332 {
334 }
335 else
336 {
337 bool cont = false;
338 size_t buflen = 2048, off = 0, 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:77
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()

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

static 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 731 of file nntp.c.

732{
733 struct NntpAccountData *adata = mdata->adata;
734 if (adata->status == NNTP_BYE)
735 return -1;
736
737 char buf[1024] = { 0 };
738 int rc = -1;
739
740 while (true)
741 {
742 if (adata->status == NNTP_OK)
743 {
744 int rc_send = 0;
745
746 if (*line)
747 {
748 rc_send = mutt_socket_send(adata->conn, line);
749 }
750 else if (mdata->group)
751 {
752 snprintf(buf, sizeof(buf), "GROUP %s\r\n", mdata->group);
753 rc_send = mutt_socket_send(adata->conn, buf);
754 }
755 if (rc_send >= 0)
756 rc_send = mutt_socket_readln(buf, sizeof(buf), adata->conn);
757 if (rc_send >= 0)
758 break;
759 }
760
761 /* reconnect */
762 while (true)
763 {
764 adata->status = NNTP_NONE;
765 if (nntp_open_connection(adata) == 0)
766 break;
767
768 snprintf(buf, sizeof(buf), _("Connection to %s lost. Reconnect?"),
769 adata->conn->account.host);
770 if (query_yesorno(buf, MUTT_YES) != MUTT_YES)
771 {
772 adata->status = NNTP_BYE;
773 goto done;
774 }
775 }
776
777 /* select newsgroup after reconnection */
778 if (mdata->group)
779 {
780 snprintf(buf, sizeof(buf), "GROUP %s\r\n", mdata->group);
781 if ((mutt_socket_send(adata->conn, buf) < 0) ||
782 (mutt_socket_readln(buf, sizeof(buf), adata->conn) < 0))
783 {
785 goto done;
786 }
787 }
788 if (*line == '\0')
789 break;
790 }
791
792 mutt_str_copy(line, buf, linelen);
793 rc = 0;
794
795done:
796 return rc;
797}
@ 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:1767
@ 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()

static 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 815 of file nntp.c.

817{
818 bool done = false;
819 int rc;
820
821 while (!done)
822 {
823 char buf[1024] = { 0 };
824 char *line = NULL;
825 unsigned int lines = 0;
826 size_t off = 0;
827 struct Progress *progress = NULL;
828
829 mutt_str_copy(buf, query, sizeof(buf));
830 if (nntp_query(mdata, buf, sizeof(buf)) < 0)
831 return -1;
832 if (buf[0] != '2')
833 {
834 mutt_str_copy(query, buf, qlen);
835 return 1;
836 }
837
838 line = MUTT_MEM_MALLOC(sizeof(buf), char);
839 rc = 0;
840
841 if (msg)
842 {
843 progress = progress_new(MUTT_PROGRESS_READ, 0);
844 progress_set_message(progress, "%s", msg);
845 }
846
847 while (true)
848 {
849 char *p = NULL;
850 int chunk = mutt_socket_readln_d(buf, sizeof(buf), mdata->adata->conn, MUTT_SOCK_LOG_FULL);
851 if (chunk < 0)
852 {
853 mdata->adata->status = NNTP_NONE;
854 break;
855 }
856
857 p = buf;
858 if (!off && (buf[0] == '.'))
859 {
860 if (buf[1] == '\0')
861 {
862 done = true;
863 break;
864 }
865 if (buf[1] == '.')
866 p++;
867 }
868
869 mutt_str_copy(line + off, p, sizeof(buf));
870
871 if (chunk >= sizeof(buf))
872 {
873 off += strlen(p);
874 }
875 else
876 {
877 progress_update(progress, ++lines, -1);
878
879 if ((rc == 0) && (func(line, data) < 0))
880 rc = -2;
881 off = 0;
882 }
883
884 MUTT_MEM_REALLOC(&line, off + sizeof(buf), char);
885 }
886 FREE(&line);
887 func(NULL, data);
888 progress_free(&progress);
889 }
890
891 return rc;
892}
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:731
@ 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()

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

Parse newsgroup description.

Parameters
lineString to parse
dataNNTP Server
Return values
0Always

Definition at line 900 of file nntp.c.

901{
902 if (!line)
903 return 0;
904
905 struct NntpAccountData *adata = data;
906
907 char *desc = strpbrk(line, " \t");
908 if (desc)
909 {
910 *desc++ = '\0';
911 desc += strspn(desc, " \t");
912 }
913 else
914 {
915 desc = strchr(line, '\0');
916 }
917
919 if (mdata && !mutt_str_equal(desc, mdata->desc))
920 {
921 mutt_str_replace(&mdata->desc, desc);
922 mutt_debug(LL_DEBUG2, "group: %s, desc: %s\n", line, desc);
923 }
924 return 0;
925}
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:364
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:46
void * mdata
Driver specific data.
Definition mailbox.h:131
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()

static 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 937 of file nntp.c.

938{
939 char buf[256] = { 0 };
940 const char *cmd = NULL;
941
942 /* get newsgroup description, if possible */
943 struct NntpAccountData *adata = mdata->adata;
944 if (!wildmat)
945 wildmat = mdata->group;
946 if (adata->hasLIST_NEWSGROUPS)
947 cmd = "LIST NEWSGROUPS";
948 else if (adata->hasXGTITLE)
949 cmd = "XGTITLE";
950 else
951 return 0;
952
953 snprintf(buf, sizeof(buf), "%s %s\r\n", cmd, wildmat);
954 int rc = nntp_fetch_lines(mdata, buf, sizeof(buf), msg, fetch_description, adata);
955 if (rc > 0)
956 {
957 mutt_error("%s: %s", cmd, buf);
958 }
959 return rc;
960}
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:815
static int fetch_description(char *line, void *data)
Parse newsgroup description.
Definition nntp.c:900
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ nntp_parse_xref()

static 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 969 of file nntp.c.

970{
971 struct NntpMboxData *mdata = m->mdata;
972
973 char *buf = mutt_str_dup(e->env->xref);
974 char *p = buf;
975 while (p)
976 {
977 anum_t anum = 0;
978
979 /* skip to next word */
980 p += strspn(p, " \t");
981 char *grp = p;
982
983 /* skip to end of word */
984 p = strpbrk(p, " \t");
985 if (p)
986 *p++ = '\0';
987
988 /* find colon */
989 char *colon = strchr(grp, ':');
990 if (!colon)
991 continue;
992 *colon++ = '\0';
993 if (sscanf(colon, ANUM_FMT, &anum) != 1)
994 continue;
995
996 nntp_article_status(m, e, grp, anum);
997 if (!nntp_edata_get(e)->article_num && mutt_str_equal(mdata->group, grp))
998 nntp_edata_get(e)->article_num = anum;
999 }
1000 FREE(&buf);
1001}
void nntp_article_status(struct Mailbox *m, struct Email *e, char *group, anum_t anum)
Get status of articles from .newsrc.
Definition newsrc.c:1144
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()

static 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 1010 of file nntp.c.

1011{
1012 FILE *fp = data;
1013
1014 if (!line)
1015 rewind(fp);
1016 else if ((fputs(line, fp) == EOF) || (fputc('\n', fp) == EOF))
1017 return -1;
1018 return 0;
1019}
+ Here is the caller graph for this function:

◆ fetch_numbers()

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

Parse article number.

Parameters
lineArticle number
dataFetchCtx
Return values
0Always

Definition at line 1027 of file nntp.c.

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

◆ parse_overview_line()

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

Parse overview line.

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

Definition at line 1049 of file nntp.c.

1050{
1051 if (!line || !data)
1052 return 0;
1053
1054 struct FetchCtx *fc = data;
1055 struct Mailbox *m = fc->mailbox;
1056 if (!m)
1057 return -1;
1058
1059 struct NntpMboxData *mdata = m->mdata;
1060 struct Email *e = NULL;
1061 char *header = NULL, *field = NULL;
1062 bool save = true;
1063 anum_t anum = 0;
1064
1065 /* parse article number */
1066 field = strchr(line, '\t');
1067 if (field)
1068 *field++ = '\0';
1069 if (sscanf(line, ANUM_FMT, &anum) != 1)
1070 return 0;
1071 mutt_debug(LL_DEBUG2, "" ANUM_FMT "\n", anum);
1072
1073 /* out of bounds */
1074 if ((anum < fc->first) || (anum > fc->last))
1075 return 0;
1076
1077 /* not in LISTGROUP */
1078 if (!fc->messages[anum - fc->first])
1079 {
1080 progress_update(fc->progress, anum - fc->first + 1, -1);
1081 return 0;
1082 }
1083
1084 /* convert overview line to header */
1085 FILE *fp = mutt_file_mkstemp();
1086 if (!fp)
1087 return -1;
1088
1089 header = mdata->adata->overview_fmt;
1090 while (field)
1091 {
1092 char *b = field;
1093
1094 if (*header)
1095 {
1096 if (!strstr(header, ":full") && (fputs(header, fp) == EOF))
1097 {
1098 mutt_file_fclose(&fp);
1099 return -1;
1100 }
1101 header = strchr(header, '\0') + 1;
1102 }
1103
1104 field = strchr(field, '\t');
1105 if (field)
1106 *field++ = '\0';
1107 if ((fputs(b, fp) == EOF) || (fputc('\n', fp) == EOF))
1108 {
1109 mutt_file_fclose(&fp);
1110 return -1;
1111 }
1112 }
1113 rewind(fp);
1114
1115 /* allocate memory for headers */
1117
1118 /* parse header */
1119 m->emails[m->msg_count] = email_new();
1120 e = m->emails[m->msg_count];
1121 e->env = mutt_rfc822_read_header(fp, e, false, false);
1122 e->env->newsgroups = mutt_str_dup(mdata->group);
1123 e->received = e->date_sent;
1124 mutt_file_fclose(&fp);
1125
1126#ifdef USE_HCACHE
1127 if (fc->hc)
1128 {
1129 char buf[16] = { 0 };
1130
1131 /* try to replace with header from cache */
1132 snprintf(buf, sizeof(buf), ANUM_FMT, anum);
1133 struct HCacheEntry hce = hcache_fetch_email(fc->hc, buf, strlen(buf), 0);
1134 if (hce.email)
1135 {
1136 mutt_debug(LL_DEBUG2, "hcache_fetch_email %s\n", buf);
1137 email_free(&e);
1138 e = hce.email;
1139 m->emails[m->msg_count] = e;
1140 e->edata = NULL;
1141 e->read = false;
1142 e->old = false;
1143
1144 /* skip header marked as deleted in cache */
1145 if (e->deleted && !fc->restore)
1146 {
1147 if (mdata->bcache)
1148 {
1149 mutt_debug(LL_DEBUG2, "mutt_bcache_del %s\n", buf);
1150 mutt_bcache_del(mdata->bcache, buf);
1151 }
1152 save = false;
1153 }
1154 }
1155 else
1156 {
1157 /* not cached yet, store header */
1158 mutt_debug(LL_DEBUG2, "hcache_store_email %s\n", buf);
1159 hcache_store_email(fc->hc, buf, strlen(buf), e, 0);
1160 }
1161 }
1162#endif
1163
1164 if (save)
1165 {
1166 e->index = m->msg_count++;
1167 e->read = false;
1168 e->old = false;
1169 e->deleted = false;
1170 e->edata = nntp_edata_new();
1172 nntp_edata_get(e)->article_num = anum;
1173 if (fc->restore)
1174 {
1175 e->changed = true;
1176 }
1177 else
1178 {
1179 nntp_article_status(m, e, NULL, anum);
1180 if (!e->read)
1181 nntp_parse_xref(m, e);
1182 }
1183 if (anum > mdata->last_loaded)
1184 mdata->last_loaded = anum;
1185 }
1186 else
1187 {
1188 email_free(&e);
1189 }
1190
1191 progress_update(fc->progress, anum - fc->first + 1, -1);
1192 return 0;
1193}
int mutt_bcache_del(struct BodyCache *bcache, const char *id)
Delete a file from the Body Cache.
Definition bcache.c:274
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:1210
#define mutt_file_fclose(FP)
Definition file.h:139
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:570
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:684
void mx_alloc_memory(struct Mailbox *m, int req_size)
Create storage for the emails.
Definition mx.c:1208
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:969
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:97
struct Progress * progress
Progress bar.
Definition nntp.c:96
struct Mailbox * mailbox
Mailbox.
Definition nntp.c:91
bool restore
Restore message headers from cache.
Definition nntp.c:94
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:78
int msg_count
Total number of messages.
Definition mailbox.h:87
struct Email ** emails
Array of Emails.
Definition mailbox.h:95
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()

static 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 1205 of file nntp.c.

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

static 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 1442 of file nntp.c.

1443{
1444 char buf[1024] = { 0 };
1445 anum_t count = 0, first = 0, last = 0;
1446
1447 /* use GROUP command to poll newsgroup */
1448 if (nntp_query(mdata, buf, sizeof(buf)) < 0)
1449 return -1;
1450 if (sscanf(buf, "211 " ANUM_FMT " " ANUM_FMT " " ANUM_FMT, &count, &first, &last) != 3)
1451 return 0;
1452 if ((first == mdata->first_message) && (last == mdata->last_message))
1453 return 0;
1454
1455 /* articles have been renumbered */
1456 if (last < mdata->last_message)
1457 {
1458 mdata->last_cached = 0;
1459 if (mdata->newsrc_len)
1460 {
1461 MUTT_MEM_REALLOC(&mdata->newsrc_ent, 1, struct NewsrcEntry);
1462 mdata->newsrc_len = 1;
1463 mdata->newsrc_ent[0].first = 1;
1464 mdata->newsrc_ent[0].last = 0;
1465 }
1466 }
1467 mdata->first_message = first;
1468 mdata->last_message = last;
1469 if (!update_stat)
1470 {
1471 return 1;
1472 }
1473 else if (!last || (!mdata->newsrc_ent && !mdata->last_cached))
1474 {
1475 /* update counters */
1476 mdata->unread = count;
1477 }
1478 else
1479 {
1481 }
1482 return 1;
1483}
void nntp_group_unread_stat(struct NntpMboxData *mdata)
Count number of unread articles using .newsrc data.
Definition newsrc.c:133
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()

static 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 1492 of file nntp.c.

1493{
1494 if (!m || !m->mdata)
1495 return MX_STATUS_ERROR;
1496
1497 struct NntpMboxData *mdata = m->mdata;
1498 struct NntpAccountData *adata = mdata->adata;
1499 time_t now = mutt_date_now();
1500 enum MxStatus rc = MX_STATUS_OK;
1501 struct HeaderCache *hc = NULL;
1502
1503 const short c_nntp_poll = cs_subset_number(NeoMutt->sub, "nntp_poll");
1504 if (adata->check_time + c_nntp_poll > now)
1505 return MX_STATUS_OK;
1506
1507 mutt_message(_("Checking for new messages..."));
1508 if (nntp_newsrc_parse(adata) < 0)
1509 return MX_STATUS_ERROR;
1510
1511 adata->check_time = now;
1512 int rc2 = nntp_group_poll(mdata, false);
1513 if (rc2 < 0)
1514 {
1515 nntp_newsrc_close(adata);
1516 return -1;
1517 }
1518 if (rc2 != 0)
1520
1521 /* articles have been renumbered, remove all emails */
1522 if (mdata->last_message < mdata->last_loaded)
1523 {
1524 for (int i = 0; i < m->msg_count; i++)
1525 email_free(&m->emails[i]);
1526 m->msg_count = 0;
1527 m->msg_tagged = 0;
1528
1529 mdata->last_loaded = mdata->first_message - 1;
1530 const long c_nntp_context = cs_subset_long(NeoMutt->sub, "nntp_context");
1531 if (c_nntp_context && (mdata->last_message - mdata->last_loaded > c_nntp_context))
1532 mdata->last_loaded = mdata->last_message - c_nntp_context;
1533
1534 rc = MX_STATUS_REOPENED;
1535 }
1536
1537 /* .newsrc has been externally modified */
1538 if (adata->newsrc_modified)
1539 {
1540#ifdef USE_HCACHE
1541 unsigned char *messages = NULL;
1542 char buf[16] = { 0 };
1543 struct Email *e = NULL;
1544 anum_t first = mdata->first_message;
1545
1546 const long c_nntp_context = cs_subset_long(NeoMutt->sub, "nntp_context");
1547 if (c_nntp_context && ((mdata->last_message - first + 1) > c_nntp_context))
1548 first = mdata->last_message - c_nntp_context + 1;
1549 messages = MUTT_MEM_CALLOC(mdata->last_loaded - first + 1, unsigned char);
1550 hc = nntp_hcache_open(mdata);
1551 nntp_hcache_update(mdata, hc);
1552#endif
1553
1554 /* update flags according to .newsrc */
1555 int j = 0;
1556 for (int i = 0; i < m->msg_count; i++)
1557 {
1558 if (!m->emails[i])
1559 continue;
1560 bool flagged = false;
1561 anum_t anum = nntp_edata_get(m->emails[i])->article_num;
1562
1563#ifdef USE_HCACHE
1564 /* check hcache for flagged and deleted flags */
1565 if (hc)
1566 {
1567 if ((anum >= first) && (anum <= mdata->last_loaded))
1568 messages[anum - first] = 1;
1569
1570 snprintf(buf, sizeof(buf), ANUM_FMT, anum);
1571 struct HCacheEntry hce = hcache_fetch_email(hc, buf, strlen(buf), 0);
1572 if (hce.email)
1573 {
1574 bool deleted;
1575
1576 mutt_debug(LL_DEBUG2, "#1 hcache_fetch_email %s\n", buf);
1577 e = hce.email;
1578 e->edata = NULL;
1579 deleted = e->deleted;
1580 flagged = e->flagged;
1581 email_free(&e);
1582
1583 /* header marked as deleted, removing from context */
1584 if (deleted)
1585 {
1586 mutt_set_flag(m, m->emails[i], MUTT_TAG, false, true);
1587 email_free(&m->emails[i]);
1588 continue;
1589 }
1590 }
1591 }
1592#endif
1593
1594 if (!m->emails[i]->changed)
1595 {
1596 m->emails[i]->flagged = flagged;
1597 m->emails[i]->read = false;
1598 m->emails[i]->old = false;
1599 nntp_article_status(m, m->emails[i], NULL, anum);
1600 if (!m->emails[i]->read)
1601 nntp_parse_xref(m, m->emails[i]);
1602 }
1603 m->emails[j++] = m->emails[i];
1604 }
1605
1606#ifdef USE_HCACHE
1607 m->msg_count = j;
1608
1609 /* restore headers without "deleted" flag */
1610 for (anum_t anum = first; anum <= mdata->last_loaded; anum++)
1611 {
1612 if (messages[anum - first])
1613 continue;
1614
1615 snprintf(buf, sizeof(buf), ANUM_FMT, anum);
1616 struct HCacheEntry hce = hcache_fetch_email(hc, buf, strlen(buf), 0);
1617 if (hce.email)
1618 {
1619 mutt_debug(LL_DEBUG2, "#2 hcache_fetch_email %s\n", buf);
1621
1622 e = hce.email;
1623 m->emails[m->msg_count] = e;
1624 e->edata = NULL;
1625 if (e->deleted)
1626 {
1627 email_free(&e);
1628 if (mdata->bcache)
1629 {
1630 mutt_debug(LL_DEBUG2, "mutt_bcache_del %s\n", buf);
1631 mutt_bcache_del(mdata->bcache, buf);
1632 }
1633 continue;
1634 }
1635
1636 m->msg_count++;
1637 e->read = false;
1638 e->old = false;
1639 e->edata = nntp_edata_new();
1641 nntp_edata_get(e)->article_num = anum;
1642 nntp_article_status(m, e, NULL, anum);
1643 if (!e->read)
1644 nntp_parse_xref(m, e);
1645 }
1646 }
1647 FREE(&messages);
1648#endif
1649
1650 adata->newsrc_modified = false;
1651 rc = MX_STATUS_REOPENED;
1652 }
1653
1654 /* some emails were removed, mailboxview must be updated */
1655 if (rc == MX_STATUS_REOPENED)
1657
1658 /* fetch headers of new articles */
1659 if (mdata->last_message > mdata->last_loaded)
1660 {
1661 int oldmsgcount = m->msg_count;
1662 bool verbose = m->verbose;
1663 m->verbose = false;
1664#ifdef USE_HCACHE
1665 if (!hc)
1666 {
1667 hc = nntp_hcache_open(mdata);
1668 nntp_hcache_update(mdata, hc);
1669 }
1670#endif
1671 int old_msg_count = m->msg_count;
1672 rc2 = nntp_fetch_headers(m, hc, mdata->last_loaded + 1, mdata->last_message, false);
1673 m->verbose = verbose;
1674 if (rc2 == 0)
1675 {
1676 if (m->msg_count > old_msg_count)
1678 mdata->last_loaded = mdata->last_message;
1679 }
1680 if ((rc == MX_STATUS_OK) && (m->msg_count > oldmsgcount))
1681 rc = MX_STATUS_NEW_MAIL;
1682 }
1683
1684#ifdef USE_HCACHE
1685 hcache_close(&hc);
1686#endif
1687 if (rc != MX_STATUS_OK)
1688 nntp_newsrc_close(adata);
1690 return rc;
1691}
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:179
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:550
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition date.c:457
@ 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:59
@ MX_STATUS_ERROR
An error occurred.
Definition mxapi.h:60
@ MX_STATUS_OK
No changes.
Definition mxapi.h:61
@ MX_STATUS_REOPENED
Mailbox was reopened.
Definition mxapi.h:64
@ MX_STATUS_NEW_MAIL
New mail received in Mailbox.
Definition mxapi.h:62
struct HeaderCache * nntp_hcache_open(struct NntpMboxData *mdata)
Open newsgroup hcache.
Definition newsrc.c:707
void nntp_hcache_update(struct NntpMboxData *mdata, struct HeaderCache *hc)
Remove stale cached headers.
Definition newsrc.c:731
int nntp_active_save_cache(struct NntpAccountData *adata)
Save list of all newsgroups to cache.
Definition newsrc.c:646
int nntp_newsrc_parse(struct NntpAccountData *adata)
Parse .newsrc file.
Definition newsrc.c:163
void nntp_newsrc_close(struct NntpAccountData *adata)
Unlock and close .newsrc file.
Definition newsrc.c:119
static int nntp_group_poll(struct NntpMboxData *mdata, bool update_stat)
Check newsgroup for new articles.
Definition nntp.c:1442
static int nntp_fetch_headers(struct Mailbox *m, void *hc, anum_t first, anum_t last, bool restore)
Fetch headers.
Definition nntp.c:1205
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:93
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()

static 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 1700 of file nntp.c.

1701{
1702 if (adata->hasDATE)
1703 {
1704 struct NntpMboxData mdata = { 0 };
1705 char buf[1024] = { 0 };
1706 struct tm tm = { 0 };
1707
1708 mdata.adata = adata;
1709 mdata.group = NULL;
1710 mutt_str_copy(buf, "DATE\r\n", sizeof(buf));
1711 if (nntp_query(&mdata, buf, sizeof(buf)) < 0)
1712 return -1;
1713
1714 if (sscanf(buf, "111 %4d%2d%2d%2d%2d%2d%*s", &tm.tm_year, &tm.tm_mon,
1715 &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6)
1716 {
1717 tm.tm_year -= 1900;
1718 tm.tm_mon--;
1719 *now = timegm(&tm);
1720 if (*now >= 0)
1721 {
1722 mutt_debug(LL_DEBUG1, "server time is %llu\n", (unsigned long long) *now);
1723 return 0;
1724 }
1725 }
1726 }
1727 *now = mutt_date_now();
1728 return 0;
1729}
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()

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

Parse XPAT line.

Parameters
lineString to parse
dataChildCtx
Return values
0Always

Definition at line 1737 of file nntp.c.

1738{
1739 struct ChildCtx *cc = data;
1740 anum_t anum = 0;
1741
1742 if (!line || (sscanf(line, ANUM_FMT, &anum) != 1))
1743 return 0;
1744 for (unsigned int i = 0; i < cc->mailbox->msg_count; i++)
1745 {
1746 struct Email *e = cc->mailbox->emails[i];
1747 if (!e)
1748 break;
1749 if (nntp_edata_get(e)->article_num == anum)
1750 return 0;
1751 }
1752 if (cc->num >= cc->max)
1753 {
1754 cc->max *= 2;
1755 MUTT_MEM_REALLOC(&cc->child, cc->max, anum_t);
1756 }
1757 cc->child[cc->num++] = anum;
1758 return 0;
1759}
Keep track of the children of an article.
Definition nntp.c:104
anum_t * child
Array of child article numbers.
Definition nntp.c:108
struct Mailbox * mailbox
Mailbox.
Definition nntp.c:105
unsigned int max
Maximum number of children.
Definition nntp.c:107
unsigned int num
Number of children.
Definition nntp.c:106
+ 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 1767 of file nntp.c.

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

1948{
1949 struct NntpMboxData *mdata = NULL;
1950 struct NntpMboxData tmp_mdata = { 0 };
1951 char buf[1024] = { 0 };
1952 int rc = -1;
1953
1954 if (m && (m->type == MUTT_NNTP))
1955 {
1956 mdata = m->mdata;
1957 }
1958 else
1959 {
1960 const char *const c_news_server = cs_subset_string(NeoMutt->sub, "news_server");
1961 CurrentNewsSrv = nntp_select_server(m, c_news_server, false);
1962 if (!CurrentNewsSrv)
1963 goto done;
1964
1965 mdata = &tmp_mdata;
1967 mdata->group = NULL;
1968 }
1969
1970 FILE *fp = mutt_file_fopen(msg, "r");
1971 if (!fp)
1972 {
1973 mutt_perror("%s", msg);
1974 goto done;
1975 }
1976
1977 mutt_str_copy(buf, "POST\r\n", sizeof(buf));
1978 if (nntp_query(mdata, buf, sizeof(buf)) < 0)
1979 {
1980 mutt_file_fclose(&fp);
1981 goto done;
1982 }
1983 if (buf[0] != '3')
1984 {
1985 mutt_error(_("Can't post article: %s"), buf);
1986 mutt_file_fclose(&fp);
1987 goto done;
1988 }
1989
1990 buf[0] = '.';
1991 buf[1] = '\0';
1992 while (fgets(buf + 1, sizeof(buf) - 2, fp))
1993 {
1994 size_t len = strlen(buf);
1995 if (buf[len - 1] == '\n')
1996 {
1997 buf[len - 1] = '\r';
1998 buf[len] = '\n';
1999 len++;
2000 buf[len] = '\0';
2001 }
2002 if (mutt_socket_send_d(mdata->adata->conn, (buf[1] == '.') ? buf : buf + 1,
2003 MUTT_SOCK_LOG_FULL) < 0)
2004 {
2005 mutt_file_fclose(&fp);
2006 nntp_connect_error(mdata->adata);
2007 goto done;
2008 }
2009 }
2010 mutt_file_fclose(&fp);
2011
2012 if (((buf[strlen(buf) - 1] != '\n') &&
2013 (mutt_socket_send_d(mdata->adata->conn, "\r\n", MUTT_SOCK_LOG_FULL) < 0)) ||
2014 (mutt_socket_send_d(mdata->adata->conn, ".\r\n", MUTT_SOCK_LOG_FULL) < 0) ||
2015 (mutt_socket_readln(buf, sizeof(buf), mdata->adata->conn) < 0))
2016 {
2017 nntp_connect_error(mdata->adata);
2018 goto done;
2019 }
2020 if (buf[0] != '2')
2021 {
2022 mutt_error(_("Can't post article: %s"), buf);
2023 goto done;
2024 }
2025 rc = 0;
2026
2027done:
2028 return rc;
2029}
@ MUTT_NNTP
'NNTP' (Usenet) Mailbox type
Definition mailbox.h:48
#define mutt_file_fopen(PATH, MODE)
Definition file.h:138
struct NntpAccountData * CurrentNewsSrv
Current NNTP news server.
Definition nntp.c:74
struct NntpAccountData * nntp_select_server(struct Mailbox *m, const char *server, bool leave_lock)
Open a connection to an NNTP server.
Definition newsrc.c:951
enum MailboxType type
Mailbox type.
Definition mailbox.h:101
+ 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 2038 of file nntp.c.

2039{
2040 struct NntpMboxData tmp_mdata = { 0 };
2041 char msg[256] = { 0 };
2042 char buf[1024] = { 0 };
2043 unsigned int i;
2044 int rc;
2045
2046 snprintf(msg, sizeof(msg), _("Loading list of groups from server %s..."),
2048 mutt_message("%s", msg);
2049 if (nntp_date(adata, &adata->newgroups_time) < 0)
2050 return -1;
2051
2052 tmp_mdata.adata = adata;
2053 tmp_mdata.group = NULL;
2054 i = adata->groups_num;
2055 mutt_str_copy(buf, "LIST\r\n", sizeof(buf));
2056 rc = nntp_fetch_lines(&tmp_mdata, buf, sizeof(buf), msg, nntp_add_group, adata);
2057 if (rc)
2058 {
2059 if (rc > 0)
2060 {
2061 mutt_error("LIST: %s", buf);
2062 }
2063 return -1;
2064 }
2065
2066 if (mark_new)
2067 {
2068 for (; i < adata->groups_num; i++)
2069 {
2070 struct NntpMboxData *mdata = adata->groups_list[i];
2071 mdata->has_new_mail = true;
2072 }
2073 }
2074
2075 for (i = 0; i < adata->groups_num; i++)
2076 {
2077 struct NntpMboxData *mdata = adata->groups_list[i];
2078
2079 if (mdata && mdata->deleted && !mdata->newsrc_ent)
2080 {
2082 mutt_hash_delete(adata->groups_hash, mdata->group, NULL);
2083 adata->groups_list[i] = NULL;
2084 }
2085 }
2086
2087 const bool c_nntp_load_description = cs_subset_bool(NeoMutt->sub, "nntp_load_description");
2088 if (c_nntp_load_description)
2089 rc = get_description(&tmp_mdata, "*", _("Loading descriptions..."));
2090
2092 if (rc < 0)
2093 return -1;
2095 return 0;
2096}
void mutt_hash_delete(struct HashTable *table, const char *strkey, const void *data)
Remove an element from a Hash Table.
Definition hash.c:429
void nntp_delete_group_cache(struct NntpMboxData *mdata)
Remove hcache and bcache of newsgroup.
Definition newsrc.c:808
int nntp_add_group(char *line, void *data)
Parse newsgroup.
Definition newsrc.c:571
static int nntp_date(struct NntpAccountData *adata, time_t *now)
Get date and time from server.
Definition nntp.c:1700
static int get_description(struct NntpMboxData *mdata, const char *wildmat, const char *msg)
Fetch newsgroups descriptions.
Definition nntp.c:937
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 2106 of file nntp.c.

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

2217{
2218 if (!m)
2219 return -1;
2220
2221 struct NntpMboxData *mdata = m->mdata;
2222 char buf[1024] = { 0 };
2223
2224 FILE *fp = mutt_file_mkstemp();
2225 if (!fp)
2226 {
2227 mutt_perror(_("Can't create temporary file"));
2228 return -1;
2229 }
2230
2231 snprintf(buf, sizeof(buf), "HEAD %s\r\n", msgid);
2232 int rc = nntp_fetch_lines(mdata, buf, sizeof(buf), NULL, fetch_tempfile, fp);
2233 if (rc)
2234 {
2235 mutt_file_fclose(&fp);
2236 if (rc < 0)
2237 return -1;
2238 if (mutt_str_startswith(buf, "430"))
2239 return 1;
2240 mutt_error("HEAD: %s", buf);
2241 return -1;
2242 }
2243
2244 /* parse header */
2246 m->emails[m->msg_count] = email_new();
2247 struct Email *e = m->emails[m->msg_count];
2248 e->edata = nntp_edata_new();
2250 e->env = mutt_rfc822_read_header(fp, e, false, false);
2251 mutt_file_fclose(&fp);
2252
2253 /* get article number */
2254 if (e->env->xref)
2255 {
2256 nntp_parse_xref(m, e);
2257 }
2258 else
2259 {
2260 snprintf(buf, sizeof(buf), "STAT %s\r\n", msgid);
2261 if (nntp_query(mdata, buf, sizeof(buf)) < 0)
2262 {
2263 email_free(&e);
2264 return -1;
2265 }
2266 sscanf(buf + 4, ANUM_FMT, &nntp_edata_get(e)->article_num);
2267 }
2268
2269 /* reset flags */
2270 e->read = false;
2271 e->old = false;
2272 e->deleted = false;
2273 e->changed = true;
2274 e->received = e->date_sent;
2275 e->index = m->msg_count++;
2277 return 0;
2278}
+ 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 2287 of file nntp.c.

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

Variable Documentation

◆ CurrentNewsSrv

struct NntpAccountData* CurrentNewsSrv = NULL

Current news server.

Current NNTP news server.

Definition at line 74 of file nntp.c.

◆ 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 77 of file nntp.c.