NeoMutt  2021-02-05-666-ge300cd
Teaching an old dog new tricks
DOXYGEN
newsrc.c
Go to the documentation of this file.
1 
31 #include "config.h"
32 #include <dirent.h>
33 #include <errno.h>
34 #include <limits.h>
35 #include <stdbool.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <sys/stat.h>
39 #include <time.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 
63 struct BodyCache;
64 
72 static struct NntpMboxData *mdata_find(struct NntpAccountData *adata, const char *group)
73 {
74  struct NntpMboxData *mdata = mutt_hash_find(adata->groups_hash, group);
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;
85  mutt_hash_insert(adata->groups_hash, mdata->group, mdata);
86 
87  /* add NntpMboxData to list */
88  if (adata->groups_num >= adata->groups_max)
89  {
90  adata->groups_max *= 2;
91  mutt_mem_realloc(&adata->groups_list, adata->groups_max * sizeof(mdata));
92  }
93  adata->groups_list[adata->groups_num++] = mdata;
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);
124  mutt_file_unlock(fileno(adata->fp_newsrc));
125  mutt_file_fclose(&adata->fp_newsrc);
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  char *line = NULL;
165  struct stat sb;
166 
167  if (adata->fp_newsrc)
168  {
169  /* if we already have a handle, close it and reopen */
170  mutt_file_fclose(&adata->fp_newsrc);
171  }
172  else
173  {
174  /* if file doesn't exist, create it */
175  adata->fp_newsrc = mutt_file_fopen(adata->newsrc_file, "a");
176  mutt_file_fclose(&adata->fp_newsrc);
177  }
178 
179  /* open .newsrc */
180  adata->fp_newsrc = mutt_file_fopen(adata->newsrc_file, "r");
181  if (!adata->fp_newsrc)
182  {
183  mutt_perror(adata->newsrc_file);
184  return -1;
185  }
186 
187  /* lock it */
188  mutt_debug(LL_DEBUG1, "Locking %s\n", adata->newsrc_file);
189  if (mutt_file_lock(fileno(adata->fp_newsrc), false, true))
190  {
191  mutt_file_fclose(&adata->fp_newsrc);
192  return -1;
193  }
194 
195  if (stat(adata->newsrc_file, &sb) != 0)
196  {
197  mutt_perror(adata->newsrc_file);
198  nntp_newsrc_close(adata);
199  return -1;
200  }
201 
202  if ((adata->size == sb.st_size) && (adata->mtime == sb.st_mtime))
203  return 0;
204 
205  adata->size = sb.st_size;
206  adata->mtime = sb.st_mtime;
207  adata->newsrc_modified = true;
208  mutt_debug(LL_DEBUG1, "Parsing %s\n", adata->newsrc_file);
209 
210  /* .newsrc has been externally modified or hasn't been loaded yet */
211  for (unsigned int i = 0; i < adata->groups_num; i++)
212  {
213  struct NntpMboxData *mdata = adata->groups_list[i];
214  if (!mdata)
215  continue;
216 
217  mdata->subscribed = false;
218  mdata->newsrc_len = 0;
219  FREE(&mdata->newsrc_ent);
220  }
221 
222  line = mutt_mem_malloc(sb.st_size + 1);
223  while (sb.st_size && fgets(line, sb.st_size + 1, adata->fp_newsrc))
224  {
225  char *b = NULL, *h = NULL;
226  unsigned int j = 1;
227  bool subs = false;
228 
229  /* find end of newsgroup name */
230  char *p = strpbrk(line, ":!");
231  if (!p)
232  continue;
233 
234  /* ":" - subscribed, "!" - unsubscribed */
235  if (*p == ':')
236  subs = true;
237  *p++ = '\0';
238 
239  /* get newsgroup data */
240  struct NntpMboxData *mdata = mdata_find(adata, line);
241  FREE(&mdata->newsrc_ent);
242 
243  /* count number of entries */
244  b = p;
245  while (*b)
246  if (*b++ == ',')
247  j++;
248  mdata->newsrc_ent = mutt_mem_calloc(j, sizeof(struct NewsrcEntry));
249  mdata->subscribed = subs;
250 
251  /* parse entries */
252  j = 0;
253  while (p)
254  {
255  b = p;
256 
257  /* find end of entry */
258  p = strchr(p, ',');
259  if (p)
260  *p++ = '\0';
261 
262  /* first-last or single number */
263  h = strchr(b, '-');
264  if (h)
265  *h++ = '\0';
266  else
267  h = b;
268 
269  if ((sscanf(b, ANUM, &mdata->newsrc_ent[j].first) == 1) &&
270  (sscanf(h, ANUM, &mdata->newsrc_ent[j].last) == 1))
271  {
272  j++;
273  }
274  }
275  if (j == 0)
276  {
277  mdata->newsrc_ent[j].first = 1;
278  mdata->newsrc_ent[j].last = 0;
279  j++;
280  }
281  if (mdata->last_message == 0)
282  mdata->last_message = mdata->newsrc_ent[j - 1].last;
283  mdata->newsrc_len = j;
284  mutt_mem_realloc(&mdata->newsrc_ent, j * sizeof(struct NewsrcEntry));
285  nntp_group_unread_stat(mdata);
286  mutt_debug(LL_DEBUG2, "%s\n", mdata->group);
287  }
288  FREE(&line);
289  return 1;
290 }
291 
297 {
298  if (!m)
299  return;
300 
301  struct NntpMboxData *mdata = m->mdata;
302  anum_t last = 0, first = 1;
303  bool series;
304  unsigned int entries;
305 
306  const short c_sort = cs_subset_sort(NeoMutt->sub, "sort");
307  if (c_sort != SORT_ORDER)
308  {
311  }
312 
313  entries = mdata->newsrc_len;
314  if (!entries)
315  {
316  entries = 5;
317  mdata->newsrc_ent = mutt_mem_calloc(entries, sizeof(struct NewsrcEntry));
318  }
319 
320  /* Set up to fake initial sequence from 1 to the article before the
321  * first article in our list */
322  mdata->newsrc_len = 0;
323  series = true;
324  for (int i = 0; i < m->msg_count; i++)
325  {
326  struct Email *e = m->emails[i];
327  if (!e)
328  break;
329 
330  /* search for first unread */
331  if (series)
332  {
333  /* We don't actually check sequential order, since we mark
334  * "missing" entries as read/deleted */
335  last = nntp_edata_get(e)->article_num;
336  if ((last >= mdata->first_message) && !e->deleted && !e->read)
337  {
338  if (mdata->newsrc_len >= entries)
339  {
340  entries *= 2;
341  mutt_mem_realloc(&mdata->newsrc_ent, entries * sizeof(struct NewsrcEntry));
342  }
343  mdata->newsrc_ent[mdata->newsrc_len].first = first;
344  mdata->newsrc_ent[mdata->newsrc_len].last = last - 1;
345  mdata->newsrc_len++;
346  series = false;
347  }
348  }
349 
350  /* search for first read */
351  else
352  {
353  if (e->deleted || e->read)
354  {
355  first = last + 1;
356  series = true;
357  }
358  last = nntp_edata_get(e)->article_num;
359  }
360  }
361 
362  if (series && (first <= mdata->last_loaded))
363  {
364  if (mdata->newsrc_len >= entries)
365  {
366  entries++;
367  mutt_mem_realloc(&mdata->newsrc_ent, entries * sizeof(struct NewsrcEntry));
368  }
369  mdata->newsrc_ent[mdata->newsrc_len].first = first;
370  mdata->newsrc_ent[mdata->newsrc_len].last = mdata->last_loaded;
371  mdata->newsrc_len++;
372  }
373  mutt_mem_realloc(&mdata->newsrc_ent, mdata->newsrc_len * sizeof(struct NewsrcEntry));
374 
375  if (c_sort != SORT_ORDER)
376  {
377  cs_subset_str_native_set(NeoMutt->sub, "sort", c_sort, NULL);
379  }
380 }
381 
389 static int update_file(char *filename, char *buf)
390 {
391  FILE *fp = NULL;
392  char tmpfile[PATH_MAX];
393  int rc = -1;
394 
395  while (true)
396  {
397  snprintf(tmpfile, sizeof(tmpfile), "%s.tmp", filename);
398  fp = mutt_file_fopen(tmpfile, "w");
399  if (!fp)
400  {
401  mutt_perror(tmpfile);
402  *tmpfile = '\0';
403  break;
404  }
405  if (fputs(buf, fp) == EOF)
406  {
407  mutt_perror(tmpfile);
408  break;
409  }
410  if (mutt_file_fclose(&fp) == EOF)
411  {
412  mutt_perror(tmpfile);
413  fp = NULL;
414  break;
415  }
416  fp = NULL;
417  if (rename(tmpfile, filename) < 0)
418  {
419  mutt_perror(filename);
420  break;
421  }
422  *tmpfile = '\0';
423  rc = 0;
424  break;
425  }
426  mutt_file_fclose(&fp);
427 
428  if (*tmpfile)
429  unlink(tmpfile);
430  return rc;
431 }
432 
440 {
441  if (!adata)
442  return -1;
443 
444  int rc = -1;
445 
446  size_t buflen = 10240;
447  char *buf = mutt_mem_calloc(1, buflen);
448  size_t off = 0;
449 
450  /* we will generate full newsrc here */
451  for (unsigned int i = 0; i < adata->groups_num; i++)
452  {
453  struct NntpMboxData *mdata = adata->groups_list[i];
454 
455  if (!mdata || !mdata->newsrc_ent)
456  continue;
457 
458  /* write newsgroup name */
459  if (off + strlen(mdata->group) + 3 > buflen)
460  {
461  buflen *= 2;
462  mutt_mem_realloc(&buf, buflen);
463  }
464  snprintf(buf + off, buflen - off, "%s%c ", mdata->group, mdata->subscribed ? ':' : '!');
465  off += strlen(buf + off);
466 
467  /* write entries */
468  for (unsigned int j = 0; j < mdata->newsrc_len; j++)
469  {
470  if (off + 1024 > buflen)
471  {
472  buflen *= 2;
473  mutt_mem_realloc(&buf, buflen);
474  }
475  if (j)
476  buf[off++] = ',';
477  if (mdata->newsrc_ent[j].first == mdata->newsrc_ent[j].last)
478  snprintf(buf + off, buflen - off, "%u", mdata->newsrc_ent[j].first);
479  else if (mdata->newsrc_ent[j].first < mdata->newsrc_ent[j].last)
480  {
481  snprintf(buf + off, buflen - off, "%u-%u", mdata->newsrc_ent[j].first,
482  mdata->newsrc_ent[j].last);
483  }
484  off += strlen(buf + off);
485  }
486  buf[off++] = '\n';
487  }
488  buf[off] = '\0';
489 
490  /* newrc being fully rewritten */
491  mutt_debug(LL_DEBUG1, "Updating %s\n", adata->newsrc_file);
492  if (adata->newsrc_file && (update_file(adata->newsrc_file, buf) == 0))
493  {
494  struct stat sb;
495 
496  rc = stat(adata->newsrc_file, &sb);
497  if (rc == 0)
498  {
499  adata->size = sb.st_size;
500  adata->mtime = sb.st_mtime;
501  }
502  else
503  {
504  mutt_perror(adata->newsrc_file);
505  }
506  }
507  FREE(&buf);
508  return rc;
509 }
510 
518 static void cache_expand(char *dst, size_t dstlen, struct ConnAccount *cac, const char *src)
519 {
520  char *c = NULL;
521  char file[PATH_MAX];
522 
523  /* server subdirectory */
524  if (cac)
525  {
526  struct Url url = { 0 };
527 
528  mutt_account_tourl(cac, &url);
529  url.path = mutt_str_dup(src);
530  url_tostring(&url, file, sizeof(file), U_PATH);
531  FREE(&url.path);
532  }
533  else
534  mutt_str_copy(file, src ? src : "", sizeof(file));
535 
536  const char *const c_news_cache_dir =
537  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 = mutt_buffer_pool_get();
546  mutt_buffer_addstr(tmp, dst);
548  mutt_encode_path(tmp, dst);
549  mutt_str_copy(dst, mutt_buffer_string(tmp), dstlen);
551 }
552 
559 void 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 
575 int 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 " " ANUM " %c %8191[^\n]", group, &last,
589  &first, &mod, desc) < 4)
590  {
591  mutt_debug(LL_DEBUG2, "Can't parse server line: %s\n", line);
592  return 0;
593  }
594 
595  mdata = mdata_find(adata, group);
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))
602  nntp_group_unread_stat(mdata);
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];
619  char file[4096];
620  time_t t;
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, "%ld%4095s", &t, file) != 1) || (t == 0))
629  {
630  mutt_file_fclose(&fp);
631  return -1;
632  }
633  adata->newgroups_time = t;
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 %u %u %c%s%s\n", mdata->group,
673  mdata->last_message, mdata->first_message, mdata->allowed ? 'y' : 'n',
674  mdata->desc ? " " : "", mdata->desc ? mdata->desc : "");
675  off += strlen(buf + off);
676  }
677 
678  char file[PATH_MAX];
679  cache_expand(file, sizeof(file), &adata->conn->account, ".active");
680  mutt_debug(LL_DEBUG1, "Updating %s\n", file);
681  int rc = update_file(file, buf);
682  FREE(&buf);
683  return rc;
684 }
685 
686 #ifdef USE_HCACHE
687 
690 static void nntp_hcache_namer(const char *path, struct Buffer *dest)
691 {
692  mutt_buffer_printf(dest, "%s.hcache", path);
693 
694  /* Strip out any directories in the path */
695  char *first = strchr(mutt_buffer_string(dest), '/');
696  char *last = strrchr(mutt_buffer_string(dest), '/');
697  if (first && last && (last > first))
698  {
699  memmove(first, last, strlen(last) + 1);
700  }
701 }
702 
710 {
711  struct Url url = { 0 };
712  char file[PATH_MAX];
713 
714  const bool c_save_unsubscribed =
715  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 =
726  cs_subset_path(NeoMutt->sub, "news_cache_dir");
727  return mutt_hcache_open(c_news_cache_dir, file, nntp_hcache_namer);
728 }
729 
735 void nntp_hcache_update(struct NntpMboxData *mdata, struct HeaderCache *hc)
736 {
737  if (!hc)
738  return;
739 
740  char buf[32];
741  bool old = false;
742  anum_t first = 0, last = 0;
743 
744  /* fetch previous values of first and last */
745  size_t dlen = 0;
746  void *hdata = mutt_hcache_fetch_raw(hc, "index", 5, &dlen);
747  if (hdata)
748  {
749  mutt_debug(LL_DEBUG2, "mutt_hcache_fetch index: %s\n", (char *) hdata);
750  if (sscanf(hdata, ANUM " " ANUM, &first, &last) == 2)
751  {
752  old = true;
753  mdata->last_cached = last;
754 
755  /* clean removed headers from cache */
756  for (anum_t current = first; current <= last; current++)
757  {
758  if ((current >= mdata->first_message) && (current <= mdata->last_message))
759  continue;
760 
761  snprintf(buf, sizeof(buf), "%u", current);
762  mutt_debug(LL_DEBUG2, "mutt_hcache_delete_record %s\n", buf);
763  mutt_hcache_delete_record(hc, buf, strlen(buf));
764  }
765  }
766  mutt_hcache_free_raw(hc, &hdata);
767  }
768 
769  /* store current values of first and last */
770  if (!old || (mdata->first_message != first) || (mdata->last_message != last))
771  {
772  snprintf(buf, sizeof(buf), "%u %u", mdata->first_message, mdata->last_message);
773  mutt_debug(LL_DEBUG2, "mutt_hcache_store index: %s\n", buf);
774  mutt_hcache_store_raw(hc, "index", 5, buf, strlen(buf) + 1);
775  }
776 }
777 #endif
778 
783 static int nntp_bcache_delete(const char *id, struct BodyCache *bcache, void *data)
784 {
785  struct NntpMboxData *mdata = data;
786  anum_t anum;
787  char c;
788 
789  if (!mdata || (sscanf(id, ANUM "%c", &anum, &c) != 1) ||
790  (anum < mdata->first_message) || (anum > mdata->last_message))
791  {
792  if (mdata)
793  mutt_debug(LL_DEBUG2, "mutt_bcache_del %s\n", id);
794  mutt_bcache_del(bcache, id);
795  }
796  return 0;
797 }
798 
804 {
806 }
807 
813 {
814  if (!mdata || !mdata->adata || !mdata->adata->cacheable)
815  return;
816 
817 #ifdef USE_HCACHE
818  struct Buffer file = mutt_buffer_make(PATH_MAX);
819  nntp_hcache_namer(mdata->group, &file);
820  cache_expand(file.data, file.dsize, &mdata->adata->conn->account,
821  mutt_buffer_string(&file));
822  unlink(mutt_buffer_string(&file));
823  mdata->last_cached = 0;
824  mutt_debug(LL_DEBUG2, "%s\n", mutt_buffer_string(&file));
825  mutt_buffer_dealloc(&file);
826 #endif
827 
828  if (!mdata->bcache)
829  {
830  mdata->bcache = mutt_bcache_open(&mdata->adata->conn->account, mdata->group);
831  }
832  if (mdata->bcache)
833  {
834  mutt_debug(LL_DEBUG2, "%s/*\n", mdata->group);
836  mutt_bcache_close(&mdata->bcache);
837  }
838 }
839 
847 {
848  char file[PATH_MAX];
849  char *fp = NULL;
850  struct dirent *entry = NULL;
851  DIR *dp = NULL;
852 
853  if (!adata || !adata->cacheable)
854  return;
855 
856  cache_expand(file, sizeof(file), &adata->conn->account, NULL);
857  dp = opendir(file);
858  if (dp)
859  {
860  mutt_strn_cat(file, sizeof(file), "/", 1);
861  fp = file + strlen(file);
862  while ((entry = readdir(dp)))
863  {
864  char *group = entry->d_name;
865  struct stat sb;
866  struct NntpMboxData *mdata = NULL;
867  struct NntpMboxData tmp_mdata;
868 
869  if (mutt_str_equal(group, ".") || mutt_str_equal(group, ".."))
870  continue;
871  *fp = '\0';
872  mutt_strn_cat(file, sizeof(file), group, strlen(group));
873  if (stat(file, &sb) != 0)
874  continue;
875 
876 #ifdef USE_HCACHE
877  if (S_ISREG(sb.st_mode))
878  {
879  char *ext = group + strlen(group) - 7;
880  if ((strlen(group) < 8) || !mutt_str_equal(ext, ".hcache"))
881  continue;
882  *ext = '\0';
883  }
884  else
885 #endif
886  if (!S_ISDIR(sb.st_mode))
887  continue;
888 
889  const bool c_save_unsubscribed =
890  cs_subset_bool(NeoMutt->sub, "save_unsubscribed");
891  mdata = mutt_hash_find(adata->groups_hash, group);
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  continue;
901 
903  if (S_ISDIR(sb.st_mode))
904  {
905  rmdir(file);
906  mutt_debug(LL_DEBUG2, "%s\n", file);
907  }
908  }
909  closedir(dp);
910  }
911 }
912 
925 const char *nntp_format_str(char *buf, size_t buflen, size_t col, int cols, char op,
926  const char *src, const char *prec, const char *if_str,
927  const char *else_str, intptr_t data, MuttFormatFlags flags)
928 {
929  struct NntpAccountData *adata = (struct NntpAccountData *) data;
930  struct ConnAccount *cac = &adata->conn->account;
931  char fn[128], fmt[128];
932 
933  switch (op)
934  {
935  case 'a':
936  {
937  struct Url url = { 0 };
938  mutt_account_tourl(cac, &url);
939  url_tostring(&url, fn, sizeof(fn), U_PATH);
940  char *p = strchr(fn, '/');
941  if (p)
942  *p = '\0';
943  snprintf(fmt, sizeof(fmt), "%%%ss", prec);
944  snprintf(buf, buflen, fmt, fn);
945  break;
946  }
947  case 'p':
948  snprintf(fmt, sizeof(fmt), "%%%su", prec);
949  snprintf(buf, buflen, fmt, cac->port);
950  break;
951  case 'P':
952  *buf = '\0';
953  if (cac->flags & MUTT_ACCT_PORT)
954  {
955  snprintf(fmt, sizeof(fmt), "%%%su", prec);
956  snprintf(buf, buflen, fmt, cac->port);
957  }
958  break;
959  case 's':
960  mutt_str_copy(fn, cac->host, sizeof(fn));
961  mutt_str_lower(fn);
962  snprintf(fmt, sizeof(fmt), "%%%ss", prec);
963  snprintf(buf, buflen, fmt, fn);
964  break;
965  case 'S':
966  {
967  struct Url url = { 0 };
968  mutt_account_tourl(cac, &url);
969  url_tostring(&url, fn, sizeof(fn), U_PATH);
970  char *p = strchr(fn, ':');
971  if (p)
972  *p = '\0';
973  snprintf(fmt, sizeof(fmt), "%%%ss", prec);
974  snprintf(buf, buflen, fmt, fn);
975  break;
976  }
977  case 'u':
978  snprintf(fmt, sizeof(fmt), "%%%ss", prec);
979  snprintf(buf, buflen, fmt, cac->user);
980  break;
981  }
982  return src;
983 }
984 
988 static const char *nntp_get_field(enum ConnAccountField field, void *gf_data)
989 {
990  switch (field)
991  {
992  case MUTT_CA_LOGIN:
993  case MUTT_CA_USER:
994  return cs_subset_string(NeoMutt->sub, "nntp_user");
995  case MUTT_CA_PASS:
996  return cs_subset_string(NeoMutt->sub, "nntp_pass");
997  case MUTT_CA_OAUTH_CMD:
998  case MUTT_CA_HOST:
999  default:
1000  return NULL;
1001  }
1002 }
1003 
1017 struct NntpAccountData *nntp_select_server(struct Mailbox *m, const char *server, bool leave_lock)
1018 {
1019  char file[PATH_MAX];
1020  int rc;
1021  struct ConnAccount cac = { { 0 } };
1022  struct NntpAccountData *adata = NULL;
1023  struct Connection *conn = NULL;
1024 
1025  if (!server || (*server == '\0'))
1026  {
1027  mutt_error(_("No news server defined"));
1028  return NULL;
1029  }
1030 
1031  /* create account from news server url */
1032  cac.flags = 0;
1033  cac.port = NNTP_PORT;
1034  cac.type = MUTT_ACCT_TYPE_NNTP;
1035  cac.service = "nntp";
1036  cac.get_field = nntp_get_field;
1037 
1038  snprintf(file, sizeof(file), "%s%s", strstr(server, "://") ? "" : "news://", server);
1039  struct Url *url = url_parse(file);
1040  if (!url || (url->path && *url->path) ||
1041  !((url->scheme == U_NNTP) || (url->scheme == U_NNTPS)) || !url->host ||
1042  (mutt_account_fromurl(&cac, url) < 0))
1043  {
1044  url_free(&url);
1045  mutt_error(_("%s is an invalid news server specification"), server);
1046  return NULL;
1047  }
1048  if (url->scheme == U_NNTPS)
1049  {
1050  cac.flags |= MUTT_ACCT_SSL;
1051  cac.port = NNTP_SSL_PORT;
1052  }
1053  url_free(&url);
1054 
1055  /* find connection by account */
1056  conn = mutt_conn_find(&cac);
1057  if (!conn)
1058  return NULL;
1059  if (!(conn->account.flags & MUTT_ACCT_USER) && cac.flags & MUTT_ACCT_USER)
1060  {
1061  conn->account.flags |= MUTT_ACCT_USER;
1062  conn->account.user[0] = '\0';
1063  }
1064 
1065  /* news server already exists */
1066  // adata = conn->data;
1067  if (adata)
1068  {
1069  if (adata->status == NNTP_BYE)
1070  adata->status = NNTP_NONE;
1071  if (nntp_open_connection(adata) < 0)
1072  return NULL;
1073 
1074  rc = nntp_newsrc_parse(adata);
1075  if (rc < 0)
1076  return NULL;
1077 
1078  /* check for new newsgroups */
1079  if (!leave_lock && (nntp_check_new_groups(m, adata) < 0))
1080  rc = -1;
1081 
1082  /* .newsrc has been externally modified */
1083  if (rc > 0)
1084  nntp_clear_cache(adata);
1085  if ((rc < 0) || !leave_lock)
1086  nntp_newsrc_close(adata);
1087  return (rc < 0) ? NULL : adata;
1088  }
1089 
1090  /* new news server */
1091  adata = nntp_adata_new(conn);
1092 
1093  rc = nntp_open_connection(adata);
1094 
1095  /* try to create cache directory and enable caching */
1096  adata->cacheable = false;
1097  const char *const c_news_cache_dir =
1098  cs_subset_path(NeoMutt->sub, "news_cache_dir");
1099  if ((rc >= 0) && c_news_cache_dir)
1100  {
1101  cache_expand(file, sizeof(file), &conn->account, NULL);
1102  if (mutt_file_mkdir(file, S_IRWXU) < 0)
1103  {
1104  mutt_error(_("Can't create %s: %s"), file, strerror(errno));
1105  }
1106  adata->cacheable = true;
1107  }
1108 
1109  /* load .newsrc */
1110  if (rc >= 0)
1111  {
1112  const char *const c_newsrc = cs_subset_path(NeoMutt->sub, "newsrc");
1113  mutt_expando_format(file, sizeof(file), 0, sizeof(file), NONULL(c_newsrc),
1114  nntp_format_str, (intptr_t) adata, MUTT_FORMAT_NO_FLAGS);
1115  mutt_expand_path(file, sizeof(file));
1116  adata->newsrc_file = mutt_str_dup(file);
1117  rc = nntp_newsrc_parse(adata);
1118  }
1119  if (rc >= 0)
1120  {
1121  /* try to load list of newsgroups from cache */
1122  if (adata->cacheable && (active_get_cache(adata) == 0))
1123  rc = nntp_check_new_groups(m, adata);
1124 
1125  /* load list of newsgroups from server */
1126  else
1127  rc = nntp_active_fetch(adata, false);
1128  }
1129 
1130  if (rc >= 0)
1131  nntp_clear_cache(adata);
1132 
1133 #ifdef USE_HCACHE
1134  /* check cache files */
1135  if ((rc >= 0) && adata->cacheable)
1136  {
1137  struct dirent *entry = NULL;
1138  DIR *dp = opendir(file);
1139 
1140  if (dp)
1141  {
1142  while ((entry = readdir(dp)))
1143  {
1144  struct HeaderCache *hc = NULL;
1145  void *hdata = NULL;
1146  char *group = entry->d_name;
1147 
1148  char *p = group + strlen(group) - 7;
1149  if ((strlen(group) < 8) || (strcmp(p, ".hcache") != 0))
1150  continue;
1151  *p = '\0';
1152  struct NntpMboxData *mdata = mutt_hash_find(adata->groups_hash, group);
1153  if (!mdata)
1154  continue;
1155 
1156  hc = nntp_hcache_open(mdata);
1157  if (!hc)
1158  continue;
1159 
1160  /* fetch previous values of first and last */
1161  size_t dlen = 0;
1162  hdata = mutt_hcache_fetch_raw(hc, "index", 5, &dlen);
1163  if (hdata)
1164  {
1165  anum_t first, last;
1166 
1167  if (sscanf(hdata, ANUM " " ANUM, &first, &last) == 2)
1168  {
1169  if (mdata->deleted)
1170  {
1171  mdata->first_message = first;
1172  mdata->last_message = last;
1173  }
1174  if ((last >= mdata->first_message) && (last <= mdata->last_message))
1175  {
1176  mdata->last_cached = last;
1177  mutt_debug(LL_DEBUG2, "%s last_cached=%u\n", mdata->group, last);
1178  }
1179  }
1180  mutt_hcache_free_raw(hc, &hdata);
1181  }
1182  mutt_hcache_close(hc);
1183  }
1184  closedir(dp);
1185  }
1186  }
1187 #endif
1188 
1189  if ((rc < 0) || !leave_lock)
1190  nntp_newsrc_close(adata);
1191 
1192  if (rc < 0)
1193  {
1194  mutt_hash_free(&adata->groups_hash);
1195  FREE(&adata->groups_list);
1196  FREE(&adata->newsrc_file);
1197  FREE(&adata->authenticators);
1198  FREE(&adata);
1199  mutt_socket_close(conn);
1200  FREE(&conn);
1201  return NULL;
1202  }
1203 
1204  return adata;
1205 }
1206 
1219 void nntp_article_status(struct Mailbox *m, struct Email *e, char *group, anum_t anum)
1220 {
1221  struct NntpMboxData *mdata = m->mdata;
1222 
1223  if (group)
1224  mdata = mutt_hash_find(mdata->adata->groups_hash, group);
1225 
1226  if (!mdata)
1227  return;
1228 
1229  for (unsigned int i = 0; i < mdata->newsrc_len; i++)
1230  {
1231  if ((anum >= mdata->newsrc_ent[i].first) && (anum <= mdata->newsrc_ent[i].last))
1232  {
1233  /* can't use mutt_set_flag() because ctx_update() didn't get called yet */
1234  e->read = true;
1235  return;
1236  }
1237  }
1238 
1239  /* article was not cached yet, it's new */
1240  if (anum > mdata->last_cached)
1241  return;
1242 
1243  /* article isn't read but cached, it's old */
1244  const bool c_mark_old = cs_subset_bool(NeoMutt->sub, "mark_old");
1245  if (c_mark_old)
1246  e->old = true;
1247 }
1248 
1257 {
1258  if (!adata || !adata->groups_hash || !group || (*group == '\0'))
1259  return NULL;
1260 
1261  struct NntpMboxData *mdata = mdata_find(adata, group);
1262  mdata->subscribed = true;
1263  if (!mdata->newsrc_ent)
1264  {
1265  mdata->newsrc_ent = mutt_mem_calloc(1, sizeof(struct NewsrcEntry));
1266  mdata->newsrc_len = 1;
1267  mdata->newsrc_ent[0].first = 1;
1268  mdata->newsrc_ent[0].last = 0;
1269  }
1270  return mdata;
1271 }
1272 
1281 {
1282  if (!adata || !adata->groups_hash || !group || (*group == '\0'))
1283  return NULL;
1284 
1285  struct NntpMboxData *mdata = mutt_hash_find(adata->groups_hash, group);
1286  if (!mdata)
1287  return NULL;
1288 
1289  mdata->subscribed = false;
1290  const bool c_save_unsubscribed =
1291  cs_subset_bool(NeoMutt->sub, "save_unsubscribed");
1292  if (!c_save_unsubscribed)
1293  {
1294  mdata->newsrc_len = 0;
1295  FREE(&mdata->newsrc_ent);
1296  }
1297  return mdata;
1298 }
1299 
1309  struct NntpAccountData *adata, char *group)
1310 {
1311  if (!adata || !adata->groups_hash || !group || (*group == '\0'))
1312  return NULL;
1313 
1314  struct NntpMboxData *mdata = mutt_hash_find(adata->groups_hash, group);
1315  if (!mdata)
1316  return NULL;
1317 
1318  if (mdata->newsrc_ent)
1319  {
1320  mutt_mem_realloc(&mdata->newsrc_ent, sizeof(struct NewsrcEntry));
1321  mdata->newsrc_len = 1;
1322  mdata->newsrc_ent[0].first = 1;
1323  mdata->newsrc_ent[0].last = mdata->last_message;
1324  }
1325  mdata->unread = 0;
1326  if (m && (m->mdata == mdata))
1327  {
1328  for (unsigned int i = 0; i < m->msg_count; i++)
1329  {
1330  struct Email *e = m->emails[i];
1331  if (!e)
1332  break;
1333  mutt_set_flag(m, e, MUTT_READ, true);
1334  }
1335  }
1336  return mdata;
1337 }
1338 
1348  struct NntpAccountData *adata, char *group)
1349 {
1350  if (!adata || !adata->groups_hash || !group || (*group == '\0'))
1351  return NULL;
1352 
1353  struct NntpMboxData *mdata = mutt_hash_find(adata->groups_hash, group);
1354  if (!mdata)
1355  return NULL;
1356 
1357  if (mdata->newsrc_ent)
1358  {
1359  mutt_mem_realloc(&mdata->newsrc_ent, sizeof(struct NewsrcEntry));
1360  mdata->newsrc_len = 1;
1361  mdata->newsrc_ent[0].first = 1;
1362  mdata->newsrc_ent[0].last = mdata->first_message - 1;
1363  }
1364  if (m && (m->mdata == mdata))
1365  {
1366  mdata->unread = m->msg_count;
1367  for (unsigned int i = 0; i < m->msg_count; i++)
1368  {
1369  struct Email *e = m->emails[i];
1370  if (!e)
1371  break;
1372  mutt_set_flag(m, e, MUTT_READ, false);
1373  }
1374  }
1375  else
1376  {
1377  mdata->unread = mdata->last_message;
1378  if (mdata->newsrc_ent)
1379  mdata->unread -= mdata->newsrc_ent[0].last;
1380  }
1381  return mdata;
1382 }
1383 
1390 void nntp_mailbox(struct Mailbox *m, char *buf, size_t buflen)
1391 {
1392  if (!m)
1393  return;
1394 
1395  for (unsigned int i = 0; i < CurrentNewsSrv->groups_num; i++)
1396  {
1398 
1399  if (!mdata || !mdata->subscribed || !mdata->unread)
1400  continue;
1401 
1402  if ((m->type == MUTT_NNTP) &&
1403  mutt_str_equal(mdata->group, ((struct NntpMboxData *) m->mdata)->group))
1404  {
1405  unsigned int unread = 0;
1406 
1407  for (unsigned int j = 0; j < m->msg_count; j++)
1408  {
1409  struct Email *e = m->emails[j];
1410  if (!e)
1411  break;
1412  if (!e->read && !e->deleted)
1413  unread++;
1414  }
1415  if (unread == 0)
1416  continue;
1417  }
1418  mutt_str_copy(buf, mdata->group, buflen);
1419  break;
1420  }
1421 }
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:354
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:904
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
char * path
Definition: lib.h:69
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:327
int mutt_account_fromurl(struct ConnAccount *cac, const struct Url *url)
Fill ConnAccount with information from url.
Definition: mutt_account.c:43
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:780
struct NntpAcache acache[NNTP_ACACHE_LEN]
Definition: mdata.h:48
enum MailboxType type
Mailbox type.
Definition: mailbox.h:105
uint8_t MuttFormatFlags
Flags for mutt_expando_format(), e.g. MUTT_FORMAT_FORCESUBJ.
Definition: format_flags.h:29
#define NONULL(x)
Definition: string2.h:37
char * newsrc_file
Definition: adata.h:52
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
int msg_count
Total number of messages.
Definition: mailbox.h:91
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:66
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
The envelope/body of an email.
Definition: email.h:37
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
struct NntpMboxData * mutt_newsgroup_catchup(struct Mailbox *m, struct NntpAccountData *adata, char *group)
Catchup newsgroup.
Definition: newsrc.c:1308
void nntp_newsrc_gen_entries(struct Mailbox *m)
Generate array of .newsrc entries.
Definition: newsrc.c:296
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:40
&#39;NNTP&#39; (Usenet) Mailbox type
Definition: mailbox.h:52
Structs that make up an email.
#define mutt_error(...)
Definition: logging.h:88
struct BodyCache * mutt_bcache_open(struct ConnAccount *account, const char *mailbox)
Open an Email-Body Cache.
Definition: bcache.c:143
char * mutt_strn_cat(char *d, size_t l, const char *s, size_t sl)
Concatenate two strings.
Definition: string.c:414
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition: hash.c:449
Disconnected from server.
Definition: private.h:46
A parsed URL proto://user:password@host:port/path?a=1&b=2
Definition: url.h:68
bool newsrc_modified
Definition: adata.h:50
struct NntpAccountData * adata
Definition: mdata.h:47
enum UrlScheme scheme
Scheme, e.g. U_SMTPS.
Definition: url.h:70
NeoMutt Logging.
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:61
time_t mtime
Definition: adata.h:56
header cache structure
Definition: lib.h:84
void nntp_clear_cache(struct NntpAccountData *adata)
Clear the NNTP cache.
Definition: newsrc.c:846
String manipulation buffer.
Definition: buffer.h:33
char user[128]
Username.
Definition: connaccount.h:55
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
#define _(a)
Definition: message.h:28
anum_t article_num
Definition: edata.h:36
#define NNTP_ACACHE_LEN
Definition: lib.h:82
#define U_NO_FLAGS
Definition: url.h:49
Password.
Definition: connaccount.h:36
static int nntp_bcache_delete(const char *id, struct BodyCache *bcache, void *data)
Remove bcache file - Implements bcache_list_t.
Definition: newsrc.c:783
ConnAccountField
Login credentials.
Definition: connaccount.h:31
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition: url.c:123
void mutt_hcache_free_raw(struct HeaderCache *hc, void **data)
Multiplexor for StoreOps::free.
Definition: hcache.c:541
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
struct HeaderCache * mutt_hcache_open(const char *path, const char *folder, hcache_namer_t namer)
Multiplexor for StoreOps::open.
Definition: hcache.c:332
int nntp_active_save_cache(struct NntpAccountData *adata)
Save list of all newsgroups to cache.
Definition: newsrc.c:650
void nntp_delete_group_cache(struct NntpMboxData *mdata)
Remove hcache and bcache of newsgroup.
Definition: newsrc.c:812
void nntp_hcache_update(struct NntpMboxData *mdata, struct HeaderCache *hc)
Remove stale cached headers.
Definition: newsrc.c:735
Flags to control mutt_expando_format()
#define mutt_perror(...)
Definition: logging.h:89
Container for Accounts, Notifications.
Definition: neomutt.h:36
#define MUTT_ACCT_USER
User field has been set.
Definition: connaccount.h:43
struct HashTable * groups_hash
Definition: adata.h:62
NNTP-specific Account data -.
Definition: adata.h:36
anum_t last
Definition: lib.h:78
Nntp-specific Email data.
int mutt_file_unlock(int fd)
Unlock a file previously locked by mutt_file_lock()
Definition: file.c:1226
#define MUTT_FORMAT_NO_FLAGS
No flags are set.
Definition: format_flags.h:30
bool subscribed
Definition: mdata.h:41
Convenience wrapper for the config headers.
void mutt_account_tourl(struct ConnAccount *cac, struct Url *url)
Fill URL with info from account.
Definition: mutt_account.c:79
int mutt_bcache_list(struct BodyCache *bcache, bcache_list_t want_id, void *data)
Find matching entries in the Body Cache.
Definition: bcache.c:329
anum_t last_cached
Definition: mdata.h:39
char host[128]
Server to login to.
Definition: connaccount.h:53
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
Some miscellaneous functions.
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:305
size_t dsize
Length of data.
Definition: buffer.h:37
char * mutt_expand_path(char *buf, size_t buflen)
Create the canonical path.
Definition: muttlib.c:122
bool cacheable
Definition: adata.h:49
int nntp_active_fetch(struct NntpAccountData *adata, bool mark_new)
Fetch list of all newsgroups from server.
Definition: nntp.c:1963
bool read
Email is read.
Definition: email.h:51
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 -Expando Description %a Account url %p Port %P Port...
Definition: newsrc.c:925
Header cache multiplexor.
Many unsorted constants and some structs.
Log at debug level 2.
Definition: logging.h:41
int mutt_file_lock(int fd, bool excl, bool timeout)
(try to) lock a file using fcntl()
Definition: file.c:1178
static int update_file(char *filename, char *buf)
Update file with new contents.
Definition: newsrc.c:389
bool old
Email is seen, but unread.
Definition: email.h:50
void nntp_expand_path(char *buf, size_t buflen, struct ConnAccount *cac)
Make fully qualified url from newsgroup name.
Definition: newsrc.c:559
time_t newgroups_time
Definition: adata.h:57
Sort by the order the messages appear in the mailbox.
Definition: sort2.h:48
An entry in a .newsrc (subscribed newsgroups)
Definition: lib.h:75
int mutt_file_mkdir(const char *path, mode_t mode)
Recursively create directories.
Definition: file.c:877
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
anum_t last_loaded
Definition: mdata.h:38
Convenience wrapper for the core headers.
char * group
Definition: mdata.h:34
struct NntpMboxData * mutt_newsgroup_uncatchup(struct Mailbox *m, struct NntpAccountData *adata, char *group)
Uncatchup newsgroup.
Definition: newsrc.c:1347
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:294
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:112
void * mdata
Driver specific data.
Definition: mailbox.h:136
struct Connection * mutt_conn_find(const struct ConnAccount *cac)
Find a connection from a list.
Definition: mutt_socket.c:89
Url is nntps://.
Definition: url.h:42
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
off_t size
Definition: adata.h:55
Body Caching - local copies of email bodies.
struct NntpAccountData * nntp_select_server(struct Mailbox *m, const char *server, bool leave_lock)
Open a connection to an NNTP server.
Definition: newsrc.c:1017
void nntp_bcache_update(struct NntpMboxData *mdata)
Remove stale cached messages.
Definition: newsrc.c:803
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
short cs_subset_sort(const struct ConfigSubset *sub, const char *name)
Get a sort config item by name.
Definition: helpers.c:292
Email list needs resorting.
Definition: mailbox.h:181
unsigned short port
Port to connect to.
Definition: connaccount.h:57
Prototypes for many functions.
const char *(* get_field)(enum ConnAccountField field, void *gf_data)
Function to get some login credentials.
Definition: connaccount.h:67
#define MUTT_ACCT_PORT
Port field has been set.
Definition: connaccount.h:42
#define ANUM
Definition: lib.h:61
ConnAccount object used by POP and IMAP.
struct BodyCache * bcache
Definition: mdata.h:49
unsigned int status
Definition: adata.h:48
A mailbox.
Definition: mailbox.h:81
#define PATH_MAX
Definition: mutt.h:40
bool allowed
Definition: mdata.h:43
unsigned int groups_max
Definition: adata.h:60
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
void * adata
Private data (for Mailbox backends)
Definition: account.h:43
struct HeaderCache * nntp_hcache_open(struct NntpMboxData *mdata)
Open newsgroup hcache.
Definition: newsrc.c:709
User name.
Definition: connaccount.h:35
struct NntpMboxData * mutt_newsgroup_unsubscribe(struct NntpAccountData *adata, char *group)
Unsubscribe newsgroup.
Definition: newsrc.c:1280
char * data
Pointer to data.
Definition: buffer.h:35
No connection to server.
Definition: private.h:44
unsigned int groups_num
Definition: adata.h:59
int mutt_hcache_delete_record(struct HeaderCache *hc, const char *key, size_t keylen)
Multiplexor for StoreOps::delete_record.
Definition: hcache.c:637
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:418
Nntp-specific Mailbox data.
static void nntp_hcache_namer(const char *path, struct Buffer *dest)
Compose hcache file names - Implements hcache_namer_t.
Definition: newsrc.c:690
Messages that have been read.
Definition: mutt.h:92
unsigned char type
Connection type, e.g. MUTT_ACCT_TYPE_IMAP.
Definition: connaccount.h:58
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
char * host
Host.
Definition: url.h:73
static void cache_expand(char *dst, size_t dstlen, struct ConnAccount *cac, const char *src)
Make fully qualified cache file name.
Definition: newsrc.c:518
int nntp_add_group(char *line, void *data)
Parse newsgroup.
Definition: newsrc.c:575
int mutt_bcache_del(struct BodyCache *bcache, const char *id)
Delete a file from the Body Cache.
Definition: bcache.c:264
void nntp_mailbox(struct Mailbox *m, char *buf, size_t buflen)
Get first newsgroup with new messages.
Definition: newsrc.c:1390
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:194
const char * service
Name of the service, e.g. "imap".
Definition: connaccount.h:60
OAuth refresh command.
Definition: connaccount.h:37
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
Login details for a remote server.
Definition: connaccount.h:51
struct NntpEmailData * nntp_edata_get(struct Email *e)
Get the private data for this Email.
Definition: edata.c:58
struct NewsrcEntry * newsrc_ent
Definition: mdata.h:46
int mutt_socket_close(struct Connection *conn)
Close a socket.
Definition: socket.c:97
NNTP-specific Mailbox data -.
Definition: mdata.h:32
int nntp_newsrc_parse(struct NntpAccountData *adata)
Parse .newsrc file.
Definition: newsrc.c:162
unsigned int newsrc_len
Definition: mdata.h:45
void nntp_newsrc_close(struct NntpAccountData *adata)
Unlock and close .newsrc file.
Definition: newsrc.c:118
char * path
Path.
Definition: url.h:75
Nntp (Usenet) Account.
Definition: mutt_account.h:40
void mutt_hcache_close(struct HeaderCache *hc)
Multiplexor for StoreOps::close.
Definition: hcache.c:435
Local cache of email bodies.
Definition: bcache.c:50
uint8_t flags
e.g. MB_NORMAL
Definition: mailbox.h:134
struct Connection * conn
Definition: adata.h:63
static struct NntpMboxData * mdata_find(struct NntpAccountData *adata, const char *group)
Find NntpMboxData for given newsgroup or add it.
Definition: newsrc.c:72
char * authenticators
Definition: adata.h:53
void mutt_buffer_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:322
Log at debug level 1.
Definition: logging.h:40
bool deleted
Definition: mdata.h:44
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:749
char * mutt_str_lower(char *str)
Convert all characters in the string to lowercase.
Definition: string.c:504
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:446
anum_t first_message
Definition: mdata.h:36
bool deleted
Email is deleted.
Definition: email.h:45
anum_t unread
Definition: mdata.h:40
Url is nntp://.
Definition: url.h:41
#define NNTP_SSL_PORT
Definition: private.h:37
Connection Library.
void ** groups_list
Definition: adata.h:61
void nntp_group_unread_stat(struct NntpMboxData *mdata)
Count number of unread articles using .newsrc data.
Definition: newsrc.c:132
FILE * fp_newsrc
Definition: adata.h:51
#define mutt_message(...)
Definition: logging.h:87
#define FREE(x)
Definition: memory.h:40
struct NntpAccountData * CurrentNewsSrv
Current NNTP news server.
Definition: nntp.c:78
int nntp_check_new_groups(struct Mailbox *m, struct NntpAccountData *adata)
Check for new groups/articles in subscribed groups.
Definition: nntp.c:2032
int mutt_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:615
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
int nntp_newsrc_update(struct NntpAccountData *adata)
Update .newsrc file.
Definition: newsrc.c:439
NeoMutt connections.
Login name.
Definition: connaccount.h:34
Usenet network mailbox type; talk to an NNTP server.
void * mutt_hcache_fetch_raw(struct HeaderCache *hc, const char *key, size_t keylen, size_t *dlen)
Fetch a message&#39;s header from the cache.
Definition: hcache.c:521
Nntp-specific Account data.
Convenience wrapper for the library headers.
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:589
#define anum_t
Definition: lib.h:60
static const char * nntp_get_field(enum ConnAccountField field, void *gf_data)
Get connection login credentials - Implements ConnAccount::get_field()
Definition: newsrc.c:988
static int active_get_cache(struct NntpAccountData *adata)
Load list of all newsgroups from cache.
Definition: newsrc.c:616
void mailbox_changed(struct Mailbox *m, enum NotifyMailbox action)
Notify observers of a change to a Mailbox.
Definition: mailbox.c:208
void nntp_acache_free(struct NntpMboxData *mdata)
Remove all temporarily cache files.
Definition: newsrc.c:102
#define NNTP_PORT
Definition: private.h:36
struct NntpMboxData * mutt_newsgroup_subscribe(struct NntpAccountData *adata, char *group)
Subscribe newsgroup.
Definition: newsrc.c:1256
void mutt_bcache_close(struct BodyCache **bcache)
Close an Email-Body Cache.
Definition: bcache.c:164
anum_t first
Definition: lib.h:77
void nntp_article_status(struct Mailbox *m, struct Email *e, char *group, anum_t anum)
Get status of articles from .newsrc.
Definition: newsrc.c:1219
char * src
Raw URL string.
Definition: url.h:77
#define MUTT_ACCT_SSL
Account uses SSL/TLS.
Definition: connaccount.h:46
struct NntpAccountData * nntp_adata_new(struct Connection *conn)
Allocate and initialise a new NntpAccountData structure.
Definition: adata.c:62
#define U_PATH
Definition: url.h:50
Server name.
Definition: connaccount.h:33
void mutt_encode_path(struct Buffer *buf, const char *src)
Convert a path to &#39;us-ascii&#39;.
Definition: muttlib.c:1490
int nntp_open_connection(struct NntpAccountData *adata)
Connect to server, authenticate and get capabilities.
Definition: nntp.c:1712
MuttAccountFlags flags
Which fields are initialised, e.g. MUTT_ACCT_USER.
Definition: connaccount.h:59
char * desc
Definition: mdata.h:35
struct Url * url_parse(const char *src)
Fill in Url.
Definition: url.c:234
anum_t last_message
Definition: mdata.h:37