/* * Copyright (c) 1998,1999,2000 Kazushi (Jam) Marukawa * All rights of my changes are reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice in the documentation and/or other materials provided with * the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* $Orig-Id: util.c,v 1.22 1997/07/23 18:35:18 agulbra Exp $ */ /* Written by Arnt Gulbrandsen and copyright 1995 Troll Tech AS, Postboks 6133 Etterstad, 0602 Oslo, Norway, fax +47 22646949. Use, modification and distribution is allowed without limitation, warranty, or liability of any kind. */ /* This code is derived from only leafnode+ by using same structure of Cornelius's leafnode to prepare for merging with Cornelius's code. */ #ifdef SOCKS #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "leafnode.h" struct newsgroup * active; /* insert or update a newsgroup in the global list */ void insertgroup(const char* name, unsigned long first, unsigned long last, const char* desc, unsigned long server) { struct newsgroup** a; int c; if (!desc) desc = ""; a = &active; while(a) { if (*a) { c = strcmp((*a)->name, name); if (c < 0) { a = &((*a)->left); } else if (c > 0) { a = &((*a)->right); } else { if (first >= 0) (*a)->first = first; if (last >= 0) (*a)->last = last; if (server >= 0) (*a)->server = server; if (desc && *desc) { /* This leaks memory if the new desc > old one, but I cannot see a way round this... */ if (strlen((*a)->desc) >= strlen(desc)) strcpy((*a)->desc, desc); else (*a)->desc = strdup(desc); } return; } } else { *a = (struct newsgroup *) critmalloc(sizeof(struct newsgroup) + 2 + strlen(name) + strlen(desc), "Building newsgroups info list"); if (!*a) return; (*a)->left = (*a)->right = NULL; (*a)->first = first; (*a)->last = last; (*a)->server = server; (*a)->alive = 0; (*a)->newarticles = 0; (*a)->name = (char*)(1 + *a); strcpy((*a)->name, name); (*a)->desc = strlen((*a)->name) + 1 + ((*a)->name); strcpy((*a)->desc, desc); return; } } } /* find a group by name */ struct newsgroup* findgroup(const char* name) { struct newsgroup* a; int c; a = active; while (a) { c = strcmp(a->name, name); if (c < 0) a = a->left; else if (c > 0) a = a->right; else return a; } return a; } static void freeactive(struct newsgroup* g) { if (g) { freeactive(g->right); freeactive(g->left); free((char*)g); } } static int lockfd = -1; void lockactive(void) { const char* s = getinfofname(); if ((lockfd = open(s, O_RDWR | O_CREAT, S_IWGRP|S_IWOTH)) < 0) { mysyslog(LOG_ERR, "can't open %s for lock: %s", s, strerror(errno)); exit(2); } #ifdef USE_LOCKF # ifdef NOWAIT_LOCK if (lockf(lockfd, F_TLOCK, (off_t)0)) # else if (lockf(lockfd, F_LOCK, (off_t)0)) # endif #else # ifdef NOWAIT_LOCK if (flock(lockfd, LOCK_EX | LOCK_NB)) # else if (flock(lockfd, LOCK_EX)) # endif #endif { mysyslog(LOG_ERR, "can't lock %s: %s", s, strerror(errno)); exit(2); } /* keep file open to hold a lock */ } void unlockactive(void) { if (lockfd >= 0) { close(lockfd); lockfd = -1; } } /* this uses a nifty technique for building a binary tree from a sorted list... the basic observation is that if you count from one end, with 1 being the rightmost node, then the number of nodes between N and the leaf nodes is equal to the number of zero bits at the small end of N. this tree probably doesn't make it obvious: 1 2 3 4 5 6 7 1 1 0 1 1 0 1 0 1 0 1 | | | | | | + no zeroes: leaf node | | | | | +-- one zero: one step up | | | | +---- no zeroes: leaf node | | | +------ two zeroes: two steps up | | +-------- no zeroes: leaf node | +---------- one zero: one step up +------------ no zeroes: leaf node this can be used to build a smallest-height tree, and with a bit more care, a _perfectly_ balanced tree */ void readactive(void) { char* p; char* q; unsigned long first, last, server; int fd; struct newsgroup* g; struct stat st; int line; struct newsgroup* levels[32]; /* theoretically finite size :) */ int l; static char* stuff; const char* s = getinfofname(); if (stuff) { freeactive(active); active = NULL; free((char*)stuff); stuff = NULL; } fd = open(s, O_RDONLY); if (stat(s, &st) < 0) { mysyslog(LOG_ERR, "can't stat %s: %s", s, strerror(errno)); return; } stuff = critmalloc(st.st_size + 1, "Reading group info"); if ((fd = open(s, O_RDONLY)) < 0 || read(fd, stuff, st.st_size) < st.st_size) { mysyslog(LOG_ERR, "can't open/read %s: %s", s, strerror(errno)); return; } else { close(fd); stuff[st.st_size] = '\0'; /* 0-terminate string */ } line = 1; for(l = 0; l < 32; levels[l++] = NULL) ; p = stuff; while (p && *p) { q = p; p = strchr(p, ' '); if (p && *p) { *p++ = '\0'; if (*q) { last = p ? strtoul(p, &p, 10) : 0; first = p ? strtoul(p, &p, 10) : 0; server = p ? strtoul(p, &p, 10) : 0; p = skipspaces(p); if (first < 1) first = 1; g = (struct newsgroup *)critmalloc(sizeof(struct newsgroup), "reading groupinfo"); g->right = g->left = NULL; g->first = first; g->last = last; g->server = server; g->alive = 0; g->newarticles = 0; g->name = q; g->desc = p; for (l = 0; (line & (1 << l)) == 0; l++) ; if (l) { g->right = levels[l - 1]; } if (active == g->right) active = g; if (l < 31 && levels[l + 1] && !levels[l + 1]->left) levels[l + 1]->left = g; levels[l] = g; p = strchr(p, '\n'); if (p) *p++ = '\0'; line++; } else { p = strchr(p, '\n'); if (p) p++; } } } g = NULL; for (l = 0; l < 31; l++) { if (levels[l]) { if (g && levels[l] && levels[l]->left == NULL && levels[l]->right != g) { levels[l]->left = g; g = NULL; } if (!levels[l + 1] || (levels[l] != levels[l + 1]->left && levels[l] != levels[l + 1]->right)) { if (g) mysyslog(LOG_ERR, "2+5=2"); g = levels[l]; } } } } static void helpwriteactive(struct newsgroup* g, FILE* f) { if (g) { helpwriteactive(g->right, f); fprintf(f, "%s %lu %lu %lu %s\n", g->name, g->last, g->first, 0LU, g->desc && *(g->desc) ? g->desc : "-x-"); helpwriteactive(g->left, f); } } void writeactive(void) { const char* s; FILE* a; char c[PATH_MAX]; s = getinfonewfname(); a = fopen(s, "w"); if (!a) return; helpwriteactive(active, a); fflush(a); if (ferror(a)) { fclose(a); mysyslog(LOG_ERR, "%s has error while writing: %s", s, strerror(errno)); return; } fclose(a); strcpy(c, s); s = getinfofname(); rename(c, s); } #if 0 /* Cornelius's code has fakeactive here */ /* * fake an active file if the groupinfo file is corrupted */ void fakeactive(void) { } #endif