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)  

xref.c
Go to the documentation of this file.
1/*
2 * Project : tin - a Usenet reader
3 * Module : xref.c
4 * Author : I. Lea & H. Brugge
5 * Created : 1993-07-01
6 * Updated : 2019-09-11
7 * Notes :
8 *
9 * Copyright (c) 1993-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 NEWSRC_H
45# include "newsrc.h"
46#endif /* !NEWSRC_H */
47
48/*
49 * local prototypes
50 */
51static FILE *open_overview_fmt_fp(void);
52
55
56/*
57 * Open the NEWSLIBDIR/overview.fmt file locally or send LIST OVERVIEW.FMT
58 */
59static FILE *
61 void)
62{
63#ifdef NNTP_ABLE
65 if (!*nntp_caps.over_cmd)
66 return (FILE *) 0;
67
69 return (nntp_command("LIST OVERVIEW.FMT", OK_GROUPS, NULL, 0));
70
71 return (FILE *) 0;
72 }
73#endif /* NNTP_ABLE */
74 return (fopen(overviewfmt_file, "r"));
75}
76
77
78/*
79 * Read overview.fmt file to check if Xref:full is enabled/disabled
80 */
83 void)
84{
85 FILE *fp;
86 char *ptr, *p;
87 t_bool supported = FALSE;
88 size_t res_fields = 9; /* initial number of overview fields */
89 size_t fields = 0;
90 size_t i;
91
92 ofmt[0].type = OVER_T_INT;
93 ofmt[0].name = my_strdup("Artnum:");
94
95 if ((fp = open_overview_fmt_fp()) != NULL) {
96 while ((ptr = tin_fgets(fp, FALSE)) != NULL) {
97 if (*ptr == '#' || *ptr == '\n') /* skip comments and empty lines */
98 continue;
99
100#if defined(DEBUG) && defined(NNTP_ABLE)
101 if (debug & DEBUG_NNTP)
102 debug_print_file("NNTP", "<<<%s%s", logtime(), ptr);
103#endif /* DEBUG && NNTP_ABLE */
104
105 fields++;
106
107 /* expand overview fmt array */
108 if (fields >= res_fields) {
109 res_fields <<= 1;
110 ofmt = my_realloc(ofmt, sizeof(struct t_overview_fmt) * res_fields);
111 }
112
113 if ((p = strchr(ptr, ':'))) {
114 if (p == ptr) { /* metadata items start with : */
115 /* currently there is only :lines ands :bytes reserved */
116 if (!strcasecmp(ptr, ":lines")) {
117 ofmt[fields].type = OVER_T_INT;
118 ofmt[fields].name = my_strdup("Lines:");
119 if (fields != 7) {
121#ifdef DEBUG
122 if ((debug & DEBUG_NNTP) && verbose > 1)
123 debug_print_file("NNTP", "OVERVIEW.FMT: %s at position %d expected %d", ptr, fields, 7);
124#endif /* DEBUG */
125 }
126 continue;
127 }
128
129 if (!strcasecmp(ptr, ":bytes")) {
130 ofmt[fields].type = OVER_T_INT;
131 ofmt[fields].name = my_strdup("Bytes:");
132 if (fields != 6) {
134#ifdef DEBUG
135 if ((debug & DEBUG_NNTP) && verbose > 1)
136 debug_print_file("NNTP", "OVERVIEW.FMT: %s at position %d expected %d", ptr, fields, 6);
137#endif /* DEBUG */
138 }
139 continue;
140 }
141 /* unknown metadata item */
142 }
143
144 /* non metadata items end with : or :full */
145 /* optional items require :full */
146 if (!strcasecmp(p, ":full")) {
147 ofmt[fields].type = OVER_T_FSTRING;
148 *(++p) = '\0';
149 ofmt[fields].name = my_strdup(ptr);
150 if (fields < 7) {
152#ifdef DEBUG
153 if ((debug & DEBUG_NNTP) && verbose > 1)
154 debug_print_file("NNTP", "OVERVIEW.FMT: %s at position %d expected > %d", ptr, fields, 7);
155#endif /* DEBUG */
156 }
157#ifdef DEBUG
158 else {
159 if ((debug & DEBUG_NNTP) && verbose > 1)
160 debug_print_file("NNTP", "OVERVIEW.FMT: %s at position %d", ptr, fields);
161 }
162#endif /* DEBUG */
163
164 continue;
165 }
166
167 /* mandatory items */
168 if (!strcasecmp(ptr, "Subject:")) {
169 ofmt[fields].type = OVER_T_STRING;
170 ofmt[fields].name = my_strdup(ptr);
171 if (fields != 1) {
173#ifdef DEBUG
174 if ((debug & DEBUG_NNTP) && verbose > 1)
175 debug_print_file("NNTP", "OVERVIEW.FMT: %s at position %d expected %d", ptr, fields, 1);
176#endif /* DEBUG */
177 }
178 continue;
179 }
180
181 if (!strcasecmp(ptr, "From:")) {
182 ofmt[fields].type = OVER_T_STRING;
183 ofmt[fields].name = my_strdup(ptr);
184 if (fields != 2) {
186#ifdef DEBUG
187 if ((debug & DEBUG_NNTP) && verbose > 1)
188 debug_print_file("NNTP", "OVERVIEW.FMT: %s at position %d expected %d", ptr, fields, 2);
189#endif /* DEBUG */
190 }
191 continue;
192 }
193
194 if (!strcasecmp(ptr, "Date:")) {
195 ofmt[fields].type = OVER_T_STRING;
196 ofmt[fields].name = my_strdup(ptr);
197 if (fields != 3) {
199#ifdef DEBUG
200 if ((debug & DEBUG_NNTP) && verbose > 1)
201 debug_print_file("NNTP", "OVERVIEW.FMT: %s at position %d expected %d", ptr, fields, 3);
202#endif /* DEBUG */
203 }
204 continue;
205 }
206
207 if (!strcasecmp(ptr, "Message-ID:")) {
208 ofmt[fields].type = OVER_T_STRING;
209 ofmt[fields].name = my_strdup(ptr);
210 if (fields != 4) {
212#ifdef DEBUG
213 if ((debug & DEBUG_NNTP) && verbose > 1)
214 debug_print_file("NNTP", "OVERVIEW.FMT: %s at position %d expected %d", ptr, fields, 4);
215#endif /* DEBUG */
216 }
217 continue;
218 }
219
220 if (!strcasecmp(ptr, "References:")) {
221 ofmt[fields].type = OVER_T_STRING;
222 ofmt[fields].name = my_strdup(ptr);
223 if (fields != 5) {
225#ifdef DEBUG
226 if ((debug & DEBUG_NNTP) && verbose > 1)
227 debug_print_file("NNTP", "OVERVIEW.FMT: %s at position %d expected %d", ptr, fields, 5);
228#endif /* DEBUG */
229 }
230 continue;
231 }
232
233 if (!strcasecmp(ptr, "Bytes:")) {
234 ofmt[fields].type = OVER_T_INT;
235 ofmt[fields].name = my_strdup(ptr);
236 if (fields != 6) {
238#ifdef DEBUG
239 if ((debug & DEBUG_NNTP) && verbose > 1)
240 debug_print_file("NNTP", "OVERVIEW.FMT: %s at position %d expected %d", ptr, fields, 6);
241#endif /* DEBUG */
242 }
243 continue;
244 }
245
246 if (!strcasecmp(ptr, "Lines:")) {
247 ofmt[fields].type = OVER_T_INT;
248 ofmt[fields].name = my_strdup(ptr);
249 if (fields != 7) {
251#ifdef DEBUG
252 if ((debug & DEBUG_NNTP) && verbose > 1)
253 debug_print_file("NNTP", "OVERVIEW.FMT: %s at position %d expected %d", ptr, fields, 7);
254#endif /* DEBUG */
255 }
256 continue;
257 }
258 }
259 /* bogus entry */
260 ofmt[fields].type = OVER_T_ERROR;
261 ofmt[fields].name = my_strdup(ptr);
262 }
263 TIN_FCLOSE(fp);
264 }
265
266 fields++;
267 /* resize */
268 ofmt = my_realloc(ofmt, sizeof(struct t_overview_fmt) * (fields + 1));
269
270 /* end marker */
271 ofmt[fields].type = OVER_T_ERROR;
272 ofmt[fields].name = NULL;
273
274 if (fields < 2) {
275#ifdef DEBUG
276 if ((debug & DEBUG_NNTP) && verbose > 1)
277 debug_print_file("NNTP", fp ? "OVERVIEW.FMT: Empty response - using safe defaults" : "OVERVIEW.FMT: not advertised - using safe defaults");
278#endif /* DEBUG */
279 ofmt = my_realloc(ofmt, sizeof(struct t_overview_fmt) * (8 + 1));
281 ofmt[1].name = my_strdup("Subject:");
283 ofmt[2].name = my_strdup("From:");
285 ofmt[3].name = my_strdup("Date:");
287 ofmt[4].name = my_strdup("Message-ID:");
289 ofmt[5].name = my_strdup("References:");
290 ofmt[6].type = OVER_T_INT;
291 ofmt[6].name = my_strdup("Bytes:");
292 ofmt[7].type = OVER_T_INT;
293 ofmt[7].name = my_strdup("Lines:");
295 ofmt[8].name = NULL;
296 fields = 8;
297 }
298
299 for (i = 0; i <= fields; i++) {
300 if (ofmt[i].type == OVER_T_FSTRING) {
301 if (!strcasecmp(ofmt[i].name, "Xref:"))
302 supported = TRUE;
303 }
304 }
305
306 /*
307 * If user aborted with 'q', then we continue regardless. If Xref was
308 * found, then fair enough. If not, tough. No real harm done
309 */
310 /*
311 * TODO: warning message is not correct
312 * - in the NNTP_ABLE but !read_news_via_nntp case when
313 * OVERVIEW.FMT-file wasn't found or didn't mention Xref:
314 * - if the used command is OVER instead of XOVER
315 * (use nntp_caps.over_cmd in txt_warn_xref_not_supported ?)
316 * - if the used command is HDR XREF instead of XHDR XREF
317 * (use nntp_caps.hdr_cmd in txt_warn_xref_not_supported ?)
318 * - if server doesn't mention XREF in LIST HEADERS
319 */
320 if (read_news_via_nntp && !supported)
322
323 return supported;
324}
325
326
327/*
328 * mark all other Xref: crossposted articles as read when one article read
329 * Xref: sitename newsgroup:artnum newsgroup:artnum [newsgroup:artnum ...]
330 */
331void
333 struct t_article *art)
334{
335 char *xref_ptr;
336 char *groupname;
337 char *ptr, c;
338 t_artnum artnum;
339 struct t_group *group;
340#ifdef DEBUG
341 char *debug_mesg;
342#endif /* DEBUG */
343
344 if (art->xref == NULL)
345 return;
346
347 xref_ptr = art->xref;
348
349 /*
350 * check sitename matches nodename of current machine (ignore for now!)
351 */
352 while (*xref_ptr != ' ' && *xref_ptr)
353 xref_ptr++;
354
355 /*
356 * tokenize each pair and update that newsgroup if it is in my_group[].
357 */
358 forever {
359 while (*xref_ptr == ' ')
360 xref_ptr++;
361
362 groupname = xref_ptr;
363 while (*xref_ptr != ':' && *xref_ptr)
364 xref_ptr++;
365
366 if (*xref_ptr != ':')
367 break;
368
369 ptr = xref_ptr++;
370 artnum = atoartnum(xref_ptr);
371 while (isdigit((int) *xref_ptr))
372 xref_ptr++;
373
374 if (&ptr[1] == xref_ptr)
375 break;
376
377 c = *ptr;
378 *ptr = '\0';
379 group = group_find(groupname, FALSE);
380
381#ifdef DEBUG
382 if (debug & DEBUG_NEWSRC) {
383 debug_mesg = fmt_string("LOOKUP Xref: [%s:%"T_ARTNUM_PFMT"] active=[%s] num_unread=[%"T_ARTNUM_PFMT"]",
384 groupname, artnum,
385 (group ? group->name : ""),
386 (group ? group->newsrc.num_unread : 0));
387 debug_print_comment(debug_mesg);
388 debug_print_bitmap(group, NULL);
389/* error_message(2, debug_mesg); */
390 free(debug_mesg);
391 }
392#endif /* DEBUG */
393
394 if (group && group->newsrc.xbitmap) {
395 if (artnum >= group->newsrc.xmin && artnum <= group->xmax) {
396 if (!((NTEST(group->newsrc.xbitmap, artnum - group->newsrc.xmin) == ART_READ) ? TRUE : FALSE)) {
397 NSET0(group->newsrc.xbitmap, artnum - group->newsrc.xmin);
398 if (group->newsrc.num_unread > 0)
399 group->newsrc.num_unread--;
400#ifdef DEBUG
401 if (debug & DEBUG_NEWSRC) {
402 debug_mesg = fmt_string("FOUND!Xref: [%s:%"T_ARTNUM_PFMT"] marked READ num_unread=[%"T_ARTNUM_PFMT"]",
403 groupname, artnum, group->newsrc.num_unread);
404 debug_print_comment(debug_mesg);
405 debug_print_bitmap(group, NULL);
406/* error_message(2, debug_mesg); */
407 free(debug_mesg);
408 }
409#endif /* DEBUG */
410 }
411 }
412 }
413 *ptr = c;
414 }
415}
416
417
418/*
419 * Set bits [low..high] of 'bitmap' to 1's
420 */
421void
423 t_bitmap *bitmap,
424 t_artnum low,
425 t_artnum high)
426{
427 t_artnum i;
428
429 if (bitmap == NULL) {
430#ifdef DEBUG
431 error_message(2, "NSETRNG1() failed. Bitmap == NULL");
432#endif /* DEBUG */
433 return;
434 }
435
436 if (high >= low) {
437 if (NOFFSET(high) == NOFFSET(low)) {
438 for (i = low; i <= high; i++) {
439 NSET1(bitmap, i);
440 }
441 } else {
442 BIT_OR(bitmap, low, (NBITSON << NBITIDX(low)));
443 if (NOFFSET(high) > NOFFSET(low) + 1)
444 memset(&bitmap[NOFFSET(low) + 1], NBITSON, (size_t) (NOFFSET(high) - NOFFSET(low) - 1));
445
446 BIT_OR(bitmap, high, ~ (NBITNEG1 << NBITIDX(high)));
447 }
448 }
449}
450
451
452/*
453 * Set bits [low..high] of 'bitmap' to 0's
454 */
455void
457 t_bitmap *bitmap,
458 t_artnum low,
459 t_artnum high)
460{
461 t_artnum i;
462
463 if (bitmap == NULL) {
464 error_message(2, "NSETRNG0() failed. Bitmap == NULL");
465 return;
466 }
467
468 if (high >= low) {
469 if (NOFFSET(high) == NOFFSET(low)) {
470 for (i = low; i <= high; i++) {
471 NSET0(bitmap, i);
472 }
473 } else {
474 BIT_AND(bitmap, low, ~(NBITSON << NBITIDX(low)));
475 if (NOFFSET(high) > NOFFSET(low) + 1)
476 memset(&bitmap[NOFFSET(low) + 1], 0, (size_t) (NOFFSET(high) - NOFFSET(low) - 1));
477
478 BIT_AND(bitmap, high, NBITNEG1 << NBITIDX(high));
479 }
480 }
481}
unsigned t_bool
Definition: bool.h:77
#define TRUE
Definition: bool.h:74
#define FALSE
Definition: bool.h:70
static t_openartinfo * art
Definition: cook.c:78
#define DEBUG_NEWSRC
Definition: debug.h:50
#define DEBUG_NNTP
Definition: debug.h:47
int verbose
Definition: init.c:154
struct t_capabilities nntp_caps
Definition: init.c:513
t_bool read_saved_news
Definition: init.c:152
constext txt_warn_xref_not_supported[]
Definition: lang.c:1159
char overviewfmt_file[PATH_LEN]
Definition: init.c:103
unsigned short debug
Definition: debug.c:51
t_bool read_news_via_nntp
Definition: init.c:151
#define NSET1(n, b)
Definition: newsrc.h:135
#define NSET0(n, b)
Definition: newsrc.h:136
#define BIT_AND(n, b, mask)
Definition: newsrc.h:138
#define NBITNEG1
Definition: newsrc.h:116
#define NTEST(n, b)
Definition: newsrc.h:122
#define NBITSON
Definition: newsrc.h:115
#define NBITIDX(b)
Definition: newsrc.h:118
#define BIT_OR(n, b, mask)
Definition: newsrc.h:137
#define NOFFSET(b)
Definition: newsrc.h:117
#define OK_GROUPS
Definition: nntplib.h:96
@ CAPABILITIES
Definition: nntplib.h:171
@ OVER_T_INT
Definition: nntplib.h:164
@ OVER_T_STRING
Definition: nntplib.h:164
@ OVER_T_ERROR
Definition: nntplib.h:164
@ OVER_T_FSTRING
Definition: nntplib.h:164
struct t_group * group_find(const char *group_name, t_bool ignore_case)
Definition: list.c:154
void error_message(unsigned int sdelay, const char *fmt,...)
Definition: screen.c:224
char * my_strdup(const char *str)
Definition: string.c:139
char * tin_fgets(FILE *fp, t_bool header)
Definition: read.c:317
void wait_message(unsigned int sdelay, const char *fmt,...)
Definition: screen.c:133
int strcasecmp(const char *p, const char *q)
Definition: string.c:475
char * fmt_string(const char *fmt,...)
Definition: string.c:1386
const char * name
Definition: signal.c:117
Definition: tin.h:1533
t_bool list_overview_fmt
Definition: nntplib.h:199
const char * over_cmd
Definition: nntplib.h:210
enum extension_type type
Definition: nntplib.h:187
Definition: tin.h:1816
t_artnum xmax
Definition: tin.h:1823
char * name
Definition: tin.h:1817
struct t_newsrc newsrc
Definition: tin.h:1833
t_bitmap * xbitmap
Definition: tin.h:1810
t_artnum num_unread
Definition: tin.h:1806
t_artnum xmin
Definition: tin.h:1808
enum f_type type
Definition: tin.h:2538
char * name
Definition: tin.h:2537
long t_artnum
Definition: tin.h:229
#define TIN_FCLOSE(x)
Definition: tin.h:1048
#define atoartnum
Definition: tin.h:233
#define _(Text)
Definition: tin.h:94
#define forever
Definition: tin.h:816
#define T_ARTNUM_PFMT
Definition: tin.h:230
unsigned char t_bitmap
Definition: tin.h:1475
#define ART_READ
Definition: tin.h:1345
#define my_realloc(ptr, size)
Definition: tin.h:2247
void art_mark_xref_read(struct t_article *art)
Definition: xref.c:332
void NSETRNG0(t_bitmap *bitmap, t_artnum low, t_artnum high)
Definition: xref.c:456
struct t_overview_fmt * ofmt
Definition: xref.c:53
t_bool overview_xref_support(void)
Definition: xref.c:82
void NSETRNG1(t_bitmap *bitmap, t_artnum low, t_artnum high)
Definition: xref.c:422
static FILE * open_overview_fmt_fp(void)
Definition: xref.c:60
t_bool expensive_over_parse
Definition: xref.c:54