1 /*
2 * Project : tin - a Usenet reader
3 * Module : newsrc.c
4 * Author : I. Lea & R. Skrenta
5 * Created : 1991-04-01
6 * Updated : 2020-05-09
7 * Notes : ArtCount = (ArtMax - ArtMin) + 1 [could have holes]
8 *
9 * Copyright (c) 1991-2021 Iain Lea <iain@bricbrac.de>, Rich Skrenta <skrenta@pbm.com>
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 #ifndef TNNTP_H
48 # include "tnntp.h"
49 #endif /* !TNNTP_H */
50 #ifndef NEWSRC_H
51 # include "newsrc.h"
52 #endif /* !NEWSRC_H */
53
54 static mode_t newsrc_mode = 0;
55
56 /*
57 * Local prototypes
58 */
59 static FILE *open_subscription_fp(void);
60 static char *parse_newsrc_line(char *line, int *sub);
61 static char *parse_subseq(struct t_group *group, char *seq, t_artnum *low, t_artnum *high, t_artnum *sum);
62 static char *parse_get_seq(char *seq, t_artnum *low, t_artnum *high);
63 static int write_newsrc_line(FILE *fp, char *line);
64 static t_bool create_newsrc(char *newsrc_file);
65 static void auto_subscribe_groups(char *newsrc_file);
66 static void get_subscribe_info(struct t_group *grp);
67 static void parse_bitmap_seq(struct t_group *group, char *seq);
68 static void print_bitmap_seq(FILE *fp, struct t_group *group);
69
70
71 /*
72 * Read .newsrc into my_group[]. my_group[] ints point to active[] entries.
73 * If allgroups is set, then my_group[] is completely overwritten,
74 * otherwise, groups are appended. Any bogus groups will be handled
75 * accordingly. Bogus groups will _not_ be subscribed to as a design
76 * principle.
77 *
78 * Returns the number of lines read(useful for a check newsrc >= oldnewsrc)
79 * < 0 error
80 * >=0 number of lines read
81 */
82 signed long int
read_newsrc(char * newsrc_file,t_bool allgroups)83 read_newsrc(
84 char *newsrc_file,
85 t_bool allgroups)
86 {
87 FILE *fp;
88 char *grp, *seq;
89 int sub, i;
90 signed long line_count = 0;
91 struct stat statbuf;
92
93 if (allgroups)
94 selmenu.max = skip_newgroups();
95
96 /*
97 * make a .newsrc if none exist & auto subscribe to set groups
98 */
99 if (stat(newsrc_file, &statbuf) == -1) {
100 if (!create_newsrc(newsrc_file))
101 return -1L; /* ouch */
102 auto_subscribe_groups(newsrc_file);
103 } else
104 newsrc_mode = statbuf.st_mode;
105
106 if ((fp = fopen(newsrc_file, "r")) != NULL) {
107 if (!batch_mode || verbose)
108 wait_message(0, _(txt_reading_newsrc));
109
110 while ((grp = tin_fgets(fp, FALSE)) != NULL) {
111 seq = parse_newsrc_line(grp, &sub);
112 line_count++;
113
114 if (sub == SUBSCRIBED) {
115 if ((i = my_group_add(grp, FALSE)) >= 0) {
116 if (!active[my_group[i]].bogus) {
117 active[my_group[i]].subscribed = SUB_BOOL(sub);
118 parse_bitmap_seq(&active[my_group[i]], seq);
119 }
120 } else
121 process_bogus(grp);
122 }
123 }
124 fclose(fp);
125 /* If you aborted with 'q', then you get what you get. */
126
127 if (!batch_mode || verbose)
128 my_fputc('\n', stdout);
129
130 if (!cmd_line && !batch_mode)
131 clear_message();
132 }
133 return line_count;
134 }
135
136
137 /*
138 * Parse a line from the newsrc file and write it back out with updated
139 * sequence information. Return number of lines written (ie, 0 or 1)
140 */
141 static int
write_newsrc_line(FILE * fp,char * line)142 write_newsrc_line(
143 FILE *fp,
144 char *line)
145 {
146 char *seq;
147 int sub;
148 struct t_group *group;
149
150 seq = parse_newsrc_line(line, &sub);
151
152 if (line[0] == '\0' || sub == 0) /* Insurance against blank line */
153 return 0;
154
155 if (seq == NULL) { /* line has no ':' or '!' in it */
156 if (tinrc.strip_bogus == BOGUS_REMOVE)
157 wait_message(2, _(txt_remove_bogus), line);
158 return 0;
159 }
160
161 /*
162 * Find the group in active. If we cannot, then junk it if bogus groups
163 * are set to auto removal. Also check for bogus flag just in case
164 * strip_bogus was changed since tin started
165 */
166 group = group_find(line, FALSE);
167
168 if (tinrc.strip_bogus == BOGUS_REMOVE) {
169 if (group == NULL || group->bogus) { /* group doesn't exist */
170 wait_message(2, _(txt_remove_bogus), line);
171 return 0;
172 }
173 }
174
175 if ((group && group->newsrc.present) && (group->subscribed || !tinrc.strip_newsrc)) {
176 fprintf(fp, "%s%c ", group->name, SUB_CHAR(group->subscribed));
177 print_bitmap_seq(fp, group);
178 return 1;
179 } else {
180 if (sub == SUBSCRIBED || !tinrc.strip_newsrc) {
181 fprintf(fp, "%s%c %s\n", line, sub, seq);
182 return 1;
183 }
184 }
185 return 0;
186 }
187
188
189 /*
190 * Read in the users newsrc file and write a new file with all the changes
191 * changes from the current session. If this works, replace the original
192 * newsrc file.
193 * Returns < 0 on error
194 * >=0 number of lines written
195 */
196 signed long int
write_newsrc(void)197 write_newsrc(
198 void)
199 {
200 FILE *fp_ip;
201 FILE *fp_op;
202 char *line;
203 signed long int tot = 0L;
204 struct stat note_stat_newsrc;
205 t_bool write_ok = FALSE;
206 int err;
207
208 if (no_write)
209 return 0L;
210
211 if ((fp_ip = fopen(newsrc, "r")) == NULL)
212 return -1L; /* can't open newsrc */
213
214 /* get size of original newsrc */
215 if (fstat(fileno(fp_ip), ¬e_stat_newsrc) != 0) {
216 fclose(fp_ip);
217 return -1L; /* can't access newsrc */
218 }
219
220 if (!note_stat_newsrc.st_size) {
221 fclose(fp_ip);
222 return 0L; /* newsrc is empty */
223 }
224
225 if ((fp_op = fopen(newnewsrc, "w")) != NULL) {
226 if (newsrc_mode)
227 #ifdef HAVE_FCHMOD
228 fchmod(fileno(fp_op), newsrc_mode);
229 #else
230 # ifdef HAVE_CHMOD
231 chmod(newnewsrc, newsrc_mode);
232 # endif /* HAVE_CHMOD */
233 #endif /* HAVE_FCHMOD */
234
235 while ((line = tin_fgets(fp_ip, FALSE)) != NULL)
236 tot += write_newsrc_line(fp_op, line);
237
238 /*
239 * Don't rename if either fclose() fails or ferror() is set
240 */
241 if ((err = ferror(fp_op)) || fclose(fp_op)) {
242 error_message(2, _(txt_filesystem_full), NEWSRC_FILE);
243 unlink(newnewsrc);
244 if (err) {
245 clearerr(fp_op);
246 fclose(fp_op);
247 }
248 } else
249 write_ok = TRUE;
250 }
251
252 fclose(fp_ip);
253
254 if (tot < 1) {
255 error_message(2, _(txt_newsrc_nogroups));
256 unlink(newnewsrc);
257 return 0L; /* So we don't get prompted to try again */
258 }
259
260 if (write_ok)
261 rename_file(newnewsrc, newsrc);
262
263 return tot;
264 }
265
266
267 /*
268 * Create a newsrc from active[] groups. Subscribe to all groups.
269 */
270 static t_bool
create_newsrc(char * newsrc_file)271 create_newsrc(
272 char *newsrc_file)
273 {
274 FILE *fp;
275 int i;
276
277 if ((fp = fopen(newsrc_file, "w")) != NULL) {
278 wait_message(0, _(txt_creating_newsrc));
279
280 for_each_group(i)
281 fprintf(fp, "%s!\n", active[i].name);
282
283 if ((i = ferror(fp)) || fclose(fp)) {
284 error_message(2, _(txt_filesystem_full), NEWSRC_FILE);
285 if (i) {
286 clearerr(fp);
287 fclose(fp);
288 }
289 return FALSE;
290 }
291 return TRUE; /* newsrc created */
292 }
293 return FALSE;
294 }
295
296
297 /*
298 * Get a list of default groups to subscribe to
299 */
300 static FILE *
open_subscription_fp(void)301 open_subscription_fp(
302 void)
303 {
304 if (!read_saved_news) {
305 #ifdef NNTP_ABLE
306 if (read_news_via_nntp) {
307 /* RFC 6048 2.6 */
308 if (nntp_caps.type == CAPABILITIES && !nntp_caps.list_subscriptions)
309 return NULL;
310 else
311 return (nntp_command("LIST SUBSCRIPTIONS", OK_GROUPS, NULL, 0));
312 } else
313 #endif /* NNTP_ABLE */
314 return (fopen(subscriptions_file, "r"));
315 } else
316 return NULL;
317 }
318
319
320 /*
321 * Automatically subscribe user to newsgroups specified in
322 * NEWSLIBDIR/subscriptions (locally) or same file but from NNTP
323 * server (LIST SUBSCRIPTIONS) and create .newsrc
324 */
325 static void
auto_subscribe_groups(char * newsrc_file)326 auto_subscribe_groups(
327 char *newsrc_file)
328 {
329 FILE *fp_newsrc;
330 FILE *fp_subs;
331 char *ptr;
332 int err;
333
334 /*
335 * If subscription file exists then first unsubscribe to all groups
336 * and then subscribe to just the auto specified groups.
337 */
338 if ((fp_subs = open_subscription_fp()) == NULL)
339 return;
340
341 if (!batch_mode)
342 wait_message(0, _(txt_autosubscribing_groups));
343
344 if ((fp_newsrc = fopen(newsrc_file, "w")) == NULL) {
345 TIN_FCLOSE(fp_subs);
346 return;
347 }
348
349 if (newsrc_mode)
350 #ifdef HAVE_FCHMOD
351 fchmod(fileno(fp_newsrc), newsrc_mode);
352 #else
353 # ifdef HAVE_CHMOD
354 chmod(newsrc_file, newsrc_mode);
355 # endif /* HAVE_CHMOD */
356 #endif /* HAVE_FCHMOD */
357
358 /* TODO: test me! */
359 while ((ptr = tin_fgets(fp_subs, FALSE)) != NULL) {
360 if (ptr[0] != '#') {
361 if (group_find(ptr, FALSE) != NULL)
362 fprintf(fp_newsrc, "%s:\n", ptr);
363 }
364 }
365
366 /* We ignore user 'q'uits here. They will get them next time in any case */
367
368 if ((err = ferror(fp_newsrc)) || fclose(fp_newsrc)) {
369 error_message(2, _(txt_filesystem_full), NEWSRC_FILE);
370 if (err) {
371 clearerr(fp_newsrc);
372 fclose(fp_newsrc);
373 }
374 }
375
376 TIN_FCLOSE(fp_subs);
377 }
378
379
380 /*
381 * make a backup of users .newsrc in case of the bogie man
382 */
383 void
backup_newsrc(void)384 backup_newsrc(
385 void)
386 {
387 char dirbuf[PATH_LEN];
388 char filebuf[PATH_LEN];
389 struct stat statbuf;
390
391 #ifdef NNTP_ABLE
392 if (read_news_via_nntp && !read_saved_news && nntp_tcp_port != IPPORT_NNTP)
393 snprintf(filebuf, sizeof(filebuf), "%s:%u", nntp_server, nntp_tcp_port);
394 else
395 #endif /* NNTP_ABLE */
396 {
397 STRCPY(filebuf, quote_space_to_dash(nntp_server));
398 }
399 joinpath(dirbuf, sizeof(dirbuf), rcdir, filebuf);
400 joinpath(filebuf, sizeof(filebuf), dirbuf, OLDNEWSRC_FILE);
401
402 if (stat(dirbuf, &statbuf) == -1) {
403 if (my_mkdir(dirbuf, (mode_t) (S_IRWXU)) == -1)
404 /* Can't create directory: Fall back on Homedir */
405 joinpath(filebuf, sizeof(filebuf), homedir, OLDNEWSRC_FILE);
406 }
407
408 if (!backup_file(newsrc, filebuf))
409 error_message(2, _(txt_filesystem_full_backup), NEWSRC_FILE);
410 }
411
412
413 /*
414 * Find the total, max & min articles number for specified group
415 * Use nntp GROUP command or read local spool
416 * Return 0, or -error
417 */
418 int
group_get_art_info(char * tin_spooldir,char * groupname,int grouptype,t_artnum * art_count,t_artnum * art_max,t_artnum * art_min)419 group_get_art_info(
420 char *tin_spooldir,
421 char *groupname,
422 int grouptype,
423 t_artnum *art_count,
424 t_artnum *art_max,
425 t_artnum *art_min)
426 {
427 DIR *dir;
428 DIR_BUF *direntry;
429 t_artnum artnum;
430
431 if (read_news_via_nntp && grouptype == GROUP_TYPE_NEWS) {
432 #ifdef NNTP_ABLE
433 char line[NNTP_STRLEN];
434
435 snprintf(line, sizeof(line), "GROUP %s", groupname);
436 # ifdef DEBUG
437 if ((debug & DEBUG_NNTP) && verbose > 1)
438 debug_print_file("NNTP", "group_get_art_info %s", line);
439 # endif /* DEBUG */
440 put_server(line);
441
442 switch (get_respcode(line, sizeof(line))) {
443 case OK_GROUP:
444 if (sscanf(line, "%"T_ARTNUM_SFMT" %"T_ARTNUM_SFMT" %"T_ARTNUM_SFMT, art_count, art_min, art_max) != 3) {
445 # ifdef DEBUG
446 if ((debug & DEBUG_NNTP) && verbose > 1)
447 debug_print_file("NNTP", "Invalid response to GROUP command, %s", line);
448 # endif /* DEBUG */
449 }
450 break;
451
452 case ERR_NOGROUP:
453 *art_count = T_ARTNUM_CONST(0);
454 *art_min = T_ARTNUM_CONST(1);
455 *art_max = T_ARTNUM_CONST(0);
456 return -ERR_NOGROUP;
457
458 case ERR_ACCESS:
459 tin_done(NNTP_ERROR_EXIT, "%s", line);
460 /* keep lint quiet: */
461 /* NOTREACHED */
462 break;
463
464 default:
465 # ifdef DEBUG
466 if ((debug & DEBUG_NNTP) && verbose > 1)
467 debug_print_file("NNTP", "NOT_OK %s", line);
468 # endif /* DEBUG */
469 return -1;
470 }
471 #else
472 my_fprintf(stderr, _("Unreachable?\n")); /* TODO: -> lang.c */
473 return 0;
474 #endif /* NNTP_ABLE */
475 } else {
476 char group_path[PATH_LEN];
477 *art_count = T_ARTNUM_CONST(0);
478 *art_min = T_ARTNUM_CONST(0);
479 *art_max = T_ARTNUM_CONST(0);
480
481 make_base_group_path(tin_spooldir, groupname, group_path, sizeof(group_path));
482
483 if ((dir = opendir(group_path)) != NULL) {
484 while ((direntry = readdir(dir)) != NULL) {
485 artnum = atoartnum(direntry->d_name); /* should be '\0' terminated... */
486 if (artnum >= T_ARTNUM_CONST(1)) {
487 if (artnum > *art_max) {
488 *art_max = artnum;
489 if (*art_min == T_ARTNUM_CONST(0))
490 *art_min = artnum;
491 } else if (artnum < *art_min)
492 *art_min = artnum;
493 (*art_count)++;
494 }
495 }
496 CLOSEDIR(dir);
497 if (*art_min == T_ARTNUM_CONST(0))
498 *art_min = T_ARTNUM_CONST(1);
499 } else {
500 *art_min = T_ARTNUM_CONST(1);
501 return -1;
502 }
503 }
504
505 return 0;
506 }
507
508
509 /*
510 * Get and fixup (if needed) the counters for a newly subscribed group
511 */
512 static void
get_subscribe_info(struct t_group * grp)513 get_subscribe_info(
514 struct t_group *grp)
515 {
516 t_artnum oldmin = grp->xmin;
517 t_artnum oldmax = grp->xmax;
518
519 group_get_art_info(grp->spooldir, grp->name, grp->type, &grp->count, &grp->xmax, &grp->xmin);
520
521 if (grp->newsrc.num_unread > grp->count) {
522 #ifdef DEBUG
523 if (debug & DEBUG_NEWSRC) { /* TODO: is this the right debug-level? */
524 my_printf(cCRLF "Unread WRONG %s unread=[%"T_ARTNUM_PFMT"] count=[%"T_ARTNUM_PFMT"]", grp->name, grp->newsrc.num_unread, grp->count);
525 my_flush();
526 }
527 #endif /* DEBUG */
528 grp->newsrc.num_unread = grp->count;
529 }
530
531 if (grp->xmin != oldmin || grp->xmax != oldmax) {
532 expand_bitmap(grp, 0);
533 #ifdef DEBUG
534 if (debug & DEBUG_NEWSRC) { /* TODO: is this the right debug-level? */
535 my_printf(cCRLF "Min/Max DIFF %s old=[%"T_ARTNUM_PFMT"-%"T_ARTNUM_PFMT"] new=[%"T_ARTNUM_PFMT"-%"T_ARTNUM_PFMT"]", grp->name, oldmin, oldmax, grp->xmin, grp->xmax);
536 my_flush();
537 }
538 #endif /* DEBUG */
539 }
540 }
541
542
543 /*
544 * Subscribe/unsubscribe to a group in .newsrc.
545 * This involves rewriting the .newsrc with the new info
546 * If get_info is set we are allowed to issue NNTP commands if needed
547 */
548 void
subscribe(struct t_group * group,int sub_state,t_bool get_info)549 subscribe(
550 struct t_group *group,
551 int sub_state,
552 t_bool get_info)
553 {
554 FILE *fp;
555 FILE *newfp;
556 char *line;
557 char *seq;
558 int sub;
559 t_bool found = FALSE;
560
561 if (no_write)
562 return;
563
564 if ((newfp = fopen(newnewsrc, "w")) == NULL)
565 return;
566
567 if (newsrc_mode)
568 #ifdef HAVE_FCHMOD
569 fchmod(fileno(newfp), newsrc_mode);
570 #else
571 # ifdef HAVE_CHMOD
572 chmod(newnewsrc, newsrc_mode);
573 # endif /* HAVE_CHMOD */
574 #endif /* HAVE_FCHMOD */
575
576 if ((fp = fopen(newsrc, "r")) != NULL) {
577 while ((line = tin_fgets(fp, FALSE)) != NULL) {
578 if ((seq = parse_newsrc_line(line, &sub))) {
579 if (STRCMPEQ(line, group->name)) {
580 fprintf(newfp, "%s%c %s\n", line, sub_state, seq);
581 group->subscribed = SUB_BOOL(sub_state);
582
583 /* If previously subscribed to in .newsrc, load up any existing information */
584 if (sub_state == SUBSCRIBED)
585 parse_bitmap_seq(group, seq);
586
587 found = TRUE;
588 } else
589 fprintf(newfp, "%s%c %s\n", line, sub, seq);
590 }
591 }
592
593 fclose(fp);
594
595 if (!found) {
596 wait_message(0, _(txt_subscribing));
597 group->subscribed = SUB_BOOL(sub_state);
598 if (sub_state == SUBSCRIBED) {
599 fprintf(newfp, "%s%c ", group->name, sub_state);
600 if (get_info) {
601 get_subscribe_info(group);
602 print_bitmap_seq(newfp, group);
603 } else /* we are not allowed to issue NNTP cmds during AUTOSUBSCRIBE loop */
604 fprintf(newfp, "1\n");
605 } else
606 fprintf(newfp, "%s%c\n", group->name, sub_state);
607 }
608 }
609
610 if ((sub = ferror(newfp)) || fclose(newfp)) {
611 error_message(2, _(txt_filesystem_full), NEWSRC_FILE);
612 if (sub) {
613 clearerr(newfp);
614 fclose(newfp);
615 }
616 unlink(newnewsrc);
617 } else
618 rename_file(newnewsrc, newsrc);
619 }
620
621
622 void
reset_newsrc(void)623 reset_newsrc(
624 void)
625 {
626 FILE *fp;
627 FILE *newfp;
628 char *line;
629 int sub, i;
630
631 if (!no_write && (newfp = fopen(newnewsrc, "w")) != NULL) {
632 if (newsrc_mode)
633 #ifdef HAVE_FCHMOD
634 fchmod(fileno(newfp), newsrc_mode);
635 #else
636 # ifdef HAVE_CHMOD
637 chmod(newnewsrc, newsrc_mode);
638 # endif /* HAVE_CHMOD */
639 #endif /* HAVE_FCHMOD */
640
641 if ((fp = fopen(newsrc, "r")) != NULL) {
642 while ((line = tin_fgets(fp, FALSE)) != NULL) {
643 (void) parse_newsrc_line(line, &sub);
644 fprintf(newfp, "%s%c\n", line, sub);
645 }
646 fclose(fp);
647 }
648 if ((sub = ferror(newfp)) || fclose(newfp)) {
649 error_message(2, _(txt_filesystem_full), NEWSRC_FILE);
650 if (sub) {
651 clearerr(newfp);
652 fclose(newfp);
653 }
654 unlink(newnewsrc);
655 } else
656 rename_file(newnewsrc, newsrc);
657 }
658
659 for (i = 0; i < selmenu.max; i++)
660 set_default_bitmap(&active[my_group[i]]);
661 }
662
663
664 /*
665 * Rewrite the newsrc file, without the specified group
666 */
667 void
delete_group(char * group)668 delete_group(
669 char *group)
670 {
671 FILE *fp;
672 FILE *newfp;
673 char *line;
674 char *seq;
675 int sub;
676
677 if (no_write)
678 return;
679
680 if ((newfp = fopen(newnewsrc, "w")) != NULL) {
681 if (newsrc_mode)
682 #ifdef HAVE_FCHMOD
683 fchmod(fileno(newfp), newsrc_mode);
684 #else
685 # ifdef HAVE_CHMOD
686 chmod(newnewsrc, newsrc_mode);
687 # endif /* HAVE_CHMOD */
688 #endif /* HAVE_FCHMOD */
689
690 if ((fp = fopen(newsrc, "r")) != NULL) {
691 while ((line = tin_fgets(fp, FALSE)) != NULL) {
692 if ((seq = parse_newsrc_line(line, &sub))) {
693 if (!STRCMPEQ(line, group))
694 fprintf(newfp, "%s%c %s\n", line, sub, seq);
695 }
696 }
697 fclose(fp);
698 }
699
700 if ((sub = ferror(newfp)) || fclose(newfp)) {
701 error_message(2, _(txt_filesystem_full), NEWSRC_FILE);
702 if (sub) {
703 clearerr(newfp);
704 fclose(newfp);
705 }
706 unlink(newnewsrc);
707 } else
708 rename_file(newnewsrc, newsrc);
709 }
710 }
711
712
713 /*
714 * Mark a group as read
715 * If art != NULL then we explicitly process each article thus
716 * catching crossposts as well, otherwise we simply scrub the
717 * bitmap and adjust the highwater mark.
718 */
719 void
grp_mark_read(struct t_group * group,struct t_article * art)720 grp_mark_read(
721 struct t_group *group,
722 struct t_article *art)
723 {
724 int i;
725
726 #ifdef DEBUG
727 if (debug & DEBUG_NEWSRC)
728 debug_print_comment("c/C command");
729 #endif /* DEBUG */
730
731 if (art != NULL) {
732 for_each_art(i)
733 art_mark(group, &art[i], ART_READ);
734 } else {
735 FreeAndNull(group->newsrc.xbitmap);
736 group->newsrc.xbitlen = 0;
737 if (group->xmax > group->newsrc.xmax)
738 group->newsrc.xmax = group->xmax;
739 group->newsrc.xmin = group->newsrc.xmax + 1;
740 group->newsrc.num_unread = 0;
741 }
742 }
743
744
745 void
grp_mark_unread(struct t_group * group)746 grp_mark_unread(
747 struct t_group *group)
748 {
749 t_artnum bitlength;
750 t_bitmap *newbitmap = (t_bitmap *) 0;
751
752 #ifdef DEBUG
753 if (debug & DEBUG_NEWSRC)
754 debug_print_comment("Z command");
755 #endif /* DEBUG */
756
757 group_get_art_info(group->spooldir, group->name, group->type, &group->count, &group->xmax, &group->xmin);
758
759 group->newsrc.num_unread = group->count;
760 if (group->xmax > group->newsrc.xmax)
761 group->newsrc.xmax = group->xmax;
762 if (group->xmin > 0)
763 group->newsrc.xmin = group->xmin;
764
765 bitlength = MAX(0, group->newsrc.xmax - group->newsrc.xmin + 1);
766
767 if (bitlength > 0)
768 newbitmap = my_malloc(BITS_TO_BYTES(bitlength));
769
770 FreeIfNeeded(group->newsrc.xbitmap);
771 group->newsrc.xbitmap = newbitmap;
772 group->newsrc.xbitlen = bitlength;
773
774 if (bitlength)
775 NSETRNG1(group->newsrc.xbitmap, T_ARTNUM_CONST(0), bitlength - T_ARTNUM_CONST(1));
776
777 #ifdef DEBUG
778 if (debug & DEBUG_NEWSRC)
779 debug_print_bitmap(group, NULL);
780 #endif /* DEBUG */
781 }
782
783
784 void
thd_mark_read(struct t_group * group,long thread)785 thd_mark_read(
786 struct t_group *group,
787 long thread)
788 {
789 int i;
790
791 #ifdef DEBUG
792 if (debug & DEBUG_NEWSRC)
793 debug_print_comment("Mark thread read K command");
794 #endif /* DEBUG */
795
796 for (i = (int) thread; i >= 0; i = arts[i].thread)
797 art_mark(group, &arts[i], ART_READ);
798 }
799
800
801 void
thd_mark_unread(struct t_group * group,long thread)802 thd_mark_unread(
803 struct t_group *group,
804 long thread)
805 {
806 int i;
807
808 #ifdef DEBUG
809 if (debug & DEBUG_NEWSRC)
810 debug_print_comment("Mark thread unread Z command");
811 #endif /* DEBUG */
812
813 for (i = (int) thread; i >= 0; i = arts[i].thread)
814 art_mark(group, &arts[i], ART_WILL_RETURN);
815 }
816
817
818 /*
819 * Parse the newsrc sequence for the specified group
820 */
821 static void
parse_bitmap_seq(struct t_group * group,char * seq)822 parse_bitmap_seq(
823 struct t_group *group,
824 char *seq)
825 {
826 char *ptr;
827 t_artnum sum = T_ARTNUM_CONST(0);
828 t_artnum low = T_ARTNUM_CONST(0);
829 t_artnum high = T_ARTNUM_CONST(0);
830 t_artnum min, max;
831 t_bool gotseq = FALSE;
832
833 /*
834 * Skip possible non-numeric prefix
835 */
836 ptr = seq;
837 while (ptr && *ptr && (*ptr < '0' || *ptr > '9'))
838 ptr++;
839
840 #ifdef DEBUG
841 if (debug & DEBUG_NEWSRC) {
842 char buf[NEWSRC_LINE];
843
844 snprintf(buf, sizeof(buf), "Parsing [%s%c %.*s]", group->name, SUB_CHAR(group->subscribed), (int) (NEWSRC_LINE - strlen(group->name) - 14), BlankIfNull(ptr));
845 debug_print_comment(buf);
846 debug_print_bitmap(group, NULL);
847 }
848 #endif /* DEBUG */
849
850 if (ptr) {
851 gotseq = TRUE;
852 ptr = parse_get_seq(ptr, &low, &high);
853
854 if (high < group->xmin - 1)
855 high = group->xmin - 1;
856
857 min = ((low <= 1) ? (high + 1) : 1);
858
859 if (group->xmin > min)
860 min = group->xmin;
861
862 if (group->xmax > high)
863 max = group->xmax;
864 else
865 max = high; /* trust newsrc's max */
866
867 FreeAndNull(group->newsrc.xbitmap);
868 group->newsrc.xmax = max;
869 group->newsrc.xmin = min;
870 group->newsrc.xbitlen = (max - min) + 1;
871 if (group->newsrc.xbitlen > 0) {
872 group->newsrc.xbitmap = my_malloc(BITS_TO_BYTES(group->newsrc.xbitlen));
873 NSETRNG1(group->newsrc.xbitmap, T_ARTNUM_CONST(0), group->newsrc.xbitlen - T_ARTNUM_CONST(1));
874 }
875
876 if (min <= high) {
877 if (low > min)
878 sum = low - min;
879 else
880 low = min;
881 NSETRNG0(group->newsrc.xbitmap, low - min, high - min);
882 }
883
884 /*
885 * Pick up any additional articles/ranges after the first
886 */
887 while (*ptr)
888 ptr = parse_subseq(group, ptr, &low, &high, &sum);
889 } else {
890 FreeAndNull(group->newsrc.xbitmap);
891 group->newsrc.xmax = group->xmax;
892 if (group->xmin > 0)
893 group->newsrc.xmin = group->xmin;
894 else
895 group->newsrc.xmin = 1;
896 group->newsrc.xbitlen = (group->newsrc.xmax - group->newsrc.xmin) + 1;
897 if (group->newsrc.xbitlen > 0) {
898 group->newsrc.xbitmap = my_malloc(BITS_TO_BYTES(group->newsrc.xbitlen));
899 NSETRNG1(group->newsrc.xbitmap, T_ARTNUM_CONST(0), group->newsrc.xbitlen - T_ARTNUM_CONST(1));
900 }
901 /*
902 wait_message(2, "BITMAP Grp=[%s] MinMax=[%"T_ARTNUM_PFMT"-%"T_ARTNUM_PFMT"] Len=[%"T_ARTNUM_PFMT"]\n",
903 group->name, group->xmin, group->xmax, group->newsrc.xbitlen);
904 */
905 }
906
907 group->newsrc.present = TRUE;
908
909 if (gotseq) {
910 if (group->newsrc.xmax > high)
911 sum += group->newsrc.xmax - high;
912 } else
913 sum = (group->count >= 0) ? group->count : ((group->newsrc.xmax - group->newsrc.xmin) + 1);
914
915 group->newsrc.num_unread = sum;
916 #ifdef DEBUG
917 if (debug & DEBUG_NEWSRC)
918 debug_print_bitmap(group, NULL);
919 #endif /* DEBUG */
920 }
921
922
923 /*
924 * Parse a subsection of the newsrc sequencer ie., 1-34,23-90,93,97-99
925 * would parse the sequence if called in a loop in the following way:
926 * 1st call would parse 1-34 and return 23-90,93,97-99
927 * 2nd call would parse 23-90 and return 93,97-99
928 * 3rd call would parse 93 and return 97-99
929 * 4th call would parse 97-99 and return NULL
930 */
931 static char *
parse_subseq(struct t_group * group,char * seq,t_artnum * low,t_artnum * high,t_artnum * sum)932 parse_subseq(
933 struct t_group *group,
934 char *seq,
935 t_artnum *low,
936 t_artnum *high,
937 t_artnum *sum)
938 {
939 t_artnum bitmin;
940 t_artnum bitmax;
941 t_artnum last_high = *high;
942
943 seq = parse_get_seq(seq, low, high);
944
945 /*
946 * Bitmap index
947 */
948 bitmin = *low - group->newsrc.xmin;
949
950 /*
951 * check that seq is not out of order
952 */
953 if (*low > last_high)
954 *sum += (*low - last_high) - 1;
955
956 if (*high == *low) {
957 if (bitmin >= 0) {
958 if (*high > group->newsrc.xmax) {
959 /* We trust .newsrc's max. */
960 t_artnum bitlen;
961 t_bitmap *newbitmap;
962
963 group->newsrc.xmax = *high;
964 bitlen = group->newsrc.xmax - group->newsrc.xmin + 1;
965 newbitmap = my_malloc(BITS_TO_BYTES(bitlen));
966
967 /* Copy over old bitmap */
968 memcpy(newbitmap, group->newsrc.xbitmap, BITS_TO_BYTES(group->newsrc.xbitlen));
969
970 /* Mark high numbered articles as unread */
971 NSETRNG1(newbitmap, group->newsrc.xbitlen, bitlen - 1);
972
973 free(group->newsrc.xbitmap);
974 group->newsrc.xbitmap = newbitmap;
975 group->newsrc.xbitlen = bitlen;
976 }
977 NSET0(group->newsrc.xbitmap, bitmin);
978 }
979 } else if ((*low < *high) && (*high >= group->newsrc.xmin)) {
980 /*
981 * Restrict the range to min..max
982 */
983 if (bitmin < 0)
984 bitmin = 0;
985
986 bitmax = *high;
987
988 if (bitmax > group->newsrc.xmax) {
989 /* We trust .newsrc's max. */
990 t_artnum bitlen;
991 t_bitmap *newbitmap;
992
993 group->newsrc.xmax = bitmax;
994 bitlen = group->newsrc.xmax - group->newsrc.xmin + 1;
995 newbitmap = my_malloc(BITS_TO_BYTES(bitlen));
996
997 /* Copy over old bitmap */
998 memcpy(newbitmap, group->newsrc.xbitmap, BITS_TO_BYTES(group->newsrc.xbitlen));
999
1000 /* Mark high numbered articles as unread */
1001 NSETRNG1(newbitmap, group->newsrc.xbitlen, bitlen - 1);
1002
1003 free(group->newsrc.xbitmap);
1004 group->newsrc.xbitmap = newbitmap;
1005 group->newsrc.xbitlen = bitlen;
1006 }
1007
1008 bitmax -= group->newsrc.xmin;
1009
1010 /*
1011 * Fill in the whole range as read
1012 */
1013 NSETRNG0(group->newsrc.xbitmap, bitmin, bitmax);
1014 }
1015 return seq;
1016 }
1017
1018
1019 static char *
parse_get_seq(char * seq,t_artnum * low,t_artnum * high)1020 parse_get_seq(
1021 char *seq,
1022 t_artnum *low,
1023 t_artnum *high)
1024 {
1025 *low = strtoartnum(seq, &seq, 10);
1026
1027 if (*seq == '-') { /* Range of articles */
1028 seq++;
1029 *high = strtoartnum(seq, &seq, 10);
1030 } else /* Single article */
1031 *high = *low;
1032
1033 while (*seq && (*seq < '0' || *seq > '9'))
1034 seq++;
1035
1036 return seq;
1037 }
1038
1039
1040 /*
1041 * Loop through arts[] array marking state of each article READ/UNREAD
1042 */
1043 void
parse_unread_arts(struct t_group * group,t_artnum min)1044 parse_unread_arts(
1045 struct t_group *group,
1046 t_artnum min)
1047 {
1048 int i;
1049 t_artnum unread = T_ARTNUM_CONST(0);
1050 t_artnum bitmin, bitmax;
1051 t_bitmap *newbitmap = (t_bitmap *) 0;
1052
1053 bitmin = group->newsrc.xmin;
1054 bitmax = group->newsrc.xmax;
1055
1056 /*
1057 * TODO
1058 * what about group->newsrc.xmax > group->xmax?
1059 * that should indicate an artnum 'reset' on the server
1060 * (or using the "wrong" newsrc for that server)
1061 */
1062
1063 if (group->xmax > group->newsrc.xmax)
1064 group->newsrc.xmax = group->xmax;
1065
1066 if (group->newsrc.xmax >= bitmin) {
1067 newbitmap = my_malloc(BITS_TO_BYTES(group->newsrc.xmax - bitmin + 1));
1068 NSETRNG0(newbitmap, T_ARTNUM_CONST(0), group->newsrc.xmax - bitmin);
1069 }
1070
1071 /*
1072 * if getart_limit > 0 preserve read/unread state
1073 * of all articles below the new minimum
1074 */
1075 if (min > 0 && newbitmap) {
1076 t_artnum j, tmp_bitmax;
1077
1078 tmp_bitmax = (bitmax < min) ? bitmax : min;
1079 for (j = bitmin; j < tmp_bitmax; j++) {
1080 if (NTEST(group->newsrc.xbitmap, j - bitmin) != ART_READ)
1081 NSET1(newbitmap, j - bitmin);
1082 }
1083 while (j < min) {
1084 NSET1(newbitmap, j - bitmin);
1085 j++;
1086 }
1087 }
1088
1089 for_each_art(i) {
1090 if (arts[i].artnum < bitmin)
1091 arts[i].status = ART_READ;
1092 else if (arts[i].artnum > bitmax)
1093 arts[i].status = ART_UNREAD;
1094 else if (NTEST(group->newsrc.xbitmap, arts[i].artnum - bitmin) == ART_READ)
1095 arts[i].status = ART_READ;
1096 else
1097 arts[i].status = ART_UNREAD;
1098
1099 /* TODO: logic correct? */
1100 if (newbitmap != NULL && arts[i].status == ART_UNREAD && arts[i].artnum >= bitmin) {
1101 #if 0
1102 /*
1103 * check for wrong article numbers in the overview
1104 *
1105 * TODO: check disabled as we currently catch the artnum > high_mark
1106 * case in read_overview() where we might be able to
1107 * fix the broken artnum (via xref:-parsing). currently
1108 * we just skip the art there.
1109 */
1110 if (arts[i].artnum <= group->xmax)
1111 #endif /* 0 */
1112 NSET1(newbitmap, arts[i].artnum - bitmin);
1113 unread++;
1114 }
1115 }
1116
1117 group->newsrc.xbitlen = group->newsrc.xmax - bitmin + 1;
1118
1119 FreeIfNeeded(group->newsrc.xbitmap);
1120
1121 group->newsrc.xbitmap = newbitmap;
1122 group->newsrc.num_unread = unread;
1123 }
1124
1125
1126 static void
print_bitmap_seq(FILE * fp,struct t_group * group)1127 print_bitmap_seq(
1128 FILE *fp,
1129 struct t_group *group)
1130 {
1131 t_artnum artnum;
1132 t_artnum i;
1133 t_bool flag = FALSE;
1134
1135 #ifdef DEBUG
1136 if (debug & DEBUG_NEWSRC) {
1137 debug_print_comment("print_bitmap_seq()");
1138 debug_print_bitmap(group, NULL);
1139 }
1140 #endif /* DEBUG */
1141
1142 if (group->count == 0 || group->xmin > group->xmax) {
1143 if (group->newsrc.xmax > 1)
1144 fprintf(fp, "1-%"T_ARTNUM_PFMT, group->newsrc.xmax);
1145
1146 fprintf(fp, "\n");
1147 fflush(fp);
1148 #ifdef DEBUG
1149 if (debug & DEBUG_NEWSRC)
1150 debug_print_comment("print_bitmap_seq(): group->count == 0");
1151 #endif /* DEBUG */
1152 return;
1153 }
1154
1155 i = group->newsrc.xmin;
1156 if (i <= group->newsrc.xmax) {
1157 forever {
1158 if (group->newsrc.xbitmap && NTEST(group->newsrc.xbitmap, i - group->newsrc.xmin) == ART_READ) {
1159 if (flag) {
1160 artnum = i;
1161 fprintf(fp, ",%"T_ARTNUM_PFMT, i);
1162 } else {
1163 artnum = 1;
1164 flag = TRUE;
1165 fprintf(fp, "1");
1166 }
1167 while (i < group->newsrc.xmax && NTEST(group->newsrc.xbitmap, (i + 1) - group->newsrc.xmin) == ART_READ)
1168 i++;
1169
1170 if (artnum != i)
1171 fprintf(fp, "-%"T_ARTNUM_PFMT, i);
1172
1173 } else if (!flag) {
1174 flag = TRUE;
1175 if (group->newsrc.xmin > 1) {
1176 fprintf(fp, "1");
1177
1178 if (group->newsrc.xmin > 2)
1179 fprintf(fp, "-%"T_ARTNUM_PFMT, group->newsrc.xmin - 1);
1180
1181 }
1182 }
1183 if (group->newsrc.xmax == i)
1184 break;
1185
1186 i++;
1187 }
1188 }
1189
1190 if (!flag && group->newsrc.xmin > 1) {
1191 fprintf(fp, "1");
1192
1193 if (group->newsrc.xmin > 2)
1194 fprintf(fp, "-%"T_ARTNUM_PFMT, group->newsrc.xmin - 1);
1195
1196 #ifdef DEBUG
1197 if (debug & DEBUG_NEWSRC)
1198 debug_print_comment("print_bitmap_seq(): !flag && group->newsrc.xmin > 1");
1199 #endif /* DEBUG */
1200 }
1201
1202 fprintf(fp, "\n");
1203 fflush(fp);
1204 }
1205
1206
1207 /*
1208 * rewrite .newsrc and position group at specified position
1209 */
1210 t_bool
pos_group_in_newsrc(struct t_group * group,int pos)1211 pos_group_in_newsrc(
1212 struct t_group *group,
1213 int pos)
1214 {
1215 FILE *fp_in = NULL, *fp_out = NULL;
1216 FILE *fp_sub = NULL, *fp_unsub = NULL;
1217 char *newsgroup = NULL;
1218 char *line;
1219 char filename[PATH_LEN];
1220 char sub[PATH_LEN];
1221 char unsub[PATH_LEN];
1222 int subscribed_pos = 1;
1223 int err;
1224 size_t group_len;
1225 t_bool found = FALSE;
1226 t_bool newnewsrc_created = FALSE;
1227 t_bool option_line = FALSE;
1228 t_bool repositioned = FALSE;
1229 t_bool ret_code = FALSE;
1230 t_bool sub_created = FALSE;
1231 t_bool unsub_created = FALSE;
1232 t_bool fs_error = FALSE;
1233
1234 if (no_write)
1235 goto rewrite_group_done;
1236
1237 if ((fp_in = fopen(newsrc, "r")) == NULL)
1238 goto rewrite_group_done;
1239
1240 if ((fp_out = fopen(newnewsrc, "w")) == NULL)
1241 goto rewrite_group_done;
1242
1243 newnewsrc_created = TRUE;
1244
1245 if (newsrc_mode)
1246 #ifdef HAVE_FCHMOD
1247 fchmod(fileno(fp_out), newsrc_mode);
1248 #else
1249 # ifdef HAVE_CHMOD
1250 chmod(newnewsrc, newsrc_mode);
1251 # endif /* HAVE_CHMOD */
1252 #endif /* HAVE_FCHMOD */
1253
1254 joinpath(filename, sizeof(filename), TMPDIR, ".subrc");
1255 snprintf(sub, sizeof(sub), "%s.%ld", filename, (long) process_id);
1256
1257 joinpath(filename, sizeof(filename), TMPDIR, ".unsubrc");
1258 snprintf(unsub, sizeof(unsub), "%s.%ld", filename, (long) process_id);
1259
1260 if ((fp_sub = fopen(sub, "w")) == NULL)
1261 goto rewrite_group_done;
1262
1263 sub_created = TRUE;
1264
1265 if ((fp_unsub = fopen(unsub, "w")) == NULL)
1266 goto rewrite_group_done;
1267
1268 unsub_created = TRUE;
1269
1270 /*
1271 * split newsrc into subscribed and unsubscribed to files
1272 */
1273 group_len = strlen(group->name);
1274
1275 while ((line = tin_fgets(fp_in, FALSE)) != NULL) {
1276 if (STRNCMPEQ(group->name, line, group_len) && line[group_len] == SUBSCRIBED) {
1277 FreeIfNeeded(newsgroup);
1278 newsgroup = my_strdup(line); /* Take a copy of this line */
1279 found = TRUE;
1280 continue;
1281 } else if (strchr(line, SUBSCRIBED) != NULL) {
1282 write_newsrc_line(fp_sub, line);
1283 } else if (strchr(line, UNSUBSCRIBED) != NULL) {
1284 write_newsrc_line(fp_unsub, line);
1285 } else { /* options line at beginning of .newsrc */
1286 fprintf(fp_sub, "%s\n", line);
1287 option_line = TRUE;
1288 }
1289 }
1290
1291 if ((err = ferror(fp_sub)) || fclose(fp_sub)) {
1292 error_message(2, _(txt_filesystem_full), NEWSRC_FILE);
1293 if (err) {
1294 clearerr(fp_sub);
1295 fclose(fp_sub);
1296 }
1297 fs_error = TRUE;
1298 }
1299 if ((err = ferror(fp_unsub)) || fclose(fp_unsub)) {
1300 if (!fs_error) /* avoid repeatd error message */
1301 error_message(2, _(txt_filesystem_full), NEWSRC_FILE);
1302 if (err) {
1303 clearerr(fp_unsub);
1304 fclose(fp_unsub);
1305 }
1306 fs_error = TRUE;
1307 }
1308 fp_sub = fp_unsub = NULL;
1309
1310 if (fs_error)
1311 goto rewrite_group_done;
1312
1313 fclose(fp_in);
1314 fp_in = NULL;
1315
1316 /*
1317 * The group to be moved cannot be found, so give up now
1318 */
1319 if (!found)
1320 goto rewrite_group_done;
1321
1322 /*
1323 * write subscribed groups & repositioned group to newnewsrc
1324 */
1325 if ((fp_sub = fopen(sub, "r")) == NULL)
1326 goto rewrite_group_done;
1327
1328 while ((line = tin_fgets(fp_sub, FALSE)) != NULL) {
1329 if (option_line) {
1330 if (strchr(line, SUBSCRIBED) == NULL && strchr(line, UNSUBSCRIBED) == NULL) {
1331 fprintf(fp_out, "%s\n", line);
1332 continue;
1333 } else
1334 option_line = FALSE;
1335 }
1336
1337 if (pos == subscribed_pos) {
1338 write_newsrc_line(fp_out, newsgroup);
1339 repositioned = TRUE;
1340 }
1341
1342 fprintf(fp_out, "%s\n", line);
1343
1344 subscribed_pos++;
1345 }
1346
1347 if (!repositioned)
1348 write_newsrc_line(fp_out, newsgroup);
1349
1350 /*
1351 * append unsubscribed groups file to newnewsrc
1352 */
1353 if ((fp_unsub = fopen(unsub, "r")) == NULL)
1354 goto rewrite_group_done;
1355
1356 while ((line = tin_fgets(fp_unsub, FALSE)) != NULL)
1357 fprintf(fp_out, "%s\n", line);
1358
1359 /*
1360 * Try and cleanly close out the newnewsrc file
1361 */
1362 if ((err = ferror(fp_out)) || fclose(fp_out)) {
1363 error_message(2, _(txt_filesystem_full), NEWSRC_FILE);
1364 if (err) {
1365 clearerr(fp_out);
1366 fclose(fp_out);
1367 }
1368 } else {
1369 rename_file(newnewsrc, newsrc);
1370 ret_code = TRUE;
1371 }
1372 fp_out = NULL;
1373 newnewsrc_created = FALSE;
1374
1375 rewrite_group_done:
1376 if (fp_in != NULL)
1377 fclose(fp_in);
1378
1379 if (fp_out != NULL)
1380 fclose(fp_out);
1381
1382 if (fp_sub != NULL)
1383 fclose(fp_sub);
1384
1385 if (fp_unsub != NULL)
1386 fclose(fp_unsub);
1387
1388 if (newnewsrc_created)
1389 unlink(newnewsrc);
1390
1391 if (sub_created)
1392 unlink(sub);
1393
1394 if (unsub_created)
1395 unlink(unsub);
1396
1397 FreeIfNeeded(newsgroup);
1398
1399 return ret_code;
1400 }
1401
1402
1403 /*
1404 * catchup all groups in .newsrc
1405 */
1406 void
catchup_newsrc_file(void)1407 catchup_newsrc_file(
1408 void)
1409 {
1410 int i;
1411 struct t_group *group;
1412
1413 for (i = 0; i < selmenu.max; i++) {
1414 group = &active[my_group[i]];
1415 group->newsrc.present = TRUE;
1416 FreeAndNull(group->newsrc.xbitmap);
1417 if (group->xmax > group->newsrc.xmax)
1418 group->newsrc.xmax = group->xmax;
1419 group->newsrc.xmin = group->newsrc.xmax + 1;
1420 group->newsrc.num_unread = 0;
1421 group->newsrc.xbitlen = 0;
1422 }
1423 }
1424
1425
1426 /*
1427 * Break down a line of .newsrc file
1428 * The sequence information [ eg; 1-3,10,12 ] is returned, line is truncated to
1429 * just the group name and the subscription flag is copied to sub.
1430 */
1431 static char *
parse_newsrc_line(char * line,int * sub)1432 parse_newsrc_line(
1433 char *line,
1434 int *sub)
1435 {
1436 char *ptr, *tmp;
1437
1438 *sub = UNSUBSCRIBED; /* Default to no entry */
1439
1440 if ((ptr = strpbrk(line, "!:")) == NULL) /* space|SUBSCRIBED|UNSUBSCRIBED */
1441 return NULL;
1442
1443 *sub = *ptr; /* Save the subscription status */
1444 tmp = ptr; /* Keep this blank for later */
1445 *(ptr++) = '\0'; /* Terminate the group name */
1446
1447 #if 0
1448 if (ptr == NULL) /* No seq info, so return a blank */
1449 return tmp;
1450 #endif /* 0 */
1451
1452 if ((ptr = strpbrk(ptr, " \t")) == NULL)
1453 return tmp;
1454
1455 return (ptr + 1); /* Return pointer to sequence info. At worst this will be \0 */
1456 }
1457
1458
1459 /*
1460 * expand group->newsrc information if group->xmax is larger than
1461 * group->newsrc.xmax or min is smaller than group->newsrc.xmin.
1462 */
1463 void
expand_bitmap(struct t_group * group,t_artnum min)1464 expand_bitmap(
1465 struct t_group *group,
1466 t_artnum min)
1467 {
1468 t_artnum bitlen;
1469 t_artnum first;
1470 t_artnum tmp;
1471 t_artnum max;
1472 t_bool need_full_copy = FALSE;
1473
1474 /* calculate new max */
1475 if (group->newsrc.xmax > group->xmax)
1476 max = group->newsrc.xmax;
1477 else
1478 max = group->xmax;
1479
1480 /* adjust min */
1481 if (!min)
1482 min = group->newsrc.xmin;
1483
1484 /* calculate first */
1485 if (min >= group->newsrc.xmin)
1486 first = group->newsrc.xmin;
1487 else
1488 first = group->newsrc.xmin - ((group->newsrc.xmin - min + (NBITS - 1)) & ~(NBITS - 1));
1489
1490 /* adjust first */
1491 if (first > group->newsrc.xmax + 1)
1492 first = first - ((first - (group->newsrc.xmax + 1) + (NBITS - 1)) & ~(NBITS - 1));
1493
1494 /* check first */
1495 if (first < 1) {
1496 need_full_copy = TRUE;
1497 first = 1;
1498 }
1499
1500 bitlen = max - first + 1;
1501
1502 if (bitlen <= 0) {
1503 bitlen = 0;
1504 FreeIfNeeded(group->newsrc.xbitmap);
1505 group->newsrc.xbitmap = (t_bitmap *) 0;
1506 #ifdef DEBUG
1507 if (debug & DEBUG_NEWSRC)
1508 debug_print_comment("expand_bitmap: group->newsrc.bitlen == 0");
1509 #endif /* DEBUG */
1510 } else if (group->newsrc.xbitmap == NULL) {
1511 group->newsrc.xbitmap = my_malloc(BITS_TO_BYTES(bitlen));
1512 if (group->newsrc.xmin > first)
1513 NSETRNG0(group->newsrc.xbitmap, T_ARTNUM_CONST(0), group->newsrc.xmin - first - T_ARTNUM_CONST(1));
1514 if (bitlen > group->newsrc.xmin - first)
1515 NSETRNG1(group->newsrc.xbitmap, group->newsrc.xmin - first, bitlen - 1);
1516 #ifdef DEBUG
1517 if (debug & DEBUG_NEWSRC)
1518 debug_print_comment("expand_bitmap: group->newsrc.xbitmap == NULL");
1519 #endif /* DEBUG */
1520 } else if (need_full_copy) {
1521 t_bitmap *newbitmap = my_malloc(BITS_TO_BYTES(bitlen));
1522
1523 /* Copy over old bitmap */
1524 /* TODO: change to use shift */
1525 for (tmp = group->newsrc.xmin; tmp <= group->newsrc.xmax; tmp++) {
1526 if (NTEST(group->newsrc.xbitmap, tmp - group->newsrc.xmin) == ART_READ)
1527 NSET0(newbitmap, tmp - first);
1528 else
1529 NSET1(newbitmap, tmp - first);
1530 }
1531
1532 /* Mark earlier articles as read, updating num_unread */
1533
1534 if (first < group->newsrc.xmin) {
1535 NSETRNG0(newbitmap, T_ARTNUM_CONST(0), group->newsrc.xmin - first - T_ARTNUM_CONST(1));
1536 }
1537
1538 for (tmp = group->newsrc.xmin; tmp < min; tmp++) {
1539 if (NTEST(newbitmap, tmp - first) != ART_READ) {
1540 NSET0(newbitmap, tmp - first);
1541 if (group->newsrc.num_unread)
1542 group->newsrc.num_unread--;
1543 }
1544 }
1545
1546 /* Mark high numbered articles as unread */
1547
1548 if (group->newsrc.xmin - first + group->newsrc.xbitlen < bitlen) {
1549 tmp = group->newsrc.xmin - first + group->newsrc.xbitlen;
1550 NSETRNG1(newbitmap, tmp, bitlen - 1);
1551 }
1552
1553 free(group->newsrc.xbitmap);
1554 group->newsrc.xbitmap = newbitmap;
1555 #ifdef DEBUG
1556 if (debug & DEBUG_NEWSRC)
1557 debug_print_comment("expand_bitmap: group->newsrc.bitlen != (group->max-group->min)+1 and need full copy");
1558 #endif /* DEBUG */
1559 } else if (max != group->newsrc.xmax || first != group->newsrc.xmin) {
1560 t_bitmap *newbitmap;
1561 newbitmap = my_malloc(BITS_TO_BYTES(bitlen));
1562
1563 /* Copy over old bitmap */
1564
1565 assert((group->newsrc.xmin - first) / NBITS + BITS_TO_BYTES(group->newsrc.xbitlen) <= BITS_TO_BYTES(bitlen));
1566
1567 memcpy(newbitmap + (group->newsrc.xmin - first) / NBITS, group->newsrc.xbitmap, BITS_TO_BYTES(group->newsrc.xbitlen));
1568
1569 /* Mark earlier articles as read, updating num_unread */
1570
1571 if (first < group->newsrc.xmin) {
1572 NSETRNG0(newbitmap, T_ARTNUM_CONST(0), group->newsrc.xmin - first - T_ARTNUM_CONST(1));
1573 }
1574
1575 for (tmp = group->newsrc.xmin; tmp < min; tmp++) {
1576 if (NTEST(newbitmap, tmp - first) != ART_READ) {
1577 NSET0(newbitmap, tmp - first);
1578 if (group->newsrc.num_unread)
1579 group->newsrc.num_unread--;
1580 }
1581 }
1582
1583 /* Mark high numbered articles as unread */
1584
1585 if (group->newsrc.xmin - first + group->newsrc.xbitlen < bitlen) {
1586 tmp = group->newsrc.xmin - first + group->newsrc.xbitlen;
1587 NSETRNG1(newbitmap, tmp, bitlen - 1);
1588 }
1589
1590 free(group->newsrc.xbitmap);
1591 group->newsrc.xbitmap = newbitmap;
1592 #ifdef DEBUG
1593 if (debug & DEBUG_NEWSRC)
1594 debug_print_comment("expand_bitmap: group->newsrc.bitlen != (group->max-group->min)+1");
1595 #endif /* DEBUG */
1596 }
1597 group->newsrc.xmin = first;
1598 if (group->newsrc.xmax < max)
1599 group->newsrc.num_unread += max - group->newsrc.xmax;
1600 group->newsrc.xmax = max;
1601 group->newsrc.xbitlen = bitlen;
1602 group->newsrc.present = TRUE;
1603 }
1604
1605
1606 void
art_mark(struct t_group * group,struct t_article * art,int flag)1607 art_mark(
1608 struct t_group *group,
1609 struct t_article *art,
1610 int flag)
1611 {
1612 if (art == NULL)
1613 return;
1614
1615 switch (flag) {
1616 case ART_READ:
1617 if (group != NULL) {
1618 if (art->artnum >= group->newsrc.xmin && art->artnum <= group->newsrc.xmax)
1619 NSET0(group->newsrc.xbitmap, art->artnum - group->newsrc.xmin);
1620 #ifdef DEBUG
1621 if (debug & DEBUG_NEWSRC)
1622 debug_print_bitmap(group, art);
1623 #endif /* DEBUG */
1624 }
1625 if ((art->status == ART_UNREAD) || (art->status == ART_WILL_RETURN)) {
1626 art_mark_xref_read(art);
1627
1628 if (group != NULL) {
1629 if (group->newsrc.num_unread)
1630 group->newsrc.num_unread--;
1631
1632 if (group->attribute->show_only_unread_arts)
1633 art->keep_in_base = TRUE;
1634 }
1635
1636 art->status = ART_READ;
1637 }
1638 break;
1639
1640 case ART_UNREAD:
1641 case ART_WILL_RETURN:
1642 if (art->status == ART_READ) {
1643 if (group != NULL) {
1644 group->newsrc.num_unread++;
1645
1646 if (group->attribute->show_only_unread_arts)
1647 art->keep_in_base = FALSE;
1648 }
1649
1650 art->status = flag;
1651 }
1652 if (group != NULL) {
1653 if (art->artnum < group->newsrc.xmin)
1654 expand_bitmap(group, art->artnum);
1655 else {
1656 NSET1(group->newsrc.xbitmap, art->artnum - group->newsrc.xmin);
1657 #ifdef DEBUG
1658 if (debug & DEBUG_NEWSRC)
1659 debug_print_bitmap(group, art);
1660 #endif /* DEBUG */
1661 }
1662 }
1663 break;
1664
1665 default:
1666 break;
1667 }
1668 }
1669
1670
1671 void
set_default_bitmap(struct t_group * group)1672 set_default_bitmap(
1673 struct t_group *group)
1674 {
1675 if (group != NULL) {
1676 group->newsrc.num_unread = 0;
1677 group->newsrc.present = FALSE;
1678
1679 FreeIfNeeded(group->newsrc.xbitmap);
1680
1681 group->newsrc.xbitmap = (t_bitmap *) 0;
1682 group->newsrc.xbitlen = 0;
1683 if (group->xmin > 0)
1684 group->newsrc.xmin = group->xmin;
1685 else
1686 group->newsrc.xmin = 1;
1687 group->newsrc.xmax = group->newsrc.xmin - 1;
1688 }
1689 }
1690