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

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

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

Go to the source code of this file.

Data Structures

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

Functions

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

Variables

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

Detailed Description

Usenet network mailbox type; talk to an NNTP server.

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

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file nntp.c.

Function Documentation

◆ nntp_connect_error()

static int nntp_connect_error ( struct NntpAccountData * adata)
static

Signal a failed connection.

Parameters
adataNNTP server
Return values
-1Always

Definition at line 122 of file nntp.c.

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

◆ nntp_capabilities()

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

137{
138 struct Connection *conn = adata->conn;
139 bool mode_reader = false;
140 char authinfo[1024] = { 0 };
141
142 adata->hasCAPABILITIES = false;
143 adata->hasSTARTTLS = false;
144 adata->hasDATE = false;
145 adata->hasLIST_NEWSGROUPS = false;
146 adata->hasLISTGROUP = false;
147 adata->hasLISTGROUPrange = false;
148 adata->hasOVER = false;
149 FREE(&adata->authenticators);
150
151 struct Buffer *buf = buf_pool_get();
152
153 if ((mutt_socket_send(conn, "CAPABILITIES\r\n") < 0) ||
154 (mutt_socket_buffer_readln(buf, conn) < 0))
155 {
156 buf_pool_release(&buf);
157 return nntp_connect_error(adata);
158 }
159
160 /* no capabilities */
161 if (!mutt_str_startswith(buf_string(buf), "101"))
162 {
163 buf_pool_release(&buf);
164 return 1;
165 }
166 adata->hasCAPABILITIES = true;
167
168 /* parse capabilities */
169 do
170 {
171 size_t plen = 0;
172 if (mutt_socket_buffer_readln(buf, conn) < 0)
173 {
174 buf_pool_release(&buf);
175 return nntp_connect_error(adata);
176 }
177 if (mutt_str_equal("STARTTLS", buf_string(buf)))
178 {
179 adata->hasSTARTTLS = true;
180 }
181 else if (mutt_str_equal("MODE-READER", buf_string(buf)))
182 {
183 mode_reader = true;
184 }
185 else if (mutt_str_equal("READER", buf_string(buf)))
186 {
187 adata->hasDATE = true;
188 adata->hasLISTGROUP = true;
189 adata->hasLISTGROUPrange = true;
190 }
191 else if ((plen = mutt_str_startswith(buf_string(buf), "AUTHINFO ")))
192 {
193 buf_addch(buf, ' ');
194 mutt_str_copy(authinfo, buf->data + plen - 1, sizeof(authinfo));
195 }
196#ifdef USE_SASL_CYRUS
197 else if ((plen = mutt_str_startswith(buf_string(buf), "SASL ")))
198 {
199 char *p = buf->data + plen;
200 while (*p == ' ')
201 p++;
202 adata->authenticators = mutt_str_dup(p);
203 }
204#endif
205 else if (mutt_str_equal("OVER", buf_string(buf)))
206 {
207 adata->hasOVER = true;
208 }
209 else if (mutt_str_startswith(buf_string(buf), "LIST "))
210 {
211 const char *p = buf_find_string(buf, " NEWSGROUPS");
212 if (p)
213 {
214 p += 11;
215 if ((*p == '\0') || (*p == ' '))
216 adata->hasLIST_NEWSGROUPS = true;
217 }
218 }
219 } while (!mutt_str_equal(".", buf_string(buf)));
220 buf_reset(buf);
221
222#ifdef USE_SASL_CYRUS
223 if (adata->authenticators && mutt_istr_find(authinfo, " SASL "))
224 buf_strcpy(buf, adata->authenticators);
225#endif
226 if (mutt_istr_find(authinfo, " USER "))
227 {
228 if (!buf_is_empty(buf))
229 buf_addch(buf, ' ');
230 buf_addstr(buf, "USER");
231 }
233 buf_pool_release(&buf);
234
235 /* current mode is reader */
236 if (adata->hasDATE)
237 return 0;
238
239 /* server is mode-switching, need to switch to reader mode */
240 if (mode_reader)
241 return 1;
242
243 mutt_socket_close(conn);
244 adata->status = NNTP_BYE;
245 mutt_error(_("Server doesn't support reader mode"));
246 return -1;
247}
const char * buf_find_string(const struct Buffer *buf, const char *s)
Return a pointer to a substring found in the buffer.
Definition buffer.c: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:122
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
int mutt_socket_close(struct Connection *conn)
Close a socket.
Definition socket.c:100
#define mutt_socket_send(conn, buf)
Definition socket.h:56
#define mutt_socket_buffer_readln(buf, conn)
Definition socket.h:60
String manipulation buffer.
Definition buffer.h:36
char * data
Pointer to data.
Definition buffer.h:37
struct Connection * conn
Connection to NNTP Server.
Definition adata.h:63
char * authenticators
Authenticators list.
Definition adata.h:52
bool hasCAPABILITIES
Server supports CAPABILITIES command.
Definition adata.h:37
bool hasSTARTTLS
Server supports STARTTLS command.
Definition adata.h:38
bool hasLISTGROUPrange
Server supports LISTGROUPrange command.
Definition adata.h:43
bool hasLISTGROUP
Server supports LISTGROUP command.
Definition adata.h:42
bool hasOVER
Server supports OVER command.
Definition adata.h:44
bool hasDATE
Server supports DATE command.
Definition adata.h:39
bool hasLIST_NEWSGROUPS
Server supports LIST_NEWSGROUPS command.
Definition adata.h:40
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ nntp_attempt_features()

static int nntp_attempt_features ( struct NntpAccountData * adata)
static

Detect supported commands.

Parameters
adataNNTP server
Return values
0Success
-1Failure

Definition at line 255 of file nntp.c.

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

◆ nntp_auth()

static int nntp_auth ( struct NntpAccountData * adata)
static

Get login, password and authenticate.

Parameters
adataNNTP server
Return values
0Success
-1Failure

Definition at line 451 of file nntp.c.

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

◆ nntp_query()

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

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

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

899{
900 if (!line)
901 return 0;
902
903 struct NntpAccountData *adata = data;
904
905 char *desc = strpbrk(line, " \t");
906 if (desc)
907 {
908 *desc++ = '\0';
909 desc += strspn(desc, " \t");
910 }
911 else
912 {
913 desc = strchr(line, '\0');
914 }
915
917 if (mdata && !mutt_str_equal(desc, mdata->desc))
918 {
919 mutt_str_replace(&mdata->desc, desc);
920 mutt_debug(LL_DEBUG2, "group: %s, desc: %s\n", line, desc);
921 }
922 return 0;
923}
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:134
struct HashTable * groups_hash
Hash Table: "newsgroup" -> NntpMboxData.
Definition adata.h:62
NNTP-specific Mailbox data -.
Definition mdata.h:34
char * desc
Description of newsgroup.
Definition mdata.h:36
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ get_description()

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

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

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

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

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

◆ parse_overview_line()

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

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

◆ nntp_fetch_headers()

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

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

◆ nntp_group_poll()

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

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

◆ check_mailbox()

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

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

◆ nntp_date()

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

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

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

◆ nntp_open_connection()

int nntp_open_connection ( struct NntpAccountData * adata)

Connect to server, authenticate and get capabilities.

Parameters
adataNNTP server
Return values
0Success
-1Failure

Definition at line 1765 of file nntp.c.

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

◆ nntp_post()

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

Post article.

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

Definition at line 1945 of file nntp.c.

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

◆ nntp_active_fetch()

int nntp_active_fetch ( struct NntpAccountData * adata,
bool mark_new )

Fetch list of all newsgroups from server.

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

Definition at line 2037 of file nntp.c.

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

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

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

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

Variable Documentation

◆ OverviewFmt

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

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

Definition at line 75 of file nntp.c.