/*
* 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 <agulbra@troll.no> 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 <socks.h>
#endif
#include <fcntl.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <netdb.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include <dirent.h>
#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;
}
syntax highlighted by Code2HTML, v. 0.9.1