/* * 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 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 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef NOFNMATCH #include #endif #ifdef HAVE_POSIX_REGCOMP #include #endif #ifdef HAVE_REGCMP char *regcmp(); char *regex(); extern char *__loc1; #endif #ifdef HAVE_V8_REGCOMP #include #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); }