NeoMutt  2023-11-03-107-g582dc1
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
newsrc.c
Go to the documentation of this file.
1
31#include "config.h"
32#include <dirent.h>
33#include <errno.h>
34#include <inttypes.h>
35#include <limits.h>
36#include <stdbool.h>
37#include <stdio.h>
38#include <string.h>
39#include <sys/stat.h>
40#include <unistd.h>
41#include "private.h"
42#include "mutt/lib.h"
43#include "config/lib.h"
44#include "email/lib.h"
45#include "core/lib.h"
46#include "conn/lib.h"
47#include "mutt.h"
48#include "lib.h"
49#include "bcache/lib.h"
50#include "adata.h"
51#include "edata.h"
52#include "format_flags.h"
53#include "mdata.h"
54#include "mutt_account.h"
55#include "mutt_logging.h"
56#include "mutt_socket.h"
57#include "muttlib.h"
58#include "protos.h"
59#ifdef USE_HCACHE
60#include "hcache/lib.h"
61#endif
62
63struct BodyCache;
64
72static struct NntpMboxData *mdata_find(struct NntpAccountData *adata, const char *group)
73{
75 if (mdata)
76 return mdata;
77
78 size_t len = strlen(group) + 1;
79 /* create NntpMboxData structure and add it to hash */
80 mdata = mutt_mem_calloc(1, sizeof(struct NntpMboxData) + len);
81 mdata->group = (char *) mdata + sizeof(struct NntpMboxData);
82 mutt_str_copy(mdata->group, group, len);
83 mdata->adata = adata;
84 mdata->deleted = true;
86
87 /* add NntpMboxData to list */
89 {
90 adata->groups_max *= 2;
92 }
94
95 return mdata;
96}
97
103{
104 for (int i = 0; i < NNTP_ACACHE_LEN; i++)
105 {
106 if (mdata->acache[i].path)
107 {
108 unlink(mdata->acache[i].path);
109 FREE(&mdata->acache[i].path);
110 }
111 }
112}
113
119{
120 if (!adata->fp_newsrc)
121 return;
122
123 mutt_debug(LL_DEBUG1, "Unlocking %s\n", adata->newsrc_file);
126}
127
133{
134 mdata->unread = 0;
135 if ((mdata->last_message == 0) ||
136 (mdata->first_message > mdata->last_message) || !mdata->newsrc_ent)
137 {
138 return;
139 }
140
141 mdata->unread = mdata->last_message - mdata->first_message + 1;
142 for (unsigned int i = 0; i < mdata->newsrc_len; i++)
143 {
144 anum_t first = mdata->newsrc_ent[i].first;
145 if (first < mdata->first_message)
146 first = mdata->first_message;
147 anum_t last = mdata->newsrc_ent[i].last;
148 if (last > mdata->last_message)
149 last = mdata->last_message;
150 if (first <= last)
151 mdata->unread -= last - first + 1;
152 }
153}
154
163{
164 if (!adata)
165 return -1;
166
167 char *line = NULL;
168 struct stat st = { 0 };
169
170 if (adata->fp_newsrc)
171 {
172 /* if we already have a handle, close it and reopen */
174 }
175 else
176 {
177 /* if file doesn't exist, create it */
178 adata->fp_newsrc = mutt_file_fopen(adata->newsrc_file, "a");
180 }
181
182 /* open .newsrc */
183 adata->fp_newsrc = mutt_file_fopen(adata->newsrc_file, "r");
184 if (!adata->fp_newsrc)
185 {
186 mutt_perror("%s", adata->newsrc_file);
187 return -1;
188 }
189
190 /* lock it */
191 mutt_debug(LL_DEBUG1, "Locking %s\n", adata->newsrc_file);
192 if (mutt_file_lock(fileno(adata->fp_newsrc), false, true))
193 {
195 return -1;
196 }
197
198 if (stat(adata->newsrc_file, &st) != 0)
199 {
200 mutt_perror("%s", adata->newsrc_file);
201 nntp_newsrc_close(adata);
202 return -1;
203 }
204
205 if ((adata->size == st.st_size) && (adata->mtime == st.st_mtime))
206 return 0;
207
208 adata->size = st.st_size;
209 adata->mtime = st.st_mtime;
210 adata->newsrc_modified = true;
211 mutt_debug(LL_DEBUG1, "Parsing %s\n", adata->newsrc_file);
212
213 /* .newsrc has been externally modified or hasn't been loaded yet */
214 for (unsigned int i = 0; i < adata->groups_num; i++)
215 {
216 struct NntpMboxData *mdata = adata->groups_list[i];
217 if (!mdata)
218 continue;
219
220 mdata->subscribed = false;
221 mdata->newsrc_len = 0;
222 FREE(&mdata->newsrc_ent);
223 }
224
225 line = mutt_mem_malloc(st.st_size + 1);
226 while (st.st_size && fgets(line, st.st_size + 1, adata->fp_newsrc))
227 {
228 char *b = NULL, *h = NULL;
229 unsigned int j = 1;
230 bool subs = false;
231
232 /* find end of newsgroup name */
233 char *p = strpbrk(line, ":!");
234 if (!p)
235 continue;
236
237 /* ":" - subscribed, "!" - unsubscribed */
238 if (*p == ':')
239 subs = true;
240 *p++ = '\0';
241
242 /* get newsgroup data */
243 struct NntpMboxData *mdata = mdata_find(adata, line);
244 FREE(&mdata->newsrc_ent);
245
246 /* count number of entries */
247 b = p;
248 while (*b)
249 if (*b++ == ',')
250 j++;
251 mdata->newsrc_ent = mutt_mem_calloc(j, sizeof(struct NewsrcEntry));
252 mdata->subscribed = subs;
253
254 /* parse entries */
255 j = 0;
256 while (p)
257 {
258 b = p;
259
260 /* find end of entry */
261 p = strchr(p, ',');
262 if (p)
263 *p++ = '\0';
264
265 /* first-last or single number */
266 h = strchr(b, '-');
267 if (h)
268 *h++ = '\0';
269 else
270 h = b;
271
272 if ((sscanf(b, ANUM, &mdata->newsrc_ent[j].first) == 1) &&
273 (sscanf(h, ANUM, &mdata->newsrc_ent[j].last) == 1))
274 {
275 j++;
276 }
277 }
278 if (j == 0)
279 {
280 mdata->newsrc_ent[j].first = 1;
281 mdata->newsrc_ent[j].last = 0;
282 j++;
283 }
284 if (mdata->last_message == 0)
285 mdata->last_message = mdata->newsrc_ent[j - 1].last;
286 mdata->newsrc_len = j;
287 mutt_mem_realloc(&mdata->newsrc_ent, j * sizeof(struct NewsrcEntry));
289 mutt_debug(LL_DEBUG2, "%s\n", mdata->group);
290 }
291 FREE(&line);
292 return 1;
293}
294
300{
301 if (!m)
302 return;
303
304 struct NntpMboxData *mdata = m->mdata;
305 anum_t last = 0, first = 1;
306 bool series;
307 unsigned int entries;
308
309 const enum SortType c_sort = cs_subset_sort(NeoMutt->sub, "sort");
310 if (c_sort != SORT_ORDER)
311 {
314 }
315
316 entries = mdata->newsrc_len;
317 if (!entries)
318 {
319 entries = 5;
320 mdata->newsrc_ent = mutt_mem_calloc(entries, sizeof(struct NewsrcEntry));
321 }
322
323 /* Set up to fake initial sequence from 1 to the article before the
324 * first article in our list */
325 mdata->newsrc_len = 0;
326 series = true;
327 for (int i = 0; i < m->msg_count; i++)
328 {
329 struct Email *e = m->emails[i];
330 if (!e)
331 break;
332
333 /* search for first unread */
334 if (series)
335 {
336 /* We don't actually check sequential order, since we mark
337 * "missing" entries as read/deleted */
338 last = nntp_edata_get(e)->article_num;
339 if ((last >= mdata->first_message) && !e->deleted && !e->read)
340 {
341 if (mdata->newsrc_len >= entries)
342 {
343 entries *= 2;
344 mutt_mem_realloc(&mdata->newsrc_ent, entries * sizeof(struct NewsrcEntry));
345 }
346 mdata->newsrc_ent[mdata->newsrc_len].first = first;
347 mdata->newsrc_ent[mdata->newsrc_len].last = last - 1;
348 mdata->newsrc_len++;
349 series = false;
350 }
351 }
352 else
353 {
354 /* search for first read */
355 if (e->deleted || e->read)
356 {
357 first = last + 1;
358 series = true;
359 }
360 last = nntp_edata_get(e)->article_num;
361 }
362 }
363
364 if (series && (first <= mdata->last_loaded))
365 {
366 if (mdata->newsrc_len >= entries)
367 {
368 entries++;
369 mutt_mem_realloc(&mdata->newsrc_ent, entries * sizeof(struct NewsrcEntry));
370 }
371 mdata->newsrc_ent[mdata->newsrc_len].first = first;
372 mdata->newsrc_ent[mdata->newsrc_len].last = mdata->last_loaded;
373 mdata->newsrc_len++;
374 }
375 mutt_mem_realloc(&mdata->newsrc_ent, mdata->newsrc_len * sizeof(struct NewsrcEntry));
376
377 if (c_sort != SORT_ORDER)
378 {
379 cs_subset_str_native_set(NeoMutt->sub, "sort", c_sort, NULL);
381 }
382}
383
391static int update_file(char *filename, char *buf)
392{
393 FILE *fp = NULL;
394 char tmpfile[PATH_MAX] = { 0 };
395 int rc = -1;
396
397 while (true)
398 {
399 snprintf(tmpfile, sizeof(tmpfile), "%s.tmp", filename);
400 fp = mutt_file_fopen(tmpfile, "w");
401 if (!fp)
402 {
403 mutt_perror("%s", tmpfile);
404 *tmpfile = '\0';
405 break;
406 }
407 if (fputs(buf, fp) == EOF)
408 {
409 mutt_perror("%s", tmpfile);
410 break;
411 }
412 if (mutt_file_fclose(&fp) == EOF)
413 {
414 mutt_perror("%s", tmpfile);
415 fp = NULL;
416 break;
417 }
418 fp = NULL;
419 if (rename(tmpfile, filename) < 0)
420 {
421 mutt_perror("%s", filename);
422 break;
423 }
424 *tmpfile = '\0';
425 rc = 0;
426 break;
427 }
428 mutt_file_fclose(&fp);
429
430 if (*tmpfile)
431 unlink(tmpfile);
432 return rc;
433}
434
442{
443 if (!adata)
444 return -1;
445
446 int rc = -1;
447
448 size_t buflen = 10240;
449 char *buf = mutt_mem_calloc(1, buflen);
450 size_t off = 0;
451
452 /* we will generate full newsrc here */
453 for (unsigned int i = 0; i < adata->groups_num; i++)
454 {
455 struct NntpMboxData *mdata = adata->groups_list[i];
456
457 if (!mdata || !mdata->newsrc_ent)
458 continue;
459
460 /* write newsgroup name */
461 if ((off + strlen(mdata->group) + 3) > buflen)
462 {
463 buflen *= 2;
464 mutt_mem_realloc(&buf, buflen);
465 }
466 snprintf(buf + off, buflen - off, "%s%c ", mdata->group, mdata->subscribed ? ':' : '!');
467 off += strlen(buf + off);
468
469 /* write entries */
470 for (unsigned int j = 0; j < mdata->newsrc_len; j++)
471 {
472 if ((off + 1024) > buflen)
473 {
474 buflen *= 2;
475 mutt_mem_realloc(&buf, buflen);
476 }
477 if (j)
478 buf[off++] = ',';
479 if (mdata->newsrc_ent[j].first == mdata->newsrc_ent[j].last)
480 {
481 snprintf(buf + off, buflen - off, ANUM, mdata->newsrc_ent[j].first);
482 }
483 else if (mdata->newsrc_ent[j].first < mdata->newsrc_ent[j].last)
484 {
485 snprintf(buf + off, buflen - off, ANUM "-" ANUM,
486 mdata->newsrc_ent[j].first, mdata->newsrc_ent[j].last);
487 }
488 off += strlen(buf + off);
489 }
490 buf[off++] = '\n';
491 }
492 buf[off] = '\0';
493
494 /* newrc being fully rewritten */
495 mutt_debug(LL_DEBUG1, "Updating %s\n", adata->newsrc_file);
496 if (adata->newsrc_file && (update_file(adata->newsrc_file, buf) == 0))
497 {
498 struct stat st = { 0 };
499
500 rc = stat(adata->newsrc_file, &st);
501 if (rc == 0)
502 {
503 adata->size = st.st_size;
504 adata->mtime = st.st_mtime;
505 }
506 else
507 {
508 mutt_perror("%s", adata->newsrc_file);
509 }
510 }
511 FREE(&buf);
512 return rc;
513}
514
522static void cache_expand(char *dst, size_t dstlen, struct ConnAccount *cac, const char *src)
523{
524 char *c = NULL;
525 char file[PATH_MAX] = { 0 };
526
527 /* server subdirectory */
528 if (cac)
529 {
530 struct Url url = { 0 };
531
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 else
538 {
539 mutt_str_copy(file, src ? src : "", sizeof(file));
540 }
541
542 const char *const c_news_cache_dir = cs_subset_path(NeoMutt->sub, "news_cache_dir");
543 snprintf(dst, dstlen, "%s/%s", c_news_cache_dir, file);
544
545 /* remove trailing slash */
546 c = dst + strlen(dst) - 1;
547 if (*c == '/')
548 *c = '\0';
549
550 struct Buffer *tmp = buf_pool_get();
551 buf_addstr(tmp, dst);
552 buf_expand_path(tmp);
553 mutt_encode_path(tmp, dst);
554 mutt_str_copy(dst, buf_string(tmp), dstlen);
555 buf_pool_release(&tmp);
556}
557
564void nntp_expand_path(char *buf, size_t buflen, struct ConnAccount *cac)
565{
566 struct Url url = { 0 };
567
568 mutt_account_tourl(cac, &url);
569 url.path = mutt_str_dup(buf);
570 url_tostring(&url, buf, buflen, U_NO_FLAGS);
571 FREE(&url.path);
572}
573
580int nntp_add_group(char *line, void *data)
581{
582 struct NntpAccountData *adata = data;
583 struct NntpMboxData *mdata = NULL;
584 char group[1024] = { 0 };
585 char desc[8192] = { 0 };
586 char mod;
587 anum_t first, last;
588
589 if (!adata || !line)
590 return 0;
591
592 /* These sscanf limits must match the sizes of the group and desc arrays */
593 if (sscanf(line, "%1023s " ANUM " " ANUM " %c %8191[^\n]", group, &last,
594 &first, &mod, desc) < 4)
595 {
596 mutt_debug(LL_DEBUG2, "Can't parse server line: %s\n", line);
597 return 0;
598 }
599
601 mdata->deleted = false;
602 mdata->first_message = first;
603 mdata->last_message = last;
604 mdata->allowed = (mod == 'y') || (mod == 'm');
605 mutt_str_replace(&mdata->desc, desc);
606 if (mdata->newsrc_ent || (mdata->last_cached != 0))
608 else if (mdata->last_message && (mdata->first_message <= mdata->last_message))
609 mdata->unread = mdata->last_message - mdata->first_message + 1;
610 else
611 mdata->unread = 0;
612 return 0;
613}
614
622{
623 char buf[8192] = { 0 };
624 char file[4096] = { 0 };
625 time_t t = 0;
626
627 cache_expand(file, sizeof(file), &adata->conn->account, ".active");
628 mutt_debug(LL_DEBUG1, "Parsing %s\n", file);
629 FILE *fp = mutt_file_fopen(file, "r");
630 if (!fp)
631 return -1;
632
633 if (!fgets(buf, sizeof(buf), fp) || (sscanf(buf, "%jd%4095s", &t, file) != 1) || (t == 0))
634 {
635 mutt_file_fclose(&fp);
636 return -1;
637 }
639
640 mutt_message(_("Loading list of groups from cache..."));
641 while (fgets(buf, sizeof(buf), fp))
642 nntp_add_group(buf, adata);
643 nntp_add_group(NULL, NULL);
644 mutt_file_fclose(&fp);
646 return 0;
647}
648
656{
657 if (!adata->cacheable)
658 return 0;
659
660 size_t buflen = 10240;
661 char *buf = mutt_mem_calloc(1, buflen);
662 snprintf(buf, buflen, "%lu\n", (unsigned long) adata->newgroups_time);
663 size_t off = strlen(buf);
664
665 for (unsigned int i = 0; i < adata->groups_num; i++)
666 {
667 struct NntpMboxData *mdata = adata->groups_list[i];
668
669 if (!mdata || mdata->deleted)
670 continue;
671
672 if ((off + strlen(mdata->group) + (mdata->desc ? strlen(mdata->desc) : 0) + 50) > buflen)
673 {
674 buflen *= 2;
675 mutt_mem_realloc(&buf, buflen);
676 }
677 snprintf(buf + off, buflen - off, "%s " ANUM " " ANUM " %c%s%s\n", mdata->group,
678 mdata->last_message, mdata->first_message, mdata->allowed ? 'y' : 'n',
679 mdata->desc ? " " : "", mdata->desc ? mdata->desc : "");
680 off += strlen(buf + off);
681 }
682
683 char file[PATH_MAX] = { 0 };
684 cache_expand(file, sizeof(file), &adata->conn->account, ".active");
685 mutt_debug(LL_DEBUG1, "Updating %s\n", file);
686 int rc = update_file(file, buf);
687 FREE(&buf);
688 return rc;
689}
690
691#ifdef USE_HCACHE
695static void nntp_hcache_namer(const char *path, struct Buffer *dest)
696{
697 buf_printf(dest, "%s.hcache", path);
698
699 /* Strip out any directories in the path */
700 char *first = strchr(buf_string(dest), '/');
701 char *last = strrchr(buf_string(dest), '/');
702 if (first && last && (last > first))
703 {
704 memmove(first, last, strlen(last) + 1);
705 }
706}
707
715{
716 struct Url url = { 0 };
717 char file[PATH_MAX] = { 0 };
718
719 const bool c_save_unsubscribed = cs_subset_bool(NeoMutt->sub, "save_unsubscribed");
720 if (!mdata->adata || !mdata->adata->cacheable || !mdata->adata->conn || !mdata->group ||
721 !(mdata->newsrc_ent || mdata->subscribed || c_save_unsubscribed))
722 {
723 return NULL;
724 }
725
726 mutt_account_tourl(&mdata->adata->conn->account, &url);
727 url.path = mdata->group;
728 url_tostring(&url, file, sizeof(file), U_PATH);
729 const char *const c_news_cache_dir = cs_subset_path(NeoMutt->sub, "news_cache_dir");
730 return hcache_open(c_news_cache_dir, file, nntp_hcache_namer);
731}
732
738void nntp_hcache_update(struct NntpMboxData *mdata, struct HeaderCache *hc)
739{
740 if (!hc)
741 return;
742
743 char buf[32] = { 0 };
744 bool old = false;
745 anum_t first = 0, last = 0;
746
747 /* fetch previous values of first and last */
748 char *hdata = hcache_fetch_raw_str(hc, "index", 5);
749 if (hdata)
750 {
751 mutt_debug(LL_DEBUG2, "hcache_fetch_email index: %s\n", hdata);
752 if (sscanf(hdata, ANUM " " ANUM, &first, &last) == 2)
753 {
754 old = true;
755 mdata->last_cached = last;
756
757 /* clean removed headers from cache */
758 for (anum_t current = first; current <= last; current++)
759 {
760 if ((current >= mdata->first_message) && (current <= mdata->last_message))
761 continue;
762
763 snprintf(buf, sizeof(buf), ANUM, current);
764 mutt_debug(LL_DEBUG2, "hcache_delete_email %s\n", buf);
765 hcache_delete_email(hc, buf, strlen(buf));
766 }
767 }
768 FREE(&hdata);
769 }
770
771 /* store current values of first and last */
772 if (!old || (mdata->first_message != first) || (mdata->last_message != last))
773 {
774 snprintf(buf, sizeof(buf), ANUM " " ANUM, mdata->first_message, mdata->last_message);
775 mutt_debug(LL_DEBUG2, "hcache_store_email index: %s\n", buf);
776 hcache_store_raw(hc, "index", 5, buf, strlen(buf) + 1);
777 }
778}
779#endif
780
785static int nntp_bcache_delete(const char *id, struct BodyCache *bcache, void *data)
786{
787 struct NntpMboxData *mdata = data;
788 anum_t anum;
789 char c;
790
791 if (!mdata || (sscanf(id, ANUM "%c", &anum, &c) != 1) ||
792 (anum < mdata->first_message) || (anum > mdata->last_message))
793 {
794 if (mdata)
795 mutt_debug(LL_DEBUG2, "mutt_bcache_del %s\n", id);
797 }
798 return 0;
799}
800
806{
808}
809
815{
816 if (!mdata || !mdata->adata || !mdata->adata->cacheable)
817 return;
818
819#ifdef USE_HCACHE
820 struct Buffer file = buf_make(PATH_MAX);
821 nntp_hcache_namer(mdata->group, &file);
822 cache_expand(file.data, file.dsize, &mdata->adata->conn->account, buf_string(&file));
823 unlink(buf_string(&file));
824 mdata->last_cached = 0;
825 mutt_debug(LL_DEBUG2, "%s\n", buf_string(&file));
826 buf_dealloc(&file);
827#endif
828
829 if (!mdata->bcache)
830 {
831 mdata->bcache = mutt_bcache_open(&mdata->adata->conn->account, mdata->group);
832 }
833 if (mdata->bcache)
834 {
835 mutt_debug(LL_DEBUG2, "%s/*\n", mdata->group);
837 mutt_bcache_close(&mdata->bcache);
838 }
839}
840
848{
849 char file[PATH_MAX] = { 0 };
850 char *fp = NULL;
851 struct dirent *de = NULL;
852 DIR *dir = NULL;
853
854 if (!adata || !adata->cacheable)
855 return;
856
857 cache_expand(file, sizeof(file), &adata->conn->account, NULL);
859 if (dir)
860 {
861 mutt_strn_cat(file, sizeof(file), "/", 1);
862 fp = file + strlen(file);
863 while ((de = readdir(dir)))
864 {
865 char *group = de->d_name;
866 struct stat st = { 0 };
867 struct NntpMboxData *mdata = NULL;
868 struct NntpMboxData tmp_mdata = { 0 };
869
870 if (mutt_str_equal(group, ".") || mutt_str_equal(group, ".."))
871 continue;
872 *fp = '\0';
873 mutt_strn_cat(file, sizeof(file), group, strlen(group));
874 if (stat(file, &st) != 0)
875 continue;
876
877#ifdef USE_HCACHE
878 if (S_ISREG(st.st_mode))
879 {
880 char *ext = group + strlen(group) - 7;
881 if ((strlen(group) < 8) || !mutt_str_equal(ext, ".hcache"))
882 continue;
883 *ext = '\0';
884 }
885 else
886#endif
887 if (!S_ISDIR(st.st_mode))
888 continue;
889
890 const bool c_save_unsubscribed = cs_subset_bool(NeoMutt->sub, "save_unsubscribed");
892 if (!mdata)
893 {
894 mdata = &tmp_mdata;
895 mdata->adata = adata;
896 mdata->group = group;
897 mdata->bcache = NULL;
898 }
899 else if (mdata->newsrc_ent || mdata->subscribed || c_save_unsubscribed)
900 {
901 continue;
902 }
903
905 if (S_ISDIR(st.st_mode))
906 {
907 rmdir(file);
908 mutt_debug(LL_DEBUG2, "%s\n", file);
909 }
910 }
911 closedir(dir);
912 }
913}
914
927const char *nntp_format_str(char *buf, size_t buflen, size_t col, int cols, char op,
928 const char *src, const char *prec, const char *if_str,
929 const char *else_str, intptr_t data, MuttFormatFlags flags)
930{
931 struct NntpAccountData *adata = (struct NntpAccountData *) data;
932 struct ConnAccount *cac = &adata->conn->account;
933 char fn[128], fmt[128];
934
935 switch (op)
936 {
937 case 'a':
938 {
939 struct Url url = { 0 };
940 mutt_account_tourl(cac, &url);
941 url_tostring(&url, fn, sizeof(fn), U_PATH);
942 char *p = strchr(fn, '/');
943 if (p)
944 *p = '\0';
945 snprintf(fmt, sizeof(fmt), "%%%ss", prec);
946 snprintf(buf, buflen, fmt, fn);
947 break;
948 }
949 case 'p':
950 snprintf(fmt, sizeof(fmt), "%%%su", prec);
951 snprintf(buf, buflen, fmt, cac->port);
952 break;
953 case 'P':
954 *buf = '\0';
955 if (cac->flags & MUTT_ACCT_PORT)
956 {
957 snprintf(fmt, sizeof(fmt), "%%%su", prec);
958 snprintf(buf, buflen, fmt, cac->port);
959 }
960 break;
961 case 's':
962 mutt_str_copy(fn, cac->host, sizeof(fn));
963 mutt_str_lower(fn);
964 snprintf(fmt, sizeof(fmt), "%%%ss", prec);
965 snprintf(buf, buflen, fmt, fn);
966 break;
967 case 'S':
968 {
969 struct Url url = { 0 };
970 mutt_account_tourl(cac, &url);
971 url_tostring(&url, fn, sizeof(fn), U_PATH);
972 char *p = strchr(fn, ':');
973 if (p)
974 *p = '\0';
975 snprintf(fmt, sizeof(fmt), "%%%ss", prec);
976 snprintf(buf, buflen, fmt, fn);
977 break;
978 }
979 case 'u':
980 snprintf(fmt, sizeof(fmt), "%%%ss", prec);
981 snprintf(buf, buflen, fmt, cac->user);
982 break;
983 }
984 return src;
985}
986
990static const char *nntp_get_field(enum ConnAccountField field, void *gf_data)
991{
992 switch (field)
993 {
994 case MUTT_CA_LOGIN:
995 case MUTT_CA_USER:
996 return cs_subset_string(NeoMutt->sub, "nntp_user");
997 case MUTT_CA_PASS:
998 return cs_subset_string(NeoMutt->sub, "nntp_pass");
1000 case MUTT_CA_HOST:
1001 default:
1002 return NULL;
1003 }
1004}
1005
1021struct NntpAccountData *nntp_select_server(struct Mailbox *m, const char *server, bool leave_lock)
1022{
1023 char file[PATH_MAX] = { 0 };
1024 int rc;
1025 struct ConnAccount cac = { { 0 } };
1026 struct NntpAccountData *adata = NULL;
1027 struct Connection *conn = NULL;
1028
1029 if (!server || (*server == '\0'))
1030 {
1031 mutt_error(_("No news server defined"));
1032 return NULL;
1033 }
1034
1035 /* create account from news server url */
1036 cac.flags = 0;
1037 cac.port = NNTP_PORT;
1039 cac.service = "nntp";
1041
1042 snprintf(file, sizeof(file), "%s%s", strstr(server, "://") ? "" : "news://", server);
1043 struct Url *url = url_parse(file);
1044 if (!url || (url->path && *url->path) ||
1045 !((url->scheme == U_NNTP) || (url->scheme == U_NNTPS)) || !url->host ||
1046 (mutt_account_fromurl(&cac, url) < 0))
1047 {
1048 url_free(&url);
1049 mutt_error(_("%s is an invalid news server specification"), server);
1050 return NULL;
1051 }
1052 if (url->scheme == U_NNTPS)
1053 {
1054 cac.flags |= MUTT_ACCT_SSL;
1055 cac.port = NNTP_SSL_PORT;
1056 }
1057 url_free(&url);
1058
1059 // If nntp_user and nntp_pass are specified in the config, use them to find the connection
1060 const char *user = NULL;
1061 const char *pass = NULL;
1062 if ((user = cac.get_field(MUTT_CA_USER, NULL)))
1063 {
1064 mutt_str_copy(cac.user, user, sizeof(cac.user));
1065 cac.flags |= MUTT_ACCT_USER;
1066 }
1067 if ((pass = cac.get_field(MUTT_CA_PASS, NULL)))
1068 {
1069 mutt_str_copy(cac.pass, pass, sizeof(cac.pass));
1070 cac.flags |= MUTT_ACCT_PASS;
1071 }
1072
1073 /* find connection by account */
1074 conn = mutt_conn_find(&cac);
1075 if (!conn)
1076 return NULL;
1077 if (!(conn->account.flags & MUTT_ACCT_USER) && cac.flags & MUTT_ACCT_USER)
1078 {
1079 conn->account.flags |= MUTT_ACCT_USER;
1080 conn->account.user[0] = '\0';
1081 }
1082
1083 /* new news server */
1084 adata = nntp_adata_new(conn);
1085
1086 rc = nntp_open_connection(adata);
1087
1088 /* try to create cache directory and enable caching */
1089 adata->cacheable = false;
1090 const char *const c_news_cache_dir = cs_subset_path(NeoMutt->sub, "news_cache_dir");
1091 if ((rc >= 0) && c_news_cache_dir)
1092 {
1093 cache_expand(file, sizeof(file), &conn->account, NULL);
1094 if (mutt_file_mkdir(file, S_IRWXU) < 0)
1095 {
1096 mutt_error(_("Can't create %s: %s"), file, strerror(errno));
1097 }
1098 adata->cacheable = true;
1099 }
1100
1101 /* load .newsrc */
1102 if (rc >= 0)
1103 {
1104 const char *const c_newsrc = cs_subset_path(NeoMutt->sub, "newsrc");
1105 mutt_expando_format(file, sizeof(file), 0, sizeof(file), NONULL(c_newsrc),
1106 nntp_format_str, (intptr_t) adata, MUTT_FORMAT_NO_FLAGS);
1107 mutt_expand_path(file, sizeof(file));
1108 adata->newsrc_file = mutt_str_dup(file);
1109 rc = nntp_newsrc_parse(adata);
1110 }
1111 if (rc >= 0)
1112 {
1113 /* try to load list of newsgroups from cache */
1114 if (adata->cacheable && (active_get_cache(adata) == 0))
1115 {
1116 rc = nntp_check_new_groups(m, adata);
1117 }
1118 else
1119 {
1120 /* load list of newsgroups from server */
1121 rc = nntp_active_fetch(adata, false);
1122 }
1123 }
1124
1125 if (rc >= 0)
1126 nntp_clear_cache(adata);
1127
1128#ifdef USE_HCACHE
1129 /* check cache files */
1130 if ((rc >= 0) && adata->cacheable)
1131 {
1132 struct dirent *de = NULL;
1133 DIR *dir = mutt_file_opendir(file, MUTT_OPENDIR_NONE);
1134
1135 if (dir)
1136 {
1137 while ((de = readdir(dir)))
1138 {
1139 struct HeaderCache *hc = NULL;
1140 char *group = de->d_name;
1141
1142 char *p = group + strlen(group) - 7;
1143 if ((strlen(group) < 8) || !mutt_str_equal(p, ".hcache"))
1144 continue;
1145 *p = '\0';
1147 if (!mdata)
1148 continue;
1149
1150 hc = nntp_hcache_open(mdata);
1151 if (!hc)
1152 continue;
1153
1154 /* fetch previous values of first and last */
1155 char *hdata = hcache_fetch_raw_str(hc, "index", 5);
1156 if (hdata)
1157 {
1158 anum_t first, last;
1159
1160 if (sscanf(hdata, ANUM " " ANUM, &first, &last) == 2)
1161 {
1162 if (mdata->deleted)
1163 {
1164 mdata->first_message = first;
1165 mdata->last_message = last;
1166 }
1167 if ((last >= mdata->first_message) && (last <= mdata->last_message))
1168 {
1169 mdata->last_cached = last;
1170 mutt_debug(LL_DEBUG2, "%s last_cached=" ANUM "\n", mdata->group, last);
1171 }
1172 }
1173 FREE(&hdata);
1174 }
1175 hcache_close(&hc);
1176 }
1177 closedir(dir);
1178 }
1179 }
1180#endif
1181
1182 if ((rc < 0) || !leave_lock)
1184
1185 if (rc < 0)
1186 {
1191 FREE(&adata);
1192 mutt_socket_close(conn);
1193 FREE(&conn);
1194 return NULL;
1195 }
1196
1197 return adata;
1198}
1199
1212void nntp_article_status(struct Mailbox *m, struct Email *e, char *group, anum_t anum)
1213{
1214 struct NntpMboxData *mdata = m->mdata;
1215
1216 if (group)
1217 mdata = mutt_hash_find(mdata->adata->groups_hash, group);
1218
1219 if (!mdata)
1220 return;
1221
1222 for (unsigned int i = 0; i < mdata->newsrc_len; i++)
1223 {
1224 if ((anum >= mdata->newsrc_ent[i].first) && (anum <= mdata->newsrc_ent[i].last))
1225 {
1226 /* can't use mutt_set_flag() because mview_update() didn't get called yet */
1227 e->read = true;
1228 return;
1229 }
1230 }
1231
1232 /* article was not cached yet, it's new */
1233 if (anum > mdata->last_cached)
1234 return;
1235
1236 /* article isn't read but cached, it's old */
1237 const bool c_mark_old = cs_subset_bool(NeoMutt->sub, "mark_old");
1238 if (c_mark_old)
1239 e->old = true;
1240}
1241
1250{
1251 if (!adata || !adata->groups_hash || !group || (*group == '\0'))
1252 return NULL;
1253
1255 mdata->subscribed = true;
1256 if (!mdata->newsrc_ent)
1257 {
1258 mdata->newsrc_ent = mutt_mem_calloc(1, sizeof(struct NewsrcEntry));
1259 mdata->newsrc_len = 1;
1260 mdata->newsrc_ent[0].first = 1;
1261 mdata->newsrc_ent[0].last = 0;
1262 }
1263 return mdata;
1264}
1265
1274{
1275 if (!adata || !adata->groups_hash || !group || (*group == '\0'))
1276 return NULL;
1277
1279 if (!mdata)
1280 return NULL;
1281
1282 mdata->subscribed = false;
1283 const bool c_save_unsubscribed = cs_subset_bool(NeoMutt->sub, "save_unsubscribed");
1284 if (!c_save_unsubscribed)
1285 {
1286 mdata->newsrc_len = 0;
1287 FREE(&mdata->newsrc_ent);
1288 }
1289 return mdata;
1290}
1291
1301 struct NntpAccountData *adata, char *group)
1302{
1303 if (!adata || !adata->groups_hash || !group || (*group == '\0'))
1304 return NULL;
1305
1307 if (!mdata)
1308 return NULL;
1309
1310 if (mdata->newsrc_ent)
1311 {
1312 mutt_mem_realloc(&mdata->newsrc_ent, sizeof(struct NewsrcEntry));
1313 mdata->newsrc_len = 1;
1314 mdata->newsrc_ent[0].first = 1;
1315 mdata->newsrc_ent[0].last = mdata->last_message;
1316 }
1317 mdata->unread = 0;
1318 if (m && (m->mdata == mdata))
1319 {
1320 for (unsigned int i = 0; i < m->msg_count; i++)
1321 {
1322 struct Email *e = m->emails[i];
1323 if (!e)
1324 break;
1325 mutt_set_flag(m, e, MUTT_READ, true, true);
1326 }
1327 }
1328 return mdata;
1329}
1330
1340 struct NntpAccountData *adata, char *group)
1341{
1342 if (!adata || !adata->groups_hash || !group || (*group == '\0'))
1343 return NULL;
1344
1346 if (!mdata)
1347 return NULL;
1348
1349 if (mdata->newsrc_ent)
1350 {
1351 mutt_mem_realloc(&mdata->newsrc_ent, sizeof(struct NewsrcEntry));
1352 mdata->newsrc_len = 1;
1353 mdata->newsrc_ent[0].first = 1;
1354 mdata->newsrc_ent[0].last = mdata->first_message - 1;
1355 }
1356 if (m && (m->mdata == mdata))
1357 {
1358 mdata->unread = m->msg_count;
1359 for (unsigned int i = 0; i < m->msg_count; i++)
1360 {
1361 struct Email *e = m->emails[i];
1362 if (!e)
1363 break;
1364 mutt_set_flag(m, e, MUTT_READ, false, true);
1365 }
1366 }
1367 else
1368 {
1369 mdata->unread = mdata->last_message;
1370 if (mdata->newsrc_ent)
1371 mdata->unread -= mdata->newsrc_ent[0].last;
1372 }
1373 return mdata;
1374}
1375
1382void nntp_mailbox(struct Mailbox *m, char *buf, size_t buflen)
1383{
1384 if (!m)
1385 return;
1386
1387 for (unsigned int i = 0; i < CurrentNewsSrv->groups_num; i++)
1388 {
1390
1391 if (!mdata || !mdata->subscribed || !mdata->unread)
1392 continue;
1393
1394 if ((m->type == MUTT_NNTP) &&
1395 mutt_str_equal(mdata->group, ((struct NntpMboxData *) m->mdata)->group))
1396 {
1397 unsigned int unread = 0;
1398
1399 for (unsigned int j = 0; j < m->msg_count; j++)
1400 {
1401 struct Email *e = m->emails[j];
1402 if (!e)
1403 break;
1404 if (!e->read && !e->deleted)
1405 unread++;
1406 }
1407 if (unread == 0)
1408 continue;
1409 }
1410 mutt_str_copy(buf, mdata->group, buflen);
1411 break;
1412 }
1413}
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:173
void buf_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:389
struct Buffer buf_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:70
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:238
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:93
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
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
#define MUTT_ACCT_PORT
Port field has been set.
Definition: connaccount.h:43
Convenience wrapper for the core headers.
Structs that make up an email.
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:636
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:152
int mutt_file_mkdir(const char *path, mode_t mode)
Recursively create directories.
Definition: file.c:977
int mutt_file_lock(int fd, bool excl, bool timeout)
(Try to) Lock a file using fcntl()
Definition: file.c:1243
int mutt_file_unlock(int fd)
Unlock a file previously locked by mutt_file_lock()
Definition: file.c:1290
DIR * mutt_file_opendir(const char *path, enum MuttOpenDirMode mode)
Open a directory.
Definition: file.c:616
@ MUTT_OPENDIR_NONE
Plain opendir()
Definition: file.h:73
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:53
Flags to control mutt_expando_format()
#define MUTT_FORMAT_NO_FLAGS
No flags are set.
Definition: format_flags.h:30
uint8_t MuttFormatFlags
Flags for mutt_expando_format(), e.g. MUTT_FORMAT_FORCESUBJ.
Definition: format_flags.h:29
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:785
static const char * nntp_get_field(enum ConnAccountField field, void *gf_data)
Get connection login credentials - Implements ConnAccount::get_field() -.
Definition: newsrc.c:990
const char * nntp_format_str(char *buf, size_t buflen, size_t col, int cols, char op, const char *src, const char *prec, const char *if_str, const char *else_str, intptr_t data, MuttFormatFlags flags)
Expand the newsrc filename - Implements format_t -.
Definition: newsrc.c:927
void mutt_expando_format(char *buf, size_t buflen, size_t col, int cols, const char *src, format_t callback, intptr_t data, MuttFormatFlags flags)
Expand expandos (x) in a string -.
Definition: muttlib.c:739
static void nntp_hcache_namer(const char *path, struct Buffer *dest)
Compose hcache file names - Implements hcache_namer_t -.
Definition: newsrc.c:695
#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
Header cache multiplexor.
struct HeaderCache * hcache_open(const char *path, const char *folder, hcache_namer_t namer)
Multiplexor for StoreOps::open.
Definition: hcache.c:473
int hcache_delete_email(struct HeaderCache *hc, const char *key, size_t keylen)
Multiplexor for StoreOps::delete_record.
Definition: hcache.c:739
void hcache_close(struct HeaderCache **ptr)
Multiplexor for StoreOps::close.
Definition: hcache.c:542
char * hcache_fetch_raw_str(struct HeaderCache *hc, const char *key, size_t keylen)
Fetch a string from the cache.
Definition: hcache.c:652
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:724
@ LL_DEBUG2
Log at debug level 2.
Definition: logging2.h:44
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:43
void mailbox_changed(struct Mailbox *m, enum NotifyMailbox action)
Notify observers of a change to a Mailbox.
Definition: mailbox.c:226
@ NT_MAILBOX_RESORT
Email list needs resorting.
Definition: mailbox.h:177
@ MUTT_NNTP
'NNTP' (Usenet) Mailbox type
Definition: mailbox.h:49
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:251
char * mutt_str_lower(char *str)
Convert all characters in the string to lowercase.
Definition: string.c:385
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:763
char * mutt_strn_cat(char *d, size_t l, const char *s, size_t sl)
Concatenate two strings.
Definition: string.c:295
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:653
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:327
Many unsorted constants and some structs.
@ MUTT_READ
Messages that have been read.
Definition: mutt.h:72
#define PATH_MAX
Definition: mutt.h:41
int mutt_account_fromurl(struct ConnAccount *cac, const struct Url *url)
Fill ConnAccount with information from url.
Definition: mutt_account.c:43
void mutt_account_tourl(struct ConnAccount *cac, struct Url *url)
Fill URL with info from account.
Definition: mutt_account.c:79
ConnAccount object used by POP and IMAP.
@ MUTT_ACCT_TYPE_NNTP
Nntp (Usenet) Account.
Definition: mutt_account.h:40
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:73
NeoMutt Logging.
struct Connection * mutt_conn_find(const struct ConnAccount *cac)
Find a connection from a list.
Definition: mutt_socket.c:89
NeoMutt connections.
char * mutt_expand_path(char *buf, size_t buflen)
Create the canonical path.
Definition: muttlib.c:124
void buf_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:329
void mutt_encode_path(struct Buffer *buf, const char *src)
Convert a path to 'us-ascii'.
Definition: muttlib.c:1447
Some miscellaneous functions.
struct HeaderCache * nntp_hcache_open(struct NntpMboxData *mdata)
Open newsgroup hcache.
Definition: newsrc.c:714
void nntp_clear_cache(struct NntpAccountData *adata)
Clear the NNTP cache.
Definition: newsrc.c:847
void nntp_delete_group_cache(struct NntpMboxData *mdata)
Remove hcache and bcache of newsgroup.
Definition: newsrc.c:814
void nntp_newsrc_gen_entries(struct Mailbox *m)
Generate array of .newsrc entries.
Definition: newsrc.c:299
void nntp_hcache_update(struct NntpMboxData *mdata, struct HeaderCache *hc)
Remove stale cached headers.
Definition: newsrc.c:738
void nntp_article_status(struct Mailbox *m, struct Email *e, char *group, anum_t anum)
Get status of articles from .newsrc.
Definition: newsrc.c:1212
static int update_file(char *filename, char *buf)
Update file with new contents.
Definition: newsrc.c:391
int nntp_add_group(char *line, void *data)
Parse newsgroup.
Definition: newsrc.c:580
int nntp_newsrc_parse(struct NntpAccountData *adata)
Parse .newsrc file.
Definition: newsrc.c:162
void nntp_mailbox(struct Mailbox *m, char *buf, size_t buflen)
Get first newsgroup with new messages.
Definition: newsrc.c:1382
void nntp_newsrc_close(struct NntpAccountData *adata)
Unlock and close .newsrc file.
Definition: newsrc.c:118
struct NntpMboxData * mutt_newsgroup_catchup(struct Mailbox *m, struct NntpAccountData *adata, char *group)
Catchup newsgroup.
Definition: newsrc.c:1300
int nntp_newsrc_update(struct NntpAccountData *adata)
Update .newsrc file.
Definition: newsrc.c:441
struct NntpMboxData * mutt_newsgroup_subscribe(struct NntpAccountData *adata, char *group)
Subscribe newsgroup.
Definition: newsrc.c:1249
void nntp_expand_path(char *buf, size_t buflen, struct ConnAccount *cac)
Make fully qualified url from newsgroup name.
Definition: newsrc.c:564
static int active_get_cache(struct NntpAccountData *adata)
Load list of all newsgroups from cache.
Definition: newsrc.c:621
static void cache_expand(char *dst, size_t dstlen, struct ConnAccount *cac, const char *src)
Make fully qualified cache file name.
Definition: newsrc.c:522
static struct NntpMboxData * mdata_find(struct NntpAccountData *adata, const char *group)
Find NntpMboxData for given newsgroup or add it.
Definition: newsrc.c:72
struct NntpMboxData * mutt_newsgroup_uncatchup(struct Mailbox *m, struct NntpAccountData *adata, char *group)
Uncatchup newsgroup.
Definition: newsrc.c:1339
int nntp_active_save_cache(struct NntpAccountData *adata)
Save list of all newsgroups to cache.
Definition: newsrc.c:655
void nntp_bcache_update(struct NntpMboxData *mdata)
Remove stale cached messages.
Definition: newsrc.c:805
void nntp_group_unread_stat(struct NntpMboxData *mdata)
Count number of unread articles using .newsrc data.
Definition: newsrc.c:132
void nntp_acache_free(struct NntpMboxData *mdata)
Remove all temporarily cache files.
Definition: newsrc.c:102
struct NntpAccountData * nntp_select_server(struct Mailbox *m, const char *server, bool leave_lock)
Open a connection to an NNTP server.
Definition: newsrc.c:1021
struct NntpMboxData * mutt_newsgroup_unsubscribe(struct NntpAccountData *adata, char *group)
Unsubscribe newsgroup.
Definition: newsrc.c:1273
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:1989
#define NNTP_ACACHE_LEN
Definition: lib.h:83
struct NntpAccountData * CurrentNewsSrv
Current NNTP news server.
Definition: nntp.c:77
#define ANUM
Definition: lib.h:62
#define anum_t
Definition: lib.h:61
#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:2057
int nntp_open_connection(struct NntpAccountData *adata)
Connect to server, authenticate and get capabilities.
Definition: nntp.c:1734
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.
GUI display the mailboxes in a side panel.
int mutt_socket_close(struct Connection *conn)
Close a socket.
Definition: socket.c:101
SortType
Methods for sorting.
Definition: sort2.h:38
@ SORT_ORDER
Sort by the order the messages appear in the mailbox.
Definition: sort2.h:44
Key value store.
#define NONULL(x)
Definition: string2.h:37
void * adata
Private data (for Mailbox backends)
Definition: account.h:43
Local cache of email bodies.
Definition: bcache.c:51
String manipulation buffer.
Definition: buffer.h:34
size_t dsize
Length of data.
Definition: buffer.h:37
char * data
Pointer to data.
Definition: buffer.h:35
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
unsigned short port
Port to connect to.
Definition: connaccount.h:58
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:50
The envelope/body of an email.
Definition: email.h:37
bool read
Email is read.
Definition: email.h:48
bool old
Email is seen, but unread.
Definition: email.h:47
bool deleted
Email is deleted.
Definition: email.h:76
Header Cache.
Definition: lib.h:88
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:131
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:77
anum_t last
Last article number in run.
Definition: lib.h:79
anum_t first
First article number in run.
Definition: lib.h:78
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:304
struct Url * url_parse(const char *src)
Fill in Url.
Definition: url.c:238
int url_tostring(struct Url *url, char *dest, size_t len, uint8_t flags)
Output the URL string for a given Url object.
Definition: url.c:422
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition: url.c:123
#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