/*
* 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. */
#ifdef SOCKS
#include <socks.h>
#endif
#include <assert.h>
#include <stdarg.h>
#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>
#ifndef NOFNMATCH
#include <fnmatch.h>
#endif
#ifdef HAVE_POSIX_REGCOMP
#include <regex.h>
#endif
#ifdef HAVE_REGCMP
char *regcmp();
char *regex();
extern char *__loc1;
#endif
#ifdef HAVE_V8_REGCOMP
#include <regexp.h>
#endif
#include "leafnode.h"
char* fqdn = NULL;
int verbose = 0;
char* getaheader(FILE* f, const char* h)
{
int hlen = strlen(h);
static char* buf; /* buffer for line */
static size_t size; /* size of buffer */
size_t len; /* # of chars stored into buf before '\0' */
char* l;
if (!f)
return "";
if (!size)
size = 256;
if (!buf)
buf = critmalloc(size, "reading header");
/* look for header: */
do {
l = getaline(f);
} while (l && *l && (l[hlen] != ':' || strncasecmp(h, l, hlen)));
/* not find, then return "" */
if (!l || !*l) {
rewind(f);
return "";
}
/* process following lines */
len = 0;
buf[len] = 0;
do {
int llen = strlen(l);
if (len + llen > size) {
size = len + llen + 100;
buf = critrealloc(buf, size, "extending header");
}
strcat(&buf[len], l);
len += llen;
l = getaline(f);
} while (l && isspace(*l));
/* split only header: value */
l = skipspaces(skipnonspaces(buf));
/* rewind f and return header or "" */
rewind(f);
return l;
}
void removearts(const char* msgid)
{
struct articles* arts;
int i;
struct stat st;
const char* p;
assert(msgid != NULL && *msgid != '\0');
arts = findarticles(msgid);
if (arts == 0)
return;
for (i = 0; i < arts->num; i++) {
p = getartfname(arts->arts[i].ng, arts->arts[i].art);
if (unlink(p) < 0) {
if (errno != ENOTDIR && errno != ENOENT) {
mysyslog(LOG_WARNING, "failed to unlink %s: %s",
p, strerror(errno));
}
} else {
if (verbose > 1)
printf("removed %s\n", p);
}
}
p = getmsgidfname(msgid);
if (stat(p, &st) == 0) {
if (st.st_nlink == 1) {
if (unlink(p) < 0) {
mysyslog(LOG_WARNING, "failed to unlink %s: %s",
p, strerror(errno));
} else {
if (verbose > 1)
printf("removed %s\n", p);
}
} else {
mysyslog(LOG_WARNING, "Leave %s because it has links more than 1",
p);
}
} else {
mysyslog(LOG_WARNING, "failed to unlink %s since it isn't be there", p);
}
}
/*
* replacement for malloc, syslogs allocation failures
* and exits with the error message
*/
void* critmalloc(size_t size, const char* message)
{
void* a;
a = malloc(size);
if (!a) {
mysyslog(LOG_ERR, "malloc(%d) failed: %s: %s",
(int)size, message, strerror(errno));
exit(1);
}
return a;
}
/*
* replacement for realloc, syslogs allocation failures
* and exits with the error message
*/
void* critrealloc(void* a, size_t size, const char* message)
{
a = realloc(a, size);
if (!a) {
mysyslog(LOG_ERR, "realloc(%d) failed: %s: %s",
(int)size, message, strerror(errno));
exit(1);
}
return a;
}
#define LM_SIZE 65536
static void makedir(const char* d)
{
static struct string a = { 0, 0 };
char* p;
char* q;
struct stat st;
if (d == 0 || *d != '/')
return;
setstring(&a, d);
if (stat(a.str, &st) == 0 && S_ISDIR(st.st_mode))
return;
for (p = a.str + strlen(a.str) - 1; p > a.str; --p) {
if (*p == '/') {
*p = '\0';
if (stat(a.str, &st) == 0 && S_ISDIR(st.st_mode))
break;
*p = '/';
}
}
while ((q = strchr(&p[1], '/')) != 0) {
*q = '\0';
*p = '/';
if (mkdir(a.str, 0775) < 0) {
mysyslog(LOG_ERR, "mkdir %s: %s", d, strerror(errno));
exit(1);
}
p = q;
}
*p = '/';
if (mkdir(a.str, 0775) < 0) {
mysyslog(LOG_ERR, "mkdir %s: %s", d, strerror(errno));
exit(1);
}
}
/* chdir to the directory of the argument if it's a valid group
return TRUE if it is valid, FALSE otherwise */
int chdirgroup(const struct newsgroup* ng, int creatf)
{
static const struct newsgroup* lastng = 0;
const char* s;
if (ng == 0)
return 0;
if (lastng == ng)
return 1; /* group is same as a last chdirgrouped group */
lastng = ng;
/* chdir for the new group */
s = getngdname(ng);
if (chdir(s) < 0) {
if (!creatf)
return 0; /* don't change directory */
makedir(s);
if (chdir(s) < 0)
return 0;
}
return 1;
}
/*
* get active file from remote server
*/
void nntpactive(void)
{
char* l;
if (verbose)
printf("Reading list of active newsgroups\n");
sprintf(lineout, "LIST ACTIVE\r\n");
putaline();
if (nntpreply() != 215)
return;
while ((l = getaline(nntpin)) && *l != '.') {
char* p;
p = skipnonspaces(l);
if (*p)
*p++ = '\0';
/*
* Doesn't register all of newsgroups to local groupinfo.
* Skips some of them if "newgroups" or "filteredngs" are defined.
*/
if (servers->newsgroups.num > 0) {
/* should we register this news group? */
int i;
for (i = 0; i < servers->newsgroups.num; i++) {
if (myfnmatch(servers->newsgroups.strarray[i], l) == 0)
break;
}
/* no, skip this news group. */
if (i >= servers->newsgroups.num) {
if (verbose > 1)
printf("Not registering %s since it isn't in newsgroups list\n", l);
continue;
}
}
if (servers->filteredngs.num > 0) {
/* should we filter this news group? */
int i;
for (i = 0; i < servers->filteredngs.num; i++) {
if (myfnmatch(servers->filteredngs.strarray[i], l) == 0)
break;
}
/* yes, skip this news group. */
if (i < servers->filteredngs.num) {
if (verbose > 1)
printf("Not registering %s since it is in filter list\n", l);
continue;
}
}
if (!findgroup(l)) {
/*
* There are no article, so we start it from 2.
* And we have to minus one again to build pseudo article.
* Currently server's last article number is read
* from each server's information file. So we use 0 here.
*/
insertgroup(l, 2, 1, "", 0);
if (verbose > 1)
printf("Registered group %s\n", l);
}
}
if (verbose)
printf("Reading newsgroup descriptions\n");
/*
* add descriptions
*/
fprintf(nntpout, "LIST NEWSGROUPS\r\n");
fflush(nntpout);
if (nntpreply() == 215) {
while ((l = getaline(nntpin)) && *l != '.') {
char* p;
struct newsgroup* g;
p = skipnonspaces(l);
if (*p)
*p++ = '\0';
p = skipspaces(p);
if ((g = findgroup(l)) != 0) {
if (*p && legalnewsdesc(p)) {
if (g->desc && strcmp(g->desc, p) == 0) {
/* nothing to do */
} else {
if (verbose)
printf("new description of %s: '%s' changed from "
"'%s'\n", g->name, p, g->desc);
g->desc = strdup(p); /* discard original data */
}
} else {
if (verbose > 1) {
printf("ignore a description since it is wrong: "
"%s: %s\n", l, p);
}
}
} else {
if (verbose > 2)
printf("ignore a description since there is no group: "
"%s: %s\n", l, p);
}
}
}
}
/*
* correct newsgroup descriptions
*/
static void helpcorrectnewsdesc(struct newsgroup* g)
{
if (g) {
helpcorrectnewsdesc(g->right);
if (strcmp(g->desc, "-x-") != 0 && !legalnewsdesc(g->desc)) {
if (verbose)
printf("Deleting wrong news description: %s, %s\n",
g->name, g->desc);
g->desc = "";
}
helpcorrectnewsdesc(g->left);
}
}
void correctnewsdesc(void)
{
helpcorrectnewsdesc(active);
}
static void clearserver(struct newsgroup* g)
{
if (g) {
clearserver(g->right);
clearserver(g->left);
g->server = 0;
g->newarticles = 0;
}
}
void readserver(void)
{
char* p;
char* q;
unsigned long server;
int fd;
struct newsgroup * g;
struct stat st;
static char* stuff;
const char* s;
if (stuff) {
free((char*)stuff);
stuff = NULL;
}
s = getserverfname(servers->servername);
fd = open(s, O_RDONLY);
if (stat(s, &st) < 0) {
mysyslog(LOG_ERR, "can't stat %s: %s", s, strerror(errno));
return;
}
clearserver(active);
stuff = critmalloc(st.st_size + 1, "Reading group info");
if ((fd = open(s, O_RDONLY)) < 0 ||
read(fd, stuff, st.st_size) < st.st_size) {
mysyslog(LOG_ERR, "can't open/read %s: %s", s, strerror(errno));
return;
} else {
close(fd);
stuff[st.st_size] = '\0'; /* 0-terminate string */
}
p = stuff;
while (p && *p) {
q = p;
p = strchr(p, ' ');
if (p && *p)
*p++ = '\0';
if (*q) {
server = p ? strtoul(p, &p, 10) : 0;
if ((g = findgroup(q)) == NULL) {
mysyslog(LOG_ERR, "can't find group %s", q);
} else {
g->server = server;
}
}
p = strchr(p, '\n');
if (p)
p++;
}
}
static void helpwriteserver(struct newsgroup* g, FILE* f)
{
if (g) {
helpwriteserver(g->right, f);
if (g->server > 0)
fprintf(f, "%s %lu\n", g->name, g->server);
helpwriteserver(g->left, f);
}
}
void writeserver(void)
{
FILE* a;
const char* s;
char c[PATH_MAX];
s = getservernewfname(servers->servername);
a = fopen(s, "w");
if (!a)
return;
helpwriteserver(active, a);
fclose(a);
strcpy(c, s);
s = getserverfname(servers->servername);
rename(c, s);
}
/* get the fully qualified domain name of this box into fqdn */
void whoami(void)
{
#ifdef INET6
struct addrinfo hints, *res0;
#else
struct hostent* he;
#endif
#ifdef NI_MAXHOST
char name[NI_MAXHOST];
#else
char name[1025];
#endif
if (fqdn != NULL) {
/* fqdn is already set (via config file) */
return;
}
#ifdef INET6
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_CANONNAME;
if (!gethostname(name, sizeof(name)) &&
!getaddrinfo(name, NULL, &hints, &res0)) {
if(res0->ai_canonname){
strncpy(name, res0->ai_canonname, sizeof(name));
}
freeaddrinfo(res0);
fqdn = strdup(name);
} else {
fqdn = NULL;
}
#else
if (!gethostname(name, sizeof(name)) &&
(he = gethostbyname(name)) != NULL) {
strncpy(name, he->h_name, sizeof(name));
name[sizeof(name)-1] = '\0';
if (strchr(name, '.') == NULL) {
char** alias;
alias = he->h_aliases;
while (alias && *alias) {
if (strchr(*alias, '.') && (strlen(*alias) > strlen(name))) {
strncpy(name, *alias, sizeof(name));
name[sizeof(name)-1] = '\0';
} else {
alias++;
}
}
}
fqdn = strdup(name);
} else {
fqdn = NULL;
}
#endif
}
/* next few routines implement a mapping from message-id to article
number, and clearing the entire space */
struct msgidtree {
struct msgidtree* left;
struct msgidtree* right;
unsigned long art;
char msgid[1];
};
static struct msgidtree* head; /* starts as NULL */
void insertmsgid(const char* msgid, unsigned long art)
{
struct msgidtree** a;
int c;
if (strchr(msgid, '@') == 0)
return;
a = &head;
while (a) {
if (*a) {
/* comparing only by msgid is uncool because the tree becomes
very unbalanced */
c = strcmp(strchr((*a)->msgid, '@'), strchr(msgid, '@'));
if (c == 0)
c = strcmp((*a)->msgid, msgid);
if (c < 0)
a = &((*a)->left);
else if (c > 0)
a = &((*a)->right);
else {
return;
}
} else {
*a = (struct msgidtree *)
critmalloc(sizeof(struct msgidtree) + strlen(msgid),
"Building expiry database");
(*a)->left = (*a)->right = NULL;
strcpy((*a)->msgid, msgid);
(*a)->art = art;
return;
}
}
}
unsigned long findmsgid( const char* msgid)
{
struct msgidtree* a;
int c;
char* domainpart;
/* domain part differs more than local-part, so try it first */
domainpart = strchr(msgid, '@');
if (domainpart == NULL)
return 0;
a = head;
while (a) {
c = strcmp(strchr(a->msgid, '@'), domainpart);
if (c == 0)
c = strcmp(a->msgid, msgid);
if (c < 0)
a = a->left;
else if (c > 0)
a = a->right;
else
return a->art;
}
return 0;
}
static void begone(struct msgidtree* m)
{
if (m) {
begone(m->right);
begone(m->left);
free((char*)m);
}
}
void clearidtree(void)
{
if (head) {
begone(head->right);
begone(head->left);
free((char*)head);
}
head = NULL;
}
struct newsgroup* getexpire(struct newsgroup* g, char* l)
{
struct newsgroup* dummy = NULL;
if (g) {
if (strcasecmp(l, g->name) == 0)
dummy = g;
else if ((dummy = getexpire(g->left, l)) == NULL)
dummy = getexpire(g->right, l);
}
return dummy;
}
/*
05/25/97 - T. Sweeney - Modified to read user name and password for AUTHINFO.
Security questionable as password is stored in
plaintext in insecure file.
*/
/* this thing -really- needs a redo. that many parameters means that
cut and paste is not a good strategy any more */
int artlimit = 0;
int initiallimit = 0;
int maxold = 10;
int maxlines = 0;
int minlines = 0;
int maxbytes = 0;
int maxgroups = 0;
struct strlist killsubject = {0, 0, 0};
struct strlist killsubjecti = {0, 0, 0};
struct strlist killfrom = {0, 0, 0};
struct strlist killfromaddress = {0, 0, 0};
time_t expire;
long timeout_short = TIMEOUT_SHORT;
long timeout_long = TIMEOUT_LONG;
long timeout_active = TIMEOUT_ACTIVE;
struct groupexpires groupexpires = {0, 0, 0};
struct server * servers;
struct newsgroup * g;
void addexpire(struct groupexpires* p, const struct groupexpire* a)
{
if (p->num + 1 >= p->size) {
p->size = (p->num + 1 + 255) / 256 * 256;
p->array = critrealloc(p->array, p->size * sizeof(struct groupexpire),
"addexpire");
}
p->array[p->num++] = *a;
}
static struct server* reverse_servers(struct server* list)
{
static int postable = 1;
if (list->postable)
list->postable = postable++;
if (list->next) {
struct server* reversed_rest = reverse_servers(list->next);
struct server* p;
for (p = reversed_rest; p->next; p = p->next)
;
p->next = list;
list->next = NULL;
return reversed_rest;
} else {
return list;
}
}
static int parsestring(char* l, char* string, char** value)
{
int len = strlen(string);
char* p;
if (strncasecmp(l, string, len) == 0) {
p = skipspaces(l + len);
if (*p == '=') {
p = skipspaces(p + 1);
*value = strdup(p);
} else {
mysyslog(LOG_ERR, "unable to parse %s (%s has no =)", string, l);
exit(2);
}
return 1;
} else {
return 0;
}
}
static int parselong(char* l, char* string, long* value)
{
int len = strlen(string);
char* p;
if (strncasecmp(l, string, len) == 0) {
p = skipspaces(l + len);
if (*p == '=') {
unsigned long i;
char* q;
p = skipspaces(p + 1);
q = skipnonspaces(p);
*q = '\0';
i = strtoul(p, &q, 0);
if (p && *p && q && *q == '\0') {
*value = i;
} else {
mysyslog(LOG_ERR, "unable to parse %s (%s number)", string, l);
exit(2);
}
} else {
mysyslog(LOG_ERR, "unable to parse %s (%s has no =)", string, l);
exit(2);
}
return 1;
} else {
return 0;
}
}
static int parsestrlong(char* l, char* string, char** svalue, long* lvalue)
{
int len = strlen(string);
char* p;
if (strncasecmp(l, string, len) == 0) {
p = skipspaces(l + len);
if (*p == '=') {
unsigned long i;
char* q;
p = skipspaces(p + 1);
q = skipnonspaces(p);
if (*q == '\0') {
mysyslog(LOG_ERR, "unable to parse %s (%s has only string "
"not number)", string, l);
exit(2);
}
*q = '\0';
*svalue = strdup(p);
p = skipnonspaces(++q);
*p = '\0';
i = strtoul(q, &p, 0);
if (q && *q && p && *p == '\0') {
*lvalue = i;
} else {
mysyslog(LOG_ERR, "unable to parse %s (%s has only string "
"not number)", string, l);
exit(2);
}
} else {
mysyslog(LOG_ERR, "unable to parse %s (%s has no =)", string, l);
exit(2);
}
return 1;
} else {
return 0;
}
}
void readconfig(void)
{
FILE* f;
char* l;
char* p;
const char* s;
long lvalue;
char* svalue;
servers = NULL;
s = getconfigfname();
if ((f = fopen(s, "r"))) {
while ((l = getaline(f))) {
p = strchr(l, '#');
if (p)
*p = '\0';
l = skipspaces(l);
if (parsestring(l, "server", &svalue)) {
/* get server value */
struct server* ns = (struct server*)
critmalloc(sizeof(struct server),
"allocating for server info");
ns->servername = svalue;
ns->viahost = 0;
ns->port = 0;
ns->preconnect = 0;
ns->newsgroups.num = 0;
ns->newsgroups.size = 0;
ns->newsgroups.strarray = 0;
ns->filteredngs.num = 0;
ns->filteredngs.size = 0;
ns->filteredngs.strarray = 0;
if (!servers)
ns->postable = 1; /* first server is marked as postable */
else
ns->postable = 0; /* others are not marked */
ns->username = 0;
ns->password = 0;
ns->timeout_open = TIMEOUT_OPEN;
ns->timeout_read = TIMEOUT_READ;
ns->next = servers;
servers = ns;
} else if (parsestring(l, "viahost", &svalue)) {
/* get viahost value */
if (servers) {
servers->viahost = svalue;
} else {
mysyslog(LOG_ERR, "no servers before viahost");
exit(2);
}
} else if (parselong(l, "port", &lvalue)) {
/* get port value */
if (servers) {
servers->port = lvalue;
} else {
mysyslog(LOG_ERR, "no servers number before port");
exit(2);
}
} else if (parsestring(l, "preconnect", &svalue)) {
/* get preconnect value */
if (servers) {
servers->preconnect = svalue;
} else {
mysyslog(LOG_ERR, "no servers before preconnect");
exit(2);
}
} else if (parsestring(l, "newsgroups", &svalue)) {
/* get newsgroups value */
if (servers) {
addstr(&servers->newsgroups, svalue);
} else {
mysyslog(LOG_ERR, "no servers before newsgroups");
exit(2);
}
} else if (parsestring(l, "filteredngs", &svalue)) {
/* get filteredngs value */
if (servers) {
addstr(&servers->filteredngs, svalue);
} else {
mysyslog(LOG_ERR, "no servers before filteredngs");
exit(2);
}
} else if (parselong(l, "postable", &lvalue)) {
/* get postable value */
if (servers) {
servers->postable = lvalue;
} else {
mysyslog(LOG_ERR, "no servers before postable");
exit(2);
}
} else if (parsestring(l, "username", &svalue)) {
/* get username value */
if (servers) {
servers->username = svalue;
} else {
mysyslog(LOG_ERR, "no servers before username");
exit(2);
}
} else if (parsestring(l, "password", &svalue)) {
/* get password value */
if (servers) {
servers->password = svalue;
} else {
mysyslog(LOG_ERR, "no server before password");
exit(2);
}
} else if (parselong(l, "timeout_open", &lvalue)) {
/* get timeout_short value */
if (servers) {
servers->timeout_open = lvalue;
} else {
mysyslog(LOG_ERR, "no servers before timeout_open");
exit(2);
}
} else if (parselong(l, "timeout_read", &lvalue)) {
/* get timeout_read value */
if (servers) {
servers->timeout_read = lvalue;
} else {
mysyslog(LOG_ERR, "no servers before timeout_read");
exit(2);
}
} else if (parselong(l, "expire", &lvalue)) {
/* get expire value */
expire = time(NULL)-(time_t)(86400*lvalue);
} else if (parsestrlong(l, "groupexpire", &svalue, &lvalue)) {
/* get expire value */
struct groupexpire a;
a.group = svalue;
a.xtime = time(NULL)-(time_t)(86400*lvalue);
addexpire(&groupexpires, &a);
} else if (parselong(l, "timeout_short", &lvalue)) {
/* get timeout_short value */
timeout_short = lvalue;
} else if (parselong(l, "timeout_long", &lvalue)) {
/* get timeout_long value */
timeout_long = lvalue;
} else if (parselong(l, "timeout_active", &lvalue)) {
/* get timeout_active value */
timeout_active = lvalue;
} else if (parselong(l, "maxcount", &lvalue)) {
/* get maxcount value */
artlimit = lvalue;
} else if (parselong(l, "maxfetch", &lvalue)) {
/* get maxfetch value */
artlimit = lvalue;
} else if (parselong(l, "initialfetch", &lvalue)) {
/* get initialfetch value */
initiallimit = lvalue;
} else if (parselong(l, "maxold", &lvalue)) {
/* get maxold value */
maxold = lvalue;
} else if (parselong(l, "maxage", &lvalue)) {
/* get maxage value; for compatibility with leafnode-1.7 */
maxold = lvalue;
} else if (parselong(l, "maxlines", &lvalue)) {
/* get maxlines value */
maxlines = lvalue;
} else if (parselong(l, "minlines", &lvalue)) {
/* get minlines value; for compatibility with leafnode-1.7 */
minlines = lvalue;
} else if (parselong(l, "maxbytes", &lvalue)) {
/* get maxbytes value; for compatibility with leafnode-1.7 */
maxbytes = lvalue;
} else if (parselong(l, "maxgroups", &lvalue)) {
/* get maxgroups value */
maxgroups = lvalue;
} else if (parselong(l, "maxcrosspost", &lvalue)) {
/* get maxcrosspost value; for compatibility with */
/* leafnode-1.7 */
maxgroups = lvalue;
} else if (parsestring(l, "killsubjecti", &svalue)) {
/* get killsubjecti value */
addstr(&killsubjecti, svalue);
} else if (parsestring(l, "killsubject", &svalue)) {
/* get killsubject value */
addstr(&killsubject, svalue);
} else if (parsestring(l, "killfromaddress", &svalue)) {
/* get killfromaddress value */
addstr(&killfromaddress, svalue);
} else if (parsestring(l, "killfrom", &svalue)) {
/* get killfrom value */
addstr(&killfrom, svalue);
} else if (parsestring(l, "create_all_links", &svalue)) {
/* get create_all_links flag; for compatibility with */
/* leafnode-1.7 */
/* XXX: But it is not implemented yet. */
} else if (parselong(l, "delay_body", &lvalue)) {
/* get delay_body value; for compatibility with leafnode-1.7 */
/* XXX: But it is not implemented yet. */
} else if (parsestring(l, "supplement", &svalue)) {
/* get supplement value */
struct server* ns = (struct server*)
critmalloc(sizeof(struct server),
"allocating for supplementary server info");
ns->servername = svalue;
ns->viahost = 0;
ns->port = 0;
ns->preconnect = 0;
ns->newsgroups.num = 0;
ns->newsgroups.size = 0;
ns->newsgroups.strarray = 0;
ns->filteredngs.num = 0;
ns->filteredngs.size = 0;
ns->filteredngs.strarray = 0;
ns->postable = 0;
ns->username = 0;
ns->password = 0;
ns->timeout_open = TIMEOUT_OPEN;
ns->timeout_read = TIMEOUT_READ;
ns->next = servers;
servers = ns;
} else if (parsestring(l, "fqdn", &svalue)) {
/* get fqdn value */
fqdn = svalue;
}
}
/* reverse servers and calcurate postable number */
servers = reverse_servers(servers);
} else {
mysyslog(LOG_ERR, "cannot open %s", s);
}
}
/* return 1 if newsdesc is a legal newsgroup description, 0 else */
int legalnewsdesc(const char* newsdesc)
{
if (newsdesc && *newsdesc &&
strcmp(newsdesc, "?") != 0 &&
strcmp(newsdesc, "-x-") != 0 &&
strcmp(newsdesc, "???") != 0 &&
strncasecmp(newsdesc, "No description", 14) != 0)
return 1;
else
return 0;
}
void stripspace(char* p)
{
char* p1;
char* p2;
p1 = p2 = p;
while (*p1) {
if (isspace(*p1)) {
*p2 = ' ';
do {
p1++;
} while (isspace(*p1));
if (*p1)
p2++;
} else {
*p2++ = *p1++;
}
}
*p2 = '\0';
}
const char* rfctime(void)
{
static char date[128];
const char* months[] = { "Jan", "Feb", "Mar", "Apr",
"May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
const char* days[] = { "Sun", "Mon", "Tue", "Wed",
"Thu", "Fri", "Sat" };
time_t now;
struct tm gmt;
struct tm local;
struct tm conv;
signed int diff;
/* get local and Greenwich times */
now = time(0);
gmt = *(gmtime(&now));
local = *(localtime(&now));
conv = *(localtime(&now));
/* if GMT were local, what would it be? */
conv.tm_year = gmt.tm_year;
conv.tm_mon = gmt.tm_mon;
conv.tm_mday = gmt.tm_mday;
conv.tm_hour = gmt.tm_hour;
conv.tm_min = gmt.tm_min;
conv.tm_sec = gmt.tm_sec;
conv.tm_wday = gmt.tm_wday;
conv.tm_yday = gmt.tm_yday;
/* see how far the real local is from GMT-as-local */
/* should convert to signed int from double properly */
diff = (mktime(&local) - mktime(&conv)) / 60.0;
/* finally print the string */
sprintf(date, "%3s, %d %3s %4d %02d:%02d:%02d %+03d%02d",
days[local.tm_wday], local.tm_mday, months[local.tm_mon],
local.tm_year+1900, local.tm_hour, local.tm_min, local.tm_sec,
diff/60, diff%60);
return (date);
}
char* skipspaces(char* p)
{
if (p == 0)
return 0;
while (isspace(*p))
p++;
return p;
}
char* skipnonspaces(char* p)
{
if (p == 0)
return 0;
while (*p && !isspace(*p))
p++;
return p;
}
char* skipre(char* p)
{
int i;
if ((p[0] == 'R' || p[0] == 'r') && (p[1] == 'E' || p[1] == 'e')) {
if (p[2] != '^' || p[3] < '0' || p[3] > '9') {
i = 2;
} else {
/* allow Re^2: */
for (i = 4; p[i] >= '0' && p[i] <= '9'; i++);
}
if (p[i] == ':') p += i + 1;
}
return p;
}
void strlower(char* p)
{
while (*p) {
*p = tolower(*p);
p++;
}
}
void setstring(struct string* p, const char* str)
{
int len = strlen(str);
if (len + 1 > p->size) {
p->size = ((len + 1 + 255) / 256) * 256;
p->str = critrealloc(p->str, p->size, "setstring");
}
strcpy(p->str, str);
}
void addstr(struct strlist* p, char* str)
{
if (p->num + 1 > p->size) {
p->size = ((p->size + 1 + 63) / 64) * 64;
p->strarray = critrealloc(p->strarray, p->size * sizeof(char*),
"addstr");
}
p->strarray[p->num++] = str;
}
void clearstrlist(struct strlist* p)
{
int i;
for (i = 0; i < p->num; i++) {
free(p->strarray[i]);
}
if (p->strarray)
free(p->strarray);
p->num = 0;
}
void initpatterns(struct patterns* p, struct strlist* patlist, int icase)
{
if (p->num > 0)
clearpatterns(p);
if (patlist->num > 0) {
/* compile patterns. */
int i;
char* pat;
#ifdef HAVE_POSIX_REGCOMP
regex_t* array = (regex_t*)critmalloc(sizeof(regex_t) * patlist->num,
"compiling patterns");
int err;
char errbuf[2048];
#endif
#ifdef HAVE_REGCMP
char** array = (char**)critmalloc(sizeof(char*) * patlist->num,
"compiling patterns");
#endif
#ifdef HAVE_V8_REGCOMP
struct regexp** array = (struct regexp**)
critmalloc(sizeof(struct regexp*) * patlist->num,
"compiling patterns");
#endif
#ifdef NO_RE
char** array = (char**)critmalloc(sizeof(char*) * patlist->num,
"compiling patterns");
#endif
p->num = patlist->num;
p->icase = icase;
p->patarray = array;
for (i = 0; i < patlist->num; i++) {
static struct string a = { 0, 0 };
pat = patlist->strarray[i];
if (icase) {
setstring(&a, pat);
strlower(a.str);
pat = a.str;
}
#ifdef HAVE_POSIX_REGCOMP
if ((err = regcomp(&array[i], pat, REG_EXTENDED)) != 0) {
regerror(err, &array[i], errbuf, sizeof(errbuf));
mysyslog(LOG_ERR, "regcomp: %s", errbuf);
}
#endif
#ifdef HAVE_REGCMP
if ((array[i] = regcmp(pat, NULL)) == NULL) {
mysyslog(LOG_ERR, "regcomp: invalid pattern: %s", pat);
}
#endif
#ifdef HAVE_V8_REGCOMP
if ((array[i] = regcomp(pat)) == NULL) {
mysyslog(LOG_ERR, "regcomp: invalid pattern: %s", pat);
}
#endif
#ifdef NO_RE
if ((array[i] = strdup(pat)) == NULL) {
mysyslog(LOG_ERR, "strdup: %s", strerror(errno));
}
#endif
}
}
}
int matchpatterns(struct patterns* p, char* str)
{
int i;
if (p->icase) {
static struct string a = { 0, 0 };
setstring(&a, str);
strlower(a.str);
str = a.str;
}
for (i = 0; i < p->num; i++) {
#ifdef HAVE_POSIX_REGCOMP
int err;
char errbuf[2048];
regex_t* array = (regex_t*)p->patarray;
if ((err = regexec(&array[i], str, 0, NULL, 0)) == 0) {
return i;
} else if (err != REG_NOMATCH) {
regerror(err, &array[i], errbuf, sizeof(errbuf));
mysyslog(LOG_ERR, "regexec: %s", errbuf);
}
#endif
#ifdef HAVE_REGCMP
char** array = (char**)p->patarray;
if (regex(array[i], str) != NULL) {
return i;
}
#endif
#ifdef HAVE_V8_REGCOMP
struct regexp** array = (struct regexp**)p->patarray;
if (regexec(array[i], str)) {
return i;
}
#endif
#ifdef NO_RE
char** array = (char**)p->patarray;
if (strstr(str, array[i]) != NULL) {
return i;
}
#endif
}
return -1;
}
void clearpatterns(struct patterns* p)
{
if (p->num > 0) {
int i;
for (i = 0; i < p->num; i++) {
#ifdef HAVE_POSIX_REGCOMP
regex_t* array = (regex_t*)p->patarray;
regfree(&array[i]);
#endif
#ifdef HAVE_REGCMP
char** array = (char**)p->patarray;
free(array[i]);
#endif
#ifdef HAVE_V8_REGCOMP
struct regexp** array = (struct regexp**)p->patarray;
free(array[i]);
#endif
#ifdef NO_RE
char** array = (char**)p->patarray;
free(array[i]);
#endif
}
free(p->patarray);
p->num = 0;
}
}
#ifndef HAVE_STRDUP
char* strdup(const char* s)
{
char* s1;
s1 = malloc(strlen(s) + 1);
if (!s1)
return 0;
strcpy(s1, s);
return s1;
}
#endif
int myfnmatch(const char* pattern, const char* str)
{
#ifndef NOFNMATCH
return fnmatch(pattern, str, 0);
#else
int len = strlen(pattern);
if (pattern[0] == '*') {
if (len == 1) {
/* If a pattern is '*', any strings are matched. */
return 0;
} else if (pattern[len - 1] != '*') {
/* If a pattern has leading '*' and no other '*' at the end, */
/* compare it at the end of string. */
len = strlen(str) - len + 1; /* skip leading '*' */
if (len >= 0 && strcmp(pattern + 1, str + len) == 0)
return 0;
} else if (len == 2) {
/* If a pattern is '**', any strings are matched. */
return 0;
} else {
/* If a pattern has '*' at the beginning and the end, */
/* comprare it with all substrings of a string. */
if (len == 3) {
if (strchr(str, pattern[1]))
return 0;
} else {
/* make a new pattern removed '*' from beginning and end. */
static struct string a = { 0, 0 };
setstring(&a, &pattern[1]);
a.str[len - 2] = '\0';
/* compare it with all substrings of the given string. */
if (strstr(str, a.str) != NULL)
return 0;
}
}
} else if (pattern[len - 1] == '*') {
/* If pattern is terminated with '*', compare it at the beginning. */
if (strncmp(pattern, str, len - 1) == 0)
return 0;
} else {
/* Otherwise, treat string as just string. */
if (strcmp(pattern, str) == 0)
return 0;
}
return 1;
#endif
}
void mysyslog(int priority, const char* message, ...)
{
va_list ap;
va_start(ap, message);
vsyslog(priority, message, ap);
if (priority <= LOG_ERR) {
vprintf(message, ap);
printf("\n");
} else if (priority == LOG_WARNING) {
if (verbose) {
vprintf(message, ap);
printf("\n");
}
} else {
if (verbose > 1) {
vprintf(message, ap);
printf("\n");
}
}
va_end(ap);
}
syntax highlighted by Code2HTML, v. 0.9.1