/* $Id: ov.c 6135 2003-01-19 01:15:40Z rra $
**
** The implementation of the overview API.
**
** This code handles calls to the overview API by passing them along to the
** appropriate underlying overview method, as well as implementing those
** portions of the overview subsystem that are independent of storage
** method.
*/
#include "config.h"
#include "clibrary.h"
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <syslog.h>
#include "inn/innconf.h"
#include "libinn.h"
#include "ov.h"
#include "ovinterface.h"
#include "ovmethods.h"
/* FIXME: The following variables are shared between this file and expire.c.
This should be cleaned up with a better internal interface. */
static bool OVdelayrm;
static OV_METHOD ov;
bool
OVopen(int mode)
{
int i;
bool val;
char *p;
if (ov.open)
/* already opened */
return true;
/* if innconf isn't already read in, do so. */
if (innconf == NULL)
if (!innconf_read(NULL))
return false;
if (!innconf->enableoverview) {
syslog(L_FATAL, "enableoverview is not true");
fprintf(stderr, "enableoverview is not true\n");
return false;
}
if (innconf->ovmethod == NULL) {
syslog(L_FATAL, "ovmethod is not defined");
fprintf(stderr, "ovmethod is not defined\n");
return false;
}
for (i=0;i<NUM_OV_METHODS;i++) {
if (!strcmp(innconf->ovmethod, ov_methods[i].name))
break;
}
if (i == NUM_OV_METHODS) {
syslog(L_FATAL, "%s is not found for ovmethod", innconf->ovmethod);
fprintf(stderr, "%s is not found for ovmethod\n", innconf->ovmethod);
return false;
}
ov = ov_methods[i];
val = (*ov.open)(mode);
if (atexit(OVclose) < 0) {
OVclose();
return false;
}
if (innconf->ovgrouppat != NULL) {
for (i = 1, p = innconf->ovgrouppat; *p && (p = strchr(p+1, ',')); i++);
OVnumpatterns = i;
OVpatterns = xmalloc(OVnumpatterns * sizeof(char *));
for (i = 0, p = strtok(innconf->ovgrouppat, ","); p != NULL && i <= OVnumpatterns ; i++, p = strtok(NULL, ","))
OVpatterns[i] = xstrdup(p);
if (i != OVnumpatterns) {
syslog(L_FATAL, "extra ',' in pattern");
fprintf(stderr, "extra ',' in pattern");
return false;
}
}
return val;
}
bool
OVgroupstats(char *group, int *lo, int *hi, int *count, int *flag)
{
if (!ov.open) {
/* must be opened */
syslog(L_ERROR, "ovopen must be called first");
fprintf(stderr, "ovopen must be called first");
return false;
}
return ((*ov.groupstats)(group, lo, hi, count, flag));
}
bool
OVgroupadd(char *group, ARTNUM lo, ARTNUM hi, char *flag)
{
/* lomark should never be changed in each ovmethod if lo is 0 */
if (!ov.open) {
/* must be opened */
syslog(L_ERROR, "ovopen must be called first");
fprintf(stderr, "ovopen must be called first");
return false;
}
return ((*ov.groupadd)(group, lo, hi, flag));
}
bool
OVgroupdel(char *group)
{
if (!ov.open) {
/* must be opened */
syslog(L_ERROR, "ovopen must be called first");
fprintf(stderr, "ovopen must be called first");
return false;
}
return ((*ov.groupdel)(group));
}
OVADDRESULT
OVadd(TOKEN token, char *data, int len, time_t arrived, time_t expires)
{
char *next, *nextcheck;
static char *xrefdata, *patcheck, *overdata;
char *xrefstart = NULL;
char *xrefend;
static int xrefdatalen = 0, overdatalen = 0;
bool found = false;
int xreflen;
int i;
char *group;
ARTNUM artnum;
if (!ov.open) {
/* must be opened */
syslog(L_ERROR, "ovopen must be called first");
fprintf(stderr, "ovopen must be called first");
return OVADDFAILED;
}
/*
* find last Xref: in the overview line. Note we need to find the *last*
* Xref:, since there have been corrupted articles on Usenet with Xref:
* fragments stuck in other header lines. The last Xref: is guaranteed
* to be from our server.
*/
for (next = data; ((len - (next - data)) > 6 ) && ((next = memchr(next, 'X', len - (next - data))) != NULL); ) {
if (memcmp(next, "Xref: ", 6) == 0) {
found = true;
xrefstart = next;
}
next++;
}
if (!found)
return OVADDFAILED;
next = xrefstart;
for (i = 0; (i < 2) && (next < (data + len)); i++) {
if ((next = memchr(next, ' ', len - (next - data))) == NULL)
return OVADDFAILED;
next++;
}
xreflen = len - (next - data);
/*
* If there are other fields beyond Xref in overview, then
* we must find Xref's end, or data following is misinterpreted.
*/
if ((xrefend = memchr(next, '\t', xreflen)) != NULL)
xreflen = xrefend - next;
if (xrefdatalen == 0) {
xrefdatalen = BIG_BUFFER;
xrefdata = xmalloc(xrefdatalen);
if (innconf->ovgrouppat != NULL)
patcheck = xmalloc(xrefdatalen);
}
if (xreflen > xrefdatalen) {
xrefdatalen = xreflen;
xrefdata = xrealloc(xrefdata, xrefdatalen + 1);
if (innconf->ovgrouppat != NULL)
patcheck = xrealloc(patcheck, xrefdatalen + 1);
}
if (overdatalen == 0) {
overdatalen = BIG_BUFFER;
overdata = xmalloc(overdatalen);
}
if (len + 16 > overdatalen) {
overdatalen = len + 16;
overdata = xrealloc(overdata, overdatalen);
}
if (innconf->ovgrouppat != NULL) {
memcpy(patcheck, next, xreflen);
patcheck[xreflen] = '\0';
for (group = patcheck; group && *group; group = memchr(nextcheck, ' ', xreflen - (nextcheck - patcheck))) {
while (isspace((int)*group))
group++;
if ((nextcheck = memchr(group, ':', xreflen - (patcheck - group))) == NULL)
return OVADDFAILED;
*nextcheck++ = '\0';
if (!OVgroupmatch(group)) {
if (!SMprobe(SELFEXPIRE, &token, NULL) && innconf->groupbaseexpiry)
/* this article will never be expired, since it does not
have self expiry function in stored method and
groupbaseexpiry is true */
return OVADDFAILED;
return OVADDGROUPNOMATCH;
}
}
}
memcpy(xrefdata, next, xreflen);
xrefdata[xreflen] = '\0';
for (group = xrefdata; group && *group; group = memchr(next, ' ', xreflen - (next - xrefdata))) {
/* Parse the xref part into group name and article number */
while (isspace((int)*group))
group++;
if ((next = memchr(group, ':', xreflen - (group - xrefdata))) == NULL)
return OVADDFAILED;
*next++ = '\0';
artnum = atoi(next);
if (artnum <= 0)
continue;
sprintf(overdata, "%ld\t", artnum);
i = strlen(overdata);
memcpy(overdata + i, data, len);
i += len;
memcpy(overdata + i, "\r\n", 2);
i += 2;
if(! (*ov.add)(group, artnum, token, overdata, i, arrived, expires))
return OVADDFAILED;
}
return OVADDCOMPLETED;
}
bool
OVcancel(TOKEN token)
{
if (!ov.open) {
/* must be opened */
syslog(L_ERROR, "ovopen must be called first");
fprintf(stderr, "ovopen must be called first");
return false;
}
return ((*ov.cancel)(token));
}
void *
OVopensearch(char *group, int low, int high)
{
if (!ov.open) {
/* must be opened */
syslog(L_ERROR, "ovopen must be called first");
fprintf(stderr, "ovopen must be called first");
return false;
}
return ((*ov.opensearch)(group, low, high));
}
bool
OVsearch(void *handle, ARTNUM *artnum, char **data, int *len, TOKEN *token,
time_t *arrived)
{
if (!ov.open) {
/* must be opened */
syslog(L_ERROR, "ovopen must be called first");
fprintf(stderr, "ovopen must be called first");
return false;
}
return ((*ov.search)(handle, artnum, data, len, token, arrived));
}
void
OVclosesearch(void *handle)
{
if (!ov.open) {
/* must be opened */
syslog(L_ERROR, "ovopen must be called first");
fprintf(stderr, "ovopen must be called first");
return;
}
(*ov.closesearch)(handle);
return;
}
bool
OVgetartinfo(char *group, ARTNUM artnum, TOKEN *token)
{
if (!ov.open) {
/* must be opened */
syslog(L_ERROR, "ovopen must be called first");
fprintf(stderr, "ovopen must be called first");
return false;
}
return ((*ov.getartinfo)(group, artnum, token));
}
bool
OVexpiregroup(char *group, int *lo, struct history *h)
{
if (!ov.open) {
/* must be opened */
syslog(L_ERROR, "ovopen must be called first");
fprintf(stderr, "ovopen must be called first");
return false;
}
return ((*ov.expiregroup)(group, lo, h));
}
bool
OVctl(OVCTLTYPE type, void *val)
{
if (!ov.open) {
/* must be opened */
syslog(L_ERROR, "ovopen must be called first");
fprintf(stderr, "ovopen must be called first");
return false;
}
switch (type) {
case OVGROUPBASEDEXPIRE:
if (!innconf->groupbaseexpiry) {
syslog(L_ERROR, "OVGROUPBASEDEXPIRE is not allowed if groupbaseexpiry if false");
fprintf(stderr, "OVGROUPBASEDEXPIRE is not allowed if groupbaseexpiry if false");
return false;
}
if (((OVGE *)val)->delayrm) {
if ((((OVGE *)val)->filename == NULL) || (strlen(((OVGE *)val)->filename) == 0)) {
syslog(L_ERROR, "file name must be specified");
fprintf(stderr, "file name must be specified");
return false;
}
if ((EXPunlinkfile = fopen(((OVGE *)val)->filename, "w")) == NULL) {
syslog(L_ERROR, "fopen: %s failed: %m", ((OVGE *)val)->filename);
fprintf(stderr, "fopen: %s failed: %s", ((OVGE *)val)->filename,
strerror(errno));
return false;
}
}
OVdelayrm = ((OVGE *)val)->delayrm;
OVusepost = ((OVGE *)val)->usepost;
OVrealnow = ((OVGE *)val)->now;
OVnow = ((OVGE *)val)->now + (time_t)((OVGE *)val)->timewarp;
OVquiet = ((OVGE *)val)->quiet;
OVkeep = ((OVGE *)val)->keep;
OVearliest = ((OVGE *)val)->earliest;
OVignoreselfexpire = ((OVGE *)val)->ignoreselfexpire;
return true;
case OVSTATALL:
OVstatall = *(bool *)val;
return true;
default:
return ((*ov.ctl)(type, val));
}
}
void
OVclose(void)
{
if (!ov.open)
return;
(*ov.close)();
memset(&ov, '\0', sizeof(ov));
OVEXPcleanup();
}
syntax highlighted by Code2HTML, v. 0.9.1