NeoMutt  2025-12-11-980-ge38c27
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:644
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:89
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:298
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition buffer.c:248
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:233
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:401
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:666
const char * mutt_istr_find(const char *haystack, const char *needle)
Find first occurrence of string (ignoring case)
Definition string.c:528
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition string.c:234
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition string.c:587
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition string.c:284
@ NNTP_BYE
Disconnected from server.
Definition private.h:46
static int nntp_connect_error(struct NntpAccountData *adata)
Signal a failed connection.
Definition nntp.c:122
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
int mutt_socket_close(struct Connection *conn)
Close a socket.
Definition socket.c:100
#define mutt_socket_send(conn, buf)
Definition socket.h:56
#define mutt_socket_buffer_readln(buf, conn)
Definition socket.h:60
String manipulation buffer.
Definition buffer.h:36
char * data
Pointer to data.
Definition buffer.h:37
struct Connection * conn
Connection to NNTP Server.
Definition adata.h:63
char * authenticators
Authenticators list.
Definition adata.h:52
bool hasCAPABILITIES
Server supports CAPABILITIES command.
Definition adata.h:37
bool hasSTARTTLS
Server supports STARTTLS command.
Definition adata.h:38
bool hasLISTGROUPrange
Server supports LISTGROUPrange command.
Definition adata.h:43
bool hasLISTGROUP
Server supports LISTGROUP command.
Definition adata.h:42
bool hasOVER
Server supports OVER command.
Definition adata.h:44
bool hasDATE
Server supports DATE command.
Definition adata.h:39
bool hasLIST_NEWSGROUPS
Server supports LIST_NEWSGROUPS command.
Definition adata.h:40
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ nntp_attempt_features()

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;
337 size_t off = 0;
338 size_t b = 0;
339
340 FREE(&adata->overview_fmt);
341 adata->overview_fmt = MUTT_MEM_MALLOC(buflen, char);
342
343 while (true)
344 {
345 if ((buflen - off) < 1024)
346 {
347 buflen *= 2;
348 MUTT_MEM_REALLOC(&adata->overview_fmt, buflen, char);
349 }
350
351 const int chunk = mutt_socket_readln_d(adata->overview_fmt + off,
352 buflen - off, conn, MUTT_SOCK_LOG_HDR);
353 if (chunk < 0)
354 {
355 FREE(&adata->overview_fmt);
356 goto fail;
357 }
358
359 if (!cont && mutt_str_equal(".", adata->overview_fmt + off))
360 break;
361
362 cont = (chunk >= (buflen - off));
363 off += strlen(adata->overview_fmt + off);
364 if (!cont)
365 {
366 if (adata->overview_fmt[b] == ':')
367 {
368 memmove(adata->overview_fmt + b, adata->overview_fmt + b + 1, off - b - 1);
369 adata->overview_fmt[off - 1] = ':';
370 }
371 char *colon = strchr(adata->overview_fmt + b, ':');
372 if (!colon)
373 adata->overview_fmt[off++] = ':';
374 else if (!mutt_str_equal(colon + 1, "full"))
375 off = colon + 1 - adata->overview_fmt;
376 if (strcasecmp(adata->overview_fmt + b, "Bytes:") == 0)
377 {
378 size_t len = strlen(adata->overview_fmt + b);
379 mutt_str_copy(adata->overview_fmt + b, "Content-Length:", len + 1);
380 off = b + len;
381 }
382 adata->overview_fmt[off++] = '\0';
383 b = off;
384 }
385 }
386 adata->overview_fmt[off++] = '\0';
387 MUTT_MEM_REALLOC(&adata->overview_fmt, off, char);
388 }
389 }
390 rc = 0; // Success
391
392fail:
393 if (rc < 0)
394 nntp_connect_error(adata);
395
396 return rc;
397}
#define MUTT_MEM_REALLOC(pptr, n, type)
Definition memory.h:55
#define MUTT_MEM_MALLOC(n, type)
Definition memory.h:53
static const char * OverviewFmt
Fields to get from server, if it supports the LIST OVERVIEW.FMT feature.
Definition nntp.c:75
int mutt_socket_readln_d(char *buf, size_t buflen, struct Connection *conn, int dbg)
Read a line from a socket.
Definition socket.c:238
#define MUTT_SOCK_LOG_HDR
Log commands and headers.
Definition socket.h:52
#define mutt_socket_readln(buf, buflen, conn)
Definition socket.h:55
bool hasXOVER
Server supports XOVER command.
Definition adata.h:45
char * overview_fmt
Overview format.
Definition adata.h:53
bool hasXGTITLE
Server supports XGTITLE command.
Definition adata.h:41
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ nntp_auth()

static int nntp_auth ( struct NntpAccountData * adata)
static

Get login, password and authenticate.

Parameters
adataNNTP server
Return values
0Success
-1Failure

Definition at line 453 of file nntp.c.

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

◆ nntp_query()

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

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

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

◆ fetch_description()

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

Parse newsgroup description.

Parameters
lineString to parse
dataNNTP Server
Return values
0Always

Definition at line 902 of file nntp.c.

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

◆ get_description()

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

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

◆ nntp_parse_xref()

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

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

◆ fetch_tempfile()

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

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

1030{
1031 struct FetchCtx *fc = data;
1032 anum_t anum = 0;
1033
1034 if (!line)
1035 return 0;
1036 if (sscanf(line, ANUM_FMT, &anum) != 1)
1037 return 0;
1038 if ((anum < fc->first) || (anum > fc->last))
1039 return 0;
1040 fc->messages[anum - fc->first] = 1;
1041 return 0;
1042}
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 1051 of file nntp.c.

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

◆ nntp_fetch_headers()

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

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

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

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

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

1743{
1744 struct ChildCtx *cc = data;
1745 anum_t anum = 0;
1746
1747 if (!line || (sscanf(line, ANUM_FMT, &anum) != 1))
1748 return 0;
1749 for (unsigned int i = 0; i < cc->mailbox->msg_count; i++)
1750 {
1751 struct Email *e = cc->mailbox->emails[i];
1752 if (!e)
1753 break;
1754 if (nntp_edata_get(e)->article_num == anum)
1755 return 0;
1756 }
1757 if (cc->num >= cc->max)
1758 {
1759 cc->max *= 2;
1760 MUTT_MEM_REALLOC(&cc->child, cc->max, anum_t);
1761 }
1762 cc->child[cc->num++] = anum;
1763 return 0;
1764}
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 1772 of file nntp.c.

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

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

◆ nntp_active_fetch()

int nntp_active_fetch ( struct NntpAccountData * adata,
bool mark_new )

Fetch list of all newsgroups from server.

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

Definition at line 2044 of file nntp.c.

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

◆ nntp_check_new_groups()

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

Check for new groups/articles in subscribed groups.

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

Definition at line 2112 of file nntp.c.

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

◆ nntp_check_msgid()

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

Fetch article by Message-ID.

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

Definition at line 2223 of file nntp.c.

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

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