leafnode  1.12.0
About: Leafnode is a store & forward NNTP proxy for small (dialup) sites.
  Fossies Dox: leafnode-1.12.0.tar.xz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

activutil.c
Go to the documentation of this file.
1/*
2 libutil -- deal with active file
3
4 Written by Cornelius Krasel <krasel@wpxx02.toxi.uni-wuerzburg.de>.
5 Copyright 2000.
6 Reused some old code written by Arnt Gulbrandsen <agulbra@troll.no>,
7 copyright 1995, modified by (amongst others) Cornelius Krasel
8 <krasel@wpxx02.toxi.uni-wuerzburg.de>, Randolf Skerka
9 <Randolf.Skerka@gmx.de>, Kent Robotti <robotti@erols.com> and
10 Markus Enzenberger <enz@cip.physik.uni-muenchen.de>. Copyright
11 for the modifications 1997-1999.
12
13 Modified and copyright of the modifications by:
14 2001 - 2010 Matthias Andree <matthias.andree@gmx.de>
15 2002 Ralf Wildenhues <ralf.wildenhues@gmx.de>
16
17 See file COPYING for restrictions on the use of this software.
18*/
19
20#include "leafnode.h"
21#include "activutil.h"
22#include "ln_log.h"
23#include "mastring.h"
24
25#include <ctype.h>
26#include "system.h"
27#include <limits.h>
28#include <stdio.h>
29#include <string.h>
30#include <sys/stat.h>
31#include <stdlib.h>
32#include <string.h>
33#include <unistd.h>
34
35#ifdef CHECKGROUPORDER
36#include "ln_log.h"
37
38#endif /* CHECKGROUPORDER */
40struct newsgroup *active = NULL;
42struct newsgroup *oldactive = NULL;
43
44struct nglist {
46 struct nglist *next;
47};
48
49/* warning: this function does not do a deep copy: it does not copy
50 * name or description */
51void
52newsgroup_copy(struct newsgroup *d, const struct newsgroup *s)
53{
54 d->first = s->first;
55 d->last = s->last;
56 d->age = s->age;
57 d->name = s->name;
58 d->desc = s->desc;
59}
60
61int
62compactive(const void *a, const void *b)
63{
64 const struct newsgroup *la = (const struct newsgroup *)a;
65 const struct newsgroup *lb = (const struct newsgroup *)b;
66
67 return strcasecmp(la->name, lb->name);
68}
69
70static struct nglist *newgroup = NULL;
71
72/*
73 * insert a group into a list of groups
74 * if oldactive is set, keep old data of known groups
75 */
76void
77insertgroup(const char *name, long unsigned first,
78 long unsigned last, time_t age)
79{
80 struct nglist *l;
81 static struct nglist *lold;
82 struct newsgroup *g, *o;
83 char *desc = NULL;
84
85 g = findgroup(name);
86 if (g)
87 return;
88
89 if (*name == '.' || strstr(name, "..") || name[strlen(name)-1] == '.') {
90 ln_log(LNLOG_SWARNING, LNLOG_CTOP, "Warning: skipping group \"%s\", "
91 "invalid name (NULL component).", name);
92 return;
93 }
94
95 if (oldactivesize != 0 && oldactive != NULL) {
97 if (o) {
98 last = o->last;
99 first = o->first;
100 if (o->age) age = o->age;
101 if (o->desc) desc = critstrdup(o->desc, "insertgroup");
102 }
103 }
104
105 g = (struct newsgroup *)critmalloc(sizeof(struct newsgroup),
106 "Allocating space for new group");
107 g->name = critstrdup(name, "insertgroup");
108 g->first = first;
109 g->last = last;
110 g->age = age;
111 g->desc = desc;
112 l = (struct nglist *)critmalloc(sizeof(struct nglist),
113 "Allocating space for newsgroup list");
114 l->entry = g;
115 l->next = NULL;
116 if (newgroup == NULL)
117 newgroup = l;
118 else
119 lold->next = l;
120 lold = l;
121}
122
123void
124newgroupdesc(const char *groupname, const char *description)
125{
126 struct nglist *l = newgroup;
127
128 while(l) {
129 if (0 == strcasecmp(groupname, l->entry->name)) {
130 if (l->entry->desc)
131 free(l->entry->desc);
132 l->entry->desc = critstrdup(description, "newgroupdesc");
133 break;
134 }
135 l = l->next;
136 }
137}
138
139
140/*
141 * change description of newsgroup
142 */
143void
144changegroupdesc(const char *groupname, const char *description)
145{
146 struct newsgroup *ng;
147
148 if (groupname && description) {
149 ng = findgroup(groupname);
150 if (ng) {
151 if (ng->desc)
152 free(ng->desc);
153 ng->desc = critstrdup(description, "changegroupdesc");
154 }
155 }
156}
157
158/*
159 * merge nglist with active group, then free it
160 */
161void
163{
164 struct nglist *l, *la;
165 size_t count = 0;
166
167#ifdef CHECKGROUPORDER
168 checkgrouporder();
169#endif /* CHECKGROUPORDER */
170 l = newgroup;
171 while (l) {
172 count++;
173 l = l->next;
174 }
175
176 active = (struct newsgroup *)critrealloc((char *)active,
177 (1 + count +
178 activesize) *
179 sizeof(struct newsgroup),
180 "reallocating active");
181
182 l = newgroup;
183 count = activesize;
184 while (l) {
185 la = l;
186 newsgroup_copy(active + count, l->entry);
187 l = l->next;
188 count++;
189 free(la->entry);
190 free(la); /* clean up */
191 }
192 newgroup = NULL;
193 active[count].name = NULL;
194
195 activesize = count;
196 qsort(active, activesize, sizeof(struct newsgroup), &compactive);
198}
199
200#ifdef CHECKGROUPORDER
201void checkgrouporder(void) {
202 unsigned long i;
203 int s = 1;
204
205 for (i = 1; i < activesize; i++) {
206 if (compactive(&active[i-1], &active[i]) >= 0) {
207 ln_log(LNLOG_SERR, LNLOG_CTOP, "in-core active file misorder at pos. %lu: \"%s\" vs. \"%s\"", i-1, active[i-1].name, active[i].name);
208 break;
209 s = 0;
210 }
211 }
212}
213#endif /* CHECKGROUPORDER */
214
215/*
216 * finds a group by name
217 * expects the group list to be sorted in strcasecmp order
218 * does a binary search, recursively implemented
219 */
220static long
221helpfindgroup(struct newsgroup *act, const char *name, long low, long high)
222{
223 int result;
224 long newp;
225
226 if (low > high)
227 return -1;
228 newp = (high - low) / 2 + low;
229 result = strcasecmp(name, act[newp].name);
230 if (result == 0)
231 return newp;
232 else if (result < 0)
233 return helpfindgroup(act, name, low, newp - 1);
234 else
235 return helpfindgroup(act, name, newp + 1, high);
236}
237
238/*
239 * find a newsgroup in the active file
240 */
241struct newsgroup *
242xfindgroup(struct newsgroup *act, const char *name, unsigned long actsize)
243{
244 long i;
245
246 if (actsize > (unsigned long)LONG_MAX) {
247 syslog(LOG_CRIT, "xfindgroup: count %lu too large (max. %ld), aborting",
248 actsize, LONG_MAX);
249 abort();
250 }
251
252 i = helpfindgroup(act, name, 0, actsize - 1);
253 if (i < 0)
254 return NULL;
255 else
256 return (&act[i]);
257}
258
259struct newsgroup *
260findgroup(const char *name) {
261#ifdef CHECKGROUPORDER
262 checkgrouporder();
263#endif /* CHECKGROUPORDER */
265}
266
267/* write active file, returns 0 for success, -1 for failure */
268int
270{
271 FILE *a;
272 struct newsgroup *g;
273 mastr *c = mastr_new(PATH_MAX), *d;
274 int err;
275 size_t count = 0;
276
277 mastr_vcat(c, spooldir, "/leaf.node/groupinfo.new", NULL);
278 (void)unlink(mastr_str(c));
279 a = fopen(mastr_str(c), "w");
280 if (!a) {
281 ln_log(LNLOG_SERR, LNLOG_CTOP, "cannot open new groupinfo file \"%s\": %m", mastr_str(c));
282 mastr_delete(c);
283 return -1;
284 }
285
286 /* count members in array and sort it */
287 g = active;
288 err = 0;
289 if (g) {
290 while (g->name) {
291 count++;
292 g++;
293 }
294 qsort(active, count, sizeof(struct newsgroup), &compactive);
296
297 g = active;
298 while ((err != EOF) && g->name) {
299 if (strlen(g->name)) {
300 err = fputs(g->name, a);
301 if (err == EOF) break;
302 if (fprintf(a, " %lu %lu %lu ", g->last, g->first,
303 (unsigned long)g->age) < 0) {
304 err = EOF;
305 break;
306 }
307 if (err == EOF) break;
308 err = fputs(g->desc && *(g->desc) ? g->desc : "-x-", a);
309 if (err == EOF) break;
310 err = fputs("\n", a);
311 if (err == EOF) break;
312 }
313 g++;
314 }
315 }
316
317 if (fflush(a) || fsync(fileno(a)) || fclose(a))
318 err = EOF;
319
320 if (err == EOF) {
322 "failed writing new groupinfo file \"%s\": %m", mastr_str(c));
323 unlink(mastr_str(c));
324 mastr_delete(c);
325 return -1;
326 }
327
328 d = mastr_new(PATH_MAX);
329 mastr_vcat(d, spooldir, "/leaf.node/groupinfo", NULL);
330 if (rename(mastr_str(c), mastr_str(d))) {
331 ln_log(LNLOG_SERR, LNLOG_CTOP, "failed to rename new groupinfo \"%s\" file into proper place \"%s\": %m", mastr_str(c), mastr_str(d));
332 unlink(mastr_str(c));
333 mastr_delete(d);
334 mastr_delete(c);
335 return -1;
336 } else {
337 if (verbose) printf("wrote active file with %lu line%s\n",
338 (unsigned long)count, PLURAL(count));
339 syslog(LOG_INFO, "wrote active file with %lu line%s",
340 (unsigned long)count, PLURAL(count));
341 }
342 mastr_delete(d);
343 mastr_delete(c);
344 return 0;
345}
346
347/*
348 * free active list. Written by Lloyd Zusman
349 */
350void
352{
353 struct newsgroup *g;
354
355 if (act == NULL)
356 return;
357
358 g = act;
359 while (g->name) {
360 free(g->name);
361 if (g->desc)
362 free(g->desc);
363 g++;
364 }
365
366 free(act);
367}
368
369/*
370 * read active file into memory
371 */
372void
374{
375 char *buf;
376 /*@dependent@*/ char *p, *q, *r;
377 unsigned long lu;
378 off_t n;
379 struct stat st;
380 FILE *f;
381 /*@dependent@*/ struct newsgroup *g;
382 char s[SIZE_s+1];
383
384 if (active) {
386 active = NULL;
387 }
388
389 xsnprintf(s, SIZE_s, "%s/leaf.node/groupinfo", spooldir);
390 if ((f = fopen(s, "r")) != NULL) {
391 if (fstat(fileno(f), &st)) {
392 syslog(LOG_ERR, "can't stat %s: %m", s);
393 (void)fclose(f);
394 return;
395 }
396 if (!S_ISREG(st.st_mode)) {
397 syslog(LOG_ERR, "%s not a regular file", s);
398 (void)fclose(f);
399 return;
400 }
401 buf = critmalloc(st.st_size + 2, "Reading group info");
402 n = fread(buf, 1, st.st_size, f);
403 if ((off_t) n < st.st_size) {
404 syslog(LOG_ERR,
405 "Groupinfo file truncated while reading: %ld < %ld.",
406 (long)n, (long)st.st_size);
407 }
408 fclose(f);
409 } else {
410 int e = errno;
411 syslog(LOG_ERR, "unable to open %s: %m", s);
412 if (e == ENOENT)
413 return;
414 fprintf(stderr, "unable to open %s: %s, aborting", s, strerror(e));
415 exit(1);
416 }
417
418 n = ((off_t) n > st.st_size) ? st.st_size : (off_t) n;
419 /* to read truncated groupinfo files correctly */
420 buf[n] = '\n';
421 buf[n + 1] = '\0'; /* 0-terminate string */
422
423 /* delete spurious 0-bytes */
424 while ((p = (char *)memchr(buf, '\0', st.st_size)) != NULL)
425 *p = ' '; /* \n might be better, but produces more errors */
426
427 /* count lines = newsgroups */
428 activesize = 0;
429 p = buf;
430 while (p && *p && ((q = (char *)memchr(p, '\n', st.st_size)) != NULL)) {
431 activesize++;
432 p = q + 1;
433 }
434
435 active = (struct newsgroup *)critmalloc((1 + activesize) *
436 sizeof(struct newsgroup),
437 "allocating active");
438 g = active;
439
440 p = buf;
441 while (p && *p) {
442 q = strchr(p, '\n');
443 if (q) {
444 *q = '\0';
445 if (strlen(p) == 0) {
446 p = q + 1;
447 continue; /* skip blank lines */
448 }
449 }
450 r = strchr(p, ' ');
451 if (!q || !r) {
452 if (!q && r)
453 *r = '\0';
454 else if (q && !r)
455 *q = '\0';
456 else if (strlen(p) > 30) {
457 q = p + 30;
458 *q = '\0';
459 }
460 syslog(LOG_ERR,
461 "Groupinfo file possibly truncated or damaged: %s", p);
462 break;
463 }
464 *r++ = '\0';
465 *q++ = '\0';
466
467 g->name = critstrdup(p, "readactive");
468 if (sscanf(r, "%lu %lu %lu", &g->last, &g->first, &lu) != 3) {
469 syslog(LOG_ERR,
470 "Groupinfo file possibly truncated or damaged: %s", p);
471 break;
472 }
473 g->age = lu;
474 if (g->first == 0)
475 g->first = 1; /* pseudoarticle */
476 if (g->last == 0)
477 g->last = 1;
478
479 for (n = 0; n < 3; n++) { /* Skip the numbers */
480 r = strchr(r, ' ') + 1;
481 }
482 if (strcmp(r, "-x-") == 0)
483 g->desc = NULL;
484 else
485 g->desc = critstrdup(r, "readactive");
486
487 p = q; /* next record */
488 g++;
489 }
490 free(buf);
491 /* last record, to mark end of array */
492 g->name = NULL;
493 g->first = 0;
494 g->last = 0;
495 g->age = 0;
496 g->desc = NULL;
497
498 /* count member in the array - maybe there were some empty lines */
499 g = active;
500 activesize = 0;
501 while (g->name) {
502 g++;
503 activesize++;
504 }
505
506 /* sort the thing, just to be sure */
507 qsort(active, activesize, sizeof(struct newsgroup), &compactive);
509}
510
511/*
512 * fake active file if it cannot be retrieved from the server
513 */
514void
516{
517 DIR *d;
518 struct dirent *de;
519 DIR *ng;
520 struct dirent *nga;
521 long unsigned int i;
522 long unsigned first, last;
523 char *p;
524 char s[SIZE_s+1];
525 struct stat st;
526 time_t age;
527
528 killactiveread(); /* force reading active file regardless */
529 xsnprintf(s, SIZE_s, "%s/interesting.groups", spooldir);
530 d = opendir(s);
531 if (!d) {
532 syslog(LOG_ERR, "cannot open directory %s: %m", s);
533 return;
534 }
535
536 while ((de = readdir(d))) {
537 if (isalnum((unsigned char)*(de->d_name)) &&
538 chdirgroup(de->d_name, FALSE)) {
539 /* get first and last article from the directory. This is
540 * the most secure way to get to that information since the
541 * .overview files may be corrupt as well
542 * If the group doesn't exist, just ignore the active entry.
543 */
544
545 first = ULONG_MAX;
546 last = 0;
547
548 ng = opendir(".");
549 while ((nga = readdir(ng)) != NULL) {
550 if (isdigit((unsigned char)*(nga->d_name))) {
551 p = NULL;
552 i = strtoul(nga->d_name, &p, 10);
553 if (*p == '\0') {
554 if (i < first)
555 first = i;
556 if (i > last)
557 last = i;
558 }
559 }
560 }
561 if (first > last) {
562 first = 1;
563 last = 1;
564 }
565 closedir(ng);
566 if (debugmode)
567 syslog(LOG_DEBUG, "parsed directory %s: first %lu, last %lu",
568 de->d_name, first, last);
569 if (0 == stat(".", &st))
570 age = st.st_ctime;
571 else
572 age = 0;
573 insertgroup(de->d_name, first, last, age);
574 }
575 }
576 mergegroups();
577
578 closedir(d);
579}
580
581char *
583{
584 const char *a = "/active.read";
585 size_t l;
586 char *t = critmalloc((l = strlen(spooldir)) + strlen(a) + 1, "activeread");
587 strcpy(t, spooldir); /* RATS: ignore */
588 strcpy(t + l, a); /* RATS: ignore */
589 return t;
590}
591
592/* Set a mark that the active file needs to be refetched (as though
593 * fetchnews -f had been used) next time, by removing active.read file */
594int
596{
597 int rc = 0;
598 char *t = activeread();
599 if (unlink(t) && errno != ENOENT) {
601 "cannot delete %s: %m", t);
602 rc = -1;
603 }
604 free(t);
605 return rc;
606}
struct newsgroup * oldactive
Definition: activutil.c:42
static struct nglist * newgroup
Definition: activutil.c:70
int compactive(const void *a, const void *b)
Definition: activutil.c:62
void newsgroup_copy(struct newsgroup *d, const struct newsgroup *s)
Definition: activutil.c:52
char * activeread(void)
Definition: activutil.c:582
int killactiveread(void)
Definition: activutil.c:595
void readactive(void)
Definition: activutil.c:373
void fakeactive(void)
Definition: activutil.c:515
void freeactive(struct newsgroup *act)
Definition: activutil.c:351
void mergegroups(void)
Definition: activutil.c:162
struct newsgroup * findgroup(const char *name)
Definition: activutil.c:260
void changegroupdesc(const char *groupname, const char *description)
Definition: activutil.c:144
size_t activesize
Definition: activutil.c:39
void insertgroup(const char *name, long unsigned first, long unsigned last, time_t age)
Definition: activutil.c:77
struct newsgroup * xfindgroup(struct newsgroup *act, const char *name, unsigned long actsize)
Definition: activutil.c:242
int writeactive(void)
Definition: activutil.c:269
size_t oldactivesize
Definition: activutil.c:41
struct newsgroup * active
Definition: activutil.c:40
static long helpfindgroup(struct newsgroup *act, const char *name, long low, long high)
Definition: activutil.c:221
void newgroupdesc(const char *groupname, const char *description)
Definition: activutil.c:124
void validateactive(void)
int verbose
Definition: applyfilter.c:31
int debugmode
Definition: configutil.c:67
char * critrealloc(char *a, size_t size, const char *message)
Definition: critmem.c:79
char * critstrdup(const char *source, const char *message)
Definition: critmem.c:92
char * critmalloc(size_t size, const char *message)
Definition: critmem.c:61
static int age(const char *date)
Definition: fetchnews.c:184
#define PLURAL(no)
Definition: leafnode.h:35
const char * spooldir
#define SIZE_s
Definition: leafnode.h:287
#define FALSE
Definition: leafnode.h:32
int xsnprintf(char *str, size_t n, const char *format,...)
Definition: miscutil.c:798
#define PATH_MAX
Definition: leafnode.h:43
int chdirgroup(const char *group, int creatdir)
Definition: miscutil.c:446
void ln_log(int sev, int ctx, const char *format,...)
Definition: ln_log.c:103
#define LNLOG_SERR
Definition: ln_log.h:13
#define LNLOG_SWARNING
Definition: ln_log.h:14
#define LNLOG_CTOP
Definition: ln_log.h:22
void mastr_delete(mastr *m)
Definition: mastring.c:223
mastr * mastr_new(size_t size)
Definition: mastring.c:62
int mastr_vcat(mastr *m,...)
Definition: mastring.c:147
#define mastr_str(m)
Definition: mastring.h:57
const char * name
Definition: miscutil.c:126
Definition: mastring.h:28
unsigned long first
Definition: leafnode.h:122
char * desc
Definition: leafnode.h:125
char * name
Definition: leafnode.h:124
time_t age
Definition: leafnode.h:126
unsigned long last
Definition: leafnode.h:123
struct newsgroup * entry
Definition: activutil.c:45
struct nglist * next
Definition: activutil.c:46
#define dirent
Definition: system.h:21
static int rc
Definition: xsnprintf.c:11