diff options
author | in2 <in2@63ad8ddf-47c3-0310-b6dd-a9e9d9715204> | 2002-03-07 23:13:44 +0800 |
---|---|---|
committer | in2 <in2@63ad8ddf-47c3-0310-b6dd-a9e9d9715204> | 2002-03-07 23:13:44 +0800 |
commit | ae31e19f92e717919ac8e3db9039eb38d2b89aae (patch) | |
tree | c70164d6a1852344f44b04a653ae2815043512af /innbbsd | |
download | pttbbs-ae31e19f92e717919ac8e3db9039eb38d2b89aae.tar pttbbs-ae31e19f92e717919ac8e3db9039eb38d2b89aae.tar.gz pttbbs-ae31e19f92e717919ac8e3db9039eb38d2b89aae.tar.bz2 pttbbs-ae31e19f92e717919ac8e3db9039eb38d2b89aae.tar.lz pttbbs-ae31e19f92e717919ac8e3db9039eb38d2b89aae.tar.xz pttbbs-ae31e19f92e717919ac8e3db9039eb38d2b89aae.tar.zst pttbbs-ae31e19f92e717919ac8e3db9039eb38d2b89aae.zip |
Initial revision
git-svn-id: http://opensvn.csie.org/pttbbs/pttbbs/trunk/pttbbs@1 63ad8ddf-47c3-0310-b6dd-a9e9d9715204
Diffstat (limited to 'innbbsd')
34 files changed, 14382 insertions, 0 deletions
diff --git a/innbbsd/.cvsignore b/innbbsd/.cvsignore new file mode 100644 index 00000000..91b3d99d --- /dev/null +++ b/innbbsd/.cvsignore @@ -0,0 +1,6 @@ +bbslink +bbsnnrp +ctlinnbbsd +innbbsd +mkhistory +*.o diff --git a/innbbsd/Makefile b/innbbsd/Makefile new file mode 100644 index 00000000..fb82be2a --- /dev/null +++ b/innbbsd/Makefile @@ -0,0 +1,206 @@ +# ------------------------------------------------------- # +# innbbsd/Makefile ( NTHU CS MapleBBS Ver 2.36 ) # +# ------------------------------------------------------- # +# target : Makefile for 轉信程式 innbbsd by skhuang # +# create : 95/03/29 # +# update : 95/12/15 # +# ------------------------------------------------------- # + +#################################################### +# this is a bbs <--> news gateway +##################################################### +VERSION=0.50beta-4 +LASTVERSION=0.50beta-3 +ADMINUSER= root@your.domain.name +BBSHOME?=$(HOME) +BBS_SRC = .. +TARGET = $(INNBBSD) $(BBSNNRP) $(BBSLINK) +#################################################### +CC=gcc +INSTALL=cp +# +OPT= -O3 -s -pipe -fomit-frame-pointer -g + +BBS_DEP = MapleBBS +BBS_UTIL = $(BBS_SRC)/util +BBS_REC = $(BBS_UTIL)/util_record.o $(BBS_UTIL)/util_cache.o $(BBS_UTIL)/util_passwd.o + +######################### +# +############### +DEBUGOBJ = /usr/lib/debug/mallocmap.o +CFLAGS= -c -I. -I$(BBS_SRC)/include -I$(BBS_SRC)/mbbsd -D$(BBS_DEP) \ +$(EXTRAFLAGS) -DDBZDEBUG -DBBSHOME='"$(BBSHOME)"' \ +-D_PATH_BBSHOME=\"$(BBSHOME)\" \ +-DVERSION=\"$(VERSION)\" -DADMINUSER=\"$(ADMINUSER)\" +# +#################################################### + +OBJS = inndchannel.o innbbsd.o connectsock.o rfc931.o \ + daemon.o file.o pmain.o his.o dbz.o \ + closeonexec.o dbztool.o inntobbs.o receive_article.o \ + echobbslib.o $(BBS_REC) + # $(BBS_REC) +SRCS = inndchannel.c innbbsd.c connectsock.c rfc931.c \ + daemon.c file.c pmain.c parsdate.y his.c dbz.c \ + closeonexec.c dbztool.c inntobbs.c bbslib.c receive_article.c \ + port.c + +MOBJS = makedbz.o bbslib.o file.o dbz.o closeonexec.o +HOBJS = mkhistory.o bbslib.o file.o his.o dbz.o port.o closeonexec.o +DBZOBJS = dbzserver.o dbzinnbbsd.o pmain.o connectsock.o bbslib.o his.o \ + daemon.o file.o dbz.o dbztool.o rfc931.o port.o closeonexec.o \ + morelog.o +NNRPOBJS = bbsnnrp.o pmain.o bbslib.o connectsock.o file.o +LINKOBJS = bbslink.o pmain.o inntobbs.o echobbslib.o connectsock.o file.o port.o +LINKOBJS2 = bbslink2.o pmain.o inntobbs.o echobbslib.o connectsock.o file.o port.o +CTLOBJS = ctlinnbbsd.o pmain.o bbslib.o connectsock.o file.o +INNBBSD = innbbsd mkhistory bbsnnrp ctlinnbbsd $(BBSLINK) +BBSNNRP = bbsnnrp +BBSLINK = bbslink +BBSLINK2 = bbslink2 +EXES = $(TARGET) + +.c.o: + $(CC) $(OPT) $(CFLAGS) $*.c + + +all: $(OSTYPE) + +sun: + @$(MAKE) EXTRAFLAGS="-DMMAP -DGETRUSAGE -DMALLOCMAP" LDFLAGS="$(DEBUGOBJ)" target + + +aix: + @$(MAKE) CC=cc EXTRAFLAGS="-DMMAP -DGETRUSAGE -DAIX -DSYSV" target + +linux: + @$(MAKE) EXTRAFLAGS="-DLINUX -DGETRUSAGE" target + +hpux: + @$(MAKE) OPT= EXTRAFLAGS="-DMMAP -DHPUX -DSYSV" target + +irix: + @$(MAKE) EXTRAFLAGS="-DMMAP -DIRIX -DSYSV" target + +solaris: + @$(MAKE) EXTRAFLAGS="-DMMAP -DSOLARIS -DSYSV" LDFLAGS="-lsocket -lnsl" YACC="bison -y" target + +FreeBSD: + @$(MAKE) CC=cc EXTRAFLAGS="-DBSD44 -DMMAP -DGETRUSAGE" LIBCRYPT=-lcrypt target + +bsd: + @$(MAKE) CC=cc EXTRAFLAGS="-DMMAP -DGETRUSAGE" target + +alpha: + @$(MAKE) CC=cc EXTRAFLAGS="-DMMAP -DDEC_ALPHA -DGETRUSAGE" target + +sysv: + @$(MAKE) OPT= EXTRAFLAGS="-DMMAP -DSYSV" target + +target: $(EXES) + +r_port.o: r_port.c + $(CC) $(OPT) $(CFLAGS) -DWITH_RECORD_O r_port.c + +echobbslib.o: echobbslib.c + $(CC) $(OPT) $(CFLAGS) -DWITH_ECHOMAIL echobbslib.c + +tar: + test -d innbbsd-$(VERSION) || mkdir innbbsd-$(VERSION) + cp *.c *.h Makefile innbbsd-$(VERSION) + tar zcf innbbsd-$(VERSION).tar.gz innbbsd-$(VERSION) + cp innbbsd-$(VERSION).tar.gz /net/ftphome/CSIE/innbbsd + +patch: + rm -rf /tmp/innbbsd-$(LASTVERSION) + zcat /net/ftphome/ftp-data/bbs/misc/innbbsd/innbbsd-$(LASTVERSION).tar.gz | (cd /tmp; tar xf -) + (cd /tmp/innbbsd-$(VERSION); make clean; rm -f r_port.c dbzserver.c dbzinnbbsd.c echobbslib.c ) + (cd /tmp/innbbsd-$(LASTVERSION); make clean; rm -f r_port.c dbzserver.c dbzinnbbsd.c echobbslib.c ) + (cd /tmp; diff -rcs innbbsd-$(LASTVERSION) innbbsd-$(VERSION) > /tmp/innbbsd-patch.$(LASTVERSION)-$(VERSION); echo "diff -rcs") + (cd /tmp/innbbsd-$(VERSION); ln -s port.c r_port.c) + (cd /tmp/innbbsd-$(VERSION); ln -s inndchannel.c dbzserver.c) + (cd /tmp/innbbsd-$(VERSION); ln -s innbbsd.c dbzinnbbsd.c) + (cd /tmp/innbbsd-$(VERSION); ln -s bbslib.c echobbslib.c) + + +distribution: + rm -rf /tmp/innbbsd-$(VERSION) + test -d /tmp/innbbsd-$(VERSION) || mkdir /tmp/innbbsd-$(VERSION) + test -d /tmp/innbbsd-$(VERSION)/innd || mkdir /tmp/innbbsd-$(VERSION)/innd + rm -f r_port.c + rm -f dbzserver.c + rm -f dbzinnbbsd.c + rm -f echobbslib.c + cp -r doc hisconvert FEATURES CHANGES README* TODO *.c *.h *.y Makefile boards /tmp/innbbsd-$(VERSION) + (cd /tmp/innbbsd-$(VERSION); ln -s port.c r_port.c) + (cd /tmp/innbbsd-$(VERSION); ln -s inndchannel.c dbzserver.c) + (cd /tmp/innbbsd-$(VERSION); ln -s innbbsd.c dbzinnbbsd.c) + (cd /tmp/innbbsd-$(VERSION); ln -s bbslib.c echobbslib.c) + (cd innd ; cp -r Makefile README innbbs.conf in.bntpd mailpost *.pl bntplink bntplink.palmbbs *.active* *.c *.y *.bbs src /tmp/innbbsd-$(VERSION)/innd) + (cd /tmp/innbbsd-$(VERSION); make clean) + (cd /tmp; tar zcf innbbsd-$(VERSION).tar.gz innbbsd-$(VERSION) ) + ln -s port.c r_port.c + ln -s inndchannel.c dbzserver.c + ln -s innbbsd.c dbzinnbbsd.c + ln -s bbslib.c echobbslib.c + +remotetest: distribution + /usr/local/krb4/rcp /tmp/innbbsd-$(VERSION).tar.gz bbs_src.tar.gz skhuang:/tmp + /usr/local/krb4/rcp /tmp/innbbsd-$(VERSION).tar.gz bbs_src.tar.gz linux:/tmp + mv /u/staff/skhuang/.tcshrc /u/staff/skhuang/skhuang.tcshrc + rcp /tmp/innbbsd-$(VERSION).tar.gz bbs_src.tar.gz ccsun36:/tmp + rcp /tmp/innbbsd-$(VERSION).tar.gz bbs_src.tar.gz ccibm1:/tmp + mv /u/staff/skhuang/skhuang.tcshrc /u/staff/skhuang/.tcshrc + rcp /tmp/innbbsd-$(VERSION).tar.gz bbs_src.tar.gz cciris3:/tmp + +ftp: distribution patch + rsh ccsun42 mv /home8/ftp/pub/bbs/misc/innbbsd/innbbsd-$(VERSION).tar.gz /home8/ftp/pub/bbs/misc/innbbsd/.innbbsd-$(VERSION).tar.gz + rcp /tmp/innbbsd-$(VERSION).tar.gz ccsun42:/home8/ftp/pub/bbs/misc/innbbsd + rcp README* ccsun42:/home8/ftp/pub/bbs/misc/innbbsd + rcp /tmp/innbbsd-patch.$(LASTVERSION)-$(VERSION) ccsun42:/home8/ftp/pub/bbs/misc/innbbsd + +innbbspost.o: innbbspost.c + $(CC) $(CFLAGS) -I$(BBS_SRC) -c innbbspost.c + +makedbz: $(MOBJS) + $(CC) $(OPT) -o makedbz $(MOBJS) $(LDFLAGS) + +dbzserver.o: dbzserver.c + $(CC) $(CFLAGS) -DDBZSERVER -I$(BBS_SRC) -c dbzserver.c + +dbzinnbbsd.o: dbzinnbbsd.c + $(CC) $(CFLAGS) -DDBZSERVER -I$(BBS_SRC) -c dbzinnbbsd.c + +dbzserver: $(DBZOBJS) + $(CC) $(OPT) -o dbzserver $(DBZOBJS) $(LDFLAGS) + +bbsnnrp: $(NNRPOBJS) + $(CC) -o bbsnnrp $(NNRPOBJS) $(OPT) $(LDFLAGS) + +bbslink: $(LINKOBJS) + $(CC) -o bbslink $(LINKOBJS) $(OPT) $(LDFLAGS) + +bbslink2: $(LINKOBJS2) + $(CC) -o bbslink2 $(LINKOBJS2) $(OPT) $(LDFLAGS) + +ctlinnbbsd: $(CTLOBJS) + $(CC) $(OPT) -o ctlinnbbsd $(CTLOBJS) $(LDFLAGS) + +mkhistory: $(HOBJS) + $(CC) $(OPT) -o mkhistory $(HOBJS) $(LDFLAGS) + +dbz_query_sample: dbz_query_sample.o + $(CC) $(OPT) -o dbz_query_sample dbz_query_sample.o $(LDFLAGS) + + +innbbsd: $(OBJS) +# mv tnrpd.exe tnrpd.exe.old + $(CC) -o innbbsd $(OBJS) $(OPT) $(LDFLAGS) + +install: $(EXES) + install -d $(BBSHOME)/innd/ + install -c -m 755 $(EXES) $(BBSHOME)/innd/ + +clean: + rm -f *.o $(EXES) core innd/src/*.o diff --git a/innbbsd/antisplam.h b/innbbsd/antisplam.h new file mode 100644 index 00000000..30289da1 --- /dev/null +++ b/innbbsd/antisplam.h @@ -0,0 +1,37 @@ +#define char_lower(c) ((c >= 'A' && c <= 'Z') ? c|32 : c) + +/*void +str_lower(t, s) + char *t, *s;*/ +void str_lower(char *t, char *s) +{ + register char ch; + do + { + ch = *s++; + *t++ = char_lower(ch); + } while (ch); +} + +#if 0 /* string.h , libc */ +int +strcasestr(str, tag) + char *str, *tag; /* tag : lower-case string */ +{ + char buf[256]; + + str_lower(buf, str); + return (int) strstr(buf, tag); +} +#endif + +int +bad_subject(char *subject) +{ + char *badkey[] = {"無碼","avcd","mp3",NULL}; + int i; + for(i=0; badkey[i]; i++) + if(strcasestr(subject, badkey[i])) return 1; + return 0; +} + diff --git a/innbbsd/bbslib.c b/innbbsd/bbslib.c new file mode 100644 index 00000000..0dc808c3 --- /dev/null +++ b/innbbsd/bbslib.c @@ -0,0 +1,712 @@ +#if defined( LINUX ) +# include "innbbsconf.h" +# include "bbslib.h" +# include <varargs.h> +#else +# include <varargs.h> +# include "innbbsconf.h" +# include "bbslib.h" +#endif + +char INNBBSCONF[MAXPATHLEN]; +char INNDHOME[MAXPATHLEN]; +char HISTORY[MAXPATHLEN]; +char LOGFILE[MAXPATHLEN]; +char MYBBSID[MAXPATHLEN]; +char ECHOMAIL[MAXPATHLEN]; +char BBSFEEDS[MAXPATHLEN]; +char LOCALDAEMON[MAXPATHLEN]; + +int His_Maint_Min= HIS_MAINT_MIN; +int His_Maint_Hour= HIS_MAINT_HOUR; +int Expiredays = EXPIREDAYS; + +nodelist_t *NODELIST=NULL, **NODELIST_BYNODE=NULL; +newsfeeds_t *NEWSFEEDS=NULL, **NEWSFEEDS_BYBOARD=NULL; +static char *NODELIST_BUF, *NEWSFEEDS_BUF; +int NFCOUNT, NLCOUNT; +int LOCALNODELIST=0, NONENEWSFEEDS=0; + +#ifndef _PATH_BBSHOME +# define _PATH_BBSHOME "/u/staff/bbsroot/csie_util/bntpd/home" +#endif + +static FILE *bbslogfp; + +static int +verboseFlag=0; + +static char* +verboseFilename=NULL; +static char verbosename[MAXPATHLEN]; + +verboseon(filename) +char *filename; +{ + verboseFlag = 1; + if ( filename != NULL ) { + if (strchr(filename,'/') == NULL) { + sprintf(verbosename,"%s/innd/%s",BBSHOME,filename); + filename = verbosename; + } + } + verboseFilename = filename; +} +verboseoff() +{ + verboseFlag = 0; +} + +setverboseon() +{ + verboseFlag = 1; +} + +isverboselog() +{ + return verboseFlag; +} + +setverboseoff() +{ + verboseoff(); + if (bbslogfp != NULL) { + fclose(bbslogfp); + bbslogfp = NULL; + } +} + +verboselog(va_alist) +va_dcl +{ + va_list ap; + register char* fmt; + char datebuf[40]; + time_t now; + + if (verboseFlag == 0) return; + + va_start(ap); + + time(&now); + strftime(datebuf, sizeof(datebuf), "%b %d %X ", localtime(&now)); + + if (bbslogfp == NULL) { + if (verboseFilename != NULL) + bbslogfp = fopen(verboseFilename, "a"); + else + bbslogfp = fdopen(1, "a"); + } + if (bbslogfp == NULL) { va_end(ap); return; } + fmt = va_arg(ap, char *) ; + fprintf(bbslogfp,"%s[%d] ",datebuf, getpid()); + vfprintf(bbslogfp, fmt, ap); + fflush(bbslogfp); + va_end(ap); +} + +#ifdef PalmBBS +xbbslog(va_alist) +#else +bbslog(va_alist) +#endif +va_dcl +{ + va_list ap; + register char* fmt; + char datebuf[40]; + time_t now; + + va_start(ap); + + time(&now); + strftime(datebuf, sizeof(datebuf), "%b %d %X ", localtime(&now)); + + if (bbslogfp == NULL) { + bbslogfp = fopen(LOGFILE, "a"); + } + if (bbslogfp == NULL) { va_end(ap); return; } + fmt = va_arg(ap, char *) ; + fprintf(bbslogfp,"%s[%d] ",datebuf,getpid()); + vfprintf(bbslogfp, fmt, ap); + fflush(bbslogfp); + va_end(ap); +} + +initial_bbs(outgoing) +char *outgoing; +{ + FILE* FN; + struct stat st; + int fd, i; + char *bbsnameptr=NULL; + +/* reopen bbslog */ + if (bbslogfp != NULL) { + fclose(bbslogfp); + bbslogfp = NULL; + } + +#ifdef WITH_ECHOMAIL + init_echomailfp(); + init_bbsfeedsfp(); +#endif + + LOCALNODELIST=0, NONENEWSFEEDS =0; + sprintf(INNDHOME,"%s/innd",BBSHOME); + sprintf(HISTORY, "%s/history",INNDHOME); + sprintf(LOGFILE, "%s/bbslog",INNDHOME); + sprintf(ECHOMAIL,"%s/echomail.log",BBSHOME); + sprintf(LOCALDAEMON,"%s/.innbbsd",INNDHOME); + sprintf(INNBBSCONF,"%s/innbbs.conf",INNDHOME); + sprintf(BBSFEEDS,"%s/bbsfeeds.log",INNDHOME); + + if (isfile(INNBBSCONF)) { + FILE *conf; + char buffer[MAXPATHLEN]; + conf = fopen(INNBBSCONF,"r"); + if (conf != NULL) { + while (fgets( buffer, sizeof buffer, conf) != NULL) { + char *ptr, *front=NULL, *value=NULL, *value2=NULL, *value3=NULL; + if ( buffer[0] == '#' || buffer[0] == '\n') continue; + for ( front = buffer; *front && isspace(*front); front++); + for ( ptr = front; *ptr && !isspace(*ptr) ; ptr++) ; + if (*ptr == '\0') continue; + *ptr++ = '\0'; + for ( ; *ptr && isspace(*ptr) ; ptr++) ; + if (*ptr == '\0') continue; + value = ptr++; + for ( ; *ptr && !isspace(*ptr) ; ptr++) ; + if (*ptr) { + *ptr++ = '\0'; + for ( ; *ptr && isspace(*ptr) ; ptr++) ; + value2 = ptr++; + for ( ; *ptr && !isspace(*ptr) ; ptr++) ; + if (*ptr) { + *ptr++ = '\0'; + for ( ; *ptr && isspace(*ptr) ; ptr++) ; + value3 = ptr++; + for ( ; *ptr && !isspace(*ptr) ; ptr++) ; + if (*ptr) { + *ptr++ = '\0'; + } + } + } + if ( strcasecmp(front,"expiredays") == 0) { + Expiredays = atoi(value); + if (Expiredays < 0) { + Expiredays = EXPIREDAYS; + } + } else if ( strcasecmp(front,"expiretime") == 0) { + ptr = strchr(value,':'); + if (ptr == NULL) { + fprintf(stderr, "Syntax error in innbbs.conf\n"); + } else { + *ptr++ = '\0'; + His_Maint_Hour = atoi(value); + His_Maint_Min = atoi(ptr); + if (His_Maint_Hour < 0) + His_Maint_Hour = HIS_MAINT_HOUR; + if (His_Maint_Min < 0) + His_Maint_Min = HIS_MAINT_MIN; + } + } else if ( strcasecmp(front,"newsfeeds") == 0) { + if (strcmp(value,"none")==0) + NONENEWSFEEDS = 1; + } else if ( strcasecmp(front,"nodelist") == 0) { + if (strcmp(value,"local")==0) + LOCALNODELIST = 1; + } /*else if ( strcasecmp(front,"newsfeeds") == 0) { + printf("newsfeeds %s\n", value); + } else if ( strcasecmp(front,"nodelist") == 0) { + printf("nodelist %s\n", value); + } else if ( strcasecmp(front,"bbsname") == 0) { + printf("bbsname %s\n", value); + } */ + } + fclose(conf); + } + } + +#ifdef WITH_ECHOMAIL + bbsnameptr = (char*) fileglue("%s/bbsname.bbs",INNDHOME); + if ((FN = fopen( bbsnameptr ,"r" ))==NULL) { + fprintf(stderr,"can't open file %s\n", bbsnameptr); + return 0; + } + while ( fscanf(FN,"%s", MYBBSID) != EOF); + fclose(FN); + if( ! isdir(fileglue("%s/out.going",BBSHOME)) ) { + mkdir( (char*)fileglue("%s/out.going",BBSHOME), 0750 ); + } + if (NONENEWSFEEDS == 0) + readnffile(INNDHOME); + if (LOCALNODELIST == 0) { + if (readnlfile(INNDHOME, outgoing) != 0) return 0; + } + +#endif + return 1; +} + +static int +nf_byboardcmp(a,b) +newsfeeds_t **a, **b; +{ +/* + if (!a || !*a || !(*a)->board) return -1; + if (!b || !*b || !(*b)->board) return 1; +*/ + return strcasecmp((*a)->board, (*b)->board); +} + +static int +nfcmp(a,b) +newsfeeds_t *a, *b; +{ +/* + if (!a || !a->newsgroups) return -1; + if (!b || !b->newsgroups) return 1; +*/ + return strcasecmp(a->newsgroups, b->newsgroups); +} + +static int +nlcmp(a,b) +nodelist_t *a, *b; +{ +/* + if (!a || !a->host) return -1; + if (!b || !b->host) return 1; +*/ + return strcasecmp(a->host, b->host); +} + +static int +nl_bynodecmp(a,b) +nodelist_t **a, **b; +{ +/* + if (!a || !*a || !(*a)->node) return -1; + if (!b || !*b || !(*b)->node) return 1; +*/ + return strcasecmp((*a)->node, (*b)->node); +} + +/* read in newsfeeds.bbs and nodelist.bbs */ +readnlfile(inndhome, outgoing) +char *inndhome; +char *outgoing; +{ + FILE *fp; + char buff[1024]; + struct stat st; + int i, count, j; + char *ptr, *nodelistptr; + static lastcount=0; + + sprintf(buff,"%s/nodelist.bbs", inndhome); + fp = fopen(buff,"r"); + if (fp == NULL) { + fprintf(stderr,"open fail %s",buff); + return -1; + } + if (fstat(fileno(fp),&st) != 0) { + fprintf(stderr,"stat fail %s", buff); + return -1; + } + if (NODELIST_BUF == NULL) { + NODELIST_BUF = (char*) mymalloc( st.st_size +1); + } else { + NODELIST_BUF = (char*) myrealloc( NODELIST_BUF, st.st_size +1); + } + i = 0, count =0; + while (fgets(buff, sizeof buff, fp) != NULL) { + if (buff[0] == '#') continue; + if (buff[0] == '\n') continue; + strcpy(NODELIST_BUF+i, buff); + i += strlen(buff); + count ++; + } + fclose(fp); + if (NODELIST == NULL) { + NODELIST = (nodelist_t*) mymalloc(sizeof(nodelist_t) * (count+1)); + NODELIST_BYNODE = (nodelist_t**) mymalloc(sizeof(nodelist_t*) * (count+1)); + } else { + NODELIST = (nodelist_t*) myrealloc(NODELIST, sizeof(nodelist_t) * (count+1)); + NODELIST_BYNODE = (nodelist_t**) myrealloc(NODELIST_BYNODE, sizeof(nodelist_t*) * (count+1)); + } + for (i=lastcount; i< count; i++) { + NODELIST[i].feedfp = NULL; + } + lastcount = count; + NLCOUNT = 0; + for (ptr = NODELIST_BUF; (nodelistptr = (char*)strchr(ptr,'\n')) != NULL; ptr = nodelistptr +1, NLCOUNT++) { + char *nptr , *bptr, *pptr, *tptr; + *nodelistptr = '\0'; + NODELIST[NLCOUNT].host = ""; + NODELIST[NLCOUNT].exclusion = ""; + NODELIST[NLCOUNT].node = ""; + NODELIST[NLCOUNT].protocol = "IHAVE(119)"; + NODELIST[NLCOUNT].comments = ""; + NODELIST_BYNODE[NLCOUNT] = NODELIST+NLCOUNT; + for (nptr= ptr ;*nptr && isspace(*nptr); ) nptr++; + if (*nptr == '\0') { + bbslog("nodelist.bbs %d entry read error\n", NLCOUNT); + return -1; + } + /*NODELIST[NLCOUNT].id = nptr;*/ + NODELIST[NLCOUNT].node = nptr; + for (nptr++; *nptr && !isspace(*nptr); ) nptr++; + if (*nptr == '\0') { + bbslog("nodelist.bbs node %d entry read error\n", NLCOUNT); + return -1; + } + *nptr = '\0'; + if ((tptr = strchr(NODELIST[NLCOUNT].node,'/'))) { + *tptr = '\0'; + NODELIST[NLCOUNT].exclusion = tptr + 1; + } else { + NODELIST[NLCOUNT].exclusion = ""; + } + for (nptr++ ;*nptr && isspace(*nptr); ) nptr++; + if (*nptr == '\0') continue; + if (*nptr=='+' || *nptr=='-') { + NODELIST[NLCOUNT].feedtype = *nptr; + if (NODELIST[NLCOUNT].feedfp != NULL) { + fclose(NODELIST[NLCOUNT].feedfp); + } + if ( NODELIST[NLCOUNT].feedtype == '+') + if (outgoing != NULL) { + NODELIST[NLCOUNT].feedfp = fopen((char*)fileglue("%s/out.going/%s.%s",BBSHOME, NODELIST[NLCOUNT].node, outgoing),"a"); + } + nptr++; + } else { + NODELIST[NLCOUNT].feedtype = ' '; + } + NODELIST[NLCOUNT].host = nptr; + for (nptr++; *nptr && !isspace(*nptr); ) nptr++; + if (*nptr == '\0') { + continue; + } + *nptr = '\0'; + for (nptr++;*nptr && isspace(*nptr); ) nptr++; + if (*nptr == '\0') continue; + NODELIST[NLCOUNT].protocol = nptr; + for (nptr++; *nptr && !isspace(*nptr); ) nptr++; + if (*nptr == '\0') continue; + *nptr = '\0'; + for (nptr++;*nptr && strchr(" \t\r\n",*nptr); ) nptr++; + if (*nptr == '\0') continue; + NODELIST[NLCOUNT].comments = nptr; + } + qsort(NODELIST, NLCOUNT, sizeof(nodelist_t), nlcmp); + qsort(NODELIST_BYNODE, NLCOUNT, sizeof(nodelist_t*), nl_bynodecmp); + return 0; +} + +readnffile(inndhome) +char *inndhome; +{ + FILE *fp; + char buff[1024]; + struct stat st; + int i, count; + char *ptr, *newsfeedsptr; + + sprintf(buff,"%s/newsfeeds.bbs", inndhome); + fp = fopen(buff,"r"); + if (fp == NULL) { + fprintf(stderr,"open fail %s",buff); + return -1; + } + if (fstat(fileno(fp),&st) != 0) { + fprintf(stderr,"stat fail %s", buff); + return -1; + } + if (NEWSFEEDS_BUF == NULL) { + NEWSFEEDS_BUF = (char*) mymalloc( st.st_size +1); + } else { + NEWSFEEDS_BUF = (char*) myrealloc( NEWSFEEDS_BUF, st.st_size +1); + } + i = 0, count =0; + while (fgets(buff, sizeof buff, fp) != NULL) { + if (buff[0] == '#') continue; + if (buff[0] == '\n') continue; + strcpy(NEWSFEEDS_BUF+i, buff); + i += strlen(buff); + count ++; + } + fclose(fp); + if (NEWSFEEDS == NULL) { + NEWSFEEDS = (newsfeeds_t*) mymalloc(sizeof(newsfeeds_t) * (count+1)); + NEWSFEEDS_BYBOARD = (newsfeeds_t**) mymalloc(sizeof(newsfeeds_t*) * (count+1)); + } else { + NEWSFEEDS = (newsfeeds_t*) myrealloc(NEWSFEEDS, sizeof(newsfeeds_t) * (count+1)); + NEWSFEEDS_BYBOARD = (newsfeeds_t**) myrealloc(NEWSFEEDS_BYBOARD, sizeof(newsfeeds_t*) * (count+1)); + } + NFCOUNT = 0; + for (ptr = NEWSFEEDS_BUF; (newsfeedsptr = (char*)strchr(ptr,'\n')) != NULL; ptr = newsfeedsptr +1, NFCOUNT++) { + char *nptr , *bptr, *pptr; + *newsfeedsptr = '\0'; + NEWSFEEDS[NFCOUNT].newsgroups = ""; + NEWSFEEDS[NFCOUNT].board = ""; + NEWSFEEDS[NFCOUNT].path = NULL; + NEWSFEEDS_BYBOARD[NFCOUNT] = NEWSFEEDS+NFCOUNT; + for (nptr= ptr ;*nptr && isspace(*nptr); ) nptr++; + if (*nptr == '\0') continue; + NEWSFEEDS[NFCOUNT].newsgroups = nptr; + for (nptr++; *nptr && !isspace(*nptr); ) nptr++; + if (*nptr == '\0') continue; + *nptr = '\0'; + for (nptr++ ;*nptr && isspace(*nptr); ) nptr++; + if (*nptr == '\0') continue; + NEWSFEEDS[NFCOUNT].board = nptr; + for (nptr++; *nptr && !isspace(*nptr); ) nptr++; + if (*nptr == '\0') continue; + *nptr = '\0'; + for (nptr++;*nptr && isspace(*nptr); ) nptr++; + if (*nptr == '\0') continue; + NEWSFEEDS[NFCOUNT].path = nptr; + for (nptr++; *nptr && !strchr("\r\n",*nptr); ) nptr++; + *nptr = '\0'; + } + qsort(NEWSFEEDS, NFCOUNT, sizeof(newsfeeds_t), nfcmp); + qsort(NEWSFEEDS_BYBOARD, NFCOUNT, sizeof(newsfeeds_t*), nf_byboardcmp); +} + +newsfeeds_t *search_board(board) +char *board; +{ + newsfeeds_t nft, *nftptr, **find; + if (NONENEWSFEEDS) return NULL; + nft.board = board; + nftptr = &nft; + find = (newsfeeds_t**)bsearch((char*)&nftptr, NEWSFEEDS_BYBOARD, NFCOUNT, sizeof(newsfeeds_t*), nf_byboardcmp); + if (find != NULL) return *find; + return NULL; +} + +nodelist_t *search_nodelist_bynode(node) +char *node; +{ + nodelist_t nlt, *nltptr, **find; + if (LOCALNODELIST) return NULL; + nlt.node = node; + nltptr = ≮ + find = (nodelist_t**)bsearch((char*)&nltptr, NODELIST_BYNODE, NLCOUNT, sizeof(nodelist_t*), nl_bynodecmp); + if (find != NULL) return *find; + return NULL; +} + + +nodelist_t *search_nodelist(site, identuser) +char *site; +char *identuser; +{ + nodelist_t nlt, *find; + char buffer[1024]; + if (LOCALNODELIST) return NULL; + nlt.host = site; + find = (nodelist_t*)bsearch((char*)&nlt, NODELIST, NLCOUNT, sizeof(nodelist_t), nlcmp); + if (find == NULL && identuser != NULL) { + sprintf(buffer,"%s@%s", identuser, site); + nlt.host = buffer; + find = (nodelist_t*)bsearch((char*)&nlt, NODELIST, NLCOUNT, sizeof(nodelist_t), nlcmp); + } + return find; +} + +newsfeeds_t *search_group(newsgroup) +char *newsgroup; +{ + newsfeeds_t nft, *find; + if (NONENEWSFEEDS) return NULL; + nft.newsgroups = newsgroup; + find = (newsfeeds_t*)bsearch((char*)&nft, NEWSFEEDS, NFCOUNT, sizeof(newsfeeds_t), nfcmp); + return find; +} + +char *ascii_date(now) +time_t now; +{ + static char datebuf[40]; + /*time_t now; + time(&now);*/ + strftime(datebuf, sizeof(datebuf), "%d %b %Y %X GMT", gmtime(&now)); + return datebuf; +} + +char * +restrdup(ptr, string) +char *ptr; +char *string; +{ + int len ; + if (string == NULL) { + if (ptr != NULL) *ptr = '\0'; + return ptr; + } + len = strlen(string) + 1; + if (ptr != NULL) { + ptr = (char*)myrealloc(ptr, len); + } else + ptr = (char*)mymalloc(len); + strcpy(ptr, string); + return ptr; +} + + + +void * +mymalloc(size) +int size; +{ + char *ptr = (char*)malloc(size); + if (ptr == NULL) { + fprintf(stderr, "cant allocate memory\n"); + syslog(LOG_ERR, "cant allocate memory %m"); + exit(1); + } + return ptr; +} + +void * +myrealloc(optr, size) +void *optr; +int size; +{ + char *ptr = (char*)realloc(optr, size); + if (ptr == NULL) { + fprintf(stderr, "cant allocate memory\n"); + syslog(LOG_ERR, "cant allocate memory %m"); + exit(1); + } + return ptr; +} + +testandmkdir(dir) +char *dir; +{ + if (!isdir(dir)) { + char path[MAXPATHLEN+12]; + sprintf(path,"mkdir -p %s",dir); + system(path); + } +} + +static char splitbuf[2048]; +static char joinbuf[1024]; +#define MAXTOK 50 +static char* Splitptr[MAXTOK]; +char **split(line,pat) +char *line,*pat; +{ + char *p; + int i; + + for (i=0;i<MAXTOK;++i) Splitptr[i] = NULL; + strncpy(splitbuf,line,sizeof splitbuf - 1 ); + /*printf("%d %d\n",strlen(line),strlen(splitbuf));*/ + splitbuf[sizeof splitbuf - 1] = '\0'; + for (i=0,p=splitbuf;*p && i< MAXTOK -1 ;){ + for (Splitptr[i++]=p;*p && !strchr(pat,*p);p++); + if (*p=='\0') break; + for (*p++='\0'; *p && strchr(pat,*p);p++); + } + return Splitptr; +} + +char **BNGsplit(line) +char *line; +{ + char **ptr = split(line,","); + newsfeeds_t *nf1, *nf2; + char *n11, *n12, *n21, *n22; + int i,j; + for (i=0; ptr[i] != NULL; i++) { + nf1 = (newsfeeds_t*)search_group(ptr[i]); + for (j=i+1; ptr[j] != NULL; j++) { + if (strcmp(ptr[i],ptr[j])==0) { + *ptr[j] = '\0'; + continue; + } + nf2 = (newsfeeds_t*)search_group(ptr[j]); + if (nf1 && nf2) { + if (strcmp(nf1->board,nf2->board)==0) { + *ptr[j] = '\0'; + continue; + } + for (n11 = nf1->board, n12 = (char*)strchr(n11,','); + n11 && *n11 ; n12 = (char*) strchr(n11,',')) { + if (n12) *n12 = '\0'; + for (n21 = nf2->board, n22 = (char*)strchr(n21,','); + n21 && *n21 ; n22 = (char*) strchr(n21,',')) { + if (n22) *n22 = '\0'; + if (strcmp(n11,n21)==0) { + *n21 = '\t'; + } + if (n22) { + *n22 = ','; + n21 = n22 + 1; + } else + break; + } + if (n12) { + *n12 = ','; + n11 = n12 +1; + } else + break; + } + } + } + } + return ptr; +} + +char **ssplit(line,pat) +char *line,*pat; +{ + char *p; + int i; + for (i=0;i<MAXTOK;++i) Splitptr[i] = NULL; + strncpy(splitbuf,line,1024); + for (i=0,p=splitbuf;*p && i< MAXTOK;){ + for (Splitptr[i++]=p;*p && !strchr(pat,*p);p++); + if (*p=='\0') break; + *p=0;p++; +/* for (*p='\0'; strchr(pat,*p);p++);*/ + } + return Splitptr; +} + +char *join(lineptr,pat,num) +char **lineptr,*pat; +int num; +{ + int i; + joinbuf[0] = '\0'; + if (lineptr[0] != NULL) + strncpy(joinbuf,lineptr[0],1024); + else { + joinbuf[0]='\0'; + return joinbuf; + } + for (i=1;i<num;i++) { + strcat(joinbuf,pat); + if (lineptr[i] != NULL) + strcat(joinbuf,lineptr[i]); + else + break; + } + return joinbuf; +} + +#ifdef BBSLIB +main() +{ + initial_bbs("feed"); + printf("%s\n",ascii_date()); +} +#endif + diff --git a/innbbsd/bbslib.h b/innbbsd/bbslib.h new file mode 100644 index 00000000..190672b7 --- /dev/null +++ b/innbbsd/bbslib.h @@ -0,0 +1,62 @@ +#ifndef BBSLIB_H +#define BBSLIB_H + +typedef struct nodelist_t { + char *node; + char *exclusion; + char *host; + char *protocol; + char *comments; + int feedtype; + FILE *feedfp; +} nodelist_t; + +typedef struct newsfeeds_t { + char *newsgroups; + char *board; + char *path; +} newsfeeds_t; + +typedef struct overview_t { + char *board, *filename, *group; + time_t mtime; + char *from, *subject; +} overview_t; + +extern char MYBBSID[]; +extern char ECHOMAIL[]; +extern char BBSFEEDS[]; +extern char LOCALDAEMON[]; +extern char INNDHOME[]; +extern char HISTORY[]; +extern char LOGFILE[]; +extern char INNBBSCONF[]; +extern nodelist_t *NODELIST; +extern nodelist_t **NODELIST_BYNODE; +extern newsfeeds_t *NEWSFEEDS, **NEWSFEEDS_BYBOARD; +extern int NFCOUNT, NLCOUNT; +extern int Expiredays, His_Maint_Min, His_Maint_Hour; +extern int LOCALNODELIST, NONENEWSFEEDS; +extern int Maxclient; + +# ifndef ARG +# ifdef __STDC__ +# define ARG(x) x +# else +# define ARG(x) () +# endif +# endif + +int initial_bbs ARG((char* )); +char *restrdup ARG((char *, char *)); +nodelist_t *search_nodelist ARG((char *, char *)); +newsfeeds_t *search_group ARG((char *)); +int bbslog ARG(()); +void *mymalloc ARG((int)); +void *myrealloc ARG((void *, int)); + +#ifdef PalmBBS +#define bbslog xbbslog +#endif + +#endif diff --git a/innbbsd/bbslink.c b/innbbsd/bbslink.c new file mode 100644 index 00000000..2b0b6f37 --- /dev/null +++ b/innbbsd/bbslink.c @@ -0,0 +1,2021 @@ +#if defined( LINUX ) +# include "innbbsconf.h" +# include "bbslib.h" +# include <varargs.h> +#else +# include <varargs.h> +# include "innbbsconf.h" +# include "bbslib.h" +#endif +#include "antisplam.h" + +#include <sys/mman.h> + +#ifndef AIX +# include <sys/fcntl.h> +#endif + +#if defined(PalmBBS) +#include <utime.h> +#endif + + +#include "daemon.h" +#include "nntp.h" +#include "externs.h" + +/* + * TODO 1. read newsfeeds.bbs, read nodelist.bbs, read bbsname.bbs 2. scan + * new posts and append to .link 3. rename .link to .send (must lock) 4. + * start to send .send out and append not sent to .link + * + * 5. node.LOCK (with pid) 6. log articles sent + */ + + +#ifndef MAXBUFLEN +#define MAXBUFLEN 256 +#endif + +#define MAX_OUTGO_POST 100 /* bbslink 一次處理的轉出最大文章數量 */ + +typedef struct my_out_bntp +{ + char *board, *filename, *userid, *nickname, *subject; +} my_out_bntp; +struct my_out_bntp out_bntp[MAX_OUTGO_POST]; + +int outgo_post=0; + +typedef struct Over_t +{ + time_t mtime; + char date[MAXBUFLEN]; + char nickname[MAXBUFLEN]; + char subject[MAXBUFLEN]; + char from[MAXBUFLEN]; + char msgid[MAXBUFLEN]; + char site[MAXBUFLEN]; + char board[MAXBUFLEN]; +} linkoverview_t; + +typedef struct SendOver_t +{ + char *board, *filename, *group, *from, *subject; + char *outgoingtype, *msgid, *path; + char *date, *control; + time_t mtime; +} soverview_t; + +typedef struct Stat_t +{ + int localsendout; + int localfailed; + int remotesendout; + int remotefailed; +} stat_t; + +static stat_t *BBSLINK_STAT; + +static int NoAction = 0; +static int Verbose = 0; +static int VisitOnly = 0; +static int NoVisit = 0; +static char *DefaultFeedSite = ""; +static int KillFormerBBSLINK = 0; + +extern char *SITE; +extern char *GROUPS; + +char NICKNAME[MAXBUFLEN]; + +char DATE_BUF[MAXBUFLEN]; +extern char *DATE; + +char FROM_BUF[MAXBUFLEN]; +extern char *FROM; + +#ifndef MapleBBS +char POSTER_BUF[MAXBUFLEN]; +char *POSTER; +#endif + +char MYADDR[MAXBUFLEN]; +char MYSITE[MAXBUFLEN]; + +char SUBJECT_BUF[MAXBUFLEN]; +extern char *SUBJECT; + +char MSGID_BUF[MAXBUFLEN]; +extern char *MSGID; + +char LINKPROTOCOL[MAXBUFLEN]; +int LINKPORT; +char ORGANIZATION[MAXBUFLEN]; +char NEWSCONTROL[MAXBUFLEN]; +char NEWSAPPROVED[MAXBUFLEN]; +char NNTPHOST_BUF[MAXBUFLEN]; +extern char *NNTPHOST; +char PATH_BUF[MAXBUFLEN]; +extern char *PATH; + +char CONTROL_BUF[MAXBUFLEN]; +extern char *CONTROL; + +char *BODY, *HEAD; + +int USEIHAVE = 1; +int USEPOST = 0; +int USEDATA = 0; +int FEEDTYPE = ' '; + +int NNTP = -1; +FILE *NNTPrfp = NULL; +FILE *NNTPwfp = NULL; +char NNTPbuffer[1024]; +static char *NEWSFEED; +static char *REMOTE = "REMOTE"; +static char *LOCAL = "LOCAL"; + +static int FD, FD_SIZE; +static char *FD_BUF; +static char *FD_END; + +static char *COMMENT = "\n"; +/*"[Ptt 送出]\n";*/ + +char *fileglue(); +int +is_outgo_post(board, filename, userid, nickname, subject) + char *board, *filename, *userid, *nickname, *subject; +{ + int mypost; + + for (mypost = 0; mypost < outgo_post; mypost++) + { + if (!strcmp(out_bntp[mypost].filename, filename)) + if (!strcmp(out_bntp[mypost].userid, userid)) + if (!strcmp(out_bntp[mypost].board, board)) + if (!strcmp(out_bntp[mypost].nickname, nickname)) + if (!strcmp(out_bntp[mypost].subject, subject)){ + if (Verbose) + printf("bad_cancel: %s, %s(%s), %s, %s\n", + board, userid, nickname, subject, filename); + bbslog("bad_cancel: %s, %s(%s), %s, %s\n", + board, userid, nickname, subject, filename); + return 1; + } + } + return 0; +} +/* +woju +Cross-fs rename() +*/ + +Rename(char* src, char* dst) +{ + char cmd[200]; + + if (rename(src, dst) == 0) + return 0; + + sprintf(cmd, "/bin/mv %s %s", src, dst); + return system(cmd); + +} + + +bbslink_un_lock(file) + char *file; +{ + char *lockfile = fileglue("%s.LOCK", file); + + if (isfile(lockfile)) + unlink(lockfile); +} + +bbslink_get_lock(file) + char *file; +{ + int lockfd; + char LockFile[MAXPATHLEN]; + + strncpy(LockFile, (char *) fileglue("%s.LOCK", file), sizeof LockFile); + if ((lockfd = open(LockFile, O_RDONLY)) >= 0) + { + char buf[10]; + int pid; + + if (read(lockfd, buf, sizeof buf) > 0 && + (pid = atoi(buf)) > 0 && kill(pid, 0) == 0) + { + if (KillFormerBBSLINK) + { + kill(pid, SIGTERM); + unlink(LockFile); + } + else + { + fprintf(stderr, "another process [%d] running\n", pid); + return 0; + } + } + else + { + fprintf(stderr, "no process [%d] running, but lock file existed, unlinked\n", pid); + unlink(LockFile); + } + close(lockfd); + } + + if ((lockfd = open(LockFile, O_RDWR | O_CREAT | O_EXCL, 0644)) < 0) + { + fprintf(stderr, "lock %s error: another bbslink process running\n", LockFile); + return 0; + } + else + { + char buf[10]; + int pid; + + sprintf(buf, "%-.8d\n", getpid()); + write(lockfd, buf, strlen(buf)); + close(lockfd); + return 1; + } +} + + +int +tcpcommand(va_alist) +va_dcl +{ + va_list ap; + register char *fmt; + char *ptr; + + va_start(ap); + fmt = va_arg(ap, char *); + vfprintf(NNTPwfp, fmt, ap); + fprintf(NNTPwfp, "\r\n"); + fflush(NNTPwfp); + + fgets(NNTPbuffer, sizeof NNTPbuffer, NNTPrfp); + ptr = strchr(NNTPbuffer, '\r'); + if (ptr) + *ptr = '\0'; + ptr = strchr(NNTPbuffer, '\n'); + if (ptr) + *ptr = '\0'; + va_end(ap); + return atoi(NNTPbuffer); +} + +char * +tcpmessage() +{ + char *ptr; + + ptr = strchr(NNTPbuffer, ' '); + if (ptr) + return ptr; + return NNTPbuffer; +} + +read_article(lover, filename, userid) + linkoverview_t *lover; + char *filename, *userid; +{ + FILE *fp; + int fd; + struct stat st; + time_t mtime; + char *buffer; + char *artptr, *artend, *artback; + + if (stat(filename, &st) != 0) + return 0; + lover->mtime = st.st_mtime; + fd = open(filename, O_RDONLY); + if (fd < 0) + { + bbslog("<bbslink> Err: can't open %s\n", filename); + return 0; + } + + if (FD_BUF == NULL) + { + FD_BUF = mymalloc(st.st_size + 1 + strlen(COMMENT)); + } + else + { + FD_BUF = myrealloc(FD_BUF, st.st_size + 1 + strlen(COMMENT)); + } + FD_BUF[st.st_size] = '\0'; + read(fd, FD_BUF, st.st_size); + sprintf(FD_BUF + st.st_size, "%s", COMMENT); + st.st_size += strlen(COMMENT); + FD_SIZE = st.st_size; + for (buffer = FD_BUF, artend = FD_BUF + st.st_size, + artback = strchr(buffer, '\n'); + buffer && buffer < artend && *buffer; + artback = strchr(buffer, '\n') + ) + { + /* while( fgets(buffer, sizeof buffer, fp) != NULL) { */ + char *m, *n; + char *ptr; + + if (artback != NULL) + *artback = '\0'; + if (*buffer == '\0') + break; + +#ifndef MapleBBS + if (strstr(buffer, userid) != NULL) + { + m = strchr(buffer, '('); + n = strrchr(buffer, ')'); + if (m != NULL && n != NULL) + { + strncpy(lover->nickname, m + 1, n - m - 1); + lover->nickname[n - m - 1] = '\0'; + } + else + { + *lover->nickname = '\0'; + } + } + else if (strncmp(buffer, "Date: ", 11) == 0) + { + strcpy(lover->date, buffer + 11); + } + else if (strncmp(buffer, "發信站: ", 8) == 0) + { + m = strchr(buffer, '('); + n = strrchr(buffer, ')'); + strncpy(lover->date, m + 1, n - m - 1); + lover->date[n - m - 1] = '\0'; + } +#endif + + if (artback != NULL) + { + *artback = '\n'; + buffer = artback + 1; + } + else + { + break; + } + } + if (artback != NULL) + BODY = artback + 1; + else + BODY = ""; + close(fd); + return 1; +} + +save_outgoing(sover, filename, userid, poster, mtime) + soverview_t *sover, *filename, *userid, *poster; + time_t mtime; +{ + newsfeeds_t *nf; + char *group, *server, *serveraddr; + char *subject, *path; + char *board; + char *ptr1, *ptr2; + + board = sover->board; + + PATH = MYBBSID; + nf = (newsfeeds_t *) search_board(board); + if (nf == NULL) + { + bbslog("<bbslink> save_outgoing: No such board %s\n", board); + return; + } + else + { + group = nf->newsgroups; + server = nf->path; + } + if (!server || !*server) + { + sprintf(PATH_BUF, "%.*s (local)", sizeof PATH_BUF - 9, MYBBSID); + PATH = PATH_BUF; + serveraddr = ""; + sover->path = PATH; + } + for (ptr1 = server; ptr1 && *ptr1;) + { + nodelist_t *nl; + char savech; + + for (; *ptr1 && isspace(*ptr1); ptr1++); + if (!*ptr1) + break; + for (ptr2 = ptr1; *ptr2 && !isspace(*ptr2); ptr2++); + savech = *ptr2; + *ptr2 = '\0'; + nl = (nodelist_t *) search_nodelist_bynode(ptr1); + *ptr2 = savech; + ptr1 = ptr2++; + if (nl == NULL) + continue; + /* if (nl->feedfp == NULL) continue; */ + + if (nl->host && *nl->host) + { + if (nl->feedfp == NULL) + { + nl->feedfp = fopen(fileglue("%s/%s.link", INNDHOME, nl->node), "a"); + if (nl->feedfp == NULL) + { + bbslog("<save outgoing> append failed for %s/%s.link", INNDHOME, nl->node); + } + } + if (nl->feedfp != NULL) + { + flock(fileno(nl->feedfp), LOCK_EX); + fprintf(nl->feedfp, "%s\t%s\t%s\t%ld\t%s\t%s\n", sover->board, filename, group, mtime, FROM, sover->subject); + fflush(nl->feedfp); + flock(fileno(nl->feedfp), LOCK_UN); + } + } + if (savech == '\0') + break; + } +} + + +#ifndef MapleBBS +save_article(board, filename, sover) + char *board, *filename; + soverview_t *sover; +{ + FILE *FN; + + if (Verbose) + printf("<save_article> %s %s\n", board, filename); + FN = fopen(fileglue("%s/boards/%s/%s", BBSHOME, board, filename), "w"); + if (FN == NULL) + { + bbslog("<save_article> err: %s %s\n", board, filename); + if (Verbose) + printf("<save_article> err: %s %s\n", board, filename); + return 0; + } + flock(fileno(FN), LOCK_EX); + fprintf(FN, "發信人: %s, 信區: %s\n", POSTER, sover->board); + fprintf(FN, "標 題: %s\n", sover->subject); + fprintf(FN, "發信站: %s (%s)\n", MYSITE, sover->date); + fprintf(FN, "轉信站: %s\n", sover->path); + fprintf(FN, "\n"); + fputs(BODY, FN); + flock(fileno(FN), LOCK_UN); + fclose(FN); + +#if defined(PalmBBS) + { + struct utimbuf times; + + times.actime = sover->mtime; + times.modtime = sover->mtime; + utime(fileglue("%s/boards/%s/%s", BBSHOME, board, filename), ×); + utime(fileglue("%s/.bcache/%s", BBSHOME, board), NULL); + } +#endif +} +#endif + +/* process_article() read_article() save_outgoing() save_article() */ + +process_article(board, filename, userid, nickname, subject) + char *board, *filename, *userid, *nickname, *subject; +{ + char *n, *filepath; + char poster[MAXBUFLEN]; + soverview_t sover; + + if (!*userid) + { + return; + } + else if (!subject || !*subject) + { + subject = "無題"; + } + filepath = fileglue("%s/boards/%s/%s", BBSHOME, board, filename); + if (isfile(filepath)) + { + linkoverview_t lover; + + if (read_article(&lover, filepath, userid)) + { + +#ifndef MapleBBS + strncpy(POSTER_BUF, fileglue("%s@%s (%s)", userid, MYBBSID, nickname), sizeof POSTER_BUF); + POSTER = POSTER_BUF; +#endif + + strncpy(FROM_BUF, fileglue("%s.bbs@%s (%s)", userid, MYADDR, nickname), sizeof FROM_BUF); + FROM = FROM_BUF; + sover.from = FROM; + sover.board = board; + sover.subject = subject; + PATH = MYBBSID; + sover.path = MYBBSID; + sover.date = lover.date; + sover.mtime = lover.mtime; + if (!VisitOnly) + { + save_outgoing(&sover, filename, userid, poster, lover.mtime); + +#ifndef MapleBBS + save_article(board, filename, &sover); +#endif + } + } + } +} + + +char * +baseN(val, base, len) + int val, base, len; +{ + int n, ans; + static char str[MAXBUFLEN]; + char *pstr = str; + int index; + + for (index = len - 1; index >= 0; index--) + { + n = val % base; + val /= base; + if (n < 10) + { + n += '0'; + } + else if (n < 36) + { + n += 'A' - 10; + } + else if (n < 62) + { + n += 'a' - 36; + } + else + { + n = '_'; + } + str[index] = n; + } + str[len] = '\0'; + return str; +} + +char * +hash_value(str) + char *str; +{ + int val, n; + char *ptr; + + if (*str) + ptr = str + strlen(str) - 1; + else + ptr = str; + val = 0; + while (ptr >= str) + { + n = *ptr; + val = (val + n * 0x100) ^ n; + ptr--; + } + return baseN(val, 64, 3); +} + +/* process_cancel() save_outgoing() hash_value(); baseN(); ascii_date(); */ + + +read_outgoing(sover) + soverview_t *sover; +{ + char *board, *filename, *group, *from, *subject, *outgoingtype, *msgid, *path; + char *buffer, *bufferp; + FILE *ECHOMAIL, *FN; + char *hash; + char times[MAXBUFLEN]; + time_t mtime; + + board = sover->board; + filename = sover->filename; + group = sover->group; + mtime = sover->mtime; + from = sover->from; + subject = sover->subject; + outgoingtype = sover->outgoingtype; + msgid = sover->msgid; + path = sover->path; + if (Verbose) + { + printf("<read_outgoing> %s:%s:%s\n", board, filename, group); + printf(" => %ld:%s\n", mtime, from); + printf(" => %s\n", subject); + printf(" => %s:%s\n", outgoingtype, msgid); + printf(" => %s\n", path); + } + if (NEWSFEED == LOCAL) + { + char *end = strrchr(filename, '.'); + + if (end) + *end = '\0'; + strncpy(times, baseN(atol(filename + 2), 48, 6), sizeof times); + if (end) + *end = '.'; + hash = hash_value(fileglue("%s.%s", filename, board)); + sprintf(MSGID_BUF, "%s$%s@%s", times, hash, MYADDR); + } + else + { + strncpy(MSGID_BUF, msgid, sizeof MSGID_BUF); + } + sover->msgid = MSGID; + if (mtime == -1) + { + static char BODY_BUF[MAXBUFLEN]; + + strncpy(BODY_BUF, fileglue("%s\r\n", subject), sizeof BODY_BUF); + BODY = BODY_BUF; + sprintf(SUBJECT_BUF, "cmsg cancel <%s>", MSGID); + SUBJECT = SUBJECT_BUF; + sprintf(CONTROL_BUF, "cancel <%s>", MSGID); + CONTROL = CONTROL_BUF; + strncpy(MSGID_BUF, fileglue("%d.%s", getpid(), MSGID_BUF), sizeof MSGID_BUF); + sprintf(DATE_BUF, "%s", ascii_date(time(NULL))); + DATE = DATE_BUF; + sover->subject = SUBJECT; + sover->control = CONTROL; + sover->msgid = MSGID; + sover->date = DATE; + } + else + { + sover->control = CONTROL; + sover->date = DATE_BUF; + DATE = DATE_BUF; + *CONTROL = '\0'; + sprintf(DATE, "%s", ascii_date((mtime))); + if (NEWSFEED == LOCAL && !NoAction) + { + SITE = MYSITE; + PATH = MYBBSID; + GROUPS = group; + +#ifndef MapleBBS + echomaillog(); +#endif + } + BODY = ""; + FD = open(fileglue("%s/boards/%s/%s", BBSHOME, board, filename), O_RDONLY); + if (FD < 0) + { + if (Verbose) + printf(" !! can't open %s/boards/%s/%s\n", BBSHOME, board, filename); + else + fprintf(stderr, "can't open %s/boards/%s/%s\n", BBSHOME, board, filename); + return -1; + } + + FD_SIZE = filesize(fileglue("%s/boards/%s/%s", BBSHOME, board, filename)); + if (FD_BUF == NULL) + { + FD_BUF = (char *) mymalloc(FD_SIZE + 1 + strlen(COMMENT)); + } + else + { + FD_BUF = (char *) myrealloc(FD_BUF, FD_SIZE + 1 + strlen(COMMENT)); + } + FD_END = FD_BUF + FD_SIZE; + *FD_END = '\0'; + read(FD, FD_BUF, FD_SIZE); + sprintf(FD_END, "%s", COMMENT); + FD_SIZE += strlen(COMMENT); + FD_END += strlen(COMMENT); + if (Verbose) + { + printf("<read in> %s/boards/%s/%s\n", BBSHOME, board, filename); + } + + *ORGANIZATION = '\0'; + *NEWSCONTROL = '\0'; + *NEWSAPPROVED = '\0'; + *NNTPHOST_BUF = '\0'; + NNTPHOST = NULL; + + for (buffer = FD_BUF, bufferp = strchr(buffer, '\n'); + buffer && *buffer; bufferp = strchr(buffer, '\n')) + { + if (bufferp) + *bufferp = '\0'; + if (*buffer == '\0') + { + break; + } + /* printf("get buffer %s\n", buffer); */ + if (NEWSFEED == REMOTE) + { + if (strncmp(buffer, "Date: ", 11) == 0) + { + strcpy(DATE_BUF, buffer + 11); + DATE = DATE_BUF; + } + else if (strncmp(buffer, "發信站: ", 8) == 0) + { + char *m, *n; + + m = strchr(buffer, '('); + n = strrchr(buffer, ')'); + if (m && n) + { + strncpy(DATE_BUF, m + 1, n - m - 1); + DATE_BUF[n - m - 1] = '\0'; + DATE = DATE_BUF; + strncpy(ORGANIZATION, buffer + 8, m - 8 - buffer - 1); + ORGANIZATION[m - 8 - buffer - 1] = '\0'; + } + } + else if (strncmp(buffer, "Control: ", 9) == 0) + { + strcpy(NEWSCONTROL, buffer + 9); + } + else if (strncmp(buffer, "Approved: ", 10) == 0) + { + strcpy(NEWSAPPROVED, buffer + 10); + } + else if (strncmp(buffer, "Origin: ", 8) == 0) + { + strcpy(NNTPHOST_BUF, buffer + 8); + NNTPHOST = NNTPHOST_BUF; + } + } + if (bufferp) + { + *bufferp = '\n'; + buffer = bufferp + 1; + } + else + { + break; + } + } + if (bufferp) + { + BODY = bufferp + 1; + } + else + BODY = ""; + if (bufferp) + for (buffer = bufferp + 1, bufferp = strchr(buffer, '\n'); + buffer && *buffer; bufferp = strchr(buffer, '\n')) + { + if (bufferp) + *bufferp = '\0'; + /* printf("get line (%s)\n", buffer); */ + /* if( strcmp(buffer,".")==0 ) { buffer[1]='.'; buffer[2]='\0'; } */ + if (NEWSFEED == REMOTE && + strncmp(NEWSCONTROL, "cancel", 5) == 0 && + strncmp(buffer, "------------------", 18) == 0) + { + break; + } + else if (strncmp(buffer, "◆ From: ", 9) == 0) + { + strcpy(NNTPHOST_BUF, buffer + 9); + NNTPHOST = NNTPHOST_BUF; + } + /* $BODY[ @BODY ] = "$_\r\n"; */ + if (bufferp) + { + *bufferp = '\n'; + buffer = bufferp + 1; + } + else + { + break; + } + } + /* # fprintf("BODY @BODY\n"; */ + close(FD); + } + return 0; +} + +#ifdef TEST +#endif + +openfeed(node) + nodelist_t *node; +{ + if (node->feedfp == NULL) + { + node->feedfp = fopen(fileglue("%s/%s.link", INNDHOME, node->node), "a"); + } +} + +queuefeed(node, textline) + nodelist_t *node; + char *textline; +{ + openfeed(node); + if (node->feedfp != NULL) + { + flock(fileno(node->feedfp), LOCK_EX); + fprintf(node->feedfp, "%s", textline); + fflush(node->feedfp); + flock(fileno(node->feedfp), LOCK_UN); + } +} + +post_article(node, site, sover, textline) + nodelist_t *node; + char *site; + soverview_t *sover; + char *textline; +{ + int status; + char *filename = sover->filename; + char *msgid = sover->msgid; + char *board = sover->board; + char *bodyp, *body; + + if (Verbose) + fprintf(stdout, "<post_article> %s %s %s\n", site, filename, msgid); + if (NoAction && Verbose) + { + printf(" ==>%s\n", sover->path); + printf(" ==>%s:%s\n", sover->from, sover->group); + printf(" ==>%s:%s\n", sover->subject, sover->date); + body = BODY; + bodyp = strchr(body, '\n'); + if (bodyp) + *bodyp = '\0'; + printf(" ==>%s\n", body); + if (bodyp) + *bodyp = '\n'; + if (bodyp) + { + body = bodyp + 1; + bodyp = strchr(body, '\n'); + if (bodyp) + *bodyp = '\0'; + printf(" ==>%s\n", body); + if (bodyp) + *bodyp = '\n'; + } + } + if (NoAction) + return 1; + if (NEWSFEED == REMOTE) + { + fprintf(NNTPwfp, "Path: %s\r\n", sover->path); + fprintf(NNTPwfp, "From: %s\r\n", sover->from); + fprintf(NNTPwfp, "Newsgroups: %s\r\n", sover->group); + fprintf(NNTPwfp, "Subject: %s\r\n", sover->subject); + /* # fprintf( NNTPwfp,"Post with subject ($subject)\n"); */ + fprintf(NNTPwfp, "Date: %s\r\n", sover->date); + if (*ORGANIZATION) + fprintf(NNTPwfp, "Organization: %s\r\n", ORGANIZATION); + fprintf(NNTPwfp, "Message-ID: <%s>\r\n", sover->msgid); + if (*NEWSCONTROL) + fprintf(NNTPwfp, "Control: %s\r\n", NEWSCONTROL); + if (*NEWSAPPROVED) + fprintf(NNTPwfp, "Approved: %s\r\n", NEWSAPPROVED); + } + else + { + fprintf(NNTPwfp, "Path: %s\r\n", MYBBSID); + fprintf(NNTPwfp, "From: %s\r\n", sover->from); + fprintf(NNTPwfp, "Newsgroups: %s\r\n", sover->group); + fprintf(NNTPwfp, "Subject: %s\r\n", sover->subject); + fprintf(NNTPwfp, "Date: %s\r\n", sover->date); + fprintf(NNTPwfp, "Organization: %s\r\n", MYSITE); + fprintf(NNTPwfp, "Message-ID: <%s>\r\n", sover->msgid); + fprintf(NNTPwfp, "X-Filename: %s/%s\r\n", sover->board, sover->filename); + } + if (NNTPHOST && *NNTPHOST && USEIHAVE) + fprintf(NNTPwfp, "NNTP-Posting-Host: %s\r\n", NNTPHOST); + else if (NNTPHOST && *NNTPHOST) + fprintf(NNTPwfp, "X-Auth-From: %s\r\n", NNTPHOST); + if (*CONTROL) + { + fprintf(NNTPwfp, "Control: %s\r\n", CONTROL); + } + fputs("\r\n", NNTPwfp); + for (body = BODY, bodyp = strchr(body, '\n'); + body && *body; bodyp = strchr(body, '\n')) + { + if (bodyp) + *bodyp = '\0'; + + fputs(body, NNTPwfp); + if (body[0] == '.' && body[1] == '\0') + fputs(".", NNTPwfp); + fputs("\r\n", NNTPwfp); + if (bodyp) + { + *bodyp = '\n'; + body = bodyp + 1; + } + else + { + break; + } + } + /* print "send out @BODY\n"; */ + status = tcpcommand("."); + /* 435 duplicated article 437 invalid header */ + + if (USEIHAVE) + { + if (status == 235) + { + if (NEWSFEED == LOCAL) + { + bbslog("Sendout <%s> from %s/%s\n", msgid, board, filename); + } + } + else if (status == 437 || status == 435) + { + bbslog("<bbslink> :Warn: %d %s <%s>\n", status, (char *) tcpmessage(), msgid); + if (Verbose) + printf(":Warn: %d %s <%s>\n", status, (char *) tcpmessage(), msgid); + return 0; + } + else + { + bbslog("<bbslink> :Err: %d %s of <%s>\n", status, (char *) tcpmessage(), msgid); + if (Verbose) + printf(":Err: %d %s of <%s>\n", status, (char *) tcpmessage(), msgid); + if (!strstr(tcpmessage(), "Article not posted")) + queuefeed(node, textline); + return 0; + } + } + else if (USEPOST) + { + if (status == 240) + { + bbslog("Sendout <%s> from %s/%s\n", msgid, board, filename); + } + else if (status == 437 || status == 435) + { + bbslog("<bbslink> :Warn: %d %s <%s>\n", status, (char *) tcpmessage(), msgid); + if (Verbose) + printf(":Warn: %d %s <%s>\n", status, (char *) tcpmessage(), msgid); + return 0; + } + else + { + bbslog("<bbslink> :Err: %d %s of <%s>\n", status, (char *) tcpmessage(), msgid); + if (!strstr(tcpmessage(), "Article not posted")) + queuefeed(node, textline); + return 0; + } + } + else + { + if (status == 250) + { + bbslog("<bbslink> DATA Sendout <%s> from %s/%s\n", msgid, board, filename); + if (Verbose) + printf("<DATA Sendout> <%s> from %s/%s\n", msgid, board, filename); + } + else + { + bbslog("<bbslink> :Err: %d %s of <%s>\n", status, (char *) tcpmessage(), msgid); + if (Verbose) + printf(":Err: %d %s of <%s>\n", status, (char *) tcpmessage(), msgid); + if (!strstr(tcpmessage(), "Article not posted")) + queuefeed(node, textline); + return 0; + } + } + return 1; +} + +process_cancel(board, filename, userid, nickname, subject) + char *board, *filename, *userid, *nickname, *subject; +{ + time_t mtime; + soverview_t sover; + + if (!userid || !*userid) + { + return; + } + mtime = -1; + strncpy(FROM_BUF, fileglue("%s.bbs@%s (%s)", userid, MYADDR, nickname), sizeof FROM_BUF); + FROM = FROM_BUF; + sover.from = FROM; + sover.board = board; + sover.subject = subject; + PATH = MYBBSID; + sover.path = MYBBSID; + /* save_outgoing(&sover, filename, userid, poster, -1); */ + save_outgoing(&sover, filename, userid, userid, -1); +} + +open_link(hostname, hostprot, hostport) + char *hostname, *hostprot, *hostport; +{ + USEIHAVE = 1; + USEPOST = 0; + USEDATA = 0; + FEEDTYPE = ' '; + if (Verbose) + printf("<OPEN_link> %s %s %s\n", hostname, hostprot, hostport); + if (strncasecmp(hostprot, "IHAVE", 5) != 0) + { + USEIHAVE = 0; + USEPOST = 1; + if (strncasecmp(hostprot, "POST", 4) == 0) + { + USEPOST = 1; + } + else if (strncasecmp(hostprot, "DATA", 4) == 0) + { + USEPOST = 0; + USEDATA = 1; + } + } + + FEEDTYPE = hostname[0]; + if (!USEDATA) + { + char *atsign; + + if (FEEDTYPE == '-' || FEEDTYPE == '+') + { + hostname = hostname + 1; + } + atsign = strchr(hostname, '@'); + if (atsign != NULL) + { + hostname = atsign + 1; + } + if (!NoAction) + { + if (Verbose) + printf("<inetclient> %s %s\n", hostname, hostport); + if ((NNTP = inetclient(hostname, hostport, "tcp")) < 0) + { + bbslog("<bbslink> :Err: server %s %s error: cant connect\n", hostname, hostport); + if (Verbose) + printf(":Err: server %s %s error: cant connect\n", hostname, hostport); + return 0; + /* exit( 0 ); */ + /* return; */ + } + NNTPrfp = fdopen(NNTP, "r"); + NNTPwfp = fdopen(NNTP, "w"); + fgets(NNTPbuffer, sizeof NNTPbuffer, NNTPrfp); + if (atoi(NNTPbuffer) != 200) + { + bbslog("<bbslink> :Err: server error: %s", NNTPbuffer); + if (Verbose) + printf(":Err: server error: %s", NNTPbuffer); + return 0; + /* exit( 0 ); */ + } + } + else + { + if (Verbose) + printf("<inetclient> %s %s\n", hostname, hostport); + } + } + else + { + if (!NoAction) + { + if (Verbose) + printf("<inetclient> localhost %s\n", hostport); + if ((NNTP = inetclient("localhost", hostport, "tcp")) < 0) + { + bbslog("<bbslink> :Err: server %s port %s error: cant connect\n", hostname, hostport); + if (Verbose) + printf(":Err: server error: cant connect"); + return 0; + /* exit( 0 ); */ + /* return; */ + } + NNTPrfp = fdopen(NNTP, "r"); + NNTPwfp = fdopen(NNTP, "w"); + fgets(NNTPbuffer, sizeof NNTPbuffer, NNTPrfp); + if (strncmp(NNTPbuffer, "220", 3) != 0) + { + bbslog("<bbslink> :Err: server error: %s", NNTPbuffer); + if (Verbose) + printf(":Err: server error: %s", NNTPbuffer); + return 0; + /* exit( 0 ); */ + } + if (strncmp(NNTPbuffer, "220-", 4) == 0) + { + fgets(NNTPbuffer, sizeof NNTPbuffer, NNTPrfp); + } + } + else + { + if (Verbose) + printf("<inetclient> %s %s\n", hostname, hostport); + } + } + return 1; +} + +send_outgoing(node, site, hostname, sover, textline) + nodelist_t *node; + soverview_t *sover; + char *hostname, *site; + char *textline; +{ + int status; + char *board, *filepath, *msgid; + int returnstatus = 0; + + board = sover->board; + filepath = sover->filename; + msgid = sover->msgid; + + if (Verbose) + printf("<send_outgoing> %s:%s:%s:%s\n", site, board, filepath, msgid); + if (BODY != NULL && !NoAction) + { + if (USEIHAVE) + { +/* status = tcpcommand("IHAVE <%s>", msgid);*/ + char buf[80]; + sprintf(buf, "IHAVE <%s>", msgid); + status = tcpcommand(buf); + if (status == 335) + { + returnstatus = post_article(node, site, sover, textline); + } + else if (status == 435) + { + bbslog("<bbslink> :Warn: %d %s, IHAVE <%s>\n", status, (char *) tcpmessage(), msgid); + if (Verbose) + printf(":Warn: %d %s, IHAVE <%s>\n", status, (char *) tcpmessage(), msgid); + returnstatus = 0; + } + else + { + bbslog("<bbslink> :Err: %d %s, IHAVE <%s>\n", status, (char *) tcpmessage(), msgid); + if (Verbose) + printf(":Err: %d %s, IHAVE <%s>\n", status, (char *) tcpmessage(), msgid); + if (!strstr(tcpmessage(), "Article not posted")) + queuefeed(node, textline); + returnstatus = 0; + } + } + else if (USEPOST) + { + tcpcommand("MODE READER"); + status = tcpcommand("POST"); + if (status == 340) + { + returnstatus = post_article(node, site, sover, textline); + } + else if (status == 441) + { + bbslog("<bbslink> :Warn: %d %s, POST <%s>\n", status, (char *) tcpmessage(), msgid); + if (Verbose) + printf(":Warn: %d %s, POST <%s>\n", status, (char *) tcpmessage(), msgid); + returnstatus = 0; + } + else + { + bbslog("<bbslink> :Err: %d %s, POST <%s>\n", status, (char *) tcpmessage(), msgid); + if (Verbose) + printf(":Err: %d %s, POST <%s>\n", status, (char *) tcpmessage(), msgid); + if (!strstr(tcpmessage(), "Article not posted")) + queuefeed(node, textline); + returnstatus = 0; + } + } + else + { + tcpcommand("HELO"); + tcpcommand("MAIL FROM: bbs"); + tcpcommand("RCPT TO: %s", hostname); + status = tcpcommand("DATA"); + if (status == 354) + { + returnstatus = post_article(node, site, sover, textline); + } + else + { + bbslog("<bbslink> :Err: %d %s, DATA <%s>\n", status, (char *) tcpmessage(), msgid); + if (Verbose) + printf(":Err: %d %s, DATA <%s>\n", status, (char *) tcpmessage(), msgid); + if (!strstr(tcpmessage(), "Article not posted")) + queuefeed(node, textline); + returnstatus = 0; + } + } + } + else if (NoAction) + { + returnstatus = post_article(node, site, sover, textline); + } + return returnstatus; +} + +save_nntplink(node, overview) + nodelist_t *node; + char *overview; +{ + FILE *POSTS; + char buffer[1024]; + + openfeed(node); + POSTS = fopen(overview, "r"); + if (POSTS == NULL) + return 0; + openfeed(node); + /* if (node->feedfp == NULL) return 0; */ + flock(fileno(node->feedfp), LOCK_EX); + while (fgets(buffer, sizeof buffer, POSTS) != NULL) + { + fputs(buffer, node->feedfp); + fflush(node->feedfp); + } + flock(fileno(node->feedfp), LOCK_UN); + fclose(POSTS); + if (Verbose) + printf("<Unlinking> %s\n", overview); + if (!NoAction) + unlink(overview); + return 1; +} + + +char * +get_tmpfile(tmpfile) + char *tmpfile; +{ + FILE *FN; + static char result[256]; + + FN = fopen(tmpfile, "r"); + fgets(result, sizeof result, FN); + fclose(FN); + unlink(tmpfile); + return (result); +} + +/* cancel moderating posts */ + +cancel_outgoing(board, filename, from, subject) + char *board, *filename, *from, *subject; +{ + char *base, filepath[MAXPATHLEN]; + FILE *FN; + char *result; + char TMPFILE[MAXPATHLEN]; + + if (Verbose) + { + printf("<cancel_outgoing> %s %s %s %s\n", board, filename, from, subject); + } + + sprintf(TMPFILE, "/tmp/cancel_outgoing.%d.%d", getuid(), getpid()); + + bbslog("<cancel_outgoing> Try to move moderated post from %s to deleted\n", board); + if (Verbose) + printf("Try to move moderated post from %s to deleted\n", board); + FN = popen(fileglue("%s/bbspost post %s/boards/deleted > %s", + INNDHOME, BBSHOME, TMPFILE), "w"); + if (FN == NULL) + { + bbslog("<cancel_outgoing> can't run %s/bbspost\n", INNDHOME); + if (Verbose) + printf("<cancel_outgoing> can't run %s/bbspost\n", INNDHOME); + return 0; + } + fprintf(FN, "%s\n", from); + fprintf(FN, "%s\n", subject); + fprintf(FN, "發信人: %s, 信區: %s\n", from, board); + fprintf(FN, "標 題: %s\n", subject); + fprintf(FN, "發信站: %s (%s)\n", MYSITE, DATE); + fprintf(FN, "轉信站: %s\n", MYBBSID); + fputs("\n", FN); + fputs(BODY, FN); + pclose(FN); + result = (char *) get_tmpfile(TMPFILE); + if (strncmp(result, "post to ", 8) == 0) + { + /* try to remove it */ + strncpy(filepath, fileglue("%s/boards/%s/%s", BBSHOME, board, filename), sizeof filepath); + if (isfile(filepath)) + { + Rename(filepath, fileglue("%s.cancel", filepath)); + } + FN = fopen(filepath, "w"); + + fprintf(FN, "發信人: %s, 信區: %s\n", from, board); + fprintf(FN, "標 題: <article cancelled and mailed to the moderator\n"); + fprintf(FN, "發信站: %s (%s)\n", MYSITE, DATE); + fprintf(FN, "轉信站: %s\n", MYBBSID); + fprintf(FN, "\n"); + fputs("\n", FN); + fprintf(FN, "你的文章 \"%s\" 已經送往審核中. 請等待回覆.\n", subject); + fputs("\n", FN); + fputs("Your post has been sent to the moderator and move\n", FN); + fputs("into the deleted board. If the post accepted by the moderator,\n", FN); + fputs("it will be posted in this board again. Please wait.\n", FN); + + } + else + { + bbslog("%s", result); + } + + bbslog("%s/bbspost cancel %s %s %s moderate\n", INNDHOME, BBSHOME, board, filename); + if (Verbose) + printf("%s/bbspost cancel %s %s %s moderate\n", INNDHOME, BBSHOME, board, filename); + system(fileglue("%s/bbspost cancel %s %s %s moderate", + INNDHOME, BBSHOME, board, filename)); + return 1; +} + +/* + * send_nntplink open_link read_outgoing send_outgoing post_article + * cancel_outgoing + */ +send_nntplink(node, site, hostname, hostprot, hostport, overview, nlcount) + nodelist_t *node; + char *site, *hostname, *hostprot, *hostport, *overview; + int nlcount; +{ + FILE *POSTS; + char textline[1024]; + char baktextline[1024]; + char *filepath; + int status; + + if (Verbose) + { + printf("<send nntplink> %s %s %s %s\n", site, hostname, hostprot, hostport); + printf(" ==> %s\n", overview); + } + if (!open_link(hostname, hostprot, hostport)) + { + save_nntplink(node, overview); + return 0; + } + POSTS = fopen(overview, "r"); + if (POSTS == NULL) + { + if (Verbose) + printf("open %s failed\n", overview); + return 0; + } + while (fgets(textline, sizeof textline, POSTS) != NULL) + { + char *linebreak = strchr(textline, '\n'); + char *ptr; + char *board, *filename, *subject, *group, *mtime, *from; + char *outgoingtype; + char *msgid, *path; + soverview_t soverview; + + strcpy(baktextline, textline); + if (linebreak) + *linebreak = '\0'; + + board = "", filename = "", mtime = "", group = "", from = "", subject = ""; + outgoingtype = "", msgid = "", path = ""; + /* get board field */ + board = textline; + ptr = strchr(textline, '\t'); + if (ptr == NULL) + continue; + *ptr++ = '\0'; + + /* filename field */ + filename = ptr; + + ptr = strchr(ptr, '\t'); + if (ptr == NULL) + continue; + + *ptr++ = '\0'; + + /* group field */ + group = ptr; + ptr = strchr(ptr, '\t'); + if (ptr == NULL) + continue; + *ptr++ = '\0'; + + /* mtime field */ + mtime = ptr; + ptr = strchr(ptr, '\t'); + if (ptr == NULL) + continue; + *ptr++ = '\0'; + + /* from field */ + from = ptr; + ptr = strchr(ptr, '\t'); + if (ptr == NULL) + continue; + *ptr++ = '\0'; + + /* subject */ + subject = ptr; + ptr = strchr(ptr, '\t'); + if (ptr == NULL) + goto try_read_outgoing; + *ptr++ = '\0'; + + /* outgoing type field */ + outgoingtype = ptr; + ptr = strchr(ptr, '\t'); + if (ptr == NULL) + goto try_read_outgoing; + *ptr++ = '\0'; + + /* msgid */ + msgid = ptr; + ptr = strchr(ptr, '\t'); + if (ptr == NULL) + goto try_read_outgoing; + *ptr++ = '\0'; + + /* path */ + path = ptr; + ptr = strchr(ptr, '\t'); + if (ptr == NULL) + goto try_read_outgoing; + +try_read_outgoing: + + NEWSFEED = LOCAL; + if (outgoingtype && msgid && path && *outgoingtype && *msgid && *path) + { + char *left, *right; + + NEWSFEED = REMOTE; + left = strchr(msgid, '<'); + right = strrchr(msgid, '>'); + if (left) + msgid = left + 1; + if (right) + *right = '\0'; + } + soverview.board = board; + soverview.filename = filename; + soverview.group = group; + soverview.mtime = atol(mtime); + soverview.from = from; + soverview.subject = subject; + soverview.outgoingtype = outgoingtype; + soverview.msgid = msgid; + soverview.path = path; + if (read_outgoing(&soverview) == 0) + { + int sendresult = send_outgoing(node, site, hostname, &soverview, baktextline); + int sendfailed = 1 - sendresult; + + if (NEWSFEED == REMOTE) + { + BBSLINK_STAT[nlcount].remotesendout += sendresult; + BBSLINK_STAT[nlcount].remotefailed += sendfailed; + } + else + { + BBSLINK_STAT[nlcount].localsendout += sendresult; + BBSLINK_STAT[nlcount].localfailed += sendfailed; + } + if (node->feedtype == '-') + { + if (!NoAction && sendresult) + cancel_outgoing(board, filename, from, subject); + } + } + } + fclose(POSTS); + close_link(); + if (Verbose) + printf("<Unlinking> %s\n", overview); + if (!NoAction) + unlink(overview); +} + +close_link() +{ + int status; + + if (Verbose) + printf("<close_link>\n"); + if (NoAction) + return; + status = tcpcommand("QUIT"); + if (status != 205 && status != 221) + { + bbslog("<bbslink> :Err: Cannot quit message '%d %s'\n", status, (char *) tcpmessage()); + if (Verbose) + printf(":Err: Cannot quit message '%d %s'\n", status, (char *) tcpmessage()); + } + fclose(NNTPwfp); + fclose(NNTPrfp); + close(NNTP); +} + + +/* + * send_article() send_nntplink() read_outgoing() + * + */ + +send_article() +{ + char *site, *addr, *protocol, *port, *op; + char *nntphost; + int nlcount; + + chdir(INNDHOME); + + for (nlcount = 0; nlcount < NLCOUNT; nlcount++) + { + nodelist_t *node; + char linkfile[MAXPATHLEN]; + char sendfile[MAXPATHLEN]; + char feedfile[MAXPATHLEN]; + char feedingfile[MAXPATHLEN]; + char protocol[MAXBUFLEN], port[MAXBUFLEN]; + + node = NODELIST + nlcount; + site = node->node; + nntphost = node->host; + op = node->protocol; + + if (DefaultFeedSite && *DefaultFeedSite) + { + if (strcmp(node->node, DefaultFeedSite) != 0) + continue; + } + + if (op && (strncasecmp(op, "ihave", 5) == 0 || + strncasecmp(op, "post", 4) == 0 || + strncasecmp(op, "data", 4) == 0)) + { + char *left, *right; + + left = strchr(op, '('), right = strrchr(op, ')'); + if (left && right) + { + *left = '\0'; + *right = '\0'; + strncpy(protocol, op, sizeof protocol); + strncpy(port, left + 1, sizeof port); + *left = '('; + *right = ')'; + } + else + { + strncpy(protocol, op, sizeof protocol); + strncpy(port, "nntp", sizeof port); + } + } + else + { + strcpy(protocol, "IHAVE"); + strcpy(port, "nntp"); + } + sprintf(linkfile, "%s.link", site); + sprintf(sendfile, "%s.sending", site); + sprintf(feedfile, "%s.feed", site); + sprintf(feedingfile, "%s.feeding", site); + if (isfile(sendfile) && !iszerofile(sendfile)) + { + if (bbslink_get_lock(sendfile)) + { + send_nntplink(node, site, nntphost, protocol, port, sendfile, nlcount); + bbslink_un_lock(sendfile); + } + } + if (isfile(linkfile) && !iszerofile(linkfile)) + { + if (!NoAction) + { + if (bbslink_get_lock(sendfile) && bbslink_get_lock(linkfile) && + bbslink_get_lock(feedingfile)) + { + if (isfile(sendfile) && !iszerofile(sendfile)) + { + save_nntplink(node, sendfile); + } + if (node->feedfp) + { + fclose(node->feedfp); + node->feedfp = NULL; + } + Rename(linkfile, sendfile); + send_nntplink(node, site, nntphost, protocol, port, sendfile, nlcount); + bbslink_un_lock(linkfile); + bbslink_un_lock(sendfile); + bbslink_un_lock(feedingfile); + } + } + else + { + send_nntplink(node, site, nntphost, protocol, port, linkfile, nlcount); + } + } + if (isfile(feedingfile) && !iszerofile(feedingfile)) + { + if (bbslink_get_lock(feedingfile)) + { + send_nntplink(node, site, nntphost, protocol, port, feedingfile, nlcount); + bbslink_un_lock(feedingfile); + } + } + if (isfile(feedfile) && !iszerofile(feedfile)) + { + if (!NoAction) + { + if (bbslink_get_lock(feedfile) && bbslink_get_lock(feedingfile)) + { + if (isfile(feedingfile) && !iszerofile(feedingfile)) + { + save_nntplink(node, feedingfile); + if (node->feedfp) + { + fclose(node->feedfp); + node->feedfp = NULL; + } + } + Rename(feedfile, feedingfile); + system(fileglue("%s/ctlinnbbsd reload > /dev/null", INNDHOME)); + send_nntplink(node, site, nntphost, protocol, port, feedingfile, nlcount); + bbslink_un_lock(feedfile); + bbslink_un_lock(feedingfile); + } + } + else + { + send_nntplink(node, site, nntphost, protocol, port, feedfile, nlcount); + } + } + } +} + +/* bntplink() bbspost() process_article() process_cancel() send_article() */ + + +show_usage(argv) + char *argv; +{ + fprintf(stderr, "%s initialization failed or improper options !!\n", argv); + fprintf(stderr, "Usage: %s [options] bbs_home\n", argv); + fprintf(stderr, " -v (show transmission status)\n"); + fprintf(stderr, " -n (dont send out articles and leave queue untouched)\n"); + fprintf(stderr, " -s site (only process articles sent to site)\n"); + fprintf(stderr, " -V (visit only: bbspost visit)\n"); + fprintf(stderr, " -N (no visit, and only process batch queue)\n"); + fprintf(stderr, " -k (kill the former bbslink process before started)\n\n"); + fprintf(stderr, "本程式要正常執行必須將以下檔案置於 %s/innd 下:\n", BBSHOME); + fprintf(stderr, "bbsname.bbs 設定貴站的 BBS ID (請儘量簡短)\n"); + fprintf(stderr, "nodelist.bbs 設定網路各 BBS 站的 ID, Address 和 fullname\n"); + fprintf(stderr, "newsfeeds.bbs 設定網路信件的 newsgroup board nodelist ...\n"); +} + + +bntplink(argc, argv) + int argc; + char **argv; +{ + static char *OUTING = ".outing"; + nodelist_t *nl; + int linkport; + char result[4096]; + char cancelfile[MAXPATHLEN], cancelpost[MAXPATHLEN]; + char bbslink_lockfile[MAXPATHLEN]; + FILE *NEWPOST; + char *left, *right; + int nlcount; + +// strcpy(BBSHOME, argv[0]); + if (initial_bbs("link") == 0) + { + return -1; + } + + BBSLINK_STAT = (stat_t *) malloc(sizeof(stat_t) * (NLCOUNT + 1)); + for (nlcount = 0; nlcount < NLCOUNT; nlcount++) + { + BBSLINK_STAT[nlcount].localsendout = 0; + BBSLINK_STAT[nlcount].remotesendout = 0; + BBSLINK_STAT[nlcount].localfailed = 0; + BBSLINK_STAT[nlcount].remotefailed = 0; + } + + nl = (nodelist_t *) search_nodelist_bynode(MYBBSID); + if (nl == NULL) + { + *MYADDR = '\0'; + *MYSITE = '\0'; + *LINKPROTOCOL = '\0'; + } + else + { + strncpy(MYADDR, nl->host, sizeof MYADDR); + strncpy(LINKPROTOCOL, nl->protocol, sizeof LINKPROTOCOL); + strncpy(MYSITE, nl->comments, sizeof MYSITE); + } + if (Verbose) + { + printf("MYADDR: %s\n", MYADDR); + printf("MYSITE: %s\n", MYSITE); + } + left = strchr(nl->protocol, '('); + right = strrchr(nl->protocol, ')'); + if (left && right) + { + *right = '\0'; + strncpy(LINKPROTOCOL, nl->protocol, sizeof LINKPROTOCOL); + LINKPORT = atoi(left + 1); + *right = ')'; + } + + if (!NoVisit) + { + sprintf(bbslink_lockfile, "%s/.bbslink.visit", INNDHOME); + if (!Rename(fileglue("%s/out.bntp", INNDHOME), OUTING) && bbslink_get_lock(bbslink_lockfile)) + { + /* When try to visit new post, try to lock it */ + NEWPOST = fopen(OUTING, "r"); + if (NEWPOST == NULL) + { + bbslog("<bbslink> Err: can't open %s\n", OUTING); + bbslink_un_lock(bbslink_lockfile); + return -1; + } + while (fgets(result, sizeof result, NEWPOST)) + { + /* chop( $_ ); */ + char *board, *filename, *userid, *nickname, *subject; + char *ptr; + + ptr = strchr(result, '\n'); + if (ptr) + *ptr = '\0'; + + board = filename = userid = nickname = subject = NULL; + /* board field */ + board = result; + ptr = strchr(result, '\t'); + if (ptr == NULL) + continue; + *ptr++ = '\0'; + + /* filename field */ + filename = ptr; + ptr = strchr(ptr, '\t'); + if (ptr == NULL) + continue; + *ptr++ = '\0'; + + /* userid field */ + userid = ptr; + ptr = strchr(ptr, '\t'); + if (ptr == NULL) + continue; + *ptr++ = '\0'; + + /* nickname field */ + nickname = ptr; + ptr = strchr(ptr, '\t'); + if (ptr == NULL) + continue; + *ptr++ = '\0'; + + /* subject field */ + subject = ptr; + /* ptr = strchr(ptr, '\t'); if (ptr == NULL) continue; ptr++ = '\0'; */ + + if(bad_subject(subject)) continue; + + if(outgo_post < MAX_OUTGO_POST){ + out_bntp[outgo_post].board = board; + out_bntp[outgo_post].filename = filename; + out_bntp[outgo_post].userid = userid; + out_bntp[outgo_post].nickname = nickname; + out_bntp[outgo_post].subject = subject; + outgo_post++; + } + + process_article(board, filename, userid, nickname, subject); + } + fclose(NEWPOST); + unlink(OUTING); + bbslink_un_lock(bbslink_lockfile); + } /* getlock */ + } /* if NoVisit is false */ + + sprintf(cancelpost, "%s/cancel.bntp", INNDHOME); + if (isfile(cancelpost)) + { + FILE *CANCELFILE; + + if (bbslink_get_lock(cancelpost) && bbslink_get_lock(cancelfile)) + { + sprintf(cancelfile, "%s.%d", cancelpost, getpid()); + Rename(cancelpost, cancelfile); + CANCELFILE = fopen(cancelfile, "r"); + while (fgets(result, sizeof result, CANCELFILE) != NULL) + { + /* chop( $_ ); */ + char *board, *filename, *userid, *nickname, *subject; + char *ptr, **sptr; + + ptr = strchr(result, '\n'); + if (ptr) + *ptr = '\0'; + board = filename = userid = nickname = subject = NULL; + + /* board field */ + board = result; + ptr = strchr(result, '\t'); + if (ptr == NULL) + continue; + *ptr++ = '\0'; + + /* filename field */ + filename = ptr; + ptr = strchr(ptr, '\t'); + if (ptr == NULL) + continue; + *ptr++ = '\0'; + + /* userid field */ + userid = ptr; + ptr = strchr(ptr, '\t'); + if (ptr == NULL) + continue; + *ptr++ = '\0'; + + /* nickname field */ + nickname = ptr; + ptr = strchr(ptr, '\t'); + if (ptr == NULL) + continue; + *ptr++ = '\0'; + + /* subject field */ + subject = ptr; + /* ptr = strchr(ptr, '\t'); if (ptr == NULL) continue; ptr++ = '\0'; */ + if(!is_outgo_post(board, filename, userid, nickname, subject)) + process_cancel(board, filename, userid, nickname, subject); + } + fclose(CANCELFILE); + if (Verbose) + printf("Unlinking %s\n", cancelfile); + if (!NoAction) + unlink(cancelfile); + bbslink_un_lock(cancelfile); + bbslink_un_lock(cancelpost); + } + } + + for (nlcount = 0; nlcount < NLCOUNT; nlcount++) + { + if (NODELIST[nlcount].feedfp != NULL) + { + fclose(NODELIST[nlcount].feedfp); + NODELIST[nlcount].feedfp = NULL; + } + } + + send_article(); + for (nlcount = 0; nlcount < NLCOUNT; nlcount++) + { + int localsendout, remotesendout, localfailed, remotefailed; + + localsendout = BBSLINK_STAT[nlcount].localsendout; + remotesendout = BBSLINK_STAT[nlcount].remotesendout; + localfailed = BBSLINK_STAT[nlcount].localfailed; + remotefailed = BBSLINK_STAT[nlcount].remotefailed; + if (localsendout || remotesendout || localfailed || remotefailed) + bbslog("<bbslink> [%s]%s lsend:%d rsend:%d lfail:%d rfail:%d\n", + NODELIST[nlcount].node, NoAction ? "NoAction" : "", localsendout, remotesendout, + localfailed, remotefailed); + if (NODELIST[nlcount].feedfp != NULL) + { + fclose(NODELIST[nlcount].feedfp); + NODELIST[nlcount].feedfp = NULL; + } + } + if (BBSLINK_STAT); + free(BBSLINK_STAT); + return 0; +} +/* +termbbslink(sig) + int sig; +{ + bbslog("kill signal received %d, terminated\n", sig); + if (Verbose) + printf("kill signal received %d, terminated\n", sig); + exit(0); +} +*/ +void +termbbslink() +{ + bbslog("kill signal received ??, terminated\n"); + if (Verbose) + printf("kill signal received ??, terminated\n"); + exit(0); +} + +char *REMOTEUSERNAME = ""; +char *REMOTEHOSTNAME = ""; + +extern char *optarg; +extern int opterr, optind; + +main(argc, argv) + int argc; + char **argv; +{ + int c, errflag = 0; + + CONTROL = CONTROL_BUF; + MSGID = MSGID_BUF; + + /* For debug Only */ +#define DEBUGBBSLINK + +#ifdef DEBUGBBSLINK + NoAction = 0; + Verbose = 0; + VisitOnly = 0; + NoVisit = 0; + DefaultFeedSite = ""; +#endif + + while ((c = getopt(argc, argv, "s:hnvVNk")) != -1) + switch (c) + { + case 's': + DefaultFeedSite = optarg; + break; + case 'n': + NoAction = 1; + break; + case 'v': + Verbose = 1; + break; + case 'V': + VisitOnly = 1; + break; + case 'N': + NoVisit = 1; + break; + case 'k': + KillFormerBBSLINK = 1; + break; + case 'h': + default: + errflag++; + break; + } + if (errflag > 0) + { + show_usage(argv[0]); + return (1); + } + if (argc - optind < 1) + { + show_usage(argv[0]); + exit(1); + } + signal(SIGTERM, termbbslink); + if (bntplink(argc - optind, argv + optind) != 0) + { + show_usage(argv[0]); + exit(1); + } + return 0; +} diff --git a/innbbsd/bbslink2.c b/innbbsd/bbslink2.c new file mode 100644 index 00000000..d79c23a9 --- /dev/null +++ b/innbbsd/bbslink2.c @@ -0,0 +1,2017 @@ +#if defined( LINUX ) +# include "innbbsconf.h" +# include "bbslib.h" +# include <varargs.h> +#else +# include <varargs.h> +# include "innbbsconf.h" +# include "bbslib.h" +#endif + +#include <sys/mman.h> + +#ifndef AIX +# include <sys/fcntl.h> +#endif + +#if defined(PalmBBS) +#include <utime.h> +#endif + + +#include "daemon.h" +#include "nntp.h" +#include "externs.h" + +/* + * TODO 1. read newsfeeds.bbs, read nodelist.bbs, read bbsname.bbs 2. scan + * new posts and append to .link 3. rename .link to .send (must lock) 4. + * start to send .send out and append not sent to .link + * + * 5. node.LOCK (with pid) 6. log articles sent + */ + + +#ifndef MAXBUFLEN +#define MAXBUFLEN 256 +#endif + +typedef struct Over_t +{ + time_t mtime; + char date[MAXBUFLEN]; + char nickname[MAXBUFLEN]; + char subject[MAXBUFLEN]; + char from[MAXBUFLEN]; + char msgid[MAXBUFLEN]; + char site[MAXBUFLEN]; + char board[MAXBUFLEN]; +} linkoverview_t; + +typedef struct SendOver_t +{ + char *board, *filename, *group, *from, *subject; + char *outgoingtype, *msgid, *path; + char *date, *control; + time_t mtime; +} soverview_t; + +typedef struct Stat_t +{ + int localsendout; + int localfailed; + int remotesendout; + int remotefailed; +} stat_t; + +static stat_t *BBSLINK_STAT; + +static int NoAction = 0; +static int Verbose = 0; +static int VisitOnly = 0; +static int NoVisit = 0; +static char *DefaultFeedSite = ""; +static int KillFormerBBSLINK = 0; + +extern char *SITE; +extern char *GROUPS; + +char NICKNAME[MAXBUFLEN]; + +char DATE_BUF[MAXBUFLEN]; +extern char *DATE; + +char FROM_BUF[MAXBUFLEN]; +extern char *FROM; + +#ifndef MapleBBS +char POSTER_BUF[MAXBUFLEN]; +char *POSTER; +#endif + +char MYADDR[MAXBUFLEN]; +char MYSITE[MAXBUFLEN]; + +char SUBJECT_BUF[MAXBUFLEN]; +extern char *SUBJECT; + +char MSGID_BUF[MAXBUFLEN]; +extern char *MSGID; + +char LINKPROTOCOL[MAXBUFLEN]; +int LINKPORT; +char ORGANIZATION[MAXBUFLEN]; +char NEWSCONTROL[MAXBUFLEN]; +char NEWSAPPROVED[MAXBUFLEN]; +char NNTPHOST_BUF[MAXBUFLEN]; +extern char *NNTPHOST; +char PATH_BUF[MAXBUFLEN]; +extern char *PATH; + +char CONTROL_BUF[MAXBUFLEN]; +extern char *CONTROL; + +char *BODY, *HEAD; + +int USEIHAVE = 1; +int USEPOST = 0; +int USEDATA = 0; +int FEEDTYPE = ' '; + +int NNTP = -1; +FILE *NNTPrfp = NULL; +FILE *NNTPwfp = NULL; +char NNTPbuffer[1024]; +static char *NEWSFEED; +static char *REMOTE = "REMOTE"; +static char *LOCAL = "LOCAL"; + +static int FD, FD_SIZE; +static char *FD_BUF; +static char *FD_END; + +static char *COMMENT = +"[Ptt 送出]\n"; + +char *fileglue(); + +/* +woju +Cross-fs rename() +*/ + +Rename(char* src, char* dst) +{ + char cmd[200]; + + if (rename(src, dst) == 0) + return 0; + + sprintf(cmd, "/bin/mv %s %s", src, dst); + return system(cmd); + +} + + +bbslink_un_lock(file) + char *file; +{ + char *lockfile = fileglue("%s.LOCK", file); + + if (isfile(lockfile)) + unlink(lockfile); +} + +bbslink_get_lock(file) + char *file; +{ + int lockfd; + char LockFile[MAXPATHLEN]; + + strncpy(LockFile, (char *) fileglue("%s.LOCK", file), sizeof LockFile); + if ((lockfd = open(LockFile, O_RDONLY)) >= 0) + { + char buf[10]; + int pid; + + if (read(lockfd, buf, sizeof buf) > 0 && + (pid = atoi(buf)) > 0 && kill(pid, 0) == 0) + { + if (KillFormerBBSLINK) + { + kill(pid, SIGTERM); + unlink(LockFile); + } + else + { + fprintf(stderr, "another process [%d] running\n", pid); + return 0; + } + } + else + { + fprintf(stderr, "no process [%d] running, but lock file existed, unlinked\n", pid); + unlink(LockFile); + } + close(lockfd); + } + + if ((lockfd = open(LockFile, O_RDWR | O_CREAT | O_EXCL, 0644)) < 0) + { + fprintf(stderr, "lock %s error: another bbslink process running\n", LockFile); + return 0; + } + else + { + char buf[10]; + int pid; + + sprintf(buf, "%-.8d\n", getpid()); + write(lockfd, buf, strlen(buf)); + close(lockfd); + return 1; + } +} + + +int +tcpcommand(va_alist) +va_dcl +{ + va_list ap; + register char *fmt; + char *ptr; + + va_start(ap); + fmt = va_arg(ap, char *); + vfprintf(NNTPwfp, fmt, ap); + fprintf(NNTPwfp, "\r\n"); + fflush(NNTPwfp); + + fgets(NNTPbuffer, sizeof NNTPbuffer, NNTPrfp); + ptr = strchr(NNTPbuffer, '\r'); + if (ptr) + *ptr = '\0'; + ptr = strchr(NNTPbuffer, '\n'); + if (ptr) + *ptr = '\0'; + va_end(ap); + return atoi(NNTPbuffer); +} + +char * +tcpmessage() +{ + char *ptr; + + ptr = strchr(NNTPbuffer, ' '); + if (ptr) + return ptr; + return NNTPbuffer; +} + +read_article(lover, filename, userid) + linkoverview_t *lover; + char *filename, *userid; +{ + FILE *fp; + int fd; + struct stat st; + time_t mtime; + char *buffer; + char *artptr, *artend, *artback; + + if (stat(filename, &st) != 0) + return 0; + lover->mtime = st.st_mtime; + fd = open(filename, O_RDONLY); + if (fd < 0) + { + bbslog("<bbslink> Err: can't open %s\n", filename); + return 0; + } + + if (FD_BUF == NULL) + { + FD_BUF = mymalloc(st.st_size + 1 + strlen(COMMENT)); + } + else + { + FD_BUF = myrealloc(FD_BUF, st.st_size + 1 + strlen(COMMENT)); + } + FD_BUF[st.st_size] = '\0'; + read(fd, FD_BUF, st.st_size); + sprintf(FD_BUF + st.st_size, "%s", COMMENT); + st.st_size += strlen(COMMENT); + FD_SIZE = st.st_size; + for (buffer = FD_BUF, artend = FD_BUF + st.st_size, + artback = strchr(buffer, '\n'); + buffer && buffer < artend && *buffer; + artback = strchr(buffer, '\n') + ) + { + /* while( fgets(buffer, sizeof buffer, fp) != NULL) { */ + char *m, *n; + char *ptr; + + if (artback != NULL) + *artback = '\0'; + if (*buffer == '\0') + break; + +#ifndef MapleBBS + if (strstr(buffer, userid) != NULL) + { + m = strchr(buffer, '('); + n = strrchr(buffer, ')'); + if (m != NULL && n != NULL) + { + strncpy(lover->nickname, m + 1, n - m - 1); + lover->nickname[n - m - 1] = '\0'; + } + else + { + *lover->nickname = '\0'; + } + } + else if (strncmp(buffer, "Date: ", 11) == 0) + { + strcpy(lover->date, buffer + 11); + } + else if (strncmp(buffer, "發信站: ", 8) == 0) + { + m = strchr(buffer, '('); + n = strrchr(buffer, ')'); + strncpy(lover->date, m + 1, n - m - 1); + lover->date[n - m - 1] = '\0'; + } +#endif + + if (artback != NULL) + { + *artback = '\n'; + buffer = artback + 1; + } + else + { + break; + } + } + if (artback != NULL) + BODY = artback + 1; + else + BODY = ""; + close(fd); + return 1; +} + +save_outgoing(sover, filename, userid, poster, mtime) + soverview_t *sover, *filename, *userid, *poster; + time_t mtime; +{ + newsfeeds_t *nf; + char *group, *server, *serveraddr; + char *subject, *path; + char *board; + char *ptr1, *ptr2; + + board = sover->board; + + PATH = MYBBSID; + nf = (newsfeeds_t *) search_board(board); + if (nf == NULL) + { + bbslog("<bbslink> save_outgoing: No such board %s\n", board); + return; + } + else + { + group = nf->newsgroups; + server = nf->path; + } + if (!server || !*server) + { + sprintf(PATH_BUF, "%.*s (local)", sizeof PATH_BUF - 9, MYBBSID); + PATH = PATH_BUF; + serveraddr = ""; + sover->path = PATH; + } + for (ptr1 = server; ptr1 && *ptr1;) + { + nodelist_t *nl; + char savech; + + for (; *ptr1 && isspace(*ptr1); ptr1++); + if (!*ptr1) + break; + for (ptr2 = ptr1; *ptr2 && !isspace(*ptr2); ptr2++); + savech = *ptr2; + *ptr2 = '\0'; + nl = (nodelist_t *) search_nodelist_bynode(ptr1); + *ptr2 = savech; + ptr1 = ptr2++; + if (nl == NULL) + continue; + /* if (nl->feedfp == NULL) continue; */ + + if (nl->host && *nl->host) + { + if (nl->feedfp == NULL) + { + nl->feedfp = fopen(fileglue("%s/%s.link", INNDHOME, nl->node), "a"); + if (nl->feedfp == NULL) + { + bbslog("<save outgoing> append failed for %s/%s.link", INNDHOME, nl->node); + } + } + if (nl->feedfp != NULL) + { + flock(fileno(nl->feedfp), LOCK_EX); + fprintf(nl->feedfp, "%s\t%s\t%s\t%ld\t%s\t%s\n", sover->board, filename, group, mtime, FROM, sover->subject); + fflush(nl->feedfp); + flock(fileno(nl->feedfp), LOCK_UN); + } + } + if (savech == '\0') + break; + } +} + + +#ifndef MapleBBS +save_article(board, filename, sover) + char *board, *filename; + soverview_t *sover; +{ + FILE *FN; + + if (Verbose) + printf("<save_article> %s %s\n", board, filename); + FN = fopen(fileglue("%s/boards/%s/%s", BBSHOME, board, filename), "w"); + if (FN == NULL) + { + bbslog("<save_article> err: %s %s\n", board, filename); + if (Verbose) + printf("<save_article> err: %s %s\n", board, filename); + return 0; + } + flock(fileno(FN), LOCK_EX); + fprintf(FN, "發信人: %s, 信區: %s\n", POSTER, sover->board); + fprintf(FN, "標 題: %s\n", sover->subject); + fprintf(FN, "發信站: %s (%s)\n", MYSITE, sover->date); + fprintf(FN, "轉信站: %s\n", sover->path); + fprintf(FN, "\n"); + fputs(BODY, FN); + flock(fileno(FN), LOCK_UN); + fclose(FN); + +#if defined(PalmBBS) + { + struct utimbuf times; + + times.actime = sover->mtime; + times.modtime = sover->mtime; + utime(fileglue("%s/boards/%s/%s", BBSHOME, board, filename), ×); + utime(fileglue("%s/.bcache/%s", BBSHOME, board), NULL); + } +#endif +} +#endif + +/* process_article() read_article() save_outgoing() save_article() */ + +process_article(board, filename, userid, nickname, subject) + char *board, *filename, *userid, *nickname, *subject; +{ + char *n, *filepath; + char poster[MAXBUFLEN]; + soverview_t sover; + + if (!*userid) + { + return; + } + else if (!subject || !*subject) + { + subject = "無題"; + } + filepath = fileglue("%s/boards/%s/%s", BBSHOME, board, filename); + if (isfile(filepath)) + { + linkoverview_t lover; + + if (read_article(&lover, filepath, userid)) + { + +#ifndef MapleBBS + strncpy(POSTER_BUF, fileglue("%s@%s (%s)", userid, MYBBSID, nickname), sizeof POSTER_BUF); + POSTER = POSTER_BUF; +#endif + + strncpy(FROM_BUF, fileglue("%s.bbs@%s (%s)", userid, MYADDR, nickname), sizeof FROM_BUF); + FROM = FROM_BUF; + sover.from = FROM; + sover.board = board; + sover.subject = subject; + PATH = MYBBSID; + sover.path = MYBBSID; + sover.date = lover.date; + sover.mtime = lover.mtime; + if (!VisitOnly) + { + save_outgoing(&sover, filename, userid, poster, lover.mtime); + +#ifndef MapleBBS + save_article(board, filename, &sover); +#endif + } + } + } +} + + +char * +baseN(val, base, len) + int val, base, len; +{ + int n, ans; + static char str[MAXBUFLEN]; + char *pstr = str; + int index; + + for (index = len - 1; index >= 0; index--) + { + n = val % base; + val /= base; + if (n < 10) + { + n += '0'; + } + else if (n < 36) + { + n += 'A' - 10; + } + else if (n < 62) + { + n += 'a' - 36; + } + else + { + n = '_'; + } + str[index] = n; + } + str[len] = '\0'; + return str; +} + +char * +hash_value(str) + char *str; +{ + int val, n; + char *ptr; + + if (*str) + ptr = str + strlen(str) - 1; + else + ptr = str; + val = 0; + while (ptr >= str) + { + n = *ptr; + val = (val + n * 0x100) ^ n; + ptr--; + } + return baseN(val, 64, 3); +} + +/* process_cancel() save_outgoing() hash_value(); baseN(); ascii_date(); */ + + +read_outgoing(sover) + soverview_t *sover; +{ + char *board, *filename, *group, *from, *subject, *outgoingtype, *msgid, *path; + char *buffer, *bufferp; + FILE *ECHOMAIL, *FN; + char *hash; + char times[MAXBUFLEN]; + time_t mtime; + + board = sover->board; + filename = sover->filename; + group = sover->group; + mtime = sover->mtime; + from = sover->from; + subject = sover->subject; + outgoingtype = sover->outgoingtype; + msgid = sover->msgid; + path = sover->path; + if (Verbose) + { + printf("<read_outgoing> %s:%s:%s\n", board, filename, group); + printf(" => %ld:%s\n", mtime, from); + printf(" => %s\n", subject); + printf(" => %s:%s\n", outgoingtype, msgid); + printf(" => %s\n", path); + } + if (NEWSFEED == LOCAL) + { + char *end = strrchr(filename, '.'); + + if (end) + *end = '\0'; + strncpy(times, baseN(atol(filename + 2), 48, 6), sizeof times); + if (end) + *end = '.'; + hash = hash_value(fileglue("%s.%s", filename, board)); + sprintf(MSGID_BUF, "%s$%s@%s", times, hash, MYADDR); + } + else + { + strncpy(MSGID_BUF, msgid, sizeof MSGID_BUF); + } + sover->msgid = MSGID; + if (mtime == -1) + { + static char BODY_BUF[MAXBUFLEN]; + + strncpy(BODY_BUF, fileglue("%s\r\n", subject), sizeof BODY_BUF); + BODY = BODY_BUF; + sprintf(SUBJECT_BUF, "cmsg cancel <%s>", MSGID); + SUBJECT = SUBJECT_BUF; + sprintf(CONTROL_BUF, "cancel <%s>", MSGID); + CONTROL = CONTROL_BUF; + strncpy(MSGID_BUF, fileglue("%d.%s", getpid(), MSGID_BUF), sizeof MSGID_BUF); + sprintf(DATE_BUF, "%s", ascii_date(time(NULL))); + DATE = DATE_BUF; + sover->subject = SUBJECT; + sover->control = CONTROL; + sover->msgid = MSGID; + sover->date = DATE; + } + else + { + sover->control = CONTROL; + sover->date = DATE_BUF; + DATE = DATE_BUF; + *CONTROL = '\0'; + sprintf(DATE, "%s", ascii_date((mtime))); + if (NEWSFEED == LOCAL && !NoAction) + { + SITE = MYSITE; + PATH = MYBBSID; + GROUPS = group; + +#ifndef MapleBBS + echomaillog(); +#endif + } + BODY = ""; + FD = open(fileglue("%s/boards/%s/%s", BBSHOME, board, filename), O_RDONLY); + if (FD < 0) + { + if (Verbose) + printf(" !! can't open %s/boards/%s/%s\n", BBSHOME, board, filename); + else + fprintf(stderr, "can't open %s/boards/%s/%s\n", BBSHOME, board, filename); + return -1; + } + + FD_SIZE = filesize(fileglue("%s/boards/%s/%s", BBSHOME, board, filename)); + if (FD_BUF == NULL) + { + FD_BUF = (char *) mymalloc(FD_SIZE + 1 + strlen(COMMENT)); + } + else + { + FD_BUF = (char *) myrealloc(FD_BUF, FD_SIZE + 1 + strlen(COMMENT)); + } + FD_END = FD_BUF + FD_SIZE; + *FD_END = '\0'; + read(FD, FD_BUF, FD_SIZE); + sprintf(FD_END, "%s", COMMENT); + FD_SIZE += strlen(COMMENT); + FD_END += strlen(COMMENT); + if (Verbose) + { + printf("<read in> %s/boards/%s/%s\n", BBSHOME, board, filename); + } + + *ORGANIZATION = '\0'; + *NEWSCONTROL = '\0'; + *NEWSAPPROVED = '\0'; + *NNTPHOST_BUF = '\0'; + NNTPHOST = NULL; + + for (buffer = FD_BUF, bufferp = strchr(buffer, '\n'); + buffer && *buffer; bufferp = strchr(buffer, '\n')) + { + if (bufferp) + *bufferp = '\0'; + if (*buffer == '\0') + { + break; + } + /* printf("get buffer %s\n", buffer); */ + if (NEWSFEED == REMOTE) + { + if (strncmp(buffer, "Date: ", 11) == 0) + { + strcpy(DATE_BUF, buffer + 11); + DATE = DATE_BUF; + } + else if (strncmp(buffer, "發信站: ", 8) == 0) + { + char *m, *n; + + m = strchr(buffer, '('); + n = strrchr(buffer, ')'); + if (m && n) + { + strncpy(DATE_BUF, m + 1, n - m - 1); + DATE_BUF[n - m - 1] = '\0'; + DATE = DATE_BUF; + strncpy(ORGANIZATION, buffer + 8, m - 8 - buffer - 1); + ORGANIZATION[m - 8 - buffer - 1] = '\0'; + } + } + else if (strncmp(buffer, "Control: ", 9) == 0) + { + strcpy(NEWSCONTROL, buffer + 9); + } + else if (strncmp(buffer, "Approved: ", 10) == 0) + { + strcpy(NEWSAPPROVED, buffer + 10); + } + else if (strncmp(buffer, "Origin: ", 8) == 0) + { + strcpy(NNTPHOST_BUF, buffer + 8); + NNTPHOST = NNTPHOST_BUF; + } + } + if (bufferp) + { + *bufferp = '\n'; + buffer = bufferp + 1; + } + else + { + break; + } + } + if (bufferp) + { + BODY = bufferp + 1; + } + else + BODY = ""; + if (bufferp) + for (buffer = bufferp + 1, bufferp = strchr(buffer, '\n'); + buffer && *buffer; bufferp = strchr(buffer, '\n')) + { + if (bufferp) + *bufferp = '\0'; + /* printf("get line (%s)\n", buffer); */ + /* if( strcmp(buffer,".")==0 ) { buffer[1]='.'; buffer[2]='\0'; } */ + if (NEWSFEED == REMOTE && + strncmp(NEWSCONTROL, "cancel", 5) == 0 && + strncmp(buffer, "------------------", 18) == 0) + { + break; + } + /* $BODY[ @BODY ] = "$_\r\n"; */ + if (bufferp) + { + *bufferp = '\n'; + buffer = bufferp + 1; + } + else + { + break; + } + } + /* # fprintf("BODY @BODY\n"; */ + close(FD); + } + return 0; +} + +#ifdef TEST +#endif + +openfeed(node) + nodelist_t *node; +{ + if (node->feedfp == NULL) + { + node->feedfp = fopen(fileglue("%s/%s.link", INNDHOME, node->node), "a"); + } +} + +queuefeed(node, textline) + nodelist_t *node; + char *textline; +{ + openfeed(node); + if (node->feedfp != NULL) + { + flock(fileno(node->feedfp), LOCK_EX); + fprintf(node->feedfp, "%s", textline); + fflush(node->feedfp); + flock(fileno(node->feedfp), LOCK_UN); + } +} + +post_article(node, site, sover, textline) + nodelist_t *node; + char *site; + soverview_t *sover; + char *textline; +{ + int status; + char *filename = sover->filename; + char *msgid = sover->msgid; + char *board = sover->board; + char *bodyp, *body; + + if (Verbose) + fprintf(stdout, "<post_article> %s %s %s\n", site, filename, msgid); + if (NoAction && Verbose) + { + printf(" ==>%s\n", sover->path); + printf(" ==>%s:%s\n", sover->from, sover->group); + printf(" ==>%s:%s\n", sover->subject, sover->date); + body = BODY; + bodyp = strchr(body, '\n'); + if (bodyp) + *bodyp = '\0'; + printf(" ==>%s\n", body); + if (bodyp) + *bodyp = '\n'; + if (bodyp) + { + body = bodyp + 1; + bodyp = strchr(body, '\n'); + if (bodyp) + *bodyp = '\0'; + printf(" ==>%s\n", body); + if (bodyp) + *bodyp = '\n'; + } + } + if (NoAction) + return 1; + if (NEWSFEED == REMOTE) + { + fprintf(NNTPwfp, "Path: %s\r\n", sover->path); + fprintf(NNTPwfp, "From: %s\r\n", sover->from); + fprintf(NNTPwfp, "Newsgroups: %s\r\n", sover->group); + fprintf(NNTPwfp, "Subject: %s\r\n", sover->subject); + /* # fprintf( NNTPwfp,"Post with subject ($subject)\n"); */ + fprintf(NNTPwfp, "Date: %s\r\n", sover->date); + if (*ORGANIZATION) + fprintf(NNTPwfp, "Organization: %s\r\n", ORGANIZATION); + fprintf(NNTPwfp, "Message-ID: <%s>\r\n", sover->msgid); + if (*NEWSCONTROL) + fprintf(NNTPwfp, "Control: %s\r\n", NEWSCONTROL); + if (*NEWSAPPROVED) + fprintf(NNTPwfp, "Approved: %s\r\n", NEWSAPPROVED); + } + else + { + fprintf(NNTPwfp, "Path: %s\r\n", MYBBSID); + fprintf(NNTPwfp, "From: %s\r\n", sover->from); + fprintf(NNTPwfp, "Newsgroups: %s\r\n", sover->group); + fprintf(NNTPwfp, "Subject: %s\r\n", sover->subject); + fprintf(NNTPwfp, "Date: %s\r\n", sover->date); + fprintf(NNTPwfp, "Organization: %s\r\n", MYSITE); + fprintf(NNTPwfp, "Message-ID: <%s>\r\n", sover->msgid); + fprintf(NNTPwfp, "X-Filename: %s/%s\r\n", sover->board, sover->filename); + } + if (NNTPHOST && *NNTPHOST && USEIHAVE) + fprintf(NNTPwfp, "NNTP-Posting-Host: %s\r\n", NNTPHOST); + else if (NNTPHOST && *NNTPHOST) + fprintf(NNTPwfp, "X-Auth-From: %s\r\n", NNTPHOST); + if (*CONTROL) + { + fprintf(NNTPwfp, "Control: %s\r\n", CONTROL); + } + fputs("\r\n", NNTPwfp); + for (body = BODY, bodyp = strchr(body, '\n'); + body && *body; bodyp = strchr(body, '\n')) + { + if (bodyp) + *bodyp = '\0'; + + fputs(body, NNTPwfp); + if (body[0] == '.' && body[1] == '\0') + fputs(".", NNTPwfp); + fputs("\r\n", NNTPwfp); + if (bodyp) + { + *bodyp = '\n'; + body = bodyp + 1; + } + else + { + break; + } + } + /* print "send out @BODY\n"; */ + status = tcpcommand("."); + /* 435 duplicated article 437 invalid header */ + + if (USEIHAVE) + { + if (status == 235) + { + if (NEWSFEED == LOCAL) + { + bbslog("Sendout <%s> from %s/%s\n", msgid, board, filename); + } + } + else if (status == 437 || status == 435) + { + bbslog("<bbslink> :Warn: %d %s <%s>\n", status, (char *) tcpmessage(), msgid); + if (Verbose) + printf(":Warn: %d %s <%s>\n", status, (char *) tcpmessage(), msgid); + return 0; + } + else + { + bbslog("<bbslink> :Err: %d %s of <%s>\n", status, (char *) tcpmessage(), msgid); + if (Verbose) + printf(":Err: %d %s of <%s>\n", status, (char *) tcpmessage(), msgid); + queuefeed(node, textline); + return 0; + } + } + else if (USEPOST) + { + if (status == 240) + { + bbslog("Sendout <%s> from %s/%s\n", msgid, board, filename); + } + else + { + bbslog("<bbslink> :Err: %d %s of <%s>\n", status, (char *) tcpmessage(), msgid); + queuefeed(node, textline); + return 0; + } + } + else + { + if (status == 250) + { + bbslog("<bbslink> DATA Sendout <%s> from %s/%s\n", msgid, board, filename); + if (Verbose) + printf("<DATA Sendout> <%s> from %s/%s\n", msgid, board, filename); + } + else + { + bbslog("<bbslink> :Err: %d %s of <%s>\n", status, (char *) tcpmessage(), msgid); + if (Verbose) + printf(":Err: %d %s of <%s>\n", status, (char *) tcpmessage(), msgid); + queuefeed(node, textline); + return 0; + } + } + return 1; +} + +process_cancel(board, filename, userid, nickname, subject) + char *board, *filename, *userid, *nickname, *subject; +{ + time_t mtime; + soverview_t sover; + + if (!userid || !*userid) + { + return; + } + mtime = -1; + strncpy(FROM_BUF, fileglue("%s.bbs@%s (%s)", userid, MYADDR, nickname), sizeof FROM_BUF); + FROM = FROM_BUF; + sover.from = FROM; + sover.board = board; + sover.subject = subject; + PATH = MYBBSID; + sover.path = MYBBSID; + /* save_outgoing(&sover, filename, userid, poster, -1); */ + save_outgoing(&sover, filename, userid, userid, -1); +} + +open_link(hostname, hostprot, hostport) + char *hostname, *hostprot, *hostport; +{ + USEIHAVE = 1; + USEPOST = 0; + USEDATA = 0; + FEEDTYPE = ' '; + if (Verbose) + printf("<OPEN_link> %s %s %s\n", hostname, hostprot, hostport); + if (strncasecmp(hostprot, "IHAVE", 5) != 0) + { + USEIHAVE = 0; + USEPOST = 1; + if (strncasecmp(hostprot, "POST", 4) == 0) + { + USEPOST = 1; + } + else if (strncasecmp(hostprot, "DATA", 4) == 0) + { + USEPOST = 0; + USEDATA = 1; + } + } + + FEEDTYPE = hostname[0]; + if (!USEDATA) + { + char *atsign; + + if (FEEDTYPE == '-' || FEEDTYPE == '+') + { + hostname = hostname + 1; + } + atsign = strchr(hostname, '@'); + if (atsign != NULL) + { + hostname = atsign + 1; + } + if (!NoAction) + { + if (Verbose) + printf("<inetclient> %s %s\n", hostname, hostport); + if ((NNTP = inetclient(hostname, hostport, "tcp")) < 0) + { + bbslog("<bbslink> :Err: server %s %s error: cant connect\n", hostname, hostport); + if (Verbose) + printf(":Err: server %s %s error: cant connect\n", hostname, hostport); + return 0; + /* exit( 0 ); */ + /* return; */ + } + NNTPrfp = fdopen(NNTP, "r"); + NNTPwfp = fdopen(NNTP, "w"); + fgets(NNTPbuffer, sizeof NNTPbuffer, NNTPrfp); + if (atoi(NNTPbuffer) != 200) + { + bbslog("<bbslink> :Err: server error: %s", NNTPbuffer); + if (Verbose) + printf(":Err: server error: %s", NNTPbuffer); + return 0; + /* exit( 0 ); */ + } + } + else + { + if (Verbose) + printf("<inetclient> %s %s\n", hostname, hostport); + } + } + else + { + if (!NoAction) + { + if (Verbose) + printf("<inetclient> localhost %s\n", hostport); + if ((NNTP = inetclient("localhost", hostport, "tcp")) < 0) + { + bbslog("<bbslink> :Err: server %s port %s error: cant connect\n", hostname, hostport); + if (Verbose) + printf(":Err: server error: cant connect"); + return 0; + /* exit( 0 ); */ + /* return; */ + } + NNTPrfp = fdopen(NNTP, "r"); + NNTPwfp = fdopen(NNTP, "w"); + fgets(NNTPbuffer, sizeof NNTPbuffer, NNTPrfp); + if (strncmp(NNTPbuffer, "220", 3) != 0) + { + bbslog("<bbslink> :Err: server error: %s", NNTPbuffer); + if (Verbose) + printf(":Err: server error: %s", NNTPbuffer); + return 0; + /* exit( 0 ); */ + } + if (strncmp(NNTPbuffer, "220-", 4) == 0) + { + fgets(NNTPbuffer, sizeof NNTPbuffer, NNTPrfp); + } + } + else + { + if (Verbose) + printf("<inetclient> %s %s\n", hostname, hostport); + } + } + return 1; +} + +send_outgoing(node, site, hostname, sover, textline) + nodelist_t *node; + soverview_t *sover; + char *hostname, *site; + char *textline; +{ + int status; + char *board, *filepath, *msgid; + int returnstatus = 0; + + board = sover->board; + filepath = sover->filename; + msgid = sover->msgid; + + if (Verbose) + printf("<send_outgoing> %s:%s:%s:%s\n", site, board, filepath, msgid); + if (BODY != NULL && !NoAction) + { + if (USEIHAVE) + { + status = tcpcommand("IHAVE <%s>", msgid); + if (status == 335) + { + returnstatus = post_article(node, site, sover, textline); + } + else if (status == 435) + { + bbslog("<bbslink> :Warn: %d %s, IHAVE <%s>\n", status, (char *) tcpmessage(), msgid); + if (Verbose) + printf(":Warn: %d %s, IHAVE <%s>\n", status, (char *) tcpmessage(), msgid); + returnstatus = 0; + } + else + { + bbslog("<bbslink> :Err: %d %s, IHAVE <%s>\n", status, (char *) tcpmessage(), msgid); + if (Verbose) + printf(":Err: %d %s, IHAVE <%s>\n", status, (char *) tcpmessage(), msgid); + queuefeed(node, textline); + returnstatus = 0; + } + } + else if (USEPOST) + { + tcpcommand("MODE READER"); + status = tcpcommand("POST"); + if (status == 340) + { + returnstatus = post_article(node, site, sover, textline); + } + else if (status == 441) + { + bbslog("<bbslink> :Warn: %d %s, POST <%s>\n", status, (char *) tcpmessage(), msgid); + if (Verbose) + printf(":Warn: %d %s, POST <%s>\n", status, (char *) tcpmessage(), msgid); + returnstatus = 0; + } + else + { + bbslog("<bbslink> :Err: %d %s, POST <%s>\n", status, (char *) tcpmessage(), msgid); + if (Verbose) + printf(":Err: %d %s, POST <%s>\n", status, (char *) tcpmessage(), msgid); + queuefeed(node, textline); + returnstatus = 0; + } + } + else + { + tcpcommand("HELO"); + tcpcommand("MAIL FROM: bbs"); + tcpcommand("RCPT TO: %s", hostname); + status = tcpcommand("DATA"); + if (status == 354) + { + returnstatus = post_article(node, site, sover, textline); + } + else + { + bbslog("<bbslink> :Err: %d %s, DATA <%s>\n", status, (char *) tcpmessage(), msgid); + if (Verbose) + printf(":Err: %d %s, DATA <%s>\n", status, (char *) tcpmessage(), msgid); + + queuefeed(node, textline); + returnstatus = 0; + } + } + } + else if (NoAction) + { + returnstatus = post_article(node, site, sover, textline); + } + return returnstatus; +} + +save_nntplink(node, overview) + nodelist_t *node; + char *overview; +{ + FILE *POSTS; + char buffer[1024]; + + openfeed(node); + POSTS = fopen(overview, "r"); + if (POSTS == NULL) + return 0; + openfeed(node); + /* if (node->feedfp == NULL) return 0; */ + flock(fileno(node->feedfp), LOCK_EX); + while (fgets(buffer, sizeof buffer, POSTS) != NULL) + { + fputs(buffer, node->feedfp); + fflush(node->feedfp); + } + flock(fileno(node->feedfp), LOCK_UN); + fclose(POSTS); + if (Verbose) + printf("<Unlinking> %s\n", overview); + if (!NoAction) + unlink(overview); + return 1; +} + + +char * +get_tmpfile(tmpfile) + char *tmpfile; +{ + FILE *FN; + static char result[256]; + + FN = fopen(tmpfile, "r"); + fgets(result, sizeof result, FN); + fclose(FN); + unlink(tmpfile); + return (result); +} + +/* cancel moderating posts */ + +cancel_outgoing(board, filename, from, subject) + char *board, *filename, *from, *subject; +{ + char *base, filepath[MAXPATHLEN]; + FILE *FN; + char *result; + char TMPFILE[MAXPATHLEN]; + + if (Verbose) + { + printf("<cancel_outgoing> %s %s %s %s\n", board, filename, from, subject); + } + + sprintf(TMPFILE, "/tmp/cancel_outgoing.%d.%d", getuid(), getpid()); + + bbslog("<cancel_outgoing> Try to move moderated post from %s to deleted\n", board); + if (Verbose) + printf("Try to move moderated post from %s to deleted\n", board); + FN = popen(fileglue("%s/bbspost post %s/boards/deleted > %s", + INNDHOME, BBSHOME, TMPFILE), "w"); + if (FN == NULL) + { + bbslog("<cancel_outgoing> can't run %s/bbspost\n", INNDHOME); + if (Verbose) + printf("<cancel_outgoing> can't run %s/bbspost\n", INNDHOME); + return 0; + } + fprintf(FN, "%s\n", from); + fprintf(FN, "%s\n", subject); + fprintf(FN, "發信人: %s, 信區: %s\n", from, board); + fprintf(FN, "標 題: %s\n", subject); + fprintf(FN, "發信站: %s (%s)\n", MYSITE, DATE); + fprintf(FN, "轉信站: %s\n", MYBBSID); + fputs("\n", FN); + fputs(BODY, FN); + pclose(FN); + result = (char *) get_tmpfile(TMPFILE); + if (strncmp(result, "post to ", 8) == 0) + { + /* try to remove it */ + strncpy(filepath, fileglue("%s/boards/%s/%s", BBSHOME, board, filename), sizeof filepath); + if (isfile(filepath)) + { + Rename(filepath, fileglue("%s.cancel", filepath)); + } + FN = fopen(filepath, "w"); + + fprintf(FN, "發信人: %s, 信區: %s\n", from, board); + fprintf(FN, "標 題: <article cancelled and mailed to the moderator\n"); + fprintf(FN, "發信站: %s (%s)\n", MYSITE, DATE); + fprintf(FN, "轉信站: %s\n", MYBBSID); + fprintf(FN, "\n"); + fputs("\n", FN); + fprintf(FN, "你的文章 \"%s\" 已經送往審核中. 請等待回覆.\n", subject); + fputs("\n", FN); + fputs("Your post has been sent to the moderator and move\n", FN); + fputs("into the deleted board. If the post accepted by the moderator,\n", FN); + fputs("it will be posted in this board again. Please wait.\n", FN); + + } + else + { + bbslog("%s", result); + } + + bbslog("%s/bbspost cancel %s %s %s moderate\n", INNDHOME, BBSHOME, board, filename); + if (Verbose) + printf("%s/bbspost cancel %s %s %s moderate\n", INNDHOME, BBSHOME, board, filename); + system(fileglue("%s/bbspost cancel %s %s %s moderate", + INNDHOME, BBSHOME, board, filename)); + return 1; +} + +/* + * send_nntplink open_link read_outgoing send_outgoing post_article + * cancel_outgoing + */ +send_nntplink(node, site, hostname, hostprot, hostport, overview, nlcount) + nodelist_t *node; + char *site, *hostname, *hostprot, *hostport, *overview; + int nlcount; +{ + FILE *POSTS; + char textline[1024]; + char baktextline[1024]; + char *filepath; + int status; + + if (Verbose) + { + printf("<send nntplink> %s %s %s %s\n", site, hostname, hostprot, hostport); + printf(" ==> %s\n", overview); + } + if (!open_link(hostname, hostprot, hostport)) + { + save_nntplink(node, overview); + return 0; + } + POSTS = fopen(overview, "r"); + if (POSTS == NULL) + { + if (Verbose) + printf("open %s failed\n", overview); + return 0; + } + while (fgets(textline, sizeof textline, POSTS) != NULL) + { + char *linebreak = strchr(textline, '\n'); + char *ptr; + char *board, *filename, *subject, *group, *mtime, *from; + char *outgoingtype; + char *msgid, *path; + soverview_t soverview; + + strcpy(baktextline, textline); + if (linebreak) + *linebreak = '\0'; + + board = "", filename = "", mtime = "", group = "", from = "", subject = ""; + outgoingtype = "", msgid = "", path = ""; + /* get board field */ + board = textline; + ptr = strchr(textline, '\t'); + if (ptr == NULL) + continue; + *ptr++ = '\0'; + + /* filename field */ + filename = ptr; + ptr = strchr(ptr, '\t'); + if (ptr == NULL) + continue; + *ptr++ = '\0'; + + /* group field */ + group = ptr; + ptr = strchr(ptr, '\t'); + if (ptr == NULL) + continue; + *ptr++ = '\0'; + + /* mtime field */ + mtime = ptr; + ptr = strchr(ptr, '\t'); + if (ptr == NULL) + continue; + *ptr++ = '\0'; + + /* from field */ + from = ptr; + ptr = strchr(ptr, '\t'); + if (ptr == NULL) + continue; + *ptr++ = '\0'; + + /* subject */ + subject = ptr; + ptr = strchr(ptr, '\t'); + if (ptr == NULL) + goto try_read_outgoing; + *ptr++ = '\0'; + + /* outgoing type field */ + outgoingtype = ptr; + ptr = strchr(ptr, '\t'); + if (ptr == NULL) + goto try_read_outgoing; + *ptr++ = '\0'; + + /* msgid */ + msgid = ptr; + ptr = strchr(ptr, '\t'); + if (ptr == NULL) + goto try_read_outgoing; + *ptr++ = '\0'; + + /* path */ + path = ptr; + ptr = strchr(ptr, '\t'); + if (ptr == NULL) + goto try_read_outgoing; + +try_read_outgoing: + + NEWSFEED = LOCAL; + if (outgoingtype && msgid && path && *outgoingtype && *msgid && *path) + { + char *left, *right; + + NEWSFEED = REMOTE; + left = strchr(msgid, '<'); + right = strrchr(msgid, '>'); + if (left) + msgid = left + 1; + if (right) + *right = '\0'; + } + soverview.board = board; + soverview.filename = filename; + soverview.group = group; + soverview.mtime = atol(mtime); + soverview.from = from; + soverview.subject = subject; + soverview.outgoingtype = outgoingtype; + soverview.msgid = msgid; + soverview.path = path; + if (read_outgoing(&soverview) == 0) + { + int sendresult = send_outgoing(node, site, hostname, &soverview, baktextline); + int sendfailed = 1 - sendresult; + + if (NEWSFEED == REMOTE) + { + BBSLINK_STAT[nlcount].remotesendout += sendresult; + BBSLINK_STAT[nlcount].remotefailed += sendfailed; + } + else + { + BBSLINK_STAT[nlcount].localsendout += sendresult; + BBSLINK_STAT[nlcount].localfailed += sendfailed; + } + if (node->feedtype == '-') + { + if (!NoAction && sendresult) + cancel_outgoing(board, filename, from, subject); + } + } + } + fclose(POSTS); + close_link(); + if (Verbose) + printf("<Unlinking> %s\n", overview); + if (!NoAction) + unlink(overview); +} + +close_link() +{ + int status; + + if (Verbose) + printf("<close_link>\n"); + if (NoAction) + return; + status = tcpcommand("QUIT"); + if (status != 205 && status != 221) + { + bbslog("<bbslink> :Err: Cannot quit message '%d %s'\n", status, (char *) tcpmessage()); + if (Verbose) + printf(":Err: Cannot quit message '%d %s'\n", status, (char *) tcpmessage()); + } + fclose(NNTPwfp); + fclose(NNTPrfp); + close(NNTP); +} + + +/* + * send_article() send_nntplink() read_outgoing() + * + */ + +send_article() +{ + char *site, *addr, *protocol, *port, *op; + char *nntphost; + int nlcount; + + chdir(INNDHOME); + + for (nlcount = 0; nlcount < NLCOUNT; nlcount++) + { + nodelist_t *node; + char linkfile[MAXPATHLEN]; + char sendfile[MAXPATHLEN]; + char feedfile[MAXPATHLEN]; + char feedingfile[MAXPATHLEN]; + char protocol[MAXBUFLEN], port[MAXBUFLEN]; + + node = NODELIST + nlcount; + site = node->node; + nntphost = node->host; + op = node->protocol; + + if (DefaultFeedSite && *DefaultFeedSite) + { + if (strcmp(node->node, DefaultFeedSite) != 0) + continue; + } + + if (op && (strncasecmp(op, "ihave", 5) == 0 || + strncasecmp(op, "post", 4) == 0 || + strncasecmp(op, "data", 4) == 0)) + { + char *left, *right; + + left = strchr(op, '('), right = strrchr(op, ')'); + if (left && right) + { + *left = '\0'; + *right = '\0'; + strncpy(protocol, op, sizeof protocol); + strncpy(port, left + 1, sizeof port); + *left = '('; + *right = ')'; + } + else + { + strncpy(protocol, op, sizeof protocol); + strncpy(port, "nntp", sizeof port); + } + } + else + { + strcpy(protocol, "IHAVE"); + strcpy(port, "nntp"); + } + sprintf(linkfile, "%s.link", site); + sprintf(sendfile, "%s.sending", site); + sprintf(feedfile, "%s.feed", site); + sprintf(feedingfile, "%s.feeding", site); + if (isfile(sendfile) && !iszerofile(sendfile)) + { + if (bbslink_get_lock(sendfile)) + { + send_nntplink(node, site, nntphost, protocol, port, sendfile, nlcount); + bbslink_un_lock(sendfile); + } + } + if (isfile(linkfile) && !iszerofile(linkfile)) + { + if (!NoAction) + { + if (bbslink_get_lock(sendfile) && bbslink_get_lock(linkfile) && + bbslink_get_lock(feedingfile)) + { + if (isfile(sendfile) && !iszerofile(sendfile)) + { + save_nntplink(node, sendfile); + } + if (node->feedfp) + { + fclose(node->feedfp); + node->feedfp = NULL; + } + Rename(linkfile, sendfile); + send_nntplink(node, site, nntphost, protocol, port, sendfile, nlcount); + bbslink_un_lock(linkfile); + bbslink_un_lock(sendfile); + bbslink_un_lock(feedingfile); + } + } + else + { + send_nntplink(node, site, nntphost, protocol, port, linkfile, nlcount); + } + } + if (isfile(feedingfile) && !iszerofile(feedingfile)) + { + if (bbslink_get_lock(feedingfile)) + { + send_nntplink(node, site, nntphost, protocol, port, feedingfile, nlcount); + bbslink_un_lock(feedingfile); + } + } + if (isfile(feedfile) && !iszerofile(feedfile)) + { + if (!NoAction) + { + if (bbslink_get_lock(feedfile) && bbslink_get_lock(feedingfile)) + { + if (isfile(feedingfile) && !iszerofile(feedingfile)) + { + save_nntplink(node, feedingfile); + if (node->feedfp) + { + fclose(node->feedfp); + node->feedfp = NULL; + } + } + Rename(feedfile, feedingfile); + system(fileglue("%s/ctlinnbbsd reload > /dev/null", INNDHOME)); + send_nntplink(node, site, nntphost, protocol, port, feedingfile, nlcount); + bbslink_un_lock(feedfile); + bbslink_un_lock(feedingfile); + } + } + else + { + send_nntplink(node, site, nntphost, protocol, port, feedfile, nlcount); + } + } + } +} + +/* bntplink() bbspost() process_article() process_cancel() send_article() */ + + +show_usage(argv) + char *argv; +{ + fprintf(stderr, "%s initialization failed or improper options !!\n", argv); +/* +woju +*/ + fprintf(stderr, "Usage: %s [options] bbs_home board\n", argv); + fprintf(stderr, " -v (show transmission status)\n"); + fprintf(stderr, " -n (dont send out articles and leave queue untouched)\n"); + fprintf(stderr, " -s site (only process articles sent to site)\n"); + fprintf(stderr, " -V (visit only: bbspost visit)\n"); + fprintf(stderr, " -N (no visit, and only process batch queue)\n"); + fprintf(stderr, " -k (kill the former bbslink process before started)\n\n"); + fprintf(stderr, "本程式要正常執行必須將以下檔案置於 %s/innd 下:\n", BBSHOME); + fprintf(stderr, "bbsname.bbs 設定貴站的 BBS ID (請儘量簡短)\n"); + fprintf(stderr, "nodelist.bbs 設定網路各 BBS 站的 ID, Address 和 fullname\n"); + fprintf(stderr, "newsfeeds.bbs 設定網路信件的 newsgroup board nodelist ...\n"); +} + + +/* +woju +*/ +struct fileheader +{ + char filename[33]; /* M.9876543210.A */ + char savemode; /* file save mode */ + char owner[12 + 2]; /* uid[.] */ + char date[6]; /* [02/02] or space(5) */ + char title[72+ 1]; + unsigned char filemode; /* must be last field @ boards.c */ +}; +typedef struct fileheader fileheader; + + +bntplink(argc, argv) + int argc; + char **argv; +{ +/* +woju + static char *OUTING = ".outing"; +*/ + static char OUTING[MAXPATHLEN]; + fileheader fhdr; + char *fname, *s1, *s2; + char nick[100]; + FILE* fp; + + nodelist_t *nl; + int linkport; + char result[4096]; + char cancelfile[MAXPATHLEN], cancelpost[MAXPATHLEN]; + char bbslink_lockfile[MAXPATHLEN]; + FILE *NEWPOST; + char *left, *right; + int nlcount; + + strcpy(BBSHOME, argv[0]); +/* +woju +*/ + sprintf(OUTING, "%s/boards/%s/", BBSHOME, argv[1]); + fname = OUTING + strlen(OUTING); + + if (initial_bbs("link") == 0) + { + return -1; + } + + BBSLINK_STAT = (stat_t *) malloc(sizeof(stat_t) * (NLCOUNT + 1)); + for (nlcount = 0; nlcount < NLCOUNT; nlcount++) + { + BBSLINK_STAT[nlcount].localsendout = 0; + BBSLINK_STAT[nlcount].remotesendout = 0; + BBSLINK_STAT[nlcount].localfailed = 0; + BBSLINK_STAT[nlcount].remotefailed = 0; + } + + nl = (nodelist_t *) search_nodelist_bynode(MYBBSID); + if (nl == NULL) + { + *MYADDR = '\0'; + *MYSITE = '\0'; + *LINKPROTOCOL = '\0'; + } + else + { + strncpy(MYADDR, nl->host, sizeof MYADDR); + strncpy(LINKPROTOCOL, nl->protocol, sizeof LINKPROTOCOL); + strncpy(MYSITE, nl->comments, sizeof MYSITE); + } + if (Verbose) + { + printf("MYADDR: %s\n", MYADDR); + printf("MYSITE: %s\n", MYSITE); + } + left = strchr(nl->protocol, '('); + right = strrchr(nl->protocol, ')'); + if (left && right) + { + *right = '\0'; + strncpy(LINKPROTOCOL, nl->protocol, sizeof LINKPROTOCOL); + LINKPORT = atoi(left + 1); + *right = ')'; + } + + if (!NoVisit) + { + sprintf(bbslink_lockfile, "%s/.bbslink.visit", INNDHOME); +/* +woju + if (!Rename(fileglue("%s/out.bntp", INNDHOME), OUTING) && bbslink_get_lock(bbslink_lockfile)) +*/ + if (strcpy(fname, ".DIR")) + { + /* When try to visit new post, try to lock it */ + NEWPOST = fopen(OUTING, "r"); + if (NEWPOST == NULL) + { + bbslog("<bbslink> Err: can't open %s\n", OUTING); +/* +woju + bbslink_un_lock(bbslink_lockfile); +*/ + return -1; + } +#ifdef 0 +woju + while (fgets(result, sizeof result, NEWPOST)) + { + /* chop( $_ ); */ + char *board, *filename, *userid, *nickname, *subject; + char *ptr; + + ptr = strchr(result, '\n'); + if (ptr) + *ptr = '\0'; + + board = filename = userid = nickname = subject = NULL; + /* board field */ + board = result; + ptr = strchr(result, '\t'); + if (ptr == NULL) + continue; + *ptr++ = '\0'; + + /* filename field */ + filename = ptr; + ptr = strchr(ptr, '\t'); + if (ptr == NULL) + continue; + *ptr++ = '\0'; + + /* userid field */ + userid = ptr; + ptr = strchr(ptr, '\t'); + if (ptr == NULL) + continue; + *ptr++ = '\0'; + + /* nickname field */ + nickname = ptr; + ptr = strchr(ptr, '\t'); + if (ptr == NULL) + continue; + *ptr++ = '\0'; + + /* subject field */ + subject = ptr; + /* ptr = strchr(ptr, '\t'); if (ptr == NULL) continue; ptr++ = '\0'; */ + + process_article(board, filename, userid, nickname, subject); + } +#endif + while (fread(&fhdr, sizeof(fhdr), 1, NEWPOST) == 1) { + nick[1] = 0; + strcpy(fname, fhdr.filename); + if (fp = fopen(OUTING, "r")) { + fgets(nick, 100, fp); + if ((s1 = strchr(nick, '(')) && (s2 = strrchr(nick, ')'))) + *s2 = 0; + else + s1 = nick; + fclose(fp); + } + printf("%s\n", fhdr.title); + process_article(argv[1], fhdr.filename, fhdr.owner, s1 + 1, fhdr.title); + } + fclose(NEWPOST); +/* +woju + unlink(OUTING); + bbslink_un_lock(bbslink_lockfile); +*/ + } /* getlock */ + } /* if NoVisit is false */ + +#if 0 +woju + sprintf(cancelpost, "%s/cancel.bntp", INNDHOME); + if (isfile(cancelpost)) + { + FILE *CANCELFILE; + + if (bbslink_get_lock(cancelpost) && bbslink_get_lock(cancelfile)) + { + sprintf(cancelfile, "%s.%d", cancelpost, getpid()); + Rename(cancelpost, cancelfile); + CANCELFILE = fopen(cancelfile, "r"); + while (fgets(result, sizeof result, CANCELFILE) != NULL) + { + /* chop( $_ ); */ + char *board, *filename, *userid, *nickname, *subject; + char *ptr, **sptr; + + ptr = strchr(result, '\n'); + if (ptr) + *ptr = '\0'; + board = filename = userid = nickname = subject = NULL; + + /* board field */ + board = result; + ptr = strchr(result, '\t'); + if (ptr == NULL) + continue; + *ptr++ = '\0'; + + /* filename field */ + filename = ptr; + ptr = strchr(ptr, '\t'); + if (ptr == NULL) + continue; + *ptr++ = '\0'; + + /* userid field */ + userid = ptr; + ptr = strchr(ptr, '\t'); + if (ptr == NULL) + continue; + *ptr++ = '\0'; + + /* nickname field */ + nickname = ptr; + ptr = strchr(ptr, '\t'); + if (ptr == NULL) + continue; + *ptr++ = '\0'; + + /* subject field */ + subject = ptr; + /* ptr = strchr(ptr, '\t'); if (ptr == NULL) continue; ptr++ = '\0'; */ + process_cancel(board, filename, userid, nickname, subject); + } + fclose(CANCELFILE); + if (Verbose) + printf("Unlinking %s\n", cancelfile); + if (!NoAction) + unlink(cancelfile); + bbslink_un_lock(cancelfile); + bbslink_un_lock(cancelpost); + } + } +#endif + + for (nlcount = 0; nlcount < NLCOUNT; nlcount++) + { + if (NODELIST[nlcount].feedfp != NULL) + { + fclose(NODELIST[nlcount].feedfp); + NODELIST[nlcount].feedfp = NULL; + } + } + + send_article(); + for (nlcount = 0; nlcount < NLCOUNT; nlcount++) + { + int localsendout, remotesendout, localfailed, remotefailed; + + localsendout = BBSLINK_STAT[nlcount].localsendout; + remotesendout = BBSLINK_STAT[nlcount].remotesendout; + localfailed = BBSLINK_STAT[nlcount].localfailed; + remotefailed = BBSLINK_STAT[nlcount].remotefailed; + if (localsendout || remotesendout || localfailed || remotefailed) + bbslog("<bbslink> [%s]%s lsend:%d rsend:%d lfail:%d rfail:%d\n", + NODELIST[nlcount].node, NoAction ? "NoAction" : "", localsendout, remotesendout, + localfailed, remotefailed); + if (NODELIST[nlcount].feedfp != NULL) + { + fclose(NODELIST[nlcount].feedfp); + NODELIST[nlcount].feedfp = NULL; + } + } + if (BBSLINK_STAT); + free(BBSLINK_STAT); + return 0; +} +/* +termbbslink(sig) + int sig; +{ + bbslog("kill signal received %d, terminated\n", sig); + if (Verbose) + printf("kill signal received %d, terminated\n", sig); + exit(0); +} +*/ +void +termbbslink() +{ + bbslog("kill signal received ??, terminated\n"); + if (Verbose) + printf("kill signal received ??, terminated\n"); + exit(0); +} + +char *REMOTEUSERNAME = ""; +char *REMOTEHOSTNAME = ""; + +extern char *optarg; +extern int opterr, optind; + +main(argc, argv) + int argc; + char **argv; +{ + int c, errflag = 0; + + CONTROL = CONTROL_BUF; + MSGID = MSGID_BUF; + + /* For debug Only */ +#define DEBUGBBSLINK + +#ifdef DEBUGBBSLINK + NoAction = 0; + Verbose = 0; + VisitOnly = 0; + NoVisit = 0; + DefaultFeedSite = ""; +#endif + + while ((c = getopt(argc, argv, "s:hnvVNk")) != -1) + switch (c) + { + case 's': + DefaultFeedSite = optarg; + break; + case 'n': + NoAction = 1; + break; + case 'v': + Verbose = 1; + break; + case 'V': + VisitOnly = 1; + break; + case 'N': + NoVisit = 1; + break; + case 'k': + KillFormerBBSLINK = 1; + break; + case 'h': + default: + errflag++; + break; + } + if (errflag > 0) + { + show_usage(argv[0]); + return (1); + } + if (argc - optind < 2) + { + show_usage(argv[0]); + exit(1); + } + signal(SIGTERM, termbbslink); + if (bntplink(argc - optind, argv + optind) != 0) + { + show_usage(argv[0]); + exit(1); + } + return 0; +} diff --git a/innbbsd/bbsnnrp.c b/innbbsd/bbsnnrp.c new file mode 100644 index 00000000..11993629 --- /dev/null +++ b/innbbsd/bbsnnrp.c @@ -0,0 +1,1187 @@ +/* + Usage: bbsnnrp [options] nntpserver activefile + -h|? (help) + -v (verbose protocol transactions) + -c (reset active files only; don't receive articles) + -r remotehost(send articles to remotehost, default=local) + -p port|(send articles to remotehost at port, default=7777) + path(send articles to local at path, default=~bbs/innd/.innbbsd) + -n (don't ask innbbsd server and stat articles) + -w seconds (wait for seconds and run infinitely, default=once) + -a max_art (maximum number of articles received for a group each time) + -s max_stat(maximum number of articles stated for a group each time) + -t stdin|nntp (default=nntp) +*/ + +#include "innbbsconf.h" +#include <sys/mman.h> +#ifndef AIX +# include <sys/fcntl.h> +#endif +#include "bbslib.h" +#include "daemon.h" +#include "nntp.h" + +#ifndef MAX_ARTS +# define MAX_ARTS 100 +#endif +#ifndef MAX_STATS +# define MAX_STATS 1000 +#endif + +#if defined(__linux) +#define NO_USE_MMAP +#else +#define USE_MMAP +#endif + +int Max_Arts= MAX_ARTS; +int Max_Stats = MAX_STATS; + +typedef struct NEWSRC_T { + char *nameptr, *lowptr, *highptr, *modeptr; + int namelen, lowlen, highlen; + ULONG low, high; + int mode, subscribe; +} newsrc_t; + +typedef struct NNRP_T { + int nnrpfd; + int innbbsfd; + FILE *nnrpin, *nnrpout; + FILE *innbbsin, *innbbsout; + char activefile[MAXPATHLEN]; + char rcfile[MAXPATHLEN]; + newsrc_t *newsrc; + char *actpointer, *actend; + int actsize, actfd, actdirty; +} nnrp_t; + +typedef struct XHDR_T { + char *header; + ULONG artno; +} xhdr_t; + +xhdr_t XHDR[MAX_ARTS]; +char LockFile[1024]; + +#define NNRPGroupOK NNTP_GROUPOK_VAL +#define NNRPXhdrOK NNTP_HEAD_FOLLOWS_VAL +#define NNRParticleOK NNTP_ARTICLE_FOLLOWS_VAL +#define INNBBSstatOK NNTP_NOTHING_FOLLOWS_VAL +#define INNBBSihaveOK NNTP_SENDIT_VAL +#define NNRPconnectOK NNTP_POSTOK_VAL +#define NNRPstatOK NNTP_NOTHING_FOLLOWS_VAL +#define INNBBSconnectOK NNTP_POSTOK_VAL + +nnrp_t BBSNNRP; + +void doterm(s) +int s; +{ + printf("bbsnnrp terminated. Signal %d\n", s); + writerc(&BBSNNRP); + if (isfile(LockFile)) + unlink(LockFile); + exit(1); +} + +extern char *optarg; +extern int opterr, optind; + +#ifndef MIN_WAIT +# define MIN_WAIT 60 +#endif + +int ResetActive = 0; +int StatHistory = 1; +int AskLocal = 1; +int RunOnce = 1; + +int DefaultWait = MIN_WAIT; + +char *DefaultPort = DefaultINNBBSPort; +char *DefaultPath = LOCALDAEMON; +char *DefaultRemoteHost; + +#ifndef MAXBUFLEN +#define MAXBUFLEN 256 +#endif +char DefaultNewsgroups[MAXBUFLEN]; +char DefaultOrganization[MAXBUFLEN]; +char DefaultModerator[MAXBUFLEN]; +char DefaultTrustfrom[MAXBUFLEN]; +char DefaultTrustFrom[MAXBUFLEN]; + +usage(arg) +char *arg; +{ +fprintf(stderr,"Usage: %s [options] nntpserver activefile\n", arg); +fprintf(stderr," -h|? (help) \n"); +fprintf(stderr," -v (verbose protocol transactions)\n"); +fprintf(stderr," -c (reset active files only; don't receive articles)\n"); +fprintf(stderr," -r [proto:]remotehost\n"); +fprintf(stderr," (send articles to remotehost, default=ihave:local)\n"); +fprintf(stderr," -p port|(send articles to remotehost at port, default=%s)\n",DefaultINNBBSPort); +fprintf(stderr," path(send articles to local at path, default=~bbs/innd/.innbbsd)\n"); +fprintf(stderr," -w seconds ( > 1 wait for seconds and run infinitely, default=once)\n"); +fprintf(stderr," -n (don't ask innbbsd server and stat articles)\n"); +fprintf(stderr," -a max_art(maximum number of articles received for a group each time)\n"); +fprintf(stderr," default=%d\n", MAX_ARTS); +fprintf(stderr," -s max_stat(maximum number of articles stated for a group each time)\n"); +fprintf(stderr," default=%d\n", MAX_STATS); +fprintf(stderr," -t stdin|nntp (default=nntp)\n"); +fprintf(stderr," -g newsgroups\n"); +fprintf(stderr," -m moderator\n"); +fprintf(stderr," -o organization\n"); +fprintf(stderr," -f trust_user (From: trust_user)\n"); +fprintf(stderr," -F trust_user (From trust_user)\n"); +fprintf(stderr," Please E-mail bug to skhuang@csie.nctu.edu.tw or\n"); +fprintf(stderr," post to tw.bbs.admin.installbbs\n"); +} + +static char *StdinInputType="stdin"; +static char *NntpInputType="nntp"; +static char *NntpIhaveProtocol="ihave"; +static char *NntpPostProtocol="post"; +static char *DefaultNntpProtocol ; +main(argc, argv) +int argc; +char **argv; +{ + char *ptr, *server, *active; + int c, errflag=0; + int lockfd; + char *inputtype; + + DefaultNntpProtocol = NntpIhaveProtocol; + *DefaultNewsgroups = '\0'; + *DefaultModerator = '\0'; + *DefaultOrganization = '\0'; + *DefaultTrustFrom = '\0'; + *DefaultTrustfrom = '\0'; + inputtype = NntpInputType; + while ((c = getopt(argc,argv,"f:F:m:o:g:w:r:p:a:s:t:h?ncv"))!= -1) + switch (c) { + case 'v': + verboseon("bbsnnrp.log"); + break; + case 'c': + ResetActive = 1; + break; + case 'g': + strncpy(DefaultNewsgroups, optarg, sizeof DefaultNewsgroups); + break; + case 'm': + strncpy(DefaultModerator, optarg, sizeof DefaultModerator); + break; + case 'o': + strncpy(DefaultOrganization, optarg, sizeof DefaultOrganization); + break; + case 'f': + strncpy(DefaultTrustfrom, optarg, sizeof DefaultTrustfrom); + break; + case 'F': + strncpy(DefaultTrustFrom, optarg, sizeof DefaultTrustFrom); + break; + case 'r': { + char *hostptr; + AskLocal = 0; + DefaultRemoteHost = optarg; + if ((hostptr = strchr(optarg,':')) != NULL) { + *hostptr = '\0'; + DefaultRemoteHost = hostptr+1; + if (strcasecmp( optarg, "post" ) == 0) + DefaultNntpProtocol = NntpPostProtocol; + *hostptr = ':'; + } + break; + } + case 'w': + RunOnce = 0; + DefaultWait = atoi(optarg); + if (DefaultWait < MIN_WAIT) + DefaultWait = MIN_WAIT; + break; + case 'p': + if (AskLocal == 0) { + DefaultPort = optarg; + } else { + DefaultPath = optarg; + } + break; + case 'n': + StatHistory = 0; + break; + case 'a': + Max_Arts = atol(optarg); + if (Max_Arts < 0) Max_Arts = 0; + break; + case 's': + Max_Stats = atol(optarg); + if (Max_Stats < 0) Max_Stats = 0; + break; + case 't': + if ( strcasecmp(optarg,StdinInputType) == 0) { + inputtype = StdinInputType; + } + break; + case 'h': + case '?': + default: + errflag ++; + } + if (errflag > 0) { + usage(argv[0]); + return(1); + } + if (inputtype == NntpInputType && argc - optind < 2) { + usage(argv[0]); + exit(1); + } + if (inputtype == NntpInputType) { + server = argv[optind]; + active = argv[optind+1]; + if ( isfile(active) ) { + strncpy(BBSNNRP.activefile, active, sizeof BBSNNRP.activefile); + } else if ( strchr(active,'/') == NULL) { + sprintf(BBSNNRP.activefile, "%s/innd/%.*s",BBSHOME, sizeof BBSNNRP.activefile - 7 - strlen(BBSHOME), active); + } else { + strncpy(BBSNNRP.activefile, active, sizeof BBSNNRP.activefile); + } + + strncpy(LockFile , (char*)fileglue("%s.lock",active), sizeof LockFile); + if ((lockfd = open(LockFile, O_RDONLY)) >= 0) { + char buf[10]; + int pid; + + if (read(lockfd, buf, sizeof buf) > 0 && (pid = atoi(buf))>0 && kill(pid,0)==0) { + fprintf(stderr, "another process [%d] running\n", pid); + exit(1); + } else { + fprintf(stderr, "no process [%d] running, but lock file existed, unlinked\n", pid); + unlink(LockFile); + } + close(lockfd); + } + if ((lockfd = open(LockFile, O_RDWR|O_CREAT|O_EXCL,0644)) < 0) { + fprintf( stderr, "maybe another %s process running\n",argv[0]); + exit(1); + } else { + char buf[10]; + int pid; + sprintf(buf,"%-.8d\n",getpid()); + write(lockfd, buf, strlen(buf)); + close(lockfd); + } + for (;;) { + if (!initial_bbs(NULL)) { + fprintf(stderr, "Initial BBS failed\n"); + exit(1); + } + initsockets(server, &BBSNNRP, inputtype); + ptr = (char*)strrchr(active,'/'); + if (ptr != NULL) + ptr++; + else + ptr = active; + sprintf(BBSNNRP.rcfile,"%s/.newsrc.%s.%s",INNDHOME, server, ptr); + initrcfiles(&BBSNNRP); + + signal(SIGTERM, doterm); + signal(SIGKILL, doterm); + signal(SIGHUP, doterm); + signal(SIGPIPE, doterm); + + readnews(&BBSNNRP); + writerc(&BBSNNRP); + closesockets(); + + if (RunOnce) break; + sleep(DefaultWait); + } + unlink(LockFile); + } /* NntpInputType */ + else { + if (!initial_bbs(NULL)) { + fprintf(stderr, "Initial BBS failed\n"); + exit(1); + } + initsockets(server, &BBSNNRP, inputtype); + signal(SIGTERM, doterm); + signal(SIGKILL, doterm); + signal(SIGHUP, doterm); + signal(SIGPIPE, doterm); + + stdinreadnews(&BBSNNRP); + closesockets(); + } /* stdin input type */ + return 0; +} + +headbegin(buffer) +char *buffer; +{ + if (strncmp(buffer,"Path: ",6)==0 ) { + if (strchr(buffer+6,'!') != NULL) + return 1; + } + if (strncmp(buffer,"From ",5)== 0 ) { + if (strchr(buffer+5,':') != NULL) + return 1; + } + return 0; +} + +stdinreadnews(bbsnnrp) +nnrp_t *bbsnnrp; +{ + int i; + char buffer[4096]; + ULONG low, high; + char tmpfilename[MAXPATHLEN]; + FILE *tmpfp = NULL; + char mid[1024]; + int pathagain; + int ngmet, submet, midmet, pathmet, orgmet, approvedmet; + int discard; + char sending_path[MAXPATHLEN]; + int sending_path_len=0; + + strncpy(tmpfilename,(char*)fileglue("/tmp/bbsnnrp-stdin-%d-%d",getuid(),getpid()),sizeof tmpfilename); + fgets(buffer, sizeof buffer, bbsnnrp->innbbsin); + verboselog("innbbsGet: %s", buffer); + if (atoi(buffer) != INNBBSconnectOK) { + fprintf(stderr, "INNBBS server not OK\n"); + return; + } + + if ( DefaultNntpProtocol == NntpPostProtocol ) { + fputs("MODE READER\r\n", bbsnnrp->innbbsout); + fflush(bbsnnrp->innbbsout); + verboselog("innbbsPut: MODE READER\n"); + fgets(buffer, sizeof buffer, bbsnnrp->innbbsin); + verboselog("innbbsGet: %s",buffer); + } + + if (StatHistory == 0) { + fputs("MIDCHECK OFF\r\n", bbsnnrp->innbbsout); + fflush(bbsnnrp->innbbsout); + verboselog("innbbsPut: MIDCHECK OFF\n"); + fgets(buffer, sizeof buffer, bbsnnrp->innbbsin); + verboselog("innbbsGet: %s",buffer); + } + tmpfp = fopen(tmpfilename,"w"); + if (tmpfp == NULL) return; + *mid = '\0'; + for (;;) { + fprintf(stderr,"Try to read from stdin ...\n"); + ngmet = 0, submet = 0, midmet = 0, pathmet = 0, orgmet = 0, approvedmet=0; + discard = 0; + while (fgets(buffer, sizeof buffer, stdin) != NULL) { + char *tmpptr; + tmpptr = strchr(buffer,'\n'); + if (tmpptr != NULL) *tmpptr = '\0'; + if (strncasecmp(buffer,"Message-ID: ",12)==0) { + strncpy(mid, buffer+12,sizeof mid); + midmet = 1; + } else if (strncmp(buffer,"Subject: ",9)==0) { + submet = 1; + } else if (strncmp(buffer,"Path: ",6)==0) { + pathmet = 1; + } else if (strncmp(buffer,"Organization: ",14)==0) { + orgmet = 1; + } else if (strncmp(buffer,"Approved: ",10) == 0) { + approvedmet = 1; + } else if (strncmp(buffer,"From: ",6)==0 && *DefaultTrustfrom ) { + if (strstr(buffer+6, DefaultTrustfrom)==NULL) { + discard = 1; + verboselog("Discard: %s for %s",buffer, DefaultTrustfrom); + } + } else if (strncmp(buffer,"From ",5)==0 && *DefaultTrustFrom ) { + if (strstr(buffer+5, DefaultTrustFrom)==NULL) { + discard = 1; + verboselog("Discard: %s for %s",buffer, DefaultTrustFrom); + } + } else if (strncmp(buffer,"Received: ",10)==0) { + char *rptr=buffer+10, *rrptr; + int savech, len; + if (strncmp(buffer+10,"from ",5)==0) { + rptr +=5; + rrptr = strchr(rptr,'('); + if (rrptr != NULL) rptr = rrptr + 1; + rrptr = strchr(rptr,' '); + savech = *rrptr; + if (rrptr != NULL) *rrptr = '\0'; + } else if (strncmp(buffer+10,"(from ",6)==0) { + rptr +=6; + rrptr = strchr(rptr,')'); + savech = *rrptr; + if (rrptr != NULL) *rrptr = '\0'; + } + len = strlen(rptr) +1; + if (*rptr && sending_path_len + len < sizeof(sending_path)) { + if (*sending_path) + strcat(sending_path, "!"); + strcat(sending_path, rptr); + sending_path_len += len ; + } + if (rrptr != NULL) *rrptr = savech; + } + + if (strncmp(buffer,"Newsgroups: ",12)==0) { + if (*DefaultNewsgroups){ + fprintf(tmpfp,"Newsgroups: %s\r\n",DefaultNewsgroups); + } else { + fprintf(tmpfp,"%s\r\n",buffer); + } + ngmet = 1; + } else { + if (buffer[0] == '\0') { + if (!ngmet && *DefaultNewsgroups) { + fprintf(tmpfp,"Newsgroups: %s\r\n",DefaultNewsgroups); + } + if (!submet) { + fprintf(tmpfp,"Subject: (no subject)\r\n"); + } + if (!pathmet) { + fprintf(tmpfp,"Path: from-mail\r\n"); + } + if (!midmet) { + static int seed; + time_t now; + time(&now); + fprintf(tmpfp,"Message-ID: <%d@%d.%d.%d>\r\n",now,getpid(), getuid(), seed); + sprintf(mid, "<%d@%d.%d.%d>", now, getpid(), getuid(), seed); + seed++; + } + if (!orgmet && *DefaultOrganization) { + fprintf(tmpfp,"Organization: %s\r\n", DefaultOrganization); + } + if (!approvedmet && *DefaultModerator) { + fprintf(tmpfp,"Approved: %s\r\n", DefaultModerator); + } + } + if (strncmp(buffer,"From ",5) != 0 && strncmp(buffer,"To: ",4) !=0) { + if (buffer[0] == '\0') { + if (*sending_path) { + fprintf(tmpfp,"X-Sending-Path: %s\r\n",sending_path); + } + } + fprintf(tmpfp,"%s\r\n",buffer); + } + } + if (buffer[0]=='\0') break; + } + fprintf(stderr,"Article Body begin ...\n"); + pathagain = 0; + while (fgets(buffer, sizeof buffer, stdin) != NULL) { + char *tmpptr; + tmpptr = strchr(buffer,'\n'); + if (tmpptr != NULL) *tmpptr = '\0'; + if ( headbegin(buffer) ) { + FILE *oldfp = bbsnnrp->nnrpin; + pathagain = 1; + fputs(".\r\n",tmpfp); + fclose(tmpfp); + fprintf(stderr,"Try to post ...\n"); + tmpfp = fopen(tmpfilename,"r"); + bbsnnrp->nnrpin = tmpfp; + if (!discard) + if (INNBBSihave(bbsnnrp, -1, mid)== -1) { + fprintf(stderr,"post failed\n"); + } + bbsnnrp->nnrpin = oldfp; + fclose(tmpfp); + *mid = '\0'; + tmpfp = fopen(tmpfilename,"w"); + fprintf(tmpfp,"%s\r\n",buffer); + break; + } else { + fprintf(tmpfp,"%s\r\n",buffer); + } + } + if (!pathagain) break; + } + if (!pathagain && tmpfp) { + FILE *oldfp = bbsnnrp->nnrpin; + fputs(".\r\n",tmpfp); + fclose(tmpfp); + fprintf(stderr,"Try to post ...\n"); + tmpfp = fopen(tmpfilename,"r"); + bbsnnrp->nnrpin = tmpfp; + if (!discard) + if (INNBBSihave(bbsnnrp, -1, mid)== -1) { + fprintf(stderr,"post failed\n"); + } + bbsnnrp->nnrpin = oldfp; + fclose(tmpfp); + } + if (isfile(tmpfilename)) { + unlink(tmpfilename); + } +} + +static char *ACT_BUF, *RC_BUF; +int ACT_COUNT; + +initrcfiles(bbsnnrp) +nnrp_t *bbsnnrp; +{ + FILE *actfp, *rcfp; + char buff[1024]; + int actfd, i, count, actcount=0, rcount=0, maxcount; + struct stat st; + char *actlistptr, *ptr; + + actfd = open(bbsnnrp->activefile, O_RDWR); + if (actfd < 0) { + fprintf( stderr, "can't read/write %s\n", bbsnnrp->activefile ); + exit(1); + } + if (fstat(actfd, &st) != 0) { + fprintf( stderr, "can't stat %s\n", bbsnnrp->activefile ); + exit(1); + } + + bbsnnrp->actfd = actfd; + bbsnnrp->actsize = st.st_size; +#ifdef USE_MMAP + bbsnnrp->actpointer = mmap(0, st.st_size, PROT_WRITE | PROT_READ, + MAP_SHARED, actfd, 0); + if (bbsnnrp->actpointer == (char*)-1) { + fprintf( stderr, "mmap error \n"); + exit(1); + } +#else + if (bbsnnrp->actpointer == NULL) { + bbsnnrp->actpointer = (char*)mymalloc(st.st_size); + } else { + bbsnnrp->actpointer = (char*)myrealloc(bbsnnrp->actpointer,st.st_size); + } + if (bbsnnrp->actpointer == NULL || read(actfd, bbsnnrp->actpointer, st.st_size) <= 0) { + fprintf( stderr, "read act error \n"); + exit(1); + } +#endif + bbsnnrp->actend = bbsnnrp->actpointer + st.st_size; + i = 0, count = 0; + for (ptr = bbsnnrp->actpointer; ptr < bbsnnrp->actend && (actlistptr = (char*)strchr(ptr,'\n')) != NULL; ptr = actlistptr +1, ACT_COUNT++) { + if (*ptr == '\n') continue; + if (*ptr == '#') continue; + count ++; + } + bbsnnrp->newsrc = (newsrc_t*) mymalloc( sizeof(newsrc_t) * count); + ACT_COUNT = 0; + for (ptr = bbsnnrp->actpointer; ptr < bbsnnrp->actend && (actlistptr = (char*)strchr(ptr,'\n')) != NULL; ptr = actlistptr +1 ) { + register newsrc_t *rcptr; + char *nptr; + /**actlistptr = '\0';*/ + if (*ptr == '\n') continue; + if (*ptr == '#') continue; + rcptr = &bbsnnrp->newsrc[ACT_COUNT]; + rcptr->nameptr = NULL; + rcptr->namelen = 0; + rcptr->lowptr = NULL; + rcptr->lowlen = 0; + rcptr->highptr = NULL; + rcptr->highlen = 0; + rcptr->modeptr = NULL; + rcptr->low = 0; + rcptr->high = 0; + rcptr->mode = 'y'; + for (nptr= ptr ;*nptr && isspace(*nptr); ) nptr++; + if ( nptr == actlistptr ) continue; + rcptr->nameptr = nptr; + for (nptr++; *nptr && !isspace(*nptr); ) nptr++; + rcptr->namelen = (int)(nptr - rcptr->nameptr); + if ( nptr == actlistptr) continue; + for (nptr++ ;*nptr && isspace(*nptr); ) nptr++; + if ( nptr == actlistptr) continue; + rcptr->highptr = nptr; + rcptr->high = atol(nptr); + for (nptr++; *nptr && !isspace(*nptr); ) nptr++; + rcptr->highlen = (int)(nptr - rcptr->highptr); + if ( nptr == actlistptr) continue; + for (nptr++ ;*nptr && isspace(*nptr); ) nptr++; + if ( nptr == actlistptr) continue; + rcptr->lowptr = nptr; + rcptr->low = atol(nptr); + for (nptr++; *nptr && !isspace(*nptr); ) nptr++; + rcptr->lowlen = (int)(nptr - rcptr->lowptr); + if ( nptr == actlistptr) continue; + for (nptr++ ;*nptr && isspace(*nptr); ) nptr++; + if ( nptr == actlistptr) continue; + rcptr->mode = *nptr; + rcptr->modeptr = nptr; + ACT_COUNT ++; + } +} + +initsockets(server, bbsnnrp, type) +char *server; +nnrp_t *bbsnnrp; +char *type; +{ + int nnrpfd ; + int innbbsfd; + if (AskLocal) { + innbbsfd = unixclient(DefaultPath,"tcp"); + if (innbbsfd < 0) { + fprintf(stderr, "Connect to %s error. You may not run innbbsd\n", LOCALDAEMON); + /* unix connect fail, may run by inetd, try to connect to local once */ + innbbsfd = inetclient("localhost",DefaultPort,"tcp"); + if (innbbsfd < 0) { + exit(2); + } + close(innbbsfd); + /* try again */ + innbbsfd = unixclient(DefaultPath,"tcp"); + if (innbbsfd < 0) { + exit(3); + } + } + verboselog("INNBBS connect to %s\n",DefaultPath); + } else { + innbbsfd = inetclient(DefaultRemoteHost,DefaultPort,"tcp"); + if (innbbsfd < 0) { + fprintf(stderr, "Connect to %s at %s error. Remote Server not Ready\n", DefaultRemoteHost, DefaultPort); + exit(2); + } + verboselog("INNBBS connect to %s\n",DefaultRemoteHost); + } + if (type == StdinInputType) { + bbsnnrp->nnrpfd = 0; + bbsnnrp->innbbsfd = innbbsfd; + if ((bbsnnrp->nnrpin = fdopen(0,"r")) == NULL || + (bbsnnrp->nnrpout= fdopen(1,"w")) == NULL || + (bbsnnrp->innbbsin= fdopen(innbbsfd,"r")) == NULL || + (bbsnnrp->innbbsout= fdopen(innbbsfd,"w"))== NULL ) { + fprintf( stderr, "fdopen error\n"); + exit(3); + } + return; + } + nnrpfd = inetclient(server, "nntp","tcp"); + if (nnrpfd < 0) { + fprintf(stderr, " connect to %s error \n", server); + exit(2); + } + verboselog("NNRP connect to %s\n", server); + bbsnnrp->nnrpfd = nnrpfd; + bbsnnrp->innbbsfd = innbbsfd; + if ((bbsnnrp->nnrpin = fdopen(nnrpfd,"r")) == NULL || + (bbsnnrp->nnrpout= fdopen(nnrpfd,"w")) == NULL || + (bbsnnrp->innbbsin= fdopen(innbbsfd,"r")) == NULL || + (bbsnnrp->innbbsout= fdopen(innbbsfd,"w"))== NULL ) { + fprintf( stderr, "fdopen error\n"); + exit(3); + } +} + +closesockets() +{ + fclose(BBSNNRP.nnrpin); + fclose(BBSNNRP.nnrpout); + fclose(BBSNNRP.innbbsin); + fclose(BBSNNRP.innbbsout); + close(BBSNNRP.nnrpfd); + close(BBSNNRP.innbbsfd); +} + +updaterc(actptr, len, value) +char *actptr; +int len; +ULONG value; +{ + for (actptr += len -1; len -- >0; ) { + *actptr-- = value % 10 + '0'; + value /= 10; + } +} + +/* + if old file is empty, don't need to update + prevent from disk full +*/ +int +myrename(old,new) +char *old, *new; +{ + struct stat st; + if (stat(old,&st) != 0) return -1; + if (st.st_size <= 0) return -1; + return rename(old,new); +} + +flushrc(bbsnnrp) +nnrp_t *bbsnnrp; +{ + int backfd; + char *bak1; + if (bbsnnrp->actdirty == 0) return; + bak1 = (char*)strdup((char*)fileglue("%s.BAK",bbsnnrp->activefile)); + if (isfile(bak1)) { + myrename(bak1, (char*)fileglue("%s.BAK.OLD",bbsnnrp->activefile)); + } +#ifdef USE_MMAP + if ((backfd=open((char*)fileglue("%s.BAK",bbsnnrp->activefile),O_WRONLY | O_TRUNC | O_CREAT, 0664)) < 0 || write(backfd, bbsnnrp->actpointer, bbsnnrp->actsize) < bbsnnrp->actsize) +#else + myrename(bbsnnrp->activefile, bak1); + if ((backfd=open(bbsnnrp->activefile,O_WRONLY | O_TRUNC | O_CREAT, 0664)) < 0 || write(backfd, bbsnnrp->actpointer, bbsnnrp->actsize) < bbsnnrp->actsize) +#endif + { + char emergent[128]; + sprintf(emergent,"/tmp/bbsnnrp.%d.active",getpid()); + fprintf(stderr, "write to backup active fail. Maybe disk full\n"); + fprintf(stderr, "try to write in %s\n",emergent); + if ((backfd = open(emergent,O_WRONLY | O_TRUNC | O_CREAT, 0644))<0 || write(backfd, bbsnnrp->actpointer, bbsnnrp->actsize) < bbsnnrp->actsize) + { + fprintf(stderr, "write to %sfail.\n", emergent); + } else { + close(backfd); + } + /* if write fail, should leave */ + /*exit(1);*/ + } else { + close(backfd); + } + free(bak1); + bbsnnrp->actdirty = 0; +} + +writerc(bbsnnrp) +nnrp_t *bbsnnrp; +{ + if (bbsnnrp->actpointer) { + flushrc(bbsnnrp); +#ifdef USE_MMAP + if (munmap(bbsnnrp->actpointer, bbsnnrp->actsize) < 0) + fprintf(stderr, "can't unmap\n"); + /*free(bbsnnrp->actpointer);*/ + bbsnnrp->actpointer = NULL; +#endif + if (close(bbsnnrp->actfd) < 0) + fprintf(stderr, "can't close actfd\n"); + } +} + +static FILE* Xhdrfp; +static char NNRPbuffer[4096]; +static char INNBBSbuffer[4096]; + +char * +NNRPgets(string, len, fp) +char *string; +int len; +FILE *fp; +{ + char* re = fgets(string, len, fp); + char *ptr; + if (re != NULL) { + if ((ptr = (char*)strchr(string,'\r'))!=NULL) + *ptr = '\0'; + if ((ptr = (char*)strchr(string,'\n'))!=NULL) + *ptr = '\0'; + } + return re; +} + +int NNRPstat(bbsnnrp, artno, mid) +nnrp_t *bbsnnrp; + ULONG artno; +char **mid; +{ + char *ptr; + int code; + + *mid = NULL; + fprintf(bbsnnrp->nnrpout,"STAT %d\r\n",artno); + fflush(bbsnnrp->nnrpout); + verboselog("nnrpPut: STAT %d\n",artno); + NNRPgets(NNRPbuffer, sizeof NNRPbuffer, bbsnnrp->nnrpin); + verboselog("nnrpGet: %s\n",NNRPbuffer); + + ptr = (char*) strchr(NNRPbuffer, ' '); + if (ptr != NULL) *ptr++ = '\0'; + code = atoi(NNRPbuffer); + ptr = (char*) strchr(ptr, ' '); + if (ptr != NULL) *ptr++ = '\0'; + *mid = ptr; + ptr = (char*) strchr(ptr, ' '); + if (ptr != NULL) *ptr++ = '\0'; + return code; +} + +int +NNRPxhdr(pattern, bbsnnrp, i, low, high) +char *pattern; +nnrp_t *bbsnnrp; +int i; +ULONG low, high; +{ + newsrc_t *rcptr = &bbsnnrp->newsrc[i]; + int size, code; + + Xhdrfp = bbsnnrp->nnrpin; + fprintf(bbsnnrp->nnrpout,"XHDR %s %d-%d\r\n",pattern,low, high ); +#ifdef BBSNNRPDEBUG + printf("XHDR %s %d-%d\r\n",pattern,low, high ); +#endif + fflush(bbsnnrp->nnrpout); + verboselog("nnrpPut: XHDR %s %d-%d\n",pattern,low, high ); + NNRPgets(NNRPbuffer, sizeof NNRPbuffer, bbsnnrp->nnrpin); + verboselog("nnrpGet: %s\n", NNRPbuffer); + code = atoi(NNRPbuffer); + return code; +} + +int NNRPxhdrget(artno, mid, iscontrol) +int *artno; +char **mid; +int iscontrol; +{ + *mid = NULL; + *artno = 0; + if (NNRPgets(NNRPbuffer, sizeof NNRPbuffer, Xhdrfp) == NULL) + return 0; + else { + char *ptr, *s; + if (strcmp(NNRPbuffer,".")==0) return 0; + ptr = (char*)strchr(NNRPbuffer,' '); + if (!ptr) return 1; + *ptr++ = '\0'; + *artno = atol(NNRPbuffer); + if (iscontrol) { + ptr = (char*)strchr(s=ptr,' '); + if (!ptr) return 1; + *ptr++ = '\0'; + if (strcmp(s,"cancel") != 0) return 1; + } + *mid = ptr; + return 1; + } +} + +int INNBBSstat(bbsnnrp, i, mid) +nnrp_t *bbsnnrp; +int i; +char *mid; +{ + newsrc_t *rcptr = &bbsnnrp->newsrc[i]; + int size, code; + + fprintf(bbsnnrp->innbbsout,"STAT %s\r\n", mid); + fflush(bbsnnrp->innbbsout); + verboselog("innbbsPut: STAT %s\n", mid); + NNRPgets(INNBBSbuffer, sizeof INNBBSbuffer, bbsnnrp->innbbsin); + verboselog("innbbsGet: %s\n", INNBBSbuffer); + return atol(INNBBSbuffer); +} + +int INNBBSihave(bbsnnrp, artno, mid) +nnrp_t *bbsnnrp; +ULONG artno; +char *mid; +{ + int size, code; + int header=1; + + if (DefaultNntpProtocol == NntpPostProtocol) { + fprintf(bbsnnrp->innbbsout,"POST\r\n"); + fflush(bbsnnrp->innbbsout); + verboselog("innbbsPut: POST %s\n", mid); + } else { + fprintf(bbsnnrp->innbbsout,"IHAVE %s\r\n", mid); + fflush(bbsnnrp->innbbsout); + verboselog("innbbsPut: IHAVE %s\n", mid); + } + if (NNRPgets(INNBBSbuffer, sizeof INNBBSbuffer, bbsnnrp->innbbsin)==NULL){ + return -1; + } + verboselog("innbbsGet: %s\n", INNBBSbuffer); +#ifdef BBSNNRPDEBUG + printf("ihave got %s\n", INNBBSbuffer); +#endif + + if (DefaultNntpProtocol == NntpPostProtocol) { + if ((code=atol(INNBBSbuffer)) != NNTP_START_POST_VAL) { + if (code == NNTP_POSTFAIL_VAL) + return 0; + else + return -1; + } + } else { + if ((code=atol(INNBBSbuffer)) != INNBBSihaveOK) { + if (code == 435 || code == 437) + return 0; + else + return -1; + } + } + if (artno != -1) { + fprintf(bbsnnrp->nnrpout,"ARTICLE %d\r\n", artno); + verboselog("nnrpPut: ARTICLE %d\n", artno); +#ifdef BBSNNRPDEBUG + printf("ARTICLE %d\r\n", artno); +#endif + fflush(bbsnnrp->nnrpout); + if (NNRPgets(NNRPbuffer, sizeof NNRPbuffer, bbsnnrp->nnrpin)==NULL) { + return -1; + } + verboselog("nnrpGet: %s\n", NNRPbuffer); +#ifdef BBSNNRPDEBUG + printf("article got %s\n", NNRPbuffer); +#endif + if (atol(NNRPbuffer) != NNRParticleOK) { + fputs(".\r\n",bbsnnrp->innbbsout); + fflush(bbsnnrp->innbbsout); + NNRPgets(INNBBSbuffer, sizeof INNBBSbuffer, bbsnnrp->innbbsin); + verboselog("innbbsGet: %s\n",INNBBSbuffer); + return 0; + } + } + header = 1; + while ( fgets(NNRPbuffer, sizeof NNRPbuffer, bbsnnrp->nnrpin) != NULL) + { + if (strcmp(NNRPbuffer,"\r\n") == 0) + header = 0; + if (strcmp(NNRPbuffer,".\r\n")==0) { + verboselog("nnrpGet: .\n"); + fputs(NNRPbuffer,bbsnnrp->innbbsout); + fflush(bbsnnrp->innbbsout); + verboselog("innbbsPut: .\n"); + if (NNRPgets(INNBBSbuffer, sizeof INNBBSbuffer, bbsnnrp->innbbsin)==NULL) + return -1; + verboselog("innbbsGet: %s\n",INNBBSbuffer); +#ifdef BBSNNRPDEBUG + printf("end ihave got %s\n", INNBBSbuffer); +#endif + code = atol(INNBBSbuffer); + if (DefaultNntpProtocol == NntpPostProtocol) { + if (code == NNTP_POSTEDOK_VAL) + return 1; + if (code == NNTP_POSTFAIL_VAL) + return 0; + } else { + if (code == 235) + return 1; + if (code == 437 || code == 435) + return 0; + } + break; + } + if ( DefaultNntpProtocol == NntpPostProtocol && + header && strncasecmp(NNRPbuffer,"NNTP-Posting-Host: ",19)==0) { + fprintf(bbsnnrp->innbbsout,"X-%s",NNRPbuffer); + } else { + fputs(NNRPbuffer,bbsnnrp->innbbsout); + } + } + fflush(bbsnnrp->innbbsout); + return -1; +} + +int +NNRPgroup(bbsnnrp, i, low, high) +nnrp_t *bbsnnrp; +int i; +ULONG *low, *high; +{ + newsrc_t *rcptr = &bbsnnrp->newsrc[i]; + int size, code; + ULONG tmp; + + fprintf(bbsnnrp->nnrpout,"GROUP %-.*s\r\n", + rcptr->namelen, rcptr->nameptr ); + printf("GROUP %-.*s\r\n", rcptr->namelen, rcptr->nameptr ); + verboselog("nnrpPut: GROUP %-.*s\n", rcptr->namelen, rcptr->nameptr ); + fflush(bbsnnrp->nnrpout); + NNRPgets(NNRPbuffer, sizeof NNRPbuffer, bbsnnrp->nnrpin); + verboselog("nnrpGet: %s\n",NNRPbuffer); + printf("%s\n",NNRPbuffer); + sscanf(NNRPbuffer, "%d %d %ld %ld", &code, &size, low, high); + if (*low > *high) { + tmp = *low; + *low = *high; + *high = tmp; + } + return code; +} + + +readnews(bbsnnrp) +nnrp_t *bbsnnrp; +{ + int i; + char buffer[4096]; + ULONG low, high; + + fgets(buffer, sizeof buffer, bbsnnrp->innbbsin); + verboselog("innbbsGet: %s", buffer); + if (atoi(buffer) != INNBBSconnectOK) { + fprintf(stderr, "INNBBS server not OK\n"); + return; + } +#ifdef BBSNNRPDEBUG + printf("%s",buffer); +#endif + fgets(buffer, sizeof buffer, bbsnnrp->nnrpin); + verboselog("nnrpGet: %s", buffer); + if (buffer[0] != '2') { + /*if (atoi(buffer) != NNRPconnectOK && atoi(buffer) != NNTP_NOPOSTOK_VAL) {*/ + fprintf(stderr, "NNRP server not OK\n"); + return; + } +#ifdef BBSNNRPDEBUG + printf("%s",buffer); +#endif + fputs("MODE READER\r\n", bbsnnrp->nnrpout); + fflush(bbsnnrp->nnrpout); + verboselog("nnrpPut: MODE READER\n"); + fgets(buffer, sizeof buffer, bbsnnrp->nnrpin); + verboselog("nnrpGet: %s",buffer); + + if ( DefaultNntpProtocol == NntpPostProtocol ) { + fputs("MODE READER\r\n", bbsnnrp->innbbsout); + fflush(bbsnnrp->innbbsout); + verboselog("innbbsPut: MODE READER\n"); + fgets(buffer, sizeof buffer, bbsnnrp->innbbsin); + verboselog("innbbsGet: %s",buffer); + } + +#ifdef BBSNNRPDEBUG + printf("%s",buffer); +#endif + + if (StatHistory == 0) { + fputs("MIDCHECK OFF\r\n", bbsnnrp->innbbsout); + fflush(bbsnnrp->innbbsout); + verboselog("innbbsPut: MIDCHECK OFF\n"); + fgets(buffer, sizeof buffer, bbsnnrp->innbbsin); + verboselog("innbbsGet: %s",buffer); + } + bbsnnrp->actdirty = 0; + for (i =0; i< ACT_COUNT; i++) { + int code = NNRPgroup(bbsnnrp, i, &low, &high); + newsrc_t *rcptr = &bbsnnrp->newsrc[i]; + int j; + ULONG artno; + char *mid; + int artcount; + +#ifdef BBSNNRPDEBUG + printf("got reply %d %ld %ld\n",code, low, high); +#endif + artcount = 0; + if (code == NNRPGroupOK) { + int xcount; + ULONG maxartno= rcptr->high; + int isCancelControl = (strncmp(rcptr->nameptr,"control",rcptr->namelen)==0) + || + (strncmp(rcptr->nameptr,"control.cancel",rcptr->namelen)==0) ; + +/* less than or equal to high, for server renumber */ + if (rcptr->low != low) { + bbsnnrp->actdirty = 1; + rcptr->low = low; + updaterc(rcptr->lowptr, rcptr->lowlen, rcptr->low); + } + if (ResetActive) { + if (rcptr->high != high) { + bbsnnrp->actdirty = 1; + rcptr->high = high; + updaterc(rcptr->highptr, rcptr->highlen, rcptr->high); + } + } else if (rcptr->high < high) { + int xhdrcode; + ULONG maxget=high; + int exception=0; + if (rcptr->high < low) { + bbsnnrp->actdirty = 1; + rcptr->high = low; + updaterc(rcptr->highptr, rcptr->highlen, low); + } + if (high > rcptr->high + Max_Stats) { + maxget = rcptr->high + Max_Stats; + } + if ( isCancelControl ) + xhdrcode = NNRPxhdr("Control",bbsnnrp, i, rcptr->high+1, maxget); + else + xhdrcode = NNRPxhdr("Message-ID",bbsnnrp, i, rcptr->high+1, maxget); + + maxartno = maxget; + if ( xhdrcode == NNRPXhdrOK) { + while (NNRPxhdrget(&artno, &mid, isCancelControl)) { +/*#define DEBUG*/ +#ifdef DEBUG + printf("no %d id %s\n",artno, mid); +#endif + if (artcount < Max_Arts) { + if (mid != NULL && !isCancelControl) { + if (!StatHistory || INNBBSstat(bbsnnrp,i,mid) != INNBBSstatOK) { + printf("** %d ** %d need it %s\n", artcount, artno,mid); + XHDR[artcount].artno = artno; + XHDR[artcount].header = restrdup(XHDR[artcount].header,mid); + /*INNBBSihave(bbsnnrp,i,artno,mid);*/ + /* to get it */ + artcount++; + } + } else if (mid != NULL) { + if (INNBBSstat(bbsnnrp,i,mid) == INNBBSstatOK) { + printf("** %d ** %d need cancel %s\n", artcount, artno,mid); + XHDR[artcount].artno = artno; + XHDR[artcount].header = restrdup(XHDR[artcount].header,mid); + artcount++; + } + } + maxartno = artno; + } + } + }/* while xhdr OK */ + exception = 0; + for (xcount = 0; xcount < artcount; xcount++) { + ULONG artno; + char *mid; + artno = XHDR[xcount].artno; + mid = XHDR[xcount].header; + if (isCancelControl) { + if (NNRPstat(bbsnnrp,artno,&mid) == NNRPstatOK) { + } + } + printf("** %d ** %d i have it %s\n", xcount, artno, mid); + if (!ResetActive && mid != NULL) + exception = INNBBSihave(bbsnnrp,artno,mid); + if (exception == -1) break; + if (rcptr->high != artno) { + rcptr->high = artno; + updaterc(rcptr->highptr, rcptr->highlen, rcptr->high); + } + } + if (rcptr->high != maxartno && exception != -1) { + bbsnnrp->actdirty = 1; + rcptr->high = maxartno; + updaterc(rcptr->highptr, rcptr->highlen, maxartno); + } + } + } + /* + flushrc(bbsnnrp); + */ + } + fprintf(bbsnnrp->innbbsout,"quit\r\n"); + fprintf(bbsnnrp->nnrpout,"quit\r\n"); + fflush(bbsnnrp->innbbsout); + fflush(bbsnnrp->nnrpout); + fgets(NNRPbuffer, sizeof NNRPbuffer, bbsnnrp->nnrpin); + fgets(INNBBSbuffer, sizeof INNBBSbuffer, bbsnnrp->innbbsin); + +/* bbsnnrp->newsrc[0].high = 1900; + updaterc(bbsnnrp->newsrc[0].highptr, bbsnnrp->newsrc[0].highlen, + bbsnnrp->newsrc[0].high); +*/ +} + +INNBBSDhalt() +{ +} diff --git a/innbbsd/clibrary.h b/innbbsd/clibrary.h new file mode 100644 index 00000000..92ec82e0 --- /dev/null +++ b/innbbsd/clibrary.h @@ -0,0 +1,131 @@ +/* $Revision: 1.1 $ +** +** Here be declarations of routines and variables in the C library. +** You must #include <sys/types.h> and <stdio.h> before this file. +*/ + +#if defined(DO_HAVE_UNISTD) +#include <unistd.h> +#endif /* defined(DO_HAVE_UNISTD) */ + +#if defined(DO_HAVE_VFORK) +#include <vfork.h> +#endif /* defined(DO_HAVE_VFORK) */ + + /* Generic pointer, used by memcpy, malloc, etc. */ + /* =()<typedef @<POINTER>@ *POINTER;>()= */ +typedef char *POINTER; + /* What is a file offset? Will not work unless long! */ + /* =()<typedef @<OFFSET_T>@ OFFSET_T;>()= */ +typedef long OFFSET_T; + /* What is the type of an object size? */ + /* =()<typedef @<SIZE_T>@ SIZE_T;>()= */ +typedef int SIZE_T; + /* What is the type of a passwd uid and gid, for use in chown(2)? */ + /* =()<typedef @<UID_T>@ UID_T;>()= */ +typedef int UID_T; + /* =()<typedef @<GID_T>@ GID_T;>()= */ +typedef int GID_T; + /* =()<typedef @<PID_T>@ PID_T;>()= */ +typedef int PID_T; + /* What should a signal handler return? */ + /* =()<#define SIGHANDLER @<SIGHANDLER>@>()= */ +#define SIGHANDLER void + +#if defined(SIG_DFL) + /* What types of variables can be modified in a signal handler? */ + /* =()<typedef @<SIGVAR>@ SIGVAR;>()= */ +typedef int SIGVAR; +#endif /* defined(SIG_DFL) */ + +/* =()<#include @<STR_HEADER>@>()= */ +#include <string.h> +/* =()<#include @<MEM_HEADER>@>()= */ +#include <memory.h> + + +/* +** It's a pity we have to go through these contortions, for broken +** systems that have fd_set but not the FD_SET. +*/ +#if defined(FD_SETSIZE) +#define FDSET fd_set +#else +#include <sys/param.h> +#if !defined(NOFILE) + error -- #define NOFILE to the number of files allowed on your machine! +#endif /* !defined(NOFILE) */ +#if !defined(howmany) +#define howmany(x, y) (((x) + ((y) - 1)) / (y)) +#endif /* !defined(howmany) */ +#define FD_SETSIZE NOFILE +#define NFDBITS (sizeof (long) * 8) +typedef struct _FDSET { + long fds_bits[howmany(FD_SETSIZE, NFDBITS)]; +} FDSET; +#define FD_SET(n, p) (p)->fds_bits[(n) / NFDBITS] |= (1 << ((n) % NFDBITS)) +#define FD_CLR(n, p) (p)->fds_bits[(n) / NFDBITS] &= ~(1 << ((n) % NFDBITS)) +#define FD_ISSET(n, p) ((p)->fds_bits[(n) / NFDBITS] & (1 << ((n) % NFDBITS))) +#define FD_ZERO(p) (void)memset((POINTER)(p), 0, sizeof *(p)) +#endif /* defined(FD_SETSIZE) */ + + +#if !defined(SEEK_SET) +#define SEEK_SET 0 +#endif /* !defined(SEEK_SET) */ +#if !defined(SEEK_END) +#define SEEK_END 2 +#endif /* !defined(SEEK_END) */ + +/* +** We must use #define to set FREEVAL, since "typedef void FREEVAL;" doesn't +** work on some broken compilers, sigh. +*/ +/* =()<#define FREEVAL @<FREEVAL>@>()= */ +#define FREEVAL int + +extern int optind; +extern char *optarg; +#if !defined(__STDC__) +extern int errno; +#endif /* !defined(__STDC__) */ + +extern char *getenv(); +extern char *inet_ntoa(); +extern char *mktemp(); +#if !defined(strerror) +extern char *strerror(); +#endif /* !defined(strerror) */ +extern long atol(); +extern time_t time(); +extern unsigned long inet_addr(); +extern FREEVAL free(); +extern POINTER malloc(); +extern POINTER realloc(); +#if defined(ACT_MMAP) +extern char *mmap(); +#endif /* defined(ACT_MMAP) */ + +/* Some backward systems need this. */ +extern FILE *popen(); + +/* This is in <mystring.h>, but not in some system string headers, + * so we put it here just in case. */ +extern int strncasecmp(); + +/* =()<extern @<ABORTVAL>@ abort();>()= */ +extern int abort(); +/* =()<extern @<ALARMVAL>@ alarm();>()= */ +extern int alarm(); +/* =()<extern @<EXITVAL>@ exit();>()= */ +extern void exit(); +/* =()<extern @<GETPIDVAL>@ getpid();>()= */ +extern int getpid(); +/* =()<extern @<LSEEKVAL>@ lseek();>()= */ +extern off_t lseek(); +/* =()<extern @<QSORTVAL>@ qsort();>()= */ +extern int qsort(); +/* =()<extern @<SLEEPVAL>@ sleep();>()= */ +extern int sleep(); +/* =()<extern @<_EXITVAL>@ _exit();>()= */ +extern int _exit(); diff --git a/innbbsd/closeonexec.c b/innbbsd/closeonexec.c new file mode 100644 index 00000000..e005e51b --- /dev/null +++ b/innbbsd/closeonexec.c @@ -0,0 +1,66 @@ +/* $Revision: 1.1 $ +** +*/ +/*#include "configdata.h"*/ +#include <stdio.h> +#include <sys/types.h> +#include <errno.h> +#include <sys/ioctl.h> +#include "clibrary.h" + +#ifndef CLX_IOCTL +# define CLX_IOCTL +#endif +#ifndef CLX_FCNTL +# define CLX_FCNTL +#endif + + + +#if defined(CLX_IOCTL) && !defined(IRIX) +#ifdef __linux +# include <termios.h> +#else +# include <sgtty.h> +#endif + + +/* +** Mark a file close-on-exec so that it doesn't get shared with our +** children. Ignore any error codes. +*/ +void +closeOnExec(fd, flag) + int fd; + int flag; +{ + int oerrno; + + oerrno = errno; + (void)ioctl(fd, flag ? FIOCLEX : FIONCLEX, (char *)NULL); + errno = oerrno; +} +#endif /* defined(CLX_IOCTL) */ + + + +#if defined(CLX_FCNTL) +#include <fcntl.h> + + +/* +** Mark a file close-on-exec so that it doesn't get shared with our +** children. Ignore any error codes. +*/ +void +CloseOnExec(fd, flag) + int fd; + int flag; +{ + int oerrno; + + oerrno = errno; + (void)fcntl(fd, F_SETFD, flag ? 1 : 0); + errno = oerrno; +} +#endif /* defined(CLX_FCNTL) */ diff --git a/innbbsd/connectsock.c b/innbbsd/connectsock.c new file mode 100644 index 00000000..71203cfe --- /dev/null +++ b/innbbsd/connectsock.c @@ -0,0 +1,452 @@ +#include "innbbsconf.h" +#include "daemon.h" +#include <signal.h> +#include <setjmp.h> + +static jmp_buf timebuf; + +static void +timeout(sig) + int sig; +{ + longjmp(timebuf, sig); +} + +extern int errno; +static void reapchild (s) +int s; +{ + int state; + while (waitpid(-1,&state,WNOHANG|WUNTRACED)>0) { + /* printf("reaping child\n");*/ + } +} + +void dokill(s) +int s; +{ + kill(0,SIGKILL); +} + +static INETDstart = 0; +int startfrominetd(flag) +{ + INETDstart = flag ; +} + + +int standalonesetup(fd) +int fd; +{ + int on =1; + struct linger foobar; + if (setsockopt(fd,SOL_SOCKET, SO_REUSEADDR,(char *)&on,sizeof(on)) < 0) + syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m"); + foobar.l_onoff = 0; + if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&foobar, sizeof (foobar))<0) + syslog(LOG_ERR, "setsockopt (SO_LINGER): %m"); +} + +static char *UNIX_SERVER_PATH; +static int (*halt)(); + +sethaltfunction(haltfunc) +int (*haltfunc)(); +{ + halt = haltfunc; +} + +void docompletehalt(s) +int s; +{ + /*printf("try to remove %s\n", UNIX_SERVER_PATH); + unlink(UNIX_SERVER_PATH);*/ + exit(0); + /*dokill();*/ +} + +void doremove(s) +int s; +{ + if (halt != NULL) + (*halt)(s); + else + docompletehalt(s); +} + + +initunixserver(path, protocol) +char *path; +char *protocol; +{ + struct sockaddr_un s_un; + /* unix endpoint address */ + struct protoent *pe; /*protocol information entry*/ + int s; + char *ptr; + + bzero((char*)&s_un,sizeof(s_un)); + s_un.sun_family= AF_UNIX; + strcpy(s_un.sun_path, path); + if (protocol==NULL) + protocol="tcp"; + /* map protocol name to protocol number */ + pe=getprotobyname(protocol); + if (pe==NULL) { + fprintf(stderr,"%s: Unknown protocol.\n",protocol); + return (-1); + } + + /* Allocate a socket */ + s = socket(PF_UNIX,strcmp(protocol,"tcp")?SOCK_DGRAM:SOCK_STREAM,0); + if (s<0) { + printf("protocol %d\n", pe->p_proto); + perror("socket"); + return -1; + } + /*standalonesetup(s);*/ + signal(SIGHUP, SIG_IGN); + signal(SIGUSR1, SIG_IGN); + signal(SIGCHLD,reapchild); + UNIX_SERVER_PATH = path; + signal(SIGINT,doremove); + signal(SIGTERM,doremove); + + chdir("/"); + if (bind(s,(struct sockaddr*)&s_un,sizeof (struct sockaddr_un))<0){ + perror("bind"); + perror(path); + return -1; + } + listen(s,10); + return s; +} + +initinetserver(service,protocol) +char *service; +char *protocol; +{ + struct servent *se; /*service information entry*/ + struct hostent *he; /*host information entry*/ + struct protoent *pe; /*protocol information entry*/ + struct sockaddr_in sin;/*Internet endpoint address*/ + int port,s; + int randomport=0; + + bzero((char*)&sin,sizeof(sin)); + sin.sin_family= AF_INET; + if (!strcmp("0",service)) { + randomport = 1; + sin.sin_addr.s_addr = INADDR_ANY; + } + + if (service==NULL) + service=DEFAULTPORT; + if (protocol==NULL) + protocol="tcp"; + /* map service name to port number */ + /* service ---> port */ + se = getservbyname(service,protocol); + if (se==NULL) { + port = htons((u_short)atoi(service)); + if (port==0 && !randomport) { + fprintf (stderr, "%s/%s: Unknown service.\n",service,protocol); + return (-1); + } + } else + port=se->s_port; + sin.sin_port = port; + + /* map protocol name to protocol number */ + pe=getprotobyname(protocol); + if (pe==NULL) { + fprintf(stderr,"%s: Unknown protocol.\n",protocol); + return (-1); + } + + /* Allocate a socket */ + s = socket(PF_INET,strcmp(protocol,"tcp")?SOCK_DGRAM:SOCK_STREAM,pe->p_proto); + if (s<0) { + perror("socket"); + return -1; + } + standalonesetup(s); + signal(SIGHUP, SIG_IGN); + signal(SIGUSR1, SIG_IGN); + signal(SIGCHLD,reapchild); + signal(SIGINT,dokill); + signal(SIGTERM,dokill); + + chdir("/"); + if (bind(s,(struct sockaddr*)&sin,sizeof (struct sockaddr_in))<0){ + perror("bind"); + return -1; + } + listen(s,10); +#ifdef DEBUG + { int length=sizeof(sin); + getsockname(s,&sin,&length); + printf("portnum alocalted %d\n",sin.sin_port); + } +#endif + return s; +} + +int open_unix_listen(path,protocol,initfunc) +char *path; +char *protocol; +int (*initfunc) ARG((int)); +{ + int s; + s = initunixserver(path,protocol); + if (s<0) { + return -1; + } + if (initfunc != NULL) { + printf("in inetsingleserver before initfunc s %d\n",s); + if ((*initfunc)(s)<0) { + perror("initfunc error"); + return -1; + } + printf("end inetsingleserver before initfunc \n"); + } + return s; +} + +int open_listen(service,protocol,initfunc) +char *service; +char *protocol; +int (*initfunc) ARG((int)); +{ + int s; + if (! INETDstart) + s = initinetserver(service,protocol); + else + s = 0; + if (s<0) { + return -1; + } + if (initfunc != NULL) { + printf("in inetsingleserver before initfunc s %d\n",s); + if ((*initfunc)(s)<0) { + perror("initfunc error"); + return -1; + } + printf("end inetsingleserver before initfunc \n"); + } + return s; +} + +int inetsingleserver(service,protocol,serverfunc,initfunc) +char *service; +char *protocol; +int (*initfunc) ARG((int)); +int (*serverfunc) ARG((int)); +{ + int s; + if (!INETDstart) + s = initinetserver(service,protocol); + else + s = 0; + if (s<0) { + return -1; + } + if (initfunc != NULL) { + printf("in inetsingleserver before initfunc s %d\n",s); + if ((*initfunc)(s)<0) { + perror("initfunc error"); + return -1; + } + printf("end inetsingleserver before initfunc \n"); + } + { + int ns=tryaccept(s); + int result=0; + if (ns < 0 && errno != EINTR){ +#ifdef DEBUGSERVER + perror("accept"); +#endif + } + close(s); + if (serverfunc != NULL) + result = (*serverfunc)(ns); + close(ns); + return(result); + } +} + + +int tryaccept(s) +int s; +{ + int ns,fromlen; + struct sockaddr sockaddr;/*Internet endpoint address*/ + fromlen=sizeof (struct sockaddr_in); + +#ifdef DEBUGSERVER + fputs("Listening again\n",stdout); +#endif + do { + ns = accept(s,&sockaddr,&fromlen); + errno = 0; + } while ( ns < 0 && errno == EINTR ); + return ns; +} + +int inetserver(service,protocol,serverfunc) +char *service; +char *protocol; +int (*serverfunc) ARG((int)); +{ + int port,s; + + if (!INETDstart) + s = initinetserver(service,protocol); + else + s = 0; + if (s<0) { + return -1; + } + for (;;) { + int ns=tryaccept(s); + int result=0; + int pid; + if (ns < 0 && errno != EINTR){ +#ifdef DEBUGSERVER + perror("accept"); +#endif + continue; + } +#ifdef DEBUGSERVER + fputs("Accept OK\n",stdout); +#endif + pid = fork(); + if (pid==0) { + close(s); + if (serverfunc != NULL) + result = (*serverfunc)(ns); + close(ns); + exit(result); + } else if (pid < 0) { + perror("fork"); + return -1; + } + close(ns); + } + return 0; +} + +int inetclient(server,service,protocol) +char *server; +char *protocol; +char *service; +{ + struct servent *se; /*service information entry*/ + struct hostent *he; /*host information entry*/ + struct protoent *pe; /*protocol information entry*/ + struct sockaddr_in sin;/*Internet endpoint address*/ + int port,s; + + bzero((char*)&sin,sizeof(sin)); + sin.sin_family= AF_INET; + + if (service==NULL) + service=DEFAULTPORT; + if (protocol==NULL) + protocol="tcp"; + if (server==NULL) + server=DEFAULTSERVER; + /* map service name to port number */ + /* service ---> port */ + se = getservbyname(service,protocol); + if (se==NULL) { + port = htons((u_short)atoi(service)); + if (port==0) { + fprintf (stderr, "%s/%s: Unknown service.\n",service,protocol); + return (-1); + } + } else + port=se->s_port; + sin.sin_port = port; + + /* map server hostname to IP address, allowing for dotted decimal */ + he=gethostbyname(server); + if (he==NULL) { + sin.sin_addr.s_addr = inet_addr(server); + if (sin.sin_addr.s_addr==INADDR_NONE) { + fprintf (stderr, "%s: Unknown host.\n",server); + return (-1); + } + } else + bcopy(he->h_addr,(char*)&sin.sin_addr,he->h_length); + + /* map protocol name to protocol number */ + pe=getprotobyname(protocol); + if (pe==NULL) { + fprintf(stderr,"%s: Unknown protocol.\n",protocol); + return (-1); + } + + /* Allocate a socket */ + s = socket(PF_INET,strcmp(protocol,"tcp")?SOCK_DGRAM:SOCK_STREAM,pe->p_proto); + if (s<0) { + perror("socket"); + return -1; + } + + if (setjmp(timebuf) == 0) + { + signal(SIGALRM, timeout); + alarm(5); + if (connect(s,(struct sockaddr*)&sin,sizeof(sin))<0) + { + alarm(0); + return -1; + } + } + else + { + alarm(0); + return -1; + } + alarm(0); + + return s; +} + +int unixclient(path,protocol) +char *path; +char *protocol; +{ + struct protoent *pe; /*protocol information entry*/ + struct sockaddr_un s_un;/*unix endpoint address*/ + int s; + + bzero((char*)&s_un,sizeof(s_un)); + s_un.sun_family= AF_UNIX; + + if (path==NULL) + path=DEFAULTPATH; + if (protocol==NULL) + protocol="tcp"; + strcpy(s_un.sun_path , path); + + /* map protocol name to protocol number */ + pe=getprotobyname(protocol); + if (pe==NULL) { + fprintf(stderr,"%s: Unknown protocol.\n",protocol); + return (-1); + } + /* Allocate a socket */ + s = socket(PF_UNIX,strcmp(protocol,"tcp")?SOCK_DGRAM:SOCK_STREAM,0); + if (s<0) { + perror("socket"); + return -1; + } + /* Connect the socket to the server */ + if (connect(s,(struct sockaddr*)&s_un,sizeof(s_un))<0) { + /*perror("connect");*/ + return -1; + } + return s; +} diff --git a/innbbsd/ctlinnbbsd.c b/innbbsd/ctlinnbbsd.c new file mode 100644 index 00000000..4ba77b52 --- /dev/null +++ b/innbbsd/ctlinnbbsd.c @@ -0,0 +1,160 @@ +#include "innbbsconf.h" +#include "bbslib.h" + +extern char *optarg; +extern int opterr, optind; + +usage(name) +char *name; +{ + fprintf(stderr, "Usage: %s [-p path] commands\n",name); + fprintf(stderr, " where available commands:\n"); + fprintf(stderr," ctlinnbbsd reload : reload datafiles for innbbsd\n"); + fprintf(stderr," ctlinnbbsd shutdown : shutdown innbbsd gracefully\n"); + fprintf(stderr," ctlinnbbsd mode : examine mode of innbbsd\n"); + fprintf(stderr," ctlinnbbsd addhist <mid> path: add history\n"); + fprintf(stderr," ctlinnbbsd grephist <mid>: query history\n"); + fprintf(stderr," ctlinnbbsd verboselog on|off : verboselog on/off\n"); + fprintf(stderr," ctlinnbbsd hismaint : maintain history\n"); + fprintf(stderr," ctlinnbbsd listnodelist : list nodelist.bbs\n"); + fprintf(stderr," ctlinnbbsd listnewsfeeds : list newsfeeds.bbs\n"); +#ifdef GETRUSAGE + fprintf(stderr," ctlinnbbsd getrusage: get resource usage\n"); +#endif +#ifdef MALLOCMAP + fprintf(stderr," ctlinnbbsd mallocmap: get malloc map\n"); +#endif +} + + +char *DefaultPath = LOCALDAEMON; +char INNBBSbuffer[4096]; + +FILE *innbbsin, *innbbsout; +int innbbsfd; + +ctlinnbbsd(argc, argv) +int argc; +char **argv; +{ + fgets(INNBBSbuffer, sizeof INNBBSbuffer, innbbsin); + printf("%s",INNBBSbuffer); + if (strcasecmp(argv[0], "shutdown")==0 || + strcasecmp(argv[0], "reload")==0 || + strcasecmp(argv[0], "hismaint")==0 || +#ifdef GETRUSAGE + strcasecmp(argv[0], "getrusage")==0 || +#endif +#ifdef MALLOCMAP + strcasecmp(argv[0], "mallocmap")==0 || +#endif + strcasecmp(argv[0], "mode")==0 || + strcasecmp(argv[0], "listnodelist")==0 || + strcasecmp(argv[0], "listnewsfeeds")==0 + ) { + fprintf( innbbsout, "%s\r\n", argv[0]); + fflush( innbbsout); + fgets(INNBBSbuffer, sizeof INNBBSbuffer, innbbsin); + printf("%s",INNBBSbuffer); + if (strcasecmp(argv[0], "mode") ==0 +#ifdef GETRUSAGE + || + strcasecmp(argv[0], "getrusage") ==0 + || + strcasecmp(argv[0], "mallocmap") ==0 +#endif + || + strcasecmp(argv[0], "listnodelist")==0 + || + strcasecmp(argv[0], "listnewsfeeds")==0 + ) { + while (fgets(INNBBSbuffer, sizeof INNBBSbuffer, innbbsin) != NULL) { + if (strcmp(INNBBSbuffer,".\r\n")==0) { + break; + } + printf("%s",INNBBSbuffer); + } + } + } else if (strcasecmp(argv[0], "grephist")==0 || + strcasecmp(argv[0], "verboselog")==0 ) { + if (argc < 2) { + usage("ctlinnbbsd"); + } else { + fprintf( innbbsout, "%s %s\r\n", argv[0], argv[1]); + fflush( innbbsout); + fgets(INNBBSbuffer, sizeof INNBBSbuffer, innbbsin); + printf("%s\n",INNBBSbuffer); + } + } else if (strcasecmp(argv[0], "addhist")==0) { + if (argc < 3) { + usage("ctlinnbbsd"); + } else { + fprintf( innbbsout, "%s %s %s\r\n", argv[0], argv[1], argv[2]); + fflush( innbbsout); + fgets(INNBBSbuffer, sizeof INNBBSbuffer, innbbsin); + printf("%s",INNBBSbuffer); + } + } else { + fprintf(stderr, "invalid command %s\n", argv[0]); + } + if (strcasecmp(argv[0],"shutdown") != 0) { + fprintf( innbbsout, "QUIT\r\n"); + fflush(innbbsout); + fgets(INNBBSbuffer, sizeof INNBBSbuffer, innbbsin); + } +} + +initsocket() +{ + innbbsfd = unixclient(DefaultPath,"tcp"); + if (innbbsfd < 0) { + fprintf(stderr, "Connect to %s error. You may not run innbbsd\n", DefaultPath); + exit(2); + } + if ( (innbbsin= fdopen(innbbsfd,"r")) == NULL || + (innbbsout= fdopen(innbbsfd,"w"))== NULL ) { + fprintf( stderr, "fdopen error\n"); + exit(3); + } +} + +closesocket() +{ + if (innbbsin != NULL) + fclose(innbbsin); + if (innbbsout != NULL) + fclose(innbbsout); + if (innbbsfd >= 0) + close(innbbsfd); +} + +main(argc, argv) +int argc; +char **argv; +{ + int c, errflag=0; + + while ((c = getopt(argc,argv,"p:h?"))!= -1) + switch (c) { + case 'p': + DefaultPath = optarg; + break; + case 'h': + case '?': + default: + errflag ++; + break; + } + if (errflag > 0) { + usage(argv[0]); + return(1); + } + if (argc - optind < 1) { + usage(argv[0]); + exit(1); + } + initial_bbs(NULL); + initsocket(); + ctlinnbbsd(argc-optind, argv+optind); + closesocket(); +} diff --git a/innbbsd/daemon.c b/innbbsd/daemon.c new file mode 100644 index 00000000..973a96c8 --- /dev/null +++ b/innbbsd/daemon.c @@ -0,0 +1,173 @@ +#include "daemon.h" +/* +typedef struct daemoncmd { + char *cmdname; + char *usage; + int argc; + int (*main) ARG((FILE*,FILE*,int,char**,char*)); +} daemoncmd_t; + +*/ + +void deargify ARG((char ***)); +static daemoncmd_t *dcmdp=NULL; +static char *startupmessage=NULL; +static int startupcode=100; +static FILE *DIN,*DOUT,*DIO; +typedef int (*F)(); + +void installdaemon(cmds,code,startupmsg) +daemoncmd_t *cmds; +int code; +char *startupmsg; +{ + dcmdp = cmds; + startupcode = code; + startupmessage = startupmsg; +} + +daemoncmd_t *searchcmd(cmd) +char *cmd; +{ + daemoncmd_t *p; + for (p=dcmdp;p->name != NULL ; p++) { +#ifdef DEBUGCMD + printf("searching name %s for cmd %s\n",p->name,cmd); +#endif + if (!strncasecmp(p->name,cmd,1024)) + return p; + } + return NULL; +} + +#if 0 +int daemon(dfd) +int dfd; +{ + static char BUF[1024]; + /*hash_init();*/ + if (dfd > 0) { + DIO = fdopen(dfd,"rw"); + DIN = fdopen(dfd,"r"); + DOUT = fdopen(dfd,"w"); + if (DIO == NULL || DIN == NULL || DOUT == NULL) { + perror("fdopen"); + return -1; + } + } + if (startupmessage) { + fprintf(DOUT,"%d %s\n",startupcode,startupmessage); + fflush(DOUT); + } + while (fgets(BUF,1024,DIN) != NULL) { + int i; + int (*Main)(); + daemoncmd_t *dp; + argv_t Argv; + + char *p=(char*)strchr(BUF,'\r'); + if (p == NULL) p=(char*)strchr(BUF,'\n'); + if (p == NULL) continue; + *p='\0'; + if (p==BUF) continue; + + Argv.argc = 0, Argv.argv = NULL, Argv.inputline=BUF; + Argv.in = DIN, Argv.out = DOUT; + printf("command entered: %s\n",BUF); +#ifdef DEBUGSERVER + fprintf(DOUT,"BUF in client %s\n",BUF); + fprintf(stdout,"BUF in server %s\n",BUF); + fflush(DOUT); +#endif + Argv.argc = argify(BUF,&Argv.argv); +#ifdef DEBUGSERVER + fprintf(stdout,"argc %d argv ",Argv.argc); + for (i=0;i<Argv.argc;++i) + fprintf(stdout,"%s ",Argv.argv[i]); + fprintf(stdout,"\n"); +#endif + dp = searchcmd(Argv.argv[0]); + Argv.dc = dp; + if (dp) { +#ifdef DEBUGSERVER + printf("find cmd %s by %s\n",dp->name,dp->usage); +#endif + if (Argv.argc < dp->argc) { + fprintf(DOUT,"%d Usage: %s\n",dp->errorcode,dp->usage); + fflush(DOUT); + goto cont; + } + if (dp->argno != 0 && Argv.argc > dp->argno) { + fprintf(DOUT,"%d Usage: %s\n",dp->errorcode,dp->usage); + fflush(DOUT); + goto cont; + } + Main=dp->main; + if (Main) { + fflush(stdout); + (*Main)(&Argv); + } + } + else { + fprintf(DOUT,"99 command %s not available\n",Argv.argv[0]); + fflush(DOUT); + } +cont: + deargify(&Argv.argv); + } + /*hash_reclaim();*/ +} +#endif + +#define MAX_ARG 32 +#define MAX_ARG_SIZE 16384 + +int argify(line, argvp) +char *line, ***argvp; +{ + static char *argvbuffer[MAX_ARG+2]; + char **argv = argvbuffer; + int i; + static char argifybuffer[MAX_ARG_SIZE]; + char *p; + while (strchr("\t\n\r ",*line)) line++; + i=strlen(line); + /*p=(char*) mymalloc(i+1);*/ + p = argifybuffer; + strncpy(p,line, sizeof argifybuffer); + for (*argvp = argv, i=0 ;*p && i < MAX_ARG;){ + for (*argv++=p;*p && !strchr("\t\r\n ",*p);p++); + if (*p=='\0') break; + for (*p++='\0'; strchr("\t\r\n ",*p) && *p;p++); + } + *argv = NULL; + return argv - *argvp; +} + +void deargify (argv) +char ***argv; +{ + return; + /*if (*argv != NULL) { + if (*argv[0] != NULL){ + free(*argv[0]); + *argv[0] = NULL; + } + free(*argv); + *argv = NULL; + }*/ +} + +int daemonprintf(format) +char *format; +{ + fprintf(DOUT,format); + fflush(DOUT); +} + +int daemonputs(output) +char* output; +{ + fputs(output, DOUT); + fflush(DOUT); +} diff --git a/innbbsd/daemon.h b/innbbsd/daemon.h new file mode 100644 index 00000000..d056051f --- /dev/null +++ b/innbbsd/daemon.h @@ -0,0 +1,54 @@ +#ifndef DAEMON_H +#define DAEMON_H + +#include <stdio.h> +#include <time.h> + +#ifndef ARG +# ifdef __STDC__ +# define ARG(x) x +# else +# define ARG(x) () +# endif +#endif + + +struct Argv_t { + FILE *in,*out; + int argc; + char **argv; + char *inputline; + struct Daemoncmd *dc; +}; + +typedef struct Argv_t argv_t; + +typedef struct Buffer_t { + char *data; + int used, left, lastread; +} buffer_t; + +typedef struct ClientType { + char hostname[1024]; + char username[32]; + char buffer[4096]; + int mode; + argv_t Argv; + int fd, access, lastread, midcheck; + buffer_t in,out; + int ihavecount, ihavesize, ihaveduplicate, ihavefail; + int statcount, statfail; + time_t begin; +} ClientType; + +typedef struct Daemoncmd { + char *name; + char *usage; + int argc, argno, errorcode, normalcode; + int (*main) ARG(( ClientType*)); +} daemoncmd_t; + +extern void installdaemon ARG((daemoncmd_t *,int,char*)); +extern ClientType *Channel; + +#endif diff --git a/innbbsd/dbz.c b/innbbsd/dbz.c new file mode 100644 index 00000000..6fd15df8 --- /dev/null +++ b/innbbsd/dbz.c @@ -0,0 +1,1918 @@ +/* + +dbz.c V3.2 + +Copyright 1988 Jon Zeeff (zeeff@b-tech.ann-arbor.mi.us) +You can use this code in any manner, as long as you leave my name on it +and don't hold me responsible for any problems with it. + +Hacked on by gdb@ninja.UUCP (David Butler); Sun Jun 5 00:27:08 CDT 1988 + +Various improvments + INCORE by moraes@ai.toronto.edu (Mark Moraes) + +Major reworking by Henry Spencer as part of the C News project. + +Minor lint and CodeCenter (Saber) fluff removal by Rich $alz (March, 1991). +Non-portable CloseOnExec() calls added by Rich $alz (September, 1991). +Added "writethrough" and tagmask calculation code from +<rob@violet.berkeley.edu> and <leres@ee.lbl.gov> by Rich $alz (December, 1992). +Merged in MMAP code by David Robinson, formerly <david@elroy.jpl.nasa.gov> +now <david.robinson@sun.com> (January, 1993). + +These routines replace dbm as used by the usenet news software +(it's not a full dbm replacement by any means). It's fast and +simple. It contains no AT&T code. + +In general, dbz's files are 1/20 the size of dbm's. Lookup performance +is somewhat better, while file creation is spectacularly faster, especially +if the incore facility is used. + +*/ + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <ctype.h> +#include <errno.h> +#ifndef __STDC__ +extern int errno; +#endif +#include <dbz.h> +#include "clibrary.h" + +/* + * #ifdef index. "LIA" = "leave it alone unless you know what you're doing". + * + * FUNNYSEEKS SEEK_SET is not 0, get it from <unistd.h> + * INDEX_SIZE backward compatibility with old dbz; avoid using this + * NMEMORY number of days of memory for use in sizing new table (LIA) + * INCORE backward compatibility with old dbz; use dbzincore() instead + * DBZDEBUG enable debugging + * DEFSIZE default table size (not as critical as in old dbz) + * OLDBNEWS default case mapping as in old B News; set NOBUFFER + * BNEWS default case mapping as in current B News; set NOBUFFER + * DEFCASE default case-map algorithm selector + * NOTAGS fseek offsets are strange, do not do tagging (see below) + * NPAGBUF size of .pag buffer, in longs (LIA) + * SHISTBUF size of ASCII-file buffer, in bytes (LIA) + * MAXRUN length of run which shifts to next table (see below) (LIA) + * OVERFLOW long-int arithmetic overflow must be avoided, will trap + * NOBUFFER do not buffer hash-table i/o, B News locking is defective + * MMAP Use SunOS style mmap() for efficient incore + */ +/* SUPPRESS 530 *//* Empty body for statement */ +/* SUPPRESS 701 on free *//* Conflicting declaration */ + +#ifdef FUNNYSEEKS +#include <unistd.h> +#else +#define SEEK_SET 0 +#endif +#ifdef OVERFLOW +#include <limits.h> +#endif + +static int dbzversion = 3; /* for validating .dir file format */ + +/* + * The dbz database exploits the fact that when news stores a <key,value> + * tuple, the `value' part is a seek offset into a text file, pointing to + * a copy of the `key' part. This avoids the need to store a copy of + * the key in the dbz files. However, the text file *must* exist and be + * consistent with the dbz files, or things will fail. + * + * The basic format of the database is a simple hash table containing the + * values. A value is stored by indexing into the table using a hash value + * computed from the key; collisions are resolved by linear probing (just + * search forward for an empty slot, wrapping around to the beginning of + * the table if necessary). Linear probing is a performance disaster when + * the table starts to get full, so a complication is introduced. The + * database is actually one *or more* tables, stored sequentially in the + * .pag file, and the length of linear-probe sequences is limited. The + * search (for an existing item or an empty slot) always starts in the + * first table, and whenever MAXRUN probes have been done in table N, + * probing continues in table N+1. This behaves reasonably well even in + * cases of massive overflow. There are some other small complications + * added, see comments below. + * + * The table size is fixed for any particular database, but is determined + * dynamically when a database is rebuilt. The strategy is to try to pick + * the size so the first table will be no more than 2/3 full, that being + * slightly before the point where performance starts to degrade. (It is + * desirable to be a bit conservative because the overflow strategy tends + * to produce files with holes in them, which is a nuisance.) + */ + +/* + * The following is for backward compatibility. + */ +#ifdef INDEX_SIZE +#define DEFSIZE INDEX_SIZE +#endif + +/* + * ANSI C says an offset into a file is a long, not an off_t, for some + * reason. This actually does simplify life a bit, but it's still nice + * to have a distinctive name for it. Beware, this is just for readability, + * don't try to change this. + */ +#define of_t long +#define SOF (sizeof(of_t)) + +/* + * We assume that unused areas of a binary file are zeros, and that the + * bit pattern of `(of_t)0' is all zeros. The alternative is rather + * painful file initialization. Note that okayvalue(), if OVERFLOW is + * defined, knows what value of an offset would cause overflow. + */ +#define VACANT ((of_t)0) +#define BIAS(o) ((o)+1) /* make any valid of_t non-VACANT */ +#define UNBIAS(o) ((o)-1) /* reverse BIAS() effect */ + +/* + * In a Unix implementation, or indeed any in which an of_t is a byte + * count, there are a bunch of high bits free in an of_t. There is a + * use for them. Checking a possible hit by looking it up in the base + * file is relatively expensive, and the cost can be dramatically reduced + * by using some of those high bits to tag the value with a few more bits + * of the key's hash. This detects most false hits without the overhead of + * seek+read+strcmp. We use the top bit to indicate whether the value is + * tagged or not, and don't tag a value which is using the tag bits itself. + * We're in trouble if the of_t representation wants to use the top bit. + * The actual bitmasks and offset come from the configuration stuff, + * which permits fiddling with them as necessary, and also suppressing + * them completely (by defining the masks to 0). We build pre-shifted + * versions of the masks for efficiency. + */ +static of_t tagbits; /* pre-shifted tag mask */ +static of_t taghere; /* pre-shifted tag-enable bit */ +static of_t tagboth; /* tagbits|taghere */ +#define HASTAG(o) ((o)&taghere) +#define TAG(o) ((o)&tagbits) +#define NOTAG(o) ((o)&~tagboth) +#define CANTAG(o) (((o)&tagboth) == 0) +#define MKTAG(v) (((v)<<conf.tagshift)&tagbits) + +/* + * A new, from-scratch database, not built as a rebuild of an old one, + * needs to know table size, casemap algorithm, and tagging. Normally + * the user supplies this info, but there have to be defaults. + */ +#ifndef DEFSIZE +#define DEFSIZE 120011 /* 300007 might be better */ +#endif +#ifdef OLDBNEWS +#define DEFCASE '0' /* B2.10 -- no mapping */ +#define NOBUFFER /* B News locking is defective */ +#endif +#ifdef BNEWS +#define DEFCASE '=' /* B2.11 -- all mapped */ +#define NOBUFFER /* B News locking is defective */ +#endif +#ifndef DEFCASE /* C News compatibility is the default */ +#define DEFCASE 'C' /* C News -- RFC822 mapping */ +#endif +#ifndef NOTAGS +#define TAGENB 0x80 /* tag enable is top bit, tag is next 7 */ +#define TAGMASK 0x7f +#define TAGSHIFT 24 +#else +#define TAGENB 0 /* no tags */ +#define TAGMASK 0 +#define TAGSHIFT 0 +#endif + +/* + * We read configuration info from the .dir file into this structure, + * so we can avoid wired-in assumptions for an existing database. + * + * Among the info is a record of recent peak usages, so that a new table + * size can be chosen intelligently when rebuilding. 10 is a good + * number of usages to keep, since news displays marked fluctuations + * in volume on a 7-day cycle. + */ +struct dbzconfig { + int olddbz; /* .dir file empty but .pag not? */ + of_t tsize; /* table size */ +# ifndef NMEMORY +# define NMEMORY 10 /* # days of use info to remember */ +# endif +# define NUSEDS (1+NMEMORY) + of_t used[NUSEDS]; /* entries used today, yesterday, ... */ + int valuesize; /* size of table values, == SOF */ + int bytemap[SOF]; /* byte-order map */ + char casemap; /* case-mapping algorithm (see cipoint()) */ + char fieldsep; /* field separator in base file, if any */ + of_t tagenb; /* unshifted tag-enable bit */ + of_t tagmask; /* unshifted tag mask */ + int tagshift; /* shift count for tagmask and tagenb */ +}; +static struct dbzconfig conf; +static int getconf(); +static long getno(); +static int putconf(); +static void mybytemap(); +static of_t bytemap(); + +/* + * Using mmap() is a more efficent way of keeping the .pag file incore. On + * average, it cuts the number of system calls and buffer copies in half. + * It also allows one copy to be shared among many processes without + * consuming any extra resources. + */ +#ifdef MMAP +#include <sys/mman.h> +#ifdef MAP_FILE +#define MAP__ARG (MAP_FILE | MAP_SHARED) +#else +#define MAP__ARG (MAP_SHARED) +#endif +#ifndef INCORE +#define INCORE +#endif +#endif + +/* + * For a program that makes many, many references to the database, it + * is a large performance win to keep the table in core, if it will fit. + * Note that this does hurt robustness in the event of crashes, and + * dbmclose() *must* be called to flush the in-core database to disk. + * The code is prepared to deal with the possibility that there isn't + * enough memory. There *is* an assumption that a size_t is big enough + * to hold the size (in bytes) of one table, so dbminit() tries to figure + * out whether this is possible first. + * + * The preferred way to ask for an in-core table is to do dbzincore(1) + * before dbminit(). The default is not to do it, although -DINCORE + * overrides this for backward compatibility with old dbz. + * + * We keep only the first table in core. This greatly simplifies the + * code, and bounds memory demand. Furthermore, doing this is a large + * performance win even in the event of massive overflow. + */ +#ifdef INCORE +static int incore = 1; +#else +static int incore = 0; +#endif + +/* + * Write to filesystem even if incore? This replaces a single multi- + * megabyte write when doing a dbzsync with a multi-byte write each + * time an article is added. On most systems, this will give an overall + * performance boost. + */ +static int writethrough = 0; + +/* + * Stdio buffer for .pag reads. Buffering more than about 16 does not help + * significantly at the densities we try to maintain, and the much larger + * buffers that most stdios default to are much more expensive to fill. + * With small buffers, stdio is performance-competitive with raw read(), + * and it's much more portable. + */ +#ifndef NPAGBUF +#define NPAGBUF 16 +#endif +#ifndef NOBUFFER +#ifdef _IOFBF +static of_t pagbuf[NPAGBUF]; /* only needed if !NOBUFFER && _IOFBF */ +#endif +#endif + +/* + * Stdio buffer for base-file reads. Message-IDs (all news ever needs to + * read) are essentially never longer than 64 bytes, and the typical stdio + * buffer is so much larger that it is much more expensive to fill. + */ +#ifndef SHISTBUF +#define SHISTBUF 64 +#endif +#ifdef _IOFBF +static char basebuf[SHISTBUF]; /* only needed if _IOFBF exists */ +#endif + +/* + * Data structure for recording info about searches. + */ +struct searcher { + of_t place; /* current location in file */ + int tabno; /* which table we're in */ + int run; /* how long we'll stay in this table */ +# ifndef MAXRUN +# define MAXRUN 100 +# endif + long hash; /* the key's hash code (for optimization) */ + of_t tag; /* tag we are looking for */ + int seen; /* have we examined current location? */ + int aborted; /* has i/o error aborted search? */ +}; +static void start(); +#define FRESH ((struct searcher *)NULL) +static of_t search(); +#define NOTFOUND ((of_t)-1) +static int okayvalue(); +static int set(); + +/* + * Arguably the searcher struct for a given routine ought to be local to + * it, but a fetch() is very often immediately followed by a store(), and + * in some circumstances it is a useful performance win to remember where + * the fetch() completed. So we use a global struct and remember whether + * it is current. + */ +static struct searcher srch; +static struct searcher *prevp; /* &srch or FRESH */ + +/* byte-ordering stuff */ +static int mybmap[SOF]; /* my byte order (see mybytemap()) */ +static int bytesame; /* is database order same as mine? */ +#define MAPIN(o) ((bytesame) ? (o) : bytemap((o), conf.bytemap, mybmap)) +#define MAPOUT(o) ((bytesame) ? (o) : bytemap((o), mybmap, conf.bytemap)) + +/* + * The double parentheses needed to make this work are ugly, but the + * alternative (under most compilers) is to pack around 2K of unused + * strings -- there's just no way to get rid of them. + */ +#ifdef DBZDEBUG +static int debug; /* controlled by dbzdebug() */ +#define DEBUG(args) if (debug) { (void) printf args ; } else +#else +#define DEBUG(args) ; +#endif + +/* externals used */ +#if 0 +extern char *memcpy(); +extern char *memchr(); +extern char *malloc(); +extern char *calloc(); +extern void free(); /* ANSI C; some old implementations say int */ +#endif /* 0 */ +extern int atoi(); +extern long atol(); +extern void CloseOnExec(); + +/* misc. forwards */ +static long hash(); +static void crcinit(); +static char *cipoint(); +static char *mapcase(); +static int isprime(); +static FILE *latebase(); + +/* file-naming stuff */ +static char dir[] = ".dir"; +static char pag[] = ".pag"; +static char *enstring(); + +/* central data structures */ +static FILE *basef; /* descriptor for base file */ +static char *basefname; /* name for not-yet-opened base file */ +static FILE *dirf; /* descriptor for .dir file */ +static int dirronly; /* dirf open read-only? */ +static FILE *pagf = NULL; /* descriptor for .pag file */ +static of_t pagpos; /* posn in pagf; only search may set != -1 */ +static int pagronly; /* pagf open read-only? */ +static of_t *corepag; /* incore version of .pag file, if any */ +static FILE *bufpagf; /* well-buffered pagf, for incore rewrite */ +static of_t *getcore(); +#ifndef MMAP +static int putcore(); +#endif +static int written; /* has a store() been done? */ + +/* + - dbzfresh - set up a new database, no historical info + */ +int /* 0 success, -1 failure */ +dbzfresh(name, size, fs, cmap, tagmask) +char *name; /* base name; .dir and .pag must exist */ +long size; /* table size (0 means default) */ +int fs; /* field-separator character in base file */ +int cmap; /* case-map algorithm (0 means default) */ +of_t tagmask; /* 0 default, 1 no tags */ +{ + register char *fn; + struct dbzconfig c; + register of_t m; + register FILE *f; + + if (pagf != NULL) { + DEBUG(("dbzfresh: database already open\n")); + return(-1); + } + if (size != 0 && size < 2) { + DEBUG(("dbzfresh: preposterous size (%ld)\n", size)); + return(-1); + } + + /* get default configuration */ + if (getconf((FILE *)NULL, (FILE *)NULL, &c) < 0) + return(-1); /* "can't happen" */ + + /* and mess with it as specified */ + if (size != 0) + c.tsize = size; + c.fieldsep = fs; + switch (cmap) { + case 0: + case '0': + case 'B': /* 2.10 compat */ + c.casemap = '0'; /* '\0' nicer, but '0' printable! */ + break; + case '=': + case 'b': /* 2.11 compat */ + c.casemap = '='; + break; + case 'C': + c.casemap = 'C'; + break; + case '?': + c.casemap = DEFCASE; + break; + default: + DEBUG(("dbzfresh case map `%c' unknown\n", cmap)); + return(-1); + } + switch ((int)tagmask) { + case 0: /* default */ + break; + case 1: /* no tags */ + c.tagshift = 0; + c.tagmask = 0; + c.tagenb = 0; + break; + default: + m = tagmask; + c.tagshift = 0; + while (!(m&01)) { + m >>= 1; + c.tagshift++; + } + c.tagmask = m; + c.tagenb = (m << 1) & ~m; + break; + } + + /* write it out */ + fn = enstring(name, dir); + if (fn == NULL) + return(-1); + f = fopen(fn, "w"); + free((POINTER)fn); + if (f == NULL) { + DEBUG(("dbzfresh: unable to write config\n")); + return(-1); + } + if (putconf(f, &c) < 0) { + (void) fclose(f); + return(-1); + } + if (fclose(f) == EOF) { + DEBUG(("dbzfresh: fclose failure\n")); + return(-1); + } + + /* create/truncate .pag */ + fn = enstring(name, pag); + if (fn == NULL) + return(-1); + f = fopen(fn, "w"); + free((POINTER)fn); + if (f == NULL) { + DEBUG(("dbzfresh: unable to create/truncate .pag file\n")); + return(-1); + } else + (void) fclose(f); + + /* and punt to dbminit for the hard work */ + return(dbminit(name)); +} + +/* + - dbzsize - what's a good table size to hold this many entries? + */ +long +dbzsize(contents) +long contents; /* 0 means what's the default */ +{ + register long n; + + if (contents <= 0) { /* foulup or default inquiry */ + DEBUG(("dbzsize: preposterous input (%ld)\n", contents)); + return(DEFSIZE); + } + n = (contents/2)*3; /* try to keep table at most 2/3 full */ + if (!(n&01)) /* make it odd */ + n++; + DEBUG(("dbzsize: tentative size %ld\n", n)); + while (!isprime(n)) /* and look for a prime */ + n += 2; + DEBUG(("dbzsize: final size %ld\n", n)); + + return(n); +} + +/* + - isprime - is a number prime? + * + * This is not a terribly efficient approach. + */ +static int /* predicate */ +isprime(x) +register long x; +{ + static int quick[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 0 }; + register int *ip; + register long div; + register long stop; + + /* hit the first few primes quickly to eliminate easy ones */ + /* this incidentally prevents ridiculously small tables */ + for (ip = quick; (div = *ip) != 0; ip++) + if (x%div == 0) { + DEBUG(("isprime: quick result on %ld\n", (long)x)); + return(0); + } + + /* approximate square root of x */ + for (stop = x; x/stop < stop; stop >>= 1) + continue; + stop <<= 1; + + /* try odd numbers up to stop */ + for (div = *--ip; div < stop; div += 2) + if (x%div == 0) + return(0); + + return(1); +} + +/* + - dbzagain - set up a new database to be a rebuild of an old one + */ +int /* 0 success, -1 failure */ +dbzagain(name, oldname) +char *name; /* base name; .dir and .pag must exist */ +char *oldname; /* base name; all must exist */ +{ + register char *fn; + struct dbzconfig c; + register int i; + register long top; + register FILE *f; + register int newtable; + register of_t newsize; + struct stat sb; + register of_t m; + + if (pagf != NULL) { + DEBUG(("dbzagain: database already open\n")); + return(-1); + } + + /* pick up the old configuration */ + fn = enstring(oldname, dir); + if (fn == NULL) + return(-1); + f = fopen(fn, "r"); + free((POINTER)fn); + if (f == NULL) { + DEBUG(("dbzagain: cannot open old .dir file\n")); + return(-1); + } + i = getconf(f, (FILE *)NULL, &c); + (void) fclose(f); + if (i < 0) { + DEBUG(("dbzagain: getconf failed\n")); + return(-1); + } + + /* calculate tagging from old file */ + if (stat(oldname, &sb) != -1) { + for (m = 1, i = 0; m < sb.st_size; i++, m <<= 1) + continue; + + /* if we had more tags than the default, use the new data */ + if ((c.tagmask | c.tagenb) && m > (1 << TAGSHIFT)) { + c.tagshift = i; + c.tagmask = (~(unsigned long)0) >> (i + 1); + c.tagenb = (c.tagmask << 1) & ~c.tagmask; + } + } + + /* tinker with it */ + top = 0; + newtable = 0; + for (i = 0; i < NUSEDS; i++) { + if (top < c.used[i]) + top = c.used[i]; + if (c.used[i] == 0) + newtable = 1; /* hasn't got full usage history yet */ + } + if (top == 0) { + DEBUG(("dbzagain: old table has no contents!\n")); + newtable = 1; + } + for (i = NUSEDS-1; i > 0; i--) + c.used[i] = c.used[i-1]; + c.used[0] = 0; + newsize = dbzsize(top); + if (!newtable || newsize > c.tsize) /* don't shrink new table */ + c.tsize = newsize; + + /* write it out */ + fn = enstring(name, dir); + if (fn == NULL) + return(-1); + f = fopen(fn, "w"); + free((POINTER)fn); + if (f == NULL) { + DEBUG(("dbzagain: unable to write new .dir\n")); + return(-1); + } + i = putconf(f, &c); + (void) fclose(f); + if (i < 0) { + DEBUG(("dbzagain: putconf failed\n")); + return(-1); + } + + /* create/truncate .pag */ + fn = enstring(name, pag); + if (fn == NULL) + return(-1); + f = fopen(fn, "w"); + free((POINTER)fn); + if (f == NULL) { + DEBUG(("dbzagain: unable to create/truncate .pag file\n")); + return(-1); + } else + (void) fclose(f); + + /* and let dbminit do the work */ + return(dbminit(name)); +} + +/* + - dbminit - open a database, creating it (using defaults) if necessary + * + * We try to leave errno set plausibly, to the extent that underlying + * functions permit this, since many people consult it if dbminit() fails. + */ +int /* 0 success, -1 failure */ +dbminit(name) +char *name; +{ + register int i; + register size_t s; + register char *dirfname; + register char *pagfname; + + if (pagf != NULL) { + DEBUG(("dbminit: dbminit already called once\n")); + errno = 0; + return(-1); + } + + /* open the .dir file */ + dirfname = enstring(name, dir); + if (dirfname == NULL) + return(-1); + dirf = fopen(dirfname, "r+"); + if (dirf == NULL) { + dirf = fopen(dirfname, "r"); + dirronly = 1; + } else + dirronly = 0; + free((POINTER)dirfname); + if (dirf == NULL) { + DEBUG(("dbminit: can't open .dir file\n")); + return(-1); + } + CloseOnExec((int)fileno(dirf), 1); + + /* open the .pag file */ + pagfname = enstring(name, pag); + if (pagfname == NULL) { + (void) fclose(dirf); + return(-1); + } + pagf = fopen(pagfname, "r+b"); + if (pagf == NULL) { + pagf = fopen(pagfname, "rb"); + if (pagf == NULL) { + DEBUG(("dbminit: .pag open failed\n")); + (void) fclose(dirf); + free((POINTER)pagfname); + return(-1); + } + pagronly = 1; + } else if (dirronly) + pagronly = 1; + else + pagronly = 0; + if (pagf != NULL) + CloseOnExec((int)fileno(pagf), 1); +#ifdef NOBUFFER + /* + * B News does not do adequate locking on its database accesses. + * Why it doesn't get into trouble using dbm is a mystery. In any + * case, doing unbuffered i/o does not cure the problem, but does + * enormously reduce its incidence. + */ + (void) setbuf(pagf, (char *)NULL); +#else +#ifdef _IOFBF + (void) setvbuf(pagf, (char *)pagbuf, _IOFBF, sizeof(pagbuf)); +#endif +#endif + pagpos = -1; + /* don't free pagfname, need it below */ + + /* open the base file */ + basef = fopen(name, "r"); + if (basef == NULL) { + DEBUG(("dbminit: basefile open failed\n")); + basefname = enstring(name, ""); + if (basefname == NULL) { + (void) fclose(pagf); + (void) fclose(dirf); + free((POINTER)pagfname); + pagf = NULL; + return(-1); + } + } else + basefname = NULL; + if (basef != NULL) + CloseOnExec((int)fileno(basef), 1); +#ifdef _IOFBF + if (basef != NULL) + (void) setvbuf(basef, basebuf, _IOFBF, sizeof(basebuf)); +#endif + + /* pick up configuration */ + if (getconf(dirf, pagf, &conf) < 0) { + DEBUG(("dbminit: getconf failure\n")); + (void) fclose(basef); + (void) fclose(pagf); + (void) fclose(dirf); + free((POINTER)pagfname); + pagf = NULL; + errno = EDOM; /* kind of a kludge, but very portable */ + return(-1); + } + tagbits = conf.tagmask << conf.tagshift; + taghere = conf.tagenb << conf.tagshift; + tagboth = tagbits | taghere; + mybytemap(mybmap); + bytesame = 1; + for (i = 0; i < SOF; i++) + if (mybmap[i] != conf.bytemap[i]) + bytesame = 0; + + /* get first table into core, if it looks desirable and feasible */ + s = (size_t)conf.tsize * SOF; + if (incore && (of_t)(s/SOF) == conf.tsize) { + bufpagf = fopen(pagfname, (pagronly) ? "rb" : "r+b"); + if (bufpagf != NULL) { + corepag = getcore(bufpagf); + CloseOnExec((int)fileno(bufpagf), 1); + } + } else { + bufpagf = NULL; + corepag = NULL; + } + free((POINTER)pagfname); + + /* misc. setup */ + crcinit(); + written = 0; + prevp = FRESH; + DEBUG(("dbminit: succeeded\n")); + return(0); +} + +/* + - enstring - concatenate two strings into a malloced area + */ +static char * /* NULL if malloc fails */ +enstring(s1, s2) +char *s1; +char *s2; +{ + register char *p; + + p = malloc((size_t)strlen(s1) + (size_t)strlen(s2) + 1); + if (p != NULL) { + (void) strcpy(p, s1); + (void) strcat(p, s2); + } else { + DEBUG(("enstring(%s, %s) out of memory\n", s1, s2)); + } + return(p); +} + +/* + - dbmclose - close a database + */ +int +dbmclose() +{ + register int ret = 0; + + if (pagf == NULL) { + DEBUG(("dbmclose: not opened!\n")); + return(-1); + } + + if (fclose(pagf) == EOF) { + DEBUG(("dbmclose: fclose(pagf) failed\n")); + ret = -1; + } + pagf = basef; /* ensure valid pointer; dbzsync checks it */ + if (dbzsync() < 0) + ret = -1; + if (bufpagf != NULL && fclose(bufpagf) == EOF) { + DEBUG(("dbmclose: fclose(bufpagf) failed\n")); + ret = -1; + } + if (corepag != NULL) +#ifdef MMAP + if (munmap((caddr_t)corepag, (int)conf.tsize * SOF) == -1) { + DEBUG(("dbmclose: munmap failed\n")); + ret = -1; + } +#else + free((POINTER)corepag); +#endif + corepag = NULL; + if (basef) { + if (fclose(basef) == EOF) { + DEBUG(("dbmclose: fclose(basef) failed\n")); + ret = -1; + } + } + if (basefname != NULL) + free((POINTER)basefname); + basef = NULL; + pagf = NULL; + if (fclose(dirf) == EOF) { + DEBUG(("dbmclose: fclose(dirf) failed\n")); + ret = -1; + } + + DEBUG(("dbmclose: %s\n", (ret == 0) ? "succeeded" : "failed")); + return(ret); +} + +/* + - dbzsync - push all in-core data out to disk + */ +int +dbzsync() +{ + register int ret = 0; + + if (pagf == NULL) { + DEBUG(("dbzsync: not opened!\n")); + return(-1); + } + if (!written) + return(0); + +#ifndef MMAP + if (corepag != NULL && !writethrough) { + if (putcore(corepag, bufpagf) < 0) { + DEBUG(("dbzsync: putcore failed\n")); + ret = -1; + } + } +#endif + if (!conf.olddbz) + if (putconf(dirf, &conf) < 0) + ret = -1; + + DEBUG(("dbzsync: %s\n", (ret == 0) ? "succeeded" : "failed")); + return(ret); +} + +/* + - dbzcancel - cancel writing of in-core data + * Mostly for use from child processes. + * Note that we don't need to futz around with stdio buffers, because we + * always fflush them immediately anyway and so they never have stale data. + */ +int +dbzcancel() +{ + if (pagf == NULL) { + DEBUG(("dbzcancel: not opened!\n")); + return(-1); + } + + written = 0; + return(0); +} + +/* + - dbzfetch - fetch() with case mapping built in + */ +datum +dbzfetch(key) +datum key; +{ + char buffer[DBZMAXKEY + 1]; + datum mappedkey; + register size_t keysize; + + DEBUG(("dbzfetch: (%s)\n", key.dptr)); + + /* Key is supposed to be less than DBZMAXKEY */ + keysize = key.dsize; + if (keysize >= DBZMAXKEY) { + keysize = DBZMAXKEY; + DEBUG(("keysize is %d - truncated to %d\n", key.dsize, DBZMAXKEY)); + } + + mappedkey.dptr = mapcase(buffer, key.dptr, keysize); + buffer[keysize] = '\0'; /* just a debug aid */ + mappedkey.dsize = keysize; + + return(fetch(mappedkey)); +} + +/* + - fetch - get an entry from the database + * + * Disgusting fine point, in the name of backward compatibility: if the + * last character of "key" is a NUL, that character is (effectively) not + * part of the comparison against the stored keys. + */ +datum /* dptr NULL, dsize 0 means failure */ +fetch(key) +datum key; +{ + char buffer[DBZMAXKEY + 1]; + static of_t key_ptr; /* return value points here */ + datum output; + register size_t keysize; + register size_t cmplen; + register char *sepp; + + DEBUG(("fetch: (%s)\n", key.dptr)); + output.dptr = NULL; + output.dsize = 0; + prevp = FRESH; + + /* Key is supposed to be less than DBZMAXKEY */ + keysize = key.dsize; + if (keysize >= DBZMAXKEY) { + keysize = DBZMAXKEY; + DEBUG(("keysize is %d - truncated to %d\n", key.dsize, DBZMAXKEY)); + } + + if (pagf == NULL) { + DEBUG(("fetch: database not open!\n")); + return(output); + } else if (basef == NULL) { /* basef didn't exist yet */ + basef = latebase(); + if (basef == NULL) + return(output); + } + + cmplen = keysize; + sepp = &conf.fieldsep; + if (key.dptr[keysize-1] == '\0') { + cmplen--; + sepp = &buffer[keysize-1]; + } + start(&srch, &key, FRESH); + while ((key_ptr = search(&srch)) != NOTFOUND) { + DEBUG(("got 0x%lx\n", key_ptr)); + + /* fetch the key */ + if (fseek(basef, key_ptr, SEEK_SET) != 0) { + DEBUG(("fetch: seek failed\n")); + return(output); + } + if (fread((POINTER)buffer, 1, keysize, basef) != keysize) { + DEBUG(("fetch: read failed\n")); + return(output); + } + + /* try it */ + buffer[keysize] = '\0'; /* terminated for DEBUG */ + (void) mapcase(buffer, buffer, keysize); + DEBUG(("fetch: buffer (%s) looking for (%s) size = %d\n", + buffer, key.dptr, keysize)); + if (memcmp((POINTER)key.dptr, (POINTER)buffer, cmplen) == 0 && + (*sepp == conf.fieldsep || *sepp == '\0')) { + /* we found it */ + output.dptr = (char *)&key_ptr; + output.dsize = SOF; + DEBUG(("fetch: successful\n")); + return(output); + } + } + + /* we didn't find it */ + DEBUG(("fetch: failed\n")); + prevp = &srch; /* remember where we stopped */ + return(output); +} + +/* + - latebase - try to open a base file that wasn't there at the start + */ +static FILE * +latebase() +{ + register FILE *it; + + if (basefname == NULL) { + DEBUG(("latebase: name foulup\n")); + return(NULL); + } + it = fopen(basefname, "r"); + if (it == NULL) { + DEBUG(("latebase: still can't open base\n")); + } else { + DEBUG(("latebase: late open succeeded\n")); + free((POINTER)basefname); + basefname = NULL; +#ifdef _IOFBF + (void) setvbuf(it, basebuf, _IOFBF, sizeof(basebuf)); +#endif + } + if (it != NULL) + CloseOnExec((int)fileno(it), 1); + return(it); +} + +/* + - dbzstore - store() with case mapping built in + */ +int +dbzstore(key, data) +datum key; +datum data; +{ + char buffer[DBZMAXKEY + 1]; + datum mappedkey; + register size_t keysize; + + DEBUG(("dbzstore: (%s)\n", key.dptr)); + + /* Key is supposed to be less than DBZMAXKEY */ + keysize = key.dsize; + if (keysize >= DBZMAXKEY) { + DEBUG(("dbzstore: key size too big (%d)\n", key.dsize)); + return(-1); + } + + mappedkey.dptr = mapcase(buffer, key.dptr, keysize); + buffer[keysize] = '\0'; /* just a debug aid */ + mappedkey.dsize = keysize; + + return(store(mappedkey, data)); +} + +/* + - store - add an entry to the database + */ +int /* 0 success, -1 failure */ +store(key, data) +datum key; +datum data; +{ + of_t value; + + if (pagf == NULL) { + DEBUG(("store: database not open!\n")); + return(-1); + } else if (basef == NULL) { /* basef didn't exist yet */ + basef = latebase(); + if (basef == NULL) + return(-1); + } + if (pagronly) { + DEBUG(("store: database open read-only\n")); + return(-1); + } + if (data.dsize != SOF) { + DEBUG(("store: value size wrong (%d)\n", data.dsize)); + return(-1); + } + if (key.dsize >= DBZMAXKEY) { + DEBUG(("store: key size too big (%d)\n", key.dsize)); + return(-1); + } + + /* copy the value in to ensure alignment */ + (void) memcpy((POINTER)&value, (POINTER)data.dptr, SOF); + DEBUG(("store: (%s, %ld)\n", key.dptr, (long)value)); + if (!okayvalue(value)) { + DEBUG(("store: reserved bit or overflow in 0x%lx\n", value)); + return(-1); + } + + /* find the place, exploiting previous search if possible */ + start(&srch, &key, prevp); + while (search(&srch) != NOTFOUND) + continue; + + prevp = FRESH; + conf.used[0]++; + DEBUG(("store: used count %ld\n", conf.used[0])); + written = 1; + return(set(&srch, value)); +} + +/* + - dbzincore - control attempts to keep .pag file in core + */ +int /* old setting */ +dbzincore(value) +int value; +{ + register int old = incore; + +#ifndef MMAP + incore = value; +#endif + return(old); +} + +/* + - dbzwritethrough - write through the pag file in core + */ +int /* old setting */ +dbzwritethrough(value) +int value; +{ + register int old = writethrough; + + writethrough = value; + return(old); +} + +/* + - dbztagmask - calculate the correct tagmask for the given base file size + */ +long +dbztagmask(size) +register long size; +{ + register long m; + register long tagmask; + register int i; + + if (size <= 0) + return(0L); /* silly size */ + + for (m = 1, i = 0; m < size; i++, m <<= 1) + continue; + + if (m < (1 << TAGSHIFT)) + return(0L); /* not worth tagging */ + + tagmask = (~(unsigned long)0) >> (i + 1); + tagmask = tagmask << i; + return(tagmask); +} + +/* + - getconf - get configuration from .dir file + */ +static int /* 0 success, -1 failure */ +getconf(df, pf, cp) +register FILE *df; /* NULL means just give me the default */ +register FILE *pf; /* NULL means don't care about .pag */ +register struct dbzconfig *cp; +{ + register int c; + register int i; + int err = 0; + + c = (df != NULL) ? getc(df) : EOF; + if (c == EOF) { /* empty file, no configuration known */ + cp->olddbz = 0; + if (df != NULL && pf != NULL && getc(pf) != EOF) + cp->olddbz = 1; + cp->tsize = DEFSIZE; + cp->fieldsep = '\t'; + for (i = 0; i < NUSEDS; i++) + cp->used[i] = 0; + cp->valuesize = SOF; + mybytemap(cp->bytemap); + cp->casemap = DEFCASE; + cp->tagenb = TAGENB; + cp->tagmask = TAGMASK; + cp->tagshift = TAGSHIFT; + DEBUG(("getconf: defaults (%ld, %c, (0x%lx/0x%lx<<%d))\n", + cp->tsize, cp->casemap, cp->tagenb, + cp->tagmask, cp->tagshift)); + return(0); + } + (void) ungetc(c, df); + + /* first line, the vital stuff */ + if (getc(df) != 'd' || getc(df) != 'b' || getc(df) != 'z') + err = -1; + if (getno(df, &err) != dbzversion) + err = -1; + cp->tsize = getno(df, &err); + cp->fieldsep = (int)getno(df, &err); + while ((c = getc(df)) == ' ') + continue; + cp->casemap = c; + cp->tagenb = getno(df, &err); + cp->tagmask = getno(df, &err); + cp->tagshift = getno(df, &err); + cp->valuesize = getno(df, &err); + if (cp->valuesize != SOF) { + DEBUG(("getconf: wrong of_t size (%d)\n", cp->valuesize)); + err = -1; + cp->valuesize = SOF; /* to protect the loops below */ + } + for (i = 0; i < cp->valuesize; i++) + cp->bytemap[i] = getno(df, &err); + if (getc(df) != '\n') + err = -1; +#ifdef DBZDEBUG + DEBUG(("size %ld, sep %d, cmap %c, tags 0x%lx/0x%lx<<%d, ", cp->tsize, + cp->fieldsep, cp->casemap, cp->tagenb, cp->tagmask, + cp->tagshift)); + DEBUG(("bytemap (%d)", cp->valuesize)); + for (i = 0; i < cp->valuesize; i++) { + DEBUG((" %d", cp->bytemap[i])); + } + DEBUG(("\n")); +#endif + + /* second line, the usages */ + for (i = 0; i < NUSEDS; i++) + cp->used[i] = getno(df, &err); + if (getc(df) != '\n') + err = -1; + DEBUG(("used %ld %ld %ld...\n", cp->used[0], cp->used[1], cp->used[2])); + + if (err < 0) { + DEBUG(("getconf error\n")); + return(-1); + } + return(0); +} + +/* + - getno - get a long + */ +static long +getno(f, ep) +FILE *f; +int *ep; +{ + register char *p; +# define MAXN 50 + char getbuf[MAXN]; + register int c; + + while ((c = getc(f)) == ' ') + continue; + if (c == EOF || c == '\n') { + DEBUG(("getno: missing number\n")); + *ep = -1; + return(0); + } + p = getbuf; + *p++ = c; + while ((c = getc(f)) != EOF && c != '\n' && c != ' ') + if (p < &getbuf[MAXN-1]) + *p++ = c; + if (c == EOF) { + DEBUG(("getno: EOF\n")); + *ep = -1; + } else + (void) ungetc(c, f); + *p = '\0'; + + if (strspn(getbuf, "-1234567890") != strlen(getbuf)) { + DEBUG(("getno: `%s' non-numeric\n", getbuf)); + *ep = -1; + } + return(atol(getbuf)); +} + +/* + - putconf - write configuration to .dir file + */ +static int /* 0 success, -1 failure */ +putconf(f, cp) +register FILE *f; +register struct dbzconfig *cp; +{ + register int i; + register int ret = 0; + + if (fseek(f, (of_t)0, SEEK_SET) != 0) { + DEBUG(("fseek failure in putconf\n")); + ret = -1; + } + (void) fprintf(f, "dbz %d %ld %d %c %ld %ld %d %d", dbzversion, cp->tsize, + cp->fieldsep, cp->casemap, cp->tagenb, + cp->tagmask, cp->tagshift, cp->valuesize); + for (i = 0; i < cp->valuesize; i++) + (void) fprintf(f, " %d", cp->bytemap[i]); + (void) fprintf(f, "\n"); + for (i = 0; i < NUSEDS; i++) + (void) fprintf(f, "%ld%c", cp->used[i], (i < NUSEDS-1) ? ' ' : '\n'); + + (void) fflush(f); + if (ferror(f)) + ret = -1; + + DEBUG(("putconf status %d\n", ret)); + return(ret); +} + +/* + - getcore - try to set up an in-core copy of .pag file + */ +static of_t * /* pointer to copy, or NULL */ +getcore(f) +FILE *f; +{ + register of_t *p; + register size_t i; + register size_t nread; + register char *it; +#ifdef MMAP + struct stat st; + + if (fstat(fileno(f), &st) == -1) { + DEBUG(("getcore: fstat failed\n")); + return(NULL); + } + if (((size_t)conf.tsize * SOF) > st.st_size) { + /* file too small; extend it */ + if (ftruncate((int)fileno(f), conf.tsize * SOF) == -1) { + DEBUG(("getcore: ftruncate failed\n")); + return(NULL); + } + } + it = mmap((caddr_t)0, (size_t)conf.tsize * SOF, + pagronly ? PROT_READ : PROT_WRITE | PROT_READ, MAP__ARG, + (int)fileno(f), (off_t)0); + if (it == (char *)-1) { + DEBUG(("getcore: mmap failed\n")); + return(NULL); + } +#ifdef MC_ADVISE + /* not present in all versions of mmap() */ + madvise(it, (size_t)conf.tsize * SOF, MADV_RANDOM); +#endif +#else + it = malloc((size_t)conf.tsize * SOF); + if (it == NULL) { + DEBUG(("getcore: malloc failed\n")); + return(NULL); + } + + nread = fread((POINTER)it, SOF, (size_t)conf.tsize, f); + if (ferror(f)) { + DEBUG(("getcore: read failed\n")); + free((POINTER)it); + return(NULL); + } + + /* NOSTRICT *//* Possible pointer alignment problem */ + p = (of_t *)it + nread; + i = (size_t)conf.tsize - nread; + while (i-- > 0) + *p++ = VACANT; +#endif + /* NOSTRICT *//* Possible pointer alignment problem */ + return((of_t *)it); +} + +#ifndef MMAP +/* + - putcore - try to rewrite an in-core table + */ +static int /* 0 okay, -1 fail */ +putcore(tab, f) +of_t *tab; +FILE *f; +{ + if (fseek(f, (of_t)0, SEEK_SET) != 0) { + DEBUG(("fseek failure in putcore\n")); + return(-1); + } + (void) fwrite((POINTER)tab, SOF, (size_t)conf.tsize, f); + (void) fflush(f); + return((ferror(f)) ? -1 : 0); +} +#endif + +/* + - start - set up to start or restart a search + */ +static void +start(sp, kp, osp) +register struct searcher *sp; +register datum *kp; +register struct searcher *osp; /* may be FRESH, i.e. NULL */ +{ + register long h; + + h = hash(kp->dptr, kp->dsize); + if (osp != FRESH && osp->hash == h) { + if (sp != osp) + *sp = *osp; + DEBUG(("search restarted\n")); + } else { + sp->hash = h; + sp->tag = MKTAG(h / conf.tsize); + DEBUG(("tag 0x%lx\n", sp->tag)); + sp->place = h % conf.tsize; + sp->tabno = 0; + sp->run = (conf.olddbz) ? conf.tsize : MAXRUN; + sp->aborted = 0; + } + sp->seen = 0; +} + +/* + - search - conduct part of a search + */ +static of_t /* NOTFOUND if we hit VACANT or error */ +search(sp) +register struct searcher *sp; +{ + register of_t dest; + register of_t value; + of_t val; /* buffer for value (can't fread register) */ + register of_t place; + + if (sp->aborted) + return(NOTFOUND); + + for (;;) { + /* determine location to be examined */ + place = sp->place; + if (sp->seen) { + /* go to next location */ + if (--sp->run <= 0) { + sp->tabno++; + sp->run = MAXRUN; + } + place = (place+1)%conf.tsize + sp->tabno*conf.tsize; + sp->place = place; + } else + sp->seen = 1; /* now looking at current location */ + DEBUG(("search @ %ld\n", place)); + + /* get the tagged value */ + if (corepag != NULL && place < conf.tsize) { + DEBUG(("search: in core\n")); + value = MAPIN(corepag[place]); + } else { + /* seek, if necessary */ + dest = place * SOF; + if (pagpos != dest) { + if (fseek(pagf, dest, SEEK_SET) != 0) { + DEBUG(("search: seek failed\n")); + pagpos = -1; + sp->aborted = 1; + return(NOTFOUND); + } + pagpos = dest; + } + + /* read it */ + if (fread((POINTER)&val, sizeof(val), 1, pagf) == 1) + value = MAPIN(val); + else if (ferror(pagf)) { + DEBUG(("search: read failed\n")); + pagpos = -1; + sp->aborted = 1; + return(NOTFOUND); + } else + value = VACANT; + + /* and finish up */ + pagpos += sizeof(val); + } + + /* vacant slot is always cause to return */ + if (value == VACANT) { + DEBUG(("search: empty slot\n")); + return(NOTFOUND); + }; + + /* check the tag */ + value = UNBIAS(value); + DEBUG(("got 0x%lx\n", value)); + if (!HASTAG(value)) { + DEBUG(("tagless\n")); + return(value); + } else if (TAG(value) == sp->tag) { + DEBUG(("match\n")); + return(NOTAG(value)); + } else { + DEBUG(("mismatch 0x%lx\n", TAG(value))); + } + } + /* NOTREACHED */ +} + +/* + - okayvalue - check that a value can be stored + */ +static int /* predicate */ +okayvalue(value) +of_t value; +{ + if (HASTAG(value)) + return(0); +#ifdef OVERFLOW + if (value == LONG_MAX) /* BIAS() and UNBIAS() will overflow */ + return(0); +#endif + return(1); +} + +/* + - set - store a value into a location previously found by search + */ +static int /* 0 success, -1 failure */ +set(sp, value) +register struct searcher *sp; +of_t value; +{ + register of_t place = sp->place; + register of_t v = value; + + if (sp->aborted) + return(-1); + + if (CANTAG(v) && !conf.olddbz) { + v |= sp->tag | taghere; + if (v != UNBIAS(VACANT)) /* BIAS(v) won't look VACANT */ +#ifdef OVERFLOW + if (v != LONG_MAX) /* and it won't overflow */ +#endif + value = v; + } + DEBUG(("tagged value is 0x%lx\n", value)); + value = BIAS(value); + value = MAPOUT(value); + + /* If we have the index file in memory, use it */ + if (corepag != NULL && place < conf.tsize) { + corepag[place] = value; + DEBUG(("set: incore\n")); +#ifdef MMAP + return(0); +#else + if (!writethrough) + return(0); +#endif + } + + /* seek to spot */ + pagpos = -1; /* invalidate position memory */ + if (fseek(pagf, (of_t)(place * SOF), SEEK_SET) != 0) { + DEBUG(("set: seek failed\n")); + sp->aborted = 1; + return(-1); + } + + /* write in data */ + if (fwrite((POINTER)&value, SOF, 1, pagf) != 1) { + DEBUG(("set: write failed\n")); + sp->aborted = 1; + return(-1); + } + /* fflush improves robustness, and buffer re-use is rare anyway */ + if (fflush(pagf) == EOF) { + DEBUG(("set: fflush failed\n")); + sp->aborted = 1; + return(-1); + } + + DEBUG(("set: succeeded\n")); + return(0); +} + +/* + - mybytemap - determine this machine's byte map + * + * A byte map is an array of ints, sizeof(of_t) of them. The 0th int + * is the byte number of the high-order byte in my of_t, and so forth. + */ +static void +mybytemap(map) +int map[]; /* -> int[SOF] */ +{ + union { + of_t o; + char c[SOF]; + } u; + register int *mp = &map[SOF]; + register int ntodo; + register int i; + + u.o = 1; + for (ntodo = (int)SOF; ntodo > 0; ntodo--) { + for (i = 0; i < SOF; i++) + /* SUPPRESS 112 *//* Retrieving char where long is stored */ + if (u.c[i] != 0) + break; + if (i == SOF) { + /* trouble -- set it to *something* consistent */ + DEBUG(("mybytemap: nonexistent byte %d!!!\n", ntodo)); + for (i = 0; i < SOF; i++) + map[i] = i; + return; + } + DEBUG(("mybytemap: byte %d\n", i)); + *--mp = i; + /* SUPPRESS 112 *//* Retrieving char where long is stored */ + while (u.c[i] != 0) + u.o <<= 1; + } +} + +/* + - bytemap - transform an of_t from byte ordering map1 to map2 + */ +static of_t /* transformed result */ +bytemap(ino, map1, map2) +of_t ino; +int *map1; +int *map2; +{ + union oc { + of_t o; + char c[SOF]; + }; + union oc in; + union oc out; + register int i; + + in.o = ino; + for (i = 0; i < SOF; i++) + out.c[map2[i]] = in.c[map1[i]]; + return(out.o); +} + +/* + * This is a simplified version of the pathalias hashing function. + * Thanks to Steve Belovin and Peter Honeyman + * + * hash a string into a long int. 31 bit crc (from andrew appel). + * the crc table is computed at run time by crcinit() -- we could + * precompute, but it takes 1 clock tick on a 750. + * + * This fast table calculation works only if POLY is a prime polynomial + * in the field of integers modulo 2. Since the coefficients of a + * 32-bit polynomial won't fit in a 32-bit word, the high-order bit is + * implicit. IT MUST ALSO BE THE CASE that the coefficients of orders + * 31 down to 25 are zero. Happily, we have candidates, from + * E. J. Watson, "Primitive Polynomials (Mod 2)", Math. Comp. 16 (1962): + * x^32 + x^7 + x^5 + x^3 + x^2 + x^1 + x^0 + * x^31 + x^3 + x^0 + * + * We reverse the bits to get: + * 111101010000000000000000000000001 but drop the last 1 + * f 5 0 0 0 0 0 0 + * 010010000000000000000000000000001 ditto, for 31-bit crc + * 4 8 0 0 0 0 0 0 + */ + +#define POLY 0x48000000L /* 31-bit polynomial (avoids sign problems) */ + +static long CrcTable[128]; + +/* + - crcinit - initialize tables for hash function + */ +static void +crcinit() +{ + register int i, j; + register long sum; + + for (i = 0; i < 128; ++i) { + sum = 0L; + for (j = 7 - 1; j >= 0; --j) + if (i & (1 << j)) + sum ^= POLY >> j; + CrcTable[i] = sum; + } + DEBUG(("crcinit: done\n")); +} + +/* + - hash - Honeyman's nice hashing function + */ +static long +hash(name, size) +register char *name; +register int size; +{ + register long sum = 0L; + + while (size--) { + sum = (sum >> 7) ^ CrcTable[(sum ^ (*name++)) & 0x7f]; + } + DEBUG(("hash: returns (%ld)\n", sum)); + return(sum); +} + +/* + * case-mapping stuff + * + * Borrowed from C News, by permission of the authors. Somewhat modified. + * + * We exploit the fact that we are dealing only with headers here, and + * headers are limited to the ASCII characters by RFC822. It is barely + * possible that we might be dealing with a translation into another + * character set, but in particular it's very unlikely for a header + * character to be outside -128..255. + * + * Life would be a whole lot simpler if tolower() could safely and portably + * be applied to any char. + */ + +#define OFFSET 128 /* avoid trouble with negative chars */ + +/* must call casencmp before invoking TOLOW... */ +#define TOLOW(c) (cmap[(c)+OFFSET]) + +/* ...but the use of it in CISTREQN is safe without the preliminary call (!) */ +/* CISTREQN is an optimised case-insensitive strncmp(a,b,n)==0; n > 0 */ +#define CISTREQN(a, b, n) \ + (TOLOW((a)[0]) == TOLOW((b)[0]) && casencmp(a, b, n) == 0) + +#define MAPSIZE (256+OFFSET) +static char cmap[MAPSIZE]; /* relies on init to '\0' */ +static int mprimed = 0; /* has cmap been set up? */ + +/* + - mapprime - set up case-mapping stuff + */ +static void +mapprime() +{ + register char *lp; + register char *up; + register int c; + register int i; + static char lower[] = "abcdefghijklmnopqrstuvwxyz"; + static char upper[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + for (lp = lower, up = upper; *lp != '\0'; lp++, up++) { + c = *lp; + cmap[c+OFFSET] = c; + cmap[*up+OFFSET] = c; + } + for (i = 0; i < MAPSIZE; i++) + if (cmap[i] == '\0') + cmap[i] = (char)(i-OFFSET); + mprimed = 1; +} + +/* + - casencmp - case-independent strncmp + */ +static int /* < == > 0 */ +casencmp(s1, s2, len) +char *s1; +char *s2; +int len; +{ + register char *p1; + register char *p2; + register int n; + + if (!mprimed) + mapprime(); + + p1 = s1; + p2 = s2; + n = len; + while (--n >= 0 && *p1 != '\0' && TOLOW(*p1) == TOLOW(*p2)) { + p1++; + p2++; + } + if (n < 0) + return(0); + + /* + * The following case analysis is necessary so that characters + * which look negative collate low against normal characters but + * high against the end-of-string NUL. + */ + if (*p1 == '\0' && *p2 == '\0') + return(0); + else if (*p1 == '\0') + return(-1); + else if (*p2 == '\0') + return(1); + else + return(TOLOW(*p1) - TOLOW(*p2)); +} + +/* + - mapcase - do case-mapped copy + */ +static char * /* returns src or dst */ +mapcase(dst, src, siz) +char *dst; /* destination, used only if mapping needed */ +char *src; /* source; src == dst is legal */ +size_t siz; +{ + register char *s; + register char *d; + register char *c; /* case break */ + register char *e; /* end of source */ + + + c = cipoint(src, siz); + if (c == NULL) + return(src); + + if (!mprimed) + mapprime(); + s = src; + e = s + siz; + d = dst; + + while (s < c) + *d++ = *s++; + while (s < e) + *d++ = TOLOW(*s++); + + return(dst); +} + +/* + - cipoint - where in this message-ID does it become case-insensitive? + * + * The RFC822 code is not quite complete. Absolute, total, full RFC822 + * compliance requires a horrible parsing job, because of the arcane + * quoting conventions -- abc"def"ghi is not equivalent to abc"DEF"ghi, + * for example. There are three or four things that might occur in the + * domain part of a message-id that are case-sensitive. They don't seem + * to ever occur in real news, thank Cthulhu. (What? You were expecting + * a merciful and forgiving deity to be invoked in connection with RFC822? + * Forget it; none of them would come near it.) + */ +static char * /* pointer into s, or NULL for "nowhere" */ +cipoint(s, siz) +char *s; +size_t siz; +{ + register char *p; + static char post[] = "postmaster"; + static int plen = sizeof(post)-1; + + switch (conf.casemap) { + case '0': /* unmapped, sensible */ + return(NULL); + case 'C': /* C News, RFC 822 conformant (approx.) */ + p = memchr((POINTER)s, '@', siz); + if (p == NULL) /* no local/domain split */ + return(NULL); /* assume all local */ + if (p - (s+1) == plen && CISTREQN(s+1, post, plen)) { + /* crazy -- "postmaster" is case-insensitive */ + return(s); + } + return(p); + case '=': /* 2.11, neither sensible nor conformant */ + return(s); /* all case-insensitive */ + } + + DEBUG(("cipoint: unknown case mapping `%c'\n", conf.casemap)); + return(NULL); /* just leave it alone */ +} + +/* + - dbzdebug - control dbz debugging at run time + */ +#ifdef DBZDEBUG +int /* old value */ +dbzdebug(value) +int value; +{ + register int old = debug; + + debug = value; + return(old); +} +#endif diff --git a/innbbsd/dbz.h b/innbbsd/dbz.h new file mode 100644 index 00000000..3d7e8ed7 --- /dev/null +++ b/innbbsd/dbz.h @@ -0,0 +1,32 @@ +/* for dbm and dbz */ +typedef struct { + char *dptr; + int dsize; +} datum; + +/* standard dbm functions */ +extern int dbminit(); +extern datum fetch(); +extern int store(); +extern int delete(); /* not in dbz */ +extern datum firstkey(); /* not in dbz */ +extern datum nextkey(); /* not in dbz */ +extern int dbmclose(); /* in dbz, but not in old dbm */ + +/* new stuff for dbz */ +extern int dbzfresh(); +extern int dbzagain(); +extern datum dbzfetch(); +extern int dbzstore(); +extern int dbzsync(); +extern long dbzsize(); +extern int dbzincore(); +extern int dbzcancel(); +extern int dbzdebug(); + +/* + * In principle we could handle unlimited-length keys by operating a chunk + * at a time, but it's not worth it in practice. Setting a nice large + * bound on them simplifies the code and doesn't hurt anything. + */ +#define DBZMAXKEY 255 diff --git a/innbbsd/dbztool.c b/innbbsd/dbztool.c new file mode 100644 index 00000000..5318721b --- /dev/null +++ b/innbbsd/dbztool.c @@ -0,0 +1,88 @@ +#include <sys/file.h> +#include "his.h" + +#define DEBUG 1 +#undef DEBUG + +static datum content, inputkey, inputvalue; +static char dboutput[1025]; +static char dbinput[1025]; +static char valueinput[100]; + +enum {SUBJECT, FROM, NAME}; +char *DBfetch(key) +char *key; +{ + int i; + char *tail, *ptr; + if (key == NULL) return NULL; + sprintf(dbinput,"%.510s",key); + inputkey.dptr = dbinput; + inputkey.dsize = strlen(dbinput); + content.dptr = dboutput; + ptr = (char*)HISfilesfor(&inputkey,&content); + if (ptr == NULL) { + return NULL; + } + return ptr; +} + +DBstore(key,paths) +char *key; +char *paths; +{ + int i; + char *tail; + time_t now; + time(&now); + if (key == NULL) return -1; + sprintf(dbinput,"%.510s",key); + inputkey.dptr = dbinput; + inputkey.dsize = strlen(dbinput); + if (HISwrite(&inputkey, now, paths ) == FALSE) { + return -1; + } else { + return 0; + } +} + +int storeDB(mid,paths) +char *mid; +char *paths; +{ + char *key,*ptr; + int rel; + ptr = DBfetch(mid); + if (ptr != NULL) { + return 0; + } else { + return DBstore(mid , paths); + } +} + +my_mkdir (idir,mode) +char *idir; +int mode; +{ + char buffer[LEN]; + char *ptr, *dir = buffer; + struct stat st; + strncpy(dir, idir, LEN - 1); + for (;dir!=NULL && *dir;) { + ptr = (char*)strchr(dir,'/'); + if (ptr != NULL) { + *ptr = '\0'; + } + if (stat(dir,&st) != 0) { + if (mkdir(dir,mode) != 0 ) + return -1; + } + chdir(dir); + if (ptr != NULL) + dir = ptr +1; + else + dir = ptr; + } + return 0; +} + diff --git a/innbbsd/echobbslib.c b/innbbsd/echobbslib.c new file mode 100644 index 00000000..84d77de6 --- /dev/null +++ b/innbbsd/echobbslib.c @@ -0,0 +1,713 @@ +#if defined( LINUX ) +# include "innbbsconf.h" +# include "bbslib.h" +# include <varargs.h> +#else +# include <varargs.h> +# include "innbbsconf.h" +# include "bbslib.h" +#endif + +char INNBBSCONF[MAXPATHLEN]; +char INNDHOME[MAXPATHLEN]; +char HISTORY[MAXPATHLEN]; +char LOGFILE[MAXPATHLEN]; +char MYBBSID[MAXPATHLEN]; +char ECHOMAIL[MAXPATHLEN]; +char BBSFEEDS[MAXPATHLEN]; +char LOCALDAEMON[MAXPATHLEN]; + +int His_Maint_Min= HIS_MAINT_MIN; +int His_Maint_Hour= HIS_MAINT_HOUR; +int Expiredays = EXPIREDAYS; + +nodelist_t *NODELIST=NULL, **NODELIST_BYNODE=NULL; +newsfeeds_t *NEWSFEEDS=NULL, **NEWSFEEDS_BYBOARD=NULL; +static char *NODELIST_BUF, *NEWSFEEDS_BUF; +int NFCOUNT, NLCOUNT; +int LOCALNODELIST=0, NONENEWSFEEDS=0; + +#ifndef _PATH_BBSHOME +# define _PATH_BBSHOME "/u/staff/bbsroot/csie_util/bntpd/home" +#endif + +static FILE *bbslogfp; + +static int +verboseFlag=0; + +static char* +verboseFilename=NULL; +static char verbosename[MAXPATHLEN]; + +verboseon(filename) +char *filename; +{ + verboseFlag = 1; + if ( filename != NULL ) { + if (strchr(filename,'/') == NULL) { + sprintf(verbosename,"%s/innd/%s",BBSHOME,filename); + filename = verbosename; + } + } + verboseFilename = filename; +} +verboseoff() +{ + verboseFlag = 0; +} + +setverboseon() +{ + verboseFlag = 1; +} + +isverboselog() +{ + return verboseFlag; +} + +setverboseoff() +{ + verboseoff(); + if (bbslogfp != NULL) { + fclose(bbslogfp); + bbslogfp = NULL; + } +} + +verboselog(va_alist) +va_dcl +{ + va_list ap; + register char* fmt; + char datebuf[40]; + time_t now; + + if (verboseFlag == 0) return; + + va_start(ap); + + time(&now); + strftime(datebuf, sizeof(datebuf), "%b %d %X ", localtime(&now)); + + if (bbslogfp == NULL) { + if (verboseFilename != NULL) + bbslogfp = fopen(verboseFilename, "a"); + else + bbslogfp = fdopen(1, "a"); + } + if (bbslogfp == NULL) { va_end(ap); return; } + fmt = va_arg(ap, char *) ; + fprintf(bbslogfp,"%s[%d] ",datebuf, getpid()); + vfprintf(bbslogfp, fmt, ap); + fflush(bbslogfp); + va_end(ap); +} + +#ifdef PalmBBS +xbbslog(va_alist) +#else +bbslog(va_alist) +#endif +va_dcl +{ + va_list ap; + register char* fmt; + char datebuf[40]; + time_t now; + + va_start(ap); + + time(&now); + strftime(datebuf, sizeof(datebuf), "%b %d %X ", localtime(&now)); + + if (bbslogfp == NULL) { + bbslogfp = fopen(LOGFILE, "a"); + } + if (bbslogfp == NULL) { va_end(ap); return; } + fmt = va_arg(ap, char *) ; + fprintf(bbslogfp,"%s[%d] ",datebuf,getpid()); + vfprintf(bbslogfp, fmt, ap); + fflush(bbslogfp); + va_end(ap); +} + +initial_bbs(outgoing) +char *outgoing; +{ + FILE* FN; + struct stat st; + int fd, i; + char *bbsnameptr=NULL; + +/* reopen bbslog */ + if (bbslogfp != NULL) { + fclose(bbslogfp); + bbslogfp = NULL; + } + +#ifdef WITH_ECHOMAIL + init_echomailfp(); + init_bbsfeedsfp(); +#endif + + LOCALNODELIST=0, NONENEWSFEEDS =0; + sprintf(INNDHOME,"%s/innd",BBSHOME); + sprintf(HISTORY, "%s/history",INNDHOME); + sprintf(LOGFILE, "%s/bbslog",INNDHOME); + sprintf(ECHOMAIL,"%s/echomail.log",BBSHOME); + sprintf(LOCALDAEMON,"%s/.innbbsd",INNDHOME); + sprintf(INNBBSCONF,"%s/innbbs.conf",INNDHOME); + sprintf(BBSFEEDS,"%s/bbsfeeds.log",INNDHOME); + + if (isfile(INNBBSCONF)) { + FILE *conf; + char buffer[MAXPATHLEN]; + conf = fopen(INNBBSCONF,"r"); + if (conf != NULL) { + while (fgets( buffer, sizeof buffer, conf) != NULL) { + char *ptr, *front=NULL, *value=NULL, *value2=NULL, *value3=NULL; + if ( buffer[0] == '#' || buffer[0] == '\n') continue; + for ( front = buffer; *front && isspace(*front); front++); + for ( ptr = front; *ptr && !isspace(*ptr) ; ptr++) ; + if (*ptr == '\0') continue; + *ptr++ = '\0'; + for ( ; *ptr && isspace(*ptr) ; ptr++) ; + if (*ptr == '\0') continue; + value = ptr++; + for ( ; *ptr && !isspace(*ptr) ; ptr++) ; + if (*ptr) { + *ptr++ = '\0'; + for ( ; *ptr && isspace(*ptr) ; ptr++) ; + value2 = ptr++; + for ( ; *ptr && !isspace(*ptr) ; ptr++) ; + if (*ptr) { + *ptr++ = '\0'; + for ( ; *ptr && isspace(*ptr) ; ptr++) ; + value3 = ptr++; + for ( ; *ptr && !isspace(*ptr) ; ptr++) ; + if (*ptr) { + *ptr++ = '\0'; + } + } + } + if ( strcasecmp(front,"expiredays") == 0) { + Expiredays = atoi(value); + if (Expiredays < 0) { + Expiredays = EXPIREDAYS; + } + } else if ( strcasecmp(front,"expiretime") == 0) { + ptr = strchr(value,':'); + if (ptr == NULL) { + fprintf(stderr, "Syntax error in innbbs.conf\n"); + } else { + *ptr++ = '\0'; + His_Maint_Hour = atoi(value); + His_Maint_Min = atoi(ptr); + if (His_Maint_Hour < 0) + His_Maint_Hour = HIS_MAINT_HOUR; + if (His_Maint_Min < 0) + His_Maint_Min = HIS_MAINT_MIN; + } + } else if ( strcasecmp(front,"newsfeeds") == 0) { + if (strcmp(value,"none")==0) + NONENEWSFEEDS = 1; + } else if ( strcasecmp(front,"nodelist") == 0) { + if (strcmp(value,"local")==0) + LOCALNODELIST = 1; + } /*else if ( strcasecmp(front,"newsfeeds") == 0) { + printf("newsfeeds %s\n", value); + } else if ( strcasecmp(front,"nodelist") == 0) { + printf("nodelist %s\n", value); + } else if ( strcasecmp(front,"bbsname") == 0) { + printf("bbsname %s\n", value); + } */ + } + fclose(conf); + } + } + +#ifdef WITH_ECHOMAIL + bbsnameptr = (char*) fileglue("%s/bbsname.bbs",INNDHOME); + if ((FN = fopen( bbsnameptr ,"r" ))==NULL) { + fprintf(stderr,"can't open file %s\n", bbsnameptr); + return 0; + } + while ( fscanf(FN,"%s", MYBBSID) != EOF); + fclose(FN); + if( ! isdir(fileglue("%s/out.going",BBSHOME)) ) { + mkdir( (char*)fileglue("%s/out.going",BBSHOME), 0750 ); + } + if (NONENEWSFEEDS == 0) + readnffile(INNDHOME); + if (LOCALNODELIST == 0) { + if (readnlfile(INNDHOME, outgoing) != 0) return 0; + } + +#endif + return 1; +} + +static int +nf_byboardcmp(a,b) +newsfeeds_t **a, **b; +{ +/* + if (!a || !*a || !(*a)->board) return -1; + if (!b || !*b || !(*b)->board) return 1; +*/ + return strcasecmp((*a)->board, (*b)->board); +} + +static int +nfcmp(a,b) +newsfeeds_t *a, *b; +{ +/* + if (!a || !a->newsgroups) return -1; + if (!b || !b->newsgroups) return 1; +*/ + return strcasecmp(a->newsgroups, b->newsgroups); +} + +static int +nlcmp(a,b) +nodelist_t *a, *b; +{ +/* + if (!a || !a->host) return -1; + if (!b || !b->host) return 1; +*/ + return strcasecmp(a->host, b->host); +} + +static int +nl_bynodecmp(a,b) +nodelist_t **a, **b; +{ +/* + if (!a || !*a || !(*a)->node) return -1; + if (!b || !*b || !(*b)->node) return 1; +*/ + return strcasecmp((*a)->node, (*b)->node); +} + +/* read in newsfeeds.bbs and nodelist.bbs */ +readnlfile(inndhome, outgoing) +char *inndhome; +char *outgoing; +{ + FILE *fp; + char buff[1024]; + struct stat st; + int i, count, j; + char *ptr, *nodelistptr; + static lastcount=0; + + sprintf(buff,"%s/nodelist.bbs", inndhome); + fp = fopen(buff,"r"); + if (fp == NULL) { + fprintf(stderr,"open fail %s",buff); + return -1; + } + if (fstat(fileno(fp),&st) != 0) { + fprintf(stderr,"stat fail %s", buff); + return -1; + } + if (NODELIST_BUF == NULL) { + NODELIST_BUF = (char*) mymalloc( st.st_size +1); + } else { + NODELIST_BUF = (char*) myrealloc( NODELIST_BUF, st.st_size +1); + } + i = 0, count =0; + while (fgets(buff, sizeof buff, fp) != NULL) { + if (buff[0] == '#') continue; + if (buff[0] == '\n') continue; + strcpy(NODELIST_BUF+i, buff); + i += strlen(buff); + count ++; + } + fclose(fp); + if (NODELIST == NULL) { + NODELIST = (nodelist_t*) mymalloc(sizeof(nodelist_t) * (count+1)); + NODELIST_BYNODE = (nodelist_t**) mymalloc(sizeof(nodelist_t*) * (count+1)); + } else { + NODELIST = (nodelist_t*) myrealloc(NODELIST, sizeof(nodelist_t) * (count+1)); + NODELIST_BYNODE = (nodelist_t**) myrealloc(NODELIST_BYNODE, sizeof(nodelist_t*) * (count+1)); + } + for (i=lastcount; i< count; i++) { + NODELIST[i].feedfp = NULL; + } + lastcount = count; + NLCOUNT = 0; + for (ptr = NODELIST_BUF; (nodelistptr = (char*)strchr(ptr,'\n')) != NULL; ptr = nodelistptr +1, NLCOUNT++) { + char *nptr , *bptr, *pptr, *tptr; + *nodelistptr = '\0'; + NODELIST[NLCOUNT].host = ""; + NODELIST[NLCOUNT].exclusion = ""; + NODELIST[NLCOUNT].node = ""; + NODELIST[NLCOUNT].protocol = "IHAVE(119)"; + NODELIST[NLCOUNT].comments = ""; + NODELIST_BYNODE[NLCOUNT] = NODELIST+NLCOUNT; + for (nptr= ptr ;*nptr && isspace(*nptr); ) nptr++; + if (*nptr == '\0') { + bbslog("nodelist.bbs %d entry read error\n", NLCOUNT); + return -1; + } + /*NODELIST[NLCOUNT].id = nptr;*/ + NODELIST[NLCOUNT].node = nptr; + for (nptr++; *nptr && !isspace(*nptr); ) nptr++; + if (*nptr == '\0') { + bbslog("nodelist.bbs node %d entry read error\n", NLCOUNT); + return -1; + } + *nptr = '\0'; + if ((tptr = strchr(NODELIST[NLCOUNT].node,'/'))) { + *tptr = '\0'; + NODELIST[NLCOUNT].exclusion = tptr + 1; + } else { + NODELIST[NLCOUNT].exclusion = ""; + } + for (nptr++ ;*nptr && isspace(*nptr); ) nptr++; + if (*nptr == '\0') continue; + if (*nptr=='+' || *nptr=='-') { + NODELIST[NLCOUNT].feedtype = *nptr; + if (NODELIST[NLCOUNT].feedfp != NULL) { + fclose(NODELIST[NLCOUNT].feedfp); + } + if ( NODELIST[NLCOUNT].feedtype == '+') + if (outgoing != NULL) { + NODELIST[NLCOUNT].feedfp = fopen((char*)fileglue("%s/out.going/%s.%s",BBSHOME, NODELIST[NLCOUNT].node, outgoing),"a"); + } + nptr++; + } else { + NODELIST[NLCOUNT].feedtype = ' '; + } + NODELIST[NLCOUNT].host = nptr; + for (nptr++; *nptr && !isspace(*nptr); ) nptr++; + if (*nptr == '\0') { + continue; + } + *nptr = '\0'; + for (nptr++;*nptr && isspace(*nptr); ) nptr++; + if (*nptr == '\0') continue; + NODELIST[NLCOUNT].protocol = nptr; + for (nptr++; *nptr && !isspace(*nptr); ) nptr++; + if (*nptr == '\0') continue; + *nptr = '\0'; + for (nptr++;*nptr && strchr(" \t\r\n",*nptr); ) nptr++; + if (*nptr == '\0') continue; + NODELIST[NLCOUNT].comments = nptr; + } + qsort(NODELIST, NLCOUNT, sizeof(nodelist_t), nlcmp); + qsort(NODELIST_BYNODE, NLCOUNT, sizeof(nodelist_t*), nl_bynodecmp); + return 0; +} + +readnffile(inndhome) +char *inndhome; +{ + FILE *fp; + char buff[1024]; + struct stat st; + int i, count; + char *ptr, *newsfeedsptr; + + sprintf(buff,"%s/newsfeeds.bbs", inndhome); + fp = fopen(buff,"r"); + if (fp == NULL) { + fprintf(stderr,"open fail %s",buff); + return -1; + } + if (fstat(fileno(fp),&st) != 0) { + fprintf(stderr,"stat fail %s", buff); + return -1; + } + if (NEWSFEEDS_BUF == NULL) { + NEWSFEEDS_BUF = (char*) mymalloc( st.st_size +1); + } else { + NEWSFEEDS_BUF = (char*) myrealloc( NEWSFEEDS_BUF, st.st_size +1); + } + i = 0, count =0; + while (fgets(buff, sizeof buff, fp) != NULL) { + if (buff[0] == '#') continue; + if (buff[0] == '\n') continue; + strcpy(NEWSFEEDS_BUF+i, buff); + i += strlen(buff); + count ++; + } + fclose(fp); + if (NEWSFEEDS == NULL) { + NEWSFEEDS = (newsfeeds_t*) mymalloc(sizeof(newsfeeds_t) * (count+1)); + NEWSFEEDS_BYBOARD = (newsfeeds_t**) mymalloc(sizeof(newsfeeds_t*) * (count+1)); + } else { + NEWSFEEDS = (newsfeeds_t*) myrealloc(NEWSFEEDS, sizeof(newsfeeds_t) * (count+1)); + NEWSFEEDS_BYBOARD = (newsfeeds_t**) myrealloc(NEWSFEEDS_BYBOARD, sizeof(newsfeeds_t*) * (count+1)); + } + NFCOUNT = 0; + for (ptr = NEWSFEEDS_BUF; (newsfeedsptr = (char*)strchr(ptr,'\n')) != NULL; ptr = newsfeedsptr +1, NFCOUNT++) { + char *nptr , *bptr, *pptr; + *newsfeedsptr = '\0'; + NEWSFEEDS[NFCOUNT].newsgroups = ""; + NEWSFEEDS[NFCOUNT].board = ""; + NEWSFEEDS[NFCOUNT].path = NULL; + NEWSFEEDS_BYBOARD[NFCOUNT] = NEWSFEEDS+NFCOUNT; + for (nptr= ptr ;*nptr && isspace(*nptr); ) nptr++; + if (*nptr == '\0') continue; + NEWSFEEDS[NFCOUNT].newsgroups = nptr; + for (nptr++; *nptr && !isspace(*nptr); ) nptr++; + if (*nptr == '\0') continue; + *nptr = '\0'; + for (nptr++ ;*nptr && isspace(*nptr); ) nptr++; + if (*nptr == '\0') continue; + NEWSFEEDS[NFCOUNT].board = nptr; + for (nptr++; *nptr && !isspace(*nptr); ) nptr++; + if (*nptr == '\0') continue; + *nptr = '\0'; + for (nptr++;*nptr && isspace(*nptr); ) nptr++; + if (*nptr == '\0') continue; + NEWSFEEDS[NFCOUNT].path = nptr; + for (nptr++; *nptr && !strchr("\r\n",*nptr); ) nptr++; + /*if (*nptr == '\0') continue;*/ + *nptr = '\0'; + } + qsort(NEWSFEEDS, NFCOUNT, sizeof(newsfeeds_t), nfcmp); + qsort(NEWSFEEDS_BYBOARD, NFCOUNT, sizeof(newsfeeds_t*), nf_byboardcmp); +} + +newsfeeds_t *search_board(board) +char *board; +{ + newsfeeds_t nft, *nftptr, **find; + if (NONENEWSFEEDS) return NULL; + nft.board = board; + nftptr = &nft; + find = (newsfeeds_t**)bsearch((char*)&nftptr, NEWSFEEDS_BYBOARD, NFCOUNT, sizeof(newsfeeds_t*), nf_byboardcmp); + if (find != NULL) return *find; + return NULL; +} + +nodelist_t *search_nodelist_bynode(node) +char *node; +{ + nodelist_t nlt, *nltptr, **find; + if (LOCALNODELIST) return NULL; + nlt.node = node; + nltptr = ≮ + find = (nodelist_t**)bsearch((char*)&nltptr, NODELIST_BYNODE, NLCOUNT, sizeof(nodelist_t*), nl_bynodecmp); + if (find != NULL) return *find; + return NULL; +} + + +nodelist_t *search_nodelist(site, identuser) +char *site; +char *identuser; +{ + nodelist_t nlt, *find; + char buffer[1024]; + if (LOCALNODELIST) return NULL; + nlt.host = site; + find = (nodelist_t*)bsearch((char*)&nlt, NODELIST, NLCOUNT, sizeof(nodelist_t), nlcmp); + if (find == NULL && identuser != NULL) { + sprintf(buffer,"%s@%s", identuser, site); + nlt.host = buffer; + find = (nodelist_t*)bsearch((char*)&nlt, NODELIST, NLCOUNT, sizeof(nodelist_t), nlcmp); + } + return find; +} + +newsfeeds_t *search_group(newsgroup) +char *newsgroup; +{ + newsfeeds_t nft, *find; + if (NONENEWSFEEDS) return NULL; + nft.newsgroups = newsgroup; + find = (newsfeeds_t*)bsearch((char*)&nft, NEWSFEEDS, NFCOUNT, sizeof(newsfeeds_t), nfcmp); + return find; +} + +char *ascii_date(now) +time_t now; +{ + static char datebuf[40]; + /*time_t now; + time(&now);*/ + strftime(datebuf, sizeof(datebuf), "%d %b %Y %X GMT", gmtime(&now)); + return datebuf; +} + +char * +restrdup(ptr, string) +char *ptr; +char *string; +{ + int len ; + if (string == NULL) { + if (ptr != NULL) *ptr = '\0'; + return ptr; + } + len = strlen(string) + 1; + if (ptr != NULL) { + ptr = (char*)myrealloc(ptr, len); + } else + ptr = (char*)mymalloc(len); + strcpy(ptr, string); + return ptr; +} + + + +void * +mymalloc(size) +int size; +{ + char *ptr = (char*)malloc(size); + if (ptr == NULL) { + fprintf(stderr, "cant allocate memory\n"); + syslog(LOG_ERR, "cant allocate memory %m"); + exit(1); + } + return ptr; +} + +void * +myrealloc(optr, size) +void *optr; +int size; +{ + char *ptr = (char*)realloc(optr, size); + if (ptr == NULL) { + fprintf(stderr, "cant allocate memory\n"); + syslog(LOG_ERR, "cant allocate memory %m"); + exit(1); + } + return ptr; +} + +testandmkdir(dir) +char *dir; +{ + if (!isdir(dir)) { + char path[MAXPATHLEN+12]; + sprintf(path,"mkdir -p %s",dir); + system(path); + } +} + +static char splitbuf[2048]; +static char joinbuf[1024]; +#define MAXTOK 50 +static char* Splitptr[MAXTOK]; +char **split(line,pat) +char *line,*pat; +{ + char *p; + int i; + + for (i=0;i<MAXTOK;++i) Splitptr[i] = NULL; + strncpy(splitbuf,line,sizeof splitbuf - 1 ); + /*printf("%d %d\n",strlen(line),strlen(splitbuf));*/ + splitbuf[sizeof splitbuf - 1] = '\0'; + for (i=0,p=splitbuf;*p && i< MAXTOK -1 ;){ + for (Splitptr[i++]=p;*p && !strchr(pat,*p);p++); + if (*p=='\0') break; + for (*p++='\0'; *p && strchr(pat,*p);p++); + } + return Splitptr; +} + +char **BNGsplit(line) +char *line; +{ + char **ptr = split(line,","); + newsfeeds_t *nf1, *nf2; + char *n11, *n12, *n21, *n22; + int i,j; + for (i=0; ptr[i] != NULL; i++) { + nf1 = (newsfeeds_t*)search_group(ptr[i]); + for (j=i+1; ptr[j] != NULL; j++) { + if (strcmp(ptr[i],ptr[j])==0) { + *ptr[j] = '\0'; + continue; + } + nf2 = (newsfeeds_t*)search_group(ptr[j]); + if (nf1 && nf2) { + if (strcmp(nf1->board,nf2->board)==0) { + *ptr[j] = '\0'; + continue; + } + for (n11 = nf1->board, n12 = (char*)strchr(n11,','); + n11 && *n11 ; n12 = (char*) strchr(n11,',')) { + if (n12) *n12 = '\0'; + for (n21 = nf2->board, n22 = (char*)strchr(n21,','); + n21 && *n21 ; n22 = (char*) strchr(n21,',')) { + if (n22) *n22 = '\0'; + if (strcmp(n11,n21)==0) { + *n21 = '\t'; + } + if (n22) { + *n22 = ','; + n21 = n22 + 1; + } else + break; + } + if (n12) { + *n12 = ','; + n11 = n12 +1; + } else + break; + } + } + } + } + return ptr; +} + +char **ssplit(line,pat) +char *line,*pat; +{ + char *p; + int i; + for (i=0;i<MAXTOK;++i) Splitptr[i] = NULL; + strncpy(splitbuf,line,1024); + for (i=0,p=splitbuf;*p && i< MAXTOK;){ + for (Splitptr[i++]=p;*p && !strchr(pat,*p);p++); + if (*p=='\0') break; + *p=0;p++; +/* for (*p='\0'; strchr(pat,*p);p++);*/ + } + return Splitptr; +} + +char *join(lineptr,pat,num) +char **lineptr,*pat; +int num; +{ + int i; + joinbuf[0] = '\0'; + if (lineptr[0] != NULL) + strncpy(joinbuf,lineptr[0],1024); + else { + joinbuf[0]='\0'; + return joinbuf; + } + for (i=1;i<num;i++) { + strcat(joinbuf,pat); + if (lineptr[i] != NULL) + strcat(joinbuf,lineptr[i]); + else + break; + } + return joinbuf; +} + +#ifdef BBSLIB +main() +{ + initial_bbs("feed"); + printf("%s\n",ascii_date()); +} +#endif + diff --git a/innbbsd/externs.h b/innbbsd/externs.h new file mode 100644 index 00000000..2cde1141 --- /dev/null +++ b/innbbsd/externs.h @@ -0,0 +1,16 @@ +#ifndef EXTERNS_H +#define EXTERNS_H + +# ifndef ARG +# ifdef __STDC__ +# define ARG(x) x +# else +# define ARG(x) () +# endif +# endif + +char *fileglue ARG((char *, ...)); +char *ascii_date ARG(()); +char **split ARG((char *, char *)); + +#endif diff --git a/innbbsd/file.c b/innbbsd/file.c new file mode 100644 index 00000000..d4df15b2 --- /dev/null +++ b/innbbsd/file.c @@ -0,0 +1,185 @@ +#include <stdio.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <varargs.h> +#define MAXARGS 100 + +/* isfile is called by + * isfile(filenamecomp1, filecomp2, filecomp3, ..., (char *)0); + * extern "C" int isfile(const char *, const char *[]) ; +*/ + + +char FILEBUF[4096]; + + +static char DOLLAR_[8192]; +char *getstream(fp) +FILE *fp; +{ + return fgets(DOLLAR_, sizeof(DOLLAR_) -1 , fp); +} + +/* + The same as sprintf, but return the new string + fileglue("%s/%s",home,".newsrc"); +*/ + +char *fileglue(va_alist) +va_dcl +{ + va_list ap; + register char* fmt; + static char *newstring; + static char gluebuffer[8192]; + + va_start(ap); + fmt = va_arg(ap, char *) ; + vsprintf(gluebuffer, fmt, ap); + newstring = gluebuffer; + va_end(ap); + return newstring; +} + +long +filesize(filename) +char* filename; +{ + struct stat st; + + if (stat(filename,&st)) return 0; + return st.st_size; +} + +int iszerofile(filename) +char* filename; +{ + struct stat st; + + if (stat(filename,&st)) return 0; + if (st.st_size == 0) return 1; + return 0; +} + +int isfile(filename) +char* filename; +{ + struct stat st; + + if (stat(filename,&st)) return 0; + if (!S_ISREG(st.st_mode)) return 0; + return 1; +} + +int isfilev(va_alist) +va_dcl +{ + va_list ap; + struct stat st; + char *p; + va_start(ap); + + FILEBUF[0]='\0'; + while ((p = va_arg(ap, char *)) != (char *)0) { + strcat(FILEBUF,p); + } + printf("file %s\n",FILEBUF); + + va_end(ap); + return isfile(FILEBUF); +} + + +int isdir(filename) +char* filename; +{ + struct stat st; + + if (stat(filename,&st)) return 0; + if (!S_ISDIR(st.st_mode)) return 0; + return 1; +} + +int isdirv(va_alist) +va_dcl +{ + va_list ap; + struct stat st; + char *p; + va_start(ap); + + FILEBUF[0]='\0'; + while ((p = va_arg(ap, char *)) != (char *)0) { + strcat(FILEBUF,p); + } + + va_end(ap); + return isdir(FILEBUF); +} + +unsigned long mtime(filename) +char* filename; +{ + struct stat st; + if (stat(filename,&st)) return 0; + return st.st_mtime; +} + +unsigned long mtimev(va_alist) +va_dcl +{ + va_list ap; + struct stat st; + char *p; + va_start(ap); + + FILEBUF[0]='\0'; + while ((p = va_arg(ap, char *)) != (char *)0) { + strcat(FILEBUF,p); + } + + va_end(ap); + return mtime(FILEBUF); +} + +unsigned long atime(filename) +char *filename; +{ + struct stat st; + if (stat(filename,&st)) return 0; + return st.st_atime; +} + +unsigned long atimev(va_alist) +va_dcl +{ + va_list ap; + struct stat st; + char *p; + va_start(ap); + + FILEBUF[0]='\0'; + while ((p = va_arg(ap, char *)) != (char *)0) { + strcat(FILEBUF,p); + } + + va_end(ap); + return atime(FILEBUF); +} + +/*#undef TEST*/ +#ifdef TEST +main(argc,argv) +int argc; +char **argv; +{ + int i; + if (argc > 3) { + if (isfilev(argv[1],argv[2],(char*)0)) printf("%s %s %s is file\n",argv[1],argv[2],argv[3]); + if (isdirv(argv[1],argv[2],(char*)0)) printf("%s %s %s is dir\n",argv[1],argv[2],argv[3]); + printf("mtime %d\n",mtimev(argv[1],argv[2],(char*)0)); + printf("atime %d\n",atimev(argv[1],argv[2],(char*)0)); + } + printf("fileglue %s\n", fileglue("%s/%s","home",".test")); +} +#endif diff --git a/innbbsd/his.c b/innbbsd/his.c new file mode 100644 index 00000000..7fe48dc3 --- /dev/null +++ b/innbbsd/his.c @@ -0,0 +1,474 @@ +/* $Revision: 1.1 $ +** +** History file routines. +*/ +#include "innbbsconf.h" +#include "bbslib.h" +#include "his.h" + +#define STATIC static +/*STATIC char HIShistpath[] = _PATH_HISTORY;*/ +STATIC FILE *HISwritefp; +STATIC int HISreadfd; +STATIC int HISdirty; +STATIC int HISincore = XINDEX_DBZINCORE; +STATIC char *LogName = "xindexchan"; + +#ifndef EXPIREDAYS +# define EXPIREDAYS 4 +#endif + +#ifndef DEFAULT_HIST_SIZE +# define DEFAULT_HIST_SIZE 100000 +#endif + +hisincore(flag) +int flag; +{ + HISincore = flag; +} + +makedbz(histpath, entry) +char *histpath; +long entry; +{ + long size; + size = dbzsize(entry); + dbzfresh(histpath,size,'\t',0, 1); + dbmclose(); +} + +void HISsetup(); +void HISclose(); + +void +mkhistory(srchist) +char *srchist; +{ + FILE *hismaint ; + time_t lasthist, now; + char maintbuff[256]; + char *ptr; + hismaint= fopen(srchist, "r"); + if (hismaint == NULL) { + return; + } + { + char newhistpath[1024]; + char newhistdirpath[1024]; + char newhistpagpath[1024]; + sprintf(newhistpath,"%s.n",srchist); + sprintf(newhistdirpath,"%s.n.dir",srchist); + sprintf(newhistpagpath,"%s.n.pag",srchist); + if (!isfile(newhistdirpath) || !isfile(newhistpagpath)) { + makedbz(newhistpath, DEFAULT_HIST_SIZE); + } + myHISsetup(newhistpath); + while ( fgets(maintbuff, sizeof(maintbuff), hismaint) != NULL) { + datum key; + ptr = (char*) strchr(maintbuff,'\t'); + if (ptr != NULL) { *ptr = '\0'; ptr++;} + key.dptr = maintbuff; + key.dsize = strlen(maintbuff); + myHISwrite(&key, ptr); + } + (void) HISclose(); + /*rename(newhistpath, srchist); + rename(newhistdirpath, fileglue("%s.dir", srchist)); + rename(newhistpagpath, fileglue("%s.pag", srchist));*/ + } + fclose(hismaint); +} + +time_t +gethisinfo() +{ + FILE *hismaint ; + time_t lasthist, now; + char maintbuff[4096]; + char *ptr; + hismaint= fopen(HISTORY, "r"); + if (hismaint == NULL) { + return 0; + } + fgets(maintbuff,sizeof(maintbuff), hismaint); + fclose(hismaint); + ptr = (char*)strchr(maintbuff,'\t'); + if (ptr != NULL) { + ptr++; + lasthist = atol(ptr); + return lasthist; + } + return 0; +} + +void +HISmaint() +{ + FILE *hismaint ; + time_t lasthist, now; + char maintbuff[4096]; + char *ptr; + + if (!isfile(HISTORY)) { + makedbz(HISTORY, DEFAULT_HIST_SIZE); + } + hismaint= fopen(HISTORY, "r"); + if (hismaint == NULL) { + return; + } + fgets(maintbuff,sizeof(maintbuff), hismaint); + ptr = (char*)strchr(maintbuff,'\t'); + if (ptr != NULL) { + ptr++; + lasthist = atol(ptr); + time(&now); + if ( lasthist + 86400 * Expiredays * 2 < now ) { + char newhistpath[1024]; + char newhistdirpath[1024]; + char newhistpagpath[1024]; + (void) HISclose(); + sprintf(newhistpath,"%s.n",HISTORY); + sprintf(newhistdirpath,"%s.n.dir",HISTORY); + sprintf(newhistpagpath,"%s.n.pag",HISTORY); + if (!isfile(newhistdirpath)) { + makedbz(newhistpath, DEFAULT_HIST_SIZE); + } + myHISsetup(newhistpath); + while ( fgets(maintbuff, sizeof(maintbuff), hismaint) != NULL) { + datum key; + ptr = (char*) strchr(maintbuff,'\t'); + if (ptr != NULL) { + *ptr = '\0'; ptr++; + lasthist = atol(ptr); + } else { + continue ; + } + if ( lasthist + 99600 * Expiredays < now ) continue; + key.dptr = maintbuff; + key.dsize = strlen(maintbuff); + myHISwrite(&key, ptr); + } + (void) HISclose(); + rename(HISTORY, (char*)fileglue("%s.o",HISTORY)); + rename(newhistpath, HISTORY); + rename(newhistdirpath, (char*)fileglue("%s.dir", HISTORY)); + rename(newhistpagpath, (char*)fileglue("%s.pag", HISTORY)); + (void) HISsetup(); + } + } + fclose(hismaint); +} + + +/* +** Set up the history files. +*/ +void +HISsetup() +{ + myHISsetup(HISTORY); +} + +int +myHISsetup(histpath) +char *histpath; +{ + if (HISwritefp == NULL) { + /* Open the history file for appending formatted I/O. */ + if ((HISwritefp = fopen(histpath, "a")) == NULL) { + syslog(LOG_CRIT, "%s cant fopen %s %m", LogName, histpath); + exit(1); + } + CloseOnExec((int)fileno(HISwritefp), TRUE); + + /* Open the history file for reading. */ + if ((HISreadfd = open(histpath, O_RDONLY)) < 0) { + syslog(LOG_CRIT, "%s cant open %s %m", LogName, histpath); + exit(1); + } + CloseOnExec(HISreadfd, TRUE); + + /* Open the DBZ file. */ + /*(void)dbzincore(HISincore);*/ + (void)dbzincore(HISincore); + (void)dbzwritethrough(1); + if (dbminit(histpath) < 0) { + syslog(LOG_CRIT, "%s cant dbminit %s %m", histpath, LogName); + exit(1); + } + } +} + + +/* +** Synchronize the in-core history file (flush it). +*/ +void +HISsync() +{ + if (HISdirty) { + if (dbzsync()) { + syslog(LOG_CRIT, "%s cant dbzsync %m", LogName); + exit(1); + } + HISdirty = 0; + } +} + + +/* +** Close the history files. +*/ +void +HISclose() +{ + if (HISwritefp != NULL) { + /* Since dbmclose calls dbzsync we could replace this line with + * "HISdirty = 0;". Oh well, it keeps the abstraction clean. */ + HISsync(); + if (dbmclose() < 0) + syslog(LOG_ERR, "%s cant dbmclose %m", LogName); + if (fclose(HISwritefp) == EOF) + syslog(LOG_ERR, "%s cant fclose history %m", LogName); + HISwritefp = NULL; + if (close(HISreadfd) < 0) + syslog(LOG_ERR, "%s cant close history %m", LogName); + HISreadfd = -1; + } +} + + +#ifdef HISset +/* +** File in the DBZ datum for a Message-ID, making sure not to copy any +** illegal characters. +*/ +STATIC void +HISsetkey(p, keyp) + register char *p; + datum *keyp; +{ + static BUFFER MessageID; + register char *dest; + register int i; + + /* Get space to hold the ID. */ + i = strlen(p); + if (MessageID.Data == NULL) { + MessageID.Data = NEW(char, i + 1); + MessageID.Size = i; + } + else if (MessageID.Size < i) { + RENEW(MessageID.Data, char, i + 1); + MessageID.Size = i; + } + + for (keyp->dptr = dest = MessageID.Data; *p; p++) + if (*p == HIS_FIELDSEP || *p == '\n') + *dest++ = HIS_BADCHAR; + else + *dest++ = *p; + *dest = '\0'; + + keyp->dsize = dest - MessageID.Data + 1; +} + +#endif +/* +** Get the list of files under which a Message-ID is stored. +*/ +char * +HISfilesfor(key,output) + datum *key; + datum *output; +{ + char *dest; + datum val; + long offset; + register char *p; + register int i; + int Used; + + /* Get the seek value into the history file. */ + val = dbzfetch(*key); + if (val.dptr == NULL || val.dsize != sizeof offset){ + /*printf("fail here val.dptr %d\n",val.dptr);*/ + return NULL; + } + + /* Get space. */ + if (output->dptr == NULL) { + printf("fail here output->dptr null\n"); + return NULL; + } + + /* Copy the value to an aligned spot. */ + for (p = val.dptr, dest = (char *)&offset, i = sizeof offset; --i >= 0; ) + *dest++ = *p++; + if (lseek(HISreadfd, offset, SEEK_SET) == -1) { + printf("fail here lseek %d\n",offset); + return NULL; + } + + /* Read the text until \n or EOF. */ + for (output->dsize = 0,Used=0; ; ) { + i = read(HISreadfd, + &output->dptr[output->dsize], LEN - 1); + if (i <= 0) { + printf("fail here i %d\n",i); + return NULL; + } + Used += i; + output->dptr[Used] = '\0'; + if ((p = (char*)strchr(output->dptr, '\n')) != NULL) { + *p = '\0'; + break; + } + } + + /* Move past the first two fields -- Message-ID and date info. */ + if ((p = (char*)strchr(output->dptr, HIS_FIELDSEP)) == NULL) { + printf("fail here no HIS_FILE\n"); + return NULL; + } + return p+1; + /*if ((p = (char*)strchr(p + 1, HIS_FIELDSEP)) == NULL) + return NULL;*/ + + /* Translate newsgroup separators to slashes, return the fieldstart. */ +} + +/* +** Have we already seen an article? +*/ +#ifdef HISh +BOOL +HIShavearticle(MessageID) + char *MessageID; +{ + datum key; + datum val; + + HISsetkey(MessageID, &key); + val = dbzfetch(key); + return val.dptr != NULL; +} +#endif + + +/* +** Turn a history filename entry from slashes to dots. It's a pity +** we have to do this. +*/ +STATIC void +HISslashify(p) + register char *p; +{ + register char *last; + + for (last = NULL; *p; p++) { + if (*p == '/') { + *p = '.'; + last = p; + } + else if (*p == ' ' && last != NULL) + *last = '/'; + } + if (last) + *last = '/'; +} + + +IOError(error) +char *error; +{ + fprintf(stderr,"%s\n",error); +} + +/*BOOL*/ +myHISwrite(key, remain) + datum *key; + char *remain; +{ + static char NOPATHS[] = ""; + long offset; + datum val; + int i; + + val = dbzfetch(*key); + if (val.dptr != NULL){ + return FALSE; + } + + flock(fileno(HISwritefp),LOCK_EX); + offset = ftell(HISwritefp); + i = fprintf(HISwritefp, "%s%c%s", + key->dptr, HIS_FIELDSEP, remain); + if (i == EOF || fflush(HISwritefp) == EOF) { + /* The history line is now an orphan... */ + IOError("history"); + syslog(LOG_ERR, "%s cant write history %m", LogName); + flock(fileno(HISwritefp),LOCK_UN); + return FALSE; + } + + /* Set up the database values and write them. */ + val.dptr = (char *)&offset; + val.dsize = sizeof offset; + if (dbzstore(*key, val) < 0) { + IOError("my history database"); + syslog(LOG_ERR, "%s cant dbzstore %m", LogName); + flock(fileno(HISwritefp),LOCK_UN); + return FALSE; + } + + if (++HISdirty >= ICD_SYNC_COUNT) + HISsync(); + flock(fileno(HISwritefp),LOCK_UN); + return TRUE; +} + + +/* +** Write a history entry. +*/ +BOOL +HISwrite(key, date, paths) + datum *key; + char *paths; + long date; +{ + static char NOPATHS[] = ""; + long offset; + datum val; + int i; + + flock(fileno(HISwritefp),LOCK_EX); + offset = ftell(HISwritefp); + i = fprintf(HISwritefp, "%s%c%ld%c%s\n", + key->dptr, HIS_FIELDSEP, (long)date, HIS_FIELDSEP, + paths); + if (i == EOF || fflush(HISwritefp) == EOF) { + /* The history line is now an orphan... */ + IOError("history"); + syslog(LOG_ERR, "%s cant write history %m", LogName); + flock(fileno(HISwritefp),LOCK_UN); + return FALSE; + } + + /* Set up the database values and write them. */ + val.dptr = (char *)&offset; + val.dsize = sizeof offset; + if (dbzstore(*key, val) < 0) { + IOError("history database"); + syslog(LOG_ERR, "%s cant dbzstore %m", LogName); + flock(fileno(HISwritefp),LOCK_UN); + return FALSE; + } + + if (++HISdirty >= ICD_SYNC_COUNT) + HISsync(); + flock(fileno(HISwritefp),LOCK_UN); + return TRUE; +} diff --git a/innbbsd/his.h b/innbbsd/his.h new file mode 100644 index 00000000..f54efc01 --- /dev/null +++ b/innbbsd/his.h @@ -0,0 +1,80 @@ +#ifndef HIS_H +#define HIS_H +#include <stdio.h> +#include <fcntl.h> +#include <ctype.h> +#include <sys/stat.h> +#include <sys/param.h> +#ifndef SEEK_SET +#include <unistd.h> +#endif +#include "dbz.h" + +#ifndef XINDEXDIR +# define XINDEXDIR "/homec/xindex" +#endif +#ifndef _PATH_HISTORY +# define _PATH_HISTORY "/u/staff/bbsroot/csie_util/bntpd/history" +#endif + +#ifndef _PATH_COVERVIEW +# define _PATH_COVERVIEW ".coverview" +#endif + +#ifndef _PATH_COVERVIEWDIR +# define _PATH_COVERVIEWDIR "/homec/xindex" +#endif + +#ifndef XINDEX_DBZINCORE +# define XINDEX_DBZINCORE 1 +#endif +#ifndef XINDEXNAME +# define XINDEXNAME ".index" +#endif +#ifndef XINDEXDBM +# define XINDEXDBM ".dbm" +#endif +#ifndef XINDEXINFO +# define XINDEXINFO ".info" +#endif + +#define LEN 1024 +struct t_article { + long artnum; + char subject[LEN]; /* Subject: line from mail header */ + char from[LEN]; /* From: line from mail header (address) + */ + char name[LEN]; /* From: line from mail header (full nam +e) */ + long date; /* Date: line from header in seconds */ + char xref[LEN]; /* Xref: cross posted article reference +line */ + int lines; /* Lines: number of lines in article */ + char *archive; /* Archive-name: line from mail header */ + char *part; /* part no. of archive */ + char *patch; /* patch no. of archive */ +}; + +typedef struct t_article art_t; + +#define HIS_BADCHAR '_' +#define HIS_FIELDSEP '\t' +#define HIS_NOEXP "-" +#define HIS_SUBFIELDSEP '~' +/*#define HIS_FIELDSEP2 '\034'*/ +#define HIS_FIELDSEP2 'I' + +#ifndef TRUE +# define TRUE 1 +# define FALSE 0 +#endif + +#ifndef BOOL +typedef unsigned char BOOL; +#endif + +#ifndef ICD_SYNC_COUNT +# define ICD_SYNC_COUNT 1 +#endif + +#endif diff --git a/innbbsd/innbbsconf.h b/innbbsd/innbbsconf.h new file mode 100644 index 00000000..6521dde6 --- /dev/null +++ b/innbbsd/innbbsconf.h @@ -0,0 +1,192 @@ +#ifndef INNBBSCONF_H +#define INNBBSCONF_H +#include <stdio.h> +#include <syslog.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <sys/un.h> +#include <sys/param.h> +#include <sys/wait.h> + +#include <unistd.h> +#include <signal.h> +#include <fcntl.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <time.h> +#ifndef BSD44 +# include <malloc.h> +#endif +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/file.h> + +/*#include "bbs.h"*/ +#if defined(AIX) +# include <sys/select.h> +#endif + +/* + BBS home directory + It has been overridden in Makefile +*/ +#ifndef _PATH_BBSHOME +# define _PATH_BBSHOME "/u/staff/bbsroot/csie_util/bntpd/home" +/*# define _PATH_BBSHOME "/home/bbs"*/ +#endif + +#ifndef EXPIREDAYS +# define EXPIREDAYS 7 +#endif + +#ifndef DEFAULT_HIST_SIZE +# define DEFAULT_HIST_SIZE 100000 +#endif + +/* + Maximum number of connections accepted by innbbsd +*/ +#ifndef MAXCLIENT +# define MAXCLIENT 500 +#endif + +/* + Maximum number of articles received for a newsgroup by bbsnnrp each time +*/ +#ifndef MAX_ARTS +# define MAX_ARTS 100 +#endif + +/* + Maximum size of articles received +*/ +#ifndef MAX_ART_SIZE +# define MAX_ART_SIZE 1000000L +#endif + + +/* + Maximum number of articles stated for a newsgroup by bbsnnrp each time +*/ +#ifndef MAX_STATS +# define MAX_STATS 1000 +#endif + +/* + Mininum wait interval for bbsnnrp +*/ +#ifndef MIN_WAIT +# define MIN_WAIT 60 +#endif + + +#ifndef DefaultINNBBSPort +# define DefaultINNBBSPort "7777" +#endif + +/* + time to maintain history database +*/ +#ifndef HIS_MAINT +# define HIS_MAINT +# define HIS_MAINT_HOUR 4 +# define HIS_MAINT_MIN 30 +#endif + +#ifndef ChannelSize +# define ChannelSize 4096 +#endif + +#ifndef ReadSize +# define ReadSize 1024 +#endif + +#ifndef MAXPATHLEN +# define MAXPATHLEN 1024 +#endif + +#ifndef CLX_IOCTL +# define CLX_IOCTL +#endif + +#define DEFAULTSERVER "your.favorite.news.server" +#define DEFAULTPORT "nntp" +#define DEFAULTPROTOCOL "tcp" +#define DEFAULTPATH ".innbbsd" + +#ifndef INADDR_NONE +#define INADDR_NONE 0xffffffff +#endif + +/* +# ifndef ARG +# ifdef __STDC__ +# define ARG(x) (x) +# else +# define ARG(x) () +# endif +# endif +*/ +/* machine dependend */ +#if defined(__linux) +# ifndef LINUX +# define LINUX +# endif +#endif + +#if !defined(__svr4__) || defined(sun) +# define WITH_TM_GMTOFF +#endif +#if (defined(__svr4__) && defined(sun)) || defined(SOLARIS) +# ifndef SOLARIS +# define SOLARIS +# endif +# define NO_getdtablesize +# define NO_bcopy +# define NO_bzero +# define NO_flock +# define WITH_lockf +#endif + +#if defined(AIX) +# define NO_flock +# define WITH_lockf +#endif + +#if defined(HPUX) +# define NO_getdtablesize +# define NO_flock +# define WITH_lockf +#endif + +#ifdef NO_bcopy +# ifndef bcopy +# define bcopy(a,b,c) memcpy(b,a,c) +# endif +#endif + +#ifdef NO_bzero +# ifndef bzero +# define bzero(mem, size) memset(mem,'\0',size) +# endif +#endif + +#ifndef LOCK_EX +# define LOCK_EX 2 /* exclusive lock */ +# define LOCK_UN 8 /* unlock */ +#endif + +#ifdef DEC_ALPHA +# define ULONG unsigned int +#else +# define ULONG unsigned long +#endif + +#ifdef PalmBBS +#undef WITH_RECORD_O +#endif + +#endif diff --git a/innbbsd/innbbsd.c b/innbbsd/innbbsd.c new file mode 100644 index 00000000..f35c1cfb --- /dev/null +++ b/innbbsd/innbbsd.c @@ -0,0 +1,775 @@ +#include "innbbsconf.h" +#include "daemon.h" +#include "innbbsd.h" +#include <dirent.h> +#include "bbslib.h" +#include "inntobbs.h" +#include "nntp.h" + +#ifdef GETRUSAGE +#include <sys/time.h> +#include <sys/resource.h> +#endif + +#ifdef STDC +# ifndef ARG +# define ARG(x) (x) +# else +# define ARG(x) () +# endif +#endif + +/*< add <mid> <recno> ... +> 200 OK +< quit + 500 BYE + +> 300 DBZ Server ... +< query <mid> +> 250 <recno> ... +> 450 NOT FOUND! +*/ + +static int CMDhelp ARG((ClientType*)); +static int CMDquit ARG((ClientType*)); +static int CMDihave ARG((ClientType*)); +static int CMDstat ARG((ClientType*)); +static int CMDaddhist ARG((ClientType*)); +static int CMDgrephist ARG((ClientType*)); +static int CMDmidcheck ARG((ClientType*)); +static int CMDshutdown ARG((ClientType*)); +static int CMDmode ARG((ClientType*)); +static int CMDreload ARG((ClientType*)); +static int CMDhismaint ARG((ClientType*)); +static int CMDverboselog ARG((ClientType*)); +static int CMDlistnodelist ARG((ClientType*)); +static int CMDlistnewsfeeds ARG((ClientType*)); + +#ifdef GETRUSAGE +static int CMDgetrusage ARG((ClientType*)); +static int CMDmallocmap ARG((ClientType*)); +#endif + +static daemoncmd_t cmds[]= +/* cmd-name, cmd-usage, min-argc, max-argc, errorcode, normalcode, cmd-func */ +{ {"help","help [cmd]",1,2,99,100,CMDhelp}, + {"quit","quit",1,0,99,100,CMDquit}, +#ifndef DBZSERVER + {"ihave","ihave mid",2,2,435,335,CMDihave}, +#endif + {"stat","stat mid",2,2,223,430,CMDstat}, + {"addhist","addhist <mid> <path>",3,3, NNTP_ADDHIST_BAD, NNTP_ADDHIST_OK,CMDaddhist}, + {"grephist","grephist <mid>",2,2, NNTP_GREPHIST_BAD, NNTP_GREPHIST_OK, CMDgrephist}, + {"midcheck","midcheck [on|off]",1,2, NNTP_MIDCHECK_BAD, NNTP_MIDCHECK_OK, CMDmidcheck}, + {"shutdown","shutdown (local)",1,1, NNTP_SHUTDOWN_BAD, NNTP_SHUTDOWN_OK, CMDshutdown}, + {"mode","mode (local)",1,1, NNTP_MODE_BAD, NNTP_MODE_OK, CMDmode}, + {"listnodelist","listnodelist (local)",1,1, NNTP_MODE_BAD, NNTP_MODE_OK, CMDlistnodelist}, + {"listnewsfeeds","listnewsfeeds (local)",1,1, NNTP_MODE_BAD, NNTP_MODE_OK, CMDlistnewsfeeds}, + {"reload","reload (local)",1,1, NNTP_RELOAD_BAD, NNTP_RELOAD_OK, CMDreload}, + {"hismaint","hismaint (local)",1,1, NNTP_RELOAD_BAD, NNTP_RELOAD_OK, CMDhismaint}, + {"verboselog","verboselog [on|off](local)",1,2, NNTP_VERBOSELOG_BAD, NNTP_VERBOSELOG_OK, CMDverboselog}, +#ifdef GETRUSAGE + {"getrusage","getrusage (local)",1,1, NNTP_MODE_BAD, NNTP_MODE_OK, CMDgetrusage}, +#endif +#ifdef MALLOCMAP + {"mallocmap","mallocmap (local)",1,1, NNTP_MODE_BAD, NNTP_MODE_OK, CMDmallocmap}, +#endif + {NULL,NULL,0,0,99,100,NULL} +}; + +installinnbbsd() +{ + installdaemon(cmds,100,NULL); +} + +#ifdef OLDLIBINBBSINND +testandmkdir(dir) +char *dir; +{ + if (!isdir(dir)) { + char path[MAXPATHLEN+12]; + sprintf(path,"mkdir -p %s",dir); + system(path); + } +} + +static char splitbuf[2048]; +static char joinbuf[1024]; +#define MAXTOK 50 +static char* Splitptr[MAXTOK]; +char **split(line,pat) +char *line,*pat; +{ + char *p; + int i; + + for (i=0;i<MAXTOK;++i) Splitptr[i] = NULL; + strncpy(splitbuf,line,sizeof splitbuf - 1 ); + /*printf("%d %d\n",strlen(line),strlen(splitbuf));*/ + splitbuf[sizeof splitbuf - 1] = '\0'; + for (i=0,p=splitbuf;*p && i< MAXTOK -1 ;){ + for (Splitptr[i++]=p;*p && !strchr(pat,*p);p++); + if (*p=='\0') break; + for (*p++='\0'; *p && strchr(pat,*p);p++); + } + return Splitptr; +} + +char **BNGsplit(line) +char *line; +{ + char **ptr = split(line,","); + newsfeeds_t *nf1, *nf2; + char *n11, *n12, *n21, *n22; + int i,j; + for (i=0; ptr[i] != NULL; i++) { + nf1 = (newsfeeds_t*)search_group(ptr[i]); + for (j=i+1; ptr[j] != NULL; j++) { + if (strcmp(ptr[i],ptr[j])==0) { + *ptr[j] = '\0'; + continue; + } + nf2 = (newsfeeds_t*)search_group(ptr[j]); + if (nf1 && nf2) { + if (strcmp(nf1->board,nf2->board)==0) { + *ptr[j] = '\0'; + continue; + } + for (n11 = nf1->board, n12 = (char*)strchr(n11,','); + n11 && *n11 ; n12 = (char*) strchr(n11,',')) { + if (n12) *n12 = '\0'; + for (n21 = nf2->board, n22 = (char*)strchr(n21,','); + n21 && *n21 ; n22 = (char*) strchr(n21,',')) { + if (n22) *n22 = '\0'; + if (strcmp(n11,n21)==0) { + *n21 = '\t'; + } + if (n22) { + *n22 = ','; + n21 = n22 + 1; + } else + break; + } + if (n12) { + *n12 = ','; + n11 = n12 +1; + } else + break; + } + } + } + } + return ptr; +} + +char **ssplit(line,pat) +char *line,*pat; +{ + char *p; + int i; + for (i=0;i<MAXTOK;++i) Splitptr[i] = NULL; + strncpy(splitbuf,line,1024); + for (i=0,p=splitbuf;*p && i< MAXTOK;){ + for (Splitptr[i++]=p;*p && !strchr(pat,*p);p++); + if (*p=='\0') break; + *p=0;p++; +/* for (*p='\0'; strchr(pat,*p);p++);*/ + } + return Splitptr; +} + +char *join(lineptr,pat,num) +char **lineptr,*pat; +int num; +{ + int i; + joinbuf[0] = '\0'; + if (lineptr[0] != NULL) + strncpy(joinbuf,lineptr[0],1024); + else { + joinbuf[0]='\0'; + return joinbuf; + } + for (i=1;i<num;i++) { + strcat(joinbuf,pat); + if (lineptr[i] != NULL) + strcat(joinbuf,lineptr[i]); + else + break; + } + return joinbuf; +} + +#endif + +static int CMDtnrpd(client) +ClientType *client; +{ + argv_t *argv = &client->Argv; + fprintf(argv->out,"%d %s\n",argv->dc->usage); + return 0; +} + +islocalconnect(client) +ClientType *client; +{ + if (strcmp(client->username,"localuser") != 0 || + strcmp(client->hostname,"localhost") != 0) + return 0; + return 1; +} + +static shutdownflag = 0; +INNBBSDhalt() +{ + shutdownflag = 1; +} + +int INNBBSDshutdown() +{ + return shutdownflag; +} + +static int CMDshutdown(client) +ClientType *client; +{ + argv_t *argv = &client->Argv; + buffer_t *in = &client->in; + daemoncmd_t *p = argv->dc; + if (!islocalconnect(client)) { + fprintf(argv->out,"%d shutdown access denied\r\n", p->errorcode); + fflush(argv->out); + verboselog("Shutdown Put: %d shutdown access denied\n", p->errorcode); + return 1; + } + shutdownflag = 1; + fprintf(argv->out,"%d shutdown starting\r\n", p->normalcode); + fflush(argv->out); + verboselog("Shutdown Put: %d shutdown starting\n", p->normalcode); + return 1; +} + +static int CMDmode(client) +ClientType *client; +{ + /*char cwdpath[MAXPATHLEN+1];*/ + argv_t *argv = &client->Argv; + extern ClientType INNBBSD_STAT; + buffer_t *in = &client->in; + daemoncmd_t *p = argv->dc; + time_t uptime, now; + int i,j; + time_t lasthist; + ClientType *client1 = &INNBBSD_STAT; + + if (!islocalconnect(client)) { + fprintf(argv->out,"%d mode access denied\r\n", p->errorcode); + fflush(argv->out); + verboselog("Mode Put: %d mode access denied\n", p->errorcode); + return 1; + } + fprintf(argv->out,"%d mode\r\n", p->normalcode); + fflush(argv->out); + verboselog("Mode Put: %d mode\n", p->normalcode); + uptime = innbbsdstartup(); + time(&now); + fprintf(argv->out,"up since %salive %.2f days\r\n", ctime(&uptime), (double)(now - innbbsdstartup())/86400); + fprintf(argv->out,"BBSHOME %s\r\n", BBSHOME); + fprintf(argv->out,"MYBBSID %s\r\n", MYBBSID); + fprintf(argv->out,"ECHOMAIL %s\r\n", ECHOMAIL); + fprintf(argv->out,"INNDHOME %s\r\n", INNDHOME); + fprintf(argv->out,"HISTORY %s\r\n", HISTORY); + fprintf(argv->out,"LOGFILE %s\r\n", LOGFILE); + fprintf(argv->out,"INNBBSCONF %s\r\n", INNBBSCONF); + fprintf(argv->out,"BBSFEEDS %s\r\n", BBSFEEDS); + fprintf(argv->out,"Verbose log: %s\r\n", isverboselog() ?"ON":"OFF"); + fprintf(argv->out,"History Expire Days %d\r\n", Expiredays); + fprintf(argv->out,"History Expire Time %d:%d\r\n", His_Maint_Hour, His_Maint_Min); + lasthist = gethisinfo(); + if (lasthist > 0) { + time_t keep = lasthist, keep1; + time(&now); + fprintf(argv->out,"Oldest history entry created: %s",(char*)ctime(&keep)); + keep = Expiredays * 86400 * 2 + lasthist; + keep1 = keep - now ; + fprintf(argv->out,"Next time to maintain history: (%.2f days later) %s",(double)keep1/86400, (char*)ctime(&keep)); + } + fprintf(argv->out,"PID is %d\r\n", getpid()); + fprintf(argv->out,"LOCAL ONLY %d\r\n", LOCALNODELIST); + fprintf(argv->out,"NONE NEWSFEEDS %d\r\n", NONENEWSFEEDS); + fprintf(argv->out,"Max connections %d\r\n", Maxclient); +#ifdef DEBUGCWD + getwd(cwdpath); + fprintf(argv->out,"Working directory %s\r\n", cwdpath); +#endif + if (Channel) + for (i=0, j=0; i< Maxclient; ++i) { + if (Channel[i].fd == -1) continue; + if (Channel+i == client) continue; + j++; + fprintf(argv->out," %d) in->used %d, in->left %d %s@%s\r\n",i, + Channel[i].in.used, Channel[i].in.left, + Channel[i].username,Channel[i].hostname); + } + fprintf(argv->out,"Total connections %d\r\n", j); + fprintf(argv->out,"Total rec: %d dup: %d fail: %d size: %d, stat rec: %d fail: %d\n", client1->ihavecount, client1->ihaveduplicate, client1->ihavefail, client1->ihavesize, client1->statcount, client1->statfail); + fprintf(argv->out,".\r\n"); + fflush(argv->out); + return 1; +} + +static int +CMDlistnodelist(client) +ClientType *client; +{ + int nlcount; + argv_t *argv = &client->Argv; + buffer_t *in = &client->in; + daemoncmd_t *p = argv->dc; + if (!islocalconnect(client)) { + fprintf(argv->out,"%d listnodelist access denied\r\n", p->errorcode); + fflush(argv->out); + verboselog("Mallocmap Put: %d listnodelist access denied\n", p->errorcode); + return 1; + } + fprintf(argv->out,"%d listnodelist\r\n", p->normalcode); + for (nlcount =0; nlcount < NLCOUNT; nlcount++) { + nodelist_t *nl = NODELIST+nlcount; + fprintf(argv->out,"%2d %s /\\/\\ %s\r\n", nlcount+1, nl->node==NULL?"":nl->node, nl->exclusion==NULL?"":nl->exclusion); + fprintf(argv->out," %s:%s:%s\r\n",nl->host==NULL?"":nl->host, nl->protocol==NULL?"":nl->protocol, nl->comments == NULL ? "": nl->comments); + } + fprintf(argv->out,".\r\n"); + fflush(argv->out); + verboselog("Listnodelist Put: %d listnodelist complete\n", p->normalcode); + return 1; +} + +static int +CMDlistnewsfeeds(client) +ClientType *client; +{ + argv_t *argv = &client->Argv; + buffer_t *in = &client->in; + daemoncmd_t *p = argv->dc; + int nfcount; + if (!islocalconnect(client)) { + fprintf(argv->out,"%d listnewsfeeds access denied\r\n", p->errorcode); + fflush(argv->out); + verboselog("Mallocmap Put: %d listnewsfeeds access denied\n", p->errorcode); + return 1; + } + fprintf(argv->out,"%d listnewsfeeds\r\n", p->normalcode); + for (nfcount =0; nfcount < NFCOUNT; nfcount++) { + newsfeeds_t *nf = NEWSFEEDS + nfcount; + fprintf(argv->out,"%3d %s<=>%s\r\n",nfcount+1, nf->newsgroups, nf->board); + fprintf(argv->out," %s\r\n",nf->path==NULL?"(Null)":nf->path); + } + fprintf(argv->out,".\r\n"); + fflush(argv->out); + verboselog("Listnewsfeeds Put: %d listnewsfeeds complete\n", p->normalcode); + return 1; +} + +#ifdef MALLOCMAP +static int CMDmallocmap(client) +ClientType *client; +{ + argv_t *argv = &client->Argv; + buffer_t *in = &client->in; + daemoncmd_t *p = argv->dc; + struct rusage ru; + int savefd ; + if (!islocalconnect(client)) { + fprintf(argv->out,"%d mallocmap access denied\r\n", p->errorcode); + fflush(argv->out); + verboselog("Mallocmap Put: %d mallocmap access denied\n", p->errorcode); + return 1; + } + fprintf(argv->out,"%d mallocmap\r\n", p->normalcode); + savefd = dup(1); + dup2(client->fd, 1); + mallocmap(); + dup2(savefd, 1); + close(savefd); + fprintf(argv->out,".\r\n"); + fflush(argv->out); + verboselog("Mallocmap Put: %d mallocmap complete\n", p->normalcode); + return 1; +} +#endif + +#ifdef GETRUSAGE +static int CMDgetrusage(client) +ClientType *client; +{ + argv_t *argv = &client->Argv; + buffer_t *in = &client->in; + daemoncmd_t *p = argv->dc; + struct rusage ru; + if (!islocalconnect(client)) { + fprintf(argv->out,"%d getrusage access denied\r\n", p->errorcode); + fflush(argv->out); + verboselog("Getrusage Put: %d getrusage access denied\n", p->errorcode); + return 1; + } + fprintf(argv->out,"%d getrusage\r\n", p->normalcode); + if (getrusage(RUSAGE_SELF,&ru) == 0) { + fprintf(argv->out,"user time used: %.6f\r\n",(double)ru.ru_utime.tv_sec + (double)ru.ru_utime.tv_usec/1000000.0); + fprintf(argv->out,"system time used: %.6f\r\n",(double)ru.ru_stime.tv_sec + (double)ru.ru_stime.tv_usec/1000000.0); + fprintf(argv->out,"maximum resident set size: %lu\r\n",ru.ru_maxrss * getpagesize()); + fprintf(argv->out,"integral resident set size: %lu\r\n",ru.ru_idrss * getpagesize()); + fprintf(argv->out,"page faults not requiring physical I/O: %d\r\n",ru.ru_minflt); + fprintf(argv->out,"page faults requiring physical I/O: %d\r\n",ru.ru_majflt); + fprintf(argv->out,"swaps: %d\r\n",ru.ru_nswap); + fprintf(argv->out,"block input operations: %d\r\n",ru.ru_inblock); + fprintf(argv->out,"block output operations: %d\r\n",ru.ru_oublock); + fprintf(argv->out,"messages sent: %d\r\n",ru.ru_msgsnd); + fprintf(argv->out,"messages received: %d\r\n",ru.ru_msgrcv); + fprintf(argv->out,"signals received: %d\r\n",ru.ru_nsignals); + fprintf(argv->out,"voluntary context switches: %d\r\n",ru.ru_nvcsw); + fprintf(argv->out,"involuntary context switches: %d\r\n",ru.ru_nivcsw); + } + fprintf(argv->out,".\r\n"); + fflush(argv->out); + verboselog("Getrusage Put: %d getrusage complete\n", p->normalcode); + return 1; +} + +#endif + +static int CMDhismaint(client) +ClientType *client; +{ + argv_t *argv = &client->Argv; + buffer_t *in = &client->in; + daemoncmd_t *p = argv->dc; + if (!islocalconnect(client)) { + fprintf(argv->out,"%d hismaint access denied\r\n", p->errorcode); + fflush(argv->out); + verboselog("Hismaint Put: %d hismaint access denied\n", p->errorcode); + return 1; + } + verboselog("Hismaint Put: %d hismaint start\n", p->normalcode); + HISmaint(); + fprintf(argv->out,"%d hismaint complete\r\n", p->normalcode); + fflush(argv->out); + verboselog("Hismaint Put: %d hismaint complete\n", p->normalcode); + return 1; +} + +static int CMDreload(client) +ClientType *client; +{ + argv_t *argv = &client->Argv; + buffer_t *in = &client->in; + daemoncmd_t *p = argv->dc; + if (!islocalconnect(client)) { + fprintf(argv->out,"%d reload access denied\r\n", p->errorcode); + fflush(argv->out); + verboselog("Reload Put: %d reload access denied\n", p->errorcode); + return 1; + } + initial_bbs("feed"); + fprintf(argv->out,"%d reload complete\r\n", p->normalcode); + fflush(argv->out); + verboselog("Reload Put: %d reload complete\n", p->normalcode); + return 1; +} + +static int CMDverboselog(client) +ClientType *client; +{ + argv_t *argv = &client->Argv; + buffer_t *in = &client->in; + daemoncmd_t *p = argv->dc; + if (!islocalconnect(client)) { + fprintf(argv->out,"%d verboselog access denied\r\n", p->errorcode); + fflush(argv->out); + verboselog("Reload Put: %d verboselog access denied\n", p->errorcode); + return 1; + } + if (client->mode == 0) { + if (argv->argc > 1) { + if (strcasecmp(argv->argv[1],"off")==0) { + setverboseoff(); + } else { + setverboseon(); + } + } + } + fprintf(argv->out,"%d verboselog %s\r\n",p->normalcode, + isverboselog() ?"ON":"OFF"); + fflush(argv->out); + verboselog("%d verboselog %s\r\n",p->normalcode, + isverboselog()?"ON":"OFF"); +} + +static int CMDmidcheck(client) +ClientType *client; +{ + argv_t *argv = &client->Argv; + buffer_t *in = &client->in; + daemoncmd_t *p = argv->dc; + if (client->mode == 0) { + if (argv->argc > 1) { + if (strcasecmp(argv->argv[1],"off")==0) { + client->midcheck = 0; + } else { + client->midcheck = 1; + } + } + } + fprintf(argv->out,"%d mid check %s\r\n",p->normalcode, + client->midcheck == 1?"ON":"OFF"); + fflush(argv->out); + verboselog("%d mid check %s\r\n",p->normalcode, + client->midcheck == 1?"ON":"OFF"); +} + +static int CMDgrephist(client) +ClientType *client; +{ + argv_t *argv = &client->Argv; + buffer_t *in = &client->in; + daemoncmd_t *p = argv->dc; + if (client->mode == 0) { + if (argv->argc > 1) { + char *ptr; + ptr = (char*)DBfetch(argv->argv[1]); + if (ptr != NULL) { + fprintf(argv->out,"%d %s OK\r\n", p->normalcode, ptr); + fflush(argv->out); + verboselog("Addhist Put: %d %s OK\n", p->normalcode, ptr); + return 0; + } else { + fprintf(argv->out,"%d %s not found\r\n", p->errorcode,argv->argv[1]); + fflush(argv->out); + verboselog("Addhist Put: %d %s not found\n", p->errorcode, argv->argv[1]); + return 1; + } + } + } + fprintf(argv->out,"%d grephist error\r\n", p->errorcode); + fflush(argv->out); + verboselog("Addhist Put: %d grephist error\n", p->errorcode); + return 1; +} + + +static int CMDaddhist(client) +ClientType *client; +{ + argv_t *argv = &client->Argv; + buffer_t *in = &client->in; + daemoncmd_t *p = argv->dc; + /* + if (strcmp(client->username,"localuser") != 0 || + strcmp(client->hostname,"localhost") != 0) { + fprintf(argv->out,"%d add hist access denied\r\n", p->errorcode); + fflush(argv->out); + verboselog("Addhist Put: %d add hist access denied\n", p->errorcode); + return 1; + } + */ + if (client->mode == 0) { + if (argv->argc > 2) { + char *ptr; + ptr = (char*)DBfetch(argv->argv[1]); + if (ptr == NULL) { + if (storeDB(argv->argv[1], argv->argv[2]) < 0) { + fprintf(argv->out,"%d add hist store DB error\r\n", p->errorcode); + fflush(argv->out); + verboselog("Addhist Put: %d add hist store DB error\n", p->errorcode); + return 1; + } else { + fprintf(argv->out,"%d add hist OK\r\n", p->normalcode); + fflush(argv->out); + verboselog("Addhist Put: %d add hist OK\n", p->normalcode); + return 0; + } + } else { + fprintf(argv->out,"%d add hist duplicate error\r\n", p->errorcode); + fflush(argv->out); + verboselog("Addhist Put: %d add hist duplicate error\n", p->errorcode); + return 1; + } + } + } + fprintf(argv->out,"%d add hist error\r\n", p->errorcode); + fflush(argv->out); + verboselog("Addhist Put: %d add hist error\n", p->errorcode); + return 1; +} + +static int CMDstat(client) +ClientType *client; +{ + argv_t *argv = &client->Argv; + char *ptr, *frontptr; + buffer_t *in = &client->in; + daemoncmd_t *p; + if (client->mode == 0) { + client->statcount++; + if (argv->argc > 1) { + if (argv->argv[1][0] != '<') { + fprintf(argv->out,"430 No such article\r\n"); + fflush(argv->out); + verboselog("Stat Put: 430 No such article\n"); + client->statfail++; + return 0; + } + ptr = (char*)DBfetch(argv->argv[1]); + if (ptr != NULL) { + fprintf(argv->out,"223 0 status %s\r\n",argv->argv[1]); + fflush(argv->out); + client->mode = 0; + verboselog("Stat Put: 223 0 status %s\n",argv->argv[1]); + return 1; + } else { + fprintf(argv->out,"430 No such article\r\n"); + fflush(argv->out); + verboselog("Stat Put: 430 No such article\n"); + client->mode = 0; + client->statfail++; + } + } + } +} + +#ifndef DBZSERVER +static int CMDihave(client) +ClientType *client; +{ + argv_t *argv = &client->Argv; + char *ptr=NULL, *frontptr; + buffer_t *in = &client->in; + daemoncmd_t *p; + if (client->mode == 0) { + client->ihavecount++; + if (argv->argc > 1) { + if (argv->argv[1][0] != '<') { + fprintf(argv->out,"435 Bad Message-ID\r\n"); + fflush(argv->out); + verboselog("Ihave Put: 435 Bad Message-ID\n"); + client->ihavefail++; + return 0; + } + if (client->midcheck == 1) + ptr = (char*)DBfetch(argv->argv[1]); + if (ptr != NULL && client->midcheck == 1) { + fprintf(argv->out,"435 Duplicate\r\n"); + fflush(argv->out); + client->mode = 0; + verboselog("Ihave Put: 435 Duplicate\n"); + client->ihaveduplicate++; + client->ihavefail++; + return 1; + } else { + fprintf(argv->out,"335\r\n"); + fflush(argv->out); + client->mode = 1; + verboselog("Ihave Put: 335\n"); + } + } + } else { + client->mode = 0; + readlines(client); + if (HEADER[SUBJECT_H] && HEADER[FROM_H] && HEADER[DATE_H] && + HEADER[MID_H] && HEADER[NEWSGROUPS_H] ) { + char *path1, *path2; + int rel ; + rel = 0; + path1 = (char*)mymalloc(strlen(HEADER[PATH_H]) + 3); + path2 = (char*)mymalloc(strlen(MYBBSID) + 3); + sprintf(path1, "!%s!",HEADER[PATH_H]); + sprintf(path2, "!%s!",MYBBSID); + if (HEADER[CONTROL_H]) { + bbslog( "Control: %s\n", HEADER[CONTROL_H] ); + if (strncasecmp(HEADER[CONTROL_H],"cancel ",7)==0) { + rel = cancel_article_front(HEADER[CONTROL_H]+7); + } else { + rel = receive_control(); + } + } else if ( (char*)strstr(path1, path2) != NULL) { + bbslog( ":Warn: Loop back article: %s!%s\n",MYBBSID,HEADER[PATH_H] ); + } else { + rel = receive_article(); + } + free(path1); + free(path2); + if (rel == -1) { + fprintf(argv->out,"400 server side failed\r\n"); + fflush(argv->out); + verboselog("Ihave Put: 400\n"); + clearfdset(client->fd); + fclose(client->Argv.in); + fclose(client->Argv.out); + close(client->fd); + client->fd = -1; + client->mode = 0; + client->ihavefail++; + return; + } else { + fprintf(argv->out,"235\r\n"); + verboselog("Ihave Put: 235\n"); + } + fflush(argv->out); + } else if (!HEADER[PATH_H]) { + fprintf(argv->out,"437 No Path in \"ihave %s\" header\r\n",HEADER[MID_H]); + fflush(argv->out); + verboselog("Put: 437 No Path in \"ihave %s\" header\n",HEADER[MID_H]); + client->ihavefail++; + } else { + fprintf(argv->out,"437 No colon-space in \"ihave %s\" header\r\n",HEADER[MID_H]); + fflush(argv->out); + verboselog("Ihave Put: 437 No colon-space in \"ihave %s\" header\n",HEADER[MID_H]); + client->ihavefail++; + } +#ifdef DEBUG + printf("subject is %s\n",HEADER[SUBJECT_H]); + printf("from is %s\n",HEADER[FROM_H]); + printf("Date is %s\n",HEADER[DATE_H]); + printf("Newsgroups is %s\n",HEADER[NEWSGROUPS_H]); + printf("mid is %s\n",HEADER[MID_H]); + printf("path is %s\n",HEADER[PATH_H]); +#endif + } + fflush(argv->out); + return 0; +} +#endif + +static int CMDhelp(client) +ClientType *client; +{ + argv_t *argv = &client->Argv; + daemoncmd_t *p; + if (argv->argc>=1) { + fprintf(argv->out,"%d Available Commands\r\n",argv->dc->normalcode); + for (p=cmds;p->name !=NULL;p++) { + fprintf(argv->out," %s\r\n",p->usage); + } + fprintf(argv->out,"Report problems to %s\r\n",ADMINUSER); + } + fputs(".\r\n",argv->out); + fflush(argv->out); + client->mode = 0; + return 0; +} + +static int CMDquit(client) +ClientType *client; +{ + argv_t *argv = &client->Argv; + fprintf(argv->out,"205 quit\r\n"); + fflush(argv->out); + verboselog("Quit Put: 205 quit\n"); + clearfdset(client->fd); + fclose(client->Argv.in); + fclose(client->Argv.out); + close(client->fd); + client->fd = -1; + client->mode = 0; + channeldestroy(client); + /*exit(0);*/ +} diff --git a/innbbsd/innbbsd.h b/innbbsd/innbbsd.h new file mode 100644 index 00000000..ba667ee6 --- /dev/null +++ b/innbbsd/innbbsd.h @@ -0,0 +1,9 @@ +#ifndef INNBBSD_H +#define INNBBSD_H +#include "daemon.h" + +#ifndef ADMINUSER +# define ADMINUSER "usenet@csie.nctu.edu.tw" +#endif + +#endif diff --git a/innbbsd/inndchannel.c b/innbbsd/inndchannel.c new file mode 100644 index 00000000..439b2af0 --- /dev/null +++ b/innbbsd/inndchannel.c @@ -0,0 +1,657 @@ +#include "innbbsconf.h" +#include "daemon.h" +#include "bbslib.h" +#include "config.h" + +#define DEBUG +#undef DEBUG + +#ifndef MAXCLIENT +#define MAXCLIENT 500 +#endif + +#ifndef ChannelSize +#define ChannelSize 4096 +#endif + +#ifndef ReadSize +#define ReadSize 1024 +#endif + +#ifndef DefaultINNBBSPort +# define DefaultINNBBSPort "7777" +#endif + +#ifndef HIS_MAINT +# define HIS_MAINT +# define HIS_MAINT_HOUR 5 +# define HIS_MAINT_MIN 30 +#endif + +int Maxclient=MAXCLIENT; +ClientType *Channel=NULL; +ClientType INNBBSD_STAT; + +int Max_Art_Size = MAX_ART_SIZE; + +int inetdstart = 0; + +int Junkhistory = 0; + +char *REMOTEUSERNAME, *REMOTEHOSTNAME; + +static fd_set rfd,wfd,efd,orfd,owfd,oefd; + +clearfdset(fd) +int fd; +{ + FD_CLR(fd,&rfd); +} + +static +channelcreate(client) +ClientType *client; +{ + buffer_t *in, *out; + in = &client->in; + out = &client->out; + if (in->data != NULL) + free(in->data); + in->data = (char*)mymalloc( ChannelSize ); + in->left = ChannelSize; + in->used = 0; + if (out->data != NULL) + free(out->data); + out->data = (char*)mymalloc( ChannelSize ); + out->used = 0; + out->left = ChannelSize; + client->ihavecount = 0; + client->ihaveduplicate = 0; + client->ihavefail = 0; + client->ihavesize = 0; + client->statcount = 0; + client->statfail = 0; + client->begin = time(NULL); +} + +channeldestroy(client) +ClientType *client; +{ + if (client->in.data != NULL) { + free(client->in.data); + client->in.data = NULL; + } + if (client->out.data != NULL) { + free(client->out.data); + client->out.data = NULL; + } +#if !defined(PowerBBS) && !defined(DBZSERVER) + if (client->ihavecount >0 || client->statcount >0) { + bbslog("%s@%s rec: %d dup: %d fail: %d size: %d, stat rec: %d fail: %d, time sec: %d\n", + client->username, client->hostname, client->ihavecount, + client->ihaveduplicate, client->ihavefail, client->ihavesize, + client->statcount, client->statfail, time(NULL) - client->begin); + INNBBSD_STAT.ihavecount += client->ihavecount; + INNBBSD_STAT.ihaveduplicate += client->ihaveduplicate; + INNBBSD_STAT.ihavefail += client->ihavefail; + INNBBSD_STAT.ihavesize += client->ihavesize; + INNBBSD_STAT.statcount += client->statcount; + INNBBSD_STAT.statfail += client->statfail; + } +#endif +} + +inndchannel(port, path) +char *port, *path; +{ + time_t tvec; + int i; + int bbsinnd ; + int localbbsinnd; + char obuf[4096]; + struct timeval tout; + ClientType *client = (ClientType *)mymalloc( sizeof(ClientType) * Maxclient); + int localdaemonready = 0; + Channel = client; + + bbsinnd = pmain(port); + if (bbsinnd < 0) { + perror("pmain, existing"); + docompletehalt(); + return(-1); + } + + FD_ZERO(&rfd); FD_ZERO(&wfd); FD_ZERO(&efd); + + localbbsinnd = p_unix_main(path); + if (localbbsinnd < 0) { + perror("local pmain, existing"); +/* Kaede + if (!inetdstart) + fprintf(stderr, "if no other innbbsd running, try to remove %s\n",path); +*/ + close(bbsinnd); + return(-1); + } else { + FD_SET(localbbsinnd,&rfd); + localdaemonready = 1; + } + + FD_SET(bbsinnd,&rfd); + tvec = time((time_t *)0); + for (i=0;i< Maxclient ;++i) { + client[i].fd = -1; + client[i].access=0; + client[i].buffer[0] = '\0'; + client[i].mode = 0; + client[i].in.left = 0; + client[i].in.used = 0; + client[i].in.data = NULL; + client[i].out.left = 0; + client[i].out.used = 0; + client[i].out.data = NULL; + client[i].midcheck = 1; + } + for (;;) { + int nsel,i; + +/* + When to maintain history files. +*/ + time_t now; + static int maint = 0; + struct tm *local; + + if (INNBBSDshutdown()) { + HISclose(); + bbslog(" Shutdown Complete \n"); + docompletehalt(); + exit(0); + } + + time(&now); + local = localtime(&now); + if (local != NULL & local->tm_hour == His_Maint_Hour && + local->tm_min >= His_Maint_Min ) { + if (!maint) { + bbslog(":Maint: start (%d:%d).\n",local->tm_hour,local->tm_min); + HISmaint(); + time(&now); + local = localtime(&now); + if (local != NULL) + bbslog(":Maint: end (%d:%d).\n",local->tm_hour,local->tm_min); + maint = 1; + } + } else { + maint = 0; + } +/* +*/ +/* + in order to maintain history, timeout every 60 seconds in case + no connections +*/ + tout.tv_sec = 60; + tout.tv_usec = 0; + orfd = rfd; + if ((nsel=select(FD_SETSIZE,&orfd, NULL , NULL , &tout))<0){ + continue; + } + if (localdaemonready && FD_ISSET(localbbsinnd,&orfd)) { + int ns,length; + int cc; + ns=tryaccept(localbbsinnd); + if (ns < 0) continue; + for (i=0;i< Maxclient ;++i) { + if (client[i].fd==-1) break; + } + if (i== Maxclient) { + static char msg[]="502 no free descriptors\r\n"; + printf("%s",msg); + write(ns, msg, sizeof(msg)); + close(ns); + continue; + } + client[i].fd=ns; + client[i].buffer[0] = '\0'; + client[i].mode = 0; + client[i].midcheck = 1; + channelcreate(&client[i]); + FD_SET(ns,&rfd); /*FD_SET(ns,&wfd);*/ + { + strncpy(client[i].username,"localuser",20); + strncpy(client[i].hostname,"localhost",128); + client[i].Argv.in = fdopen( ns,"r"); + client[i].Argv.out = fdopen( ns,"w"); +#if !defined(PowerBBS) && !defined(DBZSERVER) + bbslog("connected from (%s@%s).\n",client[i].username, client[i].hostname); +#endif +#ifdef INNBBSDEBUG + printf("connected from (%s@%s).\n",client[i].username, client[i].hostname); +#endif +#ifdef DBZSERVER + fprintf(client[i].Argv.out,"200 %s InterNetNews DBZSERVER server %s (%s@%s).\r\n",MYBBSID, VERSION, client[i].username, client[i].hostname); +#else + fprintf(client[i].Argv.out,"200 %s InterNetNews INNBBSD server %s (%s@%s).\r\n", MYBBSID, VERSION, client[i].username, client[i].hostname ); +#endif + fflush(client[i].Argv.out); + verboselog("UNIX Connect from %s@%s\n",client[i].username, client[i].hostname); + } + } + + if (FD_ISSET(bbsinnd,&orfd)) { + int ns=tryaccept(bbsinnd), length; + struct sockaddr_in there; + char *name; + struct hostent *hp; + int cc; + if (ns < 0) continue; + for (i=0;i< Maxclient ;++i) { + if (client[i].fd==-1) break; + } + if (i== Maxclient) { + static char msg[]="502 no free descriptors\r\n"; + printf("%s",msg); + write(ns, msg, sizeof(msg)); + close(ns); + continue; + } + client[i].fd=ns; + client[i].buffer[0] = '\0'; + client[i].mode = 0; + client[i].midcheck = 1; + channelcreate(&client[i]); + FD_SET(ns,&rfd); /*FD_SET(ns,&wfd);*/ + length = sizeof(there); + if (getpeername(ns,(struct sockaddr *)&there,&length)>=0){ + time_t now=time((time_t *)0); + name=(char*)my_rfc931_name(ns,&there); + strncpy(client[i].username,name,20); + hp = (struct hostent*)gethostbyaddr((char*)&there.sin_addr, sizeof (struct in_addr), there.sin_family); + if (hp) + strncpy(client[i].hostname,hp->h_name,128); + else + strncpy(client[i].hostname,(char*)inet_ntoa(there.sin_addr),128); + + client[i].Argv.in = fdopen( ns,"r"); + client[i].Argv.out = fdopen( ns,"w"); + if ((char*)search_nodelist(client[i].hostname,client[i].username) == NULL) { + bbslog(":Err: invalid connection (%s@%s).\n",client[i].username, client[i].hostname); + fprintf(client[i].Argv.out,"502 You are not in my access file. (%s@%s)\r\n", client[i].username, client[i].hostname); + fflush(client[i].Argv.out); + fclose(client[i].Argv.in); + fclose(client[i].Argv.out); + close(client[i].fd); + FD_CLR(client[i].fd,&rfd); + client[i].fd = -1; + continue; + } + bbslog("connected from (%s@%s).\n",client[i].username, client[i].hostname); +#ifdef INNBBSDEBUG + printf("connected from (%s@%s).\n",client[i].username, client[i].hostname); +#endif +#ifdef DBZSERVER + fprintf(client[i].Argv.out,"200 %s InterNetNews DBZSERVER server %s (%s@%s).\r\n",MYBBSID, VERSION, client[i].username, client[i].hostname ); +#else + fprintf(client[i].Argv.out,"200 %s InterNetNews INNBBSD server %s (%s@%s).\r\n",MYBBSID, VERSION, client[i].username, client[i].hostname ); +#endif + fflush(client[i].Argv.out); + verboselog("INET Connect from %s@%s\n",client[i].username, client[i].hostname); + } else { + } + + } + for (i=0;i< Maxclient ;++i) { + int fd=client[i].fd; + if (fd < 0) { + continue; + } + if (FD_ISSET(fd,&orfd)) { + int nr; +#ifdef DEBUG + printf("before read i %d in.used %d in.left %d\n",i,client[i].in.used, client[i].in.left); +#endif + nr=channelreader(client+i); +#ifdef DEBUG + printf("after read i %d in.used %d in.left %d\n",i,client[i].in.used, client[i].in.left); +#endif + /*int nr=read(fd,client[i].buffer,1024);*/ + if (nr <= 0) { + FD_CLR(fd,&rfd); + fclose(client[i].Argv.in); + fclose(client[i].Argv.out); + close(fd); + client[i].fd = -1; + channeldestroy(client+i); + continue; + } +#ifdef DEBUG + printf("nr %d %.*s", nr, nr, client[i].buffer); +#endif + if (client[i].access==0) { + continue; + } + } + } + } +} + +int channelreader(client) +ClientType *client; +{ + int len, clientlen; + char buffer1[8192], buffer2[4096]; + char *ptr; + buffer_t *in = &client->in; + + if (in->left < ReadSize+3) { + int need = in->used + in->left + ReadSize + 3; + need += need/5 ; + in->data = (char*)myrealloc(in->data, need); + in->left = need - in->used; + verboselog("channelreader realloc %d\n",need); + } + len = read(client->fd, in->data+in->used, ReadSize); + + if (len <=0) return len; + + in->data[len+in->used] = '\0'; + in->lastread = len; +#ifdef DEBUG + printf("after read lastread %d\n", in->lastread); + printf("len %d client %d\n", len, strlen(in->data+in->used)); +#endif + + REMOTEHOSTNAME = client->hostname; + REMOTEUSERNAME = client->username; + if (client->mode == 0) { + if ( (ptr=(char*)strchr(in->data,'\n')) != NULL) { + if (in->data[0] != '\r') + commandparse(client); + } + } else { + commandparse(client); + } + return len; +} + +commandparse(client) +ClientType *client; +{ + char *ptr, *lastend; + argv_t *Argv = &client->Argv; + int (*Main)(); + char *buffer = client->in.data; + int fd = client->fd; + buffer_t *in = &client->in; + int dataused; + int dataleft; + +#ifdef DEBUG + printf("%s %s buffer %s",client->username, client->hostname, buffer); +#endif + ptr= (char*) strchr(in->data+in->used,'\n'); + if (client->mode == 0) { + if (ptr == NULL) { + in->used += in->lastread; + in->left -= in->lastread; + return; + } else { + dataused = ptr - (in->data + in->used) + 1; + dataleft = in->lastread - dataused; + lastend = ptr + 1; + } + } else { + if (in->used >= 5) { + ptr = (char*) strstr(in->data+in->used-5,"\r\n.\r\n"); + } else if (strncmp(in->data,".\r\n",3)==0) { + ptr = in->data; + } else { + ptr = (char*) strstr(in->data+in->used,"\r\n.\r\n"); + } + if (ptr == NULL) { + in->used += in->lastread; + in->left -= in->lastread; + return; + } else { + ptr[2]='\0'; + if ( strncmp(in->data,".\r\n",3)==0) + dataused = 3; + else + dataused = ptr - (in->data + in->used) + 5; + dataleft = in->lastread - dataused; + lastend = ptr + 5; + verboselog("Get: %s@%s end of data . size %d\n", client->username, client->hostname, in->used + dataused); + client->ihavesize += in->used + dataused; + } + } + if (client->mode == 0) { + struct Daemoncmd * dp; + Argv->argc = 0, Argv->argv = NULL, + Argv->inputline= buffer; + if ( ptr != NULL) *ptr = '\0'; + verboselog("Get: %s\n",Argv->inputline); + Argv->argc = argify( in->data + in->used,&Argv->argv); + if ( ptr != NULL) *ptr = '\n'; + dp = (struct Daemoncmd *) searchcmd(Argv->argv[0]); + Argv->dc = dp; + if (Argv->dc) { +#ifdef DEBUG + printf("enter command %s\n",Argv->argv[0]); +#endif + if (Argv->argc < dp->argc) { + fprintf(Argv->out,"%d Usage: %s\r\n",dp->errorcode,dp->usage); + fflush(Argv->out); + verboselog("Put: %d Usage: %s\n",dp->errorcode,dp->usage); + } else if (dp->argno != 0 && Argv->argc > dp->argno) { + fprintf(Argv->out,"%d Usage: %s\r\n",dp->errorcode,dp->usage); + fflush(Argv->out); + verboselog("Put: %d Usage: %s\n",dp->errorcode,dp->usage); + } else { + Main=Argv->dc->main; + if (Main) { + fflush(stdout); + (*Main)(client); + } + } + } else { + fprintf(Argv->out,"500 Syntax error or bad command\r\n"); + fflush(Argv->out); + verboselog("Put: 500 Syntax error or bad command\r\n"); + } + deargify(&Argv->argv); + } else { + if (Argv->dc) { +#ifdef DEBUG + printf("enter data mode\n"); +#endif + Main=Argv->dc->main; + if (Main) { + fflush(stdout); + (*Main)(client); + } + } + } + if (client->mode == 0) { + if (dataleft > 0) { + strncpy(in->data, lastend, dataleft); +#ifdef INNBBSDEBUG + printf("***** try to copy %x %x %d bytes\n",in->data, lastend, dataleft); +#endif + } else { + dataleft = 0; + } + in->left += in->used - dataleft; + in->used = dataleft; + } +} + +do_command() +{ +} + +void dopipesig(s) +int s; +{ + printf("catch sigpipe\n"); + signal(SIGPIPE, dopipesig); +} + +int standaloneinit(port) +char *port ; +{ + int ndescriptors; + FILE *pf; + char pidfile[24]; + ndescriptors = getdtablesize(); +/*#ifndef NOFORK*/ + if (!inetdstart) + if (fork()) + exit(0); +/*#endif*/ + + sprintf(pidfile,"/tmp/innbbsd-%s.pid",port); +/* Kaede + if (!inetdstart) + fprintf(stderr, "PID file is in %s\n", pidfile); +*/ + { int s; + for (s = 3; s < ndescriptors; s++) + (void) close(s); + } + pf=fopen(pidfile,"w"); + if (pf != NULL) { + fprintf(pf,"%d\n",getpid()); + fclose(pf); + } +} + +extern char *optarg; +extern int opterr, optind; + +innbbsusage(name) +char *name; +{ + fprintf(stderr,"Usage: %s [options] [port [path]]\n",name); + fprintf(stderr," -v (verbose log)\n"); + fprintf(stderr," -h|? (help)\n"); + fprintf(stderr," -n (not to use in core dbz)\n"); + fprintf(stderr," -i (start from inetd with wait option)\n"); + fprintf(stderr," -c connections (maximum number of connections accepted)\n"); + fprintf(stderr," default=%d\n",Maxclient); + fprintf(stderr," -j (keep history of junk article, default=none)\n"); +} + + +#ifdef DEBUGNGSPLIT +main() +{ + char **ngptr ; + char buf[1024]; + gets(buf); + ngptr = (char**)BNGsplit(buf); + printf("line %s\n",buf); + while ( *ngptr != NULL) { + printf("%s\n",*ngptr); + ngptr++; + } +} +#endif + +static time_t INNBBSDstartup; +innbbsdstartup() +{ + return INNBBSDstartup; +} + +main(argc,argv) +int argc; +char **argv; +{ + + char *port, *path; + int c, errflag=0; + extern INNBBSDhalt(); +/* +woju +*/ + setgid(BBSGID); + setuid(BBSUID); + chdir(BBSHOME); + resolve_boards(); + + port = DefaultINNBBSPort; + path = LOCALDAEMON; + Junkhistory = 0; + + time(&INNBBSDstartup); + openlog("innbbsd", LOG_PID | LOG_ODELAY, LOG_DAEMON); + while ((c = getopt(argc,argv,"c:f:s:vhidn?j"))!= -1) + switch (c) { + case 'j': + Junkhistory = 1; + break; + case 'v': + verboseon("innbbsd.log"); + break; + case 'n': + hisincore(0); + break; + case 'c': + Maxclient = atoi(optarg); + if (Maxclient < 0) Maxclient = 0; + break; + case 'i': { + struct sockaddr_in there; + int len = sizeof(there); + int rel; + if ((rel=getsockname(0,(struct sockaddr *)&there,&len))< 0){ + fprintf(stdout,"You must run -i from inetd with inetd.conf line: \n"); + fprintf(stdout,"service-port stream tcp wait bbs /home/bbs/innbbsd innbbsd -i port\n"); + fflush(stdout); + exit(5); + } + inetdstart = 1; + startfrominetd(1); + } + break; + case 'd': + dbzdebug(1); + break; + case 's': + Max_Art_Size = atol(optarg); + if (Max_Art_Size < 0) Max_Art_Size = 0; + break; + case 'h': + case '?': + default: + errflag ++; + } + if (errflag > 0) { + innbbsusage(argv[0]); + return(1); + } + if (argc - optind >= 1) { + port = argv[optind]; + } + if (argc - optind >= 2) { + path = argv[optind+1]; + } + + standaloneinit(port); + + initial_bbs("feed"); + +/* Kaede + if (!inetdstart) + fprintf(stderr, "Try to listen in port %s and path %s\n", port, path); +*/ + HISmaint(); + HISsetup(); + installinnbbsd(); + sethaltfunction(INNBBSDhalt); + + signal(SIGPIPE, dopipesig); + inndchannel(port, path); + HISclose(); +} diff --git a/innbbsd/inntobbs.c b/innbbsd/inntobbs.c new file mode 100644 index 00000000..b57d8bb1 --- /dev/null +++ b/innbbsd/inntobbs.c @@ -0,0 +1,323 @@ +#include <stdio.h> +#include "daemon.h" +#include "bbslib.h" +#include <time.h> + +#define INNTOBBS +#include "inntobbs.h" + +typedef struct Header { + char *name; + int id; +} header_t; + +/*enum HeaderValue { +SUBJECT_H, FROM_H, DATE_H, MID_H, NEWSGROUPS_H, +NNTPPOSTINGHOST_H, NNTPHOST_H, CONTROL_H, PATH_H, +ORGANIZATION_H, LASTHEADER, +}; +*/ + +char *strchr ARG((char*,int)); +char *strrchr ARG((char*,int)); +char *strstr ARG((char*,char*)); + +header_t headertable[] = { +"Subject" ,SUBJECT_H, +"From" ,FROM_H, +"Date" ,DATE_H, +"Message-ID",MID_H, +"Newsgroups",NEWSGROUPS_H, +"NNTP-Posting-Host",NNTPPOSTINGHOST_H, +"NNTP-Host", NNTPHOST_H, +"Control", CONTROL_H, +"Path", PATH_H, +"Organization", ORGANIZATION_H, +"X-Auth-From", X_Auth_From_H, +"Approved", APPROVED_H, +"Distribution", DISTRIBUTION_H, +"Keywords", KEYWORDS_H, +"Summary", SUMMARY_H, +"References",REFERENCES_H, +}; + +char *HEADER[LASTHEADER]; +char *BODY; +char *FROM, *SUBJECT, *SITE, *DATE, *POSTHOST, + *NNTPHOST, *PATH, *GROUPS, *MSGID, *CONTROL; + +#ifdef PalmBBS +char **XHEADER; +char *XPATH; +#endif + + +int +isexcluded(path1, nl) +char *path1; +nodelist_t *nl; +{ + char path2[1024]; + /*path2 = (char*)mymalloc(strlen(nl->node) + 3);*/ + sprintf(path2, "!%.*s!",sizeof path2 - 3, nl->node); + if (strstr(path1, path2) != NULL) return 1; + if (nl->exclusion && *nl->exclusion) { + char *exclude, *ptr; + for (exclude = nl->exclusion, ptr = strchr(exclude,','); + exclude && *exclude; ptr = strchr(exclude,',')) { + if (ptr) *ptr = '\0'; + sprintf(path2, "!%.*s!",sizeof path2 - 3, exclude); + if (strstr(path1, path2) != NULL) return 1; + if (ptr) { + *ptr = ','; + exclude = ptr+1; + } else { + break; + } + } + } + return 0; +} + +feedfplog(nf, filepath, type) +newsfeeds_t *nf; +char *filepath; +int type; +{ + char *path1, *path2, *hostptr; + nodelist_t *nl; + if (nf == NULL) return; + if( nf->path != NULL ) { + char *ptr1, *ptr2; + char savech; + path1 = (char*)mymalloc(strlen(HEADER[PATH_H]) + 3); + sprintf(path1, "!%s!",HEADER[PATH_H]); + for (ptr1 = nf->path; ptr1 && *ptr1;) { + for (; *ptr1 && isspace(*ptr1); ptr1++); + if (!*ptr1) break; + for (ptr2 = ptr1; *ptr2 && !isspace(*ptr2); ptr2++); + savech = *ptr2; + *ptr2 = '\0'; + /* + bbslog("search node %s\n",ptr1); + */ + nl = (nodelist_t*) search_nodelist_bynode(ptr1); + /* + bbslog("search node node %s, host %s fp %d\n",nl->node, nl->host, nl->feedfp); + */ + *ptr2 = savech; + ptr1 = ptr2++; + if (nl == NULL) continue; + if (nl->feedfp == NULL) continue; + if (isexcluded(path1,nl)) continue; + /*path2 = (char*)mymalloc(strlen(nl->node) + 3); + sprintf(path2, "!%s!",nl->node); + free(path2); + */ + /* + bbslog("path1 %s path2 %s\n",path1, path2); + */ + /*if (strstr(path1, path2) != NULL) return;*/ + /* to conform to the bntplink batch file */ + { + char *slash = strrchr(filepath,'/'); + if (slash != NULL) *slash = '\t'; + fprintf(nl->feedfp,"%s\t%s\t\t%s\t%s\t%c\t%s\t%s!%s\n", + filepath == NULL ? "": filepath, + GROUPS, FROM, SUBJECT, type, MSGID, MYBBSID, HEADER[PATH_H]); + if (slash != NULL) *slash = '/'; + } + fflush(nl->feedfp); + if (savech == '\0') break; + } + free(path1); + } +} + +static FILE* bbsfeedsfp = NULL; +static bbsfeedson = -1; + +init_bbsfeedsfp() +{ + if (bbsfeedsfp != NULL) { + fclose(bbsfeedsfp); + bbsfeedsfp = NULL; + } + bbsfeedson = -1; +} + +bbsfeedslog(filepath, type) +char *filepath; +int type; +{ + + char datebuf[40]; + time_t now; + + if (bbsfeedson ==0) return; + if (bbsfeedson == -1) { + if (!isfile(BBSFEEDS)) { + bbsfeedson = 0; + return; + } + bbsfeedson = 1; + } + + if (bbsfeedsfp == NULL) { + bbsfeedsfp = fopen(BBSFEEDS,"a"); + } + time(&now); + strftime(datebuf, sizeof(datebuf), "%b %d %X ", localtime(&now)); + + if( bbsfeedsfp != NULL ) { + fprintf(bbsfeedsfp,"%s %c %s %s %s %s!%s %s\n", datebuf, type, + REMOTEHOSTNAME, GROUPS, MSGID, MYBBSID, HEADER[PATH_H], filepath==NULL? "": filepath); + fflush(bbsfeedsfp); + } +} + +static FILE* echomailfp = NULL; +static echomaillogon = -1; + +init_echomailfp() +{ + if (echomailfp != NULL) { + fclose(echomailfp); + echomailfp = NULL; + } + echomaillogon = -1; +} + +echomaillog() +{ + + if (echomaillogon ==0) return; + if (echomaillogon == -1) { + if (!isfile(ECHOMAIL)) { + echomaillogon = 0; + return; + } + echomaillogon = 1; + } + + if (echomailfp == NULL) { + echomailfp = fopen(ECHOMAIL,"a"); + } + + if( echomailfp != NULL ) { + fprintf(echomailfp,"\n"); + fprintf(echomailfp,"發信人: %s, 信區: %s\n", FROM, GROUPS); + fprintf(echomailfp,"標 題: %s\n", SUBJECT); + fprintf(echomailfp,"發信站: %s (%s)\n", SITE, DATE); + fprintf(echomailfp,"轉信站: %s (%s)\n", PATH, REMOTEHOSTNAME); + fflush(echomailfp); + } +} + +int headercmp(a,b) +header_t *a, *b; +{ + return strcasecmp(a->name, b->name); +} + +int readlines(client) +ClientType *client; +{ + int fd = client->fd; + char *buffer = client->buffer; + buffer_t *in = &client->in; + char *front = in->data, *ptr, *hptr; + int i; + + for (i=0; i < LASTHEADER; i++ ) + HEADER[i] = NULL; + for (ptr = (char*)strchr(in->data,'\n'); ptr != NULL && *ptr != '\0' ; front = ptr+1, ptr = (char*)strchr(front,'\n')) { + *ptr = '\0'; + if (front[0] == '\r' || front[1] == '\n') { + BODY = front+2; + break; + } + hptr = (char*)strchr(front,':'); + if (hptr != NULL && hptr[1] == ' ') { + int value; + *hptr = '\0'; + value = headervalue(front); + if (value != -1) { + char *tp; + HEADER[value] = hptr + 2; + if ((tp = (char*)strchr(HEADER[value],'\r'))!=NULL) + *tp = '\0'; + } + *hptr = ':'; + } + /**ptr = '\n';*/ + } + NNTPHOST = HEADER[NNTPHOST_H]; + PATH = HEADER[PATH_H]; + FROM = HEADER[FROM_H]; + GROUPS = HEADER[NEWSGROUPS_H]; + SUBJECT = HEADER[SUBJECT_H]; + DATE = HEADER[DATE_H]; + SITE = HEADER[ORGANIZATION_H]; + MSGID = HEADER[MID_H]; + CONTROL = HEADER[CONTROL_H]; + POSTHOST = HEADER[NNTPPOSTINGHOST_H]; + if (POSTHOST == NULL) { + if (HEADER[X_Auth_From_H] != NULL) { + POSTHOST = HEADER[X_Auth_From_H]; + HEADER[NNTPPOSTINGHOST_H] = POSTHOST; + } + } +#ifdef PalmBBS + XPATH = PATH; + XHEADER = HEADER; +#endif +} + +int headervalue(inputheader) +char *inputheader; +{ + header_t key, *findkey; + static int hasinit=0; + + if (hasinit == 0) { + article_init(); + hasinit = 1; + } + + key.name = inputheader; + findkey = ( header_t *)bsearch ( + (char *) &key, (char *) headertable, + sizeof(headertable)/ sizeof(header_t), sizeof (key), + headercmp); + if (findkey != NULL) return findkey->id; + return -1; +} + +article_init() +{ + int i; + static int article_inited = 0; + + if (article_inited) return; + article_inited = 1; + + qsort(headertable, sizeof(headertable)/ sizeof(header_t), sizeof(header_t), + headercmp); + for (i=0; i < LASTHEADER; i++ ) + HEADER[i] = NULL; +} + +#ifdef INNTOBBS_MAIN +main() +{ + int i,j,k,l,m,n,o,p,q; + article_init(); + i = headervalue("Subject"); + j = headervalue("From"); + k = headervalue("Date"); + l = headervalue("NNTP-Posting-Host"); + m = headervalue("Newsgroups"); + n = headervalue("Message-ID"); +} +#endif diff --git a/innbbsd/inntobbs.h b/innbbsd/inntobbs.h new file mode 100644 index 00000000..1026e6d7 --- /dev/null +++ b/innbbsd/inntobbs.h @@ -0,0 +1,39 @@ +#ifndef INNTOBBS_H +#define INNTOBBS_H + +enum HeaderValue { +SUBJECT_H, FROM_H, DATE_H, MID_H, NEWSGROUPS_H, +NNTPPOSTINGHOST_H, NNTPHOST_H, CONTROL_H, PATH_H, +ORGANIZATION_H, X_Auth_From_H, APPROVED_H, DISTRIBUTION_H, +REFERENCES_H, KEYWORDS_H, SUMMARY_H, +LASTHEADER, +}; + +#if !defined(PalmBBS) +extern char *HEADER[]; +extern char *BODY; +extern char *FROM, *SUBJECT, *SITE, *DATE, *POSTHOST, + *NNTPHOST, *PATH, *GROUPS, *MSGID, *CONTROL; +extern char *REMOTEHOSTNAME, *REMOTEUSERNAME; +#else +extern char **XHEADER; +extern char *BODY; +extern char *FROM, *SUBJECT, *SITE, *DATE, *POSTHOST, + *NNTPHOST, *XPATH, *GROUPS, *MSGID, *CONTROL; +extern char *REMOTEHOSTNAME, *REMOTEUSERNAME; +#endif + +int receive_article(); + +#if defined(PalmBBS) +#ifndef INNTOBBS +#ifndef PATH +# define PATH XPATH +#endif +#ifndef HEADER +# define HEADER XHEADER +#endif +#endif +#endif + +#endif diff --git a/innbbsd/mkhistory.c b/innbbsd/mkhistory.c new file mode 100644 index 00000000..4be980ea --- /dev/null +++ b/innbbsd/mkhistory.c @@ -0,0 +1,14 @@ +#include "innbbsconf.h" +#include "bbslib.h" + +main(argc,argv) +int argc; +char *argv[]; +{ + if (argc < 2) { + fprintf(stderr,"Usage: %s history-file\n",argv[0]); + exit(1); + } + initial_bbs(NULL); + mkhistory(argv[1]); +} diff --git a/innbbsd/nntp.h b/innbbsd/nntp.h new file mode 100644 index 00000000..489f3502 --- /dev/null +++ b/innbbsd/nntp.h @@ -0,0 +1,145 @@ +/* $Revision: 1.1 $ +** +** Here be a set of NNTP response codes as defined in RFC977 and elsewhere. +** The reponse codes are three digits, RFI, defined like this: +** R, Response: +** 1xx Informative message +** 2xx Command ok +** 3xx Command ok so far, send the rest of it. +** 4xx Command was correct, but couldn't be performed for +** some reason. +** 5xx Command unimplemented, or incorrect, or a serious +** program error occurred. +** F, Function: +** x0x Connection, setup, and miscellaneous messages +** x1x Newsgroup selection +** x2x Article selection +** x3x Distribution functions +** x4x Posting +** x8x Nonstandard extensions (AUTHINFO, XGTITLE) +** x9x Debugging output +** I, Information: +** No defined semantics +*/ +#define NNTP_HELPOK_VAL 100 +#define NNTP_BAD_COMMAND_VAL 500 +#define NNTP_BAD_COMMAND "500 Syntax error or bad command" +#define NNTP_TEMPERR_VAL 503 +#define NNTP_ACCESS "502 Permission denied" +#define NNTP_ACCESS_VAL 502 +#define NNTP_GOODBYE_ACK "205" +#define NNTP_GOODBYE_ACK_VAL 205 +#define NNTP_GOODBYE "400" +#define NNTP_GOODBYE_VAL 400 +#define NNTP_HAVEIT "435 Duplicate" +#define NNTP_HAVEIT_BADID "435 Bad Message-ID" +#define NNTP_HAVEIT_VAL 435 +#define NNTP_LIST_FOLLOWS "215" +#define NNTP_LIST_FOLLOWS_VAL 215 +#define NNTP_HELP_FOLLOWS "100 Legal commands" +#define NNTP_HELP_FOLLOWS_VAL 100 +#define NNTP_NOTHING_FOLLOWS_VAL 223 +#define NNTP_ARTICLE_FOLLOWS "220" +#define NNTP_ARTICLE_FOLLOWS_VAL 220 +#define NNTP_NEWGROUPS_FOLLOWS_VAL 231 +#define NNTP_HEAD_FOLLOWS "221" +#define NNTP_HEAD_FOLLOWS_VAL 221 +#define NNTP_BODY_FOLLOWS_VAL 222 +#define NNTP_OVERVIEW_FOLLOWS_VAL 224 +#define NNTP_DATE_FOLLOWS_VAL 111 +#define NNTP_POSTOK "200" +#define NNTP_POSTOK_VAL 200 +#define NNTP_START_POST_VAL 340 +#define NNTP_NOPOSTOK_VAL 201 +#define NNTP_SLAVEOK_VAL 202 +#define NNTP_REJECTIT_VAL 437 +#define NNTP_REJECTIT_EMPTY "437 Empty article" +#define NNTP_DONTHAVEIT "430" +#define NNTP_DONTHAVEIT_VAL 430 +#define NNTP_RESENDIT_NOHIST "436 Can't write history" +#define NNTP_RESENDIT_NOSPACE "436 No space" +#define NNTP_RESENDIT_VAL 436 +#define NNTP_POSTEDOK "240 Article posted" +#define NNTP_POSTEDOK_VAL 240 +#define NNTP_POSTFAIL_VAL 441 +#define NNTP_GROUPOK_VAL 211 +#define NNTP_SENDIT "335" +#define NNTP_SENDIT_VAL 335 +#define NNTP_SYNTAX_USE "501 Bad command use" +#define NNTP_SYNTAX_VAL 501 +#define NNTP_TOOKIT "235" +#define NNTP_TOOKIT_VAL 235 +#define NNTP_NOTINGROUP "412 Not in a newsgroup" +#define NNTP_NOTINGROUP_VAL 412 +#define NNTP_NOSUCHGROUP "411 No such group" +#define NNTP_NOSUCHGROUP_VAL 411 +#define NNTP_NEWNEWSOK "230 New news follows" +#define NNTP_NOARTINGRP "423 Bad article number" +#define NNTP_NOARTINGRP_VAL 423 +#define NNTP_NOCURRART "420 No current article" +#define NNTP_NOCURRART_VAL 420 +#define NNTP_NONEXT_VAL 421 +#define NNTP_NOPREV_VAL 422 +#define NNTP_CANTPOST "440 Posting not allowed" +#define NNTP_CANTPOST_VAL 440 + + +/* +** The first character of an NNTP reply can be used as a category class. +*/ +#define NNTP_CLASS_OK '2' +#define NNTP_CLASS_ERROR '4' +#define NNTP_CLASS_FATAL '5' + + +/* +** The NNTP protocol currently has no way to say "offer me this article +** later, but don't close the connection." That will be fixed in NNTP2. +#define NNTP_RESENDIT_LATER "?" +#define NNTP_RESENDIT_LATER_VAL ? +*/ + + +/* +** Authentication commands from the RFC update (not official). +*/ +#define NNTP_AUTH_NEEDED "480" +#define NNTP_AUTH_NEEDED_VAL 480 +#define NNTP_AUTH_BAD "481" +#define NNTP_AUTH_NEXT "381" +#define NNTP_AUTH_NEXT_VAL 381 +#define NNTP_AUTH_OK "281" +#define NNTP_AUTH_OK_VAL 281 +#define NNTP_AUTH_REJECT_VAL 482 + +/* +** XGTITLE, from ANU news. +*/ +#define NNTP_XGTITLE_BAD 481 /* Yes, 481. */ +#define NNTP_XGTITLE_OK 282 + +#define NNTP_STRLEN 512 + +/* +** For tin newsreader +*/ +#define OK_XINDEX 218 /* Tin style group index file follows */ +#define OK_XMOTD 217 /* Motd (message of the day) file follows */ +#define ERR_XINDEX 418 /* No tin style index file for newsgroup */ +#define ERR_XMOTD 417 /* No motd (message of the day) file */ + +/* For DBZ server */ +#define NNTP_ADDHIST_OK 283 /* addhist OK */ +#define NNTP_GREPHIST_OK 284 /* grephist OK */ +#define NNTP_MIDCHECK_OK 285 /* grephist OK */ +#define NNTP_SHUTDOWN_OK 286 /* grephist OK */ +#define NNTP_RELOAD_OK 287 /* grephist OK */ +#define NNTP_MODE_OK 101 /* grephist OK */ +#define NNTP_VERBOSELOG_OK 289 /* grephist OK */ +#define NNTP_ADDHIST_BAD 483 /* addhist fail */ +#define NNTP_GREPHIST_BAD 484 /* grephist fail */ +#define NNTP_MIDCHECK_BAD 485 /* grephist fail */ +#define NNTP_SHUTDOWN_BAD 486 /* grephist fail */ +#define NNTP_RELOAD_BAD 487 /* grephist fail */ +#define NNTP_MODE_BAD 488 /* grephist fail */ +#define NNTP_VERBOSELOG_BAD 489 /* grephist fail */ diff --git a/innbbsd/pmain.c b/innbbsd/pmain.c new file mode 100644 index 00000000..39ddba22 --- /dev/null +++ b/innbbsd/pmain.c @@ -0,0 +1,62 @@ +#include "innbbsconf.h" +#include "daemon.h" + +/*char *AccessFile=ACCESSFILE;*/ +#define INNBBSDPORT1 "1904" +#define INNBBSDPORT2 "1234" +#define INNBBSDPATH1 ".innbbsd1" +#define INNBBSDPATH2 ".innbbsd2" + +pmain(port) +char *port; +{ + if (port == NULL) { + int rel; +/* installbbstalkd(); */ + fprintf(stderr,"Trying to listen in port %s\n",INNBBSDPORT1); + rel = open_listen(INNBBSDPORT1,"tcp",NULL); +#ifdef DEBUG + printf("port fd %d allocated\n",rel); +#endif + if (rel<0) { + fprintf(stderr,"Trying to listen in port %s\n",INNBBSDPORT2); + return open_listen(INNBBSDPORT2,"tcp",NULL); + } + return rel; + } else { +#ifdef DEBUG + printf("start to allocate port\n"); +#endif + return open_listen(port,"tcp",NULL); + } +} + +p_unix_main(path) +char *path; +{ + if (path == NULL) { + int rel; +/* installbbstalkd(); */ + fprintf(stderr,"Trying to listen in port %s\n",INNBBSDPATH1); + rel = open_unix_listen(INNBBSDPATH1,"tcp",NULL); +#ifdef DEBUG + printf("port fd %d allocated\n",rel); +#endif + if (rel<0) { + fprintf(stderr,"Trying to listen in port %s\n",INNBBSDPATH2); + return open_listen(INNBBSDPATH2,"tcp",NULL); + } + return rel; + } else { +#ifdef DEBUG + printf("start to allocate path %s\n", path); +#endif + int fd = unixclient(path,"tcp"); + if (fd < 0) + unlink(path); + else + close(fd); + return open_unix_listen(path,"tcp",NULL); + } +} + diff --git a/innbbsd/port.c b/innbbsd/port.c new file mode 100644 index 00000000..65e91fa4 --- /dev/null +++ b/innbbsd/port.c @@ -0,0 +1,28 @@ +#include "innbbsconf.h" + +#ifdef NO_getdtablesize +#include <sys/time.h> +#include <sys/resource.h> +getdtablesize() +{ + struct rlimit limit; + if (getrlimit(RLIMIT_NOFILE, &limit) >= 0){ + return limit.rlim_cur; + } + return -1; +} +#endif + +#if defined(SYSV) && !defined(WITH_RECORD_O) +#include <fcntl.h> +flock(fd, op) +int fd,op; +{ + switch (op) { + case LOCK_EX: op = F_LOCK; break; + case LOCK_UN: op = F_ULOCK; break; + default: return -1; + } + return lockf(fd, op, 0L); +} +#endif diff --git a/innbbsd/receive_article.c b/innbbsd/receive_article.c new file mode 100644 index 00000000..40f2609c --- /dev/null +++ b/innbbsd/receive_article.c @@ -0,0 +1,1204 @@ +/* + * BBS implementation dependendent part + * + * The only two interfaces you must provide + * + * #include "inntobbs.h" int receive_article(); 0 success not 0 fail + * + * if (storeDB(HEADER[MID_H], hispaths) < 0) { .... fail } + * + * int cancel_article_front( char *msgid ); 0 success not 0 fail + * + * char *ptr = (char*)DBfetch(msgid); + * + * 收到之文章內容 (body)在 char *BODY, 檔頭 (header)在 char *HEADER[] SUBJECT_H, + * FROM_H, DATE_H, MID_H, NEWSGROUPS_H, NNTPPOSTINGHOST_H, NNTPHOST_H, + * CONTROL_H, PATH_H, ORGANIZATION_H + */ + +/* + * Sample Implementation + * + * receive_article() --> post_article() --> bbspost_write_post(); + * cacnel_article_front(mid) --> cancel_article() --> bbspost_write_cancel(); + */ + + +#ifndef PowerBBS +#include "innbbsconf.h" +#include "daemon.h" +#include "bbslib.h" +#include "inntobbs.h" +#include "antisplam.h" + +extern int Junkhistory; + +char *post_article ARG((char *, char *, char *, int (*) (), char *, char *)); +int cancel_article ARG((char *, char *, char *)); + + +#ifdef MapleBBS +#include "config.h" +#include "pttstruct.h" +#define _BBS_UTIL_C_ +#else +report() +{ + /* Function called from record.o */ + /* Please leave this function empty */ +} +#endif + + +#if defined(PalmBBS) + +#ifndef PATH +# define PATH XPATH +#endif + +#ifndef HEADER +# define HEADER XHEADER +#endif + +#endif + +/* process post write */ +bbspost_write_post(fh, board, filename) + int fh; + char *board; + char *filename; +{ + char *fptr, *ptr; + FILE *fhfd = fdopen(fh, "w"); + + if (fhfd == NULL) + { + bbslog("can't fdopen, maybe disk full\n"); + return -1; + } + + fprintf(fhfd, "發信人: %.60s, 看板: %s\n", FROM, board); + fprintf(fhfd, "標 題: %.70s\n", SUBJECT); + fprintf(fhfd, "發信站: %.43s (%s)\n", SITE, DATE); + fprintf(fhfd, "轉信站: %.70s\n", PATH); + +#ifndef MapleBBS + if (POSTHOST != NULL) + { + fprintf(fhfd, "Origin: %.70s\n", POSTHOST); + } +#endif + + fprintf(fhfd, "\n"); + for (fptr = BODY, ptr = strchr(fptr, '\r'); ptr != NULL && *ptr != '\0'; fptr = ptr + 1, ptr = strchr(fptr, '\r')) + { + int ch = *ptr; + *ptr = '\0'; + fputs(fptr, fhfd); + *ptr = ch; + } + fputs(fptr, fhfd); + + fflush(fhfd); + fclose(fhfd); + return 0; +} + +#ifdef KEEP_NETWORK_CANCEL +/* process cancel write */ +bbspost_write_cancel(fh, board, filename) + int fh; + char *board, *filename; +{ + char *fptr, *ptr; + FILE *fhfd = fdopen(fh, "w"), *fp; + char buffer[256]; + + if (fhfd == NULL) + { + bbslog("can't fdopen, maybe disk full\n"); + return -1; + } + + fprintf(fhfd, "發信人: %s, 信區: %s\n", FROM, board); + fprintf(fhfd, "標 題: %s\n", SUBJECT); + fprintf(fhfd, "發信站: %.43s (%s)\n", SITE, DATE); + fprintf(fhfd, "轉信站: %.70s\n", PATH); + if (HEADER[CONTROL_H] != NULL) + { + fprintf(fhfd, "Control: %s\n", HEADER[CONTROL_H]); + } + if (POSTHOST != NULL) + { + fprintf(fhfd, "Origin: %s\n", POSTHOST); + } + fprintf(fhfd, "\n"); + for (fptr = BODY, ptr = strchr(fptr, '\r'); ptr != NULL && *ptr != '\0'; fptr = ptr + 1, ptr = strchr(fptr, '\r')) + { + int ch = *ptr; + *ptr = '\0'; + fputs(fptr, fhfd); + *ptr = ch; + } + fputs(fptr, fhfd); + if (POSTHOST != NULL) + { + fprintf(fhfd, "\n * Origin: ● %.26s ● From: %.40s\n", SITE, POSTHOST); + } + fprintf(fhfd, "\n---------------------\n"); + fp = fopen(filename, "r"); + if (fp == NULL) + { + bbslog("can't open %s\n", filename); + return -1; + } + while (fgets(buffer, sizeof buffer, fp) != NULL) + { + fputs(buffer, fhfd); + } + fclose(fp); + fflush(fhfd); + fclose(fhfd); + + { + fp = fopen(filename, "w"); + if (fp == NULL) + { + bbslog("can't write %s\n", filename); + return -1; + } + fprintf(fp, "發信人: %s, 信區: %s\n", FROM, board); + fprintf(fp, "標 題: %.70s\n", SUBJECT); + fprintf(fp, "發信站: %.43s (%s)\n", SITE, DATE); + fprintf(fp, "轉信站: %.70s\n", PATH); + if (POSTHOST != NULL) + { + fprintf(fhfd, "Origin: %s\n", POSTHOST); + } + if (HEADER[CONTROL_H] != NULL) + { + fprintf(fhfd, "Control: %s\n", HEADER[CONTROL_H]); + } + fprintf(fp, "\n"); + for (fptr = BODY, ptr = strchr(fptr, '\r'); ptr != NULL && *ptr != '\0'; fptr = ptr + 1, ptr = strchr(fptr, '\r')) + { + *ptr = '\0'; + fputs(fptr, fp); + } + fputs(fptr, fp); + if (POSTHOST != NULL) + { + fprintf(fp, "\n * Origin: ● %.26s ● From: %.40s\n", SITE, POSTHOST); + } + fclose(fp); + } + return 0; +} +#endif + + +bbspost_write_control(fh, board, filename) + int fh; + char *board; + char *filename; +{ + char *fptr, *ptr; + FILE *fhfd = fdopen(fh, "w"); + + if (fhfd == NULL) + { + bbslog("can't fdopen, maybe disk full\n"); + return -1; + } + + fprintf(fhfd, "Path: %s!%s\n", MYBBSID, HEADER[PATH_H]); + fprintf(fhfd, "From: %s\n", FROM); + fprintf(fhfd, "Newsgroups: %s\n", GROUPS); + fprintf(fhfd, "Subject: %s\n", SUBJECT); + fprintf(fhfd, "Date: %s\n", DATE); + fprintf(fhfd, "Organization: %s\n", SITE); + if (POSTHOST != NULL) + { + fprintf(fhfd, "NNTP-Posting-Host: %.70s\n", POSTHOST); + } + if (HEADER[CONTROL_H] != NULL) + { + fprintf(fhfd, "Control: %s\n", HEADER[CONTROL_H]); + } + if (HEADER[APPROVED_H] != NULL) + { + fprintf(fhfd, "Approved: %s\n", HEADER[APPROVED_H]); + } + if (HEADER[DISTRIBUTION_H] != NULL) + { + fprintf(fhfd, "Distribution: %s\n", HEADER[DISTRIBUTION_H]); + } + fprintf(fhfd, "\n"); + for (fptr = BODY, ptr = strchr(fptr, '\r'); ptr != NULL && *ptr != '\0'; fptr = ptr + 1, ptr = strchr(fptr, '\r')) + { + int ch = *ptr; + *ptr = '\0'; + fputs(fptr, fhfd); + *ptr = ch; + } + fputs(fptr, fhfd); + + + fflush(fhfd); + fclose(fhfd); + return 0; +} + + +time_t datevalue; + + +/* process cancel write */ +receive_article() +{ + int i; + char *user, *userptr; + char *ngptr, *nngptr, *pathptr; + char **splitptr; + static char userid[32]; + static char xdate[32]; + static char xpath[180]; + newsfeeds_t *nf; + char *boardhome; + char hispaths[4096]; + char firstpath[MAXPATHLEN], *firstpathbase; + char *lesssym, *nameptrleft, *nameptrright; + static char sitebuf[80]; + +#ifdef HMM_USE_ANTI_SPAM + char *notitle[] = + {"行銷", "業務代表", "徵", "資訊", "免費", "大贈送", "傳銷", "未滿", + "年費", "傳呼", "價", "操你媽", "未成年", "馬賽克", "信用", "賺錢", + "=?", "!!!", + "操你", "操你", "幹妳", "操妳","**","★★","**","$$","泡麵", NULL}, + *nofrom[] = + {"TaipeiNet.Net", "hotmail.com", "mt.touc.edu.tw", "ms11.hinet.net", NULL}, + *nocont[] = + {"名額有限", "優惠價", "動作要快", "訂購", "特價", "專賣", "BBC", + "幹你", "操你", "幹妳", "操妳","每片","最新目錄", "http://", "收錢", + "創業", "付款", "廣告信", "只賣", "市價", "NCg", "ICAg", NULL}; +#endif + + if (FROM == NULL) + { + bbslog(":Err: article without usrid %s\n", MSGID); + return 0; + } + else + { +#ifdef HMM_USE_ANTI_SPAM + for(i=0; nofrom[i]; i++) + if(strstr(FROM, nofrom[i])) + { + morelog_to(INNBBSD_SPAM, "spam from [%s]: %s\n", nofrom[i], FROM); + morelog_to(INNBBSD_SPAM, " %s %s %s %s\n", FROM, PATH, GROUPS, SUBJECT); + bbslog(":Ptt: spam from [%s]: %s\n", nofrom[i], FROM); + return 0; + } +#endif + } + + if(!BODY) + { + bbslog(":Err: article without body %s\n", MSGID); + return 0; + } + else + { +#ifdef HMM_USE_ANTI_SPAM + for(i=0; nocont[i]; i++) + if(strstr(BODY, nocont[i])) + { + morelog_to(INNBBSD_SPAM, "spam body [%s]: %s\n", nocont[i]); + morelog_to(INNBBSD_SPAM, " %s %s %s %s\n", FROM, PATH, GROUPS, SUBJECT); + bbslog(":Ptt: spam body [%s]: %s\n", nocont[i]); + return 0; + } +#endif + } + + if(!SUBJECT) + { + bbslog(":Err: article without subject %s\n", MSGID); + return 0; + } + else + { +#ifdef HMM_USE_ANTI_SPAM + for(i=0; notitle[i]; i++) + if(strstr(SUBJECT, notitle[i])) + { + morelog_to(INNBBSD_SPAM, "spam title [%s]: %s\n", notitle[i], SUBJECT); + morelog_to(INNBBSD_SPAM, " %s %s %s %s\n", FROM, PATH, GROUPS, SUBJECT); + bbslog(":Ptt: spam title [%s]: %s\n", notitle[i], SUBJECT); + return 0; + } +#endif + } + + + user = (char *) strchr(FROM, '@'); + lesssym = (char *) strchr(FROM, '<'); + nameptrleft = NULL, nameptrright = NULL; + if (lesssym == NULL || lesssym >= user) + { + lesssym = FROM; + nameptrleft = strchr(FROM, '('); + if (nameptrleft != NULL) + nameptrleft++; + nameptrright = strrchr(FROM, ')'); + } + else + { + nameptrleft = FROM; + nameptrright = strrchr(FROM, '<'); + lesssym++; + } + if (user != NULL) + { + *user = '\0'; + userptr = (char *) strchr(FROM, '.'); + if (userptr != NULL) + { + *userptr = '\0'; + strncpy(userid, lesssym, sizeof userid); + *userptr = '.'; + } + else + { + strncpy(userid, lesssym, sizeof userid); + } + *user = '@'; + } + else + { + strncpy(userid, lesssym, sizeof userid); + } + strcat(userid, "."); + + { + struct tm tmbuf; + + strptime(DATE, "%d %b %Y %X GMT", &tmbuf); + datevalue = timegm(&tmbuf); + } + + if (datevalue > 0) + { + char *p; + strncpy(xdate, ctime(&datevalue), sizeof(xdate)); + p = (char *) strchr(xdate, '\n'); + if (p != NULL) + *p = '\0'; + DATE = xdate; + } + +#ifndef MapleBBS + if (SITE == NULL || *SITE == '\0') + { + if (nameptrleft != NULL && nameptrright != NULL) + { + char savech = *nameptrright; + *nameptrright = '\0'; + strncpy(sitebuf, nameptrleft, sizeof sitebuf); + *nameptrright = savech; + SITE = sitebuf; + } + else + /* SITE = "(Unknown)"; */ + SITE = ""; + } + if (strlen(MYBBSID) > 70) + { + bbslog(" :Err: your bbsid %s too long\n", MYBBSID); + return 0; + } +#endif + + sprintf(xpath, "%s!%.*s", MYBBSID, sizeof(xpath) - strlen(MYBBSID) - 2, PATH); + PATH = xpath; + for (pathptr = PATH; pathptr != NULL && (pathptr = strstr(pathptr, ".edu.tw")) != NULL;) + { + if (pathptr != NULL) + { + strcpy(pathptr, pathptr + 7); + } + } + xpath[71] = '\0'; + +#ifndef MapleBBS + echomaillog(); +#endif + + *hispaths = '\0'; + splitptr = (char **) BNGsplit(GROUPS); + firstpath[0] = '\0'; + firstpathbase = firstpath; + + for (ngptr = *splitptr; ngptr != NULL; ngptr = *(++splitptr)) + { + char *boardptr, *nboardptr; + + if (*ngptr == '\0') + continue; + nf = (newsfeeds_t *) search_group(ngptr); + if (nf == NULL) + { + bbslog("unwanted \'%s\'\n", ngptr); + continue; + } + if (nf->board == NULL || !*nf->board) + continue; + if (nf->path == NULL || !*nf->path) + continue; + for (boardptr = nf->board, nboardptr = (char *) strchr(boardptr, ','); boardptr != NULL && *boardptr != '\0'; nboardptr = (char *) strchr(boardptr, ',')) + { + if (nboardptr != NULL) + { + *nboardptr = '\0'; + } + if (*boardptr == '\t') + { + goto boardcont; + } + boardhome = (char *) fileglue("%s/boards/%s", BBSHOME, boardptr); + if (!isdir(boardhome)) + { + bbslog(":Err: unable to write %s\n", boardhome); + } + else + { + char *fname; + /* + * if ( !isdir( boardhome )) { bbslog( ":Err: unable to write + * %s\n",boardhome); testandmkdir(boardhome); } + */ + fname = (char *) post_article(boardhome, userid, boardptr, + bbspost_write_post, NULL, firstpath); + if (fname != NULL) + { + fname = (char *) fileglue("%s/%s", boardptr, fname); + if (firstpath[0] == '\0') + { + sprintf(firstpath, "%s/boards/%s", BBSHOME, fname); + firstpathbase = firstpath + strlen(BBSHOME) + strlen("/boards/"); + } + if (strlen(fname) + strlen(hispaths) + 1 < sizeof(hispaths)) + { + strcat(hispaths, fname); + strcat(hispaths, " "); + } + } + else + { + bbslog("fname is null %s\n", boardhome); + return -1; + } + } + + boardcont: + if (nboardptr != NULL) + { + *nboardptr = ','; + boardptr = nboardptr + 1; + } + else + break; + + } /* for board1,board2,... */ + /* + * if (nngptr != NULL) ngptr = nngptr + 1; else break; + */ + if (*firstpathbase) + feedfplog(nf, firstpathbase, 'P'); + } + if (*hispaths) + bbsfeedslog(hispaths, 'P'); + + if (Junkhistory || *hispaths) + { + if (storeDB(HEADER[MID_H], hispaths) < 0) + { + bbslog("store DB fail\n"); + /* I suspect here will introduce duplicated articles */ + /* return -1; */ + } + } + return 0; +} + +receive_control() +{ + char *boardhome, *fname; + char firstpath[MAXPATHLEN], *firstpathbase; + char **splitptr, *ngptr; + newsfeeds_t *nf; + + bbslog("control post %s\n", HEADER[CONTROL_H]); + boardhome = (char *) fileglue("%s/boards/control", BBSHOME); + testandmkdir(boardhome); + *firstpath = '\0'; + if (isdir(boardhome)) + { + fname = (char *) post_article(boardhome, FROM, "control", bbspost_write_control, NULL, firstpath); + if (fname != NULL) + { + if (firstpath[0] == '\0') + sprintf(firstpath, "%s/boards/control/%s", BBSHOME, fname); + if (storeDB(HEADER[MID_H], (char *) fileglue("control/%s", fname)) < 0) + { + } + bbsfeedslog(fileglue("control/%s", fname), 'C'); + firstpathbase = firstpath + strlen(BBSHOME) + strlen("/boards/"); + splitptr = (char **) BNGsplit(GROUPS); + for (ngptr = *splitptr; ngptr != NULL; ngptr = *(++splitptr)) + { + if (*ngptr == '\0') + continue; + nf = (newsfeeds_t *) search_group(ngptr); + if (nf == NULL) + continue; + if (nf->board == NULL) + continue; + if (nf->path == NULL) + continue; + feedfplog(nf, firstpathbase, 'C'); + } + } + } + return 0; +} + +cancel_article_front(msgid) + char *msgid; +{ + char *ptr = (char *) DBfetch(msgid); + char *filelist, filename[2048]; + char histent[4096]; + char firstpath[MAXPATHLEN], *firstpathbase; + if (ptr == NULL) + { + bbslog("cancel failed(DBfetch): %s\n", msgid); + return 0; + } + strncpy(histent, ptr, sizeof histent); + ptr = histent; + +#ifdef DEBUG + printf("**** try to cancel %s *****\n", ptr); +#endif + + filelist = strchr(ptr, '\t'); + if (filelist != NULL) + { + filelist++; + } + *firstpath = '\0'; + for (ptr = filelist; ptr && *ptr;) + { + char *file; + for (; *ptr && isspace(*ptr); ptr++); + if (*ptr == '\0') + break; + file = ptr; + for (ptr++; *ptr && !isspace(*ptr); ptr++); + if (*ptr != '\0') + { + *ptr++ = '\0'; + } + sprintf(filename, "%s/boards/%s", BBSHOME, file); + bbslog("cancel post %s\n", filename); + if (isfile(filename)) + { + FILE *fp = fopen(filename, "r"); + char buffer[1024]; + char xfrom0[100], xfrom[100], xpath[1024], *boardhome; + + if (fp == NULL) + continue; + strncpy(xfrom0, HEADER[FROM_H], 99); + xfrom0[99] = 0; + strtok(xfrom0, ", "); + while (fgets(buffer, sizeof buffer, fp) != NULL) + { + char *hptr; + if (buffer[0] == '\n') + break; + hptr = strchr(buffer, '\n'); + if (hptr != NULL) + *hptr = '\0'; + if (strncmp(buffer, "發信人: ", 8) == 0) + { + strncpy(xfrom, buffer + 8, 99); + xfrom[99] = 0; + strtok(xfrom, ", "); + } + else if (strncmp(buffer, "轉信站: ", 8) == 0) + { + strcpy(xpath, buffer + 8); + } + } + fclose(fp); + if (strcmp(xfrom0, xfrom)) + { + bbslog("Invalid cancel %s, path: %s!%s, [`%s` != `%s`]\n", + FROM, MYBBSID, PATH, xfrom0, xfrom); + return 0; + } + +#ifdef KEEP_NETWORK_CANCEL + bbslog("cancel post %s\n", filename); + boardhome = (char *) fileglue("%s/boards/deleted", BBSHOME); + testandmkdir(boardhome); + if (isdir(boardhome)) + { + char subject[1024]; + char *fname; + if (POSTHOST) + { + sprintf(subject, "cancel by: %.1000s", POSTHOST); + } + else + { + char *body, *body2; + body = strchr(BODY, '\r'); + if (body != NULL) + *body = '\0'; + body2 = strchr(BODY, '\n'); + if (body2 != NULL) + *body = '\0'; + sprintf(subject, "%.1000s", BODY); + if (body != NULL) + *body = '\r'; + if (body2 != NULL) + *body = '\n'; + } + if (*subject) + SUBJECT = subject; + fname = (char *) post_article(boardhome, FROM, "deleted", bbspost_write_cancel, filename, firstpath); + if (fname != NULL) + { + if (firstpath[0] == '\0') + { + sprintf(firstpath, "%s/boards/deleted/%s", BBSHOME, fname); + firstpathbase = firstpath + strlen(BBSHOME) + strlen("/boards/"); + } + if (storeDB(HEADER[MID_H], (char *) fileglue("deleted/%s", fname)) < 0) + { + /* should do something */ + bbslog("store DB fail\n"); + /* return -1; */ + } + bbsfeedslog(fileglue("deleted/%s", fname), 'D'); + +#ifdef OLDDISPATCH + { + char board[256]; + newsfeeds_t *nf; + char *filebase = filename + strlen(BBSHOME) + strlen("/boards/"); + char *filetail = strrchr(filename, '/'); + if (filetail != NULL) + { + strncpy(board, filebase, filetail - filebase); + nf = (newsfeeds_t *) search_board(board); + if (nf != NULL && nf->board && nf->path) + { + feedfplog(nf, firstpathbase, 'D'); + } + } + } +#endif + } + else + { + bbslog(" fname is null %s %s\n", boardhome, filename); + return -1; + } + } +#else + /* bbslog("**** %s should be removed\n", filename); */ +/* + unlink(filename); +*/ +#endif + + { + char *fp = strrchr(file, '/'); + if (fp != NULL) + { + *fp = '\0'; + cancel_article(BBSHOME, file, fp + 1); + *fp = '/'; + } + } + } + } + if (*firstpath) + { + char **splitptr, *ngptr; + newsfeeds_t *nf; + splitptr = (char **) BNGsplit(GROUPS); + for (ngptr = *splitptr; ngptr != NULL; ngptr = *(++splitptr)) + { + if (*ngptr == '\0') + continue; + nf = (newsfeeds_t *) search_group(ngptr); + if (nf == NULL) + continue; + if (nf->board == NULL) + continue; + if (nf->path == NULL) + continue; + feedfplog(nf, firstpathbase, 'D'); + } + } + return 0; +} + + +#if defined(PhoenixBBS) || defined(SecretBBS) || defined(PivotBBS) || defined(MapleBBS) +/* for PhoenixBBS's post article and cancel article */ +#include "config.h" + + +char * +post_article(homepath, userid, board, writebody, pathname, firstpath) + char *homepath; + char *userid, *board; + int (*writebody) (); +char *pathname, *firstpath; +{ + struct userec_t record; + struct fileheader_t header; + char *subject = SUBJECT; + char index[MAXPATHLEN]; + static char name[MAXPATHLEN]; + char article[MAXPATHLEN]; + char buf[MAXPATHLEN], *ptr; + FILE *fidx; + int fh, bid; + time_t now; + int linkflag; +/* Ptt + if(bad_subject(subject)) return NULL; +*/ + sprintf(index, "%s/.DIR", homepath); + if ((fidx = fopen(index, "r")) == NULL) + { + if ((fidx = fopen(index, "w")) == NULL) + { + bbslog(":Err: Unable to post in %s.\n", homepath); + return NULL; + } + } + fclose(fidx); + + now = time(NULL); + while (1) + { + sprintf(name, "M.%d.A", ++now); + sprintf(article, "%s/%s", homepath, name); + fh = open(article, O_CREAT | O_EXCL | O_WRONLY, 0644); + if (fh >= 0) + break; + if (errno != EEXIST) + { + bbslog(" Err: can't writable or other errors\n"); + return NULL; + } + } + +#ifdef DEBUG + printf("post to %s\n", article); +#endif + + linkflag = 1; + if (firstpath && *firstpath) + { + close(fh); + unlink(article); + +#ifdef DEBUGLINK + bbslog("try to link %s to %s", firstpath, article); +#endif + + linkflag = link(firstpath, article); + if (linkflag) + { + fh = open(article, O_CREAT | O_EXCL | O_WRONLY, 0644); + } + } + if (linkflag) + { + if (writebody) + { + if ((*writebody) (fh, board, pathname) < 0) + return NULL; + } + else + { + if (bbspost_write_post(fh, board, pathname) < 0) + return NULL; + } + close(fh); + } + + bzero((void *) &header, sizeof(header)); + +#ifndef MapleBBS + strcpy(header.filename, name); + strncpy(header.owner, userid, IDLEN); + strncpy(header.title, subject, STRLEN); + header.filename[STRLEN - 1] = 'M'; +#else + + strcpy(header.filename, name); + if (userid[IDLEN]) + strcpy(&userid[IDLEN], "."); + strcpy(header.owner, userid); + strncpy(header.title, subject, TTLEN); + header.savemode = 'M'; + { + struct tm *ptime; + ptime = localtime(&datevalue); + sprintf(header.date, "%2d/%02d", ptime->tm_mon + 1, ptime->tm_mday); + } +#endif + + append_record(index, &header, sizeof(header)); + + if((bid = getbnum(board)) > 0) + touchbtotal(bid); + return name; +} + +/* +woju +Cross-fs rename() +*/ + +Rename(char* src, char* dst) +{ + char cmd[200]; + + bbslog("Rename: %s -> %s\n", src, dst); + if (rename(src, dst) == 0) + return 0; + + sprintf(cmd, "/bin/mv %s %s", src, dst); + return system(cmd); +} + + +cancelpost(fileheader_t *fhdr, char* boardname) +{ + int fd; + char fpath[MAXPATHLEN]; + + sprintf(fpath, BBSHOME "/boards/%s/%s", boardname, fhdr->filename); + if ((fd = open(fpath, O_RDONLY)) >= 0) { + fileheader_t postfile; + char fn2[MAXPATHLEN] = BBSHOME "/boards/deleted", *junkdir; + + stampfile(fn2, &postfile); + memcpy(postfile.owner, fhdr->owner, IDLEN + TTLEN + 10); + postfile.savemode = 'D'; + close(fd); + Rename(fpath, fn2); + strcpy(strrchr(fn2, '/') + 1, ".DIR"); + append_record(fn2, &postfile, sizeof(postfile)); + } + else + bbslog("cancelpost: %s opened error\n", fpath); +} + + +/* ---------------------------- */ +/* new/old/lock file processing */ +/* ---------------------------- */ + +typedef struct +{ + char newfn[MAXPATHLEN]; + char oldfn[MAXPATHLEN]; + char lockfn[MAXPATHLEN]; +} nol; + + +static void +nolfilename(n, fpath) + nol *n; + char *fpath; +{ + sprintf(n->newfn, "%s.new", fpath); + sprintf(n->oldfn, "%s.old", fpath); + sprintf(n->lockfn, "%s.lock", fpath); +} + + + +int +delete_record(char *fpath, int size, int id) +{ + nol my; + char abuf[512]; + int fdr, fdw, fd; + int count; + fileheader_t fhdr; + + nolfilename(&my, fpath); + + if ((fd = open(my.lockfn, O_RDWR | O_CREAT | O_APPEND, 0644)) == -1) + return -1; + flock(fd, LOCK_EX); + + if ((fdr = open(fpath, O_RDONLY, 0)) == -1) + { + +#ifdef HAVE_REPORT + report("delete_record failed!!! (open)"); +#endif + + flock(fd, LOCK_UN); + close(fd); + return -1; + } + if ((fdw = open(my.newfn, O_WRONLY | O_CREAT | O_EXCL, 0644)) == -1) + { + flock(fd, LOCK_UN); + +#ifdef HAVE_REPORT + report("delete_record failed!!! (open tmpfile)"); +#endif + + close(fd); + close(fdr); + return -1; + } + count = 1; + while (read(fdr, abuf, size) == size) + { + if (id == count) { + memcpy(&fhdr, abuf, sizeof(fhdr)); + bbslog("delete_record: %d, %s, %s\n", count, fhdr.owner, fhdr.title); + } + if (id != count++ && (write(fdw, abuf, size) == -1)) + { + + bbslog("delete_record: %s failed!!! (write)\n", fpath); +#ifdef HAVE_REPORT + report("delete_record failed!!! (write)"); +#endif + + unlink(my.newfn); + close(fdr); + close(fdw); + flock(fd, LOCK_UN); + close(fd); + return -1; + } + } + close(fdr); + close(fdw); + if (Rename(fpath, my.oldfn) == -1 || Rename(my.newfn, fpath) == -1) + { + +#ifdef HAVE_REPORT + report("delete_record failed!!! (Rename)"); +#endif + + flock(fd, LOCK_UN); + close(fd); + return -1; + } + flock(fd, LOCK_UN); + close(fd); + return 0; +} + +cancel_article(homepath, board, file) + char *homepath; + char *board, *file; +{ + struct fileheader_t header; + struct stat state; + char dirname[MAXPATHLEN]; + char buf[MAXPATHLEN]; + long numents, size, time, now; + int fd, lower, ent; + + + if (file == NULL || file[0] != 'M' || file[1] != '.' || + (time = atoi(file + 2)) <= 0) { + bbslog("cancel_article: invalid filename `%s`\n", file); + return 0; + } + size = sizeof(header); + sprintf(dirname, "%s/boards/%s/.DIR", homepath, board); + if ((fd = open(dirname, O_RDONLY)) == -1) { + bbslog("cancel_article: open `%s` error\n", dirname); + return 0; + } + fstat(fd, &state); + ent = ((long) state.st_size) / size; + lower = 0; + while (1) + { + ent -= 8; + if (ent <= 0 || lower >= 2) + break; + lseek(fd, size * ent, SEEK_SET); + if (read(fd, &header, size) != size) + { + ent = 0; + break; + } + now = atoi(header.filename + 2); + lower = (now < time) ? lower + 1 : 0; + } + if (ent < 0) + ent = 0; + while (read(fd, &header, size) == size) + { + if (strcmp(file, header.filename) == 0) + { + if ((header.filemode & FILE_MARKED) + || (header.filemode & FILE_DIGEST) || (header.owner[0] == '-')) + break; + delete_record(dirname, sizeof(fileheader_t), lseek(fd, 0, SEEK_CUR) / size); + cancelpost(&header, board); + break; + } + now = atoi(header.filename + 2); + if (now > time) + break; + } + close(fd); + return 0; +} + +#elif defined(PalmBBS) +# undef PATH XPATH +# undef HEADER XHEADER +#include "server.h" + +char * +post_article(homepath, userid, board, writebody, pathname, firstpath) + char *homepath; + char *userid, *board; + int (*writebody) (); +char *pathname, *firstpath; +{ + PATH msgdir, msgfile; + static PATH name; + + READINFO readinfo; + SHORT fileid; + char buf[MAXPATHLEN]; + struct stat stbuf; + int fh; + + strcpy(msgdir, homepath); + if (stat(msgdir, &stbuf) == -1 || !S_ISDIR(stbuf.st_mode)) + { + /* A directory is missing! */ + bbslog(":Err: Unable to post in %s.\n", msgdir); + return NULL; + } + get_filelist_ids(msgdir, &readinfo); + + for (fileid = 1; fileid <= BBS_MAX_FILES; fileid++) + { + int oumask; + if (test_readbit(&readinfo, fileid)) + continue; + fileid_to_fname(msgdir, fileid, msgfile); + sprintf(name, "%04x", fileid); + +#ifdef DEBUG + printf("post to %s\n", msgfile); +#endif + + if (firstpath && *firstpath) + { + +#ifdef DEBUGLINK + bbslog("try to link %s to %s", firstpath, msgfile); +#endif + + if (link(firstpath, msgfile) == 0) + break; + } + oumask = umask(0); + fh = open(msgfile, O_CREAT | O_EXCL | O_WRONLY, 0664); + umask(oumask); + if (writebody) + { + if ((*writebody) (fh, board, pathname) < 0) + return NULL; + } + else + { + if (bbspost_write_post(fh, board, pathname) < 0) + return NULL; + } + close(fh); + break; + } + +#ifdef CACHED_OPENBOARD + { + char *bname; + bname = strrchr(msgdir, '/'); + if (bname) + notify_new_post(++bname, 1, fileid, stbuf.st_mtime); + } +#endif + + return name; +} + +cancel_article(homepath, board, file) + char *homepath; + char *board, *file; +{ + PATH fname; + +#ifdef CACHED_OPENBOARD + PATH bdir; + struct stat stbuf; + + sprintf(bdir, "%s/boards/%s", homepath, board); + stat(bdir, &stbuf); +#endif + + sprintf(fname, "%s/boards/%s/%s", homepath, board, file); + unlink(fname); + /* kill it now! the function is far small then original.. :) */ + /* because it won't make system load heavy like before */ + +#ifdef CACHED_OPENBOARD + notify_new_post(board, -1, hex2SHORT(file), stbuf.st_mtime); +#endif +} + +#else +error("You should choose one of the systems: PhoenixBBS, PowerBBS, or PalmBBS") +#endif + +#else + +receive_article() +{ +} + +cancel_article_front(msgid) + char *msgid; +{ +} +#endif diff --git a/innbbsd/rfc931.c b/innbbsd/rfc931.c new file mode 100644 index 00000000..0d59d771 --- /dev/null +++ b/innbbsd/rfc931.c @@ -0,0 +1,144 @@ + /* + * rfc931_user() speaks a common subset of the RFC 931, AUTH, TAP and IDENT + * protocols. It consults an RFC 931 etc. compatible daemon on the client + * host to look up the remote user name. The information should not be used + * for authentication purposes. + * + * Diagnostics are reported through syslog(3). + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + * + * Inspired by the authutil package (comp.sources.unix volume 22) by Dan + * Bernstein (brnstnd@kramden.acf.nyu.edu). + */ + +#ifndef lint +static char sccsid[] = "@(#) rfc931.c 1.4 93/03/07 22:47:52"; +#endif + +#include <stdio.h> +#include <syslog.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <setjmp.h> +#include <signal.h> + +/*#include "log_tcp.h"*/ + +#define RFC931_PORT 113 /* Semi-well-known port */ + +#ifndef RFC931_TIMEOUT +#define RFC931_TIMEOUT 30 /* wait for at most 30 seconds */ +#endif + +extern char *strchr(); +extern char *inet_ntoa(); + +static jmp_buf timebuf; + +/* timeout - handle timeouts */ + +static void timeout(sig) +int sig; +{ + longjmp(timebuf, sig); +} + +/* rfc931_name - return remote user name */ + +char *my_rfc931_name(herefd,there) +int herefd; +struct sockaddr_in *there; /* remote link information */ +{ + struct sockaddr_in here; /* local link information */ + struct sockaddr_in sin; /* for talking to RFC931 daemon */ + int length; + int s; + unsigned remote; + unsigned local; + static char user[256]; /* XXX */ + char buffer[512]; /* YYY */ + FILE *fp; + char *cp; + char *result = "unknown"; + + /* Find out local address and port number of stdin. */ + + length = sizeof(here); + if (getsockname(herefd, (struct sockaddr *) & here, &length) == -1) { + syslog(LOG_ERR, "getsockname: %m"); + return (result); + } + + /* + * The socket that will be used for user name lookups should be bound to + * the same local IP address as stdin. This will automagically happen on + * hosts that have only one IP network address. When the local host has + * more than one IP network address, we must do an explicit bind() call. + */ + + if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) + return (result); + + sin = here; + sin.sin_port = 0; + if (bind(s, (struct sockaddr *) & sin, sizeof sin) < 0) { + syslog(LOG_ERR, "bind: %s: %m", inet_ntoa(here.sin_addr)); + return (result); + } + /* Set up timer so we won't get stuck. */ + + signal(SIGALRM, timeout); + if (setjmp(timebuf)) { + close(s); /* not: fclose(fp) */ + return (result); + } + alarm(RFC931_TIMEOUT); + + /* Connect to the RFC931 daemon. */ + + sin = *there; + sin.sin_port = htons(RFC931_PORT); + if (connect(s, (struct sockaddr *) & sin, sizeof(sin)) == -1 + || (fp = fdopen(s, "w+")) == 0) { + close(s); + alarm(0); + return (result); + } + + /* + * Use unbuffered I/O or we may read back our own query. setbuf() must be + * called before doing any I/O on the stream. Thanks for the reminder, + * Paul Kranenburg <pk@cs.few.eur.nl>! + */ + + setbuf(fp, (char *) 0); + + /* Query the RFC 931 server. Would 13-byte writes ever be broken up? */ + + fprintf(fp, "%u,%u\r\n", ntohs(there->sin_port), ntohs(here.sin_port)); + fflush(fp); + + /* + * Read response from server. Use fgets()/sscanf() instead of fscanf() + * because there is no buffer for pushback. Thanks, Chris Turbeville + * <turbo@cse.uta.edu>. + */ + + if (fgets(buffer, sizeof(buffer), fp) != 0 + && ferror(fp) == 0 && feof(fp) == 0 + && sscanf(buffer, "%u , %u : USERID :%*[^:]:%255s", + &remote, &local, user) == 3 + && ntohs(there->sin_port) == remote + && ntohs(here.sin_port) == local) { + /* Strip trailing carriage return. */ + + if (cp = strchr(user, '\r')) + *cp = 0; + result = user; + } + alarm(0); + fclose(fp); + return (result); +} |