You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
bforce/source/bforce/nodelist.c

653 lines
15 KiB
C

/*
* binkleyforce -- unix FTN mailer project
*
* Copyright (c) 1998-2000 Alexander Belkin, 2:5020/1398.11
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* $Id$
*/
#include "includes.h"
#include "confread.h"
#include "logger.h"
#include "util.h"
#include "nodelist.h"
#define NODELIST_LOCK_TRIES 3
#define NODELIST_LOCK_DELAY 1 /* seconds */
struct keyword {
const char *keystr;
const int keyval;
} keywords[] = {
{ "", KEYWORD_EMPTY },
{ "Zone", KEYWORD_ZONE },
{ "Region", KEYWORD_REGION },
{ "Host", KEYWORD_HOST },
{ "Hub", KEYWORD_HUB },
{ "Pvt", KEYWORD_PVT },
{ "Hold", KEYWORD_HOLD },
{ "Down", KEYWORD_DOWN },
{ "Boss", KEYWORD_BOSS },
{ "Point", KEYWORD_POINT },
{ NULL, 0 }
};
/*****************************************************************************
* Check flag for existing in nodelist flags string
*
* Arguments:
* nodeflags pointer to the node's nodelist flags string
* flag pointer to the flag that we want to check
*
* Return value:
* zero value if flag is presented in flags, and non-zero if not
*/
int nodelist_checkflag(const char *nodeflags, const char *flag)
{
char *p, *q;
if( (p = strstr(nodeflags, flag)) )
{
if( p == nodeflags || *(p-1) == ',' )
{
if( (q = strchr(p, ',')) == NULL || (q - p) == strlen(flag) )
return 0;
}
}
return 1;
}
/*****************************************************************************
* Get nodelist keyword (e.g. Host, Hub, Point, etc.) value
* (e.g. KEYWORD_HOST, KEYWORD_HUB, etc.)
*
* Arguments:
* keywordval pointer to the keyword string
*
* Return value:
* One of KEYWORD_* values, and -1 for unknown keywords
*/
int nodelist_keywordval(const char *keyword)
{
int i;
for( i = 0; keywords[i].keystr; i++ )
{
if( strcmp(keyword, keywords[i].keystr) == 0 )
{
return keywords[i].keyval;
}
}
return -1;
}
int nodelist_parse_Txy(s_node *node, const char *xy)
{
long beg;
long end;
long tz;
if( xy[0] >= 'A' && xy[0] <= 'X' )
beg = (xy[0] - 'A') * 60;
else if( xy[0] >= 'a' && xy[0] <= 'x' )
beg = (xy[0] - 'a') * 60 + 30;
else
return -1;
if( xy[1] >= 'A' && xy[1] <= 'X' )
end = (xy[1] - 'A') * 60;
else if( xy[1] >= 'a' && xy[1] <= 'x' )
end = (xy[1] - 'a') * 60 + 30;
else
return -1;
if( beg == end )
return -1;
/* Convert it to local time */
if( (tz = time_gmtoffset()) )
{
beg -= tz;
if( beg > 1440 )
beg = beg - 1440;
else if( beg < 0 )
beg = beg + 1440;
else if( beg == 1440 )
beg = 0;
end -= tz;
if( end > 1440 )
end = end - 1440;
else if( end < 0 )
end = end + 1440;
else if( end == 1440 )
end = 0;
}
timevec_add(&node->worktime, DAY_MONDAY, DAY_SUNDAY, beg, end);
return 0;
}
/*****************************************************************************
* Nodelist string parser
*
* Arguments:
* node put here all obtained information
* str pointer to the nodelist string
*
* Return value:
* zero value if string was parsed successfuly, and non-zero if wasn't
*/
int nodelist_parsestring(s_node *node, char *str)
{
char *argv[NODELIST_POSFLAGS+1];
char *p;
if( string_parse(argv, NODELIST_POSFLAGS+1, str, ',') != NODELIST_POSFLAGS+1 )
return -1;
if( !ISDEC(argv[NODELIST_POSNUMBER]) || !ISDEC(argv[NODELIST_POSSPEED]) )
return -1;
if( (node->keyword = nodelist_keywordval(argv[NODELIST_POSKEYWORD])) == -1 )
return -1;
if( node->addr.zone )
{
int number = atoi(argv[NODELIST_POSNUMBER]);
bool goodstr = FALSE;
switch(node->keyword) {
case KEYWORD_ZONE:
goodstr = (node->addr.zone == number);
break;
case KEYWORD_REGION:
case KEYWORD_HOST:
goodstr = (node->addr.net == number);
break;
case KEYWORD_POINT:
goodstr = (node->addr.point == number);
break;
default:
goodstr = (node->addr.node == number);
break;
}
if( !goodstr ) return -1;
}
strnxcpy(node->name, argv[NODELIST_POSNAME], sizeof(node->name));
strnxcpy(node->location, argv[NODELIST_POSLOCATION], sizeof(node->location));
strnxcpy(node->sysop, argv[NODELIST_POSSYSOP], sizeof(node->sysop));
strnxcpy(node->phone, argv[NODELIST_POSPHONE], sizeof(node->phone));
strnxcpy(node->flags, argv[NODELIST_POSFLAGS], sizeof(node->flags));
node->speed = atoi(argv[NODELIST_POSSPEED]);
/*
* Replace all '_' by space character
*/
string_replchar(node->name, '_', ' ');
string_replchar(node->location, '_', ' ');
string_replchar(node->sysop, '_', ' ');
/*
* Get system work time (usefull flags: CM,Txy)
*/
if( nodelist_checkflag(node->flags, "CM") == 0 )
{
timevec_add(&node->worktime, DAY_MONDAY, DAY_SUNDAY, 0, 1440);
}
else
{
for( p = node->flags; p && *p; p = strchr(p, ',') )
{
if( p[1] == 'T' && p[2] && p[3] && (p[4] == ',' || p[4] == '\0') )
{
if( nodelist_parse_Txy(node, p+2) == -1 )
log("invalid nodelist Txy flag in \"%s\"", node->flags);
break;
}
p++;
}
/*
* Set default work time according to ZMH
*/
if( node->addr.point == 0 )
{
switch(node->addr.zone) {
case 1: nodelist_parse_Txy(node, "JK"); break;
case 2: nodelist_parse_Txy(node, "cd"); break;
case 3: nodelist_parse_Txy(node, "ST"); break;
case 4: nodelist_parse_Txy(node, "IJ"); break;
case 5: nodelist_parse_Txy(node, "BC"); break;
case 6: nodelist_parse_Txy(node, "UV"); break;
}
}
}
return 0;
}
/*****************************************************************************
* Open nodelist, nodelist index, do some checks
*
* Arguments:
* dir pointer to the nodelist's directory
* name pointer to the nodelist file name
* mode are we going to read nodelist index? Or write?
*
* Return value:
* pointer to the allocated nodelist structure (will be used with all
* nodelist operations), and NULL to indicate errors.
*/
s_nodelist *nodelist_open(const char *dir, char *name, int mode)
{
s_nodelist tmp;
const char *openmode;
int lockmode;
char *ext; /* extension */
char *lastname;
memset(&tmp, '\0', sizeof(s_nodelist));
/*
* Select nodelist index open mode
*/
if( mode == NODELIST_READ )
{
openmode = "r";
lockmode = FILELOCK_READ;
}
else if( mode == NODELIST_WRITE )
{
openmode = "w";
lockmode = FILELOCK_WRITE;
}
else
ASSERT(0);
/*
* Get nodelist and nodelist index file names
*/
if( *name == DIRSEPCHR )
/* if nodelist name contains full path */
{
logerr("nodelist.c: you shold specify only filename, without path");
exit(255);
}
/* if only just a filename */
else
{
/* checking, if nodelist name contains mask (see
* example config for details) */
/* If nodelist extension == "999" then we assume that it
* is a mask, and try to find the latest nodelist for
* this mask. If we find it, then we change "name"
* parameter.
* If not, we do logerr
*
* TODO: as in qico, do mask-search not case-sentesive
*/
if( strcmp(name+strlen(name)-4, ".999") == 0 )
{
char tmpseek[MAX_NAME];
char tmpseekdir[MAX_NAME];
char tmpname[MAX_NAME];
struct stat ndfile;
time_t lasttime = 0;
struct dirent *ndir;
DIR *ndirstream;
if( (ndirstream = opendir(dir)) == NULL )
{
log("error opening nodelist directory: %s", dir);
}
else
{
strnxcpy(tmpname, name, sizeof(tmpname));
while( (ndir = readdir(ndirstream)) )
{
strnxcpy(tmpseek, ndir->d_name, sizeof(tmpseek));
if ( strlen(tmpseek) < 3 ) /* Checking for */
continue; /* "." and ".." */
if( strlen(tmpseek) == strlen(tmpname) )
{
if( (strncmp(tmpseek, tmpname, strlen(tmpseek)-3 ) == 0) )
{
strnxcpy(tmpseekdir, dir, sizeof(tmpseekdir));
strnxcat(tmpseekdir, tmpseek, sizeof(tmpseekdir));
if( (stat(tmpseekdir, &ndfile)) == 0 )
{
if( ndfile.st_ctime > lasttime )
{
lasttime = ndfile.st_ctime;
strcpy(name, tmpseek);
}
}
}
}
}
}
closedir(ndirstream);
}
if( strcmp(name+strlen(name)-4, ".999") == 0 )
/* we haven`t found any nodelist for this mask */
{
logerr("No nodelist found for this mask: %s", name);
}
strnxcpy(tmp.name_nodelist, dir, sizeof(tmp.name_nodelist));
strnxcat(tmp.name_nodelist, name, sizeof(tmp.name_nodelist));
strnxcpy(tmp.name_index, tmp.name_nodelist, sizeof(tmp.name_index));
}
strnxcat(tmp.name_index, ".index", sizeof(tmp.name_index));
DEB((D_NODELIST, "nodelist_open: nodelist name = \"%s\"", tmp.name_nodelist));
DEB((D_NODELIST, "nodelist_open: nodelist index = \"%s\"", tmp.name_index));
DEB((D_NODELIST, "nodelist_open: open mode = \"%s\"", openmode));
/*
* Try to open and lock nodelist
*/
if( (tmp.fp_nodelist = file_open(tmp.name_nodelist, "r")) == NULL )
{
logerr("cannot open nodelist \"%s\"", tmp.name_nodelist);
return NULL;
}
/*
* Try to open and lock nodelist index
*/
if( (tmp.fp_index = file_open(tmp.name_index, openmode)) == NULL )
{
logerr("cannot open nodelist index \"%s\"", tmp.name_index);
fclose(tmp.fp_nodelist);
return NULL;
}
/*
* If we open nodelist for reading then we should check that
* nodelist index has correct header and up to date
*/
if( mode == NODELIST_READ )
{
if( nodelist_checkheader(&tmp) == -1 )
{
file_close(tmp.fp_nodelist);
file_close(tmp.fp_index);
return NULL;
}
}
return xmemcpy(&tmp, sizeof(s_nodelist));
}
/*****************************************************************************
* Check nodelist index header for valid nodelist date and size
*
* Arguments:
* nlp opened nodelist
*
* Return value:
* zero value if header is correct, and -1 if not
*/
int nodelist_checkheader(s_nodelist *nlp)
{
unsigned long nltime;
unsigned long nlsize;
struct stat nlstat;
char buffer[NODELIST_HDRSIZE];
if( fread(buffer, sizeof(buffer), 1, nlp->fp_index) != 1 )
{
logerr("cannot read header from nodelist index \"%s\"", nlp->name_index);
return -1;
}
if( fstat(fileno(nlp->fp_nodelist), &nlstat) == -1 )
{
logerr("cannot stat nodelist \"%s\"", nlp->name_nodelist);
return -1;
}
nltime = buffer_getlong(buffer + 0);
nlsize = buffer_getlong(buffer + 4);
if( (unsigned long)nlstat.st_mtime != nltime )
{
log("invalid nodelist index: incorrect nodelist date %ld (expected %ld)",
(long)nlstat.st_mtime, (long)nltime);
return -1;
}
if( (unsigned long)nlstat.st_size != nlsize )
{
log("invalid nodelist index: incorrect nodelist size %ld (expected %ld)",
(long)nlstat.st_size, (long)nlsize);
return -1;
}
return 0;
}
/*****************************************************************************
* Create/update nodelist index header. Nodelist must be opened for
* writing. Now nodelist index header contain nodelist modification
* time and nodelist size.
*
* Arguments:
* nlp opened nodelist
*
* Return value:
* zero value on success, and -1 at errors
*/
int nodelist_createheader(s_nodelist *nlp)
{
struct stat nlstat;
char hdrbuf[NODELIST_HDRSIZE];
memset(&hdrbuf, '\0', sizeof(hdrbuf));
if( fstat(fileno(nlp->fp_nodelist), &nlstat) == -1 )
{
logerr("cannot stat nodelist \"%s\"", nlp->name_nodelist);
return -1;
}
buffer_putlong(hdrbuf + 0, nlstat.st_mtime);
buffer_putlong(hdrbuf + 4, nlstat.st_size);
if( fseek(nlp->fp_index, 0L, SEEK_SET) == -1 )
{
logerr("cannot seek to zero offset of index");
return -1;
}
if( fwrite(hdrbuf, sizeof(hdrbuf), 1, nlp->fp_index) != 1 )
{
logerr("cannot write nodelist index header");
return -1;
}
return 0;
}
/*****************************************************************************
* Close nodelist and nodelist index files
*
* Arguments:
* nlp pointer to the opened nodelist
*
* Return value:
* Zero value if close was successful, and non-zero if not
*/
int nodelist_close(s_nodelist *nlp)
{
int rc = 0;
ASSERT(nlp && nlp->fp_nodelist && nlp->fp_index);
if( nlp->fp_nodelist && file_close(nlp->fp_nodelist) )
logerr("cannot close nodelist \"%s\"", nlp->name_nodelist);
if( nlp->fp_index && file_close(nlp->fp_index) )
{
logerr("cannot close nodelist index \"%s\"", nlp->name_index);
rc = 1;
}
free(nlp);
return rc;
}
int nodelist_putindex(s_nodelist *nlp, const s_bni *bni)
{
char buffer[NODELIST_ENTRYSIZE];
ASSERT(nlp && nlp->fp_index);
buffer_putint(buffer + 0, bni->zone);
buffer_putint(buffer + 2, bni->net);
buffer_putint(buffer + 4, bni->node);
buffer_putint(buffer + 6, bni->point);
buffer_putint(buffer + 8, bni->hub);
buffer_putlong(buffer + 10, bni->offset);
if( fwrite(buffer, sizeof(buffer), 1, nlp->fp_index) != 1 )
{
logerr("error writing nodelist index file \"%s\"", nlp->name_index);
return -1;
}
return 0;
}
int nodelist_findindex(s_nodelist *nlp, s_bni *bni, s_faddr addr)
{
long readitems;
char buffer[NODELIST_ENTRYSIZE * NODELIST_READAHEAD];
char *p;
if( fseek(nlp->fp_index, NODELIST_HDRSIZE, SEEK_SET) ) return -1;
while(1)
{
if( (readitems = fread(buffer, NODELIST_ENTRYSIZE, NODELIST_READAHEAD, nlp->fp_index)) > 0 )
{
for( p = buffer; readitems > 0; readitems-- )
{
if( buffer_getint(p + 0) == addr.zone
&& buffer_getint(p + 2) == addr.net
&& buffer_getint(p + 4) == addr.node
&& buffer_getint(p + 6) == addr.point )
{
bni->zone = buffer_getint(p + 0);
bni->net = buffer_getint(p + 2);
bni->node = buffer_getint(p + 4);
bni->point = buffer_getint(p + 6);
bni->hub = buffer_getint(p + 8);
bni->offset = buffer_getlong(p + 10);
return 0;
}
p += NODELIST_ENTRYSIZE;
}
} else
return -1;
}
return -1;
}
int nodelist_getstr(s_nodelist *nlp, size_t offset, char *buffer, size_t buflen)
{
ASSERT(nlp && nlp->fp_nodelist);
if( fseek(nlp->fp_nodelist, offset, SEEK_SET) == 0
&& fgets(buffer, buflen, nlp->fp_nodelist) )
{
string_chomp(buffer);
return 0;
}
return -1;
}
int nodelist_lookup_string(char *buffer, size_t buflen, s_faddr addr)
{
s_bni bni;
s_cval_entry *ptrl;
s_nodelist *nlp;
const char *ndldir = NULL;
ndldir = conf_string(cf_nodelist_directory);
/*
* Try all nodelists with matching address mask
*/
for( ptrl = conf_first(cf_nodelist); ptrl; ptrl = conf_next(ptrl) )
{
if( ftn_addrcomp_mask(addr, ptrl->d.falist.addr) == 0 )
{
if( (nlp = nodelist_open(ndldir, ptrl->d.falist.what, NODELIST_READ)) )
{
int rc = nodelist_findindex(nlp, &bni, addr);
if( !rc )
rc = nodelist_getstr(nlp, bni.offset, buffer, buflen);
nodelist_close(nlp);
if( !rc )
return 0;
}
}
}
return -1;
}
int nodelist_lookup(s_node *node, s_faddr addr)
{
char buf[512];
char abuf[BF_MAXADDRSTR+1];
nodelist_initnode(node, addr);
if( nodelist_lookup_string(buf, sizeof(buf), addr) == 0 )
{
node->listed = TRUE;
if( nodelist_parsestring(node, buf) == -1 )
{
log("invalid nodelist string for address %s",
ftn_addrstr(abuf, addr));
}
return 0;
}
return -1;
}
void nodelist_initnode(s_node *node, s_faddr addr)
{
memset(node, '\0', sizeof(s_node));
node->addr = addr;
strcpy(node->name, "<none>");
strcpy(node->sysop, "<none>");
strcpy(node->location, "<none>");
strcpy(node->phone, "<none>");
}