tin  2.6.1
About: TIN is a threaded NNTP and spool based UseNet newsreader.
  Fossies Dox: tin-2.6.1.tar.xz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

active.c
Go to the documentation of this file.
1/*
2 * Project : tin - a Usenet reader
3 * Module : active.c
4 * Author : I. Lea
5 * Created : 1992-02-16
6 * Updated : 2021-07-23
7 * Notes :
8 *
9 * Copyright (c) 1992-2022 Iain Lea <iain@bricbrac.de>
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 *
16 * 1. Redistributions of source code must retain the above copyright notice,
17 * this list of conditions and the following disclaimer.
18 *
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 *
23 * 3. Neither the name of the copyright holder nor the names of its
24 * contributors may be used to endorse or promote products derived from
25 * this software without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
31 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
38 */
39
40
41#ifndef TIN_H
42# include "tin.h"
43#endif /* !TIN_H */
44#ifndef TCURSES_H
45# include "tcurses.h"
46#endif /* !TCURSES_H */
47
48/*
49 * List of allowed separator chars in active file
50 * unused in parse_active_line()
51 */
52#define ACTIVE_SEP " \n"
53
54#ifdef NNTP_ABLE
55# ifdef DISABLE_PIPELINING
56# define NUM_SIMULTANEOUS_GROUP_COMMAND 1
57# else
58# define NUM_SIMULTANEOUS_GROUP_COMMAND 50
59# endif /* DISABLE_PIPELINING */
60#endif /* NNTP_ABLE */
61
63static time_t active_timestamp; /* time active file read (local) */
64
65
66/*
67 * Local prototypes
68 */
69static FILE *open_newgroups_fp(int idx);
70static FILE *open_news_active_fp(void);
71static int check_for_any_new_groups(void);
72static void active_add(struct t_group *ptr, t_artnum count, t_artnum max, t_artnum min, const char *moderated);
73static void append_group_line(char *active_file, char *group_path, t_artnum art_max, t_artnum art_min, char *base_dir);
74static void make_group_list(char *active_file, char *base_dir, char *fixed_base, char *group_path);
75static void read_active_file(void);
76static void read_newsrc_active_file(void);
77static void subscribe_new_group(char *group, char *autosubscribe, char *autounsubscribe);
78#ifdef NNTP_ABLE
79 static t_bool do_read_newsrc_active_file(FILE *fp);
80 static t_bool parse_count_line(char *line, t_artnum *max, t_artnum *min, t_artnum *count, char *moderated);
81 static void read_active_counts(void);
82#else
83 static void do_read_newsrc_active_file(FILE *fp);
84#endif /* NNTP_ABLE */
85
86
89 void)
90{
92 (int) (time(NULL) - active_timestamp) >= tinrc.reread_active_file_secs));
93}
94
95
96/*
97 * Resync active file when reread_active_file_secs have passed or
98 * force_reread_active_file is set.
99 * Return TRUE if a reread was performed
100 */
101t_bool
103 void)
104{
105 char *old_group = NULL;
106 t_bool command_line = FALSE;
107
109 return FALSE;
110
112
113 if (selmenu.curr >= 0 && selmenu.max)
114 old_group = my_strdup(CURR_GROUP.name);
115
116 write_newsrc();
118
119#ifdef HAVE_MH_MAIL_HANDLING
120 read_mail_active_file();
121#endif /* HAVE_MH_MAIL_HANDLING */
122
124 command_line = TRUE;
125
126 read_newsrc(newsrc, bool_not(command_line));
127
128 if (command_line) /* Can't show only unread groups with cmd line groups */
130 else
131 toggle_my_groups(old_group);
132
133 FreeIfNeeded(old_group);
135
136 return TRUE;
137}
138
139
140/*
141 * Populate a slot in the active[] array
142 * TODO: 1) Have a preinitialised default slot and block assign it for speed
143 * TODO: 2) Lump count/max/min/moderat into a t_active, big patch but much cleaner throughout tin
144 */
145static void
147 struct t_group *ptr,
148 t_artnum count,
149 t_artnum max,
150 t_artnum min,
151 const char *moderated)
152{
153 /* name - pre-initialised when group is made */
154 ptr->aliasedto = ((moderated[0] == '=') ? my_strdup(moderated + 1) : NULL);
155 ptr->description = NULL;
156 /* spool - see below */
157 ptr->moderated = moderated[0];
158 ptr->count = count;
159 ptr->xmax = max;
160 ptr->xmin = min;
161 /* type - see below */
162 ptr->inrange = FALSE;
164 ptr->art_was_posted = FALSE;
165 ptr->subscribed = FALSE; /* not in my_group[] yet */
166 ptr->newgroup = FALSE;
167 ptr->bogus = FALSE;
168 ptr->next = -1; /* hash chain */
169 ptr->newsrc.xbitmap = (t_bitmap *) 0;
170 ptr->attribute = (struct t_attribute *) 0;
171 ptr->glob_filter = &glob_filter;
173
174 if (moderated[0] == '/') {
175 ptr->type = GROUP_TYPE_SAVE;
176 ptr->spooldir = my_strdup(moderated); /* TODO: Unix'ism, other OSs need transformation */
177 } else {
178 ptr->type = GROUP_TYPE_NEWS;
179 ptr->spooldir = spooldir; /* another global - sigh */
180 }
181}
182
183
184/*
185 * Decide how to handle a bogus groupname.
186 * If we process them interactively, create an empty active[] for this
187 * group and mark it bogus for display in the group selection page
188 * Otherwise, bogus groups are not displayed and are dealt with when newsrc
189 * is written.
190 */
191t_bool
193 char *name) /* return value is always ignored */
194{
195 struct t_group *ptr;
196
198 return FALSE;
199
200 if ((ptr = group_add(name)) == NULL)
201 return FALSE;
202
204 ptr->bogus = TRUE; /* Mark it bogus */
205 /*
206 * Set pointer to default attributes
207 */
208 if (ptr->attribute && !ptr->attribute->global)
209 free(ptr->attribute);
210 ptr->attribute = scopes[0].attribute;
211
212 if (my_group_add(name, FALSE) < 0)
213 return TRUE;
214
215 return FALSE; /* Nothing was printed yet */
216}
217
218
219/*
220 * Parse line from news or mail active files
221 */
222t_bool
224 char *line,
225 t_artnum *max,
226 t_artnum *min,
227 char *moderated)
228{
229 char *p = NULL, *q = NULL, *r = NULL;
230 t_bool lineok = FALSE;
231
232 if (line[0] == '#' || line[0] == '\0')
233 return FALSE;
234
235 if (strtok(line, ACTIVE_SEP)) { /* skip group name */
236 if ((p = strtok(NULL, ACTIVE_SEP))) { /* group max count */
237 if ((q = strtok(NULL, ACTIVE_SEP))) { /* group min count */
238 r = strtok(NULL, ACTIVE_SEP); /* mod status or path to mailgroup */
239 lineok = TRUE;
240 }
241 }
242 }
243
244 if (!p || !q || !r || !lineok) {
245#ifdef DEBUG
246 /* TODO: This also logs broken non NNTP active lines (i.e. mail.active) to NNTP */
247 if ((debug & DEBUG_NNTP) && verbose > 1)
248 debug_print_file("NNTP", "Active file corrupt - %s", line);
249#endif /* DEBUG */
250 return FALSE;
251 }
252
253 *max = atoartnum(p);
254 *min = atoartnum(q);
255 strcpy(moderated, r);
256
257 return TRUE;
258}
259
260
261#ifdef NNTP_ABLE
262/*
263 * Parse line from "LIST COUNTS" (RFC 6048)
264 * group high low count status
265 */
266static t_bool
267parse_count_line(
268 char *line,
269 t_artnum *max,
270 t_artnum *min,
272 char *moderated)
273{
274 char *p = NULL, *q = NULL, *r = NULL, *s = NULL;
275 t_bool lineok = FALSE;
276
277 if (line[0] == '#' || line[0] == '\0')
278 return FALSE;
279
280 if (strtok(line, ACTIVE_SEP)) { /* skip group name */
281 if ((p = strtok(NULL, ACTIVE_SEP))) { /* group max */
282 if ((q = strtok(NULL, ACTIVE_SEP))) { /* group min */
283 if ((r = strtok(NULL, ACTIVE_SEP))) { /* group count */
284 s = strtok(NULL, ACTIVE_SEP); /* mod status or path to mailgroup */
285 lineok = TRUE;
286 }
287 }
288 }
289 }
290
291 if (!p || !q || !r || !s || !lineok) {
292# ifdef DEBUG
293 if ((debug & DEBUG_NNTP) && verbose > 1)
294 debug_print_file("NNTP", "unparsable \"LIST COUNTS\" line: \"%s\"", line);
295# endif /* DEBUG */
296 return FALSE;
297 }
298
299 *max = atoartnum(p);
300 *min = atoartnum(q);
301 *count = atoartnum(r);
302 strcpy(moderated, s);
303
304 return TRUE;
305}
306#endif /* NNTP_ABLE */
307
308
309/*
310 * Load the active information into active[] by counting the min/max/count
311 * for each news group.
312 * Parse a line from the .newsrc file
313 * Send GROUP command to NNTP server directly to keep window.
314 * We can't know the 'moderator' status and always return 'y'
315 * But we don't change if the 'moderator' status is already checked by
316 * read_active_file()
317 * Returns TRUE if NNTP is enabled and authentication is needed
318 */
319#ifdef NNTP_ABLE
320static t_bool
321#else
322static void
323#endif /* NNTP_ABLE */
325 FILE *fp)
326{
327 char *ptr;
328 char *p;
329 char moderated[PATH_LEN];
330 int window = 0;
331 long processed = 0L;
333 static char ngname[NNTP_GRPLEN + 1]; /* RFC 3977 3.1 limits group names to 497 octets */
334 struct t_group *grpptr;
335#ifdef NNTP_ABLE
336 t_bool need_auth = FALSE;
337 char *ngnames[NUM_SIMULTANEOUS_GROUP_COMMAND];
338 int index_i = 0;
339 int index_o = 0;
340#endif /* NNTP_ABLE */
341
342 rewind(fp);
343
344 if (!batch_mode || verbose)
346
347 while ((ptr = tin_fgets(fp, FALSE)) != NULL || window != 0) {
348 if (ptr) {
349 p = strpbrk(ptr, ":!");
350
351 if (!p || *p != SUBSCRIBED) /* Invalid line or unsubscribed */
352 continue;
353 *p = '\0'; /* Now ptr is the group name */
354
355 /*
356 * 128 should be enough for a groupname, >256 and we overflow buffers
357 * later on
358 * TODO: check RFCs for possible max. size
359 */
360 my_strncpy(ngname, ptr, 128);
361 ptr = ngname;
362 }
363
365#ifdef NNTP_ABLE
366 char buf[NNTP_STRLEN];
367 char line[NNTP_STRLEN];
368
369 if (window < NUM_SIMULTANEOUS_GROUP_COMMAND && ptr && (!list_active || (newsrc_active && list_active && group_find(ptr, FALSE)))) {
370 ngnames[index_i] = my_strdup(ptr);
371 snprintf(buf, sizeof(buf), "GROUP %s", ngnames[index_i]);
372# ifdef DEBUG
373 if ((debug & DEBUG_NNTP) && verbose > 1)
374 debug_print_file("NNTP", "read_newsrc_active_file() %s", buf);
375# endif /* DEBUG */
376 put_server(buf);
377 index_i = (index_i + 1) % NUM_SIMULTANEOUS_GROUP_COMMAND;
378 window++;
379 }
380 if (window == NUM_SIMULTANEOUS_GROUP_COMMAND || ptr == NULL) {
381 int respcode = get_only_respcode(line, sizeof(line));
382
383 if (reconnected_in_last_get_server) {
384 /*
385 * If tin reconnected, last output is resended to server.
386 * So received data is for ngnames[last window_i].
387 * We resend all buffered command except for last window_i.
388 * And rotate buffer to use data received.
389 */
390 int i;
391 int j = index_o;
392 for (i = 0; i < window - 1; i++) {
393 snprintf(buf, sizeof(buf), "GROUP %s", ngnames[j]);
394# ifdef DEBUG
395 if ((debug & DEBUG_NNTP) && verbose > 1)
396 debug_print_file("NNTP", "read_newsrc_active_file() %s", buf);
397# endif /* DEBUG */
398 put_server(buf);
399 j = (j + 1) % NUM_SIMULTANEOUS_GROUP_COMMAND;
400 }
401 if (--index_o < 0)
402 index_o = NUM_SIMULTANEOUS_GROUP_COMMAND - 1;
403 if (--index_i < 0)
404 index_i = NUM_SIMULTANEOUS_GROUP_COMMAND - 1;
405 if (index_i != index_o)
406 ngnames[index_o] = ngnames[index_i];
407 }
408
409 switch (respcode) {
410
411 case OK_GROUP:
412 {
413 char fmt[25];
414
415 snprintf(fmt, sizeof(fmt), "%%"T_ARTNUM_SFMT" %%"T_ARTNUM_SFMT" %%"T_ARTNUM_SFMT" %%%ds", NNTP_GRPLEN);
416 if (sscanf(line, fmt, &count, &min, &max, ngname) != 4) {
417# ifdef DEBUG
418 if ((debug & DEBUG_NNTP) && verbose > 1)
419 debug_print_file("NNTP", "Invalid response to \"GROUP %s\": \"%s\"", ngnames[index_o], line);
420# endif /* DEBUG */
421 }
422 if (strcmp(ngname, ngnames[index_o]) != 0) {
423# ifdef DEBUG
424 if ((debug & DEBUG_NNTP) && verbose > 1)
425 debug_print_file("NNTP", "Groupname mismatch in response to \"GROUP %s\": \"%s\"", ngnames[index_o], line);
426# endif /* DEBUG */
427 }
428 ptr = ngname;
429 free(ngnames[index_o]);
430 index_o = (index_o + 1) % NUM_SIMULTANEOUS_GROUP_COMMAND;
431 window--;
432 break;
433 }
434
435 case ERR_NOAUTH:
436 case NEED_AUTHINFO:
437 need_auth = TRUE; /* delay auth till end of loop */
438 /* keep lint quiet: */
439 /* FALLTHROUGH */
440
441 case ERR_NOGROUP:
442 free(ngnames[index_o]);
443 index_o = (index_o + 1) % NUM_SIMULTANEOUS_GROUP_COMMAND;
444 window--;
445 continue;
446
447 case ERR_ACCESS:
448 tin_done(NNTP_ERROR_EXIT, "%s", line);
449 /* keep lint quiet: */
450 /* FALLTHROUGH */
451
452 default:
453# ifdef DEBUG
454 if ((debug & DEBUG_NNTP) && verbose > 1)
455 debug_print_file("NNTP", "NOT_OK %s", line);
456# endif /* DEBUG */
457 free(ngnames[index_o]);
458 index_o = (index_o + 1) % NUM_SIMULTANEOUS_GROUP_COMMAND;
459 window--;
460 continue;
461 }
462 } else
463 continue;
464#endif /* NNTP_ABLE */
465 } else {
466 if (group_get_art_info(spooldir, ptr, GROUP_TYPE_NEWS, &count, &max, &min))
467 continue;
468 }
469
470 strcpy(moderated, "y");
471
472 if (++processed % 5 == 0)
473 spin_cursor();
474
475 /*
476 * Load group into group hash table
477 * NULL means group already present, so we just fixup the counters
478 * This call may implicitly ++num_active
479 */
480 if ((grpptr = group_add(ptr)) == NULL) {
481 t_bool changed = FALSE;
482
483 if ((grpptr = group_find(ptr, FALSE)) == NULL)
484 continue;
485
486 if (max > grpptr->xmax) {
487 grpptr->xmax = max;
488 changed = TRUE;
489 }
490 if (min > grpptr->xmin) {
491 grpptr->xmin = min;
492 changed = TRUE;
493 }
494 if (changed) {
495 grpptr->count = count;
496 expand_bitmap(grpptr, 0); /* TODO: expand_bitmap(grpptr,grpptr->xmin) should be enough */
497 }
498 continue;
499 }
500
501 /*
502 * Load the new group in active[]
503 */
504 active_add(grpptr, count, max, min, moderated);
505 }
506#ifdef NNTP_ABLE
507 return need_auth;
508#endif /* NNTP_ABLE */
509}
510
511
512/*
513 * Wrapper for do_read_newsrc_active_file() to handle
514 * missing authentication
515 */
516static void
518 void)
519{
520 FILE *fp;
521#ifdef NNTP_ABLE
522 t_bool need_auth;
523#endif /* NNTP_ABLE */
524
525 /*
526 * return immediately if no .newsrc can be found or .newsrc is empty
527 * when function asked to use .newsrc
528 */
529 if ((fp = fopen(newsrc, "r")) == NULL)
530 return;
531
532 if (file_size(newsrc) <= 0L) {
533 fclose(fp);
534 return;
535 }
536
537#ifdef NNTP_ABLE
538 need_auth = do_read_newsrc_active_file(fp);
539#else
541#endif /* NNTP_ABLE */
542
543#ifdef NNTP_ABLE
544 if (need_auth) { /* delayed auth */
545 if (!authenticate(nntp_server, userid, FALSE) || do_read_newsrc_active_file(fp))
546 tin_done(EXIT_FAILURE, _(txt_auth_failed), ERR_ACCESS);
547 }
548#endif /* NNTP_ABLE */
549
550 fclose(fp);
551
552 /*
553 * Exit if active file wasn't read correctly or is empty
554 */
555 if (tin_errno || !num_active) {
558 else
560 }
561
562 if (!batch_mode || verbose)
563 my_fputc('\n', stdout);
564}
565
566
567/*
568 * Open the news active file locally or send the LIST command
569 */
570static FILE *
572 void)
573{
574#ifdef NNTP_ABLE
576 return (nntp_command("LIST", OK_GROUPS, NULL, 0));
577#endif /* NNTP_ABLE */
578 return (fopen(news_active_file, "r"));
579}
580
581
582/*
583 * Load the active file into active[]
584 */
585static void
587 void)
588{
589 FILE *fp;
590 char *ptr;
591 char moderated[PATH_LEN];
592 long processed = 0L;
593 struct t_group *grpptr;
595
596 if (!batch_mode || verbose)
598
599 if ((fp = open_news_active_fp()) == NULL) {
600 if (cmd_line && !batch_mode)
601 my_fputc('\n', stderr);
602
603#ifdef NNTP_ABLE
605 tin_done(EXIT_FAILURE, _(txt_cannot_retrieve), ACTIVE_FILE);
606# ifndef NNTP_ONLY
607 else
608 tin_done(EXIT_FAILURE, _(txt_cannot_open_active_file), news_active_file, tin_progname);
609# endif /* !NNTP_ONLY */
610#else
612#endif /* NNTP_ABLE */
613 }
614
615 while ((ptr = tin_fgets(fp, FALSE)) != NULL) {
616#if defined(DEBUG) && defined(NNTP_ABLE)
617 if ((debug & DEBUG_NNTP) && verbose) /* long multiline response */
618 debug_print_file("NNTP", "<<<%s%s", logtime(), ptr);
619#endif /* DEBUG && NNTP_ABLE */
620
621 if (++processed % MODULO_COUNT_NUM == 0)
622 spin_cursor();
623
624 if (!parse_active_line(ptr, &max, &min, moderated))
625 continue;
626
627 /*
628 * Load group into group hash table
629 * NULL means group already present, so we just fixup the counters
630 * This call may implicitly ++num_active
631 */
632 if ((grpptr = group_add(ptr)) == NULL) {
633 if ((grpptr = group_find(ptr, FALSE)) == NULL)
634 continue;
635
636 if (max > grpptr->xmax) {
637 grpptr->xmax = max;
638 grpptr->count = count;
639 }
640
641 if (min > grpptr->xmin) {
642 grpptr->xmin = min;
643 grpptr->count = count;
644 }
645
646 continue;
647 }
648
649 /*
650 * Load the new group in active[]
651 */
652 active_add(grpptr, count, max, min, moderated);
653 }
654
655 TIN_FCLOSE(fp);
656
657 /*
658 * Exit if active file wasn't read correctly or is empty
659 */
660 if (tin_errno || !num_active)
662
663 if (!batch_mode || verbose)
664 my_fputc('\n', stdout);
665}
666
667
668#ifdef NNTP_ABLE
669/*
670 * Load the active file into active[] via LIST COUNTS
671 */
672static void
673read_active_counts(
674 void)
675{
676 FILE *fp;
677 char *ptr;
678 char moderated[PATH_LEN];
679 long processed = 0L;
680 struct t_group *grpptr;
682
683 if (!batch_mode || verbose)
685
686 if ((fp = nntp_command("LIST COUNTS", OK_GROUPS, NULL, 0)) == NULL) {
687 if (cmd_line && !batch_mode)
688 my_fputc('\n', stderr);
689
690 tin_done(EXIT_FAILURE, _(txt_cannot_retrieve), ACTIVE_FILE);
691 }
692
693 while ((ptr = tin_fgets(fp, FALSE)) != NULL) {
694# ifdef DEBUG
695 if ((debug & DEBUG_NNTP) && verbose) /* long multiline response */
696 debug_print_file("NNTP", "<<<%s%s", logtime(), ptr);
697# endif /* DEBUG */
698
699 if (++processed % MODULO_COUNT_NUM == 0)
700 spin_cursor();
701
702 if (!parse_count_line(ptr, &max, &min, &count, moderated))
703 continue;
704
705 /*
706 * Load group into group hash table
707 * NULL means group already present, so we just fixup the counters
708 * This call may implicitly ++num_active
709 */
710 if ((grpptr = group_add(ptr)) == NULL) {
711 if ((grpptr = group_find(ptr, FALSE)) == NULL)
712 continue;
713
714 if (max > grpptr->xmax) {
715 grpptr->xmax = max;
716 grpptr->count = count;
717 }
718
719 if (min > grpptr->xmin) {
720 grpptr->xmin = min;
721 grpptr->count = count;
722 }
723
724 continue;
725 }
726
727 /*
728 * Load the new group in active[]
729 */
730 active_add(grpptr, count, max, min, moderated);
731 }
732
733 /*
734 * Exit if active file wasn't read correctly or is empty
735 */
736 if (tin_errno || !num_active)
738
739 if (!batch_mode || verbose)
740 my_fputc('\n', stdout);
741}
742#endif /* NNTP_ABLE */
743
744
745/*
746 * Load the active file into active[]
747 * Check and preload any new newgroups into my_group[]
748 */
749int
751 void)
752{
753 FILE *fp;
754 int newgrps = 0;
755 t_bool do_group_cmds = !nntp_caps.list_counts;
756#if defined(NNTP_ABLE) && !defined(DISABLE_PIPELINING)
757 t_bool did_list_cmd = FALSE;
758#endif /* NNTP_ABLE && !DISABLE_PIPELINING */
759
760 /*
761 * Ignore -n if no .newsrc can be found or .newsrc is empty
762 */
763 if (newsrc_active) {
764 if ((fp = fopen(newsrc, "r")) == NULL) {
767 } else {
768 fclose(fp);
769 if (file_size(newsrc) <= 0L) {
772 }
773 }
774 }
775
776 /* Read an active file if it is allowed */
777 if (list_active) {
778#ifdef NNTP_ABLE
779# ifndef DISABLE_PIPELINING
780 did_list_cmd = TRUE;
781# endif /* !DISABLE_PIPELINING */
783 read_active_counts();
784 else
785#endif /* NNTP_ABLE */
787 }
788
789 /* Read .newsrc and check each group */
790 if (newsrc_active) {
791#ifdef NNTP_ABLE
792# ifndef DISABLE_PIPELINING
793 /*
794 * prefer LIST COUNTS, otherwise use LIST ACTIVE (-l) or GROUP (-n)
795 * or both (-ln); LIST COUNTS/ACTIVE grplist is used up to
796 * PIPELINE_LIMIT groups in newsrc
797 */
798 if (read_news_via_nntp && (list_active || nntp_caps.list_counts) && !did_list_cmd) {
799 char buff[NNTP_STRLEN];
800 char *ptr, *q;
801 char moderated[PATH_LEN];
802 int r = 0, j = 0;
803 int i;
804 struct t_group *grpptr;
806 t_bool need_auth = FALSE;
807
808 *buff = '\0';
809 /* we can't use for_each_group(i) yet, so we have to parse the newsrc */
810 if ((fp = fopen(newsrc, "r")) != NULL) {
811 while (tin_fgets(fp, FALSE) != NULL)
812 j++;
813 rewind(fp);
814 if (j < PIPELINE_LIMIT) {
815 while ((ptr = tin_fgets(fp, FALSE)) != NULL) {
816 if (!(q = strpbrk(ptr, ":!")))
817 continue;
818 *q = '\0';
820 /* LIST ACTIVE or LIST COUNTS takes wildmats */
821 if (*buff && ((strlen(buff) + strlen(ptr)) < (NNTP_GRPLEN - 1))) { /* append group name */
822 snprintf(buff + strlen(buff), sizeof(buff) - strlen(buff), ",%s", ptr);
823 } else {
824 if (*buff) {
825 put_server(buff);
826 r++;
827 }
828 snprintf(buff, sizeof(buff), "LIST %s %s", nntp_caps.list_counts ? "COUNTS" : "ACTIVE", ptr);
829 }
830 continue;
831 } else
832 snprintf(buff, sizeof(buff), "LIST ACTIVE %s", ptr);
833 put_server(buff);
834 r++;
835 *buff = '\0';
836 }
837 if (*buff) {
838 put_server(buff);
839 r++;
840 }
841 } else
842 do_group_cmds = TRUE;
843
844 fclose(fp);
845
846 if (j < PIPELINE_LIMIT) {
847 for (i = 0; i < r && !did_reconnect; i++) {
848 if ((j = get_only_respcode(buff, sizeof(buff))) != OK_GROUPS) {
849 /* TODO: add 483 (RFC 3977) code */
850 if (j == ERR_NOAUTH || j == NEED_AUTHINFO)
851 need_auth = TRUE;
852# if 0 /* do we need something like this? */
853 if (j == ERR_CMDSYN)
855# endif /* 0 */
856 continue;
857 } else {
858 while ((ptr = tin_fgets(FAKE_NNTP_FP, FALSE)) != NULL) {
859# ifdef DEBUG
860 if ((debug & DEBUG_NNTP) && verbose) /* long multiline response */
861 debug_print_file("NNTP", "<<<%s%s", logtime(), ptr);
862# endif /* DEBUG */
864 if (!parse_count_line(ptr, &max, &min, &count, moderated))
865 continue;
866 } else {
867 if (!parse_active_line(ptr, &max, &min, moderated))
868 continue;
869 }
870
871 if ((grpptr = group_add(ptr)) == NULL) {
872 if ((grpptr = group_find(ptr, FALSE)) == NULL)
873 continue;
874
875 if (max > grpptr->xmax) {
876 grpptr->xmax = max;
877 grpptr->count = count;
878 }
879 if (min > grpptr->xmin) {
880 grpptr->xmin = min;
881 grpptr->count = count;
882 }
883 continue;
884 }
885 active_add(grpptr, count, max, min, moderated);
886 }
887 }
888 }
889 if (need_auth) { /* retry after auth is overkill here, so just auth */
890 if (!authenticate(nntp_server, userid, FALSE))
892 }
893 }
894 did_reconnect = FALSE;
895 }
896 }
897# endif /* !DISABLE_PIPELINING */
898#endif /* NNTP_ABLE */
899 if (!nntp_caps.list_counts || do_group_cmds)
901 }
902
903 (void) time(&active_timestamp);
905
906 /*
907 * check_for_any_new_groups() also does $AUTOSUBSCRIBE
908 */
910 newgrps = check_for_any_new_groups();
911
912 /*
913 * finally we have a list of all groups and can set the attributes
914 */
916
917 return newgrps;
918}
919
920
921/*
922 * Open the active.times file locally or send the NEWGROUPS command
923 * "NEWGROUPS yymmdd hhmmss"
924 */
925static FILE *
927 int idx)
928{
929#ifdef NNTP_ABLE
930 char line[NNTP_STRLEN];
931 struct tm *ngtm;
932
934 /*
935 * not checking for caps_type == CAPABILITIES && reader as some
936 * servers do not support it even if advertising READER so we must
937 * handle errors anyway and just issue the cmd.
938 */
939 if (idx == -1 || ((ngtm = localtime(&newnews[idx].time)) == NULL))
940 return (FILE *) 0;
941
942 /*
943 * RFC 3077 states that we SHOULD use 4 digit year but some servers
944 * still do not support it.
945 */
946 snprintf(line, sizeof(line), "NEWGROUPS %02d%02d%02d %02d%02d%02d",
947 ngtm->tm_year % 100, ngtm->tm_mon + 1, ngtm->tm_mday,
948 ngtm->tm_hour, ngtm->tm_min, ngtm->tm_sec);
949
950 return (nntp_command(line, OK_NEWGROUPS, NULL, 0));
951 }
952#endif /* NNTP_ABLE */
953 return (fopen(active_times_file, "r"));
954}
955
956
957/*
958 * Check for any newly created newsgroups.
959 *
960 * If reading news locally check the NEWSLIBDIR/active.times file.
961 * Format: Groupname Seconds Creator
962 *
963 * If reading news via NNTP issue a NEWGROUPS command.
964 * Format: (as active file) Groupname Maxart Minart moderated
965 */
966static int
968 void)
969{
970 FILE *fp;
971 char *autosubscribe, *autounsubscribe;
972 char *ptr, *line, buf[NNTP_STRLEN];
973 char old_newnews_host[PATH_LEN];
974 int newnews_index;
975 int newgrps = 0;
976 time_t old_newnews_time;
977 time_t new_newnews_time;
978
979 if (!batch_mode /* || verbose */)
981
982 (void) time(&new_newnews_time);
983
984 /*
985 * find out if we have read news from here before otherwise -1
986 */
987 if ((newnews_index = find_newnews_index(nntp_server)) >= 0) {
988 STRCPY(old_newnews_host, newnews[newnews_index].host);
989 old_newnews_time = newnews[newnews_index].time;
990 } else {
991 STRCPY(old_newnews_host, "UNKNOWN");
992 old_newnews_time = (time_t) 0;
993 }
994
995#ifdef DEBUG
996 if ((debug & DEBUG_NNTP) && verbose > 1)
997 debug_print_file("NNTP", "Newnews old=[%lu] new=[%lu]", (unsigned long int) old_newnews_time, (unsigned long int) new_newnews_time);
998#endif /* DEBUG */
999
1000 if ((fp = open_newgroups_fp(newnews_index)) != NULL) {
1001 /*
1002 * Need these later. They list user-defined groups to be
1003 * automatically subscribed or unsubscribed.
1004 */
1005 autosubscribe = getenv("AUTOSUBSCRIBE");
1006 autounsubscribe = getenv("AUTOUNSUBSCRIBE");
1007
1008 while ((line = tin_fgets(fp, FALSE)) != NULL) {
1009 /*
1010 * Split the group name off and subscribe. If we're reading local,
1011 * we must check the creation date manually
1012 */
1013 if ((ptr = strchr(line, ' ')) != NULL) {
1014 if (!read_news_via_nntp && ((time_t) atol(ptr) < old_newnews_time || old_newnews_time == (time_t) 0))
1015 continue;
1016
1017 *ptr = '\0';
1018 }
1019 subscribe_new_group(line, autosubscribe, autounsubscribe);
1020 newgrps++;
1021 }
1022 TIN_FCLOSE(fp);
1023
1024 if (tin_errno)
1025 return 0; /* Don't update the time if we quit */
1026 }
1027
1028 /*
1029 * Update (if already existing) or create (if new) the in-memory
1030 * 'last time newgroups checked' slot for this server. It will be written
1031 * out as part of tinrc.
1032 */
1033 if (newnews_index >= 0)
1034 newnews[newnews_index].time = new_newnews_time;
1035 else {
1036 snprintf(buf, sizeof(buf), "%s %lu", nntp_server, (unsigned long int) new_newnews_time);
1038 }
1039
1040 if (!batch_mode)
1041 my_fputc('\n', stdout);
1042
1043 return newgrps;
1044}
1045
1046
1047/*
1048 * Subscribe to a new news group:
1049 * Handle the AUTOSUBSCRIBE/AUTOUNSUBSCRIBE env vars
1050 * They hold a wildcard list of groups that should be automatically
1051 * (un)subscribed when a new group is found
1052 * If a group is autounsubscribed, completely ignore it
1053 * If a group is autosubscribed, subscribe to it
1054 * Otherwise, mark it as New for inclusion in selection screen
1055 */
1056static void
1058 char *group,
1059 char *autosubscribe,
1060 char *autounsubscribe)
1061{
1062 int idx;
1063 struct t_group *ptr;
1064
1065 /*
1066 * If we explicitly don't auto subscribe to this group, then don't bother going on
1067 */
1068 if ((autounsubscribe != NULL) && match_group_list(group, autounsubscribe))
1069 return;
1070
1071 /*
1072 * Try to add the group to our selection list. If this fails, we're
1073 * probably using -n, so we fake an entry with no counts. The count will
1074 * be properly updated when we enter the group. Otherwise there is some
1075 * mismatch in the active.times data and we ignore the newgroup.
1076 */
1077 if ((idx = my_group_add(group, FALSE)) < 0) {
1078 if (list_active) {
1079/* my_fprintf(stderr, "subscribe_new_group: %s not in active[] && list_active\n", group); */
1080 return;
1081 }
1082
1083 if ((ptr = group_add(group)) != NULL)
1085
1086 if ((idx = my_group_add(group, FALSE)) < 0)
1087 return;
1088 }
1089
1090 if (!no_write && (autosubscribe != NULL) && match_group_list(group, autosubscribe)) {
1091 if (!batch_mode || verbose)
1093
1094 /*
1095 * as subscribe_new_group() is called from check_for_any_new_groups()
1096 * which has pending data on the socket if reading via NNTP we are not
1097 * allowed to issue any NNTP commands yet
1098 */
1100 /*
1101 * Bad kluge to stop group later appearing in New newsgroups. This
1102 * effectively loses the group, and it has now been subscribed to and
1103 * so will be reread later by read_newsrc()
1104 */
1105 selmenu.max--;
1106 } else
1107 active[my_group[idx]].newgroup = TRUE;
1108}
1109
1110
1111/*
1112 * See if group is a member of group_list, returning a boolean.
1113 * group_list is a comma separated list of newsgroups, ! implies NOT
1114 * The same degree of wildcarding as used elsewhere in tin is allowed
1115 */
1116t_bool
1118 const char *group,
1119 const char *group_list)
1120{
1121 char *separator;
1122 char pattern[HEADER_LEN];
1123 size_t group_len, list_len;
1124 t_bool negate, accept = FALSE;
1125
1126 list_len = strlen(group_list);
1127 /*
1128 * walk through comma-separated entries in list
1129 */
1130 while (list_len != 0) {
1131 /*
1132 * find end/length of this entry
1133 */
1134 separator = strchr(group_list, ',');
1135 group_len = MIN(((separator == NULL) ? list_len : (size_t) (separator - group_list)), sizeof(pattern) - 1);
1136
1137 if ((negate = (*group_list == '!'))) {
1138 /*
1139 * a '!' before the pattern inverts sense of match
1140 */
1141 group_list++;
1142 group_len--;
1143 list_len--;
1144 }
1145 /*
1146 * copy out the entry and terminate it properly
1147 */
1148 strncpy(pattern, group_list, group_len);
1149 pattern[group_len] = '\0';
1150 /*
1151 * case-insensitive wildcard match
1152 */
1153 if (GROUP_MATCH(group, pattern, TRUE))
1154 accept = bool_not(negate); /* matched! */
1155
1156 /*
1157 * now examine next entry if any
1158 */
1159 if (group_list[group_len] != '\0')
1160 group_len++; /* skip the separator */
1161
1162 group_list += group_len;
1163 list_len -= group_len;
1164 }
1165 return accept;
1166}
1167
1168
1169/*
1170 * Add or update an entry to the in-memory newnews[] array (The times newgroups
1171 * were last checked for a particular news server)
1172 * If this is first time we've been called, zero out the array.
1173 *
1174 * Side effects:
1175 * 'info' is modified. Caller should not depend on it.
1176 */
1177void
1179 char *info)
1180{
1181 char *ptr;
1182 int i;
1183 time_t new_time;
1184
1185 /*
1186 * initialize newnews[] if no entries
1187 */
1188 if (!num_newnews) {
1189 for (i = 0; i < max_newnews; i++) {
1190 newnews[i].host = NULL;
1191 newnews[i].time = (time_t) 0;
1192 }
1193 }
1194
1195 /*
1196 * Split 'info' into hostname and time
1197 */
1198 if ((ptr = strchr(info, ' ')) == NULL)
1199 return;
1200
1201 *ptr++ = '\0';
1202 new_time = (time_t) atol(ptr);
1203
1204 /*
1205 * If this is a new host entry, set it up
1206 */
1207 if ((i = find_newnews_index(info)) == -1) {
1208 i = num_newnews++;
1209
1210 if (i >= max_newnews)
1212 newnews[i].host = my_strdup(info);
1213 }
1214
1215 newnews[i].time = new_time;
1216
1217#ifdef DEBUG
1218 if ((debug & DEBUG_NNTP) && verbose > 1)
1219 debug_print_file("NNTP", "ACTIVE host=[%s] time=[%lu]", newnews[i].host, (unsigned long int) newnews[i].time);
1220#endif /* DEBUG */
1221}
1222
1223
1224/*
1225 * Return the index of cur_newnews_host in newnews[] or -1 if not found
1226 */
1227int
1229 const char *cur_newnews_host)
1230{
1231 int i;
1232
1233 for (i = 0; i < num_newnews; i++) {
1234 if (STRCMPEQ(cur_newnews_host, newnews[i].host))
1235 return i;
1236 }
1237
1238 return -1;
1239}
1240
1241
1242/*
1243 * Get a single status char from the moderated field. Used on selection screen
1244 * and in header of group screen
1245 */
1246char
1248 char ch)
1249{
1250 switch (ch) {
1251 case 'm':
1252 return 'M';
1253
1254 case 'x':
1255 case 'n':
1256 case 'j':
1257 return 'X';
1258
1259 case '=':
1260 return '=';
1261
1262 default:
1263 return ' ';
1264 }
1265}
1266
1267
1268/* ex actived.c functions */
1269void
1271 void)
1272{
1273 char *fb;
1274 char group_path[PATH_LEN];
1275 char local_save_active_file[PATH_LEN];
1276
1277 joinpath(local_save_active_file, sizeof(local_save_active_file), rcdir, ACTIVE_SAVE_FILE);
1278
1279 if (no_write && file_size(local_save_active_file) != -1L)
1280 return;
1281
1282 if (strfpath((cmdline.args & CMDLINE_SAVEDIR) ? cmdline.savedir : tinrc.savedir, group_path, sizeof(group_path), NULL, FALSE)) {
1284 print_active_head(local_save_active_file);
1285
1286 while (strlen(group_path) && group_path[strlen(group_path) - 1] == '/')
1287 group_path[strlen(group_path) - 1] = '\0';
1288
1289 fb = my_strdup(group_path);
1290 make_group_list(local_save_active_file, (cmdline.args & CMDLINE_SAVEDIR) ? cmdline.savedir : tinrc.savedir, fb, group_path);
1291 free(fb);
1292 }
1293}
1294
1295
1296static void
1298 char *active_file,
1299 char *base_dir,
1300 char *fixed_base,
1301 char *group_path)
1302{
1303 DIR *dir;
1304 DIR_BUF *direntry;
1305 char *ptr;
1306 char filename[PATH_LEN];
1307 char path[PATH_LEN];
1308 t_artnum art_max;
1309 t_artnum art_min;
1310 struct stat stat_info;
1311 t_bool is_dir;
1312
1313 if ((dir = opendir(group_path)) != NULL) {
1314 is_dir = FALSE;
1315 while ((direntry = readdir(dir)) != NULL) {
1316 STRCPY(filename, direntry->d_name);
1317 joinpath(path, sizeof(path), group_path, filename);
1318 if (!(filename[0] == '.' && filename[1] == '\0') &&
1319 !(filename[0] == '.' && filename[1] == '.' && filename[2] == '\0')) {
1320 if (stat(path, &stat_info) != -1) {
1321 if (S_ISDIR(stat_info.st_mode))
1322 is_dir = TRUE;
1323 }
1324 }
1325 if (is_dir) {
1326 is_dir = FALSE;
1327 strcpy(group_path, path);
1328
1329 make_group_list(active_file, base_dir, fixed_base, group_path);
1330 find_art_max_min(group_path, &art_max, &art_min);
1331 append_group_line(active_file, group_path + strlen(fixed_base) + 1, art_max, art_min, fixed_base);
1332 if ((ptr = strrchr(group_path, '/')) != NULL)
1333 *ptr = '\0';
1334 }
1335 }
1336 CLOSEDIR(dir);
1337 }
1338}
1339
1340
1341static void
1343 char *active_file,
1344 char *group_path,
1345 t_artnum art_max,
1346 t_artnum art_min,
1347 char *base_dir)
1348{
1349 FILE *fp;
1350 char *file_tmp;
1351
1352 if (art_max == 0 && art_min == 1)
1353 return;
1354
1355 file_tmp = get_tmpfilename(active_file);
1356
1357 if (!backup_file(active_file, file_tmp)) {
1358 free(file_tmp);
1359 return;
1360 }
1361
1362 if ((fp = fopen(active_file, "a+")) != NULL) {
1363 char *ptr;
1364 char *group_name;
1365 int err;
1366
1367 ptr = group_name = my_strdup(group_path);
1368 ptr++;
1369 while ((ptr = strchr(ptr, '/')) != NULL)
1370 *ptr = '.';
1371
1372 wait_message(0, "Appending=[%s %"T_ARTNUM_PFMT" %"T_ARTNUM_PFMT" %s]\n", group_name, art_max, art_min, base_dir);
1373 print_group_line(fp, group_name, art_max, art_min, base_dir);
1374 if ((err = ferror(fp)) || fclose(fp)) { /* TODO: issue warning? */
1375 if (err) {
1376 clearerr(fp);
1377 fclose(fp);
1378 }
1379 err = rename(file_tmp, active_file);
1380#ifdef DEBUG
1381 if ((debug & DEBUG_MISC) && err) /* TODO: is this the right debug-level? */
1382 perror_message(_(txt_rename_error), file_tmp, active_file);
1383#endif /* DEBUG */
1384 }
1385 free(group_name);
1386 }
1387 unlink(file_tmp);
1388 free(file_tmp);
1389}
static FILE * open_newgroups_fp(int idx)
Definition: active.c:926
static void active_add(struct t_group *ptr, t_artnum count, t_artnum max, t_artnum min, const char *moderated)
Definition: active.c:146
static void subscribe_new_group(char *group, char *autosubscribe, char *autounsubscribe)
Definition: active.c:1057
void create_save_active_file(void)
Definition: active.c:1270
static FILE * open_news_active_fp(void)
Definition: active.c:571
static void read_active_file(void)
Definition: active.c:586
static void do_read_newsrc_active_file(FILE *fp)
Definition: active.c:324
t_bool match_group_list(const char *group, const char *group_list)
Definition: active.c:1117
int find_newnews_index(const char *cur_newnews_host)
Definition: active.c:1228
char group_flag(char ch)
Definition: active.c:1247
t_bool resync_active_file(void)
Definition: active.c:102
static time_t active_timestamp
Definition: active.c:63
static void read_newsrc_active_file(void)
Definition: active.c:517
static int check_for_any_new_groups(void)
Definition: active.c:967
#define ACTIVE_SEP
Definition: active.c:52
t_bool parse_active_line(char *line, t_artnum *max, t_artnum *min, char *moderated)
Definition: active.c:223
static void append_group_line(char *active_file, char *group_path, t_artnum art_max, t_artnum art_min, char *base_dir)
Definition: active.c:1342
static void make_group_list(char *active_file, char *base_dir, char *fixed_base, char *group_path)
Definition: active.c:1297
int read_news_active_file(void)
Definition: active.c:750
t_bool process_bogus(char *name)
Definition: active.c:192
t_bool need_reread_active_file(void)
Definition: active.c:88
void load_newnews_info(char *info)
Definition: active.c:1178
t_bool force_reread_active_file
Definition: active.c:62
unsigned t_bool
Definition: bool.h:77
#define bool_not(b)
Definition: bool.h:81
#define TRUE
Definition: bool.h:74
#define FALSE
Definition: bool.h:70
#define DEBUG_MISC
Definition: debug.h:54
#define DEBUG_NNTP
Definition: debug.h:47
struct t_filters glob_filter
Definition: filter.c:87
int verbose
Definition: init.c:154
constext txt_creating_active[]
Definition: lang.c:158
char active_times_file[PATH_LEN]
Definition: init.c:66
int max_newnews
Definition: memory.c:52
t_bool check_for_new_newsgroups
Definition: init.c:128
int * my_group
Definition: memory.c:64
struct t_scope * scopes
Definition: memory.c:67
constext txt_rename_error[]
Definition: lang.c:790
struct t_newnews * newnews
Definition: memory.c:68
struct t_capabilities nntp_caps
Definition: init.c:513
char * nntp_server
Definition: nntplib.c:28
int tin_errno
Definition: read.c:59
t_bool cmd_line
Definition: init.c:129
t_bool read_saved_news
Definition: init.c:152
t_menu selmenu
Definition: select.c:84
constext txt_checking_new_groups[]
Definition: lang.c:143
t_bool reread_active_for_posted_arts
Definition: init.c:149
constext txt_reading_news_active_file[]
Definition: lang.c:780
constext txt_reading_news_newsrc_file[]
Definition: lang.c:781
char news_active_file[PATH_LEN]
Definition: init.c:94
constext txt_cannot_open[]
Definition: lang.c:128
char userid[PATH_LEN]
Definition: init.c:107
t_bool newsrc_active
Definition: init.c:144
constext txt_autosubscribed[]
Definition: lang.c:106
t_bool list_active
Definition: init.c:143
char rcdir[PATH_LEN]
Definition: init.c:100
constext txt_active_file_is_empty[]
Definition: lang.c:49
char newsrc[PATH_LEN]
Definition: init.c:96
char * tin_progname
Definition: init.c:105
int num_newnews
Definition: memory.c:53
constext txt_error_server_has_no_listed_groups[]
Definition: lang.c:267
struct t_config tinrc
Definition: init.c:192
unsigned short debug
Definition: debug.c:51
char spooldir[PATH_LEN]
Definition: init.c:102
t_bool no_write
Definition: init.c:145
struct t_cmdlineopts cmdline
Definition: init.c:190
t_bool read_news_via_nntp
Definition: init.c:151
constext txt_servers_active[]
Definition: lang.c:855
int num_active
Definition: memory.c:51
struct t_group * active
Definition: memory.c:66
t_bool batch_mode
Definition: init.c:127
static char buf[16]
Definition: langinfo.c:50
#define NNTP_STRLEN
Definition: nntplib.h:155
#define OK_GROUPS
Definition: nntplib.h:96
#define ERR_NOGROUP
Definition: nntplib.h:126
#define OK_NEWGROUPS
Definition: nntplib.h:108
#define NNTP_GRPLEN
Definition: nntplib.h:159
#define OK_GROUP
Definition: nntplib.h:95
#define ERR_CMDSYN
Definition: nntplib.h:145
@ CAPABILITIES
Definition: nntplib.h:171
#define ERR_AUTHFAIL
Definition: nntplib.h:140
#define ERR_NOAUTH
Definition: nntplib.h:139
#define ERR_ACCESS
Definition: nntplib.h:146
#define NEED_AUTHINFO
Definition: nntplib.h:119
struct t_group * group_find(const char *group_name, t_bool ignore_case)
Definition: list.c:154
char * strpbrk(const char *str1, const char *str2)
Definition: string.c:312
int read_cmd_line_groups(void)
Definition: main.c:996
_Noreturn void tin_done(int ret, const char *fmt,...)
Definition: misc.c:562
void perror_message(const char *fmt,...)
Definition: screen.c:260
void print_active_head(const char *active_file)
Definition: mail.c:531
void expand_bitmap(struct t_group *group, t_artnum min)
Definition: newsrc.c:1468
void spin_cursor(void)
Definition: screen.c:497
void show_selection_page(void)
Definition: select.c:595
void joinpath(char *result, size_t result_size, const char *dir, const char *file)
Definition: joinpath.c:50
t_bool backup_file(const char *filename, const char *backupname)
Definition: misc.c:213
char * my_strdup(const char *str)
Definition: string.c:139
long atol(const char *s)
Definition: string.c:392
void set_default_bitmap(struct t_group *group)
Definition: newsrc.c:1676
struct t_group * group_add(const char *group)
Definition: list.c:213
void subscribe(struct t_group *group, int sub_state, t_bool get_info)
Definition: newsrc.c:553
signed long int read_newsrc(char *newsrc_file, t_bool allgroups)
Definition: newsrc.c:83
void print_group_line(FILE *fp, const char *group_name, t_artnum art_max, t_artnum art_min, const char *base_dir)
Definition: mail.c:578
char * tin_fgets(FILE *fp, t_bool header)
Definition: read.c:317
signed long int write_newsrc(void)
Definition: newsrc.c:201
int strfpath(const char *format, char *str, size_t maxsize, struct t_group *group, t_bool expand_all)
Definition: misc.c:1699
void wait_message(unsigned int sdelay, const char *fmt,...)
Definition: screen.c:133
void my_strncpy(char *p, const char *q, size_t n)
Definition: string.c:196
int group_get_art_info(char *tin_spooldir, char *groupname, int grouptype, t_artnum *art_count, t_artnum *art_max, t_artnum *art_min)
Definition: newsrc.c:423
void assign_attributes_to_groups(void)
Definition: attrib.c:977
void expand_newnews(void)
Definition: memory.c:200
void find_art_max_min(const char *group_path, t_artnum *art_max, t_artnum *art_min)
Definition: mail.c:547
void toggle_my_groups(const char *group)
Definition: select.c:1292
char * get_tmpfilename(const char *filename)
Definition: misc.c:101
long file_size(const char *file)
Definition: misc.c:2133
const char * name
Definition: signal.c:117
unsigned global
Definition: tin.h:1644
t_bool list_counts
Definition: nntplib.h:204
enum extension_type type
Definition: nntplib.h:187
t_bool list_active
Definition: nntplib.h:192
char savedir[255]
Definition: tin.h:1502
unsigned int args
Definition: tin.h:1503
int strip_bogus
Definition: tinrc.h:163
char savedir[PATH_LEN]
Definition: tinrc.h:138
t_bool show_only_unread_groups
Definition: tinrc.h:244
int reread_active_file_secs
Definition: tinrc.h:155
Definition: tin.h:1816
struct t_filters * glob_filter
Definition: tin.h:1835
t_bool newgroup
Definition: tin.h:1830
char * description
Definition: tin.h:1819
t_bool inrange
Definition: tin.h:1826
t_bool bogus
Definition: tin.h:1831
char moderated
Definition: tin.h:1821
t_artnum xmin
Definition: tin.h:1824
t_artnum xmax
Definition: tin.h:1823
t_bool read_during_session
Definition: tin.h:1827
struct t_attribute * attribute
Definition: tin.h:1834
int next
Definition: tin.h:1832
unsigned int type
Definition: tin.h:1825
t_bool subscribed
Definition: tin.h:1829
t_artnum count
Definition: tin.h:1822
char * spooldir
Definition: tin.h:1820
t_bool art_was_posted
Definition: tin.h:1828
struct t_newsrc newsrc
Definition: tin.h:1833
char * aliasedto
Definition: tin.h:1818
int curr
Definition: tin.h:2056
int max
Definition: tin.h:2057
time_t time
Definition: tin.h:2015
char * host
Definition: tin.h:2014
t_bitmap * xbitmap
Definition: tin.h:1810
struct t_attribute * attribute
Definition: tin.h:1796
#define my_printf
Definition: tcurses.h:175
#define my_fputc(ch, stream)
Definition: tcurses.h:158
#define SUBSCRIBED
Definition: tin.h:1366
#define STRCMPEQ(s1, s2)
Definition: tin.h:822
long t_artnum
Definition: tin.h:229
#define TIN_FCLOSE(x)
Definition: tin.h:1048
#define ACTIVE_SAVE_FILE
Definition: tin.h:632
#define PIPELINE_LIMIT
Definition: tin.h:2527
#define T_ARTNUM_SFMT
Definition: tin.h:231
#define STRCPY(dst, src)
Definition: tin.h:820
#define MIN(a, b)
Definition: tin.h:811
#define MODULO_COUNT_NUM
Definition: tin.h:868
#define ACTIVE_FILE
Definition: tin.h:630
#define unlink(file)
Definition: tin.h:387
#define NNTP_ERROR_EXIT
Definition: tin.h:1306
#define atoartnum
Definition: tin.h:233
#define GROUP_MATCH(s1, pat, case_s)
Definition: tin.h:1025
#define S_ISDIR(m)
Definition: tin.h:2176
#define FreeIfNeeded(p)
Definition: tin.h:2252
#define EXIT_FAILURE
Definition: tin.h:1302
#define CURR_GROUP
Definition: tin.h:1054
#define _(Text)
Definition: tin.h:94
#define CMDLINE_SAVEDIR
Definition: tin.h:1105
#define PATH_LEN
Definition: tin.h:843
#define snprintf
Definition: tin.h:2464
#define T_ARTNUM_CONST(v)
Definition: tin.h:232
#define GROUP_TYPE_SAVE
Definition: tin.h:1071
#define BOGUS_SHOW
Definition: tin.h:1212
#define GROUP_TYPE_NEWS
Definition: tin.h:1070
#define HEADER_LEN
Definition: tin.h:863
#define T_ARTNUM_PFMT
Definition: tin.h:230
unsigned char t_bitmap
Definition: tin.h:1475
#define my_group_add(x, y)
Definition: tin.h:2258
#define DIR_BUF
Definition: tin.h:383
#define CLOSEDIR(DIR)
Definition: tin.h:2398
#define FAKE_NNTP_FP
Definition: tin.h:458