NeoMutt  2024-04-16-36-g75b6fb
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
newsrc.c
Go to the documentation of this file.
1
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 <unistd.h>
42#include "private.h"
43#include "mutt/lib.h"
44#include "config/lib.h"
45#include "email/lib.h"
46#include "core/lib.h"
47#include "conn/lib.h"
48#include "mutt.h"
49#include "lib.h"
50#include "bcache/lib.h"
51#include "expando/lib.h"
52#include "adata.h"
53#include "edata.h"
54#include "mdata.h"
55#include "mutt_account.h"
56#include "mutt_logging.h"
57#include "mutt_socket.h"
58#include "muttlib.h"
59#include "protos.h"
60#ifdef USE_HCACHE
61#include "hcache/lib.h"
62#endif
63
64struct BodyCache;
65
67
75static struct NntpMboxData *mdata_find(struct NntpAccountData *adata, const char *group)
76{
78 if (mdata)
79 return mdata;
80
81 size_t len = strlen(group) + 1;
82 /* create NntpMboxData structure and add it to hash */
83 mdata = mutt_mem_calloc(1, sizeof(struct NntpMboxData) + len);
84 mdata->group = (char *) mdata + sizeof(struct NntpMboxData);
85 mutt_str_copy(mdata->group, group, len);
86 mdata->adata = adata;
87 mdata->deleted = true;
89
90 /* add NntpMboxData to list */
92 {
93 adata->groups_max *= 2;
95 }
97
98 return mdata;
99}
100
106{
107 for (int i = 0; i < NNTP_ACACHE_LEN; i++)
108 {
109 if (mdata->acache[i].path)
110 {
111 unlink(mdata->acache[i].path);
112 FREE(&mdata->acache[i].path);
113 }
114 }
115}
116
122{
123 if (!adata->fp_newsrc)
124 return;
125
126 mutt_debug(LL_DEBUG1, "Unlocking %s\n", adata->newsrc_file);
129}
130
136{
137 mdata->unread = 0;
138 if ((mdata->last_message == 0) ||
139 (mdata->first_message > mdata->last_message) || !mdata->newsrc_ent)
140 {
141 return;
142 }
143
144 mdata->unread = mdata->last_message - mdata->first_message + 1;
145 for (unsigned int i = 0; i < mdata->newsrc_len; i++)
146 {
147 anum_t first = mdata->newsrc_ent[i].first;
148 if (first < mdata->first_message)
149 first = mdata->first_message;
150 anum_t last = mdata->newsrc_ent[i].last;
151 if (last > mdata->last_message)
152 last = mdata->last_message;
153 if (first <= last)
154 mdata->unread -= last - first + 1;
155 }
156}
157
166{
167 if (!adata)
168 return -1;
169
170 char *line = NULL;
171 struct stat st = { 0 };
172
173 if (adata->fp_newsrc)
174 {
175 /* if we already have a handle, close it and reopen */
177 }
178 else
179 {
180 /* if file doesn't exist, create it */
181 adata->fp_newsrc = mutt_file_fopen(adata->newsrc_file, "a");
183 }
184
185 /* open .newsrc */
186 adata->fp_newsrc = mutt_file_fopen(adata->newsrc_file, "r");
187 if (!adata->fp_newsrc)
188 {
189 mutt_perror("%s", adata->newsrc_file);
190 return -1;
191 }
192
193 /* lock it */
194 mutt_debug(LL_DEBUG1, "Locking %s\n", adata->newsrc_file);
195 if (mutt_file_lock(fileno(adata->fp_newsrc), false, true))
196 {
198 return -1;
199 }
200
201 if (stat(adata->newsrc_file, &st) != 0)
202 {
203 mutt_perror("%s", adata->newsrc_file);
204 nntp_newsrc_close(adata);
205 return -1;
206 }
207
208 if ((adata->size == st.st_size) && (adata->mtime == st.st_mtime))
209 return 0;
210
211 adata->size = st.st_size;
212 adata->mtime = st.st_mtime;
213 adata->newsrc_modified = true;
214 mutt_debug(LL_DEBUG1, "Parsing %s\n", adata->newsrc_file);
215
216 /* .newsrc has been externally modified or hasn't been loaded yet */
217 for (unsigned int i = 0; i < adata->groups_num; i++)
218 {
219 struct NntpMboxData *mdata = adata->groups_list[i];
220 if (!mdata)
221 continue;
222
223 mdata->subscribed = false;
224 mdata->newsrc_len = 0;
225 FREE(&mdata->newsrc_ent);
226 }
227
228 line = mutt_mem_malloc(st.st_size + 1);
229 while (st.st_size && fgets(line, st.st_size + 1, adata->fp_newsrc))
230 {
231 char *b = NULL, *h = NULL;
232 unsigned int j = 1;
233 bool subs = false;
234
235 /* find end of newsgroup name */
236 char *p = strpbrk(line, ":!");
237 if (!p)
238 continue;
239
240 /* ":" - subscribed, "!" - unsubscribed */
241 if (*p == ':')
242 subs = true;
243 *p++ = '\0';
244
245 /* get newsgroup data */
246 struct NntpMboxData *mdata = mdata_find(adata, line);
247 FREE(&mdata->newsrc_ent);
248
249 /* count number of entries */
250 b = p;
251 while (*b)
252 if (*b++ == ',')
253 j++;
254 mdata->newsrc_ent = mutt_mem_calloc(j, sizeof(struct NewsrcEntry));
255 mdata->subscribed = subs;
256
257 /* parse entries */
258 j = 0;
259 while (p)
260 {
261 b = p;
262
263 /* find end of entry */
264 p = strchr(p, ',');
265 if (p)
266 *p++ = '\0';
267
268 /* first-last or single number */
269 h = strchr(b, '-');
270 if (h)
271 *h++ = '\0';
272 else
273 h = b;
274
275 if ((sscanf(b, ANUM_FMT, &mdata->newsrc_ent[j].first) == 1) &&
276 (sscanf(h, ANUM_FMT, &mdata->newsrc_ent[j].last) == 1))
277 {
278 j++;
279 }
280 }
281 if (j == 0)
282 {
283 mdata->newsrc_ent[j].first = 1;
284 mdata->newsrc_ent[j].last = 0;
285 j++;
286 }
287 if (mdata->last_message == 0)
288 mdata->last_message = mdata->newsrc_ent[j - 1].last;
289 mdata->newsrc_len = j;
290 mutt_mem_realloc(&mdata->newsrc_ent, j * sizeof(struct NewsrcEntry));
292 mutt_debug(LL_DEBUG2, "%s\n", mdata->group);
293 }
294 FREE(&line);
295 return 1;
296}
297
303{
304 if (!m)
305 return;
306
307 struct NntpMboxData *mdata = m->mdata;
308 anum_t last = 0, first = 1;
309 bool series;
310 unsigned int entries;
311
312 const enum SortType c_sort = cs_subset_sort(NeoMutt->sub, "sort");
313 if (c_sort != SORT_ORDER)
314 {
317 }
318
319 entries = mdata->newsrc_len;
320 if (!entries)
321 {
322 entries = 5;
323 mdata->newsrc_ent = mutt_mem_calloc(entries, sizeof(struct NewsrcEntry));
324 }
325
326 /* Set up to fake initial sequence from 1 to the article before the
327 * first article in our list */
328 mdata->newsrc_len = 0;
329 series = true;
330 for (int i = 0; i < m->msg_count; i++)
331 {
332 struct Email *e = m->emails[i];
333 if (!e)
334 break;
335
336 /* search for first unread */
337 if (series)
338 {
339 /* We don't actually check sequential order, since we mark
340 * "missing" entries as read/deleted */
341 last = nntp_edata_get(e)->article_num;
342 if ((last >= mdata->first_message) && !e->deleted && !e->read)
343 {
344 if (mdata->newsrc_len >= entries)
345 {
346 entries *= 2;
347 mutt_mem_realloc(&mdata->newsrc_ent, entries * sizeof(struct NewsrcEntry));
348 }
349 mdata->newsrc_ent[mdata->newsrc_len].first = first;
350 mdata->newsrc_ent[mdata->newsrc_len].last = last - 1;
351 mdata->newsrc_len++;
352 series = false;
353 }
354 }
355 else
356 {
357 /* search for first read */
358 if (e->deleted || e->read)
359 {
360 first = last + 1;
361 series = true;
362 }
363 last = nntp_edata_get(e)->article_num;
364 }
365 }
366
367 if (series && (first <= mdata->last_loaded))
368 {
369 if (mdata->newsrc_len >= entries)
370 {
371 entries++;
372 mutt_mem_realloc(&mdata->newsrc_ent, entries * sizeof(struct NewsrcEntry));
373 }
374 mdata->newsrc_ent[mdata->newsrc_len].first = first;
375 mdata->newsrc_ent[mdata->newsrc_len].last = mdata->last_loaded;
376 mdata->newsrc_len++;
377 }
378 mutt_mem_realloc(&mdata->newsrc_ent, mdata->newsrc_len * sizeof(struct NewsrcEntry));
379
380 if (c_sort != SORT_ORDER)
381 {
382 cs_subset_str_native_set(NeoMutt->sub, "sort", c_sort, NULL);
384 }
385}
386
394static int update_file(char *filename, char *buf)
395{
396 FILE *fp = NULL;
397 char tmpfile[PATH_MAX] = { 0 };
398 int rc = -1;
399
400 while (true)
401 {
402 snprintf(tmpfile, sizeof(tmpfile), "%s.tmp", filename);
403 fp = mutt_file_fopen(tmpfile, "w");
404 if (!fp)
405 {
406 mutt_perror("%s", tmpfile);
407 *tmpfile = '\0';
408 break;
409 }
410 if (fputs(buf, fp) == EOF)
411 {
412 mutt_perror("%s", tmpfile);
413 break;
414 }
415 if (mutt_file_fclose(&fp) == EOF)
416 {
417 mutt_perror("%s", tmpfile);
418 fp = NULL;
419 break;
420 }
421 fp = NULL;
422 if (rename(tmpfile, filename) < 0)
423 {
424 mutt_perror("%s", filename);
425 break;
426 }
427 *tmpfile = '\0';
428 rc = 0;
429 break;
430 }
431 mutt_file_fclose(&fp);
432
433 if (*tmpfile)
434 unlink(tmpfile);
435 return rc;
436}
437
445{
446 if (!adata)
447 return -1;
448
449 int rc = -1;
450
451 size_t buflen = 10240;
452 char *buf = mutt_mem_calloc(1, buflen);
453 size_t off = 0;
454
455 /* we will generate full newsrc here */
456 for (unsigned int i = 0; i < adata->groups_num; i++)
457 {
458 struct NntpMboxData *mdata = adata->groups_list[i];
459
460 if (!mdata || !mdata->newsrc_ent)
461 continue;
462
463 /* write newsgroup name */
464 if ((off + strlen(mdata->group) + 3) > buflen)
465 {
466 buflen *= 2;
467 mutt_mem_realloc(&buf, buflen);
468 }
469 snprintf(buf + off, buflen - off, "%s%c ", mdata->group, mdata->subscribed ? ':' : '!');
470 off += strlen(buf + off);
471
472 /* write entries */
473 for (unsigned int j = 0; j < mdata->newsrc_len; j++)
474 {
475 if ((off + 1024) > buflen)
476 {
477 buflen *= 2;
478 mutt_mem_realloc(&buf, buflen);
479 }
480 if (j)
481 buf[off++] = ',';
482 if (mdata->newsrc_ent[j].first == mdata->newsrc_ent[j].last)
483 {
484 snprintf(buf + off, buflen - off, ANUM_FMT, mdata->newsrc_ent[j].first);
485 }
486 else if (mdata->newsrc_ent[j].first < mdata->newsrc_ent[j].last)
487 {
488 snprintf(buf + off, buflen - off, ANUM_FMT "-" ANUM_FMT,
489 mdata->newsrc_ent[j].first, mdata->newsrc_ent[j].last);
490 }
491 off += strlen(buf + off);
492 }
493 buf[off++] = '\n';
494 }
495 buf[off] = '\0';
496
497 /* newrc being fully rewritten */
498 mutt_debug(LL_DEBUG1, "Updating %s\n", adata->newsrc_file);
499 if (adata->newsrc_file && (update_file(adata->newsrc_file, buf) == 0))
500 {
501 struct stat st = { 0 };
502
503 rc = stat(adata->newsrc_file, &st);
504 if (rc == 0)
505 {
506 adata->size = st.st_size;
507 adata->mtime = st.st_mtime;
508 }
509 else
510 {
511 mutt_perror("%s", adata->newsrc_file);
512 }
513 }
514 FREE(&buf);
515 return rc;
516}
517
525static void cache_expand(char *dst, size_t dstlen, struct ConnAccount *cac, const char *src)
526{
527 char *c = NULL;
528 char file[PATH_MAX] = { 0 };
529
530 /* server subdirectory */
531 struct Url url = { 0 };
532 mutt_account_tourl(cac, &url);
533 url.path = mutt_str_dup(src);
534 url_tostring(&url, file, sizeof(file), U_PATH);
535 FREE(&url.path);
536
537 const char *const c_news_cache_dir = cs_subset_path(NeoMutt->sub, "news_cache_dir");
538 snprintf(dst, dstlen, "%s/%s", c_news_cache_dir, file);
539
540 /* remove trailing slash */
541 c = dst + strlen(dst) - 1;
542 if (*c == '/')
543 *c = '\0';
544
545 struct Buffer *tmp = buf_pool_get();
546 buf_addstr(tmp, dst);
547 buf_expand_path(tmp);
548 mutt_encode_path(tmp, dst);
549 mutt_str_copy(dst, buf_string(tmp), dstlen);
550 buf_pool_release(&tmp);
551}
552
559void nntp_expand_path(char *buf, size_t buflen, struct ConnAccount *cac)
560{
561 struct Url url = { 0 };
562
563 mutt_account_tourl(cac, &url);
564 url.path = mutt_str_dup(buf);
565 url_tostring(&url, buf, buflen, U_NO_FLAGS);
566 FREE(&url.path);
567}
568
575int nntp_add_group(char *line, void *data)
576{
577 struct NntpAccountData *adata = data;
578 struct NntpMboxData *mdata = NULL;
579 char group[1024] = { 0 };
580 char desc[8192] = { 0 };
581 char mod;
582 anum_t first, last;
583
584 if (!adata || !line)
585 return 0;
586
587 /* These sscanf limits must match the sizes of the group and desc arrays */
588 if (sscanf(line, "%1023s " ANUM_FMT " " ANUM_FMT " %c %8191[^\n]", group,
589 &last, &first, &mod, desc) < 4)
590 {
591 mutt_debug(LL_DEBUG2, "Can't parse server line: %s\n", line);
592 return 0;
593 }
594
596 mdata->deleted = false;
597 mdata->first_message = first;
598 mdata->last_message = last;
599 mdata->allowed = (mod == 'y') || (mod == 'm');
600 mutt_str_replace(&mdata->desc, desc);
601 if (mdata->newsrc_ent || (mdata->last_cached != 0))
603 else if (mdata->last_message && (mdata->first_message <= mdata->last_message))
604 mdata->unread = mdata->last_message - mdata->first_message + 1;
605 else
606 mdata->unread = 0;
607 return 0;
608}
609
617{
618 char buf[8192] = { 0 };
619 char file[4096] = { 0 };
620 time_t t = 0;
621
622 cache_expand(file, sizeof(file), &adata->conn->account, ".active");
623 mutt_debug(LL_DEBUG1, "Parsing %s\n", file);
624 FILE *fp = mutt_file_fopen(file, "r");
625 if (!fp)
626 return -1;
627
628 if (!fgets(buf, sizeof(buf), fp) || (sscanf(buf, "%jd%4095s", &t, file) != 1) || (t == 0))
629 {
630 mutt_file_fclose(&fp);
631 return -1;
632 }
634
635 mutt_message(_("Loading list of groups from cache..."));
636 while (fgets(buf, sizeof(buf), fp))
637 nntp_add_group(buf, adata);
638 nntp_add_group(NULL, NULL);
639 mutt_file_fclose(&fp);
641 return 0;
642}
643
651{
652 if (!adata->cacheable)
653 return 0;
654
655 size_t buflen = 10240;
656 char *buf = mutt_mem_calloc(1, buflen);
657 snprintf(buf, buflen, "%lu\n", (unsigned long) adata->newgroups_time);
658 size_t off = strlen(buf);
659
660 for (unsigned int i = 0; i < adata->groups_num; i++)
661 {
662 struct NntpMboxData *mdata = adata->groups_list[i];
663
664 if (!mdata || mdata->deleted)
665 continue;
666
667 if ((off + strlen(mdata->group) + (mdata->desc ? strlen(mdata->desc) : 0) + 50) > buflen)
668 {
669 buflen *= 2;
670 mutt_mem_realloc(&buf, buflen);
671 }
672 snprintf(buf + off, buflen - off, "%s " ANUM_FMT " " ANUM_FMT " %c%s%s\n",
673 mdata->group, mdata->last_message, mdata->first_message,
674 mdata->allowed ? 'y' : 'n', mdata->desc ? " " : "",
675 mdata->desc ? mdata->desc : "");
676 off += strlen(buf + off);
677 }
678
679 char file[PATH_MAX] = { 0 };
680 cache_expand(file, sizeof(file), &adata->conn->account, ".active");
681 mutt_debug(LL_DEBUG1, "Updating %s\n", file);
682 int rc = update_file(file, buf);
683 FREE(&buf);
684 return rc;
685}
686
687#ifdef USE_HCACHE
691static void nntp_hcache_namer(const char *path, struct Buffer *dest)
692{
693 buf_printf(dest, "%s.hcache", path);
694
695 /* Strip out any directories in the path */
696 char *first = strchr(buf_string(dest), '/');
697 char *last = strrchr(buf_string(dest), '/');
698 if (first && last && (last > first))
699 {
700 memmove(first, last, strlen(last) + 1);
701 }
702}
703
711{
712 struct Url url = { 0 };
713 char file[PATH_MAX] = { 0 };
714
715 const bool c_save_unsubscribed = cs_subset_bool(NeoMutt->sub, "save_unsubscribed");
716 if (!mdata->adata || !mdata->adata->cacheable || !mdata->adata->conn || !mdata->group ||
717 !(mdata->newsrc_ent || mdata->subscribed || c_save_unsubscribed))
718 {
719 return NULL;
720 }
721
722 mutt_account_tourl(&mdata->adata->conn->account, &url);
723 url.path = mdata->group;
724 url_tostring(&url, file, sizeof(file), U_PATH);
725 const char *const c_news_cache_dir = cs_subset_path(NeoMutt->sub, "news_cache_dir");
726 return hcache_open(c_news_cache_dir, file, nntp_hcache_namer);
727}
728
734void nntp_hcache_update(struct NntpMboxData *mdata, struct HeaderCache *hc)
735{
736 if (!hc)
737 return;
738
739 char buf[32] = { 0 };
740 bool old = false;
741 anum_t first = 0, last = 0;
742
743 /* fetch previous values of first and last */
744 char *hdata = hcache_fetch_raw_str(hc, "index", 5);
745 if (hdata)
746 {
747 mutt_debug(LL_DEBUG2, "hcache_fetch_email index: %s\n", hdata);
748 if (sscanf(hdata, ANUM_FMT " " ANUM_FMT, &first, &last) == 2)
749 {
750 old = true;
751 mdata->last_cached = last;
752
753 /* clean removed headers from cache */
754 for (anum_t current = first; current <= last; current++)
755 {
756 if ((current >= mdata->first_message) && (current <= mdata->last_message))
757 continue;
758
759 snprintf(buf, sizeof(buf), ANUM_FMT, current);
760 mutt_debug(LL_DEBUG2, "hcache_delete_email %s\n", buf);
761 hcache_delete_email(hc, buf, strlen(buf));
762 }
763 }
764 FREE(&hdata);
765 }
766
767 /* store current values of first and last */
768 if (!old || (mdata->first_message != first) || (mdata->last_message != last))
769 {
770 snprintf(buf, sizeof(buf), ANUM_FMT " " ANUM_FMT, mdata->first_message,
771 mdata->last_message);
772 mutt_debug(LL_DEBUG2, "hcache_store_email index: %s\n", buf);
773 hcache_store_raw(hc, "index", 5, buf, strlen(buf) + 1);
774 }
775}
776#endif
777
782static int nntp_bcache_delete(const char *id, struct BodyCache *bcache, void *data)
783{
784 struct NntpMboxData *mdata = data;
785 anum_t anum;
786 char c;
787
788 if (!mdata || (sscanf(id, ANUM_FMT "%c", &anum, &c) != 1) ||
789 (anum < mdata->first_message) || (anum > mdata->last_message))
790 {
791 if (mdata)
792 mutt_debug(LL_DEBUG2, "mutt_bcache_del %s\n", id);
794 }
795 return 0;
796}
797
803{
805}
806
812{
813 if (!mdata || !mdata->adata || !mdata->adata->cacheable)
814 return;
815
816#ifdef USE_HCACHE
817 struct Buffer *file = buf_pool_get();
818 nntp_hcache_namer(mdata->group, file);
819 cache_expand(file->data, file->dsize, &mdata->adata->conn->account, buf_string(file));
820 unlink(buf_string(file));
821 mdata->last_cached = 0;
822 mutt_debug(LL_DEBUG2, "%s\n", buf_string(file));
823 buf_pool_release(&file);
824#endif
825
826 if (!mdata->bcache)
827 {
828 mdata->bcache = mutt_bcache_open(&mdata->adata->conn->account, mdata->group);
829 }
830 if (mdata->bcache)
831 {
832 mutt_debug(LL_DEBUG2, "%s/*\n", mdata->group);
834 mutt_bcache_close(&mdata->bcache);
835 }
836}
837
845{
846 if (!adata || !adata->cacheable)
847 return;
848
849 struct dirent *de = NULL;
850 DIR *dir = NULL;
851 struct Buffer *cache = buf_pool_get();
852 struct Buffer *file = buf_pool_get();
853
854 cache_expand(cache->data, cache->dsize, &adata->conn->account, NULL);
856 if (!dir)
857 goto done;
858
859 buf_addch(cache, '/');
860 const bool c_save_unsubscribed = cs_subset_bool(NeoMutt->sub, "save_unsubscribed");
861
862 while ((de = readdir(dir)))
863 {
864 char *group = de->d_name;
865 if (mutt_str_equal(group, ".") || mutt_str_equal(group, ".."))
866 continue;
867
868 buf_printf(file, "%s%s", buf_string(cache), group);
869 struct stat st = { 0 };
870 if (stat(buf_string(file), &st) != 0)
871 continue;
872
873#ifdef USE_HCACHE
874 if (S_ISREG(st.st_mode))
875 {
876 char *ext = group + strlen(group) - 7;
877 if ((strlen(group) < 8) || !mutt_str_equal(ext, ".hcache"))
878 continue;
879 *ext = '\0';
880 }
881 else
882#endif
883 if (!S_ISDIR(st.st_mode))
884 continue;
885
886 struct NntpMboxData tmp_mdata = { 0 };
888 if (!mdata)
889 {
890 mdata = &tmp_mdata;
891 mdata->adata = adata;
892 mdata->group = group;
893 mdata->bcache = NULL;
894 }
895 else if (mdata->newsrc_ent || mdata->subscribed || c_save_unsubscribed)
896 {
897 continue;
898 }
899
901 if (S_ISDIR(st.st_mode))
902 {
903 rmdir(buf_string(file));
904 mutt_debug(LL_DEBUG2, "%s\n", buf_string(file));
905 }
906 }
907 closedir(dir);
908
909done:
910 buf_pool_release(&cache);
911 buf_pool_release(&file);
912}
913
917void nntp_a(const struct ExpandoNode *node, void *data, MuttFormatFlags flags,
918 int max_cols, struct Buffer *buf)
919{
920 struct NntpAccountData *adata = data;
921 struct ConnAccount *cac = &adata->conn->account;
922
923 char tmp[128] = { 0 };
924
925 struct Url url = { 0 };
926 mutt_account_tourl(cac, &url);
927 url_tostring(&url, tmp, sizeof(tmp), U_PATH);
928 char *p = strchr(tmp, '/');
929 if (p)
930 {
931 *p = '\0';
932 }
933
934 buf_strcpy(buf, tmp);
935}
936
940long nntp_p_num(const struct ExpandoNode *node, void *data, MuttFormatFlags flags)
941{
942 const struct NntpAccountData *adata = data;
943 const struct ConnAccount *cac = &adata->conn->account;
944
945 return cac->port;
946}
947
951long nntp_P_num(const struct ExpandoNode *node, void *data, MuttFormatFlags flags)
952{
953 const struct NntpAccountData *adata = data;
954 const struct ConnAccount *cac = &adata->conn->account;
955
956 if (cac->flags & MUTT_ACCT_PORT)
957 return cac->port;
958
959 return 0;
960}
961
965void nntp_P(const struct ExpandoNode *node, void *data, MuttFormatFlags flags,
966 int max_cols, struct Buffer *buf)
967{
968 const struct NntpAccountData *adata = data;
969 const struct ConnAccount *cac = &adata->conn->account;
970
971 if (cac->flags & MUTT_ACCT_PORT)
972 {
973 buf_add_printf(buf, "%hd", cac->port);
974 }
975}
976
980void nntp_s(const struct ExpandoNode *node, void *data, MuttFormatFlags flags,
981 int max_cols, struct Buffer *buf)
982{
983 const struct NntpAccountData *adata = data;
984 const struct ConnAccount *cac = &adata->conn->account;
985
986 char tmp[128] = { 0 };
987
988 mutt_str_copy(tmp, cac->host, sizeof(tmp));
989 mutt_str_lower(tmp);
990
991 buf_strcpy(buf, tmp);
992}
993
997void nntp_S(const struct ExpandoNode *node, void *data, MuttFormatFlags flags,
998 int max_cols, struct Buffer *buf)
999{
1000 struct NntpAccountData *adata = data;
1001 struct ConnAccount *cac = &adata->conn->account;
1002
1003 char tmp[128] = { 0 };
1004
1005 struct Url url = { 0 };
1006 mutt_account_tourl(cac, &url);
1007 url_tostring(&url, tmp, sizeof(tmp), U_PATH);
1008 char *p = strchr(tmp, ':');
1009 if (p)
1010 {
1011 *p = '\0';
1012 }
1013
1014 buf_strcpy(buf, tmp);
1015}
1016
1020void nntp_u(const struct ExpandoNode *node, void *data, MuttFormatFlags flags,
1021 int max_cols, struct Buffer *buf)
1022{
1023 const struct NntpAccountData *adata = data;
1024 const struct ConnAccount *cac = &adata->conn->account;
1025
1026 const char *s = cac->user;
1027 buf_strcpy(buf, s);
1028}
1029
1033static const char *nntp_get_field(enum ConnAccountField field, void *gf_data)
1034{
1035 switch (field)
1036 {
1037 case MUTT_CA_LOGIN:
1038 case MUTT_CA_USER:
1039 return cs_subset_string(NeoMutt->sub, "nntp_user");
1040 case MUTT_CA_PASS:
1041 return cs_subset_string(NeoMutt->sub, "nntp_pass");
1042 case MUTT_CA_OAUTH_CMD:
1043 case MUTT_CA_HOST:
1044 default:
1045 return NULL;
1046 }
1047}
1048
1064struct NntpAccountData *nntp_select_server(struct Mailbox *m, const char *server, bool leave_lock)
1065{
1066 char file[PATH_MAX] = { 0 };
1067 int rc;
1068 struct ConnAccount cac = { { 0 } };
1069 struct NntpAccountData *adata = NULL;
1070 struct Connection *conn = NULL;
1071
1072 if (!server || (*server == '\0'))
1073 {
1074 mutt_error(_("No news server defined"));
1075 return NULL;
1076 }
1077
1078 /* create account from news server url */
1079 cac.flags = 0;
1080 cac.port = NNTP_PORT;
1082 cac.service = "nntp";
1084
1085 snprintf(file, sizeof(file), "%s%s", strstr(server, "://") ? "" : "news://", server);
1086 struct Url *url = url_parse(file);
1087 if (!url || (url->path && *url->path) ||
1088 !((url->scheme == U_NNTP) || (url->scheme == U_NNTPS)) || !url->host ||
1089 (mutt_account_fromurl(&cac, url) < 0))
1090 {
1091 url_free(&url);
1092 mutt_error(_("%s is an invalid news server specification"), server);
1093 return NULL;
1094 }
1095 if (url->scheme == U_NNTPS)
1096 {
1097 cac.flags |= MUTT_ACCT_SSL;
1098 cac.port = NNTP_SSL_PORT;
1099 }
1100 url_free(&url);
1101
1102 // If nntp_user and nntp_pass are specified in the config, use them to find the connection
1103 const char *user = NULL;
1104 const char *pass = NULL;
1105 if ((user = cac.get_field(MUTT_CA_USER, NULL)))
1106 {
1107 mutt_str_copy(cac.user, user, sizeof(cac.user));
1108 cac.flags |= MUTT_ACCT_USER;
1109 }
1110 if ((pass = cac.get_field(MUTT_CA_PASS, NULL)))
1111 {
1112 mutt_str_copy(cac.pass, pass, sizeof(cac.pass));
1113 cac.flags |= MUTT_ACCT_PASS;
1114 }
1115
1116 /* find connection by account */
1117 conn = mutt_conn_find(&cac);
1118 if (!conn)
1119 return NULL;
1120 if (!(conn->account.flags & MUTT_ACCT_USER) && cac.flags & MUTT_ACCT_USER)
1121 {
1122 conn->account.flags |= MUTT_ACCT_USER;
1123 conn->account.user[0] = '\0';
1124 }
1125
1126 /* new news server */
1127 adata = nntp_adata_new(conn);
1128
1129 rc = nntp_open_connection(adata);
1130
1131 /* try to create cache directory and enable caching */
1132 adata->cacheable = false;
1133 const char *const c_news_cache_dir = cs_subset_path(NeoMutt->sub, "news_cache_dir");
1134 if ((rc >= 0) && c_news_cache_dir)
1135 {
1136 cache_expand(file, sizeof(file), &conn->account, NULL);
1137 if (mutt_file_mkdir(file, S_IRWXU) < 0)
1138 {
1139 mutt_error(_("Can't create %s: %s"), file, strerror(errno));
1140 }
1141 adata->cacheable = true;
1142 }
1143
1144 /* load .newsrc */
1145 if (rc >= 0)
1146 {
1147 const struct Expando *c_newsrc = cs_subset_expando(NeoMutt->sub, "newsrc");
1148 struct Buffer *buf = buf_pool_get();
1149 expando_render(c_newsrc, NntpRenderData, adata, MUTT_FORMAT_NO_FLAGS, buf->dsize, buf);
1150 mutt_expand_path(buf->data, buf->dsize);
1151 adata->newsrc_file = buf_strdup(buf);
1152 buf_pool_release(&buf);
1153 rc = nntp_newsrc_parse(adata);
1154 }
1155 if (rc >= 0)
1156 {
1157 /* try to load list of newsgroups from cache */
1158 if (adata->cacheable && (active_get_cache(adata) == 0))
1159 {
1160 rc = nntp_check_new_groups(m, adata);
1161 }
1162 else
1163 {
1164 /* load list of newsgroups from server */
1165 rc = nntp_active_fetch(adata, false);
1166 }
1167 }
1168
1169 if (rc >= 0)
1170 nntp_clear_cache(adata);
1171
1172#ifdef USE_HCACHE
1173 /* check cache files */
1174 if ((rc >= 0) && adata->cacheable)
1175 {
1176 struct dirent *de = NULL;
1177 DIR *dir = mutt_file_opendir(file, MUTT_OPENDIR_NONE);
1178
1179 if (dir)
1180 {
1181 while ((de = readdir(dir)))
1182 {
1183 struct HeaderCache *hc = NULL;
1184 char *group = de->d_name;
1185
1186 char *p = group + strlen(group) - 7;
1187 if ((strlen(group) < 8) || !mutt_str_equal(p, ".hcache"))
1188 continue;
1189 *p = '\0';
1191 if (!mdata)
1192 continue;
1193
1194 hc = nntp_hcache_open(mdata);
1195 if (!hc)
1196 continue;
1197
1198 /* fetch previous values of first and last */
1199 char *hdata = hcache_fetch_raw_str(hc, "index", 5);
1200 if (hdata)
1201 {
1202 anum_t first, last;
1203
1204 if (sscanf(hdata, ANUM_FMT " " ANUM_FMT, &first, &last) == 2)
1205 {
1206 if (mdata->deleted)
1207 {
1208 mdata->first_message = first;
1209 mdata->last_message = last;
1210 }
1211 if ((last >= mdata->first_message) && (last <= mdata->last_message))
1212 {
1213 mdata->last_cached = last;
1214 mutt_debug(LL_DEBUG2, "%s last_cached=" ANUM_FMT "\n", mdata->group, last);
1215 }
1216 }
1217 FREE(&hdata);
1218 }
1219 hcache_close(&hc);
1220 }
1221 closedir(dir);
1222 }
1223 }
1224#endif
1225
1226 if ((rc < 0) || !leave_lock)
1228
1229 if (rc < 0)
1230 {
1235 FREE(&adata);
1236 mutt_socket_close(conn);
1237 FREE(&conn);
1238 return NULL;
1239 }
1240
1241 return adata;
1242}
1243
1256void nntp_article_status(struct Mailbox *m, struct Email *e, char *group, anum_t anum)
1257{
1258 struct NntpMboxData *mdata = m->mdata;
1259
1260 if (group)
1261 mdata = mutt_hash_find(mdata->adata->groups_hash, group);
1262
1263 if (!mdata)
1264 return;
1265
1266 for (unsigned int i = 0; i < mdata->newsrc_len; i++)
1267 {
1268 if ((anum >= mdata->newsrc_ent[i].first) && (anum <= mdata->newsrc_ent[i].last))
1269 {
1270 /* can't use mutt_set_flag() because mview_update() didn't get called yet */
1271 e->read = true;
1272 return;
1273 }
1274 }
1275
1276 /* article was not cached yet, it's new */
1277 if (anum > mdata->last_cached)
1278 return;
1279
1280 /* article isn't read but cached, it's old */
1281 const bool c_mark_old = cs_subset_bool(NeoMutt->sub, "mark_old");
1282 if (c_mark_old)
1283 e->old = true;
1284}
1285
1294{
1295 if (!adata || !adata->groups_hash || !group || (*group == '\0'))
1296 return NULL;
1297
1299 mdata->subscribed = true;
1300 if (!mdata->newsrc_ent)
1301 {
1302 mdata->newsrc_ent = mutt_mem_calloc(1, sizeof(struct NewsrcEntry));
1303 mdata->newsrc_len = 1;
1304 mdata->newsrc_ent[0].first = 1;
1305 mdata->newsrc_ent[0].last = 0;
1306 }
1307 return mdata;
1308}
1309
1318{
1319 if (!adata || !adata->groups_hash || !group || (*group == '\0'))
1320 return NULL;
1321
1323 if (!mdata)
1324 return NULL;
1325
1326 mdata->subscribed = false;
1327 const bool c_save_unsubscribed = cs_subset_bool(NeoMutt->sub, "save_unsubscribed");
1328 if (!c_save_unsubscribed)
1329 {
1330 mdata->newsrc_len = 0;
1331 FREE(&mdata->newsrc_ent);
1332 }
1333 return mdata;
1334}
1335
1345 struct NntpAccountData *adata, char *group)
1346{
1347 if (!adata || !adata->groups_hash || !group || (*group == '\0'))
1348 return NULL;
1349
1351 if (!mdata)
1352 return NULL;
1353
1354 if (mdata->newsrc_ent)
1355 {
1356 mutt_mem_realloc(&mdata->newsrc_ent, sizeof(struct NewsrcEntry));
1357 mdata->newsrc_len = 1;
1358 mdata->newsrc_ent[0].first = 1;
1359 mdata->newsrc_ent[0].last = mdata->last_message;
1360 }
1361 mdata->unread = 0;
1362 if (m && (m->mdata == mdata))
1363 {
1364 for (unsigned int i = 0; i < m->msg_count; i++)
1365 {
1366 struct Email *e = m->emails[i];
1367 if (!e)
1368 break;
1369 mutt_set_flag(m, e, MUTT_READ, true, true);
1370 }
1371 }
1372 return mdata;
1373}
1374
1384 struct NntpAccountData *adata, char *group)
1385{
1386 if (!adata || !adata->groups_hash || !group || (*group == '\0'))
1387 return NULL;
1388
1390 if (!mdata)
1391 return NULL;
1392
1393 if (mdata->newsrc_ent)
1394 {
1395 mutt_mem_realloc(&mdata->newsrc_ent, sizeof(struct NewsrcEntry));
1396 mdata->newsrc_len = 1;
1397 mdata->newsrc_ent[0].first = 1;
1398 mdata->newsrc_ent[0].last = mdata->first_message - 1;
1399 }
1400 if (m && (m->mdata == mdata))
1401 {
1402 mdata->unread = m->msg_count;
1403 for (unsigned int i = 0; i < m->msg_count; i++)
1404 {
1405 struct Email *e = m->emails[i];
1406 if (!e)
1407 break;
1408 mutt_set_flag(m, e, MUTT_READ, false, true);
1409 }
1410 }
1411 else
1412 {
1413 mdata->unread = mdata->last_message;
1414 if (mdata->newsrc_ent)
1415 mdata->unread -= mdata->newsrc_ent[0].last;
1416 }
1417 return mdata;
1418}
1419
1426void nntp_mailbox(struct Mailbox *m, char *buf, size_t buflen)
1427{
1428 if (!m)
1429 return;
1430
1431 for (unsigned int i = 0; i < CurrentNewsSrv->groups_num; i++)
1432 {
1434
1435 if (!mdata || !mdata->subscribed || !mdata->unread)
1436 continue;
1437
1438 if ((m->type == MUTT_NNTP) &&
1439 mutt_str_equal(mdata->group, ((struct NntpMboxData *) m->mdata)->group))
1440 {
1441 unsigned int unread = 0;
1442
1443 for (unsigned int j = 0; j < m->msg_count; j++)
1444 {
1445 struct Email *e = m->emails[j];
1446 if (!e)
1447 break;
1448 if (!e->read && !e->deleted)
1449 unread++;
1450 }
1451 if (unread == 0)
1452 continue;
1453 }
1454 mutt_str_copy(buf, mdata->group, buflen);
1455 break;
1456 }
1457}
1458
1465 // clang-format off
1466 { ED_NNTP, ED_NTP_ACCOUNT, nntp_a, NULL },
1467 { ED_NNTP, ED_NTP_PORT, NULL, nntp_p_num },
1469 { ED_NNTP, ED_NTP_SCHEMA, nntp_S, NULL },
1470 { ED_NNTP, ED_NTP_SERVER, nntp_s, NULL },
1471 { ED_NNTP, ED_NTP_USERNAME, nntp_u, NULL },
1472 { -1, -1, NULL, NULL },
1473 // clang-format on
1474};
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:143
int mutt_bcache_list(struct BodyCache *bcache, bcache_list_t want_id, void *data)
Find matching entries in the Body Cache.
Definition: bcache.c:331
void mutt_bcache_close(struct BodyCache **ptr)
Close an Email-Body Cache.
Definition: bcache.c:164
int mutt_bcache_del(struct BodyCache *bcache, const char *id)
Delete a file from the Body Cache.
Definition: bcache.c:266
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:203
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:394
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition: buffer.c:570
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:292
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:169
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:48
short cs_subset_sort(const struct ConfigSubset *sub, const char *name)
Get a sort config item by name.
Definition: helpers.c:267
const struct Expando * cs_subset_expando(const struct ConfigSubset *sub, const char *name)
Get an Expando config item by name.
Definition: config_type.c:358
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
@ ED_NTP_SCHEMA
ConnAccount.account.
Definition: connaccount.h:85
@ ED_NTP_USERNAME
ConnAccount.user.
Definition: connaccount.h:87
@ ED_NTP_PORT_IF
ConnAccount.port.
Definition: connaccount.h:84
@ ED_NTP_SERVER
ConnAccount.account.
Definition: connaccount.h:86
@ ED_NTP_ACCOUNT
ConnAccount.account.
Definition: connaccount.h:82
@ ED_NTP_PORT
ConnAccount.port.
Definition: connaccount.h:83
#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
#define MUTT_ACCT_PORT
Port field has been set.
Definition: connaccount.h:43
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:234
@ NT_MAILBOX_RESORT
Email list needs resorting.
Definition: mailbox.h:190
@ MUTT_NNTP
'NNTP' (Usenet) Mailbox type
Definition: mailbox.h:49
@ ED_NNTP
Nntp ED_NTP_ ExpandoDataNntp.
Definition: domain.h:50
Structs that make up an email.
Parse Expando string.
int expando_render(const struct Expando *exp, const struct ExpandoRenderData *rdata, void *data, MuttFormatFlags flags, int max_cols, struct Buffer *buf)
Render an Expando + data into a string.
Definition: expando.c:109
int mutt_file_mkdir(const char *path, mode_t mode)
Recursively create directories.
Definition: file.c:971
int mutt_file_lock(int fd, bool excl, bool timeout)
(Try to) Lock a file using fcntl()
Definition: file.c:1199
int mutt_file_unlock(int fd)
Unlock a file previously locked by mutt_file_lock()
Definition: file.c:1246
DIR * mutt_file_opendir(const char *path, enum MuttOpenDirMode mode)
Open a directory.
Definition: file.c:640
@ MUTT_OPENDIR_NONE
Plain opendir()
Definition: file.h:74
#define mutt_file_fclose(FP)
Definition: file.h:147
#define mutt_file_fopen(PATH, MODE)
Definition: file.h:146
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:57
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:782
static const char * nntp_get_field(enum ConnAccountField field, void *gf_data)
Get connection login credentials - Implements ConnAccount::get_field() -.
Definition: newsrc.c:1033
long nntp_p_num(const struct ExpandoNode *node, void *data, MuttFormatFlags flags)
Newsrc: Port - Implements ExpandoRenderData::get_number() -.
Definition: newsrc.c:940
long nntp_P_num(const struct ExpandoNode *node, void *data, MuttFormatFlags flags)
Newsrc: Port if specified - Implements ExpandoRenderData::get_number() -.
Definition: newsrc.c:951
void nntp_s(const struct ExpandoNode *node, void *data, MuttFormatFlags flags, int max_cols, struct Buffer *buf)
Newsrc: News server name - Implements ExpandoRenderData::get_string() -.
Definition: newsrc.c:980
void nntp_P(const struct ExpandoNode *node, void *data, MuttFormatFlags flags, int max_cols, struct Buffer *buf)
Newsrc: Port if specified - Implements ExpandoRenderData::get_string() -.
Definition: newsrc.c:965
void nntp_a(const struct ExpandoNode *node, void *data, MuttFormatFlags flags, int max_cols, struct Buffer *buf)
Newsrc: Account url - Implements ExpandoRenderData::get_string() -.
Definition: newsrc.c:917
void nntp_u(const struct ExpandoNode *node, void *data, MuttFormatFlags flags, int max_cols, struct Buffer *buf)
Newsrc: Username - Implements ExpandoRenderData::get_string() -.
Definition: newsrc.c:1020
void nntp_S(const struct ExpandoNode *node, void *data, MuttFormatFlags flags, int max_cols, struct Buffer *buf)
Newsrc: Url schema - Implements ExpandoRenderData::get_string() -.
Definition: newsrc.c:997
static void nntp_hcache_namer(const char *path, struct Buffer *dest)
Compose hcache file names - Implements hcache_namer_t -.
Definition: newsrc.c:691
#define mutt_error(...)
Definition: logging2.h:92
#define mutt_message(...)
Definition: logging2.h:91
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
#define mutt_perror(...)
Definition: logging2.h:93
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:335
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:362
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition: hash.c:457
struct HeaderCache * hcache_open(const char *path, const char *folder, hcache_namer_t namer)
Multiplexor for StoreOps::open.
Definition: hcache.c:471
int hcache_delete_email(struct HeaderCache *hc, const char *key, size_t keylen)
Multiplexor for StoreOps::delete_record.
Definition: hcache.c:737
void hcache_close(struct HeaderCache **ptr)
Multiplexor for StoreOps::close.
Definition: hcache.c:540
char * hcache_fetch_raw_str(struct HeaderCache *hc, const char *key, size_t keylen)
Fetch a string from the cache.
Definition: hcache.c:650
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:722
Header cache multiplexor.
@ LL_DEBUG2
Log at debug level 2.
Definition: logging2.h:44
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:43
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
#define FREE(x)
Definition: memory.h:45
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:253
char * mutt_str_lower(char *str)
Convert all characters in the string to lowercase.
Definition: string.c:307
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:654
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:575
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:274
Many unsorted constants and some structs.
@ MUTT_READ
Messages that have been read.
Definition: mutt.h:73
#define PATH_MAX
Definition: mutt.h:42
int mutt_account_fromurl(struct ConnAccount *cac, const struct Url *url)
Fill ConnAccount with information from url.
Definition: mutt_account.c:44
void mutt_account_tourl(struct ConnAccount *cac, struct Url *url)
Fill URL with info from account.
Definition: mutt_account.c:80
ConnAccount object used by POP and IMAP.
@ MUTT_ACCT_TYPE_NNTP
Nntp (Usenet) Account.
Definition: mutt_account.h:41
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:74
NeoMutt Logging.
struct Connection * mutt_conn_find(const struct ConnAccount *cac)
Find a connection from a list.
Definition: mutt_socket.c:90
NeoMutt connections.
char * mutt_expand_path(char *buf, size_t buflen)
Create the canonical path.
Definition: muttlib.c:123
void buf_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:328
void mutt_encode_path(struct Buffer *buf, const char *src)
Convert a path to 'us-ascii'.
Definition: muttlib.c:907
Some miscellaneous functions.
struct HeaderCache * nntp_hcache_open(struct NntpMboxData *mdata)
Open newsgroup hcache.
Definition: newsrc.c:710
void nntp_clear_cache(struct NntpAccountData *adata)
Clear the NNTP cache.
Definition: newsrc.c:844
void nntp_delete_group_cache(struct NntpMboxData *mdata)
Remove hcache and bcache of newsgroup.
Definition: newsrc.c:811
const struct ExpandoRenderData NntpRenderData[]
Callbacks for Newsrc Expandos.
Definition: newsrc.c:66
void nntp_newsrc_gen_entries(struct Mailbox *m)
Generate array of .newsrc entries.
Definition: newsrc.c:302
void nntp_hcache_update(struct NntpMboxData *mdata, struct HeaderCache *hc)
Remove stale cached headers.
Definition: newsrc.c:734
void nntp_article_status(struct Mailbox *m, struct Email *e, char *group, anum_t anum)
Get status of articles from .newsrc.
Definition: newsrc.c:1256
static int update_file(char *filename, char *buf)
Update file with new contents.
Definition: newsrc.c:394
int nntp_add_group(char *line, void *data)
Parse newsgroup.
Definition: newsrc.c:575
int nntp_newsrc_parse(struct NntpAccountData *adata)
Parse .newsrc file.
Definition: newsrc.c:165
void nntp_mailbox(struct Mailbox *m, char *buf, size_t buflen)
Get first newsgroup with new messages.
Definition: newsrc.c:1426
void nntp_newsrc_close(struct NntpAccountData *adata)
Unlock and close .newsrc file.
Definition: newsrc.c:121
struct NntpMboxData * mutt_newsgroup_catchup(struct Mailbox *m, struct NntpAccountData *adata, char *group)
Catchup newsgroup.
Definition: newsrc.c:1344
int nntp_newsrc_update(struct NntpAccountData *adata)
Update .newsrc file.
Definition: newsrc.c:444
struct NntpMboxData * mutt_newsgroup_subscribe(struct NntpAccountData *adata, char *group)
Subscribe newsgroup.
Definition: newsrc.c:1293
void nntp_expand_path(char *buf, size_t buflen, struct ConnAccount *cac)
Make fully qualified url from newsgroup name.
Definition: newsrc.c:559
static int active_get_cache(struct NntpAccountData *adata)
Load list of all newsgroups from cache.
Definition: newsrc.c:616
static void cache_expand(char *dst, size_t dstlen, struct ConnAccount *cac, const char *src)
Make fully qualified cache file name.
Definition: newsrc.c:525
static struct NntpMboxData * mdata_find(struct NntpAccountData *adata, const char *group)
Find NntpMboxData for given newsgroup or add it.
Definition: newsrc.c:75
struct NntpMboxData * mutt_newsgroup_uncatchup(struct Mailbox *m, struct NntpAccountData *adata, char *group)
Uncatchup newsgroup.
Definition: newsrc.c:1383
int nntp_active_save_cache(struct NntpAccountData *adata)
Save list of all newsgroups to cache.
Definition: newsrc.c:650
void nntp_bcache_update(struct NntpMboxData *mdata)
Remove stale cached messages.
Definition: newsrc.c:802
void nntp_group_unread_stat(struct NntpMboxData *mdata)
Count number of unread articles using .newsrc data.
Definition: newsrc.c:135
void nntp_acache_free(struct NntpMboxData *mdata)
Remove all temporarily cache files.
Definition: newsrc.c:105
struct NntpAccountData * nntp_select_server(struct Mailbox *m, const char *server, bool leave_lock)
Open a connection to an NNTP server.
Definition: newsrc.c:1064
struct NntpMboxData * mutt_newsgroup_unsubscribe(struct NntpAccountData *adata, char *group)
Unsubscribe newsgroup.
Definition: newsrc.c:1317
struct NntpAccountData * nntp_adata_new(struct Connection *conn)
Allocate and initialise a new NntpAccountData structure.
Definition: adata.c:65
struct NntpEmailData * nntp_edata_get(struct Email *e)
Get the private data for this Email.
Definition: edata.c:60
int nntp_active_fetch(struct NntpAccountData *adata, bool mark_new)
Fetch list of all newsgroups from server.
Definition: nntp.c:2009
#define NNTP_ACACHE_LEN
Definition: lib.h:82
#define ANUM_FMT
Definition: lib.h:61
struct NntpAccountData * CurrentNewsSrv
Current NNTP news server.
Definition: nntp.c:77
#define anum_t
Definition: lib.h:60
#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:2077
int nntp_open_connection(struct NntpAccountData *adata)
Connect to server, authenticate and get capabilities.
Definition: nntp.c:1754
Notmuch-specific Mailbox data.
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
Pop-specific Account data.
Pop-specific Email data.
Prototypes for many functions.
#define MUTT_FORMAT_NO_FLAGS
No flags are set.
Definition: render.h:33
uint8_t MuttFormatFlags
Flags for expando_render(), e.g. MUTT_FORMAT_FORCESUBJ.
Definition: render.h:32
GUI display the mailboxes in a side panel.
int mutt_socket_close(struct Connection *conn)
Close a socket.
Definition: socket.c:100
SortType
Methods for sorting.
Definition: sort2.h:34
@ SORT_ORDER
Sort by the order the messages appear in the mailbox.
Definition: sort2.h:40
Key value store.
void * adata
Private data (for Mailbox backends)
Definition: account.h:42
Local cache of email bodies.
Definition: bcache.c:51
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
char host[128]
Server to login to.
Definition: connaccount.h:54
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
void * gf_data
Private data to pass to get_field()
Definition: connaccount.h:72
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
Basic Expando Node.
Definition: node.h:67
Parsed Expando trees.
Definition: expando.h:41
Header Cache.
Definition: lib.h:86
A mailbox.
Definition: mailbox.h:79
int msg_count
Total number of messages.
Definition: mailbox.h:88
enum MailboxType type
Mailbox type.
Definition: mailbox.h:102
void * mdata
Driver specific data.
Definition: mailbox.h:132
struct Email ** emails
Array of Emails.
Definition: mailbox.h:96
Container for Accounts, Notifications.
Definition: neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:45
An entry in a .newsrc (subscribed newsgroups)
Definition: lib.h:76
anum_t last
Last article number in run.
Definition: lib.h:78
anum_t first
First article number in run.
Definition: lib.h:77
NNTP-specific Account data -.
Definition: adata.h:36
time_t newgroups_time
Definition: adata.h:56
bool newsrc_modified
Definition: adata.h:49
struct HashTable * groups_hash
Hash Table: "newsgroup" -> NntpMboxData.
Definition: adata.h:61
off_t size
Definition: adata.h:54
struct Connection * conn
Connection to NNTP Server.
Definition: adata.h:62
char * authenticators
Definition: adata.h:52
unsigned int groups_num
Definition: adata.h:58
time_t mtime
Definition: adata.h:55
unsigned int groups_max
Definition: adata.h:59
FILE * fp_newsrc
Definition: adata.h:50
void ** groups_list
Definition: adata.h:60
bool cacheable
Definition: adata.h:48
char * newsrc_file
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
Definition: mdata.h:40
anum_t last_message
Definition: mdata.h:38
struct BodyCache * bcache
Definition: mdata.h:50
char * group
Name of newsgroup.
Definition: mdata.h:35
struct NntpAccountData * adata
Definition: mdata.h:48
char * desc
Description of newsgroup.
Definition: mdata.h:36
struct NewsrcEntry * newsrc_ent
Definition: mdata.h:47
anum_t unread
Definition: mdata.h:41
anum_t last_loaded
Definition: mdata.h:39
unsigned int newsrc_len
Definition: mdata.h:46
anum_t first_message
Definition: mdata.h:37
bool subscribed
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:297
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
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
Definition: url.h:50