"Fossies" - the Fresh Open Source Software Archive 
Member "leafnode-1.12.0/activutil.c" (28 Dec 2021, 13756 Bytes) of package /linux/misc/leafnode-1.12.0.tar.xz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "activutil.c" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
1.11.12_vs_1.12.0.
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 */
39 size_t activesize;
40 struct newsgroup *active = NULL;
41 size_t oldactivesize;
42 struct newsgroup *oldactive = NULL;
43
44 struct nglist {
45 struct newsgroup *entry;
46 struct nglist *next;
47 };
48
49 /* warning: this function does not do a deep copy: it does not copy
50 * name or description */
51 void
52 newsgroup_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
61 int
62 compactive(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
70 static 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 */
76 void
77 insertgroup(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) {
96 o = xfindgroup(oldactive, name, oldactivesize);
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
123 void
124 newgroupdesc(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 */
143 void
144 changegroupdesc(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 */
161 void
162 mergegroups(void)
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);
197 validateactive();
198 }
199
200 #ifdef CHECKGROUPORDER
201 void 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 */
220 static long
221 helpfindgroup(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 */
241 struct newsgroup *
242 xfindgroup(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
259 struct newsgroup *
260 findgroup(const char *name) {
261 #ifdef CHECKGROUPORDER
262 checkgrouporder();
263 #endif /* CHECKGROUPORDER */
264 return xfindgroup(active, name, activesize);
265 }
266
267 /* write active file, returns 0 for success, -1 for failure */
268 int
269 writeactive(void)
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);
295 validateactive();
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) {
321 ln_log(LNLOG_SERR, LNLOG_CTOP,
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 */
350 void
351 freeactive(struct newsgroup *act)
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 */
372 void
373 readactive(void)
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) {
385 freeactive(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);
508 validateactive();
509 }
510
511 /*
512 * fake active file if it cannot be retrieved from the server
513 */
514 void
515 fakeactive(void)
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
581 char *
582 activeread(void)
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 */
594 int
595 killactiveread(void)
596 {
597 int rc = 0;
598 char *t = activeread();
599 if (unlink(t) && errno != ENOENT) {
600 ln_log(LNLOG_SERR, LNLOG_CTOP,
601 "cannot delete %s: %m", t);
602 rc = -1;
603 }
604 free(t);
605 return rc;
606 }