/*
* DREADART [-h] [-v] 'messageid'
* DREADART [-h] [-v] < file
*
* (c)Copyright 1998, Matthew Dillon, All Rights Reserved. Refer to
* the COPYRIGHT file in the base directory of this distribution
* for specific rights granted.
*/
#include "defs.h"
int DumpArticle(char *fname, History *h, const char *msgid);
int LookupHash(hash_t hv, const char *msgid, History *ph);
int VerifyOnly = 0;
int HeadOnly = 0;
int ForceOpt = 0;
int StripCR = 1;
int QuietOpt = 0;
int ShowFileHeader = 0;
FILE *LogFo;
void
Usage(char *progname)
{
printf("Retrieve an article from the spool\n");
printf("Usage: dreadart [-F] [-f scanfile] [-H] [-h] [-s] OBJECT\n");
printf("\n");
printf("\t-F\tforce retrieval, ignoring possible errors\n");
printf("\t-f file\tspecify a file containing Message-ID's to retrieve\n");
printf("\t-H\tonly show file header data\n");
printf("\t-h\tonly retrieve article header\n");
printf("\t-q\tdon't display the article/header\n");
printf("\t-s\tdon't strip CR from each line (if wireformat)\n");
printf("\n");
printf(" OBJECT can be one of:\n");
printf("\t<message-id>\t\tMessage-ID\n");
printf("\thv1.hv2\t\t\tMessage-ID hash\n");
printf("\t/path/file:offset,size\tfile offset\n");
printf("\n");
exit(1);
}
int
main(int ac, char **av)
{
int r = 0;
int i;
char *arg = NULL;
char *file = NULL;
LogFo = stderr;
LoadDiabloConfig(ac, av);
for (i = 1; i < ac; ++i) {
char *ptr = av[i];
if (*ptr != '-') {
arg = ptr;
continue;
}
ptr += 2;
switch(ptr[-1]) {
case 'C':
if (*ptr == 0)
++i;
break;
case 'd':
DebugOpt = atoi(*ptr ? ptr : av[++i]);
break;
case 'F':
ForceOpt = 1;
break;
case 'f':
file = (*ptr) ? ptr : av[++i];
break;
case 'H':
ShowFileHeader = 1;
break;
case 'h':
HeadOnly = 1;
break;
case 'q':
QuietOpt = 1;
break;
case 's':
StripCR = 0;
break;
case 'V':
PrintVersion();
break;
case 'v':
VerifyOnly = 1;
LogFo = stdout;
break;
default:
fprintf(stderr, "dreadart: Illegal option: %s\n", ptr - 2);
Usage(av[0]);
}
}
if (arg == NULL && file == NULL)
Usage(av[0]);
HistoryOpen(NULL, HGF_READONLY);
LoadSpoolCtl(0, 1);
if (arg == NULL) {
char buf[8192];
char msgid[MAXMSGIDLEN];
FILE *fi = (strcmp(file, "-") == 0) ? stdin : fopen(file, "r");
if (fi) {
while (fgets(buf, sizeof(buf), fi) != NULL) {
hash_t hv;
char *m;
if (strncmp(buf, "DUMP ", 5) == 0) {
History h = { 0 };
if (sscanf(buf + 5, "%s gm=%d ex=%hd boff=%d bsize=%d",
msgid, &h.gmt, &h.exp,
&h.boffset, &h.bsize) == 5) {
char *p;
h.hv.h1 = (int32)strtoul(msgid, &p, 16);
if (*p == '.')
h.hv.h2 = (int32)strtoul(p + 1, &p, 16);
if (*p == '.')
h.iter = (int16)strtoul(p + 1, NULL, 16);
r = LookupHash(h.hv, NULL, &h);
}
} else if ((m = strchr(buf, '<')) != NULL) {
char *p;
if ((p = strchr(m, '>')) == NULL)
continue;
*++p = 0;
hv = hhash(m);
r = LookupHash(hv, m, NULL);
} else if (sscanf(buf, "%x.%x", &hv.h1, &hv.h2) == 2) {
r = LookupHash(hv, NULL, NULL);
}
}
if (fi != stdin)
fclose(fi);
} else {
fprintf(stderr, "Unable to open %s (%s)\n", file, strerror(errno));
}
} else {
hash_t hv;
char *msgid = NULL;
if (arg[0] == '<') {
msgid = arg;
hv = hhash(arg);
r = LookupHash(hv, msgid, NULL);
} else if (arg[0] == 'D' && arg[1] == '.') {
int32 dummy;
if (sscanf(arg + 2, "%x/%x.%x", &dummy, &hv.h1, &hv.h2) != 3) {
fprintf(stderr, "argument error\n");
exit(1);
}
r = LookupHash(hv, msgid, NULL);
} else if (sscanf(arg, "%x.%x", &hv.h1, &hv.h2) == 2) {
r = LookupHash(hv, msgid, NULL);
} else {
char fname[PATH_MAX];
char *p = fname;
History h = { 0 };
*p = 0;
if (*arg != '/') {
sprintf(p, "%s/", PatExpand(SpoolHomePat));
p += strlen(p);
}
if (sscanf(arg, "%[^:]:%d,%d", p, &h.boffset, &h.bsize) == 3) {
DumpArticle(fname, &h, NULL);
} else {
printf("Unknown argument: %s\n", arg);
}
}
}
exit(r);
}
int
DumpFileHeader(int fd, off_t offset)
{
SpoolArtHdr artHdr;
lseek(fd, offset, SEEK_SET);
if (read(fd, &artHdr, sizeof(artHdr)) != sizeof(artHdr)) {
fprintf(stderr, "Error reading data header\n");
return(1);
}
if ((uint8)artHdr.Magic1 != STORE_MAGIC1 ||
(uint8)artHdr.Magic2 != STORE_MAGIC2) {
fprintf(stderr, "No or corrupt data header (%d:%d)\n", artHdr.Magic1,
artHdr.Magic2);
return(1);
}
printf("OK\n");
printf("Offset : %llu\n", offset);
printf("Version : %d\n", artHdr.Version);
printf("HeadLen : %d\n", artHdr.HeadLen);
printf("StoreType:%s%s%s\n",
(artHdr.StoreType & STORETYPE_TEXT) ? " text" : "",
(artHdr.StoreType & STORETYPE_GZIP) ? " gzip" : "",
(artHdr.StoreType & STORETYPE_WIRE) ? " wire" : "");
printf("ArtHdrLen: %d\n", artHdr.ArtHdrLen);
printf("ArtLen : %d\n", artHdr.ArtLen);
printf("StoreLen : %d\n", artHdr.StoreLen);
return(0);
}
int
MapArticle(int fd, char *fname, char **base, History *h, int *extra, int *artSize, int *compressedFormat)
{
if (SpoolCompressed(H_SPOOL(h->exp))) {
#ifdef USE_ZLIB
gzFile *gzf;
SpoolArtHdr tah = { 0 };
char *p;
lseek(fd, h->boffset, 0);
if (read(fd, &tah, sizeof(SpoolArtHdr)) != sizeof(SpoolArtHdr)) {
close(fd);
fprintf(LogFo, "Unable to read article header (%s)\n",
strerror(errno));
return(-1);
}
if ((uint8)tah.Magic1 != STORE_MAGIC1 &&
(uint8)tah.Magic2 != STORE_MAGIC2) {
lseek(fd, h->boffset, 0);
tah.Magic1 = STORE_MAGIC1;
tah.Magic2 = STORE_MAGIC2;
tah.HeadLen = sizeof(tah);
tah.ArtLen = h->bsize;
tah.ArtHdrLen = h->bsize;
tah.StoreLen = h->bsize;
}
gzf = gzdopen(fd, "r");
if (gzf == NULL) {
fprintf(LogFo, "Error opening compressed article\n");
return(-1);
}
*base = (char *)malloc(tah.ArtLen + tah.HeadLen + 2);
if (*base == NULL) {
fprintf(LogFo, "Unable to malloc %d bytes for article (%s)\n",
tah.ArtLen + tah.HeadLen + 2, strerror(errno));
gzclose(gzf);
return(-1);
}
p = *base;
*p++ = 0;
bcopy(&tah, p, tah.HeadLen);
p += tah.HeadLen;
if (gzread(gzf, p, tah.ArtLen) != tah.ArtLen) {
fprintf(LogFo, "Error uncompressing article\n");
free(*base);
return(-1);
}
p[tah.ArtLen] = 0;
*extra = 1;
*artSize = tah.ArtLen;
*artSize += tah.HeadLen;
*compressedFormat = 1;
gzclose(gzf);
#else
fprintf(LogFo, "Article is on a compressed spool and compression support has not been enabled\n");
#endif
} else {
*base = xmap(
NULL,
h->bsize + *extra + 1,
PROT_READ,
MAP_SHARED,
fd,
h->boffset - *extra
);
*artSize = h->bsize;
}
if (*base == NULL) {
fprintf(LogFo, "Unable to map file %s: %s (%llu,%d,%d)\n",
fname,
strerror(errno),
(off_t)(h->boffset - *extra),
(int)(h->bsize + *extra + 1),
*extra
);
return(-1);
}
return(0);
}
int
DumpArticle(char *fname, History *h, const char *msgid)
{
int fd;
int rv = 0;
int wireFormat = 0;
int artSize = 0;
int headLen;
int oldFormat = 0;
int compressedFormat = 0;
SpoolArtHdr ah;
if ((fd = open(fname, O_RDONLY)) >= 0) {
char *base = NULL;
const char *ptr;
int extra = (h->boffset == 0) ? 0 : 1;
errno = 0;
if (ShowFileHeader) {
rv = DumpFileHeader(fd, h->boffset);
close(fd);
return(rv);
}
if (MapArticle(fd, fname, &base, h, &extra, &artSize, &compressedFormat) != 0)
return(1);
/*
* check for prior terminating zero, article body does not
* begin with a null, and post terminating zero
*/
ptr = base;
if (rv == 0 && extra) {
if (*ptr != 0) {
fprintf(LogFo, " missingPreNul");
rv = 1;
}
++ptr;
}
if (rv == 0 && ptr[0] == 0) {
fprintf(LogFo, " nullArtBody");
rv = 1;
}
if (rv == 0 && ptr[artSize] != 0) {
fprintf(LogFo, " missingPostNul");
rv = 1;
}
bcopy(ptr, &ah, sizeof(ah));
if (DebugOpt) {
printf("magic1=%x magic2=%x\n", (uint8)ah.Magic1,
(uint8)ah.Magic2);
}
if ((uint8)ah.Magic1 == (uint8)STORE_MAGIC1 &&
(uint8)ah.Magic2 == (uint8)STORE_MAGIC2) {
headLen = (uint32)ah.ArtHdrLen;
artSize -= (uint8)ah.HeadLen;
ptr += (uint8)ah.HeadLen;
wireFormat = 0;
if ((uint8)ah.StoreType & STORETYPE_WIRE)
wireFormat = 1;
} else {
headLen = artSize;
wireFormat = 0;
oldFormat = 1;
}
/*
* Locate Message-ID header and test
*/
if (rv == 0 && msgid) {
const char *l;
int haveMsgId = 0;
for (l = ptr - 1; l < ptr + headLen; l = strchr(l, '\n')) {
if (l == NULL) {
rv = 1;
break;
}
++l;
if (strncasecmp(l, "Message-ID:", 11) == 0) {
int i;
haveMsgId = 1;
l += 11;
while (*l && *l != '<' && *l != '\n')
++l;
for (i = 0; l[i] && l[i] != '>' && l[i] != '\n' &&
l[i] != '\r'; ++i) {
if (msgid[i] != l[i])
rv = 1;
}
if (msgid[i] != l[i])
rv = 1;
}
if (!wireFormat && l[0] == '\n') /* end of headers */
break;
else if (wireFormat && l[0] == '\r' && l[1] == '\n')
break;
}
if (rv) {
fprintf(LogFo, " messageID-MisMatch");
} else if (haveMsgId == 0) {
fprintf(LogFo, " missing-MessageID");
rv = 1;
}
}
if (rv == 0)
fprintf(LogFo, "%sOK", oldFormat ? "(old spool) " : "");
fprintf(LogFo, "\n");
if (rv == 0 && VerifyOnly == 0) {
int i;
int lastNl = 1;
int lineLen = 0;
fflush(LogFo);
fflush(stdout);
if (HeadOnly) {
for (i = 0; i < headLen; ++i) {
if (StripCR && wireFormat && i > 0 &&
ptr[i-1] == '\r' && ptr[i] == '\n') {
if (!QuietOpt) {
write(1, ptr + i - lineLen, lineLen - 1);
write(1, "\n", 1);
}
if (lineLen == 1)
break;
lineLen = 0;
} else if (wireFormat && i > 0 &&
ptr[i-1] == '\r' && ptr[i] == '\n') {
if (lineLen == 1)
break;
lastNl = 1;
lineLen = 0;
} else if (ptr[i] == '\n') {
if (lastNl)
break;
lastNl = 1;
} else {
lastNl = 0;
lineLen++;
}
}
if ((!StripCR || !wireFormat) && !QuietOpt)
write(1, ptr, i);
} else {
if (StripCR && wireFormat) {
for (i = 0; i < artSize; ++i) {
if (i > 0 && ptr[i-1] == '\r' && ptr[i] == '\n') {
if (!QuietOpt) {
write(1, ptr + i - lineLen, lineLen - 1);
write(1, "\n", 1);
}
lineLen = 0;
} else {
lineLen++;
}
}
} else if (!QuietOpt) {
write(1, ptr, artSize);
}
}
fflush(stdout);
}
if (base != NULL) {
if (compressedFormat)
free(base);
else
xunmap((void *)base, h->bsize + extra + 1);
}
if (fd != -1 && !compressedFormat)
close(fd);
} else {
fprintf(LogFo, "Unable to open %s\n", fname);
rv = 1;
}
return(rv);
}
int
LookupHash(hash_t hv, const char *msgid, History *ph)
{
History h;
int rv = 0;
if (msgid)
fprintf(LogFo, "%60s\t", msgid);
else
fprintf(LogFo, "%08x.%08x\t", hv.h1, hv.h2);
if (ph != NULL || HistoryLookupByHash(hv, &h) == 0) {
char buf[8192];
if (ph != NULL)
memcpy(&h, ph, sizeof(h));
if (ForceOpt || (
!H_EXPIRED(h.exp) &&
h.iter != (unsigned short)-1 &&
(h.boffset || h.bsize)
)
) {
int headOnly = (int)(h.exp & EXPF_HEADONLY);
ArticleFileName(buf, sizeof(buf), &h, ARTFILE_FILE);
if (HeadOnly == 0 && headOnly) {
fprintf(LogFo, "Article stored as header-only, use -h\n");
rv = 1;
} else {
rv = DumpArticle(buf, &h, msgid);
}
} else if (h.boffset || h.bsize) {
fprintf(LogFo, "Article expired\n");
rv = 1;
} else {
fprintf(LogFo, "Article pre-expired\n");
rv = 1;
}
} else {
fprintf(LogFo, "Article not found in history\n");
rv = 1;
}
return(rv);
}
syntax highlighted by Code2HTML, v. 0.9.1