NeoMutt  2025-12-11-435-g4ac674
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
newsrc.c
Go to the documentation of this file.
1
26
32
33#include "config.h"
34#include <dirent.h>
35#include <errno.h>
36#include <limits.h>
37#include <stdbool.h>
38#include <stdio.h>
39#include <string.h>
40#include <sys/stat.h>
41#include <time.h>
42#include <unistd.h>
43#include "private.h"
44#include "mutt/lib.h"
45#include "config/lib.h"
46#include "email/lib.h"
47#include "core/lib.h"
48#include "conn/lib.h"
49#include "mutt.h"
50#include "lib.h"
51#include "bcache/lib.h"
52#include "expando/lib.h"
53#include "adata.h"
54#include "edata.h"
55#include "expando_newsrc.h"
56#include "mdata.h"
57#include "mutt_logging.h"
58#include "mutt_socket.h"
59#include "muttlib.h"
60#ifdef USE_HCACHE
61#include "hcache/lib.h"
62#endif
63
64struct BodyCache;
65
73static struct NntpMboxData *mdata_find(struct NntpAccountData *adata, const char *group)
74{
76 if (mdata)
77 return mdata;
78
79 size_t len = strlen(group) + 1;
80 /* create NntpMboxData structure and add it to hash */
81 mdata = mutt_mem_calloc(1, sizeof(struct NntpMboxData) + len);
82 mdata->group = (char *) mdata + sizeof(struct NntpMboxData);
83 mutt_str_copy(mdata->group, group, len);
84 mdata->adata = adata;
85 mdata->deleted = true;
87
88 /* add NntpMboxData to list */
90 {
91 adata->groups_max *= 2;
93 }
95
96 return mdata;
97}
98
104{
105 for (int i = 0; i < NNTP_ACACHE_LEN; i++)
106 {
107 if (mdata->acache[i].path)
108 {
109 unlink(mdata->acache[i].path);
110 FREE(&mdata->acache[i].path);
111 }
112 }
113}
114
120{
121 if (!adata->fp_newsrc)
122 return;
123
124 mutt_debug(LL_DEBUG1, "Unlocking %s\n", adata->newsrc_file);
127}
128
134{
135 mdata->unread = 0;
136 if ((mdata->last_message == 0) ||
137 (mdata->first_message > mdata->last_message) || !mdata->newsrc_ent)
138 {
139 return;
140 }
141
142 mdata->unread = mdata->last_message - mdata->first_message + 1;
143 for (unsigned int i = 0; i < mdata->newsrc_len; i++)
144 {
145 anum_t first = mdata->newsrc_ent[i].first;
146 if (first < mdata->first_message)
147 first = mdata->first_message;
148 anum_t last = mdata->newsrc_ent[i].last;
149 if (last > mdata->last_message)
150 last = mdata->last_message;
151 if (first <= last)
152 mdata->unread -= last - first + 1;
153 }
154}
155
164{
165 if (!adata)
166 return -1;
167
168 char *line = NULL;
169 struct stat st = { 0 };
170
171 if (adata->fp_newsrc)
172 {
173 /* if we already have a handle, close it and reopen */
175 }
176 else
177 {
178 /* if file doesn't exist, create it */
179 adata->fp_newsrc = mutt_file_fopen(adata->newsrc_file, "a");
181 }
182
183 /* open .newsrc */
184 adata->fp_newsrc = mutt_file_fopen(adata->newsrc_file, "r");
185 if (!adata->fp_newsrc)
186 {
187 mutt_perror("%s", adata->newsrc_file);
188 return -1;
189 }
190
191 /* lock it */
192 mutt_debug(LL_DEBUG1, "Locking %s\n", adata->newsrc_file);
193 if (mutt_file_lock(fileno(adata->fp_newsrc), false, true))
194 {
196 return -1;
197 }
198
199 if (stat(adata->newsrc_file, &st) != 0)
200 {
201 mutt_perror("%s", adata->newsrc_file);
202 nntp_newsrc_close(adata);
203 return -1;
204 }
205
206 if ((adata->size == st.st_size) && (adata->mtime == st.st_mtime))
207 return 0;
208
209 adata->size = st.st_size;
210 adata->mtime = st.st_mtime;
211 adata->newsrc_modified = true;
212 mutt_debug(LL_DEBUG1, "Parsing %s\n", adata->newsrc_file);
213
214 /* .newsrc has been externally modified or hasn't been loaded yet */
215 for (unsigned int i = 0; i < adata->groups_num; i++)
216 {
217 struct NntpMboxData *mdata = adata->groups_list[i];
218 if (!mdata)
219 continue;
220
221 mdata->subscribed = false;
222 mdata->newsrc_len = 0;
223 FREE(&mdata->newsrc_ent);
224 }
225
226 line = MUTT_MEM_MALLOC(st.st_size + 1, char);
227 while (st.st_size && fgets(line, st.st_size + 1, adata->fp_newsrc))
228 {
229 char *b = NULL, *h = NULL;
230 unsigned int j = 1;
231 bool subs = false;
232
233 /* find end of newsgroup name */
234 char *p = strpbrk(line, ":!");
235 if (!p)
236 continue;
237
238 /* ":" - subscribed, "!" - unsubscribed */
239 if (*p == ':')
240 subs = true;
241 *p++ = '\0';
242
243 /* get newsgroup data */
244 struct NntpMboxData *mdata = mdata_find(adata, line);
245 FREE(&mdata->newsrc_ent);
246
247 /* count number of entries */
248 b = p;
249 while (*b)
250 if (*b++ == ',')
251 j++;
252 mdata->newsrc_ent = MUTT_MEM_CALLOC(j, struct NewsrcEntry);
253 mdata->subscribed = subs;
254
255 /* parse entries */
256 j = 0;
257 while (p)
258 {
259 b = p;
260
261 /* find end of entry */
262 p = strchr(p, ',');
263 if (p)
264 *p++ = '\0';
265
266 /* first-last or single number */
267 h = strchr(b, '-');
268 if (h)
269 *h++ = '\0';
270 else
271 h = b;
272
273 if ((sscanf(b, ANUM_FMT, &mdata->newsrc_ent[j].first) == 1) &&
274 (sscanf(h, ANUM_FMT, &mdata->newsrc_ent[j].last) == 1))
275 {
276 j++;
277 }
278 }
279 if (j == 0)
280 {
281 mdata->newsrc_ent[j].first = 1;
282 mdata->newsrc_ent[j].last = 0;
283 j++;
284 }
285 if (mdata->last_message == 0)
286 mdata->last_message = mdata->newsrc_ent[j - 1].last;
287 mdata->newsrc_len = j;
288 MUTT_MEM_REALLOC(&mdata->newsrc_ent, j, struct NewsrcEntry);
290 mutt_debug(LL_DEBUG2, "%s\n", mdata->group);
291 }
292 FREE(&line);
293 return 1;
294}
295
301{
302 if (!m)
303 return;
304
305 struct NntpMboxData *mdata = m->mdata;
306 anum_t last = 0, first = 1;
307 bool series;
308 unsigned int entries;
309
310 const enum EmailSortType c_sort = cs_subset_sort(NeoMutt->sub, "sort");
311 if (c_sort != EMAIL_SORT_UNSORTED)
312 {
315 }
316
317 entries = mdata->newsrc_len;
318 if (!entries)
319 {
320 entries = 5;
321 mdata->newsrc_ent = MUTT_MEM_CALLOC(entries, struct NewsrcEntry);
322 }
323
324 /* Set up to fake initial sequence from 1 to the article before the
325 * first article in our list */
326 mdata->newsrc_len = 0;
327 series = true;
328 for (int i = 0; i < m->msg_count; i++)
329 {
330 struct Email *e = m->emails[i];
331 if (!e)
332 break;
333
334 /* search for first unread */
335 if (series)
336 {
337 /* We don't actually check sequential order, since we mark
338 * "missing" entries as read/deleted */
339 last = nntp_edata_get(e)->article_num;
340 if ((last >= mdata->first_message) && !e->deleted && !e->read)
341 {
342 if (mdata->newsrc_len >= entries)
343 {
344 entries *= 2;
345 MUTT_MEM_REALLOC(&mdata->newsrc_ent, entries, struct NewsrcEntry);
346 }
347 mdata->newsrc_ent[mdata->newsrc_len].first = first;
348 mdata->newsrc_ent[mdata->newsrc_len].last = last - 1;
349 mdata->newsrc_len++;
350 series = false;
351 }
352 }
353 else
354 {
355 /* search for first read */
356 if (e->deleted || e->read)
357 {
358 first = last + 1;
359 series = true;
360 }
361 last = nntp_edata_get(e)->article_num;
362 }
363 }
364
365 if (series && (first <= mdata->last_loaded))
366 {
367 if (mdata->newsrc_len >= entries)
368 {
369 entries++;
370 MUTT_MEM_REALLOC(&mdata->newsrc_ent, entries, struct NewsrcEntry);
371 }
372 mdata->newsrc_ent[mdata->newsrc_len].first = first;
373 mdata->newsrc_ent[mdata->newsrc_len].last = mdata->last_loaded;
374 mdata->newsrc_len++;
375 }
376 MUTT_MEM_REALLOC(&mdata->newsrc_ent, mdata->newsrc_len, struct NewsrcEntry);
377
378 if (c_sort != EMAIL_SORT_UNSORTED)
379 {
380 cs_subset_str_native_set(NeoMutt->sub, "sort", c_sort, NULL);
382 }
383}
384
392static int update_file(char *filename, char *buf)
393{
394 FILE *fp = NULL;
395 char tempfile[PATH_MAX] = { 0 };
396 int rc = -1;
397
398 while (true)
399 {
400 snprintf(tempfile, sizeof(tempfile), "%s.tmp", filename);
401 fp = mutt_file_fopen(tempfile, "w");
402 if (!fp)
403 {
404 mutt_perror("%s", tempfile);
405 *tempfile = '\0';
406 break;
407 }
408 if (fputs(buf, fp) == EOF)
409 {
410 mutt_perror("%s", tempfile);
411 break;
412 }
413 if (mutt_file_fclose(&fp) == EOF)
414 {
415 mutt_perror("%s", tempfile);
416 fp = NULL;
417 break;
418 }
419 fp = NULL;
420 if (rename(tempfile, filename) < 0)
421 {
422 mutt_perror("%s", filename);
423 break;
424 }
425 *tempfile = '\0';
426 rc = 0;
427 break;
428 }
429 mutt_file_fclose(&fp);
430
431 if (*tempfile)
432 unlink(tempfile);
433 return rc;
434}
435
443{
444 if (!adata)
445 return -1;
446
447 int rc = -1;
448
449 size_t buflen = 10240;
450 char *buf = MUTT_MEM_CALLOC(buflen, char);
451 size_t off = 0;
452
453 /* we will generate full newsrc here */
454 for (unsigned int i = 0; i < adata->groups_num; i++)
455 {
456 struct NntpMboxData *mdata = adata->groups_list[i];
457
458 if (!mdata || !mdata->newsrc_ent)
459 continue;
460
461 /* write newsgroup name */
462 if ((off + strlen(mdata->group) + 3) > buflen)
463 {
464 buflen *= 2;
465 MUTT_MEM_REALLOC(&buf, buflen, char);
466 }
467 snprintf(buf + off, buflen - off, "%s%c ", mdata->group, mdata->subscribed ? ':' : '!');
468 off += strlen(buf + off);
469
470 /* write entries */
471 for (unsigned int j = 0; j < mdata->newsrc_len; j++)
472 {
473 if ((off + 1024) > buflen)
474 {
475 buflen *= 2;
476 MUTT_MEM_REALLOC(&buf, buflen, char);
477 }
478 if (j)
479 buf[off++] = ',';
480 if (mdata->newsrc_ent[j].first == mdata->newsrc_ent[j].last)
481 {
482 snprintf(buf + off, buflen - off, ANUM_FMT, mdata->newsrc_ent[j].first);
483 }
484 else if (mdata->newsrc_ent[j].first < mdata->newsrc_ent[j].last)
485 {
486 snprintf(buf + off, buflen - off, ANUM_FMT "-" ANUM_FMT,
487 mdata->newsrc_ent[j].first, mdata->newsrc_ent[j].last);
488 }
489 off += strlen(buf + off);
490 }
491 buf[off++] = '\n';
492 }
493 buf[off] = '\0';
494
495 /* newrc being fully rewritten */
496 mutt_debug(LL_DEBUG1, "Updating %s\n", adata->newsrc_file);
497 if (adata->newsrc_file && (update_file(adata->newsrc_file, buf) == 0))
498 {
499 struct stat st = { 0 };
500
501 rc = stat(adata->newsrc_file, &st);
502 if (rc == 0)
503 {
504 adata->size = st.st_size;
505 adata->mtime = st.st_mtime;
506 }
507 else
508 {
509 mutt_perror("%s", adata->newsrc_file);
510 }
511 }
512 FREE(&buf);
513 return rc;
514}
515
523static void cache_expand(char *dst, size_t dstlen, struct ConnAccount *cac, const char *src)
524{
525 char file[PATH_MAX] = { 0 };
526
527 /* server subdirectory */
528 struct Url url = { 0 };
529 account_to_url(cac, &url);
530 url.path = mutt_str_dup(src);
531 url_tostring(&url, file, sizeof(file), U_PATH);
532 FREE(&url.path);
533
534 /* remove trailing slash */
535 char *c = file + strlen(file) - 1;
536 if (*c == '/')
537 *c = '\0';
538
539 struct Buffer *tmp = buf_pool_get();
540 buf_addstr(tmp, file);
541 mutt_encode_path(tmp, file);
542
543 const char *const c_news_cache_dir = cs_subset_path(NeoMutt->sub, "news_cache_dir");
544 snprintf(dst, dstlen, "%s/%s", c_news_cache_dir, buf_string(tmp));
545
546 buf_pool_release(&tmp);
547}
548
555void nntp_expand_path(char *buf, size_t buflen, struct ConnAccount *cac)
556{
557 struct Url url = { 0 };
558
559 account_to_url(cac, &url);
560 url.path = mutt_str_dup(buf);
561 url_tostring(&url, buf, buflen, U_NO_FLAGS);
562 FREE(&url.path);
563}
564
571int nntp_add_group(char *line, void *data)
572{
573 struct NntpAccountData *adata = data;
574 struct NntpMboxData *mdata = NULL;
575 char group[1024] = { 0 };
576 char desc[8192] = { 0 };
577 char mod = '\0';
578 anum_t first = 0, last = 0;
579
580 if (!adata || !line)
581 return 0;
582
583 /* These sscanf limits must match the sizes of the group and desc arrays */
584 if (sscanf(line, "%1023s " ANUM_FMT " " ANUM_FMT " %c %8191[^\n]", group,
585 &last, &first, &mod, desc) < 4)
586 {
587 mutt_debug(LL_DEBUG2, "Can't parse server line: %s\n", line);
588 return 0;
589 }
590
592 mdata->deleted = false;
593 mdata->first_message = first;
594 mdata->last_message = last;
595 mdata->allowed = (mod == 'y') || (mod == 'm');
596 mutt_str_replace(&mdata->desc, desc);
597 if (mdata->newsrc_ent || (mdata->last_cached != 0))
599 else if (mdata->last_message && (mdata->first_message <= mdata->last_message))
600 mdata->unread = mdata->last_message - mdata->first_message + 1;
601 else
602 mdata->unread = 0;
603 return 0;
604}
605
613{
614 char buf[8192] = { 0 };
615 char file[4096] = { 0 };
616 time_t t = 0;
617
618 cache_expand(file, sizeof(file), &adata->conn->account, ".active");
619 mutt_debug(LL_DEBUG1, "Parsing %s\n", file);
620 FILE *fp = mutt_file_fopen(file, "r");
621 if (!fp)
622 return -1;
623
624 if (!fgets(buf, sizeof(buf), fp) || (sscanf(buf, "%jd%4095s", &t, file) != 1) || (t == 0))
625 {
626 mutt_file_fclose(&fp);
627 return -1;
628 }
630
631 mutt_message(_("Loading list of groups from cache..."));
632 while (fgets(buf, sizeof(buf), fp))
633 nntp_add_group(buf, adata);
634 nntp_add_group(NULL, NULL);
635 mutt_file_fclose(&fp);
637 return 0;
638}
639
647{
648 if (!adata->cacheable)
649 return 0;
650
651 size_t buflen = 10240;
652 char *buf = MUTT_MEM_CALLOC(buflen, char);
653 snprintf(buf, buflen, "%lu\n", (unsigned long) adata->newgroups_time);
654 size_t off = strlen(buf);
655
656 for (unsigned int i = 0; i < adata->groups_num; i++)
657 {
658 struct NntpMboxData *mdata = adata->groups_list[i];
659
660 if (!mdata || mdata->deleted)
661 continue;
662
663 if ((off + strlen(mdata->group) + (mdata->desc ? strlen(mdata->desc) : 0) + 50) > buflen)
664 {
665 buflen *= 2;
666 MUTT_MEM_REALLOC(&buf, buflen, char);
667 }
668 snprintf(buf + off, buflen - off, "%s " ANUM_FMT " " ANUM_FMT " %c%s%s\n",
669 mdata->group, mdata->last_message, mdata->first_message,
670 mdata->allowed ? 'y' : 'n', mdata->desc ? " " : "",
671 mdata->desc ? mdata->desc : "");
672 off += strlen(buf + off);
673 }
674
675 char file[PATH_MAX] = { 0 };
676 cache_expand(file, sizeof(file), &adata->conn->account, ".active");
677 mutt_debug(LL_DEBUG1, "Updating %s\n", file);
678 int rc = update_file(file, buf);
679 FREE(&buf);
680 return rc;
681}
682
683#ifdef USE_HCACHE
687static void nntp_hcache_namer(const char *path, struct Buffer *dest)
688{
689 buf_printf(dest, "%s.hcache", path);
690
691 /* Strip out any directories in the path */
692 char *first = strchr(buf_string(dest), '/');
693 char *last = strrchr(buf_string(dest), '/');
694 if (first && last && (last > first))
695 {
696 memmove(first, last, strlen(last) + 1);
697 }
698}
699
707{
708 struct Url url = { 0 };
709 char file[PATH_MAX] = { 0 };
710
711 const bool c_save_unsubscribed = cs_subset_bool(NeoMutt->sub, "save_unsubscribed");
712 if (!mdata->adata || !mdata->adata->cacheable || !mdata->adata->conn || !mdata->group ||
713 !(mdata->newsrc_ent || mdata->subscribed || c_save_unsubscribed))
714 {
715 return NULL;
716 }
717
718 account_to_url(&mdata->adata->conn->account, &url);
719 url.path = mdata->group;
720 url_tostring(&url, file, sizeof(file), U_PATH);
721 const char *const c_news_cache_dir = cs_subset_path(NeoMutt->sub, "news_cache_dir");
722 return hcache_open(c_news_cache_dir, file, nntp_hcache_namer, true);
723}
724
730void nntp_hcache_update(struct NntpMboxData *mdata, struct HeaderCache *hc)
731{
732 if (!hc)
733 return;
734
735 char buf[32] = { 0 };
736 bool old = false;
737 anum_t first = 0, last = 0;
738
739 /* fetch previous values of first and last */
740 char *hdata = hcache_fetch_raw_str(hc, "index", 5);
741 if (hdata)
742 {
743 mutt_debug(LL_DEBUG2, "hcache_fetch_email index: %s\n", hdata);
744 if (sscanf(hdata, ANUM_FMT " " ANUM_FMT, &first, &last) == 2)
745 {
746 old = true;
747 mdata->last_cached = last;
748
749 /* clean removed headers from cache */
750 for (anum_t current = first; current <= last; current++)
751 {
752 if ((current >= mdata->first_message) && (current <= mdata->last_message))
753 continue;
754
755 snprintf(buf, sizeof(buf), ANUM_FMT, current);
756 mutt_debug(LL_DEBUG2, "hcache_delete_email %s\n", buf);
757 hcache_delete_email(hc, buf, strlen(buf));
758 }
759 }
760 FREE(&hdata);
761 }
762
763 /* store current values of first and last */
764 if (!old || (mdata->first_message != first) || (mdata->last_message != last))
765 {
766 snprintf(buf, sizeof(buf), ANUM_FMT " " ANUM_FMT, mdata->first_message,
767 mdata->last_message);
768 mutt_debug(LL_DEBUG2, "hcache_store_email index: %s\n", buf);
769 hcache_store_raw(hc, "index", 5, buf, strlen(buf) + 1);
770 }
771}
772#endif
773
778static int nntp_bcache_delete(const char *id, struct BodyCache *bcache, void *data)
779{
780 struct NntpMboxData *mdata = data;
781 anum_t anum = 0;
782 char c = '\0';
783
784 if (!mdata || (sscanf(id, ANUM_FMT "%c", &anum, &c) != 1) ||
785 (anum < mdata->first_message) || (anum > mdata->last_message))
786 {
787 if (mdata)
788 mutt_debug(LL_DEBUG2, "mutt_bcache_del %s\n", id);
790 }
791 return 0;
792}
793
802
808{
809 if (!mdata || !mdata->adata || !mdata->adata->cacheable)
810 return;
811
812#ifdef USE_HCACHE
813 struct Buffer *file = buf_pool_get();
814 nntp_hcache_namer(mdata->group, file);
815 cache_expand(file->data, file->dsize, &mdata->adata->conn->account, buf_string(file));
816 unlink(buf_string(file));
817 mdata->last_cached = 0;
818 mutt_debug(LL_DEBUG2, "%s\n", buf_string(file));
819 buf_pool_release(&file);
820#endif
821
822 if (!mdata->bcache)
823 {
824 mdata->bcache = mutt_bcache_open(&mdata->adata->conn->account, mdata->group);
825 }
826 if (mdata->bcache)
827 {
828 mutt_debug(LL_DEBUG2, "%s/*\n", mdata->group);
830 mutt_bcache_close(&mdata->bcache);
831 }
832}
833
841{
842 if (!adata || !adata->cacheable)
843 return;
844
845 struct dirent *de = NULL;
846 DIR *dir = NULL;
847 struct Buffer *cache = buf_pool_get();
848 struct Buffer *file = buf_pool_get();
849
850 cache_expand(cache->data, cache->dsize, &adata->conn->account, NULL);
852 if (!dir)
853 goto done;
854
855 buf_addch(cache, '/');
856 const bool c_save_unsubscribed = cs_subset_bool(NeoMutt->sub, "save_unsubscribed");
857
858 while ((de = readdir(dir)))
859 {
860 char *group = de->d_name;
861 if (mutt_str_equal(group, ".") || mutt_str_equal(group, ".."))
862 continue;
863
864 buf_printf(file, "%s%s", buf_string(cache), group);
865 struct stat st = { 0 };
866 if (stat(buf_string(file), &st) != 0)
867 continue;
868
869#ifdef USE_HCACHE
870 if (S_ISREG(st.st_mode))
871 {
872 char *ext = group + strlen(group) - 7;
873 if ((strlen(group) < 8) || !mutt_str_equal(ext, ".hcache"))
874 continue;
875 *ext = '\0';
876 }
877 else
878#endif
879 if (!S_ISDIR(st.st_mode))
880 continue;
881
882 struct NntpMboxData tmp_mdata = { 0 };
884 if (!mdata)
885 {
886 mdata = &tmp_mdata;
887 mdata->adata = adata;
888 mdata->group = group;
889 mdata->bcache = NULL;
890 }
891 else if (mdata->newsrc_ent || mdata->subscribed || c_save_unsubscribed)
892 {
893 continue;
894 }
895
897 if (S_ISDIR(st.st_mode))
898 {
899 rmdir(buf_string(file));
900 mutt_debug(LL_DEBUG2, "%s\n", buf_string(file));
901 }
902 }
903 closedir(dir);
904
905done:
906 buf_pool_release(&cache);
907 buf_pool_release(&file);
908}
909
913static const char *nntp_get_field(enum ConnAccountField field, void *gf_data)
914{
915 switch (field)
916 {
917 case MUTT_CA_LOGIN:
918 case MUTT_CA_USER:
919 return cs_subset_string(NeoMutt->sub, "nntp_user");
920 case MUTT_CA_PASS:
921 return cs_subset_string(NeoMutt->sub, "nntp_pass");
923 case MUTT_CA_HOST:
924 default:
925 return NULL;
926 }
927}
928
944struct NntpAccountData *nntp_select_server(struct Mailbox *m, const char *server, bool leave_lock)
945{
946 char file[PATH_MAX] = { 0 };
947 int rc;
948 struct ConnAccount cac = { { 0 } };
949 struct NntpAccountData *adata = NULL;
950 struct Connection *conn = NULL;
951
952 if (!server || (*server == '\0'))
953 {
954 mutt_error(_("No news server defined"));
955 return NULL;
956 }
957
958 /* create account from news server url */
959 cac.flags = 0;
960 cac.port = NNTP_PORT;
962 cac.service = "nntp";
964
965 snprintf(file, sizeof(file), "%s%s", strstr(server, "://") ? "" : "news://", server);
966 struct Url *url = url_parse(file);
967 if (!url || (url->path && *url->path) ||
968 !((url->scheme == U_NNTP) || (url->scheme == U_NNTPS)) || !url->host ||
969 (account_from_url(&cac, url) < 0))
970 {
971 url_free(&url);
972 mutt_error(_("%s is an invalid news server specification"), server);
973 return NULL;
974 }
975 if (url->scheme == U_NNTPS)
976 {
977 cac.flags |= MUTT_ACCT_SSL;
978 cac.port = NNTP_SSL_PORT;
979 }
980 url_free(&url);
981
982 // If nntp_user and nntp_pass are specified in the config, use them to find the connection
983 const char *user = NULL;
984 const char *pass = NULL;
985 if ((user = cac.get_field(MUTT_CA_USER, NULL)))
986 {
987 mutt_str_copy(cac.user, user, sizeof(cac.user));
988 cac.flags |= MUTT_ACCT_USER;
989 }
990 if ((pass = cac.get_field(MUTT_CA_PASS, NULL)))
991 {
992 mutt_str_copy(cac.pass, pass, sizeof(cac.pass));
993 cac.flags |= MUTT_ACCT_PASS;
994 }
995
996 /* find connection by account */
997 conn = mutt_conn_find(&cac);
998 if (!conn)
999 return NULL;
1000 if (!(conn->account.flags & MUTT_ACCT_USER) && cac.flags & MUTT_ACCT_USER)
1001 {
1002 conn->account.flags |= MUTT_ACCT_USER;
1003 conn->account.user[0] = '\0';
1004 }
1005
1006 /* new news server */
1007 adata = nntp_adata_new(conn);
1008
1009 rc = nntp_open_connection(adata);
1010
1011 /* try to create cache directory and enable caching */
1012 adata->cacheable = false;
1013 const char *const c_news_cache_dir = cs_subset_path(NeoMutt->sub, "news_cache_dir");
1014 if ((rc >= 0) && c_news_cache_dir)
1015 {
1016 cache_expand(file, sizeof(file), &conn->account, NULL);
1017 if (mutt_file_mkdir(file, S_IRWXU) < 0)
1018 {
1019 mutt_error(_("Can't create %s: %s"), file, strerror(errno));
1020 }
1021 adata->cacheable = true;
1022 }
1023
1024 /* load .newsrc */
1025 if (rc >= 0)
1026 {
1027 const struct Expando *c_newsrc = cs_subset_expando(NeoMutt->sub, "newsrc");
1028 struct Buffer *buf = buf_pool_get();
1030 buf->dsize, NeoMutt->env, buf);
1031 expand_path(buf, false);
1032 adata->newsrc_file = buf_strdup(buf);
1033 buf_pool_release(&buf);
1034 rc = nntp_newsrc_parse(adata);
1035 }
1036 if (rc >= 0)
1037 {
1038 /* try to load list of newsgroups from cache */
1039 if (adata->cacheable && (active_get_cache(adata) == 0))
1040 {
1041 rc = nntp_check_new_groups(m, adata);
1042 }
1043 else
1044 {
1045 /* load list of newsgroups from server */
1046 rc = nntp_active_fetch(adata, false);
1047 }
1048 }
1049
1050 if (rc >= 0)
1051 nntp_clear_cache(adata);
1052
1053#ifdef USE_HCACHE
1054 /* check cache files */
1055 if ((rc >= 0) && adata->cacheable)
1056 {
1057 struct dirent *de = NULL;
1058 DIR *dir = mutt_file_opendir(file, MUTT_OPENDIR_NONE);
1059
1060 if (dir)
1061 {
1062 while ((de = readdir(dir)))
1063 {
1064 struct HeaderCache *hc = NULL;
1065 char *group = de->d_name;
1066
1067 char *p = group + strlen(group) - 7;
1068 if ((strlen(group) < 8) || !mutt_str_equal(p, ".hcache"))
1069 continue;
1070 *p = '\0';
1072 if (!mdata)
1073 continue;
1074
1075 hc = nntp_hcache_open(mdata);
1076 if (!hc)
1077 continue;
1078
1079 /* fetch previous values of first and last */
1080 char *hdata = hcache_fetch_raw_str(hc, "index", 5);
1081 if (hdata)
1082 {
1083 anum_t first = 0, last = 0;
1084
1085 if (sscanf(hdata, ANUM_FMT " " ANUM_FMT, &first, &last) == 2)
1086 {
1087 if (mdata->deleted)
1088 {
1089 mdata->first_message = first;
1090 mdata->last_message = last;
1091 }
1092 if ((last >= mdata->first_message) && (last <= mdata->last_message))
1093 {
1094 mdata->last_cached = last;
1095 mutt_debug(LL_DEBUG2, "%s last_cached=" ANUM_FMT "\n", mdata->group, last);
1096 }
1097 }
1098 FREE(&hdata);
1099 }
1100 hcache_close(&hc);
1101 }
1102 closedir(dir);
1103 }
1104 }
1105#endif
1106
1107 if ((rc < 0) || !leave_lock)
1109
1110 if (rc < 0)
1111 {
1116 FREE(&adata);
1117 mutt_socket_close(conn);
1118 FREE(&conn);
1119 return NULL;
1120 }
1121
1122 return adata;
1123}
1124
1137void nntp_article_status(struct Mailbox *m, struct Email *e, char *group, anum_t anum)
1138{
1139 struct NntpMboxData *mdata = m->mdata;
1140
1141 if (group)
1142 mdata = mutt_hash_find(mdata->adata->groups_hash, group);
1143
1144 if (!mdata)
1145 return;
1146
1147 for (unsigned int i = 0; i < mdata->newsrc_len; i++)
1148 {
1149 if ((anum >= mdata->newsrc_ent[i].first) && (anum <= mdata->newsrc_ent[i].last))
1150 {
1151 /* can't use mutt_set_flag() because mview_update() didn't get called yet */
1152 e->read = true;
1153 return;
1154 }
1155 }
1156
1157 /* article was not cached yet, it's new */
1158 if (anum > mdata->last_cached)
1159 return;
1160
1161 /* article isn't read but cached, it's old */
1162 const bool c_mark_old = cs_subset_bool(NeoMutt->sub, "mark_old");
1163 if (c_mark_old)
1164 e->old = true;
1165}
1166
1175{
1176 if (!adata || !adata->groups_hash || !group || (*group == '\0'))
1177 return NULL;
1178
1180 mdata->subscribed = true;
1181 if (!mdata->newsrc_ent)
1182 {
1183 mdata->newsrc_ent = MUTT_MEM_CALLOC(1, struct NewsrcEntry);
1184 mdata->newsrc_len = 1;
1185 mdata->newsrc_ent[0].first = 1;
1186 mdata->newsrc_ent[0].last = 0;
1187 }
1188 return mdata;
1189}
1190
1199{
1200 if (!adata || !adata->groups_hash || !group || (*group == '\0'))
1201 return NULL;
1202
1204 if (!mdata)
1205 return NULL;
1206
1207 mdata->subscribed = false;
1208 const bool c_save_unsubscribed = cs_subset_bool(NeoMutt->sub, "save_unsubscribed");
1209 if (!c_save_unsubscribed)
1210 {
1211 mdata->newsrc_len = 0;
1212 FREE(&mdata->newsrc_ent);
1213 }
1214 return mdata;
1215}
1216
1226 struct NntpAccountData *adata, char *group)
1227{
1228 if (!adata || !adata->groups_hash || !group || (*group == '\0'))
1229 return NULL;
1230
1232 if (!mdata)
1233 return NULL;
1234
1235 if (mdata->newsrc_ent)
1236 {
1237 MUTT_MEM_REALLOC(&mdata->newsrc_ent, 1, struct NewsrcEntry);
1238 mdata->newsrc_len = 1;
1239 mdata->newsrc_ent[0].first = 1;
1240 mdata->newsrc_ent[0].last = mdata->last_message;
1241 }
1242 mdata->unread = 0;
1243 if (m && (m->mdata == mdata))
1244 {
1245 for (unsigned int i = 0; i < m->msg_count; i++)
1246 {
1247 struct Email *e = m->emails[i];
1248 if (!e)
1249 break;
1250 mutt_set_flag(m, e, MUTT_READ, true, true);
1251 }
1252 }
1253 return mdata;
1254}
1255
1265 struct NntpAccountData *adata, char *group)
1266{
1267 if (!adata || !adata->groups_hash || !group || (*group == '\0'))
1268 return NULL;
1269
1271 if (!mdata)
1272 return NULL;
1273
1274 if (mdata->newsrc_ent)
1275 {
1276 MUTT_MEM_REALLOC(&mdata->newsrc_ent, 1, struct NewsrcEntry);
1277 mdata->newsrc_len = 1;
1278 mdata->newsrc_ent[0].first = 1;
1279 mdata->newsrc_ent[0].last = mdata->first_message - 1;
1280 }
1281 if (m && (m->mdata == mdata))
1282 {
1283 mdata->unread = m->msg_count;
1284 for (unsigned int i = 0; i < m->msg_count; i++)
1285 {
1286 struct Email *e = m->emails[i];
1287 if (!e)
1288 break;
1289 mutt_set_flag(m, e, MUTT_READ, false, true);
1290 }
1291 }
1292 else
1293 {
1294 mdata->unread = mdata->last_message;
1295 if (mdata->newsrc_ent)
1296 mdata->unread -= mdata->newsrc_ent[0].last;
1297 }
1298 return mdata;
1299}
1300
1307void nntp_mailbox(struct Mailbox *m, char *buf, size_t buflen)
1308{
1309 if (!m)
1310 return;
1311
1312 for (unsigned int i = 0; i < CurrentNewsSrv->groups_num; i++)
1313 {
1314 struct NntpMboxData *mdata = CurrentNewsSrv->groups_list[i];
1315
1316 if (!mdata || !mdata->subscribed || !mdata->unread)
1317 continue;
1318
1319 if ((m->type == MUTT_NNTP) &&
1320 mutt_str_equal(mdata->group, ((struct NntpMboxData *) m->mdata)->group))
1321 {
1322 unsigned int unread = 0;
1323
1324 for (unsigned int j = 0; j < m->msg_count; j++)
1325 {
1326 struct Email *e = m->emails[j];
1327 if (!e)
1328 break;
1329 if (!e->read && !e->deleted)
1330 unread++;
1331 }
1332 if (unread == 0)
1333 continue;
1334 }
1335 mutt_str_copy(buf, mdata->group, buflen);
1336 break;
1337 }
1338}
Body Caching (local copies of email bodies)
struct BodyCache * mutt_bcache_open(struct ConnAccount *account, const char *mailbox)
Open an Email-Body Cache.
Definition bcache.c:146
int mutt_bcache_list(struct BodyCache *bcache, bcache_list_t want_id, void *data)
Find matching entries in the Body Cache.
Definition bcache.c:334
void mutt_bcache_close(struct BodyCache **ptr)
Close an Email-Body Cache.
Definition bcache.c:167
int mutt_bcache_del(struct BodyCache *bcache, const char *id)
Delete a file from the Body Cache.
Definition bcache.c:269
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition buffer.c:161
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
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition buffer.c:571
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition helpers.c:291
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition helpers.c:168
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
short cs_subset_sort(const struct ConfigSubset *sub, const char *name)
Get a sort config item by name.
Definition helpers.c:266
const struct Expando * cs_subset_expando(const struct ConfigSubset *sub, const char *name)
Get an Expando config item by name.
Convenience wrapper for the config headers.
Connection Library.
ConnAccountField
Login credentials.
Definition connaccount.h:33
@ MUTT_CA_OAUTH_CMD
OAuth refresh command.
Definition connaccount.h:38
@ MUTT_CA_USER
User name.
Definition connaccount.h:36
@ MUTT_CA_LOGIN
Login name.
Definition connaccount.h:35
@ MUTT_CA_HOST
Server name.
Definition connaccount.h:34
@ MUTT_CA_PASS
Password.
Definition connaccount.h:37
#define MUTT_ACCT_SSL
Account uses SSL/TLS.
Definition connaccount.h:47
#define MUTT_ACCT_PASS
Password field has been set.
Definition connaccount.h:46
#define MUTT_ACCT_USER
User field has been set.
Definition connaccount.h:44
Convenience wrapper for the core headers.
void mailbox_changed(struct Mailbox *m, enum NotifyMailbox action)
Notify observers of a change to a Mailbox.
Definition mailbox.c:232
@ NT_MAILBOX_RESORT
Email list needs resorting.
Definition mailbox.h:180
@ MUTT_NNTP
'NNTP' (Usenet) Mailbox type
Definition mailbox.h:48
Structs that make up an email.
EmailSortType
Methods for sorting Emails.
Definition sort.h:53
@ EMAIL_SORT_UNSORTED
Sort by the order the messages appear in the mailbox.
Definition sort.h:64
int expando_filter(const struct Expando *exp, const struct ExpandoRenderCallback *erc, void *data, MuttFormatFlags flags, int max_cols, char **env_list, struct Buffer *buf)
Render an Expando and run the result through a filter.
Definition filter.c:139
Parse Expando string.
const struct ExpandoRenderCallback NntpRenderCallbacks[]
Callbacks for Newsrc Expandos.
NNTP Expando definitions.
int mutt_file_mkdir(const char *path, mode_t mode)
Recursively create directories.
Definition file.c:848
int mutt_file_lock(int fd, bool excl, bool timeout)
(Try to) Lock a file using fcntl()
Definition file.c:1092
int mutt_file_unlock(int fd)
Unlock a file previously locked by mutt_file_lock()
Definition file.c:1139
DIR * mutt_file_opendir(const char *path, enum MuttOpenDirMode mode)
Open a directory.
Definition file.c:539
@ MUTT_OPENDIR_NONE
Plain opendir()
Definition file.h:63
#define mutt_file_fclose(FP)
Definition file.h:139
#define mutt_file_fopen(PATH, MODE)
Definition file.h:138
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
static int nntp_bcache_delete(const char *id, struct BodyCache *bcache, void *data)
Delete an entry from the message cache - Implements bcache_list_t -.
Definition newsrc.c:778
static const char * nntp_get_field(enum ConnAccountField field, void *gf_data)
Get connection login credentials - Implements ConnAccount::get_field() -.
Definition newsrc.c:913
static void nntp_hcache_namer(const char *path, struct Buffer *dest)
Compose hcache file names - Implements hcache_namer_t -.
Definition newsrc.c:687
#define mutt_error(...)
Definition logging2.h:94
#define mutt_message(...)
Definition logging2.h:93
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
#define mutt_perror(...)
Definition logging2.h:95
struct HashElem * mutt_hash_insert(struct HashTable *table, const char *strkey, void *data)
Add a new element to the Hash Table (with string keys)
Definition hash.c:337
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
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition hash.c:459
struct HeaderCache * hcache_open(const char *path, const char *folder, hcache_namer_t namer, bool create)
Multiplexor for StoreOps::open.
Definition hcache.c:476
int hcache_delete_email(struct HeaderCache *hc, const char *key, size_t keylen)
Multiplexor for StoreOps::delete_record.
Definition hcache.c:744
void hcache_close(struct HeaderCache **ptr)
Multiplexor for StoreOps::close.
Definition hcache.c:547
char * hcache_fetch_raw_str(struct HeaderCache *hc, const char *key, size_t keylen)
Fetch a string from the cache.
Definition hcache.c:657
int hcache_store_raw(struct HeaderCache *hc, const char *key, size_t keylen, void *data, size_t dlen)
Store a key / data pair.
Definition hcache.c:729
Header cache multiplexor.
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:46
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition memory.c:76
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
#define MUTT_MEM_CALLOC(n, type)
Definition memory.h:52
#define MUTT_MEM_REALLOC(pptr, n, type)
Definition memory.h:55
#define MUTT_MEM_MALLOC(n, type)
Definition memory.h:53
Convenience wrapper for the library headers.
#define _(a)
Definition message.h:28
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:662
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition string.c:583
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition string.c:284
Many unsorted constants and some structs.
@ MUTT_READ
Messages that have been read.
Definition mutt.h:92
#define PATH_MAX
Definition mutt.h:49
void account_to_url(struct ConnAccount *cac, struct Url *url)
Fill URL with info from account.
int account_from_url(struct ConnAccount *cac, const struct Url *url)
Fill ConnAccount with information from url.
@ MUTT_ACCT_TYPE_NNTP
Nntp (Usenet) Account.
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
NeoMutt Logging.
struct Connection * mutt_conn_find(const struct ConnAccount *cac)
Find a connection from a list.
Definition mutt_socket.c:88
NeoMutt connections.
void expand_path(struct Buffer *buf, bool regex)
Create the canonical path.
Definition muttlib.c:121
void mutt_encode_path(struct Buffer *buf, const char *src)
Convert a path to 'us-ascii'.
Definition muttlib.c:802
Some miscellaneous functions.
struct HeaderCache * nntp_hcache_open(struct NntpMboxData *mdata)
Open newsgroup hcache.
Definition newsrc.c:706
void nntp_clear_cache(struct NntpAccountData *adata)
Clear the NNTP cache.
Definition newsrc.c:840
void nntp_delete_group_cache(struct NntpMboxData *mdata)
Remove hcache and bcache of newsgroup.
Definition newsrc.c:807
void nntp_newsrc_gen_entries(struct Mailbox *m)
Generate array of .newsrc entries.
Definition newsrc.c:300
void nntp_hcache_update(struct NntpMboxData *mdata, struct HeaderCache *hc)
Remove stale cached headers.
Definition newsrc.c:730
void nntp_article_status(struct Mailbox *m, struct Email *e, char *group, anum_t anum)
Get status of articles from .newsrc.
Definition newsrc.c:1137
static int update_file(char *filename, char *buf)
Update file with new contents.
Definition newsrc.c:392
int nntp_add_group(char *line, void *data)
Parse newsgroup.
Definition newsrc.c:571
int nntp_newsrc_parse(struct NntpAccountData *adata)
Parse .newsrc file.
Definition newsrc.c:163
void nntp_mailbox(struct Mailbox *m, char *buf, size_t buflen)
Get first newsgroup with new messages.
Definition newsrc.c:1307
void nntp_newsrc_close(struct NntpAccountData *adata)
Unlock and close .newsrc file.
Definition newsrc.c:119
struct NntpMboxData * mutt_newsgroup_catchup(struct Mailbox *m, struct NntpAccountData *adata, char *group)
Catchup newsgroup.
Definition newsrc.c:1225
int nntp_newsrc_update(struct NntpAccountData *adata)
Update .newsrc file.
Definition newsrc.c:442
struct NntpMboxData * mutt_newsgroup_subscribe(struct NntpAccountData *adata, char *group)
Subscribe newsgroup.
Definition newsrc.c:1174
void nntp_expand_path(char *buf, size_t buflen, struct ConnAccount *cac)
Make fully qualified url from newsgroup name.
Definition newsrc.c:555
static int active_get_cache(struct NntpAccountData *adata)
Load list of all newsgroups from cache.
Definition newsrc.c:612
static void cache_expand(char *dst, size_t dstlen, struct ConnAccount *cac, const char *src)
Make fully qualified cache file name.
Definition newsrc.c:523
static struct NntpMboxData * mdata_find(struct NntpAccountData *adata, const char *group)
Find NntpMboxData for given newsgroup or add it.
Definition newsrc.c:73
struct NntpMboxData * mutt_newsgroup_uncatchup(struct Mailbox *m, struct NntpAccountData *adata, char *group)
Uncatchup newsgroup.
Definition newsrc.c:1264
int nntp_active_save_cache(struct NntpAccountData *adata)
Save list of all newsgroups to cache.
Definition newsrc.c:646
void nntp_bcache_update(struct NntpMboxData *mdata)
Remove stale cached messages.
Definition newsrc.c:798
void nntp_group_unread_stat(struct NntpMboxData *mdata)
Count number of unread articles using .newsrc data.
Definition newsrc.c:133
void nntp_acache_free(struct NntpMboxData *mdata)
Remove all temporarily cache files.
Definition newsrc.c:103
struct NntpAccountData * nntp_select_server(struct Mailbox *m, const char *server, bool leave_lock)
Open a connection to an NNTP server.
Definition newsrc.c:944
struct NntpMboxData * mutt_newsgroup_unsubscribe(struct NntpAccountData *adata, char *group)
Unsubscribe newsgroup.
Definition newsrc.c:1198
struct NntpAccountData * nntp_adata_new(struct Connection *conn)
Allocate and initialise a new NntpAccountData structure.
Definition adata.c:65
Nntp-specific Account data.
struct NntpEmailData * nntp_edata_get(struct Email *e)
Get the private data for this Email.
Definition edata.c:60
Nntp-specific Email data.
Usenet network mailbox type; talk to an NNTP server.
int nntp_active_fetch(struct NntpAccountData *adata, bool mark_new)
Fetch list of all newsgroups from server.
Definition nntp.c:2034
#define NNTP_ACACHE_LEN
Definition lib.h:85
#define ANUM_FMT
Definition lib.h:64
struct NntpAccountData * CurrentNewsSrv
Current NNTP news server.
Definition nntp.c:74
#define anum_t
Definition lib.h:63
Nntp-specific Mailbox data.
Usenet network mailbox type; talk to an NNTP server.
#define NNTP_SSL_PORT
Definition private.h:37
#define NNTP_PORT
Definition private.h:36
int nntp_check_new_groups(struct Mailbox *m, struct NntpAccountData *adata)
Check for new groups/articles in subscribed groups.
Definition nntp.c:2102
int nntp_open_connection(struct NntpAccountData *adata)
Connect to server, authenticate and get capabilities.
Definition nntp.c:1763
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
#define MUTT_FORMAT_NO_FLAGS
No flags are set.
Definition render.h:33
int mutt_socket_close(struct Connection *conn)
Close a socket.
Definition socket.c:100
void * adata
Private data (for Mailbox backends)
Definition account.h:42
Local cache of email bodies.
Definition bcache.c:49
String manipulation buffer.
Definition buffer.h:36
size_t dsize
Length of data.
Definition buffer.h:39
char * data
Pointer to data.
Definition buffer.h:37
Login details for a remote server.
Definition connaccount.h:53
char user[128]
Username.
Definition connaccount.h:56
char pass[256]
Password.
Definition connaccount.h:57
const char * service
Name of the service, e.g. "imap".
Definition connaccount.h:61
const char *(* get_field)(enum ConnAccountField field, void *gf_data)
Definition connaccount.h:70
unsigned char type
Connection type, e.g. MUTT_ACCT_TYPE_IMAP.
Definition connaccount.h:59
MuttAccountFlags flags
Which fields are initialised, e.g. MUTT_ACCT_USER.
Definition connaccount.h:60
unsigned short port
Port to connect to.
Definition connaccount.h:58
struct ConnAccount account
Account details: username, password, etc.
Definition connection.h:49
The envelope/body of an email.
Definition email.h:39
bool read
Email is read.
Definition email.h:50
bool old
Email is seen, but unread.
Definition email.h:49
bool deleted
Email is deleted.
Definition email.h:78
Parsed Expando trees.
Definition expando.h:41
Header Cache.
Definition lib.h:87
A mailbox.
Definition mailbox.h:78
int msg_count
Total number of messages.
Definition mailbox.h:87
enum MailboxType type
Mailbox type.
Definition mailbox.h:101
void * mdata
Driver specific data.
Definition mailbox.h:131
struct Email ** emails
Array of Emails.
Definition mailbox.h:95
Container for Accounts, Notifications.
Definition neomutt.h:41
char ** env
Private copy of the environment variables.
Definition neomutt.h:58
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
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
NNTP-specific Account data -.
Definition adata.h:36
time_t newgroups_time
Last newgroups request time.
Definition adata.h:56
bool newsrc_modified
Newsrc file was modified.
Definition adata.h:49
struct HashTable * groups_hash
Hash Table: "newsgroup" -> NntpMboxData.
Definition adata.h:62
off_t size
Newsrc file size.
Definition adata.h:54
struct NntpMboxData ** groups_list
List of newsgroups.
Definition adata.h:60
struct Connection * conn
Connection to NNTP Server.
Definition adata.h:63
char * authenticators
Authenticators list.
Definition adata.h:52
unsigned int groups_num
Number of newsgroups.
Definition adata.h:58
time_t mtime
Newsrc modification time.
Definition adata.h:55
unsigned int groups_max
Maximum number of newsgroups.
Definition adata.h:59
FILE * fp_newsrc
Newsrc file handle.
Definition adata.h:50
bool cacheable
Can be cached.
Definition adata.h:48
char * newsrc_file
Newsrc file path.
Definition adata.h:51
anum_t article_num
NNTP article number.
Definition edata.h:36
NNTP-specific Mailbox data -.
Definition mdata.h:34
anum_t last_cached
Last cached article.
Definition mdata.h:40
anum_t last_message
Last article number.
Definition mdata.h:38
struct BodyCache * bcache
Body cache.
Definition mdata.h:50
char * group
Name of newsgroup.
Definition mdata.h:35
struct NntpAccountData * adata
Account data.
Definition mdata.h:48
char * desc
Description of newsgroup.
Definition mdata.h:36
struct NewsrcEntry * newsrc_ent
Newsrc entries.
Definition mdata.h:47
anum_t unread
Unread articles.
Definition mdata.h:41
anum_t last_loaded
Last loaded article.
Definition mdata.h:39
unsigned int newsrc_len
Length of newsrc entry.
Definition mdata.h:46
anum_t first_message
First article number.
Definition mdata.h:37
bool subscribed
Subscribed to this newsgroup.
Definition mdata.h:42
A parsed URL proto://user:password@host:port/path?a=1&b=2
Definition url.h:69
char * user
Username.
Definition url.h:71
char * host
Host.
Definition url.h:73
char * src
Raw URL string.
Definition url.h:77
char * pass
Password.
Definition url.h:72
char * path
Path.
Definition url.h:75
enum UrlScheme scheme
Scheme, e.g. U_SMTPS.
Definition url.h:70
int cs_subset_str_native_set(const struct ConfigSubset *sub, const char *name, intptr_t value, struct Buffer *err)
Natively set the value of a string config item.
Definition subset.c:303
struct Url * url_parse(const char *src)
Fill in Url.
Definition url.c:239
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition url.c:124
int url_tostring(const struct Url *url, char *dest, size_t len, uint8_t flags)
Output the URL string for a given Url object.
Definition url.c:423
#define U_NO_FLAGS
No flags are set for URL parsing.
Definition url.h:49
@ U_NNTPS
Url is nntps://.
Definition url.h:42
@ U_NNTP
Url is nntp://.
Definition url.h:41
#define U_PATH
Path is included in URL.
Definition url.h:50