NeoMutt  2021-02-05-89-gabe350
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_globals.h"
56 #include "mutt_logging.h"
57 #include "mutt_socket.h"
58 #include "muttlib.h"
59 #include "protos.h"
60 #include "sort.h"
61 #ifdef USE_HCACHE
62 #include "hcache/lib.h"
63 #endif
64 
65 struct BodyCache;
66 
74 static struct NntpMboxData *mdata_find(struct NntpAccountData *adata, const char *group)
75 {
77  if (mdata)
78  return mdata;
79 
80  size_t len = strlen(group) + 1;
81  /* create NntpMboxData structure and add it to hash */
82  mdata = mutt_mem_calloc(1, sizeof(struct NntpMboxData) + len);
83  mdata->group = (char *) mdata + sizeof(struct NntpMboxData);
84  mutt_str_copy(mdata->group, group, len);
85  mdata->adata = adata;
86  mdata->deleted = true;
88 
89  /* add NntpMboxData to list */
91  {
92  adata->groups_max *= 2;
94  }
96 
97  return mdata;
98 }
99 
105 {
106  for (int i = 0; i < NNTP_ACACHE_LEN; i++)
107  {
108  if (mdata->acache[i].path)
109  {
110  unlink(mdata->acache[i].path);
111  FREE(&mdata->acache[i].path);
112  }
113  }
114 }
115 
121 {
122  if (!adata->fp_newsrc)
123  return;
124 
125  mutt_debug(LL_DEBUG1, "Unlocking %s\n", adata->newsrc_file);
126  mutt_file_unlock(fileno(adata->fp_newsrc));
128 }
129 
135 {
136  mdata->unread = 0;
137  if ((mdata->last_message == 0) ||
138  (mdata->first_message > mdata->last_message) || !mdata->newsrc_ent)
139  {
140  return;
141  }
142 
143  mdata->unread = mdata->last_message - mdata->first_message + 1;
144  for (unsigned int i = 0; i < mdata->newsrc_len; i++)
145  {
146  anum_t first = mdata->newsrc_ent[i].first;
147  if (first < mdata->first_message)
148  first = mdata->first_message;
149  anum_t last = mdata->newsrc_ent[i].last;
150  if (last > mdata->last_message)
151  last = mdata->last_message;
152  if (first <= last)
153  mdata->unread -= last - first + 1;
154  }
155 }
156 
165 {
166  char *line = NULL;
167  struct stat sb;
168 
169  if (adata->fp_newsrc)
170  {
171  /* if we already have a handle, close it and reopen */
172  mutt_file_fclose(&adata->fp_newsrc);
173  }
174  else
175  {
176  /* if file doesn't exist, create it */
177  adata->fp_newsrc = mutt_file_fopen(adata->newsrc_file, "a");
178  mutt_file_fclose(&adata->fp_newsrc);
179  }
180 
181  /* open .newsrc */
182  adata->fp_newsrc = mutt_file_fopen(adata->newsrc_file, "r");
183  if (!adata->fp_newsrc)
184  {
185  mutt_perror(adata->newsrc_file);
186  return -1;
187  }
188 
189  /* lock it */
190  mutt_debug(LL_DEBUG1, "Locking %s\n", adata->newsrc_file);
191  if (mutt_file_lock(fileno(adata->fp_newsrc), false, true))
192  {
193  mutt_file_fclose(&adata->fp_newsrc);
194  return -1;
195  }
196 
197  if (stat(adata->newsrc_file, &sb) != 0)
198  {
199  mutt_perror(adata->newsrc_file);
200  nntp_newsrc_close(adata);
201  return -1;
202  }
203 
204  if ((adata->size == sb.st_size) && (adata->mtime == sb.st_mtime))
205  return 0;
206 
207  adata->size = sb.st_size;
208  adata->mtime = sb.st_mtime;
209  adata->newsrc_modified = true;
210  mutt_debug(LL_DEBUG1, "Parsing %s\n", adata->newsrc_file);
211 
212  /* .newsrc has been externally modified or hasn't been loaded yet */
213  for (unsigned int i = 0; i < adata->groups_num; i++)
214  {
215  struct NntpMboxData *mdata = adata->groups_list[i];
216  if (!mdata)
217  continue;
218 
219  mdata->subscribed = false;
220  mdata->newsrc_len = 0;
221  FREE(&mdata->newsrc_ent);
222  }
223 
224  line = mutt_mem_malloc(sb.st_size + 1);
225  while (sb.st_size && fgets(line, sb.st_size + 1, adata->fp_newsrc))
226  {
227  char *b = NULL, *h = NULL;
228  unsigned int j = 1;
229  bool subs = false;
230 
231  /* find end of newsgroup name */
232  char *p = strpbrk(line, ":!");
233  if (!p)
234  continue;
235 
236  /* ":" - subscribed, "!" - unsubscribed */
237  if (*p == ':')
238  subs = true;
239  *p++ = '\0';
240 
241  /* get newsgroup data */
242  struct NntpMboxData *mdata = mdata_find(adata, line);
243  FREE(&mdata->newsrc_ent);
244 
245  /* count number of entries */
246  b = p;
247  while (*b)
248  if (*b++ == ',')
249  j++;
250  mdata->newsrc_ent = mutt_mem_calloc(j, sizeof(struct NewsrcEntry));
251  mdata->subscribed = subs;
252 
253  /* parse entries */
254  j = 0;
255  while (p)
256  {
257  b = p;
258 
259  /* find end of entry */
260  p = strchr(p, ',');
261  if (p)
262  *p++ = '\0';
263 
264  /* first-last or single number */
265  h = strchr(b, '-');
266  if (h)
267  *h++ = '\0';
268  else
269  h = b;
270 
271  if ((sscanf(b, ANUM, &mdata->newsrc_ent[j].first) == 1) &&
272  (sscanf(h, ANUM, &mdata->newsrc_ent[j].last) == 1))
273  {
274  j++;
275  }
276  }
277  if (j == 0)
278  {
279  mdata->newsrc_ent[j].first = 1;
280  mdata->newsrc_ent[j].last = 0;
281  j++;
282  }
283  if (mdata->last_message == 0)
284  mdata->last_message = mdata->newsrc_ent[j - 1].last;
285  mdata->newsrc_len = j;
286  mutt_mem_realloc(&mdata->newsrc_ent, j * sizeof(struct NewsrcEntry));
288  mutt_debug(LL_DEBUG2, "%s\n", mdata->group);
289  }
290  FREE(&line);
291  return 1;
292 }
293 
299 {
300  if (!m)
301  return;
302 
303  struct NntpMboxData *mdata = m->mdata;
304  anum_t last = 0, first = 1;
305  bool series;
306  enum SortType save_sort = SORT_ORDER;
307  unsigned int entries;
308 
309  if (C_Sort != SORT_ORDER)
310  {
311  save_sort = C_Sort;
312  C_Sort = SORT_ORDER;
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 
353  /* search for first read */
354  else
355  {
356  if (e->deleted || e->read)
357  {
358  first = last + 1;
359  series = true;
360  }
361  last = nntp_edata_get(e)->article_num;
362  }
363  }
364 
365  if (series && (first <= mdata->last_loaded))
366  {
367  if (mdata->newsrc_len >= entries)
368  {
369  entries++;
370  mutt_mem_realloc(&mdata->newsrc_ent, entries * sizeof(struct NewsrcEntry));
371  }
372  mdata->newsrc_ent[mdata->newsrc_len].first = first;
373  mdata->newsrc_ent[mdata->newsrc_len].last = mdata->last_loaded;
374  mdata->newsrc_len++;
375  }
376  mutt_mem_realloc(&mdata->newsrc_ent, mdata->newsrc_len * sizeof(struct NewsrcEntry));
377 
378  if (save_sort != C_Sort)
379  {
380  C_Sort = save_sort;
382  }
383 }
384 
392 static int update_file(char *filename, char *buf)
393 {
394  FILE *fp = NULL;
395  char tmpfile[PATH_MAX];
396  int rc = -1;
397 
398  while (true)
399  {
400  snprintf(tmpfile, sizeof(tmpfile), "%s.tmp", filename);
401  fp = mutt_file_fopen(tmpfile, "w");
402  if (!fp)
403  {
404  mutt_perror(tmpfile);
405  *tmpfile = '\0';
406  break;
407  }
408  if (fputs(buf, fp) == EOF)
409  {
410  mutt_perror(tmpfile);
411  break;
412  }
413  if (mutt_file_fclose(&fp) == EOF)
414  {
415  mutt_perror(tmpfile);
416  fp = NULL;
417  break;
418  }
419  fp = NULL;
420  if (rename(tmpfile, filename) < 0)
421  {
422  mutt_perror(filename);
423  break;
424  }
425  *tmpfile = '\0';
426  rc = 0;
427  break;
428  }
429  mutt_file_fclose(&fp);
430 
431  if (*tmpfile)
432  unlink(tmpfile);
433  return rc;
434 }
435 
443 {
444  if (!adata)
445  return -1;
446 
447  int rc = -1;
448 
449  size_t buflen = 10240;
450  char *buf = mutt_mem_calloc(1, buflen);
451  size_t off = 0;
452 
453  /* we will generate full newsrc here */
454  for (unsigned int i = 0; i < adata->groups_num; i++)
455  {
456  struct NntpMboxData *mdata = adata->groups_list[i];
457 
458  if (!mdata || !mdata->newsrc_ent)
459  continue;
460 
461  /* write newsgroup name */
462  if (off + strlen(mdata->group) + 3 > buflen)
463  {
464  buflen *= 2;
465  mutt_mem_realloc(&buf, buflen);
466  }
467  snprintf(buf + off, buflen - off, "%s%c ", mdata->group, mdata->subscribed ? ':' : '!');
468  off += strlen(buf + off);
469 
470  /* write entries */
471  for (unsigned int j = 0; j < mdata->newsrc_len; j++)
472  {
473  if (off + 1024 > buflen)
474  {
475  buflen *= 2;
476  mutt_mem_realloc(&buf, buflen);
477  }
478  if (j)
479  buf[off++] = ',';
480  if (mdata->newsrc_ent[j].first == mdata->newsrc_ent[j].last)
481  snprintf(buf + off, buflen - off, "%u", mdata->newsrc_ent[j].first);
482  else if (mdata->newsrc_ent[j].first < mdata->newsrc_ent[j].last)
483  {
484  snprintf(buf + off, buflen - off, "%u-%u", mdata->newsrc_ent[j].first,
485  mdata->newsrc_ent[j].last);
486  }
487  off += strlen(buf + off);
488  }
489  buf[off++] = '\n';
490  }
491  buf[off] = '\0';
492 
493  /* newrc being fully rewritten */
494  mutt_debug(LL_DEBUG1, "Updating %s\n", adata->newsrc_file);
495  if (adata->newsrc_file && (update_file(adata->newsrc_file, buf) == 0))
496  {
497  struct stat sb;
498 
499  rc = stat(adata->newsrc_file, &sb);
500  if (rc == 0)
501  {
502  adata->size = sb.st_size;
503  adata->mtime = sb.st_mtime;
504  }
505  else
506  {
507  mutt_perror(adata->newsrc_file);
508  }
509  }
510  FREE(&buf);
511  return rc;
512 }
513 
521 static void cache_expand(char *dst, size_t dstlen, struct ConnAccount *cac, const char *src)
522 {
523  char *c = NULL;
524  char file[PATH_MAX];
525 
526  /* server subdirectory */
527  if (cac)
528  {
529  struct Url url = { 0 };
530 
531  mutt_account_tourl(cac, &url);
532  url.path = mutt_str_dup(src);
533  url_tostring(&url, file, sizeof(file), U_PATH);
534  FREE(&url.path);
535  }
536  else
537  mutt_str_copy(file, src ? src : "", sizeof(file));
538 
539  snprintf(dst, dstlen, "%s/%s", C_NewsCacheDir, file);
540 
541  /* remove trailing slash */
542  c = dst + strlen(dst) - 1;
543  if (*c == '/')
544  *c = '\0';
545 
546  struct Buffer *tmp = mutt_buffer_pool_get();
547  mutt_buffer_addstr(tmp, dst);
549  mutt_encode_path(tmp, dst);
550  mutt_str_copy(dst, mutt_buffer_string(tmp), dstlen);
552 }
553 
560 void nntp_expand_path(char *buf, size_t buflen, struct ConnAccount *cac)
561 {
562  struct Url url = { 0 };
563 
564  mutt_account_tourl(cac, &url);
565  url.path = mutt_str_dup(buf);
566  url_tostring(&url, buf, buflen, U_NO_FLAGS);
567  FREE(&url.path);
568 }
569 
576 int nntp_add_group(char *line, void *data)
577 {
578  struct NntpAccountData *adata = data;
579  struct NntpMboxData *mdata = NULL;
580  char group[1024] = { 0 };
581  char desc[8192] = { 0 };
582  char mod;
583  anum_t first, last;
584 
585  if (!adata || !line)
586  return 0;
587 
588  /* These sscanf limits must match the sizes of the group and desc arrays */
589  if (sscanf(line, "%1023s " ANUM " " ANUM " %c %8191[^\n]", group, &last,
590  &first, &mod, desc) < 4)
591  {
592  mutt_debug(LL_DEBUG2, "Can't parse server line: %s\n", line);
593  return 0;
594  }
595 
597  mdata->deleted = false;
598  mdata->first_message = first;
599  mdata->last_message = last;
600  mdata->allowed = (mod == 'y') || (mod == 'm');
601  mutt_str_replace(&mdata->desc, desc);
602  if (mdata->newsrc_ent || (mdata->last_cached != 0))
604  else if (mdata->last_message && (mdata->first_message <= mdata->last_message))
605  mdata->unread = mdata->last_message - mdata->first_message + 1;
606  else
607  mdata->unread = 0;
608  return 0;
609 }
610 
618 {
619  char buf[8192];
620  char file[4096];
621  time_t t;
622 
623  cache_expand(file, sizeof(file), &adata->conn->account, ".active");
624  mutt_debug(LL_DEBUG1, "Parsing %s\n", file);
625  FILE *fp = mutt_file_fopen(file, "r");
626  if (!fp)
627  return -1;
628 
629  if (!fgets(buf, sizeof(buf), fp) || (sscanf(buf, "%ld%4095s", &t, file) != 1) || (t == 0))
630  {
631  mutt_file_fclose(&fp);
632  return -1;
633  }
634  adata->newgroups_time = t;
635 
636  mutt_message(_("Loading list of groups from cache..."));
637  while (fgets(buf, sizeof(buf), fp))
638  nntp_add_group(buf, adata);
639  nntp_add_group(NULL, NULL);
640  mutt_file_fclose(&fp);
642  return 0;
643 }
644 
652 {
653  if (!adata->cacheable)
654  return 0;
655 
656  size_t buflen = 10240;
657  char *buf = mutt_mem_calloc(1, buflen);
658  snprintf(buf, buflen, "%lu\n", (unsigned long) adata->newgroups_time);
659  size_t off = strlen(buf);
660 
661  for (unsigned int i = 0; i < adata->groups_num; i++)
662  {
663  struct NntpMboxData *mdata = adata->groups_list[i];
664 
665  if (!mdata || mdata->deleted)
666  continue;
667 
668  if ((off + strlen(mdata->group) + (mdata->desc ? strlen(mdata->desc) : 0) + 50) > buflen)
669  {
670  buflen *= 2;
671  mutt_mem_realloc(&buf, buflen);
672  }
673  snprintf(buf + off, buflen - off, "%s %u %u %c%s%s\n", mdata->group,
674  mdata->last_message, mdata->first_message, mdata->allowed ? 'y' : 'n',
675  mdata->desc ? " " : "", mdata->desc ? mdata->desc : "");
676  off += strlen(buf + off);
677  }
678 
679  char file[PATH_MAX];
680  cache_expand(file, sizeof(file), &adata->conn->account, ".active");
681  mutt_debug(LL_DEBUG1, "Updating %s\n", file);
682  int rc = update_file(file, buf);
683  FREE(&buf);
684  return rc;
685 }
686 
687 #ifdef USE_HCACHE
688 
691 static void nntp_hcache_namer(const char *path, struct Buffer *dest)
692 {
693  mutt_buffer_printf(dest, "%s.hcache", path);
694 
695  /* Strip out any directories in the path */
696  char *first = strchr(mutt_buffer_string(dest), '/');
697  char *last = strrchr(mutt_buffer_string(dest), '/');
698  if (first && last && (last > first))
699  {
700  memmove(first, last, strlen(last) + 1);
701  }
702 }
703 
711 {
712  struct Url url = { 0 };
713  char file[PATH_MAX];
714 
715  if (!mdata->adata || !mdata->adata->cacheable || !mdata->adata->conn ||
716  !mdata->group || !(mdata->newsrc_ent || mdata->subscribed || C_SaveUnsubscribed))
717  {
718  return NULL;
719  }
720 
721  mutt_account_tourl(&mdata->adata->conn->account, &url);
722  url.path = mdata->group;
723  url_tostring(&url, file, sizeof(file), U_PATH);
725 }
726 
732 void nntp_hcache_update(struct NntpMboxData *mdata, struct HeaderCache *hc)
733 {
734  if (!hc)
735  return;
736 
737  char buf[32];
738  bool old = false;
739  anum_t first = 0, last = 0;
740 
741  /* fetch previous values of first and last */
742  size_t dlen = 0;
743  void *hdata = mutt_hcache_fetch_raw(hc, "index", 5, &dlen);
744  if (hdata)
745  {
746  mutt_debug(LL_DEBUG2, "mutt_hcache_fetch index: %s\n", (char *) hdata);
747  if (sscanf(hdata, ANUM " " ANUM, &first, &last) == 2)
748  {
749  old = true;
750  mdata->last_cached = last;
751 
752  /* clean removed headers from cache */
753  for (anum_t current = first; current <= last; current++)
754  {
755  if ((current >= mdata->first_message) && (current <= mdata->last_message))
756  continue;
757 
758  snprintf(buf, sizeof(buf), "%u", current);
759  mutt_debug(LL_DEBUG2, "mutt_hcache_delete_record %s\n", buf);
760  mutt_hcache_delete_record(hc, buf, strlen(buf));
761  }
762  }
763  mutt_hcache_free_raw(hc, &hdata);
764  }
765 
766  /* store current values of first and last */
767  if (!old || (mdata->first_message != first) || (mdata->last_message != last))
768  {
769  snprintf(buf, sizeof(buf), "%u %u", mdata->first_message, mdata->last_message);
770  mutt_debug(LL_DEBUG2, "mutt_hcache_store index: %s\n", buf);
771  mutt_hcache_store_raw(hc, "index", 5, buf, strlen(buf) + 1);
772  }
773 }
774 #endif
775 
780 static int nntp_bcache_delete(const char *id, struct BodyCache *bcache, void *data)
781 {
782  struct NntpMboxData *mdata = data;
783  anum_t anum;
784  char c;
785 
786  if (!mdata || (sscanf(id, ANUM "%c", &anum, &c) != 1) ||
787  (anum < mdata->first_message) || (anum > mdata->last_message))
788  {
789  if (mdata)
790  mutt_debug(LL_DEBUG2, "mutt_bcache_del %s\n", id);
791  mutt_bcache_del(bcache, id);
792  }
793  return 0;
794 }
795 
801 {
803 }
804 
810 {
811  if (!mdata || !mdata->adata || !mdata->adata->cacheable)
812  return;
813 
814 #ifdef USE_HCACHE
815  struct Buffer file = mutt_buffer_make(PATH_MAX);
816  nntp_hcache_namer(mdata->group, &file);
817  cache_expand(file.data, file.dsize, &mdata->adata->conn->account,
818  mutt_buffer_string(&file));
819  unlink(mutt_buffer_string(&file));
820  mdata->last_cached = 0;
821  mutt_debug(LL_DEBUG2, "%s\n", mutt_buffer_string(&file));
822  mutt_buffer_dealloc(&file);
823 #endif
824 
825  if (!mdata->bcache)
826  {
827  mdata->bcache = mutt_bcache_open(&mdata->adata->conn->account, mdata->group);
828  }
829  if (mdata->bcache)
830  {
831  mutt_debug(LL_DEBUG2, "%s/*\n", mdata->group);
833  mutt_bcache_close(&mdata->bcache);
834  }
835 }
836 
844 {
845  char file[PATH_MAX];
846  char *fp = NULL;
847  struct dirent *entry = NULL;
848  DIR *dp = NULL;
849 
850  if (!adata || !adata->cacheable)
851  return;
852 
853  cache_expand(file, sizeof(file), &adata->conn->account, NULL);
854  dp = opendir(file);
855  if (dp)
856  {
857  mutt_strn_cat(file, sizeof(file), "/", 1);
858  fp = file + strlen(file);
859  while ((entry = readdir(dp)))
860  {
861  char *group = entry->d_name;
862  struct stat sb;
863  struct NntpMboxData *mdata = NULL;
864  struct NntpMboxData tmp_mdata;
865 
866  if (mutt_str_equal(group, ".") || mutt_str_equal(group, ".."))
867  continue;
868  *fp = '\0';
869  mutt_strn_cat(file, sizeof(file), group, strlen(group));
870  if (stat(file, &sb) != 0)
871  continue;
872 
873 #ifdef USE_HCACHE
874  if (S_ISREG(sb.st_mode))
875  {
876  char *ext = group + strlen(group) - 7;
877  if ((strlen(group) < 8) || !mutt_str_equal(ext, ".hcache"))
878  continue;
879  *ext = '\0';
880  }
881  else
882 #endif
883  if (!S_ISDIR(sb.st_mode))
884  continue;
885 
887  if (!mdata)
888  {
889  mdata = &tmp_mdata;
890  mdata->adata = adata;
891  mdata->group = group;
892  mdata->bcache = NULL;
893  }
894  else if (mdata->newsrc_ent || mdata->subscribed || C_SaveUnsubscribed)
895  continue;
896 
898  if (S_ISDIR(sb.st_mode))
899  {
900  rmdir(file);
901  mutt_debug(LL_DEBUG2, "%s\n", file);
902  }
903  }
904  closedir(dp);
905  }
906 }
907 
920 const char *nntp_format_str(char *buf, size_t buflen, size_t col, int cols, char op,
921  const char *src, const char *prec, const char *if_str,
922  const char *else_str, intptr_t data, MuttFormatFlags flags)
923 {
924  struct NntpAccountData *adata = (struct NntpAccountData *) data;
925  struct ConnAccount *cac = &adata->conn->account;
926  char fn[128], fmt[128];
927 
928  switch (op)
929  {
930  case 'a':
931  {
932  struct Url url = { 0 };
933  mutt_account_tourl(cac, &url);
934  url_tostring(&url, fn, sizeof(fn), U_PATH);
935  char *p = strchr(fn, '/');
936  if (p)
937  *p = '\0';
938  snprintf(fmt, sizeof(fmt), "%%%ss", prec);
939  snprintf(buf, buflen, fmt, fn);
940  break;
941  }
942  case 'p':
943  snprintf(fmt, sizeof(fmt), "%%%su", prec);
944  snprintf(buf, buflen, fmt, cac->port);
945  break;
946  case 'P':
947  *buf = '\0';
948  if (cac->flags & MUTT_ACCT_PORT)
949  {
950  snprintf(fmt, sizeof(fmt), "%%%su", prec);
951  snprintf(buf, buflen, fmt, cac->port);
952  }
953  break;
954  case 's':
955  mutt_str_copy(fn, cac->host, sizeof(fn));
956  mutt_str_lower(fn);
957  snprintf(fmt, sizeof(fmt), "%%%ss", prec);
958  snprintf(buf, buflen, fmt, fn);
959  break;
960  case 'S':
961  {
962  struct Url url = { 0 };
963  mutt_account_tourl(cac, &url);
964  url_tostring(&url, fn, sizeof(fn), U_PATH);
965  char *p = strchr(fn, ':');
966  if (p)
967  *p = '\0';
968  snprintf(fmt, sizeof(fmt), "%%%ss", prec);
969  snprintf(buf, buflen, fmt, fn);
970  break;
971  }
972  case 'u':
973  snprintf(fmt, sizeof(fmt), "%%%ss", prec);
974  snprintf(buf, buflen, fmt, cac->user);
975  break;
976  }
977  return src;
978 }
979 
983 static const char *nntp_get_field(enum ConnAccountField field, void *gf_data)
984 {
985  switch (field)
986  {
987  case MUTT_CA_LOGIN:
988  case MUTT_CA_USER:
989  return C_NntpUser;
990  case MUTT_CA_PASS:
991  return C_NntpPass;
992  case MUTT_CA_OAUTH_CMD:
993  case MUTT_CA_HOST:
994  default:
995  return NULL;
996  }
997 }
998 
1012 struct NntpAccountData *nntp_select_server(struct Mailbox *m, const char *server, bool leave_lock)
1013 {
1014  char file[PATH_MAX];
1015  int rc;
1016  struct ConnAccount cac = { { 0 } };
1017  struct NntpAccountData *adata = NULL;
1018  struct Connection *conn = NULL;
1019 
1020  if (!server || (*server == '\0'))
1021  {
1022  mutt_error(_("No news server defined"));
1023  return NULL;
1024  }
1025 
1026  /* create account from news server url */
1027  cac.flags = 0;
1028  cac.port = NNTP_PORT;
1029  cac.type = MUTT_ACCT_TYPE_NNTP;
1030  cac.service = "nntp";
1031  cac.get_field = nntp_get_field;
1032 
1033  snprintf(file, sizeof(file), "%s%s", strstr(server, "://") ? "" : "news://", server);
1034  struct Url *url = url_parse(file);
1035  if (!url || (url->path && *url->path) ||
1036  !((url->scheme == U_NNTP) || (url->scheme == U_NNTPS)) || !url->host ||
1037  (mutt_account_fromurl(&cac, url) < 0))
1038  {
1039  url_free(&url);
1040  mutt_error(_("%s is an invalid news server specification"), server);
1041  return NULL;
1042  }
1043  if (url->scheme == U_NNTPS)
1044  {
1045  cac.flags |= MUTT_ACCT_SSL;
1046  cac.port = NNTP_SSL_PORT;
1047  }
1048  url_free(&url);
1049 
1050  /* find connection by account */
1051  conn = mutt_conn_find(&cac);
1052  if (!conn)
1053  return NULL;
1054  if (!(conn->account.flags & MUTT_ACCT_USER) && cac.flags & MUTT_ACCT_USER)
1055  {
1056  conn->account.flags |= MUTT_ACCT_USER;
1057  conn->account.user[0] = '\0';
1058  }
1059 
1060  /* news server already exists */
1061  // adata = conn->data;
1062  if (adata)
1063  {
1064  if (adata->status == NNTP_BYE)
1065  adata->status = NNTP_NONE;
1066  if (nntp_open_connection(adata) < 0)
1067  return NULL;
1068 
1069  rc = nntp_newsrc_parse(adata);
1070  if (rc < 0)
1071  return NULL;
1072 
1073  /* check for new newsgroups */
1074  if (!leave_lock && (nntp_check_new_groups(m, adata) < 0))
1075  rc = -1;
1076 
1077  /* .newsrc has been externally modified */
1078  if (rc > 0)
1079  nntp_clear_cache(adata);
1080  if ((rc < 0) || !leave_lock)
1081  nntp_newsrc_close(adata);
1082  return (rc < 0) ? NULL : adata;
1083  }
1084 
1085  /* new news server */
1086  adata = nntp_adata_new(conn);
1087 
1088  rc = nntp_open_connection(adata);
1089 
1090  /* try to create cache directory and enable caching */
1091  adata->cacheable = false;
1092  if ((rc >= 0) && C_NewsCacheDir)
1093  {
1094  cache_expand(file, sizeof(file), &conn->account, NULL);
1095  if (mutt_file_mkdir(file, S_IRWXU) < 0)
1096  {
1097  mutt_error(_("Can't create %s: %s"), file, strerror(errno));
1098  }
1099  adata->cacheable = true;
1100  }
1101 
1102  /* load .newsrc */
1103  if (rc >= 0)
1104  {
1105  mutt_expando_format(file, sizeof(file), 0, sizeof(file), NONULL(C_Newsrc),
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  rc = nntp_check_new_groups(m, adata);
1116 
1117  /* load list of newsgroups from server */
1118  else
1119  rc = nntp_active_fetch(adata, false);
1120  }
1121 
1122  if (rc >= 0)
1123  nntp_clear_cache(adata);
1124 
1125 #ifdef USE_HCACHE
1126  /* check cache files */
1127  if ((rc >= 0) && adata->cacheable)
1128  {
1129  struct dirent *entry = NULL;
1130  DIR *dp = opendir(file);
1131 
1132  if (dp)
1133  {
1134  while ((entry = readdir(dp)))
1135  {
1136  struct HeaderCache *hc = NULL;
1137  void *hdata = NULL;
1138  char *group = entry->d_name;
1139 
1140  char *p = group + strlen(group) - 7;
1141  if ((strlen(group) < 8) || (strcmp(p, ".hcache") != 0))
1142  continue;
1143  *p = '\0';
1145  if (!mdata)
1146  continue;
1147 
1148  hc = nntp_hcache_open(mdata);
1149  if (!hc)
1150  continue;
1151 
1152  /* fetch previous values of first and last */
1153  size_t dlen = 0;
1154  hdata = mutt_hcache_fetch_raw(hc, "index", 5, &dlen);
1155  if (hdata)
1156  {
1157  anum_t first, last;
1158 
1159  if (sscanf(hdata, ANUM " " ANUM, &first, &last) == 2)
1160  {
1161  if (mdata->deleted)
1162  {
1163  mdata->first_message = first;
1164  mdata->last_message = last;
1165  }
1166  if ((last >= mdata->first_message) && (last <= mdata->last_message))
1167  {
1168  mdata->last_cached = last;
1169  mutt_debug(LL_DEBUG2, "%s last_cached=%u\n", mdata->group, last);
1170  }
1171  }
1172  mutt_hcache_free_raw(hc, &hdata);
1173  }
1174  mutt_hcache_close(hc);
1175  }
1176  closedir(dp);
1177  }
1178  }
1179 #endif
1180 
1181  if ((rc < 0) || !leave_lock)
1183 
1184  if (rc < 0)
1185  {
1187  FREE(&adata->groups_list);
1188  FREE(&adata->newsrc_file);
1190  FREE(&adata);
1191  mutt_socket_close(conn);
1192  FREE(&conn);
1193  return NULL;
1194  }
1195 
1196  return adata;
1197 }
1198 
1211 void nntp_article_status(struct Mailbox *m, struct Email *e, char *group, anum_t anum)
1212 {
1213  struct NntpMboxData *mdata = m->mdata;
1214 
1215  if (group)
1216  mdata = mutt_hash_find(mdata->adata->groups_hash, group);
1217 
1218  if (!mdata)
1219  return;
1220 
1221  for (unsigned int i = 0; i < mdata->newsrc_len; i++)
1222  {
1223  if ((anum >= mdata->newsrc_ent[i].first) && (anum <= mdata->newsrc_ent[i].last))
1224  {
1225  /* can't use mutt_set_flag() because ctx_update() didn't get called yet */
1226  e->read = true;
1227  return;
1228  }
1229  }
1230 
1231  /* article was not cached yet, it's new */
1232  if (anum > mdata->last_cached)
1233  return;
1234 
1235  /* article isn't read but cached, it's old */
1236  if (C_MarkOld)
1237  e->old = true;
1238 }
1239 
1248 {
1249  if (!adata || !adata->groups_hash || !group || (*group == '\0'))
1250  return NULL;
1251 
1252  struct NntpMboxData *mdata = mdata_find(adata, group);
1253  mdata->subscribed = true;
1254  if (!mdata->newsrc_ent)
1255  {
1256  mdata->newsrc_ent = mutt_mem_calloc(1, sizeof(struct NewsrcEntry));
1257  mdata->newsrc_len = 1;
1258  mdata->newsrc_ent[0].first = 1;
1259  mdata->newsrc_ent[0].last = 0;
1260  }
1261  return mdata;
1262 }
1263 
1272 {
1273  if (!adata || !adata->groups_hash || !group || (*group == '\0'))
1274  return NULL;
1275 
1277  if (!mdata)
1278  return NULL;
1279 
1280  mdata->subscribed = false;
1281  if (!C_SaveUnsubscribed)
1282  {
1283  mdata->newsrc_len = 0;
1284  FREE(&mdata->newsrc_ent);
1285  }
1286  return mdata;
1287 }
1288 
1298  struct NntpAccountData *adata, char *group)
1299 {
1300  if (!adata || !adata->groups_hash || !group || (*group == '\0'))
1301  return NULL;
1302 
1304  if (!mdata)
1305  return NULL;
1306 
1307  if (mdata->newsrc_ent)
1308  {
1309  mutt_mem_realloc(&mdata->newsrc_ent, sizeof(struct NewsrcEntry));
1310  mdata->newsrc_len = 1;
1311  mdata->newsrc_ent[0].first = 1;
1312  mdata->newsrc_ent[0].last = mdata->last_message;
1313  }
1314  mdata->unread = 0;
1315  if (m && (m->mdata == mdata))
1316  {
1317  for (unsigned int i = 0; i < m->msg_count; i++)
1318  {
1319  struct Email *e = m->emails[i];
1320  if (!e)
1321  break;
1322  mutt_set_flag(m, e, MUTT_READ, true);
1323  }
1324  }
1325  return mdata;
1326 }
1327 
1337  struct NntpAccountData *adata, char *group)
1338 {
1339  if (!adata || !adata->groups_hash || !group || (*group == '\0'))
1340  return NULL;
1341 
1343  if (!mdata)
1344  return NULL;
1345 
1346  if (mdata->newsrc_ent)
1347  {
1348  mutt_mem_realloc(&mdata->newsrc_ent, sizeof(struct NewsrcEntry));
1349  mdata->newsrc_len = 1;
1350  mdata->newsrc_ent[0].first = 1;
1351  mdata->newsrc_ent[0].last = mdata->first_message - 1;
1352  }
1353  if (m && (m->mdata == mdata))
1354  {
1355  mdata->unread = m->msg_count;
1356  for (unsigned int i = 0; i < m->msg_count; i++)
1357  {
1358  struct Email *e = m->emails[i];
1359  if (!e)
1360  break;
1361  mutt_set_flag(m, e, MUTT_READ, false);
1362  }
1363  }
1364  else
1365  {
1366  mdata->unread = mdata->last_message;
1367  if (mdata->newsrc_ent)
1368  mdata->unread -= mdata->newsrc_ent[0].last;
1369  }
1370  return mdata;
1371 }
1372 
1379 void nntp_mailbox(struct Mailbox *m, char *buf, size_t buflen)
1380 {
1381  if (!m)
1382  return;
1383 
1384  for (unsigned int i = 0; i < CurrentNewsSrv->groups_num; i++)
1385  {
1387 
1388  if (!mdata || !mdata->subscribed || !mdata->unread)
1389  continue;
1390 
1391  if ((m->type == MUTT_NNTP) &&
1392  mutt_str_equal(mdata->group, ((struct NntpMboxData *) m->mdata)->group))
1393  {
1394  unsigned int unread = 0;
1395 
1396  for (unsigned int j = 0; j < m->msg_count; j++)
1397  {
1398  struct Email *e = m->emails[j];
1399  if (!e)
1400  break;
1401  if (!e->read && !e->deleted)
1402  unread++;
1403  }
1404  if (unread == 0)
1405  continue;
1406  }
1407  mutt_str_copy(buf, mdata->group, buflen);
1408  break;
1409  }
1410 }
NntpAccountData::cacheable
bool cacheable
Definition: adata.h:46
Url::src
char * src
Raw URL string.
Definition: url.h:76
nntp_check_new_groups
int nntp_check_new_groups(struct Mailbox *m, struct NntpAccountData *adata)
Check for new groups/articles in subscribed groups.
Definition: nntp.c:2015
mutt_encode_path
void mutt_encode_path(struct Buffer *buf, const char *src)
Convert a path to 'us-ascii'.
Definition: muttlib.c:1476
IP
#define IP
Definition: set.h:54
NntpMboxData::unread
anum_t unread
Definition: mdata.h:39
NntpAccountData::size
off_t size
Definition: adata.h:52
NntpMboxData::group
char * group
Definition: mdata.h:33
mutt_newsgroup_unsubscribe
struct NntpMboxData * mutt_newsgroup_unsubscribe(struct NntpAccountData *adata, char *group)
Unsubscribe newsgroup.
Definition: newsrc.c:1271
nntp_bcache_update
void nntp_bcache_update(struct NntpMboxData *mdata)
Remove stale cached messages.
Definition: newsrc.c:800
mutt_file_lock
int mutt_file_lock(int fd, bool excl, bool timeout)
(try to) lock a file using fcntl()
Definition: file.c:1175
ConnAccount
Login details for a remote server.
Definition: connaccount.h:51
mutt_hcache_open
struct HeaderCache * mutt_hcache_open(const char *path, const char *folder, hcache_namer_t namer)
Multiplexor for StoreOps::open.
Definition: hcache.c:322
Connection
An open network connection (socket)
Definition: connection.h:34
mutt_bcache_open
struct BodyCache * mutt_bcache_open(struct ConnAccount *account, const char *mailbox)
Open an Email-Body Cache.
Definition: bcache.c:144
ConnAccount::host
char host[128]
Server to login to.
Definition: connaccount.h:53
mdata_find
static struct NntpMboxData * mdata_find(struct NntpAccountData *adata, const char *group)
Find NntpMboxData for given newsgroup or add it.
Definition: newsrc.c:74
mutt_mem_calloc
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
mutt_account_fromurl
int mutt_account_fromurl(struct ConnAccount *cac, const struct Url *url)
Fill ConnAccount with information from url.
Definition: mutt_account.c:43
NntpAccountData::status
unsigned int status
Definition: adata.h:45
_
#define _(a)
Definition: message.h:28
NONULL
#define NONULL(x)
Definition: string2.h:37
Mailbox
A mailbox.
Definition: mailbox.h:81
NntpAccountData::authenticators
char * authenticators
Definition: adata.h:50
nntp_add_group
int nntp_add_group(char *line, void *data)
Parse newsgroup.
Definition: newsrc.c:576
NntpAccountData::groups_max
unsigned int groups_max
Definition: adata.h:57
U_NNTPS
@ U_NNTPS
Url is nntps://.
Definition: url.h:41
Mailbox::emails
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
mutt_hcache_store_raw
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:584
nntp_select_server
struct NntpAccountData * nntp_select_server(struct Mailbox *m, const char *server, bool leave_lock)
Open a connection to an NNTP server.
Definition: newsrc.c:1012
nntp_hcache_update
void nntp_hcache_update(struct NntpMboxData *mdata, struct HeaderCache *hc)
Remove stale cached headers.
Definition: newsrc.c:732
MUTT_ACCT_PORT
#define MUTT_ACCT_PORT
Port field has been set.
Definition: connaccount.h:42
Buffer
String manipulation buffer.
Definition: buffer.h:33
NntpAccountData::groups_num
unsigned int groups_num
Definition: adata.h:56
nntp_open_connection
int nntp_open_connection(struct NntpAccountData *adata)
Connect to server, authenticate and get capabilities.
Definition: nntp.c:1702
edata.h
Connection::account
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:36
ConnAccount::get_field
const char *(* get_field)(enum ConnAccountField field, void *gf_data)
Function to get some login credentials.
Definition: connaccount.h:67
mutt_file_fclose
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
mutt_buffer_dealloc
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:294
format_flags.h
BodyCache
Local cache of email bodies.
Definition: bcache.c:53
cache_expand
static void cache_expand(char *dst, size_t dstlen, struct ConnAccount *cac, const char *src)
Make fully qualified cache file name.
Definition: newsrc.c:521
nntp_edata_get
struct NntpEmailData * nntp_edata_get(struct Email *e)
Get the private data for this Email.
Definition: edata.c:57
mutt_account_tourl
void mutt_account_tourl(struct ConnAccount *cac, struct Url *url)
Fill URL with info from account.
Definition: mutt_account.c:79
NntpMboxData::bcache
struct BodyCache * bcache
Definition: mdata.h:48
ANUM
#define ANUM
Definition: lib.h:73
U_NO_FLAGS
#define U_NO_FLAGS
Definition: url.h:48
mutt_account.h
SortType
SortType
Methods for sorting.
Definition: sort2.h:43
update_file
static int update_file(char *filename, char *buf)
Update file with new contents.
Definition: newsrc.c:392
nntp_format_str
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:920
mutt_socket_close
int mutt_socket_close(struct Connection *conn)
Close a socket.
Definition: socket.c:96
Url::scheme
enum UrlScheme scheme
Scheme, e.g. U_SMTPS.
Definition: url.h:69
nntp_active_fetch
int nntp_active_fetch(struct NntpAccountData *adata, bool mark_new)
Fetch list of all newsgroups from server.
Definition: nntp.c:1948
lib.h
nntp_expand_path
void nntp_expand_path(char *buf, size_t buflen, struct ConnAccount *cac)
Make fully qualified url from newsgroup name.
Definition: newsrc.c:560
NntpMboxData::desc
char * desc
Definition: mdata.h:34
mutt_str_dup
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
mutt_globals.h
nntp_adata_new
struct NntpAccountData * nntp_adata_new(struct Connection *conn)
Allocate and initialise a new NntpAccountData structure.
Definition: adata.c:63
mutt_file_fopen
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:589
LL_DEBUG1
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
Mailbox::flags
uint8_t flags
e.g. MB_NORMAL
Definition: mailbox.h:134
NntpAccountData::groups_list
void ** groups_list
Definition: adata.h:58
FREE
#define FREE(x)
Definition: memory.h:40
mutt_perror
#define mutt_perror(...)
Definition: logging.h:85
Buffer::dsize
size_t dsize
Length of data.
Definition: buffer.h:37
NntpMboxData::first_message
anum_t first_message
Definition: mdata.h:35
PATH_MAX
#define PATH_MAX
Definition: mutt.h:44
mutt_newsgroup_subscribe
struct NntpMboxData * mutt_newsgroup_subscribe(struct NntpAccountData *adata, char *group)
Subscribe newsgroup.
Definition: newsrc.c:1247
mutt_buffer_pool_release
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
MUTT_FORMAT_NO_FLAGS
#define MUTT_FORMAT_NO_FLAGS
No flags are set.
Definition: format_flags.h:30
lib.h
url_parse
struct Url * url_parse(const char *src)
Fill in Url.
Definition: url.c:234
NntpAccountData::fp_newsrc
FILE * fp_newsrc
Definition: adata.h:48
NntpAccountData::conn
struct Connection * conn
Definition: adata.h:60
NntpMboxData::adata
struct NntpAccountData * adata
Definition: mdata.h:46
MUTT_READ
@ MUTT_READ
Messages that have been read.
Definition: mutt.h:96
mutt_hash_find
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
mutt_str_equal
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:871
mutt_strn_cat
char * mutt_strn_cat(char *d, size_t l, const char *s, size_t sl)
Concatenate two strings.
Definition: string.c:414
lib.h
NewsrcEntry::last
anum_t last
Definition: lib.h:90
active_get_cache
static int active_get_cache(struct NntpAccountData *adata)
Load list of all newsgroups from cache.
Definition: newsrc.c:617
C_NntpUser
char * C_NntpUser
Config: (nntp) Username for the news server.
Definition: config.c:50
Email::old
bool old
Email is seen, but unread.
Definition: email.h:50
mutt_socket.h
protos.h
NntpAccountData::newsrc_file
char * newsrc_file
Definition: adata.h:49
NNTP_NONE
@ NNTP_NONE
No connection to server.
Definition: private.h:42
mutt_expand_path
char * mutt_expand_path(char *buf, size_t buflen)
Create the canonical path.
Definition: muttlib.c:128
mutt_buffer_pool_get
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
MUTT_NNTP
@ MUTT_NNTP
'NNTP' (Usenet) Mailbox type
Definition: mailbox.h:52
Mailbox::type
enum MailboxType type
Mailbox type.
Definition: mailbox.h:105
Mailbox::mdata
void * mdata
Driver specific data.
Definition: mailbox.h:136
url_tostring
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
U_PATH
#define U_PATH
Definition: url.h:49
MUTT_CA_PASS
@ MUTT_CA_PASS
Password.
Definition: connaccount.h:36
ConnAccountField
ConnAccountField
Login credentials.
Definition: connaccount.h:31
nntp_delete_group_cache
void nntp_delete_group_cache(struct NntpMboxData *mdata)
Remove hcache and bcache of newsgroup.
Definition: newsrc.c:809
lib.h
muttlib.h
mutt_file_unlock
int mutt_file_unlock(int fd)
Unlock a file previously locked by mutt_file_lock()
Definition: file.c:1223
nntp_mailbox
void nntp_mailbox(struct Mailbox *m, char *buf, size_t buflen)
Get first newsgroup with new messages.
Definition: newsrc.c:1379
MUTT_CA_HOST
@ MUTT_CA_HOST
Server name.
Definition: connaccount.h:33
Url
A parsed URL proto://user:password@host:port/path?a=1&b=2
Definition: url.h:67
NntpMboxData::newsrc_len
unsigned int newsrc_len
Definition: mdata.h:44
lib.h
url_free
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition: url.c:123
Mailbox::msg_count
int msg_count
Total number of messages.
Definition: mailbox.h:91
nntp_bcache_delete
static int nntp_bcache_delete(const char *id, struct BodyCache *bcache, void *data)
Remove bcache file - Implements bcache_list_t.
Definition: newsrc.c:780
mutt_clear_error
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:113
NntpMboxData::last_message
anum_t last_message
Definition: mdata.h:36
mutt_hash_insert
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
mdata.h
nntp_newsrc_parse
int nntp_newsrc_parse(struct NntpAccountData *adata)
Parse .newsrc file.
Definition: newsrc.c:164
mutt_newsgroup_catchup
struct NntpMboxData * mutt_newsgroup_catchup(struct Mailbox *m, struct NntpAccountData *adata, char *group)
Catchup newsgroup.
Definition: newsrc.c:1297
mutt_hcache_free_raw
void mutt_hcache_free_raw(struct HeaderCache *hc, void **data)
Multiplexor for StoreOps::free.
Definition: hcache.c:514
C_SaveUnsubscribed
bool C_SaveUnsubscribed
Config: (nntp) Save a list of unsubscribed newsgroups to the 'newsrc'.
Definition: config.c:52
NntpMboxData::newsrc_ent
struct NewsrcEntry * newsrc_ent
Definition: mdata.h:45
NntpAccountData::mtime
time_t mtime
Definition: adata.h:53
mutt_mem_realloc
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
ConnAccount::type
unsigned char type
Connection type, e.g. MUTT_ACCT_TYPE_IMAP.
Definition: connaccount.h:58
mutt_debug
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
mutt_bcache_close
void mutt_bcache_close(struct BodyCache **bcache)
Close an Email-Body Cache.
Definition: bcache.c:165
anum_t
#define anum_t
Definition: lib.h:72
mutt_bcache_list
int mutt_bcache_list(struct BodyCache *bcache, bcache_list_t want_id, void *data)
Find matching entries in the Body Cache.
Definition: bcache.c:330
C_NntpPass
char * C_NntpPass
Config: (nntp) Password for the news server.
Definition: config.c:48
nntp_acache_free
void nntp_acache_free(struct NntpMboxData *mdata)
Remove all temporarily cache files.
Definition: newsrc.c:104
mutt_buffer_expand_path
void mutt_buffer_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:323
mutt_newsgroup_uncatchup
struct NntpMboxData * mutt_newsgroup_uncatchup(struct Mailbox *m, struct NntpAccountData *adata, char *group)
Uncatchup newsgroup.
Definition: newsrc.c:1336
NewsrcEntry
An entry in a .newsrc (subscribed newsgroups)
Definition: lib.h:87
MUTT_ACCT_USER
#define MUTT_ACCT_USER
User field has been set.
Definition: connaccount.h:43
mutt_buffer_string
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
CurrentNewsSrv
struct NntpAccountData * CurrentNewsSrv
Current NNTP news server.
Definition: nntp.c:79
ConnAccount::flags
MuttAccountFlags flags
Which fields are initialised, e.g. MUTT_ACCT_USER.
Definition: connaccount.h:59
NntpMboxData::last_loaded
anum_t last_loaded
Definition: mdata.h:37
mutt_str_lower
char * mutt_str_lower(char *str)
Convert all characters in the string to lowercase.
Definition: string.c:504
SORT_ORDER
@ SORT_ORDER
Sort by the order the messages appear in the mailbox.
Definition: sort2.h:50
nntp_group_unread_stat
void nntp_group_unread_stat(struct NntpMboxData *mdata)
Count number of unread articles using .newsrc data.
Definition: newsrc.c:134
mutt_str_replace
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:446
nntp_newsrc_gen_entries
void nntp_newsrc_gen_entries(struct Mailbox *m)
Generate array of .newsrc entries.
Definition: newsrc.c:298
mutt_logging.h
NNTP_BYE
@ NNTP_BYE
Disconnected from server.
Definition: private.h:44
lib.h
NntpMboxData::subscribed
bool subscribed
Definition: mdata.h:40
Email::deleted
bool deleted
Email is deleted.
Definition: email.h:45
mutt_file_mkdir
int mutt_file_mkdir(const char *path, mode_t mode)
Recursively create directories.
Definition: file.c:875
nntp_get_field
static const char * nntp_get_field(enum ConnAccountField field, void *gf_data)
Get connection login credentials - Implements ConnAccount::get_field()
Definition: newsrc.c:983
mutt_bcache_del
int mutt_bcache_del(struct BodyCache *bcache, const char *id)
Delete a file from the Body Cache.
Definition: bcache.c:265
MUTT_CA_LOGIN
@ MUTT_CA_LOGIN
Login name.
Definition: connaccount.h:34
MUTT_ACCT_SSL
#define MUTT_ACCT_SSL
Account uses SSL/TLS.
Definition: connaccount.h:46
nntp_article_status
void nntp_article_status(struct Mailbox *m, struct Email *e, char *group, anum_t anum)
Get status of articles from .newsrc.
Definition: newsrc.c:1211
C_Newsrc
char * C_Newsrc
Config: (nntp) File containing list of subscribed newsgroups.
Definition: config.c:43
NntpAccountData::newsrc_modified
bool newsrc_modified
Definition: adata.h:47
HeaderCache
header cache structure
Definition: lib.h:85
NewsrcEntry::first
anum_t first
Definition: lib.h:89
NntpAccountData
NNTP-specific Account data -.
Definition: adata.h:33
nntp_newsrc_update
int nntp_newsrc_update(struct NntpAccountData *adata)
Update .newsrc file.
Definition: newsrc.c:442
ConnAccount::port
unsigned short port
Port to connect to.
Definition: connaccount.h:57
C_Sort
WHERE short C_Sort
Config: Sort method for the index.
Definition: sort.h:60
sort.h
mutt_expando_format
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:774
adata.h
mutt_mem_malloc
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
private.h
nntp_hcache_open
struct HeaderCache * nntp_hcache_open(struct NntpMboxData *mdata)
Open newsgroup hcache.
Definition: newsrc.c:710
nntp_clear_cache
void nntp_clear_cache(struct NntpAccountData *adata)
Clear the NNTP cache.
Definition: newsrc.c:843
mutt.h
C_NewsCacheDir
char * C_NewsCacheDir
Config: (nntp) Directory for cached news articles.
Definition: config.c:40
mutt_hcache_delete_record
int mutt_hcache_delete_record(struct HeaderCache *hc, const char *key, size_t keylen)
Multiplexor for StoreOps::delete_record.
Definition: hcache.c:604
U_NNTP
@ U_NNTP
Url is nntp://.
Definition: url.h:40
Url::host
char * host
Host.
Definition: url.h:72
mailbox_changed
void mailbox_changed(struct Mailbox *m, enum NotifyMailbox action)
Notify observers of a change to a Mailbox.
Definition: mailbox.c:186
Account::adata
void * adata
Private data (for Mailbox backends)
Definition: account.h:43
NntpMboxData::last_cached
anum_t last_cached
Definition: mdata.h:38
C_MarkOld
bool C_MarkOld
Config: Mark new emails as old when leaving the mailbox.
Definition: globals.c:36
NntpEmailData::article_num
anum_t article_num
Definition: edata.h:33
mutt_buffer_addstr
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
ConnAccount::service
const char * service
Name of the service, e.g. "imap".
Definition: connaccount.h:60
MUTT_ACCT_TYPE_NNTP
@ MUTT_ACCT_TYPE_NNTP
Nntp (Usenet) Account.
Definition: mutt_account.h:40
Url::path
char * path
Path.
Definition: url.h:74
mutt_hcache_fetch_raw
void * mutt_hcache_fetch_raw(struct HeaderCache *hc, const char *key, size_t keylen, size_t *dlen)
Fetch a message's header from the cache.
Definition: hcache.c:496
lib.h
mutt_hcache_close
void mutt_hcache_close(struct HeaderCache *hc)
Multiplexor for StoreOps::close.
Definition: hcache.c:419
MUTT_CA_USER
@ MUTT_CA_USER
User name.
Definition: connaccount.h:35
Buffer::data
char * data
Pointer to data.
Definition: buffer.h:35
Email
The envelope/body of an email.
Definition: email.h:37
mutt_message
#define mutt_message(...)
Definition: logging.h:83
mutt_set_flag
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:67
NNTP_ACACHE_LEN
#define NNTP_ACACHE_LEN
Definition: lib.h:94
mutt_buffer_printf
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
mutt_conn_find
struct Connection * mutt_conn_find(const struct ConnAccount *cac)
Find a connection from a list.
Definition: mutt_socket.c:86
mutt_buffer_make
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:61
NT_MAILBOX_RESORT
@ NT_MAILBOX_RESORT
Email list needs resorting.
Definition: mailbox.h:174
nntp_active_save_cache
int nntp_active_save_cache(struct NntpAccountData *adata)
Save list of all newsgroups to cache.
Definition: newsrc.c:651
Email::read
bool read
Email is read.
Definition: email.h:51
NNTP_SSL_PORT
#define NNTP_SSL_PORT
Definition: private.h:35
NntpAccountData::newgroups_time
time_t newgroups_time
Definition: adata.h:54
ConnAccount::user
char user[128]
Username.
Definition: connaccount.h:55
nntp_hcache_namer
static void nntp_hcache_namer(const char *path, struct Buffer *dest)
Compose hcache file names - Implements hcache_namer_t.
Definition: newsrc.c:691
NntpAccountData::groups_hash
struct HashTable * groups_hash
Definition: adata.h:59
LL_DEBUG2
@ LL_DEBUG2
Log at debug level 2.
Definition: logging.h:41
nntp_newsrc_close
void nntp_newsrc_close(struct NntpAccountData *adata)
Unlock and close .newsrc file.
Definition: newsrc.c:120
MUTT_CA_OAUTH_CMD
@ MUTT_CA_OAUTH_CMD
OAuth refresh command.
Definition: connaccount.h:37
mutt_hash_free
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition: hash.c:447
NntpMboxData
NNTP-specific Mailbox data -.
Definition: mdata.h:31
MuttFormatFlags
uint8_t MuttFormatFlags
Flags for mutt_expando_format(), e.g. MUTT_FORMAT_FORCESUBJ.
Definition: format_flags.h:29
mutt_error
#define mutt_error(...)
Definition: logging.h:84
mutt_str_copy
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:716
NNTP_PORT
#define NNTP_PORT
Definition: private.h:34