tin  2.4.4
About: TIN is a threaded NNTP and spool based UseNet newsreader.
  Fossies Dox: tin-2.4.4.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-2020 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  */
51 static FILE *open_overview_fmt_fp(void);
52 
55 
56 /*
57  * Open the NEWSLIBDIR/overview.fmt file locally or send LIST OVERVIEW.FMT
58  */
59 static 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  */
81 t_bool
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 advertized - using safe defaults");
278 #endif /* DEBUG */
279  ofmt = my_realloc(ofmt, sizeof(struct t_overview_fmt) * (8 + 1));
280  ofmt[1].type = OVER_T_STRING;
281  ofmt[1].name = my_strdup("Subject:");
282  ofmt[2].type = OVER_T_STRING;
283  ofmt[2].name = my_strdup("From:");
284  ofmt[3].type = OVER_T_STRING;
285  ofmt[3].name = my_strdup("Date:");
286  ofmt[4].type = OVER_T_STRING;
287  ofmt[4].name = my_strdup("Message-ID:");
288  ofmt[5].type = OVER_T_STRING;
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:");
294  ofmt[8].type = OVER_T_ERROR;
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  */
331 void
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  */
421 void
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  */
455 void
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 }
name
const char * name
Definition: signal.c:117
DEBUG_NNTP
#define DEBUG_NNTP
Definition: debug.h:47
expensive_over_parse
t_bool expensive_over_parse
Definition: xref.c:54
t_article
Definition: tin.h:1510
CAPABILITIES
@ CAPABILITIES
Definition: nntplib.h:171
strcasecmp
int strcasecmp(const char *p, const char *q)
Definition: string.c:468
_
#define _(Text)
Definition: tin.h:94
my_realloc
#define my_realloc(ptr, size)
Definition: tin.h:2198
t_overview_fmt
Definition: tin.h:2465
t_capabilities::over_cmd
const char * over_cmd
Definition: nntplib.h:210
my_strdup
char * my_strdup(const char *str)
Definition: string.c:133
t_group
Definition: tin.h:1772
NTEST
#define NTEST(n, b)
Definition: newsrc.h:122
t_capabilities::type
enum extension_type type
Definition: nntplib.h:187
open_overview_fmt_fp
static FILE * open_overview_fmt_fp(void)
Definition: xref.c:60
read_news_via_nntp
t_bool read_news_via_nntp
Definition: init.c:150
t_overview_fmt::name
char * name
Definition: tin.h:2466
OVER_T_FSTRING
@ OVER_T_FSTRING
Definition: nntplib.h:164
newsrc.h
art
static t_openartinfo * art
Definition: cook.c:78
wait_message
void wait_message(unsigned int sdelay, const char *fmt,...)
Definition: screen.c:133
NSETRNG1
void NSETRNG1(t_bitmap *bitmap, t_artnum low, t_artnum high)
Definition: xref.c:422
read_saved_news
t_bool read_saved_news
Definition: init.c:151
OK_GROUPS
#define OK_GROUPS
Definition: nntplib.h:96
fmt_string
char * fmt_string(const char *fmt,...)
Definition: string.c:1379
TIN_FCLOSE
#define TIN_FCLOSE(x)
Definition: tin.h:1037
tin.h
DEBUG_NEWSRC
#define DEBUG_NEWSRC
Definition: debug.h:50
BIT_OR
#define BIT_OR(n, b, mask)
Definition: newsrc.h:137
NBITNEG1
#define NBITNEG1
Definition: newsrc.h:116
group_find
struct t_group * group_find(const char *group_name, t_bool ignore_case)
Definition: list.c:154
forever
#define forever
Definition: tin.h:810
tin_fgets
char * tin_fgets(FILE *fp, t_bool header)
Definition: read.c:320
T_ARTNUM_PFMT
#define T_ARTNUM_PFMT
Definition: tin.h:227
t_newsrc::xbitmap
t_bitmap * xbitmap
Definition: tin.h:1766
nntp_caps
struct t_capabilities nntp_caps
Definition: init.c:516
OVER_T_STRING
@ OVER_T_STRING
Definition: nntplib.h:164
ART_READ
#define ART_READ
Definition: tin.h:1320
overviewfmt_file
char overviewfmt_file[PATH_LEN]
Definition: init.c:103
t_overview_fmt::type
enum f_type type
Definition: tin.h:2467
NOFFSET
#define NOFFSET(b)
Definition: newsrc.h:117
NSET0
#define NSET0(n, b)
Definition: newsrc.h:136
OVER_T_INT
@ OVER_T_INT
Definition: nntplib.h:164
NSET1
#define NSET1(n, b)
Definition: newsrc.h:135
NBITIDX
#define NBITIDX(b)
Definition: newsrc.h:118
NBITSON
#define NBITSON
Definition: newsrc.h:115
atoartnum
#define atoartnum
Definition: tin.h:230
t_newsrc::xmin
t_artnum xmin
Definition: tin.h:1764
t_capabilities::list_overview_fmt
t_bool list_overview_fmt
Definition: nntplib.h:199
overview_xref_support
t_bool overview_xref_support(void)
Definition: xref.c:82
t_group::xmax
t_artnum xmax
Definition: tin.h:1779
t_bitmap
unsigned char t_bitmap
Definition: tin.h:1450
OVER_T_ERROR
@ OVER_T_ERROR
Definition: nntplib.h:164
txt_warn_xref_not_supported
constext txt_warn_xref_not_supported[]
Definition: lang.c:1146
t_group::name
char * name
Definition: tin.h:1773
t_group::newsrc
struct t_newsrc newsrc
Definition: tin.h:1789
FALSE
#define FALSE
Definition: bool.h:70
debug
unsigned short debug
Definition: debug.c:51
verbose
int verbose
Definition: init.c:153
t_artnum
long t_artnum
Definition: tin.h:226
error_message
void error_message(unsigned int sdelay, const char *fmt,...)
Definition: screen.c:184
art_mark_xref_read
void art_mark_xref_read(struct t_article *art)
Definition: xref.c:332
ofmt
struct t_overview_fmt * ofmt
Definition: xref.c:53
NSETRNG0
void NSETRNG0(t_bitmap *bitmap, t_artnum low, t_artnum high)
Definition: xref.c:456
t_bool
unsigned t_bool
Definition: bool.h:77
TRUE
#define TRUE
Definition: bool.h:74
t_newsrc::num_unread
t_artnum num_unread
Definition: tin.h:1762
BIT_AND
#define BIT_AND(n, b, mask)
Definition: newsrc.h:138