/* * 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" #if !defined (UIO_MAXIOV) #define UIO_MAXIOV 8 #endif /* return 1 if xover is a legal overview line, 0 else */ int legalxoverline(const char* xover) { const char* p; const char* q; if (!xover) return 0; /* anything that isn't tab, printable ascii, or latin-* ? then killit */ #if STRICT_XOVER p = xover; while (*p) { int c = (unsigned char)*p++; if (((c != '\t' && c!= '\033') && c < ' ') || (c > 126 && c < 160)) { if (verbose) printf("illegal character: %s\n", xover); return 0; } } #endif p = xover; q = strchr(p, '\t'); if (!q) return 0; /* article number */ while (p != q) { if (!isdigit(*p)) { if (verbose) printf("non-digit in article number: %s\n", xover); return 0; } p++; } p = q + 1; q = strchr(p, '\t'); if (!q) { if (verbose) printf("premature end before subject: %s\n", xover); return 0; } /* subject: no limitations */ p = q + 1; q = strchr(p, '\t'); if (!q) { if (verbose) printf("premature end before from: %s\n", xover); return 0; } /* from: no limitations */ p = q + 1; q = strchr(p, '\t'); if (!q) { if (verbose) printf("premature end before date: %s\n", xover); return 0; } /* date: no limitations */ p = q + 1; q = strchr(p, '\t'); if (!q) { if (verbose) printf("premature end before message-id: %s\n", xover); return 0; } /* message-id: <*@*> */ p = skipspaces((char*)p); if (*p != '<') { if (verbose) printf("message-id does not start with '<': %s\n", xover); return 0; } while (p != q && *p != '@' && *p != '>' && *p != ' ') p++; if (*p != '@') { if (verbose) printf("message-id does not contain '@': %s\n", xover); return 0; } while (p != q && *p != '>' && *p != ' ') p++; if (*p != '>') { if (verbose) printf("message-id does not contain '>': %s\n", xover); return 0; } if (++p != q) { if (verbose) printf("message-id does not end with '>': %s\n", xover); return 0; } p = q + 1; q = strchr(p, '\t'); if (!q) { if (verbose) printf("premature end before references: %s\n", xover); return 0; } /* references: a series of <*@*> */ #if STRICT_XOVER while (p != q) { if (*p != '<') { if (verbose) printf("id in references does not start with '<': %s\n", xover); return 0; } while (p != q && *p != '@' && *p != '>' && *p != ' ') p++; if (*p != '@') { if (verbose) printf("id in references does not contain '@': %s\n", xover); return 0; } while (p != q && *p != '>' && *p != ' ') p++; if (*p++ != '>') { if (verbose) printf("id in references does not end with '>': %s\n", xover); return 0; } while (p != q && *p == ' ') p++; } #endif p = q + 1; q = strchr(p, '\t'); if (!q) { if (verbose) printf("premature end before byte count: %s\n", xover); return 0; } /* byte count */ #if STRICT_XOVER while (p != q) { if (!isdigit(*p)) { if (verbose) printf("illegal digit in byte count: %s\n", xover); return 0; } p++; } #endif p = q + 1; q = strchr(p, '\t'); /* line count */ #if STRICT_XOVER while (p && *p && p != q) { if (!isdigit(*p)) { if (verbose) printf("illegal digit in line count: %s\n", xover); return 0; } p++; } #endif return 1; } char* getxoverline(const char* filename) { FILE* f; if ((f = fopen(filename, "r"))) { struct header_info* p = parse_all(f); fclose (f); if (p->from && p->date && p->subject && p->msgid && p->n_bytes && p->n_lines) { char * result; result = critmalloc(strlen(p->from) + strlen(p->date) + strlen(p->subject) + strlen(p->msgid) + (p->references ? strlen(p->references) : 0) + 100 + (p->xref ? strlen(p->xref) : 0), "computing overview line"); sprintf(result, "%s\t%s\t%s\t%s\t%s\t%s\t%d\t%d", filename, p->subject, p->from, p->date, p->msgid, p->references ? p->references : "" , p->n_bytes, p->n_lines); if (p->xref) { strcat(result, "\tXref: "); strcat(result, p->xref); } free_header_info(p); return result; } free_header_info(p); } return NULL; } /* utility routine to pull the xover info into memory returns 0 if there's some error, non-zere else */ struct xoverinfo * xoverinfo; unsigned long xfirst, xlast; int getxover(void) { DIR* d; struct dirent* de; int fd; struct stat st; unsigned long art; static char* overview; int error; char* p; char* q; char newsdir[1024]; /* free any memory left over from last time */ error = 0; if (xoverinfo) { for(art = xfirst; art <= xlast; art++) { if (xoverinfo[art - xfirst].mallocd && xoverinfo[art - xfirst].text) { free(xoverinfo[art - xfirst].text); xoverinfo[art - xfirst].text = NULL; } } } if (overview) { free(overview); overview = NULL; } /* find current directory */ if (getcwd(newsdir, 1024) == NULL) { mysyslog(LOG_ERR, "getcwd: %s", strerror(errno)); return 0; } /* find article range, without locking problems */ d = opendir("."); if (!d) { mysyslog(LOG_ERR, "opendir: %s", strerror(errno)); return 0; } xfirst = ULONG_MAX; xlast = 0; while ((de = readdir(d))) { art = strtoul(de->d_name, &p, 10); if (p && !*p) { if (art < xfirst) xfirst = art; if (art > xlast) xlast = art; } } if (xlast < xfirst) { closedir(d); return 0; } /* next, read .overview, correct it if it seems too different from what the directory implies, and write the result back */ rewinddir(d); xoverinfo = (struct xoverinfo *) critrealloc((char*)xoverinfo, sizeof(struct xoverinfo) * (xlast + 1 - xfirst), "allocating overview array"); memset(xoverinfo, 0, sizeof(struct xoverinfo) * (xlast + 1 - xfirst)); if ((fd = open(".overview", O_RDONLY)) >= 0 && fstat(fd, &st) == 0 && (overview = (char*)malloc(st.st_size + 1)) != NULL && read(fd, overview, st.st_size) == st.st_size) { close(fd); overview[st.st_size] = '\0'; /* okay, we have the content, so let's parse it roughly */ p = overview; while (p && *p) { p = skipspaces(p); q = strchr(p, '\n'); if (q) *q++ = '\0'; art = strtoul(p, NULL, 10); if (art > xlast || art < xfirst) { error++; } else if (xoverinfo[art - xfirst].text) { error++; xoverinfo[art - xfirst].text = NULL; } else { xoverinfo[art - xfirst].text = p; } p = q; } } /* so, what was missing? */ while ((de = readdir(d))) { art = strtoul(de->d_name, &p, 10); if (p && !*p) { if (art >= xfirst && art <= xlast) { if (!xoverinfo[art - xfirst].text) { if (verbose > 2) printf("reading XOVER info from %s/%s\n", newsdir, de->d_name); error++; if ((xoverinfo[art - xfirst].text = getxoverline(de->d_name)) != NULL && legalxoverline(xoverinfo[art - xfirst].text)) { xoverinfo[art - xfirst].mallocd = 1; } else { #if TEST_TEXPIRE if (verbose) printf("testing about texpire: will unlink " "illegal article: %s/%s\n", newsdir, de->d_name); #else (void) unlink(de->d_name); /* most dubious, this */ if (verbose) printf("unlinked illegal article: %s/%s\n", newsdir, de->d_name); #endif } } if (xoverinfo[art - xfirst].text) xoverinfo[art - xfirst].exists = 1; } else if (verbose) { if (art > 0 && art < xfirst) printf("article %lu is below the low-water mark (%lu)\n", art, xfirst); else printf("article %lu is above the high-water mark (%lu)\n", art, xlast); } } else { if (art == 0 && strcmp(de->d_name, ".overview") != 0 && strcmp(de->d_name, ".") != 0 && strcmp(de->d_name, "..") != 0) { #if TEST_TEXPIRE if (verbose) printf("testing about texpire: will unlink junk: %s/%s\n", newsdir, de->d_name); #else if (unlink(de->d_name) == 0) { if (verbose > 1) printf("unlinked junk: %s/%s\n", newsdir, de->d_name); } else if (errno != EISDIR && errno != EPERM) { mysyslog(LOG_ERR, "attempted to delete %s in case " "it's junk: %s\n", de->d_name, errno ? strerror(errno) : "OK"); } #endif } } } /* if something had to be fixed, write a better file to disk for next time - race conditions here, but none dangerous */ if (error) { int wfd; char newfile[20]; if (verbose) { printf("corrected %d lines in %s/.overview\n", error, newsdir); } strcpy(newfile, ".overview.XXXXXX"); if ((wfd = open(mktemp(newfile), O_WRONLY|O_CREAT|O_EXCL, 0664)) >= 0) { struct iovec oooh[UIO_MAXIOV]; int vi, vc, va; vi = vc = va = 0; for (art = xfirst; art <= xlast; art++) { if (xoverinfo[art - xfirst].exists && xoverinfo[art - xfirst].text) { oooh[vi].iov_base = xoverinfo[art - xfirst].text; oooh[vi].iov_len = strlen(xoverinfo[art - xfirst].text); vc += oooh[vi++].iov_len + 1; oooh[vi].iov_base = "\n"; oooh[vi++].iov_len = 1; if (vi >= (UIO_MAXIOV - 1)) { if (writev(wfd, oooh, vi) != vc) { mysyslog(LOG_ERR, "writev() for .overview " "failed: %s", strerror(errno)); art = xlast + 1; /* so the loop will stop */ } vi = vc = 0; va = 1; } } } if (vi) { if (writev(wfd, oooh, vi) != vc) { mysyslog(LOG_ERR, "writev() for .overview failed: %s", strerror(errno)); } else { va = 1; } } fchmod(wfd, 0664); close(wfd); if (va) { if (rename(newfile, ".overview")) { if (unlink(newfile)) { mysyslog(LOG_ERR, "rename() and unlink() both " "failed: %s", strerror(errno)); } else { mysyslog(LOG_ERR, "rename(%s/%s, .overview) failed: " "%s", newsdir, newfile, strerror(errno)); } } } else { unlink(newfile); /* the group must be newly empty: I want to keep the old .overview file I think */ } } else { mysyslog(LOG_ERR, "open(O_WRONLY|O_CREAT|O_EXCL) of new " ".overview failed: %s", strerror(errno)); } } closedir(d); return 1; }