NeoMutt  2025-12-11-435-g4ac674
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:662
const char * mutt_istr_find(const char *haystack, const char *needle)
Find first occurrence of string (ignoring case)
Definition string.c:525
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:583
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 449 of file nntp.c.

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

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

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

897{
898 if (!line)
899 return 0;
900
901 struct NntpAccountData *adata = data;
902
903 char *desc = strpbrk(line, " \t");
904 if (desc)
905 {
906 *desc++ = '\0';
907 desc += strspn(desc, " \t");
908 }
909 else
910 {
911 desc = strchr(line, '\0');
912 }
913
915 if (mdata && !mutt_str_equal(desc, mdata->desc))
916 {
917 mutt_str_replace(&mdata->desc, desc);
918 mutt_debug(LL_DEBUG2, "group: %s, desc: %s\n", line, desc);
919 }
920 return 0;
921}
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 933 of file nntp.c.

934{
935 char buf[256] = { 0 };
936 const char *cmd = NULL;
937
938 /* get newsgroup description, if possible */
939 struct NntpAccountData *adata = mdata->adata;
940 if (!wildmat)
941 wildmat = mdata->group;
942 if (adata->hasLIST_NEWSGROUPS)
943 cmd = "LIST NEWSGROUPS";
944 else if (adata->hasXGTITLE)
945 cmd = "XGTITLE";
946 else
947 return 0;
948
949 snprintf(buf, sizeof(buf), "%s %s\r\n", cmd, wildmat);
950 int rc = nntp_fetch_lines(mdata, buf, sizeof(buf), msg, fetch_description, adata);
951 if (rc > 0)
952 {
953 mutt_error("%s: %s", cmd, buf);
954 }
955 return rc;
956}
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:811
static int fetch_description(char *line, void *data)
Parse newsgroup description.
Definition nntp.c:896
+ 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 965 of file nntp.c.

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

1007{
1008 FILE *fp = data;
1009
1010 if (!line)
1011 rewind(fp);
1012 else if ((fputs(line, fp) == EOF) || (fputc('\n', fp) == EOF))
1013 return -1;
1014 return 0;
1015}
+ 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 1023 of file nntp.c.

1024{
1025 struct FetchCtx *fc = data;
1026 anum_t anum = 0;
1027
1028 if (!line)
1029 return 0;
1030 if (sscanf(line, ANUM_FMT, &anum) != 1)
1031 return 0;
1032 if ((anum < fc->first) || (anum > fc->last))
1033 return 0;
1034 fc->messages[anum - fc->first] = 1;
1035 return 0;
1036}
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 1045 of file nntp.c.

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

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

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

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

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

1734{
1735 struct ChildCtx *cc = data;
1736 anum_t anum = 0;
1737
1738 if (!line || (sscanf(line, ANUM_FMT, &anum) != 1))
1739 return 0;
1740 for (unsigned int i = 0; i < cc->mailbox->msg_count; i++)
1741 {
1742 struct Email *e = cc->mailbox->emails[i];
1743 if (!e)
1744 break;
1745 if (nntp_edata_get(e)->article_num == anum)
1746 return 0;
1747 }
1748 if (cc->num >= cc->max)
1749 {
1750 cc->max *= 2;
1751 MUTT_MEM_REALLOC(&cc->child, cc->max, anum_t);
1752 }
1753 cc->child[cc->num++] = anum;
1754 return 0;
1755}
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 1763 of file nntp.c.

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

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

2035{
2036 struct NntpMboxData tmp_mdata = { 0 };
2037 char msg[256] = { 0 };
2038 char buf[1024] = { 0 };
2039 unsigned int i;
2040 int rc;
2041
2042 snprintf(msg, sizeof(msg), _("Loading list of groups from server %s..."),
2044 mutt_message("%s", msg);
2045 if (nntp_date(adata, &adata->newgroups_time) < 0)
2046 return -1;
2047
2048 tmp_mdata.adata = adata;
2049 tmp_mdata.group = NULL;
2050 i = adata->groups_num;
2051 mutt_str_copy(buf, "LIST\r\n", sizeof(buf));
2052 rc = nntp_fetch_lines(&tmp_mdata, buf, sizeof(buf), msg, nntp_add_group, adata);
2053 if (rc)
2054 {
2055 if (rc > 0)
2056 {
2057 mutt_error("LIST: %s", buf);
2058 }
2059 return -1;
2060 }
2061
2062 if (mark_new)
2063 {
2064 for (; i < adata->groups_num; i++)
2065 {
2066 struct NntpMboxData *mdata = adata->groups_list[i];
2067 mdata->has_new_mail = true;
2068 }
2069 }
2070
2071 for (i = 0; i < adata->groups_num; i++)
2072 {
2073 struct NntpMboxData *mdata = adata->groups_list[i];
2074
2075 if (mdata && mdata->deleted && !mdata->newsrc_ent)
2076 {
2078 mutt_hash_delete(adata->groups_hash, mdata->group, NULL);
2079 adata->groups_list[i] = NULL;
2080 }
2081 }
2082
2083 const bool c_nntp_load_description = cs_subset_bool(NeoMutt->sub, "nntp_load_description");
2084 if (c_nntp_load_description)
2085 rc = get_description(&tmp_mdata, "*", _("Loading descriptions..."));
2086
2088 if (rc < 0)
2089 return -1;
2091 return 0;
2092}
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:807
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:1696
static int get_description(struct NntpMboxData *mdata, const char *wildmat, const char *msg)
Fetch newsgroups descriptions.
Definition nntp.c:933
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 2102 of file nntp.c.

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

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

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