Mercurial > noffle
changeset 43:2842f50feb55 noffle
[svn] * client.c, client.h, common.h, config.c, config.h, content.c, content.h,
control.c, control.h, database.c, database.h, dynamicstring.c,
dynamicstring.h, fetch.c, fetch.h, fetchlist.c, fetchlist.h, group.c,
group.h, itemlist.c, itemlist.h, lock.c, lock.h, log.c, log.h, noffle.c,
online.c, online.h, outgoing.c, outgoing.h, over.c, over.h, post.c, post.h,
protocol.c, protocol.h, pseudo.c, pseudo.h, request.c, request.h, server.c,
server.h, util.c, util.h, wildmat.c, wildmat.h: Moved files to the
subdirectory src/
* Makefile.am, acconfig.h, configure.in, docs/Makefile.am, src/Makefile.am,
Makefile.in, aclocal.m4, config.h.in, configure, install-sh, missing,
mkinstalldirs, stamp-h.in, docs/Makefile.in, src/Makefile.in: Added files.
They are used by aclocal, autoheader, autoconf and automake.
* src/config.c, src/config.h: Renamed to configfile.c and configfile.h,
because configure will generate a config.h file itself.
* src/client.c, src/content.c, src/database.c, src/fetch.c, src/fetchlist.c,
src/group.c, src/lock.c, src/noffle.c, src/online.c, src/outgoing.c,
src/over.c, src/pseudo.c, src/request.c, src/server.c, src/util.c:
Changed '#include "config.h"' to '#include "configfile.h"'.
* src/client.c, src/content.c, src/database.c, src/fetch.c, src/fetchlist.c,
src/group.c, src/lock.c, src/online.c, src/outgoing.c, src/post.c,
src/protocol.c, src/request.c, src/server.c: Files now #include <config.h>.
Added missing <stdio.h>. This removes the warnings about snprintf() not
being declared.
* Makefile: Removed. This is now generated by configure.
line wrap: on
line diff
--- a/ChangeLog Fri May 05 21:26:14 2000 +0100 +++ b/ChangeLog Fri May 05 22:45:56 2000 +0100 @@ -2,6 +2,40 @@ NOFFLE ChangeLog ------------------------------------------------------------------------------- + +Fri May 5 23:39:52 CEST 2000 Uwe Hermann <uh1763@bingo-ev.de> + + * client.c, client.h, common.h, config.c, config.h, content.c, content.h, + control.c, control.h, database.c, database.h, dynamicstring.c, + dynamicstring.h, fetch.c, fetch.h, fetchlist.c, fetchlist.h, group.c, + group.h, itemlist.c, itemlist.h, lock.c, lock.h, log.c, log.h, noffle.c, + online.c, online.h, outgoing.c, outgoing.h, over.c, over.h, post.c, post.h, + protocol.c, protocol.h, pseudo.c, pseudo.h, request.c, request.h, server.c, + server.h, util.c, util.h, wildmat.c, wildmat.h: Moved files to the + subdirectory src/ + + * Makefile.am, acconfig.h, configure.in, docs/Makefile.am, src/Makefile.am, + Makefile.in, aclocal.m4, config.h.in, configure, install-sh, missing, + mkinstalldirs, stamp-h.in, docs/Makefile.in, src/Makefile.in: Added files. + They are used by aclocal, autoheader, autoconf and automake. + + * src/config.c, src/config.h: Renamed to configfile.c and configfile.h, + because configure will generate a config.h file itself. + + * src/client.c, src/content.c, src/database.c, src/fetch.c, src/fetchlist.c, + src/group.c, src/lock.c, src/noffle.c, src/online.c, src/outgoing.c, + src/over.c, src/pseudo.c, src/request.c, src/server.c, src/util.c: + Changed '#include "config.h"' to '#include "configfile.h"'. + + * src/client.c, src/content.c, src/database.c, src/fetch.c, src/fetchlist.c, + src/group.c, src/lock.c, src/online.c, src/outgoing.c, src/post.c, + src/protocol.c, src/request.c, src/server.c: Files now #include <config.h>. + Added missing <stdio.h>. This removes the warnings about snprintf() not + being declared. + + * Makefile: Removed. This is now generated by configure. + + Fri May 5 22:24:37 CEST 2000 Uwe Hermann <uh1763@bingo-ev.de> * AUTHORS.html, CHANGELOG.html, COPYING.html, README.html, FAQ.html,
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile.am Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,54 @@ +# Process this file with automake to produce Makefile.in + +SUBDIRS = src docs + +EXTRA_DIST = $(PACKAGE).conf.example + +install-data-local: + $(INSTALL) -m 2755 -o news -g news -d $(SPOOLDIR) + $(INSTALL) -o news -g news -d $(SPOOLDIR)/data + $(INSTALL) -o news -g news -d $(SPOOLDIR)/lock + $(INSTALL) -o news -g news -d $(SPOOLDIR)/requested + $(INSTALL) -o news -g news -d $(SPOOLDIR)/outgoing + $(INSTALL) -o news -g news -d $(SPOOLDIR)/overview + chown -R news.news $(SPOOLDIR) + $(INSTALL) -m 755 -o 0 -g 0 -d $(DOCDIR) + $(INSTALL_DATA) -o 0 -g 0 $(srcdir)/AUTHORS $(DOCDIR) + $(INSTALL_DATA) -o 0 -g 0 $(srcdir)/COPYING $(DOCDIR) + $(INSTALL_DATA) -o 0 -g 0 $(srcdir)/ChangeLog $(DOCDIR) + $(INSTALL_DATA) -o 0 -g 0 $(srcdir)/INSTALL $(DOCDIR) + $(INSTALL_DATA) -o 0 -g 0 $(srcdir)/NEWS $(DOCDIR) + $(INSTALL_DATA) -o 0 -g 0 $(srcdir)/README $(DOCDIR) + $(INSTALL_DATA) -o 0 -g 0 $(srcdir)/TODO $(DOCDIR) + $(INSTALL_DATA) -o 0 -g 0 $(srcdir)/noffle.conf.example \ + /etc/noffle.conf.example + chown news.news /etc/noffle.conf.example + chmod 600 /etc/noffle.conf.example + @echo + @echo "****************************************************************" + @echo "You should now copy /etc/noffle.conf.example to /etc/noffle.conf" + @echo "and edit that copy." + @echo "****************************************************************" + @echo + +install-exec-local: + chown news.news $(bindir)/$(PACKAGE) + chmod 4755 $(bindir)/$(PACKAGE) + +uninstall-local: + rm -f $(DOCDIR)/AUTHORS + rm -f $(DOCDIR)/COPYING + rm -f $(DOCDIR)/ChangeLog + rm -f $(DOCDIR)/INSTALL + rm -f $(DOCDIR)/NEWS + rm -f $(DOCDIR)/README + rm -f $(DOCDIR)/TODO + rm -f /etc/noffle.conf.example + @echo + @echo "****************************************************************" + @echo "The NOFFLE spool directory $(SPOOLDIR) and the configfile" + @echo "$(CONFIGFILE) have *not* been removed. Please do this" + @echo "manually." + @echo "****************************************************************" + @echo +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile.in Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,408 @@ +# Makefile.in generated automatically by automake 1.4 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +# Process this file with automake to produce Makefile.in + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = . + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +CC = @CC@ +CONFIGFILE = @CONFIGFILE@ +DOCDIR = @DOCDIR@ +MAKEINFO = @MAKEINFO@ +PACKAGE = @PACKAGE@ +SPOOLDIR = @SPOOLDIR@ +VERSION = @VERSION@ + +SUBDIRS = src docs + +EXTRA_DIST = $(PACKAGE).conf.example +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = config.h +CONFIG_CLEAN_FILES = +DIST_COMMON = README ./stamp-h.in AUTHORS COPYING ChangeLog INSTALL \ +Makefile.am Makefile.in NEWS TODO acconfig.h aclocal.m4 config.h.in \ +configure configure.in install-sh missing mkinstalldirs + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = tar +GZIP_ENV = --best +all: all-redirect +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --gnu Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status $(BUILT_SOURCES) + cd $(top_builddir) \ + && CONFIG_FILES=$@ CONFIG_HEADERS= $(SHELL) ./config.status + +$(ACLOCAL_M4): configure.in + cd $(srcdir) && $(ACLOCAL) + +config.status: $(srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck +$(srcdir)/configure: $(srcdir)/configure.in $(ACLOCAL_M4) $(CONFIGURE_DEPENDENCIES) + cd $(srcdir) && $(AUTOCONF) + +config.h: stamp-h + @if test ! -f $@; then \ + rm -f stamp-h; \ + $(MAKE) stamp-h; \ + else :; fi +stamp-h: $(srcdir)/config.h.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES= CONFIG_HEADERS=config.h \ + $(SHELL) ./config.status + @echo timestamp > stamp-h 2> /dev/null +$(srcdir)/config.h.in: $(srcdir)/stamp-h.in + @if test ! -f $@; then \ + rm -f $(srcdir)/stamp-h.in; \ + $(MAKE) $(srcdir)/stamp-h.in; \ + else :; fi +$(srcdir)/stamp-h.in: $(top_srcdir)/configure.in $(ACLOCAL_M4) acconfig.h + cd $(top_srcdir) && $(AUTOHEADER) + @echo timestamp > $(srcdir)/stamp-h.in 2> /dev/null + +mostlyclean-hdr: + +clean-hdr: + +distclean-hdr: + -rm -f config.h + +maintainer-clean-hdr: + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. + +@SET_MAKE@ + +all-recursive install-data-recursive install-exec-recursive \ +installdirs-recursive install-recursive uninstall-recursive \ +check-recursive installcheck-recursive info-recursive dvi-recursive: + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + rev=''; list='$(SUBDIRS)'; for subdir in $$list; do \ + rev="$$subdir $$rev"; \ + test "$$subdir" = "." && dot_seen=yes; \ + done; \ + test "$$dot_seen" = "no" && rev=". $$rev"; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + here=`pwd` && cd $(srcdir) \ + && mkid -f$$here/ID $$unique $(LISP) + +TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -f $$subdir/TAGS && tags="$$tags -i $$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)config.h.in$$unique$(LISP)$$tags" \ + || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags config.h.in $$unique $(LISP) -o $$here/TAGS) + +mostlyclean-tags: + +clean-tags: + +distclean-tags: + -rm -f TAGS ID + +maintainer-clean-tags: + +distdir = $(PACKAGE)-$(VERSION) +top_distdir = $(distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + -rm -rf $(distdir) + GZIP=$(GZIP_ENV) $(TAR) zxf $(distdir).tar.gz + mkdir $(distdir)/=build + mkdir $(distdir)/=inst + dc_install_base=`cd $(distdir)/=inst && pwd`; \ + cd $(distdir)/=build \ + && ../configure --srcdir=.. --prefix=$$dc_install_base \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) dvi \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) dist + -rm -rf $(distdir) + @banner="$(distdir).tar.gz is ready for distribution"; \ + dashes=`echo "$$banner" | sed s/./=/g`; \ + echo "$$dashes"; \ + echo "$$banner"; \ + echo "$$dashes" +dist: distdir + -chmod -R a+r $(distdir) + GZIP=$(GZIP_ENV) $(TAR) chozf $(distdir).tar.gz $(distdir) + -rm -rf $(distdir) +dist-all: distdir + -chmod -R a+r $(distdir) + GZIP=$(GZIP_ENV) $(TAR) chozf $(distdir).tar.gz $(distdir) + -rm -rf $(distdir) +distdir: $(DISTFILES) + -rm -rf $(distdir) + mkdir $(distdir) + -chmod 777 $(distdir) + here=`cd $(top_builddir) && pwd`; \ + top_distdir=`cd $(distdir) && pwd`; \ + distdir=`cd $(distdir) && pwd`; \ + cd $(top_srcdir) \ + && $(AUTOMAKE) --include-deps --build-dir=$$here --srcdir-name=$(top_srcdir) --output-dir=$$top_distdir --gnu Makefile + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done + for subdir in $(SUBDIRS); do \ + if test "$$subdir" = .; then :; else \ + test -d $(distdir)/$$subdir \ + || mkdir $(distdir)/$$subdir \ + || exit 1; \ + chmod 777 $(distdir)/$$subdir; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir=../$(distdir) distdir=../$(distdir)/$$subdir distdir) \ + || exit 1; \ + fi; \ + done +info-am: +info: info-recursive +dvi-am: +dvi: dvi-recursive +check-am: all-am +check: check-recursive +installcheck-am: +installcheck: installcheck-recursive +all-recursive-am: config.h + $(MAKE) $(AM_MAKEFLAGS) all-recursive + +install-exec-am: install-exec-local +install-exec: install-exec-recursive + +install-data-am: install-data-local +install-data: install-data-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-recursive +uninstall-am: uninstall-local +uninstall: uninstall-recursive +all-am: Makefile config.h +all-redirect: all-recursive-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: installdirs-recursive +installdirs-am: + + +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-hdr mostlyclean-tags mostlyclean-generic + +mostlyclean: mostlyclean-recursive + +clean-am: clean-hdr clean-tags clean-generic mostlyclean-am + +clean: clean-recursive + +distclean-am: distclean-hdr distclean-tags distclean-generic clean-am + +distclean: distclean-recursive + -rm -f config.status + +maintainer-clean-am: maintainer-clean-hdr maintainer-clean-tags \ + maintainer-clean-generic distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-recursive + -rm -f config.status + +.PHONY: mostlyclean-hdr distclean-hdr clean-hdr maintainer-clean-hdr \ +install-data-recursive uninstall-data-recursive install-exec-recursive \ +uninstall-exec-recursive installdirs-recursive uninstalldirs-recursive \ +all-recursive check-recursive installcheck-recursive info-recursive \ +dvi-recursive mostlyclean-recursive distclean-recursive clean-recursive \ +maintainer-clean-recursive tags tags-recursive mostlyclean-tags \ +distclean-tags clean-tags maintainer-clean-tags distdir info-am info \ +dvi-am dvi check check-am installcheck-am installcheck all-recursive-am \ +install-exec-local install-exec-am install-exec install-data-local \ +install-data-am install-data install-am install uninstall-local \ +uninstall-am uninstall all-redirect all-am all installdirs-am \ +installdirs mostlyclean-generic distclean-generic clean-generic \ +maintainer-clean-generic clean mostlyclean distclean maintainer-clean + + +install-data-local: + $(INSTALL) -m 2755 -o news -g news -d $(SPOOLDIR) + $(INSTALL) -o news -g news -d $(SPOOLDIR)/data + $(INSTALL) -o news -g news -d $(SPOOLDIR)/lock + $(INSTALL) -o news -g news -d $(SPOOLDIR)/requested + $(INSTALL) -o news -g news -d $(SPOOLDIR)/outgoing + $(INSTALL) -o news -g news -d $(SPOOLDIR)/overview + chown -R news.news $(SPOOLDIR) + $(INSTALL) -m 755 -o 0 -g 0 -d $(DOCDIR) + $(INSTALL_DATA) -o 0 -g 0 $(srcdir)/AUTHORS $(DOCDIR) + $(INSTALL_DATA) -o 0 -g 0 $(srcdir)/COPYING $(DOCDIR) + $(INSTALL_DATA) -o 0 -g 0 $(srcdir)/ChangeLog $(DOCDIR) + $(INSTALL_DATA) -o 0 -g 0 $(srcdir)/INSTALL $(DOCDIR) + $(INSTALL_DATA) -o 0 -g 0 $(srcdir)/NEWS $(DOCDIR) + $(INSTALL_DATA) -o 0 -g 0 $(srcdir)/README $(DOCDIR) + $(INSTALL_DATA) -o 0 -g 0 $(srcdir)/TODO $(DOCDIR) + $(INSTALL_DATA) -o 0 -g 0 $(srcdir)/noffle.conf.example \ + /etc/noffle.conf.example + chown news.news /etc/noffle.conf.example + chmod 600 /etc/noffle.conf.example + @echo + @echo "****************************************************************" + @echo "You should now copy /etc/noffle.conf.example to /etc/noffle.conf" + @echo "and edit that copy." + @echo "****************************************************************" + @echo + +install-exec-local: + chown news.news $(bindir)/$(PACKAGE) + chmod 4755 $(bindir)/$(PACKAGE) + +uninstall-local: + rm -f $(DOCDIR)/AUTHORS + rm -f $(DOCDIR)/COPYING + rm -f $(DOCDIR)/ChangeLog + rm -f $(DOCDIR)/INSTALL + rm -f $(DOCDIR)/NEWS + rm -f $(DOCDIR)/README + rm -f $(DOCDIR)/TODO + rm -f /etc/noffle.conf.example + @echo + @echo "****************************************************************" + @echo "The NOFFLE spool directory $(SPOOLDIR) and the configfile" + @echo "$(CONFIGFILE) have *not* been removed. Please do this" + @echo "manually." + @echo "****************************************************************" + @echo + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/acconfig.h Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,25 @@ +/* For use by autoheader */ + +#ifndef CONFIG_H +#define CONFIG_H /* To stop multiple inclusions. */ + +/* The NOFFLE config-file */ +#undef CONFIGFILE + +/* The default NOFFLE spool-directory */ +#undef SPOOLDIR + +/* Enable GNU extensions. */ +#undef _GNU_SOURCE + +/* Define to 'unsigned int' if not already defined by the system. */ +#undef socklen_t + + +@TOP@ +/* autoheader generated things inserted here. */ +@BOTTOM@ + + +#endif /* CONFIG_H */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/aclocal.m4 Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,127 @@ +dnl aclocal.m4 generated automatically by aclocal 1.4 + +dnl Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl This program is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY, to the extent permitted by law; without +dnl even the implied warranty of MERCHANTABILITY or FITNESS FOR A +dnl PARTICULAR PURPOSE. + +# Like AC_CONFIG_HEADER, but automatically create stamp file. + +AC_DEFUN(AM_CONFIG_HEADER, +[AC_PREREQ([2.12]) +AC_CONFIG_HEADER([$1]) +dnl When config.status generates a header, we must update the stamp-h file. +dnl This file resides in the same directory as the config header +dnl that is generated. We must strip everything past the first ":", +dnl and everything past the last "/". +AC_OUTPUT_COMMANDS(changequote(<<,>>)dnl +ifelse(patsubst(<<$1>>, <<[^ ]>>, <<>>), <<>>, +<<test -z "<<$>>CONFIG_HEADERS" || echo timestamp > patsubst(<<$1>>, <<^\([^:]*/\)?.*>>, <<\1>>)stamp-h<<>>dnl>>, +<<am_indx=1 +for am_file in <<$1>>; do + case " <<$>>CONFIG_HEADERS " in + *" <<$>>am_file "*<<)>> + echo timestamp > `echo <<$>>am_file | sed -e 's%:.*%%' -e 's%[^/]*$%%'`stamp-h$am_indx + ;; + esac + am_indx=`expr "<<$>>am_indx" + 1` +done<<>>dnl>>) +changequote([,]))]) + +# Do all the work for Automake. This macro actually does too much -- +# some checks are only needed if your package does certain things. +# But this isn't really a big deal. + +# serial 1 + +dnl Usage: +dnl AM_INIT_AUTOMAKE(package,version, [no-define]) + +AC_DEFUN(AM_INIT_AUTOMAKE, +[AC_REQUIRE([AC_PROG_INSTALL]) +PACKAGE=[$1] +AC_SUBST(PACKAGE) +VERSION=[$2] +AC_SUBST(VERSION) +dnl test to see if srcdir already configured +if test "`cd $srcdir && pwd`" != "`pwd`" && test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) +fi +ifelse([$3],, +AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package]) +AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])) +AC_REQUIRE([AM_SANITY_CHECK]) +AC_REQUIRE([AC_ARG_PROGRAM]) +dnl FIXME This is truly gross. +missing_dir=`cd $ac_aux_dir && pwd` +AM_MISSING_PROG(ACLOCAL, aclocal, $missing_dir) +AM_MISSING_PROG(AUTOCONF, autoconf, $missing_dir) +AM_MISSING_PROG(AUTOMAKE, automake, $missing_dir) +AM_MISSING_PROG(AUTOHEADER, autoheader, $missing_dir) +AM_MISSING_PROG(MAKEINFO, makeinfo, $missing_dir) +AC_REQUIRE([AC_PROG_MAKE_SET])]) + +# +# Check to make sure that the build environment is sane. +# + +AC_DEFUN(AM_SANITY_CHECK, +[AC_MSG_CHECKING([whether build environment is sane]) +# Just in case +sleep 1 +echo timestamp > conftestfile +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt $srcdir/configure conftestfile 2> /dev/null` + if test "[$]*" = "X"; then + # -L didn't work. + set X `ls -t $srcdir/configure conftestfile` + fi + if test "[$]*" != "X $srcdir/configure conftestfile" \ + && test "[$]*" != "X conftestfile $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken +alias in your environment]) + fi + + test "[$]2" = conftestfile + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +rm -f conftest* +AC_MSG_RESULT(yes)]) + +dnl AM_MISSING_PROG(NAME, PROGRAM, DIRECTORY) +dnl The program must properly implement --version. +AC_DEFUN(AM_MISSING_PROG, +[AC_MSG_CHECKING(for working $2) +# Run test in a subshell; some versions of sh will print an error if +# an executable is not found, even if stderr is redirected. +# Redirect stdin to placate older versions of autoconf. Sigh. +if ($2 --version) < /dev/null > /dev/null 2>&1; then + $1=$2 + AC_MSG_RESULT(found) +else + $1="$3/missing $2" + AC_MSG_RESULT(missing) +fi +AC_SUBST($1)]) +
--- a/client.c Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,883 +0,0 @@ -/* - client.c - - $Id: client.c 44 2000-05-05 07:23:15Z enz $ -*/ - -#include "client.h" - -#include <arpa/inet.h> -#include <ctype.h> -#include <netdb.h> -#include <netinet/in.h> -#include <signal.h> -#include <stdarg.h> -#include <sys/socket.h> -#include <unistd.h> -#include "config.h" -#include "content.h" -#include "dynamicstring.h" -#include "group.h" -#include "log.h" -#include "over.h" -#include "protocol.h" -#include "pseudo.h" -#include "request.h" -#include "util.h" -#include "wildmat.h" - -/* - Some newsgroups names are reserved for server-specific or server - pseudo groups. We don't want to fetch them. For example, INN - keeps all its control messages in a 'control' hierarchy, and - used the "to." heirarchy for dark and mysterious purposes I think - are to do with newsfeeds. The recommended restrictions are documented - in C.Lindsay, "News Article Format", <draft-ietf-usefor-article-03.txt>. -*/ - -struct ForbiddenGroupName -{ - const char *pattern; - Bool match; -} forbiddenGroupNames[] = -{ - { "*.*", FALSE }, /* Single component */ - { "control.*", TRUE }, /* control.* groups */ - { "to.*", TRUE }, /* control.* groups */ - { "*.all", TRUE }, /* 'all' as a component */ - { "*.all.*", TRUE }, - { "all.*", TRUE }, - { "*.ctl", TRUE }, /* 'ctl' as a component */ - { "*.ctl.*", TRUE }, - { "ctl.*", TRUE } -}; - -struct -{ - FILE* in; /* Receiving socket from server */ - FILE* out; /* Sending socket to server */ - Str lastCmd; /* Last command line */ - Str lastStat; /* Response from server to last command */ - Str grp; /* Selected group */ - int rmtFirst; /* First article of current group at server */ - int rmtLast; /* Last article of current group at server */ - Bool auth; /* Authetication already done? */ - Str serv; /* Remote server name */ -} client = { NULL, NULL, "", "", "", 1, 0, FALSE, "" }; - -static void -logBreakDown( void ) -{ - Log_err( "Connection to remote server lost " - "(article numbers could be inconsistent)" ); -} - -static Bool -getLn( Str line ) -{ - Bool r; - - r = Prt_getLn( line, client.in ); - if ( ! r ) - logBreakDown(); - return r; -} - -static Bool -getTxtLn( Str line, Bool *err ) -{ - Bool r; - - r = Prt_getTxtLn( line, err, client.in ); - if ( *err ) - logBreakDown(); - return r; -} - -static void -putTxtBuf( const char *buf ) -{ - Prt_putTxtBuf( buf, client.out ); - fflush( client.out ); - Log_dbg( "[S FLUSH]" ); -} - -static void -putEndOfTxt( void ) -{ - Prt_putEndOfTxt( client.out ); - fflush( client.out ); - Log_dbg( "[S FLUSH]" ); -} - -static Bool -putCmd( const char *fmt, ... ) -{ - Bool err; - unsigned int n; - Str line; - va_list ap; - - va_start( ap, fmt ); - vsnprintf( line, MAXCHAR, fmt, ap ); - va_end( ap ); - strcpy( client.lastCmd, line ); - Log_dbg( "[S] %s", line ); - n = fprintf( client.out, "%s\r\n", line ); - fflush( client.out ); - Log_dbg( "[S FLUSH]" ); - err = ( n != strlen( line ) + 2 ); - if ( err ) - logBreakDown();; - return ! err; -} - -static Bool -putCmdNoFlush( const char *fmt, ... ) -{ - Bool err; - unsigned int n; - Str line; - va_list ap; - - va_start( ap, fmt ); - vsnprintf( line, MAXCHAR, fmt, ap ); - va_end( ap ); - strcpy( client.lastCmd, line ); - Log_dbg( "[S] %s", line ); - n = fprintf( client.out, "%s\r\n", line ); - err = ( n != strlen( line ) + 2 ); - if ( err ) - logBreakDown();; - return ! err; -} - -static int getStat( void ); - -static Bool -performAuth( void ) -{ - int stat; - Str user, pass; - - Cfg_authInfo( client.serv, user, pass ); - if ( strcmp( user, "" ) == 0 ) - { - Log_err( "No username for authentication set" ); - return FALSE; - } - putCmd( "AUTHINFO USER %s", user ); - stat = getStat(); - if ( stat == STAT_AUTH_ACCEPTED ) - return TRUE; - else if ( stat != STAT_MORE_AUTH_REQUIRED ) - { - Log_err( "Username rejected. Server stat: %s", client.lastStat ); - return FALSE; - } - if ( strcmp( pass, "" ) == 0 ) - { - Log_err( "No password for authentication set" ); - return FALSE; - } - putCmd( "AUTHINFO PASS %s", pass ); - stat = getStat(); - if ( stat != STAT_AUTH_ACCEPTED ) - { - Log_err( "Password rejected. Server status: %s", client.lastStat ); - return FALSE; - } - return TRUE; -} - -static int -getStat( void ) -{ - int result; - Str lastCmd; - - if ( ! getLn( client.lastStat ) ) - result = STAT_PROGRAM_FAULT; - else if ( sscanf( client.lastStat, "%d", &result ) != 1 ) - { - Log_err( "Invalid server status: %s", client.lastStat ); - result = STAT_PROGRAM_FAULT; - } - if ( result == STAT_AUTH_REQUIRED && ! client.auth ) - { - client.auth = TRUE; - strcpy( lastCmd, client.lastCmd ); - if ( performAuth() ) - { - putCmd( lastCmd ); - return getStat(); - } - } - return result; -} - -static void -connectAlarm( int sig ) -{ - return; -} - -static sig_t -installSignalHandler( int sig, sig_t handler ) -{ - struct sigaction act, oldAct; - - act.sa_handler = handler; - sigemptyset( &act.sa_mask ); - act.sa_flags = 0; - if ( sig == SIGALRM ) - act.sa_flags |= SA_INTERRUPT; - else - act.sa_flags |= SA_RESTART; - if ( sigaction( sig, &act, &oldAct ) < 0 ) - return SIG_ERR; - return oldAct.sa_handler; -} - -static Bool -connectWithTimeout( int sock, const struct sockaddr *servAddr, - socklen_t addrLen ) -{ - sig_t oldHandler; - int r, to; - - oldHandler = installSignalHandler( SIGALRM, connectAlarm ); - if ( oldHandler == SIG_ERR ) - { - Log_err( "client.c:connectWithTimeout: signal failed." ); - return FALSE; - } - to = Cfg_connectTimeout(); - if ( alarm( to ) != 0 ) - Log_err( "client.c:connectWithTimeout: Alarm was already set." ); - r = connect( sock, servAddr, addrLen ); - alarm( 0 ); - installSignalHandler( SIGALRM, oldHandler ); - return ( r >= 0 ); -} - -Bool -Client_connect( const char *serv ) -{ - unsigned short int port; - int sock, i; - unsigned int stat; - struct hostent *hp; - char *pStart, *pColon; - Str host, s; - struct sockaddr_in sIn; - - client.auth = FALSE; - Utl_cpyStr( s, serv ); - pStart = Utl_stripWhiteSpace( s ); - pColon = strstr( pStart, ":" ); - if ( pColon == NULL ) - { - strcpy( host, pStart ); - port = 119; - } - else - { - *pColon = '\0'; - strcpy( host, pStart ); - if ( sscanf( pColon + 1, "%hi", &port ) != 1 ) - { - Log_err( "Syntax error in server name: '%s'", serv ); - return FALSE;; - } - if ( port <= 0 || port > 65535 ) - { - Log_err( "Invalid port number %hi. Must be in [1, 65535]", port ); - return FALSE;; - } - } - memset( (void *)&sIn, 0, sizeof( sIn ) ); - hp = gethostbyname( host ); - if ( hp ) - { - for ( i = 0; (hp->h_addr_list)[ i ]; ++i ) - { - sIn.sin_family = hp->h_addrtype; - sIn.sin_port = htons( port ); - sIn.sin_addr = *( (struct in_addr *)hp->h_addr_list[ i ] ); - sock = socket( AF_INET, SOCK_STREAM, 0 ); - if ( sock < 0 ) - break; - if ( ! connectWithTimeout( sock, (struct sockaddr *)&sIn, - sizeof( sIn ) ) ) - { - close( sock ); - break; - } - if ( ! ( client.out = fdopen( sock, "w" ) ) - || ! ( client.in = fdopen( dup( sock ), "r" ) ) ) - { - if ( client.out != NULL ) - fclose( client.out ); - close( sock ); - break; - } - stat = getStat(); - if ( stat == STAT_READY_POST_ALLOW || - stat == STAT_READY_NO_POST_ALLOW ) - { - /* INN needs a MODE READER before it will permit POST. */ - putCmd( "MODE READER" ); - stat = getStat(); - } - switch( stat ) { - case STAT_READY_POST_ALLOW: - case STAT_READY_NO_POST_ALLOW: - Log_inf( "Connected to %s:%d", - inet_ntoa( sIn.sin_addr ), port ); - Utl_cpyStr( client.serv, serv ); - return TRUE; - default: - Log_err( "Bad server stat %d", stat ); - } - shutdown( fileno( client.out ), 0 ); - fclose( client.in ); - fclose( client.out ); - close( sock ); - } - } - return FALSE; -} - -static Bool -isForbiddenGroupName( const char *name ) -{ - int i; - - for ( i = 0; - i < sizeof( forbiddenGroupNames ) / - sizeof( struct ForbiddenGroupName ); - i++ ) - { - /* Negate result of Wld_match to ensure it is 1 or 0. */ - if ( forbiddenGroupNames[i].match != - ( ! Wld_match( name, forbiddenGroupNames[i].pattern ) ) ) - return TRUE; - } - - return FALSE; -} - -static void -processGrps( void ) -{ - char postAllow; - Bool err; - int first, last; - Str grp, line, file; - - while ( getTxtLn( line, &err ) && ! err ) - { - if ( sscanf( line, "%s %d %d %c", - grp, &last, &first, &postAllow ) != 4 ) - { - Log_err( "Unknown reply to LIST or NEWGROUPS: %s", line ); - continue; - } - if ( isForbiddenGroupName( grp ) ) - { - Log_inf( "Group %s forbidden", grp ); - continue; - } - if ( ! Grp_exists( grp ) ) - { - Log_inf( "Registering new group '%s'", grp ); - Grp_create( grp ); - Grp_setRmtNext( grp, first ); - Grp_setServ( grp, client.serv ); - Grp_setPostAllow( grp, postAllow ); - } - else - { - if ( Cfg_servIsPreferential( client.serv, Grp_serv( grp ) ) ) - { - Log_inf( "Changing server for '%s': '%s'->'%s'", - grp, Grp_serv( grp ), client.serv ); - Grp_setServ( grp, client.serv ); - Grp_setRmtNext( grp, first ); - Grp_setPostAllow( grp, postAllow ); - } - else - Log_dbg( "Group %s is already fetched from %s", - grp, Grp_serv( grp ) ); - - } - } - if ( ! err ) - { - snprintf( file, MAXCHAR, "%s/groupinfo.lastupdate", Cfg_spoolDir() ); - Utl_stamp( file ); - } -} - -void -Client_disconnect( void ) -{ - if ( putCmd( "QUIT" ) ) - getStat(); - fclose( client.in ); - fclose( client.out ); - client.in = client.out = NULL; -} - -Bool -Client_getGrps( void ) -{ - if ( ! putCmd( "LIST ACTIVE" ) ) - return FALSE; - if ( getStat() != STAT_GRPS_FOLLOW ) - { - Log_err( "LIST ACTIVE command failed: %s", client.lastStat ); - return FALSE; - } - processGrps(); - return TRUE; -} - -Bool -Client_getDsc( void ) -{ - Bool err; - Str name, line, dsc; - - Log_inf( "Querying group descriptions" ); - if ( ! putCmd( "LIST NEWSGROUPS" ) ) - return FALSE; - if ( getStat() != STAT_GRPS_FOLLOW ) - { - Log_err( "LIST NEWSGROUPS failed: %s", client.lastStat ); - return FALSE; - } - while ( getTxtLn( line, &err ) && ! err ) - { - if ( sscanf( line, "%s", name ) != 1 ) - { - Log_err( "Unknown reply to LIST NEWSGROUPS: %s", line ); - continue; - } - strcpy( dsc, Utl_restOfLn( line, 1 ) ); - if ( Grp_exists( name ) ) - { - Log_dbg( "Description of %s: %s", name, dsc ); - Grp_setDsc( name, dsc ); - } - } - return TRUE; -} - -Bool -Client_getCreationTimes( void ) -{ - Bool err; - Str name, line; - time_t t; - - Log_inf( "Querying group creation times" ); - if ( ! putCmd( "LIST ACTIVE.TIMES" ) ) - return FALSE; - if ( getStat() != STAT_GRPS_FOLLOW ) - { - Log_err( "LIST ACTIVE.TIMES failes: %s", client.lastStat ); - return FALSE; - } - while ( getTxtLn( line, &err ) && ! err ) - { - if ( sscanf( line, "%s %ld", name, &t ) != 2 ) - { - Log_err( "Unknown reply to LIST ACTIVE.TIMES: %s", line ); - continue; - } - if ( Grp_exists( name ) ) - { - Log_inf( "Creation time of %s: %ld", name, t ); - Grp_setCreated( name, t ); - } - } - return TRUE; -} - -Bool -Client_getNewgrps( const time_t *lastTime ) -{ - Str s; - const char *p; - - ASSERT( *lastTime > 0 ); - strftime( s, MAXCHAR, "%Y%m%d %H%M00", gmtime( lastTime ) ); - /* - Do not use century for working with old server software until 2000. - According to newest IETF draft, this is still valid after 2000. - (directly using %y in fmt string causes a Y2K compiler warning) - */ - p = s + 2; - if ( ! putCmd( "NEWGROUPS %s", p ) ) - return FALSE; - if ( getStat() != STAT_NEW_GRP_FOLLOW ) - { - Log_err( "NEWGROUPS command failed: %s", client.lastStat ); - return FALSE; - } - processGrps(); - return TRUE; -} - -static const char * -readField( Str result, const char *p ) -{ - size_t len; - char *r; - - if ( ! p ) - return NULL; - r = result; - *r = '\0'; - len = 0; - while ( *p != '\t' && *p != '\n' ) - { - if ( ! *p ) - return p; - *(r++) = *(p++); - ++len; - if ( len >= MAXCHAR - 1 ) - { - *r = '\0'; - Log_err( "Field in overview too long: %s", r ); - return ++p; - } - } - *r = '\0'; - return ++p; -} - -static Bool -parseOvLn( Str line, int *numb, Str subj, Str from, - Str date, Str msgId, Str ref, size_t *bytes, size_t *lines ) -{ - const char *p; - Str t; - - p = readField( t, line ); - if ( sscanf( t, "%d", numb ) != 1 ) - return FALSE; - p = readField( subj, p ); - p = readField( from, p ); - p = readField( date, p ); - p = readField( msgId, p ); - p = readField( ref, p ); - p = readField( t, p ); - *bytes = 0; - *lines = 0; - if ( sscanf( t, "%d", bytes ) != 1 ) - return TRUE; - p = readField( t, p ); - if ( sscanf( t, "%d", lines ) != 1 ) - return TRUE; - return TRUE; -} - -static const char* -nextXref( const char *pXref, Str grp, int *numb ) -{ - Str s; - const char *pColon, *src; - char *dst; - - src = pXref; - while ( *src && isspace( *src ) ) - ++src; - dst = s; - while ( *src && ! isspace( *src ) ) - *(dst++) = *(src++); - *dst = '\0'; - if ( strlen( s ) == 0 ) - return NULL; - pColon = strstr( s, ":" ); - if ( ! pColon || sscanf( pColon + 1, "%d", numb ) != 1 ) - { - Log_err( "Corrupt Xref at position '%s'", pXref ); - return NULL; - } - Utl_cpyStrN( grp, s, pColon - s ); - Log_dbg( "client.c: nextXref: grp '%s' numb %lu", grp, numb ); - return src; -} - -static Bool -needsMark( const char *ref ) -{ - Bool done = FALSE; - char *p; - Str msgId; - int stat, len; - time_t lastAccess, nowTime; - double limit; - - nowTime = time( NULL ); - limit = Cfg_threadFollowTime() * 24. * 3600.; - while ( ! done ) - { - p = msgId; - while ( *ref != '<' ) - if ( *(ref++) == '\0' ) - return FALSE; - len = 0; - while ( *ref != '>' ) - { - if ( *ref == '\0' || ++len >= MAXCHAR - 1 ) - return FALSE; - *(p++) = *(ref++); - } - *(p++) = '>'; - *p = '\0'; - if ( Db_contains( msgId ) ) - { - stat = Db_stat( msgId ); - lastAccess = Db_lastAccess( msgId ); - if ( ( stat & DB_INTERESTING ) - && difftime( nowTime, lastAccess ) <= limit ) - return TRUE; - } - } - return FALSE; -} - -static void -prepareEntry( Over *ov ) -{ - Str g, t; - const char *msgId, *p, *xref; - int n; - - msgId = Ov_msgId( ov ); - if ( Pseudo_isGeneralInfo( msgId ) ) - Log_dbg( "Skipping general info '%s'", msgId ); - else if ( Db_contains( msgId ) ) - { - xref = Db_xref( msgId ); - Log_dbg( "Entry '%s' already in db with Xref '%s'", msgId, xref ); - p = nextXref( xref, g, &n ); - if ( p == NULL ) - Log_err( "Overview with no group in Xref '%s'", msgId ); - else - { - /* TODO: This code block seems unnessesary. Can we remove it? */ - if ( Cfg_servIsPreferential( client.serv, Grp_serv( g ) ) ) - { - Log_dbg( "Changing first server for '%s' from '%s' to '%s'", - msgId, Grp_serv( g ), client.serv ); - snprintf( t, MAXCHAR, "%s:%d %s", - client.grp, Ov_numb( ov ), xref ); - Db_setXref( msgId, t ); - } - else - { - Log_dbg( "Adding '%s' to Xref of '%s'", g, msgId ); - snprintf( t, MAXCHAR, "%s %s:%d", - xref, client.grp, Ov_numb( ov ) ); - Db_setXref( msgId, t ); - } - } - } - else - { - Log_dbg( "Preparing '%s' in database", msgId ); - Db_prepareEntry( ov, client.grp, Ov_numb( ov ) ); - } -} - -Bool -Client_getOver( int rmtFirst, int rmtLast, FetchMode mode ) -{ - Bool err; - size_t bytes, lines; - int rmtNumb, oldLast, cntMarked; - Over *ov; - Str line, subj, from, date, msgId, ref; - - ASSERT( strcmp( client.grp, "" ) != 0 ); - if ( ! putCmd( "XOVER %lu-%lu", rmtFirst, rmtLast ) ) - return FALSE; - if ( getStat() != STAT_OVERS_FOLLOW ) - { - Log_err( "XOVER command failed: %s", client.lastStat ); - return FALSE; - } - Log_dbg( "Requesting overview for remote %lu-%lu", rmtFirst, rmtLast ); - oldLast = Cont_last(); - cntMarked = 0; - while ( getTxtLn( line, &err ) && ! err ) - { - if ( ! parseOvLn( line, &rmtNumb, subj, from, date, msgId, ref, - &bytes, &lines ) ) - Log_err( "Bad overview line: %s", line ); - else - { - ov = new_Over( subj, from, date, msgId, ref, bytes, lines ); - Cont_app( ov ); - prepareEntry( ov ); - if ( mode == FULL || ( mode == THREAD && needsMark( ref ) ) ) - { - Req_add( client.serv, msgId ); - ++cntMarked; - } - } - Grp_setRmtNext( client.grp, rmtNumb + 1 ); - } - if ( oldLast != Cont_last() ) - Log_inf( "Added %s %lu-%lu", client.grp, oldLast + 1, Cont_last() ); - Log_inf( "%u articles marked for download in %s", cntMarked, client.grp ); - return err; -} - -static void -retrievingFailed( const char* msgId, const char *reason ) -{ - int stat; - - Log_err( "Retrieving of %s failed: %s", msgId, reason ); - stat = Db_stat( msgId ); - Pseudo_retrievingFailed( msgId, reason ); - Db_setStat( msgId, stat | DB_RETRIEVING_FAILED ); -} - -static Bool -retrieveAndStoreArt( const char *msgId ) -{ - Bool err; - DynStr *s = NULL; - Str line; - - Log_inf( "Retrieving %s", msgId ); - s = new_DynStr( 5000 ); - while ( getTxtLn( line, &err ) && ! err ) - DynStr_appLn( s, line ); - if ( ! err ) - Db_storeArt( msgId, DynStr_str( s ) ); - else - retrievingFailed( msgId, "Connection broke down" ); - del_DynStr( s ); - return ! err; -} - -void -Client_retrieveArt( const char *msgId ) -{ - if ( ! Db_contains( msgId ) ) - { - Log_err( "Article '%s' not prepared in database. Skipping.", msgId ); - return; - } - if ( ! ( Db_stat( msgId ) & DB_NOT_DOWNLOADED ) ) - { - Log_inf( "Article '%s' already retrieved. Skipping.", msgId ); - return; - } - if ( ! putCmd( "ARTICLE %s", msgId ) ) - retrievingFailed( msgId, "Connection broke down" ); - else if ( getStat() != STAT_ART_FOLLOWS ) - retrievingFailed( msgId, client.lastStat ); - else - retrieveAndStoreArt( msgId ); -} - -void -Client_retrieveArtList( const char *list ) -{ - Str msgId; - DynStr *s; - const char *p; - - Log_inf( "Retrieving article list" ); - s = new_DynStr( strlen( list ) ); - p = list; - while ( ( p = Utl_getLn( msgId, p ) ) ) - if ( ! Db_contains( msgId ) ) - Log_err( "Skipping retrieving of %s (not prepared in database)", - msgId ); - else if ( ! ( Db_stat( msgId ) & DB_NOT_DOWNLOADED ) ) - Log_inf( "Skipping %s (already retrieved)", msgId ); - else if ( ! putCmdNoFlush( "ARTICLE %s", msgId ) ) - { - retrievingFailed( msgId, "Connection broke down" ); - del_DynStr( s ); - return; - } - else - DynStr_appLn( s, msgId ); - fflush( client.out ); - Log_dbg( "[S FLUSH]" ); - p = DynStr_str( s ); - while ( ( p = Utl_getLn( msgId, p ) ) ) - { - if ( getStat() != STAT_ART_FOLLOWS ) - retrievingFailed( msgId, client.lastStat ); - else if ( ! retrieveAndStoreArt( msgId ) ) - break; - } - del_DynStr( s ); -} - -Bool -Client_changeToGrp( const char* name ) -{ - unsigned int stat; - int estimatedNumb, first, last; - - if ( ! Grp_exists( name ) ) - return FALSE; - if ( ! putCmd( "GROUP %s", name ) ) - return FALSE; - if ( getStat() != STAT_GRP_SELECTED ) - return FALSE; - if ( sscanf( client.lastStat, "%u %d %d %d", - &stat, &estimatedNumb, &first, &last ) != 4 ) - { - Log_err( "Bad server response to GROUP: %s", client.lastStat ); - return FALSE; - } - Utl_cpyStr( client.grp, name ); - client.rmtFirst = first; - client.rmtLast = last; - return TRUE; -} - -void -Client_rmtFirstLast( int *first, int *last ) -{ - *first = client.rmtFirst; - *last = client.rmtLast; -} - -Bool -Client_postArt( const char *msgId, const char *artTxt, - Str errStr ) -{ - if ( ! putCmd( "POST" ) ) - return FALSE; - if ( getStat() != STAT_SEND_ART ) - { - Log_err( "Posting of %s not allowed: %s", msgId, client.lastStat ); - strcpy( errStr, client.lastStat ); - return FALSE; - } - putTxtBuf( artTxt ); - putEndOfTxt(); - if ( getStat() != STAT_POST_OK ) - { - Log_err( "Posting of %s failed: %s", msgId, client.lastStat ); - strcpy( errStr, client.lastStat ); - return FALSE; - } - Log_inf( "Posted %s (Status: %s)", msgId, client.lastStat ); - return TRUE; -}
--- a/client.h Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,74 +0,0 @@ -/* - client.h - - Noffle acting as client to other NNTP-servers - - $Id: client.h 3 2000-01-04 11:35:42Z enz $ -*/ - -#ifndef CLIENT_H -#define CLIENT_H - -#include <time.h> -#include "common.h" -#include "database.h" -#include "fetchlist.h" - -/* Format of server name: <host>[:<port>] */ -Bool -Client_connect( const char *serv ); - -void -Client_disconnect( void ); - -Bool -Client_getGrps( void ); - -Bool -Client_getDsc( void ); - -Bool -Client_getCreationTimes( void ); - -Bool -Client_getNewgrps( const time_t *lastTime ); - -/* - Change to group <name> at server if it is also in current local grouplist. - Returns TRUE at success. -*/ -Bool -Client_changeToGrp( const Str name ); - -/* - Get overviews <rmtFirst> - <rmtLast> from server and append it - to the current content. For articles that are to be fetched due to FULL - or THREAD mode, store IDs in request database. -*/ -Bool -Client_getOver( int rmtFirst, int rmtLast, FetchMode mode ); - -/* - Retrieve full article text and store it into database. -*/ -void -Client_retrieveArt( const char *msgId ); - -/* - Same, but for a list of msgId's (new line after each msgId). - All ARTICLE commands are sent and then all answers read. -*/ -void -Client_retrieveArtList( const char *list ); - -/* - Store IDs of first and last article of group selected by - Client_changeToGroup at remote server. -*/ -void -Client_rmtFirstLast( int *first, int *last ); - -Bool -Client_postArt( const char *msgId, const char *artTxt, Str errStr ); - -#endif
--- a/common.h Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ -/* - common.h - - Common declarations. - - $Id: common.h 3 2000-01-04 11:35:42Z enz $ -*/ - -#ifndef COMMON_H -#define COMMON_H - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#define FALSE 0 -#define TRUE !0 -#define MAXCHAR 2048 - -#ifdef DEBUG -#include <assert.h> -#define ASSERT( x ) \ - if ( ! ( x ) ) \ - Log_err( "ASSERTION FAILED: %s line %i", __FILE__, __LINE__ ); \ - assert( x ) -#else -#define ASSERT( x ) -#endif - -typedef int Bool; -typedef char Str[ MAXCHAR ]; - -#endif
--- a/config.c Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,396 +0,0 @@ -/* - config.c - - The following macros must be set, when compiling this file: - CONFIGFILE - SPOOLDIR - VERSION - - $Id: config.c 44 2000-05-05 07:23:15Z enz $ -*/ - -#include "config.h" - -#include <limits.h> -#include "log.h" -#include "util.h" - -typedef struct -{ - Str name; - Str user; - Str pass; -} -ServEntry; - -typedef struct -{ - Str pattern; - int days; -} -ExpireEntry; - -struct -{ - /* Compile time options */ - const char *spoolDir; - const char *version; - /* Options from the config file */ - int maxFetch; - int autoUnsubscribeDays; - int threadFollowTime; - int connectTimeout; - Bool autoSubscribe; - Bool autoUnsubscribe; - Bool removeMsgId; - Bool replaceMsgId; - Str autoSubscribeMode; - Str mailTo; - int defaultExpire; - int numServ; - int maxServ; - ServEntry *serv; - int servIdx; /* for server enumeration */ - int numExpire; - int maxExpire; - ExpireEntry *expire; - int expireIdx; -} config = -{ - SPOOLDIR, /* spoolDir */ - VERSION, /* version */ - 300, /* maxFetch */ - 30, /* autoUnsubscribeDays */ - 7, /* threadFollowTime */ - 30, /* connectTimeout */ - FALSE, /* autoSubscribe */ - FALSE, /* autoUnsubscribe */ - FALSE, /* removeMsgId */ - TRUE, /* replaceMsgId */ - "over", /* autoSubscribeMode */ - "", /* mailTo */ - 14, /* defaultExpire */ - 0, /* numServ */ - 0, /* maxServ */ - NULL, /* serv */ - 0, /* servIdx */ - 0, /* numExpire */ - 0, /* maxExpire */ - NULL, /* expire */ - 0 /* expireIdx */ -}; - -const char * Cfg_spoolDir( void ) { return config.spoolDir; } -const char * Cfg_version( void ) { return config.version; } - -int Cfg_maxFetch( void ) { return config.maxFetch; } -int Cfg_autoUnsubscribeDays( void ) { return config.autoUnsubscribeDays; } -int Cfg_threadFollowTime( void ) { return config.threadFollowTime; } -int Cfg_connectTimeout( void ) { return config.connectTimeout; } -Bool Cfg_autoUnsubscribe( void ) { return config.autoUnsubscribe; } -Bool Cfg_autoSubscribe( void ) { return config.autoSubscribe; } -Bool Cfg_removeMsgId( void ) { return config.removeMsgId; } -Bool Cfg_replaceMsgId( void ) { return config.replaceMsgId; } -const char * Cfg_autoSubscribeMode( void ) { - return config.autoSubscribeMode; } -const char * Cfg_mailTo( void ) { return config.mailTo; } -int Cfg_expire( void ) { return config.defaultExpire; } - -void -Cfg_beginServEnum( void ) -{ - config.servIdx = 0; -} - -Bool -Cfg_nextServ( Str name ) -{ - if ( config.servIdx >= config.numServ ) - return FALSE; - strcpy( name, config.serv[ config.servIdx ].name ); - ++config.servIdx; - return TRUE; -} - -static Bool -searchServ( const char *name, int *idx ) -{ - int i; - - for ( i = 0; i < config.numServ; ++i ) - if ( strcmp( name, config.serv[ i ].name ) == 0 ) - { - *idx = i; - return TRUE; - } - return FALSE; -} - -Bool -Cfg_servListContains( const char *name ) -{ - int idx; - - return searchServ( name, &idx ); -} - -Bool -Cfg_servIsPreferential( const char *name1, const char *name2 ) -{ - Bool exists1, exists2; - int idx1, idx2; - - exists1 = searchServ( name1, &idx1 ); - exists2 = searchServ( name2, &idx2 ); - if ( exists1 && exists2 ) - return ( idx1 < idx2 ); - if ( exists1 && ! exists2 ) - return TRUE; - /* ( ! exists1 && exists2 ) || ( ! exists1 && ! exists2 ) */ - return FALSE; -} - -void -Cfg_authInfo( const char *name, Str user, Str pass ) -{ - int idx; - - if ( searchServ( name, &idx ) ) - { - strcpy( user, config.serv[ idx ].user ); - strcpy( pass, config.serv[ idx ].pass ); - } - else - { - user[ 0 ] = '\0'; - pass[ 0 ] = '\0'; - } -} - -void -Cfg_beginExpireEnum( void ) -{ - config.expireIdx = 0; -} - -int -Cfg_nextExpire( Str pattern ) -{ - if ( config.expireIdx >= config.numExpire ) - return -1; - strcpy( pattern, config.expire[ config.expireIdx ].pattern ); - return config.expire[ config.expireIdx++ ].days; -} - -static void -logSyntaxErr( const char *line ) -{ - Log_err( "Syntax error in config file: %s", line ); -} - -static void -getBool( Bool *variable, const char *line ) -{ - Str value, name, lowerLn; - - strcpy( lowerLn, line ); - Utl_toLower( lowerLn ); - if ( sscanf( lowerLn, "%s %s", name, value ) != 2 ) - { - logSyntaxErr( line ); - return; - } - - if ( strcmp( value, "yes" ) == 0 ) - *variable = TRUE; - else if ( strcmp( value, "no" ) == 0 ) - *variable = FALSE; - else - Log_err( "Error in config file %s must be yes or no", name ); -} - -static void -getInt( int *variable, int min, int max, const char *line ) -{ - int value; - Str name; - - if ( sscanf( line, "%s %d", name, &value ) != 2 ) - { - logSyntaxErr( line ); - return; - } - if ( value < min || value > max ) - { - Log_err( "Range error in config file %s [%d,%d]", name, min, max ); - return; - } - *variable = value; -} - -static void -getStr( char *variable, const char *line ) -{ - Str dummy; - - if ( sscanf( line, "%s %s", dummy, variable ) != 2 ) - { - logSyntaxErr( line ); - return; - } -} - -static void -getServ( const char *line ) -{ - Str dummy; - int r, len; - ServEntry entry; - - entry.user[ 0 ] = '\0'; - entry.pass[ 0 ] = '\0'; - r = sscanf( line, "%s %s %s %s", - dummy, entry.name, entry.user, entry.pass ); - if ( r < 2 ) - { - logSyntaxErr( line ); - return; - } - len = strlen( entry.name ); - /* To make server name more definit, it is made lowercase and - port is removed, if it is the default port */ - if ( len > 4 && strcmp( entry.name + len - 4, ":119" ) == 0 ) - entry.name[ len - 4 ] = '\0'; - Utl_toLower( entry.name ); - - if ( config.maxServ < config.numServ + 1 ) - { - if ( ! ( config.serv = realloc( config.serv, - ( config.maxServ + 5 ) - * sizeof( ServEntry ) ) ) ) - { - Log_err( "Could not realloc server list" ); - exit( EXIT_FAILURE ); - } - config.maxServ += 5; - } - config.serv[ config.numServ++ ] = entry; -} - -static void -getExpire( const char *line ) -{ - Str dummy; - ExpireEntry entry; - int days; - - /* - The line is either "expire <num>" or "expire <pat> <num>". - The former updates the overall default. - */ - if ( sscanf( line, "%s %s %d", dummy, entry.pattern, &days ) != 3 ) - { - logSyntaxErr( line ); - return; - } - else - { - if ( days < 0 ) - { - Log_err( "Expire days error in '%s': must be integer > 0", - line, days ); - return; - } - - Utl_toLower( entry.pattern ); - entry.days = days; - - if ( config.maxExpire < config.numExpire + 1 ) - { - if ( ! ( config.expire = realloc( config.expire, - ( config.maxExpire + 5 ) - * sizeof( ExpireEntry ) ) ) ) - { - Log_err( "Could not realloc exipre list" ); - exit( EXIT_FAILURE ); - } - config.maxExpire += 5; - } - config.expire[ config.numExpire++ ] = entry; - } -} - -void -Cfg_read( void ) -{ - char *p; - FILE *f; - Str file, line, lowerLine, name, s; - - snprintf( file, MAXCHAR, CONFIGFILE ); - if ( ! ( f = fopen( file, "r" ) ) ) - { - Log_err( "Cannot read %s", file ); - return; - } - while ( fgets( line, MAXCHAR, f ) ) - { - p = Utl_stripWhiteSpace( line ); - Utl_stripComment( p ); - Utl_cpyStr( lowerLine, p ); - Utl_toLower( lowerLine ); - if ( *p == '\0' ) - continue; - if ( sscanf( p, "%s", name ) != 1 ) - Log_err( "Syntax error in %s: %s", file, line ); - else if ( strcmp( "max-fetch", name ) == 0 ) - getInt( &config.maxFetch, 0, INT_MAX, p ); - else if ( strcmp( "auto-unsubscribe-days", name ) == 0 ) - getInt( &config.autoUnsubscribe, -1, INT_MAX, p ); - else if ( strcmp( "thread-follow-time", name ) == 0 ) - getInt( &config.threadFollowTime, 0, INT_MAX, p ); - else if ( strcmp( "connect-timeout", name ) == 0 ) - getInt( &config.connectTimeout, 0, INT_MAX, p ); - else if ( strcmp( "default-expire", name ) == 0 ) - getInt( &config.defaultExpire, 0, INT_MAX, p ); - else if ( strcmp( "auto-subscribe", name ) == 0 ) - getBool( &config.autoSubscribe, p ); - else if ( strcmp( "auto-unsubscribe", name ) == 0 ) - getBool( &config.autoUnsubscribe, p ); - else if ( strcmp( "remove-messageid", name ) == 0 ) - getBool( &config.removeMsgId, p ); - else if ( strcmp( "replace-messageid", name ) == 0 ) - getBool( &config.replaceMsgId, p ); - else if ( strcmp( "auto-subscribe-mode", name ) == 0 ) - { - getStr( s, p ); - Utl_toLower( s ); - if ( strcmp( s, "full" ) != 0 - && strcmp( s, "thread" ) != 0 - && strcmp( s, "over" ) != 0 - && strcmp( s, "off" ) != 0 ) - { - Log_err( "Syntax error in config file: %s", line ); - return; - } - else - strcpy( config.autoSubscribeMode, s ); - } - else if ( strcmp( "server", name ) == 0 ) - /* Server needs line not p, - because password may contain uppercase */ - getServ( line ); - else if ( strcmp( "mail-to", name ) == 0 ) - getStr( config.mailTo, p ); - else if ( strcmp( "expire", name ) == 0 ) - getExpire( p ); - else - Log_err( "Unknown config option: %s", name ); - } - fclose( f ); - if ( ! config.numServ ) - { - Log_err( "Config file contains no server" ); - exit( EXIT_FAILURE ); - } -}
--- a/config.h Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ -/* - config.h - - Common declarations and handling of the configuration file. - - $Id: config.h 44 2000-05-05 07:23:15Z enz $ -*/ - -#ifndef CONFIG_H -#define CONFIG_H - -#include "common.h" - -const char * Cfg_spoolDir( void ); -const char * Cfg_version( void ); - -int Cfg_maxFetch( void ); -int Cfg_autoUnsubscribeDays( void ); -int Cfg_threadFollowTime( void ); -int Cfg_connectTimeout( void ); -Bool Cfg_autoUnsubscribe( void ); -Bool Cfg_autoSubscribe( void ); -Bool Cfg_removeMsgId( void ); -Bool Cfg_replaceMsgId( void ); -const char * Cfg_autoSubscribeMode( void ); /* Can be: full, thread, over */ -const char * Cfg_mailTo( void ); - -/* Begin iteration through the server names */ -void Cfg_beginServEnum( void ); - -/* Save next server name in "name". Return TRUE if name has been was saved. - Return FALSE if there are no more server names. */ -Bool Cfg_nextServ( Str name ); - -Bool Cfg_servListContains( const char *name ); -/* Prefer server earlier in config file. Known servers are always preferential - to unknown servers. */ -Bool Cfg_servIsPreferential( const char *name1, const char *name2 ); -void Cfg_authInfo( const char *name, Str user, Str pass ); - -/* Begin iteration through expire entries. */ -void Cfg_beginExpireEnum( void ); - -/* Put next expire pattern in "pattern" and return its days count. - Return -1 if no more expire patterns. */ -int Cfg_nextExpire( Str pattern ); - -/* Return default expire days. */ -int Cfg_expire( void ); - -void Cfg_read( void ); - -#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/config.h.in Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,176 @@ +/* config.h.in. Generated automatically from configure.in by autoheader. */ +/* For use by autoheader */ + +#ifndef CONFIG_H +#define CONFIG_H /* To stop multiple inclusions. */ + +/* The NOFFLE config-file */ +#undef CONFIGFILE + +/* The default NOFFLE spool-directory */ +#undef SPOOLDIR + +/* Enable GNU extensions. */ +#undef _GNU_SOURCE + +/* Define to 'unsigned int' if not already defined by the system. */ +#undef socklen_t + + + +/* Define if on AIX 3. + System headers sometimes define this. + We just want to avoid a redefinition error message. */ +#ifndef _ALL_SOURCE +#undef _ALL_SOURCE +#endif + +/* Define to empty if the keyword does not work. */ +#undef const + +/* Define if your system has a working fnmatch function. */ +#undef HAVE_FNMATCH + +/* Define if you have the strftime function. */ +#undef HAVE_STRFTIME + +/* Define if on MINIX. */ +#undef _MINIX + +/* Define if the system does not provide POSIX.1 features except + with this defined. */ +#undef _POSIX_1_SOURCE + +/* Define if you need to in order for stat and other things to work. */ +#undef _POSIX_SOURCE + +/* Define as the return type of signal handlers (int or void). */ +#undef RETSIGTYPE + +/* Define to `unsigned' if <sys/types.h> doesn't define. */ +#undef size_t + +/* Define if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define if you can safely include both <sys/time.h> and <time.h>. */ +#undef TIME_WITH_SYS_TIME + +/* Define if your <sys/time.h> declares struct tm. */ +#undef TM_IN_SYS_TIME + +/* Define if you have the __snprintf function. */ +#undef HAVE___SNPRINTF + +/* Define if you have the __vsnprintf function. */ +#undef HAVE___VSNPRINTF + +/* Define if you have the fdopen function. */ +#undef HAVE_FDOPEN + +/* Define if you have the gethostname function. */ +#undef HAVE_GETHOSTNAME + +/* Define if you have the mkdir function. */ +#undef HAVE_MKDIR + +/* Define if you have the mktime function. */ +#undef HAVE_MKTIME + +/* Define if you have the select function. */ +#undef HAVE_SELECT + +/* Define if you have the snprintf function. */ +#undef HAVE_SNPRINTF + +/* Define if you have the socket function. */ +#undef HAVE_SOCKET + +/* Define if you have the strerror function. */ +#undef HAVE_STRERROR + +/* Define if you have the strstr function. */ +#undef HAVE_STRSTR + +/* Define if you have the uname function. */ +#undef HAVE_UNAME + +/* Define if you have the vsnprintf function. */ +#undef HAVE_VSNPRINTF + +/* Define if you have the <dirent.h> header file. */ +#undef HAVE_DIRENT_H + +/* Define if you have the <errno.h> header file. */ +#undef HAVE_ERRNO_H + +/* Define if you have the <fcntl.h> header file. */ +#undef HAVE_FCNTL_H + +/* Define if you have the <fnmatch.h> header file. */ +#undef HAVE_FNMATCH_H + +/* Define if you have the <gdbm.h> header file. */ +#undef HAVE_GDBM_H + +/* Define if you have the <getopt.h> header file. */ +#undef HAVE_GETOPT_H + +/* Define if you have the <limits.h> header file. */ +#undef HAVE_LIMITS_H + +/* Define if you have the <ndir.h> header file. */ +#undef HAVE_NDIR_H + +/* Define if you have the <netdb.h> header file. */ +#undef HAVE_NETDB_H + +/* Define if you have the <signal.h> header file. */ +#undef HAVE_SIGNAL_H + +/* Define if you have the <sys/dir.h> header file. */ +#undef HAVE_SYS_DIR_H + +/* Define if you have the <sys/ndir.h> header file. */ +#undef HAVE_SYS_NDIR_H + +/* Define if you have the <sys/resource.h> header file. */ +#undef HAVE_SYS_RESOURCE_H + +/* Define if you have the <sys/socket.h> header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define if you have the <sys/time.h> header file. */ +#undef HAVE_SYS_TIME_H + +/* Define if you have the <sys/utsname.h> header file. */ +#undef HAVE_SYS_UTSNAME_H + +/* Define if you have the <syslog.h> header file. */ +#undef HAVE_SYSLOG_H + +/* Define if you have the <time.h> header file. */ +#undef HAVE_TIME_H + +/* Define if you have the <unistd.h> header file. */ +#undef HAVE_UNISTD_H + +/* Define if you have the gdbm library (-lgdbm). */ +#undef HAVE_LIBGDBM + +/* Define if you have the nsl library (-lnsl). */ +#undef HAVE_LIBNSL + +/* Define if you have the socket library (-lsocket). */ +#undef HAVE_LIBSOCKET + +/* Name of package */ +#undef PACKAGE + +/* Version number of package */ +#undef VERSION + + + +#endif /* CONFIG_H */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/configure Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,2902 @@ +#! /bin/sh + +# Guess values for system-dependent variables and create Makefiles. +# Generated automatically using autoconf version 2.13 +# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. + +# Defaults: +ac_help= +ac_default_prefix=/usr/local +# Any additions from configure.in: +ac_help="$ac_help + +NOFFLE options: + --enable-debug turn on debugging (default=yes)" +ac_help="$ac_help + --with-docdir=PATH specify where to put the documentation +" + +# Initialize some variables set by options. +# The variables have the same names as the options, with +# dashes changed to underlines. +build=NONE +cache_file=./config.cache +exec_prefix=NONE +host=NONE +no_create= +nonopt=NONE +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +target=NONE +verbose= +x_includes=NONE +x_libraries=NONE +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +# Initialize some other variables. +subdirs= +MFLAGS= MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} +# Maximum number of lines to put in a shell here document. +ac_max_here_lines=12 + +ac_prev= +for ac_option +do + + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + case "$ac_option" in + -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) ac_optarg= ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case "$ac_option" in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir="$ac_optarg" ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build="$ac_optarg" ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file="$ac_optarg" ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir="$ac_optarg" ;; + + -disable-* | --disable-*) + ac_feature=`echo $ac_option|sed -e 's/-*disable-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + eval "enable_${ac_feature}=no" ;; + + -enable-* | --enable-*) + ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "enable_${ac_feature}='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix="$ac_optarg" ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he) + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat << EOF +Usage: configure [options] [host] +Options: [defaults in brackets after descriptions] +Configuration: + --cache-file=FILE cache test results in FILE + --help print this message + --no-create do not create output files + --quiet, --silent do not print \`checking...' messages + --version print the version of autoconf that created configure +Directory and file names: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [same as prefix] + --bindir=DIR user executables in DIR [EPREFIX/bin] + --sbindir=DIR system admin executables in DIR [EPREFIX/sbin] + --libexecdir=DIR program executables in DIR [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data in DIR + [PREFIX/share] + --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data in DIR + [PREFIX/com] + --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var] + --libdir=DIR object code libraries in DIR [EPREFIX/lib] + --includedir=DIR C header files in DIR [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include] + --infodir=DIR info documentation in DIR [PREFIX/info] + --mandir=DIR man documentation in DIR [PREFIX/man] + --srcdir=DIR find the sources in DIR [configure dir or ..] + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM + run sed PROGRAM on installed program names +EOF + cat << EOF +Host type: + --build=BUILD configure for building on BUILD [BUILD=HOST] + --host=HOST configure for HOST [guessed] + --target=TARGET configure for TARGET [TARGET=HOST] +Features and packages: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --x-includes=DIR X include files are in DIR + --x-libraries=DIR X library files are in DIR +EOF + if test -n "$ac_help"; then + echo "--enable and --with options recognized:$ac_help" + fi + exit 0 ;; + + -host | --host | --hos | --ho) + ac_prev=host ;; + -host=* | --host=* | --hos=* | --ho=*) + host="$ac_optarg" ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir="$ac_optarg" ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir="$ac_optarg" ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir="$ac_optarg" ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir="$ac_optarg" ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir="$ac_optarg" ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir="$ac_optarg" ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir="$ac_optarg" ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix="$ac_optarg" ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix="$ac_optarg" ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix="$ac_optarg" ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name="$ac_optarg" ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir="$ac_optarg" ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir="$ac_optarg" ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site="$ac_optarg" ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir="$ac_optarg" ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir="$ac_optarg" ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target="$ac_optarg" ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers) + echo "configure generated by autoconf version 2.13" + exit 0 ;; + + -with-* | --with-*) + ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "with_${ac_package}='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`echo $ac_option|sed -e 's/-*without-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + eval "with_${ac_package}=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes="$ac_optarg" ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries="$ac_optarg" ;; + + -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; } + ;; + + *) + if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then + echo "configure: warning: $ac_option: invalid host type" 1>&2 + fi + if test "x$nonopt" != xNONE; then + { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } + fi + nonopt="$ac_option" + ;; + + esac +done + +if test -n "$ac_prev"; then + { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; } +fi + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +# File descriptor usage: +# 0 standard input +# 1 file creation +# 2 errors and warnings +# 3 some systems may open it to /dev/tty +# 4 used on the Kubota Titan +# 6 checking for... messages and results +# 5 compiler messages saved in config.log +if test "$silent" = yes; then + exec 6>/dev/null +else + exec 6>&1 +fi +exec 5>./config.log + +echo "\ +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. +" 1>&5 + +# Strip out --no-create and --no-recursion so they do not pile up. +# Also quote any args containing shell metacharacters. +ac_configure_args= +for ac_arg +do + case "$ac_arg" in + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) ;; + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*) + ac_configure_args="$ac_configure_args '$ac_arg'" ;; + *) ac_configure_args="$ac_configure_args $ac_arg" ;; + esac +done + +# NLS nuisances. +# Only set these to C if already set. These must not be set unconditionally +# because not all systems understand e.g. LANG=C (notably SCO). +# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'! +# Non-C LC_CTYPE values break the ctype check. +if test "${LANG+set}" = set; then LANG=C; export LANG; fi +if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi +if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi +if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo > confdefs.h + +# A filename unique to this package, relative to the directory that +# configure is in, which we can look for to find out if srcdir is correct. +ac_unique_file=src/noffle.c + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_prog=$0 + ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'` + test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; } + else + { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; } + fi +fi +srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'` + +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + echo "loading site script $ac_site_file" + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + echo "loading cache $cache_file" + . $cache_file +else + echo "creating cache $cache_file" + > $cache_file +fi + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +ac_exeext= +ac_objext=o +if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then + # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. + if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then + ac_n= ac_c=' +' ac_t=' ' + else + ac_n=-n ac_c= ac_t= + fi +else + ac_n= ac_c='\c' ac_t= +fi + + + + + +PACKAGE=noffle +VERSION=1.0pre6 + + + + +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; } +fi +ac_config_guess=$ac_aux_dir/config.guess +ac_config_sub=$ac_aux_dir/config.sub +ac_configure=$ac_aux_dir/configure # This should be Cygnus configure. + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# ./install, which can be erroneously created by make from ./install.sh. +echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 +echo "configure:572: checking for a BSD compatible install" >&5 +if test -z "$INSTALL"; then +if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS=":" + for ac_dir in $PATH; do + # Account for people who put trailing slashes in PATH elements. + case "$ac_dir/" in + /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + if test -f $ac_dir/$ac_prog; then + if test $ac_prog = install && + grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + else + ac_cv_path_install="$ac_dir/$ac_prog -c" + break 2 + fi + fi + done + ;; + esac + done + IFS="$ac_save_IFS" + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL="$ac_cv_path_install" + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL="$ac_install_sh" + fi +fi +echo "$ac_t""$INSTALL" 1>&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +echo $ac_n "checking whether build environment is sane""... $ac_c" 1>&6 +echo "configure:625: checking whether build environment is sane" >&5 +# Just in case +sleep 1 +echo timestamp > conftestfile +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt $srcdir/configure conftestfile 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t $srcdir/configure conftestfile` + fi + if test "$*" != "X $srcdir/configure conftestfile" \ + && test "$*" != "X conftestfile $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + { echo "configure: error: ls -t appears to fail. Make sure there is not a broken +alias in your environment" 1>&2; exit 1; } + fi + + test "$2" = conftestfile + ) +then + # Ok. + : +else + { echo "configure: error: newly created file is older than distributed files! +Check your system clock" 1>&2; exit 1; } +fi +rm -f conftest* +echo "$ac_t""yes" 1>&6 +if test "$program_transform_name" = s,x,x,; then + program_transform_name= +else + # Double any \ or $. echo might interpret backslashes. + cat <<\EOF_SED > conftestsed +s,\\,\\\\,g; s,\$,$$,g +EOF_SED + program_transform_name="`echo $program_transform_name|sed -f conftestsed`" + rm -f conftestsed +fi +test "$program_prefix" != NONE && + program_transform_name="s,^,${program_prefix},; $program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s,\$\$,${program_suffix},; $program_transform_name" + +# sed with no file args requires a program. +test "$program_transform_name" = "" && program_transform_name="s,x,x," + +echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6 +echo "configure:682: checking whether ${MAKE-make} sets \${MAKE}" >&5 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftestmake <<\EOF +all: + @echo 'ac_maketemp="${MAKE}"' +EOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftestmake +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$ac_t""yes" 1>&6 + SET_MAKE= +else + echo "$ac_t""no" 1>&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + + +PACKAGE=$PACKAGE + +VERSION=$VERSION + +if test "`cd $srcdir && pwd`" != "`pwd`" && test -f $srcdir/config.status; then + { echo "configure: error: source directory already configured; run "make distclean" there first" 1>&2; exit 1; } +fi +cat >> confdefs.h <<EOF +#define PACKAGE "$PACKAGE" +EOF + +cat >> confdefs.h <<EOF +#define VERSION "$VERSION" +EOF + + + +missing_dir=`cd $ac_aux_dir && pwd` +echo $ac_n "checking for working aclocal""... $ac_c" 1>&6 +echo "configure:728: checking for working aclocal" >&5 +# Run test in a subshell; some versions of sh will print an error if +# an executable is not found, even if stderr is redirected. +# Redirect stdin to placate older versions of autoconf. Sigh. +if (aclocal --version) < /dev/null > /dev/null 2>&1; then + ACLOCAL=aclocal + echo "$ac_t""found" 1>&6 +else + ACLOCAL="$missing_dir/missing aclocal" + echo "$ac_t""missing" 1>&6 +fi + +echo $ac_n "checking for working autoconf""... $ac_c" 1>&6 +echo "configure:741: checking for working autoconf" >&5 +# Run test in a subshell; some versions of sh will print an error if +# an executable is not found, even if stderr is redirected. +# Redirect stdin to placate older versions of autoconf. Sigh. +if (autoconf --version) < /dev/null > /dev/null 2>&1; then + AUTOCONF=autoconf + echo "$ac_t""found" 1>&6 +else + AUTOCONF="$missing_dir/missing autoconf" + echo "$ac_t""missing" 1>&6 +fi + +echo $ac_n "checking for working automake""... $ac_c" 1>&6 +echo "configure:754: checking for working automake" >&5 +# Run test in a subshell; some versions of sh will print an error if +# an executable is not found, even if stderr is redirected. +# Redirect stdin to placate older versions of autoconf. Sigh. +if (automake --version) < /dev/null > /dev/null 2>&1; then + AUTOMAKE=automake + echo "$ac_t""found" 1>&6 +else + AUTOMAKE="$missing_dir/missing automake" + echo "$ac_t""missing" 1>&6 +fi + +echo $ac_n "checking for working autoheader""... $ac_c" 1>&6 +echo "configure:767: checking for working autoheader" >&5 +# Run test in a subshell; some versions of sh will print an error if +# an executable is not found, even if stderr is redirected. +# Redirect stdin to placate older versions of autoconf. Sigh. +if (autoheader --version) < /dev/null > /dev/null 2>&1; then + AUTOHEADER=autoheader + echo "$ac_t""found" 1>&6 +else + AUTOHEADER="$missing_dir/missing autoheader" + echo "$ac_t""missing" 1>&6 +fi + +echo $ac_n "checking for working makeinfo""... $ac_c" 1>&6 +echo "configure:780: checking for working makeinfo" >&5 +# Run test in a subshell; some versions of sh will print an error if +# an executable is not found, even if stderr is redirected. +# Redirect stdin to placate older versions of autoconf. Sigh. +if (makeinfo --version) < /dev/null > /dev/null 2>&1; then + MAKEINFO=makeinfo + echo "$ac_t""found" 1>&6 +else + MAKEINFO="$missing_dir/missing makeinfo" + echo "$ac_t""missing" 1>&6 +fi + + + + +cat >> confdefs.h <<\EOF +#define _GNU_SOURCE 1 +EOF + + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + + + + +# Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:813: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="gcc" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:843: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_prog_rejected=no + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + break + fi + done + IFS="$ac_save_ifs" +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# -gt 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + set dummy "$ac_dir/$ac_word" "$@" + shift + ac_cv_prog_CC="$@" + fi +fi +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + if test -z "$CC"; then + case "`uname -s`" in + *win32* | *WIN32*) + # Extract the first word of "cl", so it can be a program name with args. +set dummy cl; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:894: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="cl" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + ;; + esac + fi + test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; } +fi + +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 +echo "configure:926: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +cat > conftest.$ac_ext << EOF + +#line 937 "configure" +#include "confdefs.h" + +main(){return(0);} +EOF +if { (eval echo configure:942: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + ac_cv_prog_cc_works=yes + # If we can't run a trivial program, we are probably using a cross compiler. + if (./conftest; exit) 2>/dev/null; then + ac_cv_prog_cc_cross=no + else + ac_cv_prog_cc_cross=yes + fi +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_prog_cc_works=no +fi +rm -fr conftest* +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +echo "$ac_t""$ac_cv_prog_cc_works" 1>&6 +if test $ac_cv_prog_cc_works = no; then + { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } +fi +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 +echo "configure:968: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 +cross_compiling=$ac_cv_prog_cc_cross + +echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 +echo "configure:973: checking whether we are using GNU C" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.c <<EOF +#ifdef __GNUC__ + yes; +#endif +EOF +if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:982: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then + ac_cv_prog_gcc=yes +else + ac_cv_prog_gcc=no +fi +fi + +echo "$ac_t""$ac_cv_prog_gcc" 1>&6 + +if test $ac_cv_prog_gcc = yes; then + GCC=yes +else + GCC= +fi + +ac_test_CFLAGS="${CFLAGS+set}" +ac_save_CFLAGS="$CFLAGS" +CFLAGS= +echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 +echo "configure:1001: checking whether ${CC-cc} accepts -g" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo 'void f(){}' > conftest.c +if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then + ac_cv_prog_cc_g=yes +else + ac_cv_prog_cc_g=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_prog_cc_g" 1>&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS="$ac_save_CFLAGS" +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi + +echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 +echo "configure:1033: checking how to run the C preprocessor" >&5 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then +if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + # This must be in double quotes, not single quotes, because CPP may get + # substituted into the Makefile and "${CC-cc}" will confuse make. + CPP="${CC-cc} -E" + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. + cat > conftest.$ac_ext <<EOF +#line 1048 "configure" +#include "confdefs.h" +#include <assert.h> +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1054: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -E -traditional-cpp" + cat > conftest.$ac_ext <<EOF +#line 1065 "configure" +#include "confdefs.h" +#include <assert.h> +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1071: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -nologo -E" + cat > conftest.$ac_ext <<EOF +#line 1082 "configure" +#include "confdefs.h" +#include <assert.h> +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1088: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP=/lib/cpp +fi +rm -f conftest* +fi +rm -f conftest* +fi +rm -f conftest* + ac_cv_prog_CPP="$CPP" +fi + CPP="$ac_cv_prog_CPP" +else + ac_cv_prog_CPP="$CPP" +fi +echo "$ac_t""$CPP" 1>&6 + +if test $ac_cv_prog_gcc = yes; then + echo $ac_n "checking whether ${CC-cc} needs -traditional""... $ac_c" 1>&6 +echo "configure:1114: checking whether ${CC-cc} needs -traditional" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc_traditional'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_pattern="Autoconf.*'x'" + cat > conftest.$ac_ext <<EOF +#line 1120 "configure" +#include "confdefs.h" +#include <sgtty.h> +Autoconf TIOCGETP +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "$ac_pattern" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_prog_gcc_traditional=yes +else + rm -rf conftest* + ac_cv_prog_gcc_traditional=no +fi +rm -f conftest* + + + if test $ac_cv_prog_gcc_traditional = no; then + cat > conftest.$ac_ext <<EOF +#line 1138 "configure" +#include "confdefs.h" +#include <termio.h> +Autoconf TCGETA +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "$ac_pattern" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_prog_gcc_traditional=yes +fi +rm -f conftest* + + fi +fi + +echo "$ac_t""$ac_cv_prog_gcc_traditional" 1>&6 + if test $ac_cv_prog_gcc_traditional = yes; then + CC="$CC -traditional" + fi +fi + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# ./install, which can be erroneously created by make from ./install.sh. +echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 +echo "configure:1171: checking for a BSD compatible install" >&5 +if test -z "$INSTALL"; then +if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS=":" + for ac_dir in $PATH; do + # Account for people who put trailing slashes in PATH elements. + case "$ac_dir/" in + /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + if test -f $ac_dir/$ac_prog; then + if test $ac_prog = install && + grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + else + ac_cv_path_install="$ac_dir/$ac_prog -c" + break 2 + fi + fi + done + ;; + esac + done + IFS="$ac_save_IFS" + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL="$ac_cv_path_install" + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL="$ac_install_sh" + fi +fi +echo "$ac_t""$INSTALL" 1>&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6 +echo "configure:1224: checking whether ${MAKE-make} sets \${MAKE}" >&5 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftestmake <<\EOF +all: + @echo 'ac_maketemp="${MAKE}"' +EOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftestmake +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$ac_t""yes" 1>&6 + SET_MAKE= +else + echo "$ac_t""no" 1>&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + + + + +echo $ac_n "checking for AIX""... $ac_c" 1>&6 +echo "configure:1254: checking for AIX" >&5 +cat > conftest.$ac_ext <<EOF +#line 1256 "configure" +#include "confdefs.h" +#ifdef _AIX + yes +#endif + +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "yes" >/dev/null 2>&1; then + rm -rf conftest* + echo "$ac_t""yes" 1>&6; cat >> confdefs.h <<\EOF +#define _ALL_SOURCE 1 +EOF + +else + rm -rf conftest* + echo "$ac_t""no" 1>&6 +fi +rm -f conftest* + + +echo $ac_n "checking for POSIXized ISC""... $ac_c" 1>&6 +echo "configure:1278: checking for POSIXized ISC" >&5 +if test -d /etc/conf/kconfig.d && + grep _POSIX_VERSION /usr/include/sys/unistd.h >/dev/null 2>&1 +then + echo "$ac_t""yes" 1>&6 + ISC=yes # If later tests want to check for ISC. + cat >> confdefs.h <<\EOF +#define _POSIX_SOURCE 1 +EOF + + if test "$GCC" = yes; then + CC="$CC -posix" + else + CC="$CC -Xp" + fi +else + echo "$ac_t""no" 1>&6 + ISC= +fi + +ac_safe=`echo "minix/config.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for minix/config.h""... $ac_c" 1>&6 +echo "configure:1300: checking for minix/config.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1305 "configure" +#include "confdefs.h" +#include <minix/config.h> +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1310: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + MINIX=yes +else + echo "$ac_t""no" 1>&6 +MINIX= +fi + +if test "$MINIX" = yes; then + cat >> confdefs.h <<\EOF +#define _POSIX_SOURCE 1 +EOF + + cat >> confdefs.h <<\EOF +#define _POSIX_1_SOURCE 2 +EOF + + cat >> confdefs.h <<\EOF +#define _MINIX 1 +EOF + +fi + + + + +# Whenever both -lsocket and -lnsl are needed, it seems to be always the +# case that gethostbyname requires -lnsl. So, check -lnsl first, for it +# to be in LIBS before the setsockopt checks are performed. *However*, +# on SINIX-N 5.43, this is false, and gethostent seems to be a better +# candidate. So, let's use it below instead of gethostbyname, and see. + +# [ This is ripped from GNU tar. ] + +echo $ac_n "checking for gethostent""... $ac_c" 1>&6 +echo "configure:1359: checking for gethostent" >&5 +if eval "test \"`echo '$''{'ac_cv_func_gethostent'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1364 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char gethostent(); below. */ +#include <assert.h> +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char gethostent(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_gethostent) || defined (__stub___gethostent) +choke me +#else +gethostent(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1387: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_gethostent=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_gethostent=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'gethostent`\" = yes"; then + echo "$ac_t""yes" 1>&6 + : +else + echo "$ac_t""no" 1>&6 +fi + +if test $ac_cv_func_gethostent = no; then + echo $ac_n "checking for gethostent in -lnsl""... $ac_c" 1>&6 +echo "configure:1408: checking for gethostent in -lnsl" >&5 +ac_lib_var=`echo nsl'_'gethostent | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lnsl $LIBS" +cat > conftest.$ac_ext <<EOF +#line 1416 "configure" +#include "confdefs.h" +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char gethostent(); + +int main() { +gethostent() +; return 0; } +EOF +if { (eval echo configure:1427: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo nsl | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <<EOF +#define $ac_tr_lib 1 +EOF + + LIBS="-lnsl $LIBS" + +else + echo "$ac_t""no" 1>&6 +fi + +fi +echo $ac_n "checking for setsockopt""... $ac_c" 1>&6 +echo "configure:1456: checking for setsockopt" >&5 +if eval "test \"`echo '$''{'ac_cv_func_setsockopt'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1461 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char setsockopt(); below. */ +#include <assert.h> +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char setsockopt(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_setsockopt) || defined (__stub___setsockopt) +choke me +#else +setsockopt(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1484: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_setsockopt=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_setsockopt=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'setsockopt`\" = yes"; then + echo "$ac_t""yes" 1>&6 + : +else + echo "$ac_t""no" 1>&6 +fi + +if test $ac_cv_func_setsockopt = no; then + echo $ac_n "checking for setsockopt in -lsocket""... $ac_c" 1>&6 +echo "configure:1505: checking for setsockopt in -lsocket" >&5 +ac_lib_var=`echo socket'_'setsockopt | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lsocket $LIBS" +cat > conftest.$ac_ext <<EOF +#line 1513 "configure" +#include "confdefs.h" +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char setsockopt(); + +int main() { +setsockopt() +; return 0; } +EOF +if { (eval echo configure:1524: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo socket | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <<EOF +#define $ac_tr_lib 1 +EOF + + LIBS="-lsocket $LIBS" + +else + echo "$ac_t""no" 1>&6 +fi + +fi + + +echo $ac_n "checking for gdbm_open in -lgdbm""... $ac_c" 1>&6 +echo "configure:1555: checking for gdbm_open in -lgdbm" >&5 +ac_lib_var=`echo gdbm'_'gdbm_open | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lgdbm $LIBS" +cat > conftest.$ac_ext <<EOF +#line 1563 "configure" +#include "confdefs.h" +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char gdbm_open(); + +int main() { +gdbm_open() +; return 0; } +EOF +if { (eval echo configure:1574: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo gdbm | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <<EOF +#define $ac_tr_lib 1 +EOF + + LIBS="-lgdbm $LIBS" + +else + echo "$ac_t""no" 1>&6 +fi + + + + +echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6 +echo "configure:1605: checking for ANSI C header files" >&5 +if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1610 "configure" +#include "confdefs.h" +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <float.h> +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1618: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + ac_cv_header_stdc=yes +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. +cat > conftest.$ac_ext <<EOF +#line 1635 "configure" +#include "confdefs.h" +#include <string.h> +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "memchr" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. +cat > conftest.$ac_ext <<EOF +#line 1653 "configure" +#include "confdefs.h" +#include <stdlib.h> +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "free" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. +if test "$cross_compiling" = yes; then + : +else + cat > conftest.$ac_ext <<EOF +#line 1674 "configure" +#include "confdefs.h" +#include <ctype.h> +#define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int main () { int i; for (i = 0; i < 256; i++) +if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); +exit (0); } + +EOF +if { (eval echo configure:1685: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + : +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_header_stdc=no +fi +rm -fr conftest* +fi + +fi +fi + +echo "$ac_t""$ac_cv_header_stdc" 1>&6 +if test $ac_cv_header_stdc = yes; then + cat >> confdefs.h <<\EOF +#define STDC_HEADERS 1 +EOF + +fi + +ac_header_dirent=no +for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr that defines DIR""... $ac_c" 1>&6 +echo "configure:1713: checking for $ac_hdr that defines DIR" >&5 +if eval "test \"`echo '$''{'ac_cv_header_dirent_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1718 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <$ac_hdr> +int main() { +DIR *dirp = 0; +; return 0; } +EOF +if { (eval echo configure:1726: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + eval "ac_cv_header_dirent_$ac_safe=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_dirent_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_dirent_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <<EOF +#define $ac_tr_hdr 1 +EOF + ac_header_dirent=$ac_hdr; break +else + echo "$ac_t""no" 1>&6 +fi +done +# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix. +if test $ac_header_dirent = dirent.h; then +echo $ac_n "checking for opendir in -ldir""... $ac_c" 1>&6 +echo "configure:1751: checking for opendir in -ldir" >&5 +ac_lib_var=`echo dir'_'opendir | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-ldir $LIBS" +cat > conftest.$ac_ext <<EOF +#line 1759 "configure" +#include "confdefs.h" +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char opendir(); + +int main() { +opendir() +; return 0; } +EOF +if { (eval echo configure:1770: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + LIBS="$LIBS -ldir" +else + echo "$ac_t""no" 1>&6 +fi + +else +echo $ac_n "checking for opendir in -lx""... $ac_c" 1>&6 +echo "configure:1792: checking for opendir in -lx" >&5 +ac_lib_var=`echo x'_'opendir | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lx $LIBS" +cat > conftest.$ac_ext <<EOF +#line 1800 "configure" +#include "confdefs.h" +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char opendir(); + +int main() { +opendir() +; return 0; } +EOF +if { (eval echo configure:1811: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + LIBS="$LIBS -lx" +else + echo "$ac_t""no" 1>&6 +fi + +fi + +for ac_hdr in gdbm.h syslog.h sys/resource.h sys/utsname.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:1837: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1842 "configure" +#include "confdefs.h" +#include <$ac_hdr> +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1847: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <<EOF +#define $ac_tr_hdr 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi +done + +for ac_hdr in fnmatch.h errno.h getopt.h time.h sys/time.h signal.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:1877: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1882 "configure" +#include "confdefs.h" +#include <$ac_hdr> +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1887: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <<EOF +#define $ac_tr_hdr 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi +done + +for ac_hdr in netdb.h sys/socket.h fcntl.h limits.h unistd.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:1917: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1922 "configure" +#include "confdefs.h" +#include <$ac_hdr> +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1927: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <<EOF +#define $ac_tr_hdr 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi +done + + + + +echo $ac_n "checking for working const""... $ac_c" 1>&6 +echo "configure:1957: checking for working const" >&5 +if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1962 "configure" +#include "confdefs.h" + +int main() { + +/* Ultrix mips cc rejects this. */ +typedef int charset[2]; const charset x; +/* SunOS 4.1.1 cc rejects this. */ +char const *const *ccp; +char **p; +/* NEC SVR4.0.2 mips cc rejects this. */ +struct point {int x, y;}; +static struct point const zero = {0,0}; +/* AIX XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in an arm + of an if-expression whose if-part is not a constant expression */ +const char *g = "string"; +ccp = &g + (g ? g-g : 0); +/* HPUX 7.0 cc rejects these. */ +++ccp; +p = (char**) ccp; +ccp = (char const *const *) p; +{ /* SCO 3.2v4 cc rejects this. */ + char *t; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; +} +{ /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; +} +{ /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; +} +{ /* AIX XL C 1.02.0.0 rejects this saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; }; + struct s *b; b->j = 5; +} +{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; +} + +; return 0; } +EOF +if { (eval echo configure:2011: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_c_const=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_c_const=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_c_const" 1>&6 +if test $ac_cv_c_const = no; then + cat >> confdefs.h <<\EOF +#define const +EOF + +fi + +echo $ac_n "checking for size_t""... $ac_c" 1>&6 +echo "configure:2032: checking for size_t" >&5 +if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2037 "configure" +#include "confdefs.h" +#include <sys/types.h> +#if STDC_HEADERS +#include <stdlib.h> +#include <stddef.h> +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "(^|[^a-zA-Z_0-9])size_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_size_t=yes +else + rm -rf conftest* + ac_cv_type_size_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_size_t" 1>&6 +if test $ac_cv_type_size_t = no; then + cat >> confdefs.h <<\EOF +#define size_t unsigned +EOF + +fi + +echo $ac_n "checking whether time.h and sys/time.h may both be included""... $ac_c" 1>&6 +echo "configure:2065: checking whether time.h and sys/time.h may both be included" >&5 +if eval "test \"`echo '$''{'ac_cv_header_time'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2070 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <sys/time.h> +#include <time.h> +int main() { +struct tm *tp; +; return 0; } +EOF +if { (eval echo configure:2079: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_header_time=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_header_time=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_header_time" 1>&6 +if test $ac_cv_header_time = yes; then + cat >> confdefs.h <<\EOF +#define TIME_WITH_SYS_TIME 1 +EOF + +fi + +echo $ac_n "checking whether struct tm is in sys/time.h or time.h""... $ac_c" 1>&6 +echo "configure:2100: checking whether struct tm is in sys/time.h or time.h" >&5 +if eval "test \"`echo '$''{'ac_cv_struct_tm'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2105 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <time.h> +int main() { +struct tm *tp; tp->tm_sec; +; return 0; } +EOF +if { (eval echo configure:2113: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_struct_tm=time.h +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_struct_tm=sys/time.h +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_struct_tm" 1>&6 +if test $ac_cv_struct_tm = sys/time.h; then + cat >> confdefs.h <<\EOF +#define TM_IN_SYS_TIME 1 +EOF + +fi + + +echo $ac_n "checking socklen_t""... $ac_c" 1>&6 +echo "configure:2135: checking socklen_t" >&5 +if eval "test \"`echo '$''{'noffle_cv_type_socklen_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2140 "configure" +#include "confdefs.h" + +#include <sys/types.h> +#include <sys/socket.h> + +int main() { + socklen_t s; +; return 0; } +EOF +if { (eval echo configure:2150: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + noffle_cv_type_socklen_t=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + noffle_cv_type_socklen_t=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$noffle_cv_type_socklen_t" 1>&6 + +if test "x$noffle_cv_type_socklen_t" = "xno" +then + cat >> confdefs.h <<EOF +#define socklen_t unsigned int +EOF + +fi + + + +echo $ac_n "checking return type of signal handlers""... $ac_c" 1>&6 +echo "configure:2176: checking return type of signal handlers" >&5 +if eval "test \"`echo '$''{'ac_cv_type_signal'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2181 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <signal.h> +#ifdef signal +#undef signal +#endif +#ifdef __cplusplus +extern "C" void (*signal (int, void (*)(int)))(int); +#else +void (*signal ()) (); +#endif + +int main() { +int i; +; return 0; } +EOF +if { (eval echo configure:2198: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_type_signal=void +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_type_signal=int +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_type_signal" 1>&6 +cat >> confdefs.h <<EOF +#define RETSIGTYPE $ac_cv_type_signal +EOF + + +echo $ac_n "checking for strftime""... $ac_c" 1>&6 +echo "configure:2217: checking for strftime" >&5 +if eval "test \"`echo '$''{'ac_cv_func_strftime'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2222 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char strftime(); below. */ +#include <assert.h> +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char strftime(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_strftime) || defined (__stub___strftime) +choke me +#else +strftime(); +#endif + +; return 0; } +EOF +if { (eval echo configure:2245: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_strftime=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_strftime=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'strftime`\" = yes"; then + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<\EOF +#define HAVE_STRFTIME 1 +EOF + +else + echo "$ac_t""no" 1>&6 +# strftime is in -lintl on SCO UNIX. +echo $ac_n "checking for strftime in -lintl""... $ac_c" 1>&6 +echo "configure:2267: checking for strftime in -lintl" >&5 +ac_lib_var=`echo intl'_'strftime | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lintl $LIBS" +cat > conftest.$ac_ext <<EOF +#line 2275 "configure" +#include "confdefs.h" +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char strftime(); + +int main() { +strftime() +; return 0; } +EOF +if { (eval echo configure:2286: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<\EOF +#define HAVE_STRFTIME 1 +EOF + +LIBS="-lintl $LIBS" +else + echo "$ac_t""no" 1>&6 +fi + +fi + +echo $ac_n "checking for working fnmatch""... $ac_c" 1>&6 +echo "configure:2313: checking for working fnmatch" >&5 +if eval "test \"`echo '$''{'ac_cv_func_fnmatch_works'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + # Some versions of Solaris or SCO have a broken fnmatch function. +# So we run a test program. If we are cross-compiling, take no chance. +# Thanks to John Oleynick and Franc,ois Pinard for this test. +if test "$cross_compiling" = yes; then + ac_cv_func_fnmatch_works=no +else + cat > conftest.$ac_ext <<EOF +#line 2324 "configure" +#include "confdefs.h" +main() { exit (fnmatch ("a*", "abc", 0) != 0); } +EOF +if { (eval echo configure:2328: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + ac_cv_func_fnmatch_works=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_func_fnmatch_works=no +fi +rm -fr conftest* +fi + +fi + +echo "$ac_t""$ac_cv_func_fnmatch_works" 1>&6 +if test $ac_cv_func_fnmatch_works = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_FNMATCH 1 +EOF + +fi + +for ac_func in fdopen vsnprintf snprintf __vsnprintf __snprintf +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:2353: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2358 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func(); below. */ +#include <assert.h> +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:2381: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <<EOF +#define $ac_tr_func 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi +done + +for ac_func in gethostname mkdir mktime select socket strerror strstr uname +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:2408: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2413 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func(); below. */ +#include <assert.h> +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:2436: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <<EOF +#define $ac_tr_func 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi +done + + + + + + +# Check whether --enable-debug or --disable-debug was given. +if test "${enable_debug+set}" = set; then + enableval="$enable_debug" + : +else + enable_debug=yes +fi + + +if test "x$enable_debug" = "xyes" +then + CFLAGS="$CFLAGS \ +-Wall -O2 -g -pedantic -ansi -W -Wtraditional -Wshadow -Wpointer-arith \ +-Wcast-qual -Wcast-align -Wwrite-strings -Wconversion \ +-Waggregate-return -Wstrict-prototypes -Wmissing-prototypes \ +-Wmissing-declarations -Wnested-externs -Winline \ +-D__USE_BSD -D__USE_XOPEN_EXTENDED -D__USE_POSIX -DDEBUG" +fi + + +# [ This is adapted from mutt. ] +# Check whether --with-docdir or --without-docdir was given. +if test "${with_docdir+set}" = set; then + withval="$with_docdir" + noffle_cv_docdir=$withval +else + echo $ac_n "checking where to put the documentation""... $ac_c" 1>&6 +echo "configure:2492: checking where to put the documentation" >&5 +if eval "test \"`echo '$''{'noffle_cv_docdir'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test x$prefix = xNONE; then + noffle_cv_prefix=$ac_default_prefix + else + noffle_cv_prefix=$prefix + fi + noffle_cv_docdir=$noffle_cv_prefix/doc/noffle + +fi + +echo "$ac_t""$noffle_cv_docdir" 1>&6 + +fi + + + + +CONFIGFILE="/etc/noffle.conf" +cat >> confdefs.h <<EOF +#define CONFIGFILE "$CONFIGFILE" +EOF + + + + + +SPOOLDIR="/var/spool/$PACKAGE" +cat >> confdefs.h <<EOF +#define SPOOLDIR "$SPOOLDIR" +EOF + + + +DOCDIR=$noffle_cv_docdir + + + + +trap '' 1 2 15 +cat > confcache <<\EOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs. It is not useful on other systems. +# If it contains results you don't want to keep, you may remove or edit it. +# +# By default, configure uses ./config.cache as the cache file, +# creating it if it does not exist already. You can give configure +# the --cache-file=FILE option to use a different cache file; that is +# what configure does when it calls configure scripts in +# subdirectories, so they share the cache. +# Giving --cache-file=/dev/null disables caching, for debugging configure. +# config.status only pays attention to the cache file if you give it the +# --recheck option to rerun configure. +# +EOF +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +(set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote substitution + # turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + -e "s/'/'\\\\''/g" \ + -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p' + ;; + esac >> confcache +if cmp -s $cache_file confcache; then + : +else + if test -w $cache_file; then + echo "updating cache $cache_file" + cat confcache > $cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Any assignment to VPATH causes Sun make to only execute +# the first set of double-colon rules, so remove it if not needed. +# If there is a colon in the path, we need to keep it. +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d' +fi + +trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 + +DEFS=-DHAVE_CONFIG_H + +# Without the "./", some shells look in PATH for config.status. +: ${CONFIG_STATUS=./config.status} + +echo creating $CONFIG_STATUS +rm -f $CONFIG_STATUS +cat > $CONFIG_STATUS <<EOF +#! /bin/sh +# Generated automatically by configure. +# Run this file to recreate the current configuration. +# This directory was configured as follows, +# on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# +# $0 $ac_configure_args +# +# Compiler output produced by configure, useful for debugging +# configure, is in ./config.log if it exists. + +ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]" +for ac_option +do + case "\$ac_option" in + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion" + exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;; + -version | --version | --versio | --versi | --vers | --ver | --ve | --v) + echo "$CONFIG_STATUS generated by autoconf version 2.13" + exit 0 ;; + -help | --help | --hel | --he | --h) + echo "\$ac_cs_usage"; exit 0 ;; + *) echo "\$ac_cs_usage"; exit 1 ;; + esac +done + +ac_given_srcdir=$srcdir +ac_given_INSTALL="$INSTALL" + +trap 'rm -fr `echo "Makefile src/Makefile docs/Makefile config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 +EOF +cat >> $CONFIG_STATUS <<EOF + +# Protect against being on the right side of a sed subst in config.status. +sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g; + s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF +$ac_vpsub +$extrasub +s%@SHELL@%$SHELL%g +s%@CFLAGS@%$CFLAGS%g +s%@CPPFLAGS@%$CPPFLAGS%g +s%@CXXFLAGS@%$CXXFLAGS%g +s%@FFLAGS@%$FFLAGS%g +s%@DEFS@%$DEFS%g +s%@LDFLAGS@%$LDFLAGS%g +s%@LIBS@%$LIBS%g +s%@exec_prefix@%$exec_prefix%g +s%@prefix@%$prefix%g +s%@program_transform_name@%$program_transform_name%g +s%@bindir@%$bindir%g +s%@sbindir@%$sbindir%g +s%@libexecdir@%$libexecdir%g +s%@datadir@%$datadir%g +s%@sysconfdir@%$sysconfdir%g +s%@sharedstatedir@%$sharedstatedir%g +s%@localstatedir@%$localstatedir%g +s%@libdir@%$libdir%g +s%@includedir@%$includedir%g +s%@oldincludedir@%$oldincludedir%g +s%@infodir@%$infodir%g +s%@mandir@%$mandir%g +s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g +s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g +s%@INSTALL_DATA@%$INSTALL_DATA%g +s%@PACKAGE@%$PACKAGE%g +s%@VERSION@%$VERSION%g +s%@ACLOCAL@%$ACLOCAL%g +s%@AUTOCONF@%$AUTOCONF%g +s%@AUTOMAKE@%$AUTOMAKE%g +s%@AUTOHEADER@%$AUTOHEADER%g +s%@MAKEINFO@%$MAKEINFO%g +s%@SET_MAKE@%$SET_MAKE%g +s%@CC@%$CC%g +s%@CPP@%$CPP%g +s%@CONFIGFILE@%$CONFIGFILE%g +s%@SPOOLDIR@%$SPOOLDIR%g +s%@DOCDIR@%$DOCDIR%g + +CEOF +EOF + +cat >> $CONFIG_STATUS <<\EOF + +# Split the substitutions into bite-sized pieces for seds with +# small command number limits, like on Digital OSF/1 and HP-UX. +ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script. +ac_file=1 # Number of current file. +ac_beg=1 # First line for current file. +ac_end=$ac_max_sed_cmds # Line after last line for current file. +ac_more_lines=: +ac_sed_cmds="" +while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file + else + sed "${ac_end}q" conftest.subs > conftest.s$ac_file + fi + if test ! -s conftest.s$ac_file; then + ac_more_lines=false + rm -f conftest.s$ac_file + else + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f conftest.s$ac_file" + else + ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file" + fi + ac_file=`expr $ac_file + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_cmds` + fi +done +if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat +fi +EOF + +cat >> $CONFIG_STATUS <<EOF + +CONFIG_FILES=\${CONFIG_FILES-"Makefile src/Makefile docs/Makefile"} +EOF +cat >> $CONFIG_STATUS <<\EOF +for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories. + + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`" + # A "../" for each directory in $ac_dir_suffix. + ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'` + else + ac_dir_suffix= ac_dots= + fi + + case "$ac_given_srcdir" in + .) srcdir=. + if test -z "$ac_dots"; then top_srcdir=. + else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;; + /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;; + *) # Relative path. + srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix" + top_srcdir="$ac_dots$ac_given_srcdir" ;; + esac + + case "$ac_given_INSTALL" in + [/$]*) INSTALL="$ac_given_INSTALL" ;; + *) INSTALL="$ac_dots$ac_given_INSTALL" ;; + esac + + echo creating "$ac_file" + rm -f "$ac_file" + configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure." + case "$ac_file" in + *Makefile*) ac_comsub="1i\\ +# $configure_input" ;; + *) ac_comsub= ;; + esac + + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + sed -e "$ac_comsub +s%@configure_input@%$configure_input%g +s%@srcdir@%$srcdir%g +s%@top_srcdir@%$top_srcdir%g +s%@INSTALL@%$INSTALL%g +" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file +fi; done +rm -f conftest.s* + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='\([ ][ ]*\)[^ ]*%\1#\2' +ac_dC='\3' +ac_dD='%g' +# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE". +ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='\([ ]\)%\1#\2define\3' +ac_uC=' ' +ac_uD='\4%g' +# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_eB='$%\1#\2define\3' +ac_eC=' ' +ac_eD='%g' + +if test "${CONFIG_HEADERS+set}" != set; then +EOF +cat >> $CONFIG_STATUS <<EOF + CONFIG_HEADERS="config.h" +EOF +cat >> $CONFIG_STATUS <<\EOF +fi +for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + echo creating $ac_file + + rm -f conftest.frag conftest.in conftest.out + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + cat $ac_file_inputs > conftest.in + +EOF + +# Transform confdefs.h into a sed script conftest.vals that substitutes +# the proper values into config.h.in to produce config.h. And first: +# Protect against being on the right side of a sed subst in config.status. +# Protect against being in an unquoted here document in config.status. +rm -f conftest.vals +cat > conftest.hdr <<\EOF +s/[\\&%]/\\&/g +s%[\\$`]%\\&%g +s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp +s%ac_d%ac_u%gp +s%ac_u%ac_e%gp +EOF +sed -n -f conftest.hdr confdefs.h > conftest.vals +rm -f conftest.hdr + +# This sed command replaces #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +cat >> conftest.vals <<\EOF +s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */% +EOF + +# Break up conftest.vals because some shells have a limit on +# the size of here documents, and old seds have small limits too. + +rm -f conftest.tail +while : +do + ac_lines=`grep -c . conftest.vals` + # grep -c gives empty output for an empty file on some AIX systems. + if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi + # Write a limited-size here document to conftest.frag. + echo ' cat > conftest.frag <<CEOF' >> $CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS + echo 'CEOF + sed -f conftest.frag conftest.in > conftest.out + rm -f conftest.in + mv conftest.out conftest.in +' >> $CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail + rm -f conftest.vals + mv conftest.tail conftest.vals +done +rm -f conftest.vals + +cat >> $CONFIG_STATUS <<\EOF + rm -f conftest.frag conftest.h + echo "/* $ac_file. Generated automatically by configure. */" > conftest.h + cat conftest.in >> conftest.h + rm -f conftest.in + if cmp -s $ac_file conftest.h 2>/dev/null; then + echo "$ac_file is unchanged" + rm -f conftest.h + else + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + fi + rm -f $ac_file + mv conftest.h $ac_file + fi +fi; done + +EOF +cat >> $CONFIG_STATUS <<EOF + + +EOF +cat >> $CONFIG_STATUS <<\EOF +test -z "$CONFIG_HEADERS" || echo timestamp > stamp-h + +exit 0 +EOF +chmod +x $CONFIG_STATUS +rm -fr confdefs* $ac_clean_files +test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1 + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/configure.in Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,188 @@ +dnl --------------------------------------------------------------------------- +dnl Process this file with autoconf to produce a configure script. +dnl --------------------------------------------------------------------------- + +AC_INIT(src/noffle.c) + +AC_PREREQ(2.13) + +PACKAGE=noffle +VERSION=1.0pre6 + +AM_CONFIG_HEADER(config.h) +AM_INIT_AUTOMAKE($PACKAGE,$VERSION) + +dnl AM_MAINTAINER_MODE + +AC_DEFINE(_GNU_SOURCE) + +AC_LANG_C + + +dnl --------------------------------------------------------------------------- +dnl Checks for programs. +dnl --------------------------------------------------------------------------- + +AC_PROG_CC +AC_PROG_GCC_TRADITIONAL +AC_PROG_INSTALL +AC_PROG_MAKE_SET + + +dnl --------------------------------------------------------------------------- +dnl System checks. +dnl --------------------------------------------------------------------------- + +AC_AIX +AC_ISC_POSIX +AC_MINIX + + +dnl --------------------------------------------------------------------------- +dnl Checks for libraries. +dnl --------------------------------------------------------------------------- + +# Whenever both -lsocket and -lnsl are needed, it seems to be always the +# case that gethostbyname requires -lnsl. So, check -lnsl first, for it +# to be in LIBS before the setsockopt checks are performed. *However*, +# on SINIX-N 5.43, this is false, and gethostent seems to be a better +# candidate. So, let's use it below instead of gethostbyname, and see. + +# [ This is ripped from GNU tar. ] + +AC_CHECK_FUNC(gethostent) +if test $ac_cv_func_gethostent = no; then + AC_CHECK_LIB(nsl, gethostent) +fi +AC_CHECK_FUNC(setsockopt) +if test $ac_cv_func_setsockopt = no; then + AC_CHECK_LIB(socket, setsockopt) +fi + + +AC_CHECK_LIB(gdbm, gdbm_open) + + +dnl --------------------------------------------------------------------------- +dnl Checks for header files. +dnl --------------------------------------------------------------------------- + +AC_HEADER_STDC +AC_HEADER_DIRENT +AC_CHECK_HEADERS(gdbm.h syslog.h sys/resource.h sys/utsname.h) +AC_CHECK_HEADERS(fnmatch.h errno.h getopt.h time.h sys/time.h signal.h) +AC_CHECK_HEADERS(netdb.h sys/socket.h fcntl.h limits.h unistd.h) + + +dnl --------------------------------------------------------------------------- +dnl Checks for typedefs, structures, and compiler characteristics. +dnl --------------------------------------------------------------------------- + +AC_C_CONST +AC_TYPE_SIZE_T +AC_HEADER_TIME +AC_STRUCT_TM + +AC_CACHE_CHECK(socklen_t, noffle_cv_type_socklen_t, +AC_TRY_COMPILE([ +#include <sys/types.h> +#include <sys/socket.h> +], [ socklen_t s; ], +noffle_cv_type_socklen_t=yes, noffle_cv_type_socklen_t=no) +) + +if test "x$noffle_cv_type_socklen_t" = "xno" +then + AC_DEFINE_UNQUOTED(socklen_t, unsigned int) +fi + + +dnl --------------------------------------------------------------------------- +dnl Checks for library functions. +dnl --------------------------------------------------------------------------- + +AC_TYPE_SIGNAL +AC_FUNC_STRFTIME +AC_FUNC_FNMATCH +AC_CHECK_FUNCS(fdopen vsnprintf snprintf __vsnprintf __snprintf) +AC_CHECK_FUNCS(gethostname mkdir mktime select socket strerror strstr uname) + + +dnl --------------------------------------------------------------------------- +dnl Checks for system services. +dnl --------------------------------------------------------------------------- + + +dnl --------------------------------------------------------------------------- +dnl Handle user-specified configure options +dnl --------------------------------------------------------------------------- + +AC_ARG_ENABLE(debug, [ +NOFFLE options: + --enable-debug turn on debugging (default=yes)], , +enable_debug=yes) + +if test "x$enable_debug" = "xyes" +then + CFLAGS="$CFLAGS \ +-Wall -O2 -g -pedantic -ansi -W -Wtraditional -Wshadow -Wpointer-arith \ +-Wcast-qual -Wcast-align -Wwrite-strings -Wconversion \ +-Waggregate-return -Wstrict-prototypes -Wmissing-prototypes \ +-Wmissing-declarations -Wnested-externs -Winline \ +-D__USE_BSD -D__USE_XOPEN_EXTENDED -D__USE_POSIX -DDEBUG" +fi + + +# [ This is adapted from mutt. ] +AC_ARG_WITH(docdir, +[ --with-docdir=PATH specify where to put the documentation +], [noffle_cv_docdir=$withval], +[ AC_CACHE_CHECK(where to put the documentation, noffle_cv_docdir, + [if test x$prefix = xNONE; then + noffle_cv_prefix=$ac_default_prefix + else + noffle_cv_prefix=$prefix + fi + noffle_cv_docdir=$noffle_cv_prefix/doc/noffle + ]) +]) + + +dnl --------------------------------------------------------------------------- +dnl Unquoted defines and substituted variables. +dnl --------------------------------------------------------------------------- + +CONFIGFILE="/etc/noffle.conf" +AC_DEFINE_UNQUOTED(CONFIGFILE,"$CONFIGFILE") +AC_SUBST(CONFIGFILE) + +dnl NOFFLE_EXPAND_DIR(VARNAME, DIR) +dnl expands occurrences of ${prefix} and ${exec_prefix} in the given DIR, +dnl and assigns the resulting string to VARNAME +dnl example: NOFFLE_EXPAND_DIR(LOCALEDIR, "$datadir/locale") +dnl eg, then: AC_DEFINE_UNQUOTED(LOCALEDIR, "$LOCALEDIR") +dnl by Alexandre Oliva +dnl from http://www.cygnus.com/ml/automake/1998-Aug/0040.html +AC_DEFUN(NOFFLE_EXPAND_DIR, [ + $1=$2 + $1=`( + test "x$prefix" = xNONE && prefix="$ac_default_prefix" + test "x$exec_prefix" = xNONE && exec_prefix="${prefix}" + eval echo \""[$]$1"\" + )` +]) + +SPOOLDIR="/var/spool/$PACKAGE" +AC_DEFINE_UNQUOTED(SPOOLDIR, "$SPOOLDIR") +AC_SUBST(SPOOLDIR) + +DOCDIR=$noffle_cv_docdir +AC_SUBST(DOCDIR) + + +dnl --------------------------------------------------------------------------- +dnl Output files. +dnl --------------------------------------------------------------------------- + +AC_OUTPUT(Makefile src/Makefile docs/Makefile) +
--- a/content.c Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,261 +0,0 @@ -/* - content.c - - $Id: content.c 44 2000-05-05 07:23:15Z enz $ -*/ - -#include <dirent.h> -#include <fcntl.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> -#include "common.h" -#include "config.h" -#include "group.h" -#include "log.h" -#include "over.h" -#include "pseudo.h" -#include "util.h" - -struct -{ - DIR *dir; /* Directory for browsing through all - groups */ - int vecFirst; /* First article number in vector */ - int first; /* First live article number */ - int last; /* Last article number */ - unsigned int size; /* Number of overviews. */ - unsigned int max; /* Size of elem. */ - Over **elem; /* Ptr to array with ptrs to overviews. - NULL entries for non-existing article numbers - in group. */ - Str name; - Str file; -} cont = { NULL, 1, 1, 0, 0, 0, NULL, "", "" }; - -void -Cont_app( Over *ov ) -{ - if ( cont.max < cont.size + 1 ) - { - if ( ! ( cont.elem = realloc( cont.elem, - ( cont.max + 500 ) - * sizeof( cont.elem[ 0 ] ) ) ) ) - { - Log_err( "Could not realloc overview list" ); - exit( EXIT_FAILURE ); - } - cont.max += 500; - } - ASSERT( cont.vecFirst > 0 ); - if ( ov ) - Ov_setNumb( ov, cont.vecFirst + cont.size ); - cont.elem[ cont.size++ ] = ov; - cont.last = cont.vecFirst + cont.size - 1; -} - -Bool -Cont_validNumb( int n ) -{ - return ( n != 0 && n >= cont.first && n <= cont.last - && cont.elem[ n - cont.vecFirst ] ); -} - -void -Cont_delete( int n ) -{ - Over **ov; - - if ( ! Cont_validNumb( n ) ) - return; - ov = &cont.elem[ n - cont.vecFirst ]; - free( *ov ); - *ov = NULL; -} - -/* Remove all overviews from content. */ -static void -clearCont() -{ - int i; - - for ( i = 0; i < cont.size; ++i ) - del_Over( cont.elem[ i ] ); - cont.size = 0; -} - -static void -setupEmpty( const char *name ) -{ - cont.last = Grp_last( name ); - cont.first = cont.vecFirst = cont.last + 1; - ASSERT( cont.first > 0 ); -} - -/* Extend content list to size "cnt" and append NULL entries. */ -static void -extendCont( int cnt ) -{ - int i, n; - - if ( cont.size < cnt ) - { - n = cnt - cont.size; - for ( i = 0; i < n; ++i ) - Cont_app( NULL ); - } -} - -/* Discard all cached overviews, and read in the overviews of a new group - from its overviews file. */ -void -Cont_read( const char *name ) -{ - FILE *f; - Over *ov; - int numb; - Str line; - - /* Delete old overviews and make room for new ones. */ - cont.vecFirst = 0; - cont.first = 0; - cont.last = 0; - Utl_cpyStr( cont.name, name ); - clearCont(); - - /* read overviews from overview file and store them in the overviews - list */ - snprintf( cont.file, MAXCHAR, "%s/overview/%s", Cfg_spoolDir(), name ); - f = fopen( cont.file, "r" ); - if ( ! f ) - { - Log_dbg( "No group overview file: %s", cont.file ); - setupEmpty( name ); - return; - } - Log_dbg( "Reading %s", cont.file ); - while ( fgets( line, MAXCHAR, f ) ) - { - if ( ! ( ov = Ov_read( line ) ) ) - { - Log_err( "Overview corrupted in %s: %s", name, line ); - continue; - } - numb = Ov_numb( ov ); - if ( numb < cont.first ) - { - Log_err( "Wrong ordering in %s: %s", name, line ); - continue; - } - if ( cont.first == 0 ) - cont.first = cont.vecFirst = numb; - cont.last = numb; - extendCont( numb - cont.first + 1 ); - cont.elem[ numb - cont.first ] = ov; - } - fclose( f ); - - if ( cont.first == 0 ) - setupEmpty( name ); /* Corrupt overview file recovery */ -} - -void -Cont_write( void ) -{ - Bool anythingWritten; - int i; - FILE *f; - const Over *ov; - - - /* Move the first article no. to the first active article */ - while ( ! Cont_validNumb( cont.first ) && cont.first <= cont.last ) - ++cont.first; - - /* Save the overview */ - if ( ! ( f = fopen( cont.file, "w" ) ) ) - { - Log_err( "Could not open %s for writing", cont.file ); - return; - } - Log_dbg( "Writing %s (%lu)", cont.file, cont.size ); - anythingWritten = FALSE; - for ( i = 0; i < cont.size; ++i ) - { - if ( ( ov = cont.elem[ i ] ) ) - { - if ( ! Pseudo_isGeneralInfo( Ov_msgId( ov ) ) ) - { - if ( ! Ov_write( ov, f ) ) - { - Log_err( "Writing of overview line failed" ); - break; - } - else - anythingWritten = TRUE; - } - } - } - fclose( f ); - - /* - If empty, remove the overview file and set set first to one - beyond last to flag said emptiness. - */ - if ( ! anythingWritten ) - { - unlink( cont.file ); - cont.first = cont.last + 1; - } -} - -const Over * -Cont_get( int numb ) -{ - if ( ! Cont_validNumb( numb ) ) - return NULL; - return cont.elem[ numb - cont.vecFirst ]; -} - -int -Cont_first( void ) { return cont.first; } - -int -Cont_last( void ) { return cont.last; } - -const char * -Cont_grp( void ) { return cont.name; } - -Bool -Cont_nextGrp( Str result ) -{ - struct dirent *d; - - ASSERT( cont.dir ); - if ( ! ( d = readdir( cont.dir ) ) ) - { - cont.dir = NULL; - return FALSE; - } - if ( ! d->d_name ) - return FALSE; - Utl_cpyStr( result, d->d_name ); - result[ MAXCHAR - 1 ] = '\0'; - return TRUE; -} - -Bool -Cont_firstGrp( Str result ) -{ - Str name; - - snprintf( name, MAXCHAR, "%s/overview", Cfg_spoolDir() ); - if ( ! ( cont.dir = opendir( name ) ) ) - { - Log_err( "Cannot open %s", name ); - return FALSE; - } - Cont_nextGrp( result ); /* "." */ - Cont_nextGrp( result ); /* ".." */ - return Cont_nextGrp( result ); -}
--- a/content.h Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,65 +0,0 @@ -/* - content.h - - Contents of a newsgroup - - list of article overviews for selected group. - - The overviews of all articles of a group are stored in an overview file, - filename SPOOLDIR/overview/GROUPNAME. One entire overview file is read - and cached in memory, at a time. - - $Id: content.h 3 2000-01-04 11:35:42Z enz $ -*/ - -#ifndef CONT_H -#define CONT_H - -#include "over.h" - -/* - Try to read overviews from overview file for group <grp>. - Fill with fake articles, if something goes wrong. -*/ -void -Cont_read( const char *grp ); - -/* - Append overview to current list and increment the current - group's last article counter. Ownership of the ptr is transfered - to content -*/ -void -Cont_app( Over *ov ); - -/* Write content */ -void -Cont_write( void ); - -Bool -Cont_validNumb( int numb ); - -const Over * -Cont_get( int numb ); - -void -Cont_delete( int numb ); - -int -Cont_first( void ); - -int -Cont_last( void ); - -const char * -Cont_grp( void ); - -Bool -Cont_nextGrp( Str result ); - -Bool -Cont_firstGrp( Str result ); - -void -Cont_expire( void ); - -#endif
--- a/control.c Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,74 +0,0 @@ -/* - control.c - - $Id: control.c 32 2000-04-29 14:45:56Z enz $ -*/ - -#include "control.h" -#include <stdio.h> -#include "common.h" -#include "content.h" -#include "database.h" -#include "group.h" -#include "itemlist.h" -#include "log.h" -#include "outgoing.h" - -int -Ctrl_cancel( const char *msgId ) -{ - ItemList *refs; - const char *ref; - Str server; - Bool seen = FALSE; - int res = CANCEL_OK; - - /* See if in outgoing and zap if so. */ - if ( Out_find( msgId, server ) ) - { - Out_remove( server, msgId ); - Log_inf( "'%s' cancelled from outgoing queue for '%s'.\n", - msgId, server ); - seen = TRUE; - } - - if ( ! Db_contains( msgId ) ) - { - Log_inf( "Cancel: '%s' not in database.", msgId ); - return seen ? CANCEL_OK : CANCEL_NO_SUCH_MSG; - } - - /* - Retrieve the Xrefs, remove from each group and then - remove from the database. - */ - refs = new_Itl( Db_xref( msgId ), " " ); - for( ref = Itl_first( refs ); ref != NULL; ref = Itl_next( refs ) ) - { - Str grp; - int no; - - if ( sscanf( ref, "%s:%d", grp, &no ) != 2 ) - break; - - if ( Grp_exists( grp ) ) - { - Cont_read( grp ); - Cont_delete( no ); - Cont_write(); - - if ( ! Grp_local( grp ) && ! seen ) - res = CANCEL_NEEDS_MSG; - - Log_dbg( "Removed '%s' from group '%s'.", msgId, grp ); - } - else - { - Log_inf( "Group '%s' in Xref for '%s' not found.", grp, msgId ); - } - } - del_Itl( refs ); - Db_delete( msgId ); - Log_inf( "Message '%s' cancelled.", msgId ); - return res; -}
--- a/control.h Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,25 +0,0 @@ -/* - control.h - - Control actions needed by server and command line. - - $Id: control.h 32 2000-04-29 14:45:56Z enz $ -*/ - -#ifndef CONTROL_H -#define CONTROL_H - -#define CANCEL_OK 0 -#define CANCEL_NO_SUCH_MSG 1 -#define CANCEL_NEEDS_MSG 2 - -/* - Cancel a message. Return CANCEL_OK if completely cancelled, - CANCEL_NO_SUCH_MSG if no message with that ID exists, and - CANCEL_NEEDS_MSG if a 'cancel' message should be propagated upstream - to cancel the message elsewhere. - */ -int -Ctrl_cancel( const char *msgId ); - -#endif
--- a/database.c Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,679 +0,0 @@ -/* - database.c - - $Id: database.c 44 2000-05-05 07:23:15Z enz $ - - Uses GNU gdbm library. Using Berkeley db (included in libc6) was - cumbersome. It is based on Berkeley db 1.85, which has severe bugs - (e.g. it is not recommended to delete or overwrite entries with - overflow pages). -*/ - -#include "database.h" -#include <ctype.h> -#include <errno.h> -#include <fcntl.h> -#include <gdbm.h> -#include <unistd.h> -#include <sys/types.h> -#include <sys/stat.h> -#include "config.h" -#include "itemlist.h" -#include "log.h" -#include "protocol.h" -#include "util.h" -#include "wildmat.h" - -static struct Db -{ - GDBM_FILE dbf; - - /* Start string for Xref header line: "Xref: <host>" */ - Str xrefHost; - - /* Msg Id of presently loaded article, empty if none loaded */ - Str msgId; - - /* Status of loaded article */ - int stat; /* Flags */ - time_t lastAccess; - - /* Overview of loaded article */ - Str subj; - Str from; - Str date; - Str ref; - Str xref; - size_t bytes; - size_t lines; - - /* Article text (except for overview header lines) */ - DynStr *txt; - -} db = { NULL, "(unknown)", "", 0, 0, "", "", "", "", "", 0, 0, NULL }; - -static const char * -errMsg( void ) -{ - if ( errno != 0 ) - return strerror( errno ); - return gdbm_strerror( gdbm_errno ); -} - -Bool -Db_open( void ) -{ - Str name, host; - int flags; - - ASSERT( db.dbf == NULL ); - snprintf( name, MAXCHAR, "%s/data/articles.gdbm", Cfg_spoolDir() ); - flags = GDBM_WRCREAT | GDBM_FAST; - - if ( ! ( db.dbf = gdbm_open( name, 512, flags, 0644, NULL ) ) ) - { - Log_err( "Error opening %s for r/w (%s)", name, errMsg() ); - return FALSE; - } - Log_dbg( "%s opened for r/w", name ); - - if ( db.txt == NULL ) - db.txt = new_DynStr( 5000 ); - - gethostname( host, MAXCHAR ); - snprintf( db.xrefHost, MAXCHAR, "Xref: %s", host ); - - return TRUE; -} - -void -Db_close( void ) -{ - ASSERT( db.dbf ); - Log_dbg( "Closing database" ); - gdbm_close( db.dbf ); - db.dbf = NULL; - del_DynStr( db.txt ); - db.txt = NULL; - Utl_cpyStr( db.msgId, "" ); -} - -static Bool -loadArt( const char *msgId ) -{ - static void *dptr = NULL; - - datum key, val; - Str t = ""; - const char *p; - - ASSERT( db.dbf ); - - if ( strcmp( msgId, db.msgId ) == 0 ) - return TRUE; - - key.dptr = (void *)msgId; - key.dsize = strlen( msgId ) + 1; - if ( dptr != NULL ) - { - free( dptr ); - dptr = NULL; - } - val = gdbm_fetch( db.dbf, key ); - dptr = val.dptr; - if ( dptr == NULL ) - { - Log_dbg( "database.c loadArt: gdbm_fetch found no entry" ); - return FALSE; - } - - Utl_cpyStr( db.msgId, msgId ); - p = Utl_getLn( t, (char *)dptr ); - if ( ! p || sscanf( t, "%x", &db.stat ) != 1 ) - { - Log_err( "Entry in database '%s' is corrupt (status)", msgId ); - return FALSE; - } - p = Utl_getLn( t, p ); - if ( ! p || sscanf( t, "%lu", &db.lastAccess ) != 1 ) - { - Log_err( "Entry in database '%s' is corrupt (lastAccess)", msgId ); - return FALSE; - } - p = Utl_getLn( db.subj, p ); - p = Utl_getLn( db.from, p ); - p = Utl_getLn( db.date, p ); - p = Utl_getLn( db.ref, p ); - p = Utl_getLn( db.xref, p ); - if ( ! p ) - { - Log_err( "Entry in database '%s' is corrupt (overview)", msgId ); - return FALSE; - } - p = Utl_getLn( t, p ); - if ( ! p || sscanf( t, "%u", &db.bytes ) != 1 ) - { - Log_err( "Entry in database '%s' is corrupt (bytes)", msgId ); - return FALSE; - } - p = Utl_getLn( t, p ); - if ( ! p || sscanf( t, "%u", &db.lines ) != 1 ) - { - Log_err( "Entry in database '%s' is corrupt (lines)", msgId ); - return FALSE; - } - DynStr_clear( db.txt ); - DynStr_app( db.txt, p ); - return TRUE; -} - -static Bool -saveArt( void ) -{ - DynStr *s; - Str t = ""; - datum key, val; - - if ( strcmp( db.msgId, "" ) == 0 ) - return FALSE; - s = new_DynStr( 5000 ); - snprintf( t, MAXCHAR, "%x", db.stat ); - DynStr_appLn( s, t ); - snprintf( t, MAXCHAR, "%lu", db.lastAccess ); - DynStr_appLn( s, t ); - DynStr_appLn( s, db.subj ); - DynStr_appLn( s, db.from ); - DynStr_appLn( s, db.date ); - DynStr_appLn( s, db.ref ); - DynStr_appLn( s, db.xref ); - snprintf( t, MAXCHAR, "%u", db.bytes ); - DynStr_appLn( s, t ); - snprintf( t, MAXCHAR, "%u", db.lines ); - DynStr_appLn( s, t ); - DynStr_appDynStr( s, db.txt ); - - key.dptr = (void *)db.msgId; - key.dsize = strlen( db.msgId ) + 1; - val.dptr = (void *)DynStr_str( s ); - val.dsize = DynStr_len( s ) + 1; - if ( gdbm_store( db.dbf, key, val, GDBM_REPLACE ) != 0 ) - { - Log_err( "Could not store %s in database (%s)", errMsg() ); - return FALSE; - } - - del_DynStr( s ); - return TRUE; -} - -Bool -Db_prepareEntry( const Over *ov, const char *grp, int numb ) -{ - const char *msgId; - - ASSERT( db.dbf ); - ASSERT( ov ); - ASSERT( grp ); - - msgId = Ov_msgId( ov ); - Log_dbg( "Preparing entry %s", msgId ); - if ( Db_contains( msgId ) ) - Log_err( "Preparing article twice: %s", msgId ); - - db.stat = DB_NOT_DOWNLOADED; - db.lastAccess = time( NULL ); - - Utl_cpyStr( db.msgId, msgId ); - Utl_cpyStr( db.subj, Ov_subj( ov ) ); - Utl_cpyStr( db.from, Ov_from( ov ) ); - Utl_cpyStr( db.date, Ov_date( ov ) ); - Utl_cpyStr( db.ref, Ov_ref( ov ) ); - snprintf( db.xref, MAXCHAR, "%s:%i", grp, numb ); - db.bytes = Ov_bytes( ov ); - db.lines = Ov_lines( ov ); - - DynStr_clear( db.txt ); - - return saveArt(); -} - -Bool -Db_storeArt( const char *msgId, const char *artTxt ) -{ - Str line, lineEx, field, value; - const char *startPos; - - ASSERT( db.dbf ); - - Log_dbg( "Store article %s", msgId ); - if ( ! loadArt( msgId ) ) - { - Log_err( "Cannot find info about '%s' in database", msgId ); - return FALSE; - } - if ( ! ( db.stat & DB_NOT_DOWNLOADED ) ) - { - Log_err( "Trying to store already retrieved article '%s'", msgId ); - return FALSE; - } - db.stat &= ~DB_NOT_DOWNLOADED; - db.stat &= ~DB_RETRIEVING_FAILED; - db.lastAccess = time( NULL ); - - DynStr_clear( db.txt ); - - /* Read header */ - startPos = artTxt; - while ( TRUE ) - { - artTxt = Utl_getHeaderLn( lineEx, artTxt ); - if ( lineEx[ 0 ] == '\0' ) - { - DynStr_appLn( db.txt, lineEx ); - break; - } - /* Remove fields already in overview and handle x-noffle - headers correctly in case of cascading NOFFLEs */ - if ( Prt_getField( field, value, lineEx ) ) - { - if ( strcmp( field, "x-noffle-status" ) == 0 ) - { - if ( strstr( value, "NOT_DOWNLOADED" ) != 0 ) - db.stat |= DB_NOT_DOWNLOADED; - } - else if ( strcmp( field, "message-id" ) != 0 - && strcmp( field, "xref" ) != 0 - && strcmp( field, "references" ) != 0 - && strcmp( field, "subject" ) != 0 - && strcmp( field, "from" ) != 0 - && strcmp( field, "date" ) != 0 - && strcmp( field, "bytes" ) != 0 - && strcmp( field, "lines" ) != 0 - && strcmp( field, "x-noffle-lastaccess" ) != 0 ) - DynStr_appLn( db.txt, lineEx ); - } - } - - /* Read body */ - while ( ( artTxt = Utl_getLn( line, artTxt ) ) ) - if ( ! ( db.stat & DB_NOT_DOWNLOADED ) ) - DynStr_appLn( db.txt, line ); - - return saveArt(); -} - -void -Db_setStat( const char *msgId, int stat ) -{ - if ( loadArt( msgId ) ) - { - db.stat = stat; - saveArt(); - } -} - -void -Db_updateLastAccess( const char *msgId ) -{ - if ( loadArt( msgId ) ) - { - db.lastAccess = time( NULL ); - saveArt(); - } -} - -void -Db_setXref( const char *msgId, const char *xref ) -{ - if ( loadArt( msgId ) ) - { - Utl_cpyStr( db.xref, xref ); - saveArt(); - } -} - -/* Search best position for breaking a line */ -static const char * -searchBreakPos( const char *line, int wantedLength ) -{ - const char *lastSpace = NULL; - Bool lastWasSpace = FALSE; - int len = 0; - - while ( *line != '\0' ) - { - if ( isspace( *line ) ) - { - if ( len > wantedLength && lastSpace != NULL ) - return lastSpace; - if ( ! lastWasSpace ) - lastSpace = line; - lastWasSpace = TRUE; - } - else - lastWasSpace = FALSE; - ++len; - ++line; - } - if ( len > wantedLength && lastSpace != NULL ) - return lastSpace; - return line; -} - -/* Append header line by breaking long line into multiple lines */ -static void -appendLongHeader( DynStr *target, const char *field, const char *value ) -{ - const int wantedLength = 78; - const char *breakPos, *old; - int len; - - len = strlen( field ); - DynStr_appN( target, field, len ); - DynStr_appN( target, " ", 1 ); - old = value; - while ( isspace( *old ) ) - ++old; - breakPos = searchBreakPos( old, wantedLength - len - 1 ); - DynStr_appN( target, old, breakPos - old ); - if ( *breakPos == '\0' ) - { - DynStr_appN( target, "\n", 1 ); - return; - } - DynStr_appN( target, "\n ", 2 ); - while ( TRUE ) - { - old = breakPos; - while ( isspace( *old ) ) - ++old; - breakPos = searchBreakPos( old, wantedLength - 1 ); - DynStr_appN( target, old, breakPos - old ); - if ( *breakPos == '\0' ) - { - DynStr_appN( target, "\n", 1 ); - return; - } - DynStr_appN( target, "\n ", 2 ); - } -} - -const char * -Db_header( const char *msgId ) -{ - static DynStr *s = NULL; - - Str date, t; - int stat; - const char *p; - - if ( s == NULL ) - s = new_DynStr( 5000 ); - else - DynStr_clear( s ); - ASSERT( db.dbf ); - if ( ! loadArt( msgId ) ) - return NULL; - strftime( date, MAXCHAR, "%Y-%m-%d %H:%M:%S", - localtime( &db.lastAccess ) ); - stat = db.stat; - snprintf( t, MAXCHAR, - "Message-ID: %s\n" - "X-NOFFLE-Status:%s%s%s\n" - "X-NOFFLE-LastAccess: %s\n", - msgId, - stat & DB_INTERESTING ? " INTERESTING" : "", - stat & DB_NOT_DOWNLOADED ? " NOT_DOWNLOADED" : "", - stat & DB_RETRIEVING_FAILED ? " RETRIEVING_FAILED" : "", - date ); - DynStr_app( s, t ); - appendLongHeader( s, "Subject:", db.subj ); - appendLongHeader( s, "From:", db.from ); - appendLongHeader( s, "Date:", db.date ); - appendLongHeader( s, "References:", db.ref ); - DynStr_app( s, "Bytes: " ); - snprintf( t, MAXCHAR, "%u", db.bytes ); - DynStr_appLn( s, t ); - DynStr_app( s, "Lines: " ); - snprintf( t, MAXCHAR, "%u", db.lines ); - DynStr_appLn( s, t ); - appendLongHeader( s, db.xrefHost, db.xref ); - p = strstr( DynStr_str( db.txt ), "\n\n" ); - if ( ! p ) - DynStr_appDynStr( s, db.txt ); - else - DynStr_appN( s, DynStr_str( db.txt ), p - DynStr_str( db.txt ) + 1 ); - return DynStr_str( s ); -} - -const char * -Db_body( const char *msgId ) -{ - const char *p; - - if ( ! loadArt( msgId ) ) - return ""; - p = strstr( DynStr_str( db.txt ), "\n\n" ); - if ( ! p ) - return ""; - return ( p + 2 ); -} - -int -Db_stat( const char *msgId ) -{ - if ( ! loadArt( msgId ) ) - return 0; - return db.stat; -} - -time_t -Db_lastAccess( const char *msgId ) -{ - if ( ! loadArt( msgId ) ) - return -1; - return db.lastAccess; -} - -const char * -Db_ref( const char *msgId ) -{ - if ( ! loadArt( msgId ) ) - return ""; - return db.ref; -} - -const char * -Db_xref( const char *msgId ) -{ - if ( ! loadArt( msgId ) ) - return ""; - return db.xref; -} - -const char * -Db_from( const char *msgId ) -{ - if ( ! loadArt( msgId ) ) - return ""; - return db.from; -} - -const char * -Db_date( const char *msgId ) -{ - if ( ! loadArt( msgId ) ) - return ""; - return db.date; -} - -Bool -Db_contains( const char *msgId ) -{ - datum key; - - ASSERT( db.dbf ); - if ( strcmp( msgId, db.msgId ) == 0 ) - return TRUE; - key.dptr = (void*)msgId; - key.dsize = strlen( msgId ) + 1; - return gdbm_exists( db.dbf, key ); -} - -void -Db_delete( const char *msgId ) -{ - datum key; - - ASSERT( db.dbf ); - if ( strcmp( msgId, db.msgId ) == 0 ) - db.msgId[ 0 ] = '\0'; - key.dptr = (void*)msgId; - key.dsize = strlen( msgId ) + 1; - gdbm_delete( db.dbf, key ); -} - -static datum cursor = { NULL, 0 }; - -Bool -Db_first( const char** msgId ) -{ - ASSERT( db.dbf ); - if ( cursor.dptr != NULL ) - { - free( cursor.dptr ); - cursor.dptr = NULL; - } - cursor = gdbm_firstkey( db.dbf ); - *msgId = cursor.dptr; - return ( cursor.dptr != NULL ); -} - -Bool -Db_next( const char** msgId ) -{ - void *oldDptr = cursor.dptr; - - ASSERT( db.dbf ); - if ( cursor.dptr == NULL ) - return FALSE; - cursor = gdbm_nextkey( db.dbf, cursor ); - free( oldDptr ); - *msgId = cursor.dptr; - return ( cursor.dptr != NULL ); -} - -static int -calcExpireDays( const char *msgId ) -{ - const char *xref; - ItemList *refs; - const char *ref; - int res; - - xref = Db_xref( msgId ); - if ( xref[ 0 ] == '\0' ) - return -1; - - res = -1; - refs = new_Itl( xref, " :" ); - for ( ref = Itl_first( refs ); ref != NULL; ref = Itl_next( refs ) ) - { - Str pattern; - int days; - - Cfg_beginExpireEnum(); - while ( ( days = Cfg_nextExpire( pattern ) ) != -1 ) - if ( Wld_match( ref, pattern ) - && ( ( days > res && res != 0 ) || - days == 0 ) ) - { - res = days; - Log_dbg ( "Custom expiry %d for %s in group %s", - days, msgId, ref ); - break; - } - - Itl_next( refs ); /* Throw away group number */ - } - - if ( res == -1 ) - res = Cfg_expire(); - return res; -} - -Bool -Db_expire( void ) -{ - int cntDel, cntLeft, flags, expDays; - time_t nowTime, lastAccess; - const char *msgId; - Str name, tmpName; - GDBM_FILE tmpDbf; - datum key, val; - - if ( ! Db_open() ) - return FALSE; - snprintf( name, MAXCHAR, "%s/data/articles.gdbm", Cfg_spoolDir() ); - snprintf( tmpName, MAXCHAR, "%s/data/articles.gdbm.new", Cfg_spoolDir() ); - flags = GDBM_NEWDB | GDBM_FAST; - if ( ! ( tmpDbf = gdbm_open( tmpName, 512, flags, 0644, NULL ) ) ) - { - Log_err( "Error opening %s for read/write (%s)", errMsg() ); - Db_close(); - return FALSE; - } - Log_inf( "Expiring articles" ); - cntDel = 0; - cntLeft = 0; - nowTime = time( NULL ); - if ( Db_first( &msgId ) ) - do - { - expDays = calcExpireDays( msgId ); - lastAccess = Db_lastAccess( msgId ); - if ( expDays == -1 ) - Log_err( "Internal error: Failed expiry calculation on %s", - msgId ); - else if ( lastAccess == -1 ) - Log_err( "Internal error: Getting lastAccess of %s failed", - msgId ); - else if ( expDays > 0 - && difftime( nowTime, lastAccess ) > - ( (double) expDays * 24 * 3600 ) ) - { -#ifdef DEBUG - Str last, now; - - Utl_cpyStr( last, ctime( &lastAccess ) ); - last[ strlen( last ) - 1 ] = '\0'; - Utl_cpyStr( now, ctime( &nowTime ) ); - last[ strlen( now ) - 1 ] = '\0'; - Log_dbg( "Expiring %s: last access %s, time now %s", - msgId, last, now ); -#endif - ++cntDel; - } - else - { - ++cntLeft; - key.dptr = (void *)msgId; - key.dsize = strlen( msgId ) + 1; - - val = gdbm_fetch( db.dbf, key ); - if ( val.dptr != NULL ) - { - if ( gdbm_store( tmpDbf, key, val, GDBM_INSERT ) != 0 ) - Log_err( "Could not store %s in new database (%s)", - errMsg() ); - free( val.dptr ); - } - } - } - while ( Db_next( &msgId ) ); - Log_inf( "%lu articles deleted, %lu left", cntDel, cntLeft ); - gdbm_close( tmpDbf ); - Db_close(); - rename( tmpName, name ); - return TRUE; -}
--- a/database.h Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,99 +0,0 @@ -/* - database.h - - Article database. - - $Id: database.h 44 2000-05-05 07:23:15Z enz $ -*/ - -#ifndef DB_H -#define DB_H - -#include <time.h> -#include "common.h" -#include "dynamicstring.h" -#include "over.h" - -/* Article status flags: */ -#define DB_INTERESTING 0x01 /* Was article ever tried to read? */ -#define DB_NOT_DOWNLOADED 0x02 /* Not fully downloaded */ -#define DB_RETRIEVING_FAILED 0x04 /* Retrieving of article failed */ - -/* Open database for r/w. Locking must be done by the caller! */ -Bool -Db_open( void ); - -void -Db_close( void ); - -/* - Creates an database entry for the article from the overview - information. Xref is replaced by grp:numb. -*/ -Bool -Db_prepareEntry( const Over *ov, const char *grp, int numb ); - -/* Store full article. Can only be used after Db_prepareEntry. */ -Bool -Db_storeArt( const char *msgId, const char *artTxt ); - -void -Db_setStat( const char *msgId, int stat ); - -void -Db_updateLastAccess( const char *msgId ); - -/* Xref header line without hostname */ -void -Db_setXref( const char *msgId, const char *xref ); - -const char * -Db_header( const char *msgId ); - -const char * -Db_body( const char *msgId ); - -int -Db_stat( const char *msgId ); - -/* Get last modification time of entry. Returns -1, if msgId non-existing. */ -time_t -Db_lastAccess( const char *msgId ); - -/* Value of the References header line */ -const char * -Db_ref( const char *msgId ); - -/* Value of the From header line */ -const char * -Db_from( const char *msgId ); - -/* Value of the Date header line */ -const char * -Db_date( const char *msgId ); - -/* Xref header line without hostname */ -const char * -Db_xref( const char *msgId ); - -Bool -Db_contains( const char *msgId ); - -/* Delete entry from database */ -void -Db_delete( const char *msgId ); - -Bool -Db_first( const char** msgId ); - -Bool -Db_next( const char** msgId ); - -/* - Expire all articles that have not been accessed for a number of - days determined by their group membership and noffle configuration. - */ -Bool -Db_expire( void ); - -#endif
--- a/dynamicstring.c Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,120 +0,0 @@ -/* - dynamicstring.c - - $Id: dynamicstring.c 3 2000-01-04 11:35:42Z enz $ -*/ - -#include "dynamicstring.h" - -#include <sys/types.h> -#include "log.h" - -struct DynStr -{ - size_t len; /* Current length (without trailing '\0') */ - size_t max; /* Max length that fits into buffer (incl. trailing '\0') */ - char *str; -}; - -static void -reallocStr( DynStr *self, size_t max ) -{ - if ( max <= self->max ) - return; - if ( ! ( self->str = (char *)realloc( self->str, max ) ) ) - { - Log_err( "Realloc of DynStr failed" ); - exit( EXIT_FAILURE ); - } - if ( self->max == 0 ) /* First allocation? */ - *(self->str) = '\0'; - self->max = max; -} - -DynStr * -new_DynStr( size_t reserve ) -{ - DynStr *s; - - if ( ! ( s = (DynStr *) malloc( sizeof( DynStr ) ) ) ) - { - Log_err( "Allocation of DynStr failed" ); - exit( EXIT_FAILURE ); - } - s->len = 0; - s->max = 0; - s->str = NULL; - if ( reserve > 0 ) - reallocStr( s, reserve + 1 ); - return s; -} - -void -del_DynStr( DynStr *self ) -{ - if ( ! self ) - return; - free( self->str ); - self->str = NULL; - free( self ); -} - -size_t -DynStr_len( const DynStr *self ) -{ - return self->len; -} - -const char * -DynStr_str( const DynStr *self ) -{ - return self->str; -} - -void -DynStr_app( DynStr *self, const char *s ) -{ - size_t len; - - len = strlen( s ); - if ( self->len + len + 1 > self->max ) - reallocStr( self, self->len * 2 + len + 1 ); - strcpy( self->str + self->len, s ); - self->len += len; -} - -void -DynStr_appDynStr( DynStr *self, const DynStr *s ) -{ - if ( self->len + s->len + 1 > self->max ) - reallocStr( self, self->len * 2 + s->len + 1 ); - memcpy( self->str + self->len, s->str, s->len + 1 ); - self->len += s->len; -} - -void -DynStr_appLn( DynStr *self, const char *s ) -{ - DynStr_app( self, s ); - DynStr_app( self, "\n" ); -} - -void -DynStr_appN( DynStr *self, const char *s, size_t n ) -{ - size_t len = self->len; - - if ( len + n + 1 > self->max ) - reallocStr( self, len * 2 + n + 1 ); - strncat( self->str + len, s, n ); - self->len = len + strlen( self->str + len ); -} - -void -DynStr_clear( DynStr *self ) -{ - self->len = 0; - if ( self->max > 0 ) - *(self->str) = '\0'; -} -
--- a/dynamicstring.h Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,54 +0,0 @@ -/* - dynamicstring.h - - String utilities - - $Id: dynamicstring.h 3 2000-01-04 11:35:42Z enz $ -*/ - -#ifndef DYNAMICSTRING_H -#define DYNAMICSTRING_H - -#include <sys/types.h> - -/* A dynamically growing string */ -struct DynStr; -typedef struct DynStr DynStr; - -/* Create new DynStr with given capacity */ -DynStr * -new_DynStr( size_t reserve ); - -/* Delete DynStr */ -void -del_DynStr( DynStr *self ); - -/* Return DynStr's length */ -size_t -DynStr_len( const DynStr *self ); - -/* Return DynStr's content ptr */ -const char * -DynStr_str( const DynStr *self ); - -/* append C-string to DynStr */ -void -DynStr_app( DynStr *self, const char *s ); - -/* append a DynStr to DynStr */ -void -DynStr_appDynStr( DynStr *self, const DynStr *s ); - -/* Append C-string + newline to DynStr */ -void -DynStr_appLn( DynStr *self, const char *s ); - -/* Append a maximum of n characters from C-string s to DynStr self */ -void -DynStr_appN( DynStr *self, const char *s, size_t n ); - -/* Truncate content of DynString to zero length */ -void -DynStr_clear( DynStr *self ); - -#endif
--- a/fetch.c Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,247 +0,0 @@ -/* - fetch.c - - $Id: fetch.c 3 2000-01-04 11:35:42Z enz $ -*/ - -#include "fetch.h" -#include <errno.h> -#include <time.h> -#include <signal.h> -#include "client.h" -#include "config.h" -#include "content.h" -#include "dynamicstring.h" -#include "fetchlist.h" -#include "request.h" -#include "group.h" -#include "log.h" -#include "outgoing.h" -#include "protocol.h" -#include "pseudo.h" -#include "util.h" - -struct Fetch -{ - Bool ready; - Str serv; -} fetch = { FALSE, "" }; - -static Bool -connectToServ( const char *name ) -{ - Log_inf( "Fetch from '%s'", name ); - if ( ! Client_connect( name ) ) - { - Log_err( "Could not connect to %s", name ); - return FALSE; - } - return TRUE; -} - -void -Fetch_getNewGrps( void ) -{ - time_t t; - Str file; - - ASSERT( fetch.ready ); - snprintf( file, MAXCHAR, "%s/groupinfo.lastupdate", Cfg_spoolDir() ); - if ( ! Utl_getStamp( &t, file ) ) - { - Log_err( "Cannot read %s. Please run noffle --query groups", file ); - return; - } - Log_inf( "Updating groupinfo" ); - Client_getNewgrps( &t ); - Utl_stamp( file ); -} - -void -Fetch_getNewArts( const char *name, FetchMode mode ) -{ - int next, first, last, oldLast; - - if ( ! Client_changeToGrp( name ) ) - { - Log_err( "Could not change to group %s", name ); - return; - } - Cont_read( name ); - Client_rmtFirstLast( &first, &last ); - next = Grp_rmtNext( name ); - oldLast = Cont_last(); - if ( next == last + 1 ) - { - Log_inf( "No new articles in %s", name ); - Cont_write(); - Grp_setFirstLast( name, Cont_first(), Cont_last() ); - return; - } - if ( first == 0 && last == 0 ) - { - Log_inf( "No articles in %s", name ); - Cont_write(); - Grp_setFirstLast( name, Cont_first(), Cont_last() ); - return; - } - if ( next > last + 1 ) - { - Log_err( "Article number inconsistent (%s rmt=%lu-%lu, next=%lu)", - name, first, last, next ); - Pseudo_cntInconsistent( name, first, last, next ); - } - else if ( next < first ) - { - Log_inf( "Missing articles (%s first=%lu next=%lu)", - name, first, next ); - Pseudo_missArts( name, first, next ); - } - else - first = next; - if ( last - first > Cfg_maxFetch() ) - { - Log_ntc( "Cutting number of overviews to %lu", Cfg_maxFetch() ); - first = last - Cfg_maxFetch() + 1; - } - Log_inf( "Getting remote overviews %lu-%lu for group %s", - first, last, name ); - Client_getOver( first, last, mode ); - Cont_write(); - Grp_setFirstLast( name, Cont_first(), Cont_last() ); -} - -void -Fetch_updateGrps( void ) -{ - FetchMode mode; - int i, size; - const char* name; - - ASSERT( fetch.ready ); - Fetchlist_read(); - size = Fetchlist_size(); - for ( i = 0; i < size; ++i ) - { - Fetchlist_element( &name, &mode, i ); - if ( strcmp( Grp_serv( name ), fetch.serv ) == 0 ) - Fetch_getNewArts( name, mode ); - } -} - -void -Fetch_getReq_( void ) -{ - Str msgId; - DynStr *list; - const char *p; - int count = 0; - - ASSERT( fetch.ready ); - Log_dbg( "Retrieving articles marked for download" ); - list = new_DynStr( 10000 ); - if ( Req_first( fetch.serv, msgId ) ) - do - { - DynStr_appLn( list, msgId ); - if ( ++count % 20 == 0 ) /* Send max. 20 ARTICLE cmds at once */ - { - p = DynStr_str( list ); - Client_retrieveArtList( p ); - while ( ( p = Utl_getLn( msgId, p ) ) ) - Req_remove( fetch.serv, msgId ); - DynStr_clear( list ); - } - } - while ( Req_next( msgId ) ); - p = DynStr_str( list ); - Client_retrieveArtList( p ); - while ( ( p = Utl_getLn( msgId, p ) ) ) - Req_remove( fetch.serv, msgId ); - del_DynStr( list ); -} - -void -Fetch_postArts( void ) -{ - DynStr *s; - Str msgId, cmd, errStr, sender; - int ret; - const char *txt; - FILE *f; - sig_t lastHandler; - - s = new_DynStr( 10000 ); - if ( Out_first( fetch.serv, msgId, s ) ) - { - Log_inf( "Posting articles" ); - do - { - txt = DynStr_str( s ); - Out_remove( fetch.serv, msgId ); - if ( ! Client_postArt( msgId, txt, errStr ) ) - { - Utl_cpyStr( sender, Cfg_mailTo() ); - if ( strcmp( sender, "" ) == 0 - && ! Prt_searchHeader( txt, "SENDER", sender ) - && ! Prt_searchHeader( txt, "X-NOFFLE-X-SENDER", - sender ) /* see server.c */ - && ! Prt_searchHeader( txt, "FROM", sender ) ) - Log_err( "Article %s has no From/Sender/X-Sender field", - msgId ); - else - { - Log_ntc( "Return article to '%s' by mail", sender ); - snprintf( cmd, MAXCHAR, - "mail -s '[ NOFFLE: Posting failed ]' '%s'", - sender ); - lastHandler = signal( SIGPIPE, SIG_IGN ); - f = popen( cmd, "w" ); - if ( f == NULL ) - Log_err( "Invocation of '%s' failed (%s)", cmd, - strerror( errno ) ); - else - { - fprintf( f, - "\t[ NOFFLE: POSTING OF ARTICLE FAILED ]\n" - "\n" - "\t[ The posting of your article failed. ]\n" - "\t[ Reason of failure at remote server: ]\n" - "\n" - "\t[ %s ]\n" - "\n" - "\t[ Full article text has been appended. ]\n" - "\n" - "%s" - ".\n", - errStr, txt ); - ret = pclose( f ); - if ( ret != EXIT_SUCCESS ) - Log_err( "'%s' exit value %d", cmd, ret ); - signal( SIGPIPE, lastHandler ); - } - } - } - } - while ( Out_next( msgId, s ) ); - } - del_DynStr( s ); -} - -Bool -Fetch_init( const char *serv ) -{ - if ( ! connectToServ( serv ) ) - return FALSE; - Utl_cpyStr( fetch.serv, serv ); - fetch.ready = TRUE; - return TRUE; -} - -void -Fetch_close() -{ - Client_disconnect(); - fetch.ready = FALSE; - Log_inf( "Fetch from '%s' finished", fetch.serv ); -}
--- a/fetch.h Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,38 +0,0 @@ -/* - fetch.h - - Do the daily business by using client.c - - $Id: fetch.h 3 2000-01-04 11:35:42Z enz $ -*/ - -#ifndef FETCH_H -#define FETCH_H - -#include "common.h" -#include "database.h" -#include "fetchlist.h" - -Bool -Fetch_init( const char *serv ); - -void -Fetch_close( void ); - -void -Fetch_getNewGrps( void ); - -void -Fetch_updateGrps( void ); - -void -Fetch_getReq_( void ); - -void -Fetch_postArts( void ); - -/* Get new articles in group "grp", using fetch mode "mode". */ -void -Fetch_getNewArts( const char *grp, FetchMode mode ); - -#endif
--- a/fetchlist.c Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,205 +0,0 @@ -/* - fetchlist.c - - $Id: fetchlist.c 3 2000-01-04 11:35:42Z enz $ -*/ - -#include "fetchlist.h" -#include "config.h" -#include "log.h" -#include "util.h" - -struct Elem -{ - Str name; - FetchMode mode; -}; - -static struct Fetchlist -{ - struct Elem *elem; - int size; - int max; -} fetchlist = { NULL, 0, 0 }; - -static const char * -getFile( void ) -{ - static Str file; - snprintf( file, MAXCHAR, "%s/fetchlist", Cfg_spoolDir() ); - return file; -} - -static void -clearList( void ) -{ - fetchlist.size = 0; -} - -static int -compareElem( const void *elem1, const void *elem2 ) -{ - struct Elem* e1 = (struct Elem*)elem1; - struct Elem* e2 = (struct Elem*)elem2; - return strcmp( e1->name, e2->name ); -} - -static struct Elem * -searchElem( const char *name ) -{ - int i; - - for ( i = 0; i < fetchlist.size; ++i ) - if ( strcmp( name, fetchlist.elem[ i ].name ) == 0 ) - return &fetchlist.elem[ i ]; - return NULL; -} - -static void -appGrp( const char *name, FetchMode mode ) -{ - struct Elem elem; - - if ( fetchlist.max < fetchlist.size + 1 ) - { - if ( ! ( fetchlist.elem - = realloc( fetchlist.elem, - ( fetchlist.max + 50 ) - * sizeof( fetchlist.elem[ 0 ] ) ) ) ) - { - Log_err( "Could not realloc fetchlist" ); - exit( EXIT_FAILURE ); - } - fetchlist.max += 50; - } - strcpy( elem.name, name ); - elem.mode = mode; - fetchlist.elem[ fetchlist.size++ ] = elem; -} - -void -Fetchlist_read( void ) -{ - FILE *f; - const char *file = getFile(); - char *p; - FetchMode mode = OVER; - Bool valid; - int ret; - Str line, grp, modeStr; - - Log_dbg( "Reading %s", file ); - clearList(); - if ( ! ( f = fopen( file, "r" ) ) ) - { - Log_inf( "No file %s", file ); - return; - } - while ( fgets( line, MAXCHAR, f ) ) - { - p = Utl_stripWhiteSpace( line ); - if ( *p == '#' || *p == '\0' ) - continue; - ret = sscanf( p, "%s %s", grp, modeStr ); - valid = TRUE; - if ( ret < 1 || ret > 2 ) - valid = FALSE; - else if ( ret >= 2 ) - { - if ( strcmp( modeStr, "full" ) == 0 ) - mode = FULL; - else if ( strcmp( modeStr, "thread" ) == 0 ) - mode = THREAD; - else if ( strcmp( modeStr, "over" ) == 0 ) - mode = OVER; - else - valid = FALSE; - } - if ( ! valid ) - { - Log_err( "Invalid entry in %s: %s", file, line ); - continue; - } - appGrp( grp, mode ); - } - fclose( f ); -} - -Bool -Fetchlist_write( void ) -{ - int i; - FILE *f; - const char *file = getFile(); - const char *modeStr = ""; - - qsort( fetchlist.elem, fetchlist.size, sizeof( fetchlist.elem[ 0 ] ), - compareElem ); - if ( ! ( f = fopen( file, "w" ) ) ) - { - Log_err( "Could not open %s for writing", file ); - return FALSE; - } - for ( i = 0; i < fetchlist.size; ++i ) - { - switch ( fetchlist.elem[ i ].mode ) - { - case FULL: - modeStr = "full"; break; - case THREAD: - modeStr = "thread"; break; - case OVER: - modeStr = "over"; break; - } - fprintf( f, "%s %s\n", fetchlist.elem[ i ].name, modeStr ); - } - fclose( f ); - return TRUE; -} - -int -Fetchlist_size( void ) -{ - return fetchlist.size; -} - -Bool -Fetchlist_contains( const char *name ) -{ - return ( searchElem( name ) != NULL ); -} - -Bool -Fetchlist_element( const char **name, FetchMode *mode, int index ) -{ - if ( index < 0 || index >= fetchlist.size ) - return FALSE; - *name = fetchlist.elem[ index ].name; - *mode = fetchlist.elem[ index ].mode; - return TRUE; -} - -Bool -Fetchlist_add( const char *name, FetchMode mode ) -{ - struct Elem *elem = searchElem( name ); - if ( elem == NULL ) - { - appGrp( name, mode ); - return TRUE; - } - strcpy( elem->name, name ); - elem->mode = mode; - return FALSE; -} - -Bool -Fetchlist_remove( const char *name ) -{ - struct Elem *elem = searchElem( name ); - if ( elem == NULL ) - return FALSE; - *elem = fetchlist.elem[ fetchlist.size - 1 ]; - --fetchlist.size; - return TRUE; -}
--- a/fetchlist.h Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +0,0 @@ -/* - fetchlist.h - - List of groups that are to be fetched presently. - - $Id: fetchlist.h 3 2000-01-04 11:35:42Z enz $ -*/ - -#ifndef FETCHLIST_H -#define FETCHLIST_H - -#include "common.h" - -typedef enum { FULL, THREAD, OVER } FetchMode; - -void -Fetchlist_read( void ); - -/* Invalidates any indices (list is sorted by name before saving) */ -Bool -Fetchlist_write( void ); - -int -Fetchlist_size( void ); - -Bool -Fetchlist_contains( const char *name ); - -/* Get element number index. */ -Bool -Fetchlist_element( const char **name, FetchMode *mode, int index ); - -/* Add entry. Invalidates any indices. Returns TRUE if new entry, FALSE if - entry was overwritten. */ -Bool -Fetchlist_add( const char *name, FetchMode mode ); - -/* Remove entry. Invalidates any indices. Returns FALSE if not found. */ -Bool -Fetchlist_remove( const char *name ); - -#endif
--- a/group.c Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,370 +0,0 @@ -/* - group.c - - The group database resides in groupinfo.gdbm and stores all we know about - the groups we know of. One database record is cached in the global struct - grp. Group information is transfered between the grp and the database by - loadGrp() and saveGrp(). This is done transparently. Access to the groups - database is done by group name, by the functions defined in group.h. - - $Id: group.c 47 2000-05-05 08:32:53Z enz $ -*/ - -#include "group.h" -#include <gdbm.h> -#include <errno.h> -#include <fcntl.h> -#include <sys/stat.h> -#include "config.h" -#include "log.h" -#include "util.h" - -/* currently only used within grp */ -typedef struct -{ - int first; /* number of first article within group */ - int last; /* number of last article within group */ - int rmtNext; - time_t created; - time_t lastAccess; -} Entry; - -struct -{ - Str name; /* name of the group */ - Entry entry; /* more information about this group */ - Str serv; /* server the group resides on */ - Str dsc; /* description of the group */ - char postAllow; /* Posting status */ - GDBM_FILE dbf; -} grp = { "(no grp)", { 0, 0, 0, 0, 0 }, "", "", ' ', NULL }; - -/* - Note: postAllow should really go in Entry. But changing Entry would - make backwards group file format capability tricky, so it goes - where it is, and we test the length of the retrieved record to - determine if it exists. - - Someday if we really change the record format this should be tidied up. - */ - -static const char * -errMsg( void ) -{ - if ( errno != 0 ) - return strerror( errno ); - return gdbm_strerror( gdbm_errno ); -} - -Bool -Grp_open( void ) -{ - Str name; - int flags; - - ASSERT( grp.dbf == NULL ); - snprintf( name, MAXCHAR, "%s/data/groupinfo.gdbm", Cfg_spoolDir() ); - flags = GDBM_WRCREAT | GDBM_FAST; - if ( ! ( grp.dbf = gdbm_open( name, 512, flags, 0644, NULL ) ) ) - { - Log_err( "Error opening %s for r/w (%s)", errMsg() ); - return FALSE; - } - Log_dbg( "%s opened for r/w", name ); - return TRUE; -} - -void -Grp_close( void ) -{ - ASSERT( grp.dbf ); - Log_dbg( "Closing groupinfo" ); - gdbm_close( grp.dbf ); - grp.dbf = NULL; - Utl_cpyStr( grp.name, "" ); -} - -/* Load group info from gdbm-database into global struct grp */ -static Bool -loadGrp( const char *name ) -{ - const char *p; - datum key, val; - - ASSERT( grp.dbf ); - if ( strcmp( grp.name, name ) == 0 ) - return TRUE; - key.dptr = (void *)name; - key.dsize = strlen( name ) + 1; - val = gdbm_fetch( grp.dbf, key ); - if ( val.dptr == NULL ) - return FALSE; - grp.entry = *( (Entry *)val.dptr ); - p = val.dptr + sizeof( grp.entry ); - Utl_cpyStr( grp.serv, p ); - p += strlen( p ) + 1; - Utl_cpyStr( grp.dsc, p ); - p += strlen( p) + 1; - if ( p - val.dptr < val.dsize ) - grp.postAllow = p[ 0 ]; - else - grp.postAllow = 'y'; - Utl_cpyStr( grp.name, name ); - free( val.dptr ); - return TRUE; -} - -/* Save group info from global struct grp into gdbm-database */ -static void -saveGrp( void ) -{ - size_t lenServ, lenDsc, bufLen; - datum key, val; - void *buf; - char *p; - - ASSERT( grp.dbf ); - lenServ = strlen( grp.serv ); - lenDsc = strlen( grp.dsc ); - bufLen = sizeof( grp.entry ) + lenServ + lenDsc + 2 + sizeof( char ); - buf = malloc( bufLen ); - memcpy( buf, (void *)&grp.entry, sizeof( grp.entry ) ); - p = (char *)buf + sizeof( grp.entry ); - strcpy( p, grp.serv ); - p += lenServ + 1; - strcpy( p, grp.dsc ); - p += lenDsc + 1; - p[ 0 ] = grp.postAllow; - key.dptr = (void *)grp.name; - key.dsize = strlen( grp.name ) + 1; - val.dptr = buf; - val.dsize = bufLen; - if ( gdbm_store( grp.dbf, key, val, GDBM_REPLACE ) != 0 ) - Log_err( "Could not save group %s: %s", errMsg() ); - free( buf ); -} - -Bool -Grp_exists( const char *name ) -{ - datum key; - - ASSERT( grp.dbf ); - key.dptr = (void*)name; - key.dsize = strlen( name ) + 1; - return gdbm_exists( grp.dbf, key ); -} - -Bool -Grp_local( const char *name ) -{ - if ( ! loadGrp( name ) ) - return 0; - return ( strcmp( grp.serv, GRP_LOCAL_SERVER_NAME ) == 0 ); -} - -void -Grp_create( const char *name ) -{ - Utl_cpyStr( grp.name, name ); - Utl_cpyStr( grp.serv, "(unknown)" ); - grp.dsc[ 0 ] = '\0'; - grp.entry.first = 1; - grp.entry.last = 0; - grp.entry.rmtNext = 0; - grp.entry.created = 0; - grp.entry.lastAccess = 0; - grp.postAllow = 'y'; - saveGrp(); -} - -void -Grp_delete( const char *name ) -{ - datum key; - - ASSERT( grp.dbf ); - key.dptr = (void*)name; - key.dsize = strlen( name ) + 1; - gdbm_delete( grp.dbf, key ); -} - -const char * -Grp_dsc( const char *name ) -{ - if ( ! loadGrp( name ) ) - return NULL; - return grp.dsc; -} - -const char * -Grp_serv( const char *name ) -{ - static Str serv = ""; - - if ( ! loadGrp( name ) ) - return "[unknown grp]"; - if ( Cfg_servListContains( grp.serv ) - || Grp_local( name ) ) - Utl_cpyStr( serv, grp.serv ); - else - snprintf( serv, MAXCHAR, "[%s]", grp.serv ); - return serv; -} - -int -Grp_first( const char *name ) -{ - if ( ! loadGrp( name ) ) - return 0; - return grp.entry.first; -} - -int -Grp_last( const char *name ) -{ - if ( ! loadGrp( name ) ) - return 0; - return grp.entry.last; -} - -int -Grp_lastAccess( const char *name ) -{ - if ( ! loadGrp( name ) ) - return 0; - return grp.entry.lastAccess; -} - -int -Grp_rmtNext( const char *name ) -{ - if ( ! loadGrp( name ) ) - return 0; - return grp.entry.rmtNext; -} - -time_t -Grp_created( const char *name ) -{ - if ( ! loadGrp( name ) ) - return 0; - return grp.entry.created; -} - -char -Grp_postAllow( const char *name ) -{ - if ( ! loadGrp( name ) ) - return 0; - return grp.postAllow; -} - - -/* Replace group's description (only if value != ""). */ -void -Grp_setDsc( const char *name, const char *value ) -{ - if ( loadGrp( name ) ) - { - Utl_cpyStr( grp.dsc, value ); - saveGrp(); - } -} - -void -Grp_setLocal( const char *name ) -{ - Grp_setServ( name, GRP_LOCAL_SERVER_NAME ); -} - -void -Grp_setServ( const char *name, const char *value ) -{ - if ( loadGrp( name ) ) - { - Utl_cpyStr( grp.serv, value ); - saveGrp(); - } -} - -void -Grp_setCreated( const char *name, time_t value ) -{ - if ( loadGrp( name ) ) - { - grp.entry.created = value; - saveGrp(); - } -} - -void -Grp_setRmtNext( const char *name, int value ) -{ - if ( loadGrp( name ) ) - { - grp.entry.rmtNext = value; - saveGrp(); - } -} - -void -Grp_setLastAccess( const char *name, int value ) -{ - if ( loadGrp( name ) ) - { - grp.entry.lastAccess = value; - saveGrp(); - } -} - -void -Grp_setPostAllow( const char *name, char postAllow ) -{ - if ( loadGrp( name ) ) - { - grp.postAllow = postAllow; - saveGrp(); - } -} - -void -Grp_setFirstLast( const char *name, int first, int last ) -{ - if ( loadGrp( name ) ) - { - grp.entry.first = first; - grp.entry.last = last; - saveGrp(); - } -} - -static datum cursor = { NULL, 0 }; - -Bool -Grp_firstGrp( const char **name ) -{ - ASSERT( grp.dbf ); - if ( cursor.dptr != NULL ) - { - free( cursor.dptr ); - cursor.dptr = NULL; - } - cursor = gdbm_firstkey( grp.dbf ); - *name = cursor.dptr; - return ( cursor.dptr != NULL ); -} - -Bool -Grp_nextGrp( const char **name ) -{ - void *oldDptr = cursor.dptr; - - ASSERT( grp.dbf ); - if ( cursor.dptr == NULL ) - return FALSE; - cursor = gdbm_nextkey( grp.dbf, cursor ); - free( oldDptr ); - *name = cursor.dptr; - return ( cursor.dptr != NULL ); -}
--- a/group.h Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,114 +0,0 @@ -/* - group.h - - Groups database - - $Id: group.h 47 2000-05-05 08:32:53Z enz $ -*/ - -#ifndef GRP_H -#define GRP_H - -#include <time.h> -#include "common.h" - -#define GRP_LOCAL_SERVER_NAME "(local)" - -/* open group database */ -Bool -Grp_open( void ); - -/* close group database */ -void -Grp_close( void ); - -/* does group exist? */ -Bool -Grp_exists( const char *name ); - -/* is it a local group? */ -Bool -Grp_local( const char *name ); - -/* create new group and save it in database */ -void -Grp_create( const char *name ); - -/* delete a group and its articles from the database. */ -void -Grp_delete( const char *name ); - -/* Get group description */ -const char * -Grp_dsc( const char *name ); - -/* Get server the group resides on */ -const char * -Grp_serv( const char *name ); - -/* - Get article number of the first article in the group - This number is a hint only, it is independent of the - real articles in content.c -*/ -int -Grp_first( const char *name ); - -/* - Get article number of the last article in the group - This number is a hint only, it is independent of the - real articles in content.c -*/ -int -Grp_last( const char *name ); - -int -Grp_lastAccess( const char *name ); - -int -Grp_rmtNext( const char *name ); - -time_t -Grp_created( const char *name ); - -char -Grp_postAllow( const char *name ); - -/* Replace group's description (only if value != ""). */ -void -Grp_setDsc( const char *name, const char *value ); - -void -Grp_setLocal( const char *name ); - -void -Grp_setServ( const char *name, const char *value ); - -void -Grp_setCreated( const char *name, time_t value ); - -void -Grp_setRmtNext( const char *name, int value ); - -void -Grp_setLastAccess( const char *name, int value ); - -void -Grp_setFirstLast( const char *name, int first, int last ); - -void -Grp_setPostAllow( const char *name, char postAllow ); - -/* Begin iterating trough the names of all groups. Store name of first - group (or NULL if there aren't any) in name. Returns whether there are - any groups. */ -Bool -Grp_firstGrp( const char **name ); - -/* Continue iterating trough the names of all groups. Store name of next - group (or NULL if there aren't any more) in name. Returns TRUE on - success, FALSE when there are no more groups. */ -Bool -Grp_nextGrp( const char **name ); - -#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/install-sh Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,251 @@ +#!/bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5 (mit/util/scripts/install.sh). +# +# Copyright 1991 by the Massachusetts Institute of Technology +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that +# copyright notice and this permission notice appear in supporting +# documentation, and that the name of M.I.T. not be used in advertising or +# publicity pertaining to distribution of the software without specific, +# written prior permission. M.I.T. makes no representations about the +# suitability of this software for any purpose. It is provided "as is" +# without express or implied warranty. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + chmodcmd="" + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0
--- a/itemlist.c Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,123 +0,0 @@ -/* - itemlist.c - - $Id: itemlist.c 44 2000-05-05 07:23:15Z enz $ -*/ - -#include "itemlist.h" -#include <ctype.h> -#include <string.h> -#include <stdlib.h> -#include "common.h" -#include "log.h" - -struct ItemList -{ - char *list; - char *separators; - char *next; - size_t count; -}; - -/* Make a new item list. */ -ItemList * -new_Itl( const char *list, const char *separators ) -{ - ItemList * res; - char *p; - Bool inItem; - - res = (ItemList *) malloc( sizeof( ItemList ) ); - if ( res == NULL ) - { - Log_err( "Malloc of ItemList failed." ); - exit( EXIT_FAILURE ); - } - - res->list = (char *) malloc ( strlen(list) + 2 ); - if ( res->list == NULL ) - { - Log_err( "Malloc of ItemList.list failed." ); - exit( EXIT_FAILURE ); - } - strcpy( res->list, list ); - - if ( ( res->separators = strdup( separators ) ) == NULL ) - { - Log_err( "Malloc of ItemList.separators failed." ); - exit( EXIT_FAILURE ); - } - - res->count = 0; - res->next = res->list; - - /* Separate items into strings and have final zero-length string. */ - for( p = res->list, inItem = FALSE; *p != '\0'; p++ ) - { - Bool isSep = ( strchr( separators, p[ 0 ] ) != NULL ); - - if ( inItem ) - { - if ( isSep ) - { - p[ 0 ] = '\0'; - inItem = FALSE; - res->count++; - } - } - else - { - if ( ! isSep ) - inItem = TRUE; - } - } - if ( inItem ) - res->count++; - p[ 1 ] = '\0'; - return res; -} - -/* Delete an item list. */ -void -del_Itl( ItemList *self ) -{ - if ( self == NULL ) - return; - free( self->list ); - free( self->separators ); - free( self ); -} - -/* Get first item. */ -const char * -Itl_first( ItemList *self) -{ - self->next = self->list; - return Itl_next( self ); -} - -/* Get next item or NULL. */ -const char * -Itl_next( ItemList *self ) -{ - const char *res = self->next; - - if ( res[ 0 ] == '\0' ) - return NULL; - - while ( strchr( self->separators, res[ 0 ] ) != NULL ) - res++; - - if ( res[ 0 ] == '\0' && res[ 1 ] == '\0' ) - return NULL; - - self->next += strlen( res ) + 1; - return res; -} - -/* Get count of items in list. */ -size_t -Itl_count( const ItemList *self ) -{ - return self->count; -}
--- a/itemlist.h Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,38 +0,0 @@ -/* - itemlist.h - - Copy a string wiht a list of separated items (as found in several - header lines) and provide a convenient way of accessing the - individual items. - - $Id: itemlist.h 32 2000-04-29 14:45:56Z enz $ */ - -#ifndef ITEMLIST_H -#define ITEMLIST_H - -#include <sys/types.h> - -struct ItemList; -typedef struct ItemList ItemList; - -/* Make a new item list. */ -ItemList * -new_Itl( const char *list, const char *separators ); - -/* Delete an item list. */ -void -del_Itl( ItemList *self ); - -/* Get first item. */ -const char * -Itl_first( ItemList *self); - -/* Get next item or NULL. */ -const char * -Itl_next( ItemList *self ); - -/* Get count of items in list. */ -size_t -Itl_count( const ItemList *self ); - -#endif
--- a/lock.c Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,126 +0,0 @@ -/* - lock.c - - $Id: lock.c 3 2000-01-04 11:35:42Z enz $ -*/ - -#include "lock.h" -#include <errno.h> -#include <ctype.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <time.h> -#include <unistd.h> -#include "config.h" -#include "log.h" -#include "database.h" -#include "group.h" -#include "request.h" - -struct Lock -{ - int lockFd; - Str lockFile; -} lock = { -1, "" }; - - -#ifdef DEBUG -static Bool -testLock( void ) -{ - return ( lock.lockFd != -1 ); -} -#endif - -static Bool -waitLock( void ) -{ - int fd; - struct flock l; - - ASSERT( ! testLock() ); - Log_dbg( "Waiting for lock ..." ); - snprintf( lock.lockFile, MAXCHAR, "%s/lock/global", Cfg_spoolDir() ); - if ( ( fd = open( lock.lockFile, O_WRONLY | O_CREAT, 0644 ) ) < 0 ) - { - Log_err( "Cannot open %s (%s)", lock.lockFile, strerror( errno ) ); - return FALSE; - } - l.l_type = F_WRLCK; - l.l_start = 0; - l.l_whence = SEEK_SET; - l.l_len = 0; - if ( fcntl( fd, F_SETLKW, &l ) < 0 ) - { - Log_err( "Cannot lock %s: %s", lock.lockFile, strerror( errno ) ); - return FALSE; - } - lock.lockFd = fd; - Log_dbg( "Lock successful" ); - return TRUE; -} - -static void -releaseLock( void ) -{ - struct flock l; - - ASSERT( testLock() ); - l.l_type = F_UNLCK; - l.l_start = 0; - l.l_whence = SEEK_SET; - l.l_len = 0; - if ( fcntl( lock.lockFd, F_SETLK, &l ) < 0 ) - Log_err( "Cannot release %s: %s", lock.lockFile, - strerror( errno ) ); - close( lock.lockFd ); - lock.lockFd = -1; - Log_dbg( "Releasing lock" ); -} - - -/* Open all databases and set global lock. */ -Bool -Lock_openDatabases( void ) -{ - if ( ! waitLock() ) - { - Log_err( "Could not get write lock" ); - return FALSE; - } - if ( ! Db_open() ) - { - Log_err( "Could not open database" ); - releaseLock(); - return FALSE; - } - if ( ! Grp_open() ) - { - Log_err( "Could not open groupinfo" ); - Db_close(); - releaseLock(); - return FALSE; - } - if ( ! Req_open() ) - { - Log_err( "Could not initialize request database" ); - Grp_close(); - Db_close(); - releaseLock(); - return FALSE; - } - - return TRUE; -} - - -/* Close all databases and release global lock. */ -void -Lock_closeDatabases( void ) -{ - Grp_close(); - Db_close(); - Req_close(); - releaseLock(); -}
--- a/lock.h Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,24 +0,0 @@ -/* - lock.h - - Opening/Closing of the various databases: article overview database, - articla database, groups database, outgoing articles database, requests - database. Handles global lock. - - $Id: lock.h 3 2000-01-04 11:35:42Z enz $ -*/ - -#ifndef LOCK_H -#define LOCK_H - -#include "common.h" - -/* Open all databases and set global lock. */ -Bool -Lock_openDatabases( void ); - -/* Close all databases and release global lock. */ -void -Lock_closeDatabases( void ); - -#endif
--- a/log.c Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,75 +0,0 @@ -/* - log.c - - $Id: log.c 3 2000-01-04 11:35:42Z enz $ -*/ - -#include <syslog.h> -#include <stdarg.h> -#include "common.h" - -#define MAXLENGTH 240 - -struct -{ - Bool interactive; -} log = { FALSE }; - -void -Log_init( Str name, Bool interactive, int facility ) -{ - int option = LOG_PID | LOG_CONS; - - log.interactive = interactive; - openlog( name, option, facility ); -} - -#define DO_LOG( LEVEL ) \ - va_list ap; \ - Str t; \ - \ - va_start( ap, fmt ); \ - vsnprintf( t, MAXCHAR, fmt, ap ); \ - if ( MAXLENGTH < MAXCHAR ) \ - t[ MAXLENGTH ] = '\0'; \ - syslog( LEVEL, "%s", t ); \ - if ( log.interactive ) \ - fprintf( stderr, "%s\n", t ); \ - va_end( ap ); - -void -Log_inf( const char *fmt, ... ) -{ - DO_LOG( LOG_INFO ); -} - -void -Log_err( const char *fmt, ... ) -{ - DO_LOG( LOG_ERR ); -} - -/* Ensure the condition "cond" is true; otherwise log an error and return 1 */ -int -Log_check(int cond, const char *fmt, ... ) -{ - if (!cond) { - DO_LOG( LOG_ERR ); - return 1; - } - return 0; -} - -void -Log_ntc( const char *fmt, ... ) -{ - DO_LOG( LOG_NOTICE ); -} - -void -Log_dbg( const char *fmt, ... ) -{ -#ifdef DEBUG - DO_LOG( LOG_DEBUG ); -#endif -}
--- a/log.h Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -/* - log.h - - Print log messages to syslog, stdout/stderr. - - $Id: log.h 3 2000-01-04 11:35:42Z enz $ -*/ - -#ifndef LOG_H -#define LOG_H - -#include "common.h" - -/* - Initialise logging (required before using any log functions). - name: program name for syslog - interactive: print messages also to stderr/stdout - facility: like syslog -*/ -void -Log_init( Str name, Bool interactive, int facility ); - -/* Log level info */ -void -Log_inf( const char *fmt, ... ); - -/* Log level error */ -void -Log_err( const char *fmt, ... ); - -/* Check for cond being true. Otherwise log an error, and return 1. */ -int -Log_check(int cond, const char *fmt, ... ); - -/* Log level notice */ -void -Log_ntc( const char *fmt, ... ); - -/* Log only if DEBUG is defined. */ -void -Log_dbg( const char *fmt, ... ); - -#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/missing Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,190 @@ +#! /bin/sh +# Common stub for a few missing GNU programs while installing. +# Copyright (C) 1996, 1997 Free Software Foundation, Inc. +# Franc,ois Pinard <pinard@iro.umontreal.ca>, 1996. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +if test $# -eq 0; then + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 +fi + +case "$1" in + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an +error status if there is no known handling for PROGRAM. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + +Supported PROGRAM values: + aclocal touch file \`aclocal.m4' + autoconf touch file \`configure' + autoheader touch file \`config.h.in' + automake touch all \`Makefile.in' files + bison create \`y.tab.[ch]', if possible, from existing .[ch] + flex create \`lex.yy.c', if possible, from existing .c + lex create \`lex.yy.c', if possible, from existing .c + makeinfo touch the output file + yacc create \`y.tab.[ch]', if possible, from existing .[ch]" + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing - GNU libit 0.0" + ;; + + -*) + echo 1>&2 "$0: Unknown \`$1' option" + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 + ;; + + aclocal) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`acinclude.m4' or \`configure.in'. You might want + to install the \`Automake' and \`Perl' packages. Grab them from + any GNU archive site." + touch aclocal.m4 + ;; + + autoconf) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`configure.in'. You might want to install the + \`Autoconf' and \`GNU m4' packages. Grab them from any GNU + archive site." + touch configure + ;; + + autoheader) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`acconfig.h' or \`configure.in'. You might want + to install the \`Autoconf' and \`GNU m4' packages. Grab them + from any GNU archive site." + files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' configure.in` + test -z "$files" && files="config.h" + touch_files= + for f in $files; do + case "$f" in + *:*) touch_files="$touch_files "`echo "$f" | + sed -e 's/^[^:]*://' -e 's/:.*//'`;; + *) touch_files="$touch_files $f.in";; + esac + done + touch $touch_files + ;; + + automake) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`Makefile.am', \`acinclude.m4' or \`configure.in'. + You might want to install the \`Automake' and \`Perl' packages. + Grab them from any GNU archive site." + find . -type f -name Makefile.am -print | + sed 's/\.am$/.in/' | + while read f; do touch "$f"; done + ;; + + bison|yacc) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.y' file. You may need the \`Bison' package + in order for those modifications to take effect. You can get + \`Bison' from any GNU archive site." + rm -f y.tab.c y.tab.h + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.y) + SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.c + fi + SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.h + fi + ;; + esac + fi + if [ ! -f y.tab.h ]; then + echo >y.tab.h + fi + if [ ! -f y.tab.c ]; then + echo 'main() { return 0; }' >y.tab.c + fi + ;; + + lex|flex) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.l' file. You may need the \`Flex' package + in order for those modifications to take effect. You can get + \`Flex' from any GNU archive site." + rm -f lex.yy.c + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.l) + SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" lex.yy.c + fi + ;; + esac + fi + if [ ! -f lex.yy.c ]; then + echo 'main() { return 0; }' >lex.yy.c + fi + ;; + + makeinfo) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.texi' or \`.texinfo' file, or any other file + indirectly affecting the aspect of the manual. The spurious + call might also be the consequence of using a buggy \`make' (AIX, + DU, IRIX). You might want to install the \`Texinfo' package or + the \`GNU make' package. Grab either from any GNU archive site." + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + file=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` + file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $file` + fi + touch $file + ;; + + *) + echo 1>&2 "\ +WARNING: \`$1' is needed, and you do not seem to have it handy on your + system. You might have modified some files without having the + proper tools for further handling them. Check the \`README' file, + it often tells you about the needed prerequirements for installing + this package. You may also peek at any GNU archive site, in case + some other package would contain this missing \`$1' program." + exit 1 + ;; +esac + +exit 0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mkinstalldirs Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,40 @@ +#! /bin/sh +# mkinstalldirs --- make directory hierarchy +# Author: Noah Friedman <friedman@prep.ai.mit.edu> +# Created: 1993-05-16 +# Public domain + +# $Id: mkinstalldirs 49 2000-05-05 21:45:56Z uh1763 $ + +errstatus=0 + +for file +do + set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` + shift + + pathcomp= + for d + do + pathcomp="$pathcomp$d" + case "$pathcomp" in + -* ) pathcomp=./$pathcomp ;; + esac + + if test ! -d "$pathcomp"; then + echo "mkdir $pathcomp" + + mkdir "$pathcomp" || lasterr=$? + + if test ! -d "$pathcomp"; then + errstatus=$lasterr + fi + fi + + pathcomp="$pathcomp/" + done +done + +exit $errstatus + +# mkinstalldirs ends here
--- a/noffle.c Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,798 +0,0 @@ -/* - noffle.c - - Main program. Implements specified actions, but running as server, which - is done by Serv_run(), declared in server.h. - - Locking policy: lock access to databases while noffle is running, but - not as server. If noffle runs as server, locking is performed while - executing NNTP commands, but temporarily released if no new command is - received for some seconds (to allow multiple clients connect at the same - time). - - $Id: noffle.c 44 2000-05-05 07:23:15Z enz $ -*/ - -#include <ctype.h> -#include <errno.h> -#include <getopt.h> -#include <signal.h> -#include <sys/resource.h> -#include <syslog.h> -#include <unistd.h> -#include "client.h" -#include "common.h" -#include "content.h" -#include "control.h" -#include "config.h" -#include "database.h" -#include "fetch.h" -#include "fetchlist.h" -#include "group.h" -#include "itemlist.h" -#include "log.h" -#include "online.h" -#include "outgoing.h" -#include "over.h" -#include "pseudo.h" -#include "util.h" -#include "server.h" -#include "request.h" -#include "lock.h" - -struct Noffle -{ - Bool queryGrps; - Bool queryDsc; - Bool queryTimes; - Bool interactive; -} noffle = { FALSE, FALSE, FALSE, TRUE }; - -static void -doArt( const char *msgId ) -{ - const char *id; - - if ( strcmp( msgId, "all" ) == 0 ) - { - if ( ! Db_first( &id ) ) - fprintf( stderr, "Database empty.\n" ); - else - do - { - printf( "From %s %s\n" - "%s\n" - "%s\n", - Db_from( id ), Db_date( id ), - Db_header( id ), - Db_body( id ) ); - } - while ( Db_next( &id ) ); - } - else - { - if ( ! Db_contains( msgId ) ) - fprintf( stderr, "Not in database.\n" ); - else - printf( "%s\n%s", Db_header( msgId ), Db_body( msgId ) ); - } -} - -static void -doCancel( const char *msgId ) -{ - switch( Ctrl_cancel( msgId ) ) - { - case CANCEL_NO_SUCH_MSG: - printf( "No such message '%s'.\n", msgId ); - break; - - case CANCEL_OK: - printf( "Message '%s' cancelled.\n", msgId ); - break; - - case CANCEL_NEEDS_MSG: - printf( "Message '%s' cancelled in local database only.\n", msgId ); - break; - } -} - -/* List articles requested from one particular server */ -static void -listRequested1( const char* serv ) -{ - Str msgid; - - if ( ! Req_first( serv, msgid ) ) - return; - do - printf( "%s %s\n", msgid, serv ); - while ( Req_next( msgid ) ); -} - -/* List requested articles. List for all servers if serv = "all" or serv = - NULL. */ -void -doRequested( const char *arg ) -{ - Str serv; - - if ( ! arg || ! strcmp( arg, "all" ) ) - { - Cfg_beginServEnum(); - while ( Cfg_nextServ( serv ) ) - listRequested1( serv ); - } - else - listRequested1( arg ); -} - - -static void -doDb( void ) -{ - const char *msgId; - - if ( ! Db_first( &msgId ) ) - fprintf( stderr, "Database empty.\n" ); - else - do - printf( "%s\n", msgId ); - while ( Db_next( &msgId ) ); -} - -static void -doFetch( void ) -{ - Str serv; - - Cfg_beginServEnum(); - while ( Cfg_nextServ( serv ) ) - if ( Fetch_init( serv ) ) - { - Fetch_postArts(); - - Fetch_getNewGrps(); - - /* Get overviews of new articles and store IDs of new articles - that are to be fetched becase of FULL or THREAD mode in the - request database. */ - Fetch_updateGrps(); - - /* get requested articles */ - Fetch_getReq_(); - - Fetch_close(); - } -} - -static void -doQuery( void ) -{ - Str serv; - - Cfg_beginServEnum(); - while ( Cfg_nextServ( serv ) ) - if ( Fetch_init( serv ) ) - { - if ( noffle.queryGrps ) - Client_getGrps(); - if ( noffle.queryDsc ) - Client_getDsc(); - if ( noffle.queryTimes ) - Client_getCreationTimes(); - Fetch_close(); - } -} - -/* Expire all overviews not in database */ -static void -expireContents( void ) -{ - const Over *ov; - int i; - int cntDel, cntLeft; - Str grp; - Bool autoUnsubscribe; - int autoUnsubscribeDays; - time_t now = time( NULL ), maxAge = 0; - const char *msgId; - - autoUnsubscribe = Cfg_autoUnsubscribe(); - autoUnsubscribeDays = Cfg_autoUnsubscribeDays(); - maxAge = Cfg_autoUnsubscribeDays() * 24 * 3600; - if ( ! Cont_firstGrp( grp ) ) - return; - Log_inf( "Expiring overviews not in database" ); - do - { - if ( ! Grp_exists( grp ) ) - Log_err( "Overview file for unknown group %s exists", grp ); - else - { - cntDel = cntLeft = 0; - Cont_read( grp ); - for ( i = Cont_first(); i <= Cont_last(); ++i ) - if ( ( ov = Cont_get( i ) ) ) - { - msgId = Ov_msgId( ov ); - if ( ! Db_contains( msgId ) ) - { - Cont_delete( i ); - ++cntDel; - } - else - ++cntLeft; - } - if ( ! Grp_local( grp ) - && autoUnsubscribe - && difftime( now, Grp_lastAccess( grp ) ) > maxAge ) - { - Log_ntc( "Auto-unsubscribing from %s after %d " - "days without access", - grp, autoUnsubscribeDays ); - Pseudo_autoUnsubscribed( grp, autoUnsubscribeDays ); - Fetchlist_read(); - Fetchlist_remove( grp ); - Fetchlist_write(); - } - Cont_write(); - Grp_setFirstLast( grp, Cont_first(), Cont_last() ); - Log_inf( "%ld overviews deleted from group %s, %ld left (%ld-%ld)", - cntDel, grp, cntLeft, Grp_first( grp ), Grp_last( grp ) ); - } - } - while ( Cont_nextGrp( grp ) ); -} - -static void -doExpire( void ) -{ - Db_close(); - Db_expire(); - if ( ! Db_open() ) - return; - expireContents(); -} - -static void -doCreateLocalGroup( const char * name ) -{ - Str grp; - - Utl_cpyStr( grp, name ); - Utl_toLower( grp ); - name = Utl_stripWhiteSpace( grp ); - - if ( Grp_exists( name ) ) - fprintf( stderr, "'%s' already exists.\n", name ); - else - { - Log_inf( "Creating new local group '%s'", name ); - Grp_create( name ); - Grp_setLocal( name ); - printf( "New local group '%s' created.\n", name ); - } -} - -static void -doDeleteLocalGroup( const char * name ) -{ - Str grp; - - Utl_cpyStr( grp, name ); - Utl_toLower( grp ); - name = Utl_stripWhiteSpace( grp ); - - if ( ! Grp_exists( name ) ) - fprintf( stderr, "'%s' does not exist.\n", name ); - else - { - int i; - - Log_inf( "Deleting group '%s'", name ); - - /* - Delete all articles that are only in the group. Check the - article Xref for more than one group. - */ - Cont_read( name ); - for ( i = Cont_first(); i <= Cont_last(); i++ ) - { - const Over *over; - Bool toDelete; - Str msgId; - - over = Cont_get( i ); - toDelete = TRUE; - if ( over != NULL ) - { - ItemList * xref; - - Utl_cpyStr( msgId, Ov_msgId( over ) ); - xref = new_Itl( Db_xref( msgId ), " " ); - if ( Itl_count( xref ) > 1 ) - toDelete = FALSE; - del_Itl( xref ); - } - Cont_delete( i ); - if ( toDelete ) - Db_delete( msgId ); - } - Cont_write(); - Grp_delete( name ); - printf( "Group '%s' deleted.\n", name ); - } -} - -static void -doList( void ) -{ - FetchMode mode; - int i, size; - const char *name, *modeStr = ""; - - Fetchlist_read(); - size = Fetchlist_size(); - if ( size == 0 ) - fprintf( stderr, "Fetch list is empty.\n" ); - else - for ( i = 0; i < size; ++i ) - { - Fetchlist_element( &name, &mode, i ); - switch ( mode ) - { - case FULL: - modeStr = "full"; break; - case THREAD: - modeStr = "thread"; break; - case OVER: - modeStr = "over"; break; - } - printf( "%s %s %s\n", name, Grp_serv( name ), modeStr ); - } -} - -/* A modify command. argc/argv start AFTER '-m'. */ -static Bool -doModify( const char *cmd, int argc, char **argv ) -{ - const char *grp; - - if ( argc < 2 ) - { - fprintf( stderr, "Insufficient arguments to -m\n" ); - return FALSE; - - } - else if ( strcmp( cmd, "desc" ) != 0 - && strcmp( cmd, "post" ) != 0 ) - { - fprintf( stderr, "Unknown argument -m %s\n", optarg ); - return FALSE; - } - - grp = argv[ 0 ]; - argv++; - argc--; - - if ( strcmp( cmd, "desc" ) == 0 ) - { - Str desc; - - Utl_cpyStr( desc, *( argv++ ) ); - while ( --argc > 0 ) - { - Utl_catStr( desc, " " ); - Utl_catStr( desc, *( argv++ ) ); - } - - Grp_setDsc( grp, desc ); - } - else - { - char c; - - if ( ! Grp_local( grp ) ) - { - fprintf( stderr, "%s is not a local group\n", grp ); - return FALSE; - } - - c = **argv; - if ( c == 'y' || c == 'm' || c == 'n' ) - Grp_setPostAllow( grp, c ); - else - { - fprintf( stderr, "Access must be 'y', 'n' or 'm'" ); - return FALSE; - } - } - - return TRUE; -} - -static void -doGrps( void ) -{ - const char *g; - Str dateLastAccess, dateCreated; - time_t lastAccess, created; - - if ( Grp_firstGrp( &g ) ) - do - { - lastAccess = Grp_lastAccess( g ); - created = Grp_created( g ); - ASSERT( lastAccess >= 0 ); - ASSERT( created >= 0 ); - strftime( dateLastAccess, MAXCHAR, "%Y-%m-%d %H:%M:%S", - localtime( &lastAccess ) ); - strftime( dateCreated, MAXCHAR, "%Y-%m-%d %H:%M:%S", - localtime( &created ) ); - printf( "%s\t%s\t%i\t%i\t%i\t%c\t%s\t%s\t%s\n", - g, Grp_serv( g ), Grp_first( g ), Grp_last( g ), - Grp_rmtNext( g ), Grp_postAllow( g ), dateCreated, - dateLastAccess, Grp_dsc( g ) ); - } - while ( Grp_nextGrp( &g ) ); -} - -static Bool -doSubscribe( const char *name, FetchMode mode ) -{ - if ( ! Grp_exists( name ) ) - { - fprintf( stderr, "%s is not available at remote servers.\n", name ); - return FALSE; - } - Fetchlist_read(); - if ( Fetchlist_add( name, mode ) ) - printf( "Adding %s to fetch list in %s mode.\n", - name, mode == FULL ? "full" : mode == THREAD ? - "thread" : "overview" ); - else - printf( "%s is already in fetch list. Mode is now: %s.\n", - name, mode == FULL ? "full" : mode == THREAD ? - "thread" : "overview" ); - if ( ! Fetchlist_write() ) - fprintf( stderr, "Could not save fetchlist.\n" ); - return TRUE; -} - -static void -doUnsubscribe( const char *name ) -{ - Fetchlist_read(); - if ( ! Fetchlist_remove( name ) ) - printf( "%s is not in fetch list.\n", name ); - else - printf( "%s removed from fetch list.\n", name ); - if ( ! Fetchlist_write() ) - fprintf( stderr, "Could not save fetchlist.\n" ); -} - -static void -printUsage( void ) -{ - static const char *msg = - "Usage: noffle <option>\n" - "Option is one of the following:\n" - " -a | --article <msg id>|all Show article(s) in database\n" - " -c | --cancel <msg id> Remove article from database\n" - " -C | --create <grp> Create a local group\n" - " -d | --database Show content of article database\n" - " -D | --delete <grp> Delete a group\n" - " -e | --expire Expire articles\n" - " -f | --fetch Get newsfeed from server/post articles\n" - " -g | --groups Show all groups available at server\n" - " -h | --help Show this text\n" - " -l | --list List groups on fetch list\n" - " -m | --modify desc <grp> <desc> Modify a group description\n" - " -m | --modify post <grp> (y|n) Modify posting status of a local group\n" - " -n | --online Switch to online mode\n" - " -o | --offline Switch to offline mode\n" - " -q | --query groups Get group list from server\n" - " -q | --query desc Get group descriptions from server\n" - " -q | --query times Get group creation times from server\n" - " -r | --server Run as server on stdin/stdout\n" - " -R | --requested List articles marked for download\n" - " -s | --subscribe-over <grp> Add group to fetch list (overview)\n" - " -S | --subscribe-full <grp> Add group to fetch list (full)\n" - " -t | --subscribe-thread <grp> Add group to fetch list (thread)\n" - " -u | --unsubscribe <grp> Remove group from fetch list\n" - " -v | --version Print version\n"; - fprintf( stderr, "%s", msg ); -} - -/* - Allow core files: Change core limit and change working directory - to spool directory, where news has write permissions. -*/ -static void -enableCorefiles() -{ - struct rlimit lim; - - if ( getrlimit( RLIMIT_CORE, &lim ) != 0 ) - { - Log_err( "Cannot get system core limit: %s", strerror( errno ) ); - return; - } - lim.rlim_cur = lim.rlim_max; - if ( setrlimit( RLIMIT_CORE, &lim ) != 0 ) - { - Log_err( "Cannot set system core limit: %s", strerror( errno ) ); - return; - } - Log_dbg( "Core limit set to %i", lim.rlim_max ); - if ( chdir( Cfg_spoolDir() ) != 0 ) - { - Log_err( "Cannot change to directory '%s'", Cfg_spoolDir() ); - return; - } - Log_dbg( "Changed to directory '%s'", Cfg_spoolDir() ); -} - -static Bool -initNoffle( Bool interactive ) -{ - Log_init( "noffle", interactive, LOG_NEWS ); - Cfg_read(); - Log_dbg( "NOFFLE version %s", Cfg_version() ); - noffle.interactive = interactive; - if ( interactive ) - if ( ! Lock_openDatabases() ) - return FALSE; - if ( ! interactive ) - enableCorefiles(); - return TRUE; -} - -static void -closeNoffle( void ) -{ - if ( noffle.interactive ) - Lock_closeDatabases(); -} - -static void -bugReport( int sig ) -{ - Log_err( "Received SIGSEGV. Please submit a bug report" ); - signal( SIGSEGV, SIG_DFL ); - raise( sig ); -} - -static void -logSignal( int sig ) -{ - const char *name; - Bool err = TRUE; - - switch ( sig ) - { - case SIGABRT: - name = "SIGABRT"; break; - case SIGFPE: - name = "SIGFPE"; break; - case SIGILL: - name = "SIGILL"; break; - case SIGINT: - name = "SIGINT"; break; - case SIGTERM: - name = "SIGTERM"; break; - case SIGPIPE: - name = "SIGPIPE"; err = FALSE; break; - default: - name = "?"; break; - } - if ( err ) - Log_err( "Received signal %i (%s). Aborting.", sig, name ); - else - Log_inf( "Received signal %i (%s). Aborting.", sig, name ); - signal( sig, SIG_DFL ); - raise( sig ); -} - -int main ( int argc, char **argv ) -{ - int c, result; - struct option longOptions[] = - { - { "article", required_argument, NULL, 'a' }, - { "cancel", required_argument, NULL, 'c' }, - { "create", required_argument, NULL, 'C' }, - { "database", no_argument, NULL, 'd' }, - { "delete", required_argument, NULL, 'D' }, - { "expire", no_argument, NULL, 'e' }, - { "fetch", no_argument, NULL, 'f' }, - { "groups", no_argument, NULL, 'g' }, - { "help", no_argument, NULL, 'h' }, - { "list", no_argument, NULL, 'l' }, - { "modify", required_argument, NULL, 'm' }, - { "offline", no_argument, NULL, 'o' }, - { "online", no_argument, NULL, 'n' }, - { "query", required_argument, NULL, 'q' }, - { "server", no_argument, NULL, 'r' }, - { "requested", no_argument, NULL, 'R' }, - { "subscribe-over", required_argument, NULL, 's' }, - { "subscribe-full", required_argument, NULL, 'S' }, - { "subscribe-thread", required_argument, NULL, 't' }, - { "unsubscribe", required_argument, NULL, 'u' }, - { "version", no_argument, NULL, 'v' }, - { NULL, 0, NULL, 0 } - }; - - signal( SIGSEGV, bugReport ); - signal( SIGABRT, logSignal ); - signal( SIGFPE, logSignal ); - signal( SIGILL, logSignal ); - signal( SIGINT, logSignal ); - signal( SIGTERM, logSignal ); - signal( SIGPIPE, logSignal ); - c = getopt_long( argc, argv, "a:c:C:dD:efghlm:onq:rRs:S:t:u:v", - longOptions, NULL ); - if ( ! initNoffle( c != 'r' ) ) - return EXIT_FAILURE; - result = EXIT_SUCCESS; - switch ( c ) - { - case 0: - /* Options that set a flag. */ - break; - case 'a': - if ( ! optarg ) - { - fprintf( stderr, "Option -a needs argument.\n" ); - result = EXIT_FAILURE; - } - else - doArt( optarg ); - break; - case 'c': - if ( ! optarg ) - { - fprintf( stderr, "Option -c needs argument.\n" ); - result = EXIT_FAILURE; - } - else - doCancel( optarg ); - break; - case 'C': - if ( ! optarg ) - { - fprintf( stderr, "Option -C needs argument.\n" ); - result = EXIT_FAILURE; - } - else - doCreateLocalGroup( optarg ); - break; - case 'd': - doDb(); - break; - case 'D': - if ( ! optarg ) - { - fprintf( stderr, "Option -D needs argument.\n" ); - result = EXIT_FAILURE; - } - else - doDeleteLocalGroup( optarg ); - break; - case 'e': - doExpire(); - break; - case 'f': - doFetch(); - break; - case 'g': - doGrps(); - break; - case -1: - case 'h': - printUsage(); - break; - case 'l': - doList(); - break; - case 'm': - if ( ! optarg ) - { - fprintf( stderr, "Option -m needs argument.\n" ); - result = EXIT_FAILURE; - } - else - if ( ! doModify( optarg, argc - optind, &argv[ optind ] ) ) - result = EXIT_FAILURE; - break; - case 'n': - if ( Online_true() ) - fprintf( stderr, "NOFFLE is already online\n" ); - else - Online_set( TRUE ); - break; - case 'o': - if ( ! Online_true() ) - fprintf( stderr, "NOFFLE is already offline\n" ); - else - Online_set( FALSE ); - break; - case 'q': - if ( ! optarg ) - { - fprintf( stderr, "Option -q needs argument.\n" ); - result = EXIT_FAILURE; - } - else - { - if ( strcmp( optarg, "groups" ) == 0 ) - noffle.queryGrps = TRUE; - else if ( strcmp( optarg, "desc" ) == 0 ) - noffle.queryDsc = TRUE; - else if ( strcmp( optarg, "times" ) == 0 ) - noffle.queryTimes = TRUE; - else - { - fprintf( stderr, "Unknown argument -q %s\n", optarg ); - result = EXIT_FAILURE; - } - doQuery(); - } - break; - case 'r': - Log_inf( "Starting as server" ); - Serv_run(); - break; - case 'R': - doRequested( optarg ); - break; - case 's': - if ( ! optarg ) - { - fprintf( stderr, "Option -s needs argument.\n" ); - result = EXIT_FAILURE; - } - else - result = doSubscribe( optarg, OVER ); - break; - case 'S': - if ( ! optarg ) - { - fprintf( stderr, "Option -S needs argument.\n" ); - result = EXIT_FAILURE; - } - else - doSubscribe( optarg, FULL ); - break; - case 't': - if ( ! optarg ) - { - fprintf( stderr, "Option -t needs argument.\n" ); - result = EXIT_FAILURE; - } - else - result = doSubscribe( optarg, THREAD ); - break; - case 'u': - if ( ! optarg ) - { - fprintf( stderr, "Option -u needs argument.\n" ); - result = EXIT_FAILURE; - } - else - doUnsubscribe( optarg ); - break; - case '?': - /* Error message already printed by getopt_long */ - result = EXIT_FAILURE; - break; - case 'v': - printf( "NNTP server NOFFLE, version %s.\n", Cfg_version() ); - break; - default: - abort(); /* Never reached */ - } - closeNoffle(); - return result; -}
--- a/online.c Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -/* - online.c - - $Id: online.c 3 2000-01-04 11:35:42Z enz $ -*/ - -#include <unistd.h> -#include "common.h" -#include "config.h" -#include "log.h" - -static void -fileOnline( Str s ) -{ - snprintf( s, MAXCHAR, "%s/lock/online", Cfg_spoolDir() ); -} - -Bool -Online_true( void ) -{ - FILE *f; - Str file; - - fileOnline( file ); - if ( ! ( f = fopen( file, "r" ) ) ) - return FALSE; - fclose( f ); - return TRUE; -} - -void -Online_set( Bool value ) -{ - FILE *f; - Str file; - - fileOnline( file ); - if ( value ) - { - if ( ! ( f = fopen( file, "a" ) ) ) - { - Log_err( "Could not create %s", file ); - return; - } - fclose( f ); - Log_inf( "NOFFLE is now online" ); - } - else - { - if ( unlink( file ) != 0 ) - { - Log_err( "Cannot remove %s", file ); - return; - } - Log_inf( "NOFFLE is now offline" ); - } -}
--- a/online.h Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ -/* - online.h - - Online/offline status. - - $Id: online.h 3 2000-01-04 11:35:42Z enz $ -*/ - -#ifndef ONLINE_H -#define ONLINE_H - -Bool -Online_true( void ); - -void -Online_set( Bool value ); - -#endif
--- a/outgoing.c Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,163 +0,0 @@ -/* - outgoing.c - - $Id: outgoing.c 32 2000-04-29 14:45:56Z enz $ -*/ - -#include "outgoing.h" - -#include <dirent.h> -#include <errno.h> -#include <fcntl.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <time.h> -#include <unistd.h> -#include "config.h" -#include "log.h" -#include "util.h" - -struct Outgoing -{ - DIR *dir; - Str serv; -} outgoing = { NULL, "" }; - -static void -fileOutgoing( Str file, const char *serv, const char *msgId ) -{ - snprintf( file, MAXCHAR, "%s/outgoing/%s/%s", - Cfg_spoolDir(), serv, msgId ); -} - -static void -createDir( const char *serv ) -{ - Str dir; - int r; - - snprintf( dir, MAXCHAR, "%s/outgoing/%s", Cfg_spoolDir(), serv ); - r = mkdir( dir, 0755 ); - if ( r != 0 ) - Log_dbg( "mkdir: %s", strerror( errno ) ); -} - -Bool -Out_add( const char *serv, const char *msgId, const DynStr *artTxt ) -{ - Str file; - FILE *f; - - fileOutgoing( file, serv, msgId ); - if ( ! ( f = fopen( file, "w" ) ) ) - { - createDir( serv ); - if ( ! ( f = fopen( file, "w" ) ) ) - { - Log_err( "Cannot open %s", file ); - return FALSE; - } - } - fprintf( f, "%s", DynStr_str( artTxt ) ); - fclose( f ); - return TRUE; -} - -Bool -Out_first( const char *serv, Str msgId, DynStr *artTxt ) -{ - Str file; - - snprintf( file, MAXCHAR, "%s/outgoing/%s", Cfg_spoolDir(), serv ); - if ( ! ( outgoing.dir = opendir( file ) ) ) - { - Log_dbg( "Cannot open %s", file ); - return FALSE; - } - Utl_cpyStr( outgoing.serv, serv ); - Out_next( NULL, NULL ); /* "." */ - Out_next( NULL, NULL ); /* ".." */ - return Out_next( msgId, artTxt ); -} - -Bool -Out_next( Str msgId, DynStr *artTxt ) -{ - struct dirent *d; - FILE *f; - Str file, line; - - ASSERT( outgoing.dir ); - if ( ! ( d = readdir( outgoing.dir ) ) ) - { - closedir( outgoing.dir ); - outgoing.dir = NULL; - return FALSE; - } - if ( artTxt == NULL ) - return ( d->d_name != NULL ); - fileOutgoing( file, outgoing.serv, d->d_name ); - if ( ! ( f = fopen( file, "r" ) ) ) - { - Log_err( "Cannot open %s for read", file ); - return FALSE; - } - DynStr_clear( artTxt ); - while ( fgets( line, MAXCHAR, f ) ) - DynStr_app( artTxt, line ); - Utl_cpyStr( msgId, d->d_name ); - fclose( f ); - return TRUE; -} - -void -Out_remove( const char *serv, const char *msgId ) -{ - Str file; - - fileOutgoing( file, serv, msgId ); - if ( unlink( file ) != 0 ) - Log_err( "Cannot remove %s", file ); -} - -Bool -Out_find( const char *msgId, Str server ) -{ - Str servdir; - DIR *d; - struct dirent *entry; - Bool res; - - - snprintf( servdir, MAXCHAR, "%s/outgoing", Cfg_spoolDir() ); - if ( ! ( d = opendir( servdir ) ) ) - { - Log_dbg( "Cannot open %s", servdir ); - return FALSE; - } - - readdir( d ); /* '.' */ - readdir( d ); /* '..' */ - - res = FALSE; - while ( ! res && ( entry = readdir( d ) ) != NULL ) - { - Str file; - struct stat s; - - fileOutgoing( file, entry->d_name, msgId ); - if ( stat( file, &s ) == 0 ) - { - res = TRUE; - Utl_cpyStr( server, entry->d_name ); - } - } - - closedir( d ); - return res; -} - - - - -
--- a/outgoing.h Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -/* - outgoing.h - - Collection of posted articles. - - $Id: outgoing.h 32 2000-04-29 14:45:56Z enz $ -*/ - -#ifndef OUT_H -#define OUT_H - -#include "common.h" -#include "dynamicstring.h" - -Bool -Out_add( const char *serv, const char *msgId, const DynStr *artTxt ); - -/* Start enumeration. Return TRUE on success. */ -Bool -Out_first( const char *serv, Str msgId, DynStr *artTxt ); - -/* Continue enumeration. Return TRUE on success. */ -Bool -Out_next( Str msgId, DynStr *s ); - -/* Delete article from outgoing collection */ -void -Out_remove( const char *serv, const char *msgId ); - -/* Find server for outgoing message. */ -Bool -Out_find( const char *msgId, Str server ); - -#endif
--- a/over.c Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,191 +0,0 @@ -/* - over.c - - $Id: over.c 3 2000-01-04 11:35:42Z enz $ -*/ - -#include <errno.h> -#include <time.h> -#include "config.h" -#include "content.h" -#include "database.h" -#include "fetchlist.h" -#include "log.h" -#include "util.h" -#include "protocol.h" -#include "pseudo.h" - -struct Over -{ - int numb; /* message number of the overviewed article */ - char *subj; - char *from; - char *date; - char *msgId; - char *ref; - size_t bytes; - size_t lines; - time_t time; -}; - -Over * -new_Over( const char *subj, const char *from, - const char *date, const char *msgId, char *ref, - size_t bytes, size_t lines ) -{ - Over *ov; - - if ( ! ( ov = (Over *)malloc( sizeof( Over ) ) ) ) - { - Log_err( "Cannot allocate Over" ); - exit( EXIT_FAILURE ); - } - ov->numb = 0; - Utl_allocAndCpy( &ov->subj, subj ); - Utl_allocAndCpy( &ov->from, from ); - Utl_allocAndCpy( &ov->date, date ); - Utl_allocAndCpy( &ov->msgId, msgId ); - Utl_allocAndCpy( &ov->ref, ref ); - ov->bytes = bytes; - ov->lines = lines; - return ov; -} - -void -del_Over( Over *self ) -{ - if ( ! self ) - return; - free( self->subj ); - self->subj = NULL; - free( self->from ); - self->from = NULL; - free( self->date ); - self->date = NULL; - free( self->msgId ); - self->msgId = NULL; - free( self->ref ); - self->ref = NULL; - free( self ); -} - -int -Ov_numb( const Over *self ) -{ - return self->numb; -} - -const char * -Ov_subj( const Over *self ) -{ - return self->subj; -} - -const char * -Ov_from( const Over *self ) -{ - return self->from; -} - -const char * -Ov_date( const Over *self ) -{ - return self->date; -} - -const char * -Ov_msgId( const Over *self ) -{ - return self->msgId; -} - -const char * -Ov_ref( const Over *self ) -{ - return self->ref; -} - -size_t -Ov_bytes( const Over *self ) -{ - return self->bytes; -} - -size_t -Ov_lines( const Over *self ) -{ - return self->lines; -} - -void -Ov_setNumb( Over *self, int numb ) -{ - self->numb = numb; -} - -Bool -Ov_write( const Over *self, FILE *f ) -{ - return ( fprintf( f, "%i\t%s\t%s\t%s\t%s\t%s\t%d\t%d\n", - self->numb, self->subj, - self->from, self->date, self->msgId, - self->ref, self->bytes, - self->lines ) > 0 ); -} - -static const char * -readField( Str result, const char *p ) -{ - size_t len; - char *r; - - if ( ! p ) - return NULL; - r = result; - *r = '\0'; - len = 0; - while ( *p != '\t' && *p != '\n' ) - { - if ( ! *p ) - return p; - *(r++) = *(p++); - ++len; - if ( len >= MAXCHAR - 1 ) - { - *r = '\0'; - Log_err( "Field in overview too long: %s", r ); - return ++p; - } - } - *r = '\0'; - return ++p; -} - -/* read Over-struct from line */ -Over * -Ov_read( char *line ) -{ - size_t bytes, lines; - const char *p; - Over *result; - int numb; - Str t, subj, from, date, msgId, ref; - - p = readField( t, line ); - if ( sscanf( t, "%i", &numb ) != 1 ) - return NULL; - p = readField( subj, p ); - p = readField( from, p ); - p = readField( date, p ); - p = readField( msgId, p ); - p = readField( ref, p ); - p = readField( t, p ); - if ( sscanf( t, "%d", &bytes ) != 1 ) - return NULL; - p = readField( t, p ); - if ( sscanf( t, "%d", &lines ) != 1 ) - return NULL; - result = new_Over( subj, from, date, msgId, ref, bytes, lines ); - Ov_setNumb( result, numb ); - return result; -}
--- a/over.h Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,70 +0,0 @@ -/* - over.h - - Processing of single article overviews. Handling of overview files is in - content.c. An article overview contains important article properties, - such as date, from, subject. - - $Id: over.h 3 2000-01-04 11:35:42Z enz $ -*/ - -#ifndef OVER_H -#define OVER_H - -#include <time.h> -#include "common.h" - -struct Over; -typedef struct Over Over; - -/* - Usual fields from overview databases. - Xref without hostname. -*/ -Over * -new_Over( const char *subj, const char *from, const char *date, - const char *msgId, char *ref, size_t bytes, size_t lines ); - - -/* free memory */ -void -del_Over( Over *self ); - -/* read Over-struct from line */ -Over * -Ov_read( char *line ); - -/* write struct Over to f as a line */ -Bool -Ov_write( const Over *self, FILE *f ); - -/* Access particular fields in struct over */ - -int -Ov_numb( const Over *self ); - -const char * -Ov_subj( const Over *self ); - -const char * -Ov_from( const Over *self ); - -const char * -Ov_date( const Over *self ); - -const char * -Ov_msgId( const Over *self ); - -const char * -Ov_ref( const Over *self ); - -size_t -Ov_bytes( const Over *self ); - -size_t -Ov_lines( const Over *self ); - -void -Ov_setNumb( Over *self, int numb ); - -#endif
--- a/post.c Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,150 +0,0 @@ -/* - post.c - - $Id: post.c 32 2000-04-29 14:45:56Z enz $ -*/ - -#include "post.h" -#include <string.h> -#include "common.h" -#include "content.h" -#include "database.h" -#include "group.h" -#include "log.h" -#include "over.h" -#include "protocol.h" -#include "util.h" - -struct OverInfo -{ - Str subject; - Str from; - Str date; - Str msgId; - Str ref; - size_t bytes; - size_t lines; -}; - -struct Article -{ - const char * text; - Bool posted; - struct OverInfo over; -}; - -static struct Article article = { NULL, FALSE }; - -static void -getOverInfo( struct OverInfo * o ) -{ - const char *p = article.text; - Str line, field, value; - - o->bytes = strlen( p ); - - while( p != NULL ) - { - p = Utl_getHeaderLn( line, p ); - if ( line[ 0 ] == '\0' ) - break; - - /* Look for headers we need to stash. */ - if ( Prt_getField( field, value, line ) ) - { - if ( strcmp( field, "subject" ) == 0 ) - Utl_cpyStr( o->subject, value ); - else if ( strcmp ( field, "from" ) == 0 ) - Utl_cpyStr( o->from, value ); - else if ( strcmp ( field, "date" ) == 0 ) - Utl_cpyStr( o->date, value ); - else if ( strcmp ( field, "references" ) == 0 ) - Utl_cpyStr( o->ref, value ); - else if ( strcmp ( field, "message-id" ) == 0 ) - Utl_cpyStr( o->msgId, value ); - } - } - - /* Move to start of body and count lines. */ - for ( p++, o->lines = 0; *p != '\0'; p++ ) - if ( *p == '\n' ) - o->lines++; -} - -/* Register an article for posting. */ -Bool -Post_open( const char * text ) -{ - if ( article.text != NULL ) - { - Log_err( "Busy article in Post_open." ); - return FALSE; - } - - memset( &article.over, 0, sizeof( article.over ) ); - article.text = text; - getOverInfo( &article.over ); - - if ( Db_contains( article.over.msgId ) ) - { - Log_err( "Duplicate article %s.", article.over.msgId ); - return FALSE; - } - - return TRUE; -} - - -/* Add the article to a group. */ -Bool -Post_add ( const char * grp ) -{ - Over * over; - const char *msgId; - - over = new_Over( article.over.subject, - article.over.from, - article.over.date, - article.over.msgId, - article.over.ref, - article.over.bytes, - article.over.lines ); - - msgId = article.over.msgId; - - Cont_read( grp ); - Cont_app( over ); - Log_dbg( "Added message '%s' to group '%s'.", msgId, grp ); - - if ( !article.posted ) - { - Log_inf( "Added '%s' to database.", msgId ); - if ( ! Db_prepareEntry( over, Cont_grp(), Cont_last() ) - || ! Db_storeArt ( msgId, article.text ) ) - return FALSE; - article.posted = TRUE; - } - else - { - Str t; - const char *xref; - - xref = Db_xref( msgId ); - Log_dbg( "Adding '%s' to Xref of '%s'", grp, msgId ); - snprintf( t, MAXCHAR, "%s %s:%i", xref, grp, Ov_numb( over ) ); - Db_setXref( msgId, t ); - } - - Cont_write(); - Grp_setFirstLast( Cont_grp(), Cont_first(), Cont_last() ); - return TRUE; -} - -/* Done with article - tidy up. */ -void -Post_close( void ) -{ - article.text = NULL; - article.posted = FALSE; -} -
--- a/post.h Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,28 +0,0 @@ -/* - post.h - - Take a single article received in its entirety without an overview - (i.e. received via at the server via a POST), and add it to the database - and (possibly multiple) group(s). - - $Id: post.h 32 2000-04-29 14:45:56Z enz $ -*/ - -#ifndef POST_H -#define POST_H - -#include "common.h" - -/* Register an article for posting. */ -Bool -Post_open( const char * text ); - -/* Add the article to a group. */ -Bool -Post_add ( const char * grp ); - -/* Done with article - tidy up. */ -void -Post_close( void ); - -#endif
--- a/protocol.c Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,279 +0,0 @@ -/* - protocol.c - - $Id: protocol.c 16 2000-04-11 06:50:16Z enz $ -*/ - -#include <ctype.h> -#include <netdb.h> -#include <sys/types.h> -#include <sys/utsname.h> -#include "common.h" -#include "dynamicstring.h" -#include "log.h" -#include "over.h" -#include "util.h" - -Bool -Prt_getLn( Str line, FILE *f ) -{ - size_t len; - - /* - We also accept lines ending with "\n" instead of "\r\n", some - clients wrongly send such lines. - */ - if ( ! fgets( line, MAXCHAR, f ) ) - { - Log_dbg( "Prt_getLine failed" ); - return FALSE; - } - len = strlen( line ); - if ( line[ len - 1 ] == '\n' ) - { - line[ len - 1 ] = '\0'; - if ( line[ len - 2 ] == '\r' ) - line[ len - 2 ] = '\0'; - } - Log_dbg( "[R] %s", line ); - return TRUE; -} - -Bool -Prt_getTxtLn( Str line, Bool *err, FILE *f ) -{ - Str buf; - - if ( ! Prt_getLn( buf, f ) ) - { - Log_err( "Cannot get text line" ); - *err = TRUE; - return FALSE; - } - *err = FALSE; - if ( buf[ 0 ] == '.' ) - { - if ( buf[ 1 ] == 0 ) - return FALSE; - else - strcpy( line, buf + 1 ); - } - else - strcpy( line, buf ); - return TRUE; -} - -Bool -Prt_putTxtLn( const char* line, FILE *f ) -{ - if ( line[ 0 ] == '.' ) - { - Log_dbg( "[S] .%s", line ); - return ( fprintf( f, ".%s\r\n", line ) == strlen( line ) + 3 ); - } - else - { - Log_dbg( "[S] %s", line ); - return ( fprintf( f, "%s\r\n", line ) == strlen( line ) + 2 ); - } -} - -Bool -Prt_putEndOfTxt( FILE *f ) -{ - Log_dbg( "[S] ." ); - return ( fprintf( f, ".\r\n" ) == 3 ); -} - -/* - Write text buffer of lines each ending with '\n'. - Replace '\n' by "\r\n". -*/ -Bool -Prt_putTxtBuf( const char *buf, FILE *f ) -{ - Str line; - const char *pBuf; - char *pLn; - - pBuf = buf; - pLn = line; - while ( *pBuf != '\0' ) - { - if ( *pBuf == '\n' ) - { - *pLn = '\0'; - if ( ! Prt_putTxtLn( line, f ) ) - return FALSE; - pLn = line; - ++pBuf; - } - else if ( pLn - line >= MAXCHAR - 1 ) - { - /* Put it out raw to prevent String overflow */ - Log_err( "Writing VERY long line" ); - *pLn = '\0'; - if ( fprintf( f, "%s", line ) != strlen( line ) ) - return FALSE; - pLn = line; - } - else - *(pLn++) = *(pBuf++); - } - return TRUE; -} - -Bool -Prt_getField( Str resultField, Str resultValue, const char* line ) -{ - char *dst; - const char *p; - Str lineLower, t; - - Utl_cpyStr( lineLower, line ); - Utl_toLower( lineLower ); - p = Utl_stripWhiteSpace( lineLower ); - dst = resultField; - while ( ! isspace( *p ) && *p != ':' && *p != '\0' ) - *(dst++) = *(p++); - *dst = '\0'; - while ( isspace( *p ) ) - ++p; - if ( *p == ':' ) - { - ++p; - strcpy( t, line + ( p - lineLower ) ); - p = Utl_stripWhiteSpace( t ); - strcpy( resultValue, p ); - return TRUE; - } - return FALSE; -} - -Bool -Prt_searchHeader( const char *artTxt, const char *which, Str result ) -{ - const char *src, *p; - char *dst; - Str line, whichLower, field; - int len; - - Utl_cpyStr( whichLower, which ); - Utl_toLower( whichLower ); - src = artTxt; - while ( TRUE ) - { - dst = line; - len = 0; - while ( *src != '\n' && len < MAXCHAR ) - { - if ( *src == '\0' ) - return FALSE; - *(dst++) = *(src++); - ++len; - } - if ( *src == '\n' ) - ++src; - *dst = '\0'; - p = Utl_stripWhiteSpace( line ); - if ( *p == '\0' ) - break; - if ( Prt_getField( field, result, line ) - && strcmp( field, whichLower ) == 0 ) - return TRUE; - } - return FALSE; -} - -static Bool -getFQDN( Str result ) -{ - struct hostent *myHostEnt; - struct utsname myName; - - if ( uname( &myName ) >= 0 - && ( myHostEnt = gethostbyname( myName.nodename ) ) ) - { - Utl_cpyStr( result, myHostEnt->h_name ); - return TRUE; - } - return FALSE; -} - -static void -getDomain( Str domain, const char *from ) -{ - const char *addTopLevel, *p1, *p2, *p, *domainStart; - Str myDomain; - - if ( getFQDN( myDomain ) ) - { - p = strstr( myDomain, "." ); - if ( p != NULL ) - domainStart = p + 1; - else - domainStart = myDomain; - } - else /* Take domain of From field */ - { - myDomain[ 0 ] = '\0'; - p1 = strstr( from, "@" ); - if ( p1 != NULL ) - { - p2 = strstr( p1, ">" ); - if ( p2 != NULL ) - Utl_cpyStrN( myDomain, p1 + 1, p2 - p1 - 1 ); - } - if ( myDomain[ 0 ] == '\0' ) - Utl_cpyStr( myDomain, "unknown" ); - domainStart = myDomain; - } - /* - If domain contains no dot (and is probably invalid anyway), - we add ".local", because some servers insist on domainnames with dot - in message ID. - */ - addTopLevel = strstr( domainStart, "." ) == NULL ? ".local" : ""; - snprintf( domain, MAXCHAR, "%s%s", myDomain, addTopLevel ); -} - -/* See RFC 850, section 2.1.7 */ -Bool -Prt_isValidMsgId( const char *msgId ) -{ - Str head, domain; - int len, headLen; - const char *p; - - len = strlen( msgId ); - p = strstr( msgId, "@" ); - if ( msgId[ 0 ] != '<' || msgId[ len - 1 ] != '>' || p == NULL ) - return FALSE; - strcpy( domain, p + 1 ); - domain[ strlen( domain ) - 1 ] = '\0'; - headLen = p - msgId - 1; - Utl_cpyStrN( head, msgId + 1, headLen ); - head[ headLen ] = '\0'; - /* - To do: check for special characters in head and domain (non-printable - or '@', '<', '>'). Maybe compare domain with a config option - and replace it by the config option, if not equal. - */ - if ( strstr( domain, "." ) == NULL ) - return FALSE; - return TRUE; -} - -void -Prt_genMsgId( Str msgId, const char *from, const char *suffix ) -{ - Str domain, date; - time_t t; - - getDomain( domain, from ); - time( &t ); - strftime( date, MAXCHAR, "%Y%m%d%H%M%S", gmtime( &t ) ); - srand( time( NULL ) ); - snprintf( msgId, MAXCHAR, "<%s.%X.%s@%s>", date, rand(), suffix, domain ); - ASSERT( Prt_isValidMsgId( msgId ) ); -}
--- a/protocol.h Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,105 +0,0 @@ -/* - protocol.h - - Functions related with the NNTP protocol which are useful for both - the server and the client. - - $Id: protocol.h 3 2000-01-04 11:35:42Z enz $ -*/ - -#ifndef PRT_H -#define PRT_H - -#include "dynamicstring.h" -#include "over.h" - -#define STAT_HELP_FOLLOWS 100 -#define STAT_DEBUG_FOLLOWS 199 - -#define STAT_READY_POST_ALLOW 200 -#define STAT_READY_NO_POST_ALLOW 201 -#define STAT_CMD_OK 202 -#define STAT_GOODBYE 205 -#define STAT_GRP_SELECTED 211 -#define STAT_GRPS_FOLLOW 215 -#define STAT_ART_FOLLOWS 220 -#define STAT_HEAD_FOLLOWS 221 -#define STAT_BODY_FOLLOWS 222 -#define STAT_ART_RETRIEVED 223 -#define STAT_OVERS_FOLLOW 224 -#define STAT_NEW_GRP_FOLLOW 231 -#define STAT_POST_OK 240 -#define STAT_AUTH_ACCEPTED 281 - -#define STAT_SEND_ART 340 -#define STAT_MORE_AUTH_REQUIRED 381 - -#define STAT_NO_SUCH_GRP 411 -#define STAT_NO_GRP_SELECTED 412 -#define STAT_NO_ART_SELECTED 420 -#define STAT_NO_NEXT_ART 421 -#define STAT_NO_PREV_ART 422 -#define STAT_NO_SUCH_NUMB 423 -#define STAT_NO_SUCH_ID 430 -#define STAT_ART_REJECTED 437 -#define STAT_POST_FAILED 441 -#define STAT_AUTH_REQUIRED 480 -#define STAT_AUTH_REJECTED 482 - -#define STAT_NO_SUCH_CMD 500 -#define STAT_SYNTAX_ERR 501 -#define STAT_NO_PERMISSION 502 -#define STAT_PROGRAM_FAULT 503 - -/* - Read next line from f into Str, up to "\n" or "\r\n". Don't save "\n" - or "\r\n" in line. Terminate with '\0'. -*/ -Bool -Prt_getLn( Str line, FILE *f ); - -/* - Read a text line from server. Returns TRUE if line != ".", removes - leading '.' otherwise. -*/ -Bool -Prt_getTxtLn( Str line, Bool *err, FILE *f ); - -/* - Write text line to f. Escape "." at the beginning with another ".". - Terminate with "\r\n". -*/ -Bool -Prt_putTxtLn( const char* line, FILE *f ); - -/* - Write text buffer of lines each ending with '\n'. - Replace '\n' by "\r\n". -*/ -Bool -Prt_putTxtBuf( const char *buf, FILE *f ); - -/* - Write text-ending "."-line to f -*/ -Bool -Prt_putEndOfTxt( FILE *f ); - -/* - Splits line in field and value. Field is converted to lower-case. -*/ -Bool -Prt_getField( Str resultField, Str resultValue, const char* line ); - -/* Search header. Works only with single line headers (ignores continuation - lines */ -Bool -Prt_searchHeader( const char *artTxt, const char* which, Str result ); - -Bool -Prt_isValidMsgId( const char *msgId ); - -void -Prt_genMsgId( Str msgId, const char *from, const char *suffix ); - -#endif
--- a/pseudo.c Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,330 +0,0 @@ -/* - pseudo.c - - $Id: pseudo.c 44 2000-05-05 07:23:15Z enz $ -*/ - -#include "pseudo.h" - -#include <time.h> -#include "common.h" -#include "config.h" -#include "content.h" -#include "database.h" -#include "group.h" -#include "log.h" -#include "protocol.h" -#include "util.h" - -Over * -genOv( const char *rawSubj, const char *rawBody, const char *suffix ) -{ - size_t bytes, lines; - time_t t; - Str subj, date, msgId; - - snprintf( subj, MAXCHAR, "[ %s ]", rawSubj ); - time( &t ); - Utl_rfc822Date( t, date ); - Prt_genMsgId( msgId, "", suffix ); - bytes = lines = 0; - while ( *rawBody ) - { - ++bytes; - if ( *rawBody == '\n' ) - ++lines; - ++rawBody; - } - return new_Over( subj, "news (\"[ NOFFLE ]\")" , date, msgId, "", - bytes, lines ); -} - -void -Pseudo_appGeneralInfo() -{ - Cont_app( genOv( "General info", Pseudo_generalInfoBody(), - "NOFFLE-GENERAL-INFO" ) ); -} - -Bool -Pseudo_isGeneralInfo( const char *msgId ) -{ - return ( strstr( msgId, "NOFFLE-GENERAL-INFO" ) != NULL ); -} - -const char * -Pseudo_generalInfoHead() -{ - static Str s; - - Over *ov; - - ov = genOv( "General info", Pseudo_generalInfoBody(), - "NOFFLE-GENERAL-INFO" ); - if ( ov ) - { - snprintf( s, MAXCHAR, - "Message-ID: %s\n" - "Subject: %s\n" - "From: %s\n" - "Date: %s\n" - "Bytes: %u\n" - "Lines: %u\n", - Ov_msgId( ov ), - Ov_subj( ov ), - Ov_from( ov ), - Ov_date( ov ), - Ov_bytes( ov ), - Ov_lines( ov ) ); - del_Over( ov ); - return s; - } - return NULL; -} - -const char * -Pseudo_generalInfoBody( void ) -{ - if ( Cfg_autoSubscribe() ) - return - "\n" - "\t[ NOFFLE INFO: General information ]\n" - "\n" - "\t[ This server is running NOFFLE, which is a NNTP server ]\n" - "\t[ optimized for low speed dial-up Internet connections. ]\n" - "\n" - "\t[ By reading this or any other article of this group, ]\n" - "\t[ NOFFLE has put it on its fetch list and will retrieve ]\n" - "\t[ articles next time it is online. ]\n" - "\n" - "\t[ If you have more questions about NOFFLE please talk ]\n" - "\t[ to your newsmaster or read the manual page for ]\n" - "\t[ \"noffle\". ]\n"; - else - return - "\n" - "\t[ NOFFLE INFO: General information ]\n" - "\n" - "\t[ This server is running NOFFLE, which is a NNTP server ]\n" - "\t[ optimized for low speed dial-up Internet connections. ]\n" - "\n" - "\t[ This group is presently not on the fetch list. You can ]\n" - "\t[ put groups on the fetch list by running the \"noffle\" ]\n" - "\t[ command on the computer where this server is running. ]\n" - "\n" - "\t[ If you have more questions about NOFFLE please talk ]\n" - "\t[ to your newsmaster or read the manual page for ]\n" - "\t[ \"noffle\". ]\n"; -} - -const char * -Pseudo_markedBody( void ) -{ - return - "\n" - "\t[ NOFFLE INFO: Marked for download ]\n" - "\n" - "\t[ The body of this article has been marked for download. ]\n"; -} - -const char * -Pseudo_alreadyMarkedBody( void ) -{ - return - "\n" - "\t[ NOFFLE INFO: Already marked for download ]\n" - "\n" - "\t[ The body of this article has already been marked ]\n" - "\t[ for download. ]\n"; -} - -const char * -Pseudo_markingFailedBody( void ) -{ - return - "\n" - "\t[ NOFFLE ERROR: Marking for download failed ]\n" - "\n" - "\t[ Sorry, I could not mark this article for download. ]\n" - "\t[ Either the database is corrupted, or I was unable to ]\n" - "\t[ get write access to the request directory. ]\n" - "\t[ Please contact your newsmaster to remove this problem. ]\n"; -} - -void -genPseudo( const char *rawSubj, const char* rawBody ) -{ - Over *ov; - DynStr *body = 0, *artTxt = 0; - - body = new_DynStr( 10000 ); - artTxt = new_DynStr( 10000 ); - DynStr_app( body, "\n\t[ NOFFLE INFO: " ); - DynStr_app( body, rawSubj ); - DynStr_app( body, " ]\n\n" ); - DynStr_app( body, "\t[ " ); - while( *rawBody ) - { - if ( *rawBody == '\n' ) - { - DynStr_app( body, " ]\n" ); - if ( *( rawBody + 1 ) == '\n' ) - { - DynStr_app( body, "\n\t[ " ); - ++rawBody; - } - else if ( *( rawBody + 1 ) != '\0' ) - DynStr_app( body, "\t[ " ); - } - else - DynStr_appN( body, rawBody, 1 ); - ++rawBody; - } - DynStr_appLn( body, "" ); - DynStr_appLn( artTxt, - "Comments: Pseudo article generated by news server NOFFLE" ); - DynStr_appLn( artTxt, "" ); - DynStr_appDynStr( artTxt, body ); - ov = genOv( rawSubj, DynStr_str( body ), "PSEUDO" ); - if ( body && artTxt && ov ) - { - Cont_app( ov ); - if ( Db_prepareEntry( ov, Cont_grp(), Cont_last() ) ) - Db_storeArt( Ov_msgId( ov ), DynStr_str( artTxt ) ); - Cont_write(); - Grp_setFirstLast( Cont_grp(), Cont_first(), Cont_last() ); - } - del_DynStr( body ); - del_DynStr( artTxt ); -} - -void -Pseudo_retrievingFailed( const char *msgId, const char *reason ) -{ - DynStr *artTxt = 0; - - if ( ! Db_contains( msgId ) ) - { - Log_err( "Article %s has no entry in database %s", msgId ); - return; - } - artTxt = new_DynStr( 10000 ); - DynStr_appLn( artTxt, - "Comments: Pseudo body generated by news server NOFFLE" ); - DynStr_appLn( artTxt, "" ); - DynStr_app( artTxt, - "\n" - "\t[ NOFFLE ERROR: Retrieving failed ]\n" - "\n" - "\t[ This article could not be retrieved. Maybe ]\n" - "\t[ it has already expired at the remote server ]\n" - "\t[ or it has been cancelled by its sender. See ]\n" - "\t[ the appended status line of the remote ]\n" - "\t[ server for more information. ]\n" - "\n" - "\t[ This message will disappear the next time ]\n" - "\t[ someone tries to read this article, so that ]\n" - "\t[ it can be marked for download again. ]\n" ); - DynStr_app( artTxt, "\n\t[ Remote server status: " ); - DynStr_app( artTxt, reason ); - DynStr_app( artTxt, " ]\n" ); - Db_storeArt( msgId, DynStr_str( artTxt ) ); - del_DynStr( artTxt ); -} - -void -Pseudo_cntInconsistent( const char *grp, int first, int last, int next ) -{ - DynStr *info; - Str s; - - info = new_DynStr( 10000 ); - if ( info ) - { - DynStr_app( info, - "This group's article counter is not \n" - "consistent Probably the remote news server\n" - "was changed or has reset its article counter\n" - "for this group. As a consequence there could\n" - "be some articles be duplicated in this group\n" ); - snprintf( s, MAXCHAR, "Group: %s", grp ); - DynStr_appLn( info, s ); - snprintf( s, MAXCHAR, "Remote first article number: %i", first ); - DynStr_appLn( info, s ); - snprintf( s, MAXCHAR, "Remote last article number: %i", last ); - DynStr_appLn( info, s ); - snprintf( s, MAXCHAR, "Remote next article number: %i", next ); - DynStr_appLn( info, s ); - genPseudo( "Article counter inconsistent", DynStr_str( info ) ); - } - del_DynStr( info ); -} - -void -Pseudo_missArts( const char *grp, int first, int next ) -{ - DynStr *info; - Str s; - - info = new_DynStr( 5000 ); - if ( info ) - { - DynStr_app( info, - "Some articles could not be retrieved from\n" - "the remote server, because it had already\n" - "deleted them.\n" - "If this group is on the fetch list, then\n" - "contact your newsmaster to ensure that\n" - "\"noffle\" is fetching news more frequently.\n" ); - snprintf( s, MAXCHAR, "Group: %s", grp ); - DynStr_appLn( info, s ); - snprintf( s, MAXCHAR, "Remote next article number: %i", next ); - DynStr_appLn( info, s ); - snprintf( s, MAXCHAR, "Remote first article number: %i", first ); - DynStr_appLn( info, s ); - genPseudo( "Missing articles", DynStr_str( info ) ); - del_DynStr( info ); - } -} - -void -Pseudo_autoUnsubscribed( const char *grp, int days ) -{ - DynStr *info; - Str s; - - info = new_DynStr( 10000 ); - if ( info ) - { - DynStr_app( info, - "NOFFLE has automatically unsubscribed this\n" - "group since it has not been accessed for\n" - "some time.\n" - "Re-subscribing is done either automatically\n" - "by NOFFLE (if configured) or by manually\n" - "running the 'noffle --subscribe' command\n" ); - snprintf( s, MAXCHAR, "Group: %s", grp ); - DynStr_appLn( info, s ); - snprintf( s, MAXCHAR, "Days without access: %i", days ); - DynStr_appLn( info, s ); - genPseudo( "Auto unsubscribed", DynStr_str( info ) ); - } - del_DynStr( info ); -} - -void -Pseudo_autoSubscribed() -{ - DynStr *info; - - info = new_DynStr( 10000 ); - if ( info ) - { - DynStr_app( info, - "NOFFLE has now automatically subscribed to\n" - "this group. It will fetch articles next time\n" - "it is online.\n" ); - genPseudo( "Auto subscribed", DynStr_str( info ) ); - } - del_DynStr( info ); -}
--- a/pseudo.h Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ -/* - pseudo.h - - Handling of pseudo articles. - - $Id: pseudo.h 3 2000-01-04 11:35:42Z enz $ -*/ - -#ifndef PSEUDO_H -#define PSEUDO_H - -#include "over.h" - -/* - General info is a special pseudo message for groups not on fetchlist. - It is never stored in database, but generated every time a content is read. - However the group counter is always increased. This ensures that there - is always at least 1 article visible (even if the user deletes it) for - using the auto-subscribe option. -*/ -Bool -Pseudo_isGeneralInfo( const char *msgId ); - -void -Pseudo_appGeneralInfo( void ); - -const char * -Pseudo_generalInfoHead( void ); - -const char * -Pseudo_generalInfoBody( void ); - - -const char * -Pseudo_markedBody( void ); - -const char * -Pseudo_alreadyMarkedBody( void ); - -const char * -Pseudo_markingFailedBody( void ); - -void -Pseudo_retrievingFailed( const char *msgId, const char *reason ); - - -/* - Other pseudo articles are stored in database and can contain dynamically - generated information about the failure. - */ - -void -Pseudo_cntInconsistent( const char *grp, int first, int last, int next ); - -void -Pseudo_missArts( const char *grp, int first, int next ); - -void -Pseudo_autoUnsubscribed( const char *grp, int days ); - -void -Pseudo_autoSubscribed( void ); - -#endif
--- a/request.c Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,405 +0,0 @@ -/* - request.c - - Collection of articles that are marked for download. - - $Id: request.c 3 2000-01-04 11:35:42Z enz $ -*/ - -#include "request.h" -#include <dirent.h> -#include <errno.h> -#include <fcntl.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> -#include <assert.h> -#include "config.h" -#include "log.h" -#include "util.h" - - -/* This struct keeps record of the message IDs that are to be fetched from - one particular host. Several of these are chained together via the - "next" pointer, if we have several servers. -*/ - -struct Reqserv; -typedef struct Reqserv Reqserv; - -struct Reqserv { - char* serv; /* Server the messages are to be requested - from */ - char** reql; /* List of message IDs of requested - messages. Some entries (that have been - deleted) may be NULL */ - int reql_length; /* Number of string pointers in reql, - including NULL entries */ - int reql_capacity; /* maximum number of string pointers reql - can hold */ - Bool dirty; /* whether the request list needs to be - rewritten to disk */ - Reqserv* next; /* next Reqserv in list */ - time_t mtime; /* last modification time of request file */ -}; - -/* List of servers */ -static Reqserv* reqserv = 0; - -/* sanity check */ -static Bool is_open = FALSE; - -/* for Req_first/Req_next */ -static char** iterator = 0; -static char** iterator_end = 0; - - -/* local functions */ -static Reqserv* newReqserv (const char* serv); -static Bool getReqserv (const char* serv, Reqserv** rsz); -static void fileRequest (Str file, const char *serv); -static char** searchMsgId (const Reqserv * rs, const char *msgId); -static void storeMsgId (Reqserv* rs, const char* msgId); -static Bool readRequestfile (const char* serv, Reqserv** rsz); -static time_t get_mtime (const char* serv); - -/* read modification time of request file */ -static time_t get_mtime(const char* serv) -{ - Str filename; - struct stat stat1; - - fileRequest(filename, serv); - stat(filename, &stat1); - return stat1.st_mtime; -} - - -/* create new Reqserv and queue it */ -static Reqserv* newReqserv(const char* serv) -{ - Reqserv* rs = (Reqserv*) malloc(sizeof(Reqserv)); - rs->serv = strcpy(malloc(strlen(serv)+1), serv); - rs->reql = 0; - rs->reql_length = 0; - rs->reql_capacity = 0; - rs->next = reqserv; - rs->dirty = FALSE; - rs->mtime = 0; - reqserv = rs; - return rs; -} - - -/* get Reqserv for given server, and save it in "rsz". Load from file as - necessary. Return TRUE on success. Otherwise log errors and return - FALSE. (details in errno) -*/ -static Bool getReqserv(const char* serv, Reqserv** rsz) -{ - Reqserv* rs; - for (rs = reqserv; rs; rs = rs->next) - if (!strcmp(serv, rs->serv)) { - *rsz = rs; - return TRUE; - } - return readRequestfile(serv, rsz); -} - - -/* Delete Reqserv from cache, if not up-to-date */ -static void -cleanupReqserv( void ) -{ - Reqserv *rs, *prev, *next; - - rs = reqserv; - prev = NULL; - while ( rs != NULL ) - { - ASSERT( ! rs->dirty ); - next = rs->next; - if ( get_mtime( rs->serv ) != rs->mtime ) - { - if ( prev != NULL ) - prev->next = next; - else - reqserv = next; - free( rs->serv ); - rs->serv = NULL; - free( rs->reql ); - rs->reql = NULL; - free( rs ); - } - prev = rs; - rs = next; - } -} - -/* Save name of file storing requests from server "serv" in "file" */ -static void fileRequest( Str file, const char *serv) -{ - snprintf( file, MAXCHAR, "%s/requested/%s", Cfg_spoolDir(), serv); -} - - -/* Search for msgid in Reqserv. Return pointer to list entry. Return 0 if - list does not contain msgid. */ -static char** searchMsgId(const Reqserv * rs, const char *msgId ) -{ - char** rz; - ASSERT(rs != 0); - - if (!rs->reql) - return 0; - - for (rz = rs->reql; rz < rs->reql + rs->reql_length; rz++) - if (*rz && !strcmp(*rz, msgId)) - return rz; - - return 0; -} - - -Bool -Req_contains(const char *serv, const char *msgId) -{ - Reqserv* rs; - ASSERT( is_open ); - if (getReqserv(serv, &rs) == FALSE) - return FALSE; - return searchMsgId(rs, msgId) ? TRUE : FALSE; -} - - -static void storeMsgId(Reqserv* rs, const char* msgId) -{ - char* msgid; - - if (searchMsgId(rs, msgId)) - /* already recorded */ - return; - - msgid = strcpy(malloc(strlen(msgId)+1), msgId); - - if (rs->reql_length >= rs->reql_capacity) { - int c1 = rs->reql_capacity*2 + 10; - rs->reql = (char**) realloc(rs->reql, c1*sizeof(char*)); - rs->reql_capacity = c1; - } - - *(rs->reql + rs->reql_length++) = msgid; - rs->dirty = TRUE; -} - - -/* Add request for message "msgIg" from server "serv". Return TRUE iff - successful. -*/ -Bool Req_add(const char *serv, const char *msgId) -{ - Reqserv* rs; - ASSERT( is_open ); - Log_dbg( "Marking %s on %s for download", msgId, serv ); - - if (getReqserv(serv, &rs) == FALSE) - return FALSE; - storeMsgId(rs, msgId); - return TRUE; -} - -static Bool -readLn( Str line, FILE* f ) -{ - size_t len; - - if ( ! fgets( line, MAXCHAR, f ) ) - return FALSE; - len = strlen( line ); - if ( line[ len - 1 ] == '\n' ) - line[ len - 1 ] = '\0'; - return TRUE; -} - -/* Read request file into new, non-queued Reqserv. Save new Reqserv in - "rsz" and return TRUE on success. Returns FALSE on failure, see errno. - If the file doesn't exist, an empty Reqserv is returned. -*/ -static Bool readRequestfile(const char* serv, Reqserv** rsz) -{ - Str filename; - Str line; - FILE* file; - Reqserv* rs; - - fileRequest(filename, serv); - Log_dbg("reading request file %s", filename); - - file = fopen(filename, "r"); - if (!file && (errno == ENOENT)) { - *rsz = newReqserv(serv); - (*rsz)->mtime = get_mtime(serv); - return TRUE; - } - if (Log_check(file != 0, - "could not open %s for reading: %s", - filename, strerror(errno))) - return FALSE; - - rs = *rsz = newReqserv(serv); - - while( readLn(line, file) == TRUE) { - char* line1 = Utl_stripWhiteSpace(line); - if (*line1) - storeMsgId(rs, line1); - } - - rs->dirty = FALSE; - - if (Log_check(fclose(file) != EOF, - "could not close %s properly: %s\n", - filename, strerror(errno))) - return FALSE; - - return TRUE; -} - - -/* Write out request file for given Reqserv. Return TRUE on success. If an - I/O error occurs, it is logged, and FALSE is returned. -*/ -static Bool writeRequestfile(Reqserv* rs) -{ - Str filename; - FILE* file; - char** z; - - fileRequest(filename, rs->serv); - Log_dbg("writing request file %s", filename); - - if (Log_check((file = fopen(filename, "w")) != 0, - "could not open %s for writing: %s", - filename, strerror(errno))) - return FALSE; - - if (rs->reql) - for (z = rs->reql; z < rs->reql+rs->reql_length; z++) - if (*z) { - if (Log_check( fputs(*z, file) != EOF - && fputs("\n", file) != EOF, - "write error: %s", strerror(errno))) - return FALSE; - } - - if (Log_check(fclose(file) != EOF, - "could not close %s properly: %s\n", - filename, strerror(errno))) - return FALSE; - - rs->dirty = FALSE; - rs->mtime = get_mtime(rs->serv); - - return TRUE; -} - - -void -Req_remove( const char *serv, const char *msgId ) -{ - Reqserv* rs; - char** z; - - ASSERT( is_open ); - Log_dbg("Req_remove(\"%s\", \"%s\")", serv, msgId); - - if (getReqserv(serv, &rs) == FALSE) - return; - - z = searchMsgId(rs, msgId); - if ( ! z ) - return; - - free(*z); - *z = 0; - rs->dirty = TRUE; -} - - -Bool -Req_first( const char *serv, Str msgId ) -{ - Reqserv* rs; - - ASSERT( is_open ); - ASSERT( !iterator && !iterator_end ); - - if (getReqserv(serv, &rs) == FALSE) - return FALSE; - - if (!rs->reql) - return FALSE; - - iterator = rs->reql - 1; - iterator_end = rs->reql + rs->reql_length; - - return Req_next(msgId); -} - - -Bool -Req_next( Str msgId ) -{ - ASSERT( is_open ); - ASSERT(iterator && iterator_end); - - if (iterator >= iterator_end) - return FALSE; - iterator++; - - while (iterator < iterator_end) { - if (!*iterator) - iterator++; - else { - Utl_cpyStr(msgId, *iterator); - return TRUE; - } - } - - iterator = iterator_end = 0; - return FALSE; -} - - -/* Get exclusive access to all request files. Maybe we already have had it, - and the cache is outdated. So we delete request files, which have - changed recently, from cache. These files will be reread on demand. -*/ -Bool -Req_open(void) -{ - Log_dbg("opening request database"); - ASSERT(is_open == FALSE); - cleanupReqserv(); - is_open = TRUE; - return TRUE; -} - - -/* Do not occupy the request files any longer. Write any changes to disk. - Return TRUE on success, FALSE if an IO error occurs. */ -void Req_close(void) -{ - Bool ret = TRUE; - Reqserv* rs; - Log_dbg("closing request database, writing changes to disk"); - ASSERT(is_open == TRUE); - - for (rs = reqserv; rs; rs = rs->next) { - if (rs->dirty == TRUE) { - if (!writeRequestfile(rs)) - ret = FALSE; - } - } - - is_open = FALSE; -}
--- a/request.h Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -/* - request.h - - Collection of requested articles. - - $Id: request.h 3 2000-01-04 11:35:42Z enz $ -*/ - -#ifndef REQ_H -#define REQ_H - -#include "common.h" - -/* Is request for message msgId from server serv already recorded? This - function has no error detection facility. On error, FALSE is returned. - Nevertheless, errors are logged. */ -Bool -Req_contains( const char *serv, const char *msgId ); - -/* Add request for message "msgId" from server "serv". Return TRUE if - successful. */ -Bool -Req_add( const char *serv, const char *msgId ); - -/* Remove request for message msgIg from server serv. This function does - not return any errors. Nevertheless, they are logged. */ -void -Req_remove( const char *serv, const char *msgId ); - -/* Begin iteration through all messages requested from one server. Return - TRUE if there are any requests. Save first message ID in msgId. On - error, it is logged, and FALSE is returned. -*/ -Bool -Req_first( const char *serv, Str msgId ); - -/* Continue iteration. Return TRUE on success, FALSE when there are no more - requests. Save message ID in msgId. On error, it is logged, and FALSE is - returned. */ -Bool -Req_next( Str msgId ); - -/* Get exclusive access to the request files. Refresh cache as necessary. */ -Bool -Req_open(void); - -/* Write changes to disk */ -void -Req_close(void); - -#endif
--- a/server.c Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1539 +0,0 @@ -/* - server.c - - $Id: server.c 47 2000-05-05 08:32:53Z enz $ -*/ - -#include "server.h" -#include <ctype.h> -#include <signal.h> -#include <stdarg.h> -#include <sys/time.h> -#include <sys/types.h> -#include <time.h> -#include <unistd.h> -#include "client.h" -#include "common.h" -#include "config.h" -#include "content.h" -#include "control.h" -#include "database.h" -#include "dynamicstring.h" -#include "fetch.h" -#include "fetchlist.h" -#include "group.h" -#include "itemlist.h" -#include "lock.h" -#include "log.h" -#include "online.h" -#include "outgoing.h" -#include "post.h" -#include "protocol.h" -#include "pseudo.h" -#include "request.h" -#include "util.h" -#include "wildmat.h" - -struct -{ - Bool running; - int artPtr; - Str grp; /* selected group, "" if none */ -} serv = { FALSE, 0, "" }; - -typedef struct Cmd -{ - const char *name; - const char *syntax; - /* Returns false, if quit cmd */ - Bool (*cmdProc)( char *arg, const struct Cmd *cmd ); -} -Cmd; - -static Bool doArt( char *arg, const Cmd *cmd ); -static Bool doBody( char *arg, const Cmd *cmd ); -static Bool doGrp( char *arg, const Cmd *cmd ); -static Bool doHead( char *arg, const Cmd *cmd ); -static Bool doHelp( char *arg, const Cmd *cmd ); -static Bool doIhave( char *arg, const Cmd *cmd ); -static Bool doLast( char *arg, const Cmd *cmd ); -static Bool doList( char *arg, const Cmd *cmd ); -static Bool doListgrp( char *arg, const Cmd *cmd ); -static Bool doMode( char *arg, const Cmd *cmd ); -static Bool doNewgrps( char *arg, const Cmd *cmd ); -static Bool doNext( char *arg, const Cmd *cmd ); -static Bool doPost( char *arg, const Cmd *cmd ); -static Bool doSlave( char *arg, const Cmd *cmd ); -static Bool doStat( char *arg, const Cmd *cmd ); -static Bool doQuit( char *arg, const Cmd *cmd ); -static Bool doXhdr( char *arg, const Cmd *cmd ); -static Bool doXpat( char *arg, const Cmd *cmd ); -static Bool doXOver( char *arg, const Cmd *cmd ); -static Bool notImplemented( char *arg, const Cmd *cmd ); -static void putStat( unsigned int stat, const char *fmt, ... ); - -Cmd commands[] = -{ - { "article", "ARTICLE [msg-id|n]", &doArt }, - { "body", "BODY [msg-id|n]", &doBody }, - { "head", "HEAD [msg-id|n]", &doHead }, - { "group", "GROUP grp", &doGrp }, - { "help", "HELP", &doHelp }, - { "ihave", "IHAVE (ignored)", &doIhave }, - { "last", "LAST", &doLast }, - { "list", "LIST [ACTIVE [pat]]|ACTIVE.TIMES [pat]|" - "EXTENSIONS|NEWSGROUPS [pat]|OVERVIEW.FMT", &doList }, - { "listgroup", "LISTGROUP grp", &doListgrp }, - { "mode", "MODE (ignored)", &doMode }, - { "newgroups", "NEWGROUPS [xx]yymmdd hhmmss [GMT]", &doNewgrps }, - { "newnews", "NEWNEWS (not implemented)", ¬Implemented }, - { "next", "NEXT", &doNext }, - { "post", "POST", &doPost }, - { "quit", "QUIT", &doQuit }, - { "slave", "SLAVE (ignored)", &doSlave }, - { "stat", "STAT [msg-id|n]", &doStat }, - { "xhdr", "XHDR over-field [m[-[n]]]", &doXhdr }, - { "xpat", "XPAT over-field m[-[n]] pat", &doXpat }, - { "xover", "XOVER [m[-[n]]]", &doXOver } -}; - -/* - Notice interest in reading this group. - Automatically subscribe if option set in config file. -*/ -static void -noteInterest( void ) -{ - FetchMode mode; - - Grp_setLastAccess( serv.grp, time( NULL ) ); - if ( ! Grp_local ( serv.grp ) && Cfg_autoSubscribe() && ! Online_true() ) - { - Fetchlist_read(); - if ( ! Fetchlist_contains( serv.grp ) ) - { - if ( strcmp( Cfg_autoSubscribeMode(), "full" ) == 0 ) - mode = FULL; - else if ( strcmp( Cfg_autoSubscribeMode(), "thread" ) == 0 ) - mode = THREAD; - else - mode = OVER; - Fetchlist_add( serv.grp, mode ); - Fetchlist_write(); - Pseudo_autoSubscribed(); - } - } -} - -static void -putStat( unsigned int stat, const char *fmt, ... ) -{ - Str s, line; - va_list ap; - - ASSERT( stat <= 999 ); - va_start( ap, fmt ); - vsnprintf( s, MAXCHAR, fmt, ap ); - va_end( ap ); - snprintf( line, MAXCHAR, "%u %s", stat, s ); - Log_dbg( "[S] %s", line ); - printf( "%s\r\n", line ); -} - -static void -putTxtLn( const char *fmt, ... ) -{ - Str line; - va_list ap; - - va_start( ap, fmt ); - vsnprintf( line, MAXCHAR, fmt, ap ); - va_end( ap ); - Prt_putTxtLn( line, stdout ); -} - -static void -putTxtBuf( const char *buf ) -{ - if ( buf ) - Prt_putTxtBuf( buf, stdout ); -} - -static void -putEndOfTxt( void ) -{ - Prt_putEndOfTxt( stdout ); -} - -static void -putSyntax( const Cmd *cmd ) -{ - putStat( STAT_SYNTAX_ERR, "Syntax error. Usage: %s", cmd->syntax ); -} - -static Bool -getLn( Str line ) -{ - return Prt_getLn( line, stdin ); -} - -static Bool -getTxtLn( Str line, Bool *err ) -{ - return Prt_getTxtLn( line, err, stdin ); -} - -static Bool -notImplemented( char *arg, const Cmd *cmd ) -{ - putStat( STAT_NO_PERMISSION, "Command not implemented" ); - return TRUE; -} - -static void -checkNewArts( const char *grp ) -{ - if ( ! Online_true() - || strcmp( grp, serv.grp ) == 0 - || Grp_local( grp ) - || time( NULL ) - Grp_lastAccess( serv.grp ) < 1800 ) - return; - if ( Fetch_init( Grp_serv( grp ) ) ) - { - Fetch_getNewArts( grp, OVER ); - Fetch_close(); - } -} - -static void -postArts() -{ - Str serv; - - Cfg_beginServEnum(); - while ( Cfg_nextServ( serv ) ) - if ( Fetch_init( serv ) ) - { - Fetch_postArts(); - Fetch_close(); - } -} - -static void -readCont( const char *name ) -{ - Fetchlist_read(); - Cont_read( name ); - if ( ! Grp_local ( name ) - && ! Fetchlist_contains( name ) - && ! Online_true() ) - { - Pseudo_appGeneralInfo(); - Grp_setFirstLast( name, Cont_first(), Cont_last() ); - } -} - -static void -changeToGrp( const char *grp ) -{ - checkNewArts( grp ); - Utl_cpyStr( serv.grp, grp ); - readCont( grp ); - serv.artPtr = Cont_first(); -} - -static Bool -doGrp( char *arg, const Cmd *cmd ) -{ - int first, last, numb; - - if ( arg[ 0 ] == '\0' ) - putSyntax( cmd ); - else if ( ! Grp_exists( arg ) ) - putStat( STAT_NO_SUCH_GRP, "No such group" ); - else - { - changeToGrp( arg ); - first = Cont_first(); - last = Cont_last(); - if ( ( first == 0 && last == 0 ) - || first > last ) - first = last = numb = 0; - else - numb = last - first + 1; - putStat( STAT_GRP_SELECTED, "%lu %lu %lu %s selected", - numb, first, last, arg ); - } - return TRUE; -} - -static Bool -testGrpSelected( void ) -{ - if ( *serv.grp == '\0' ) - { - putStat( STAT_NO_GRP_SELECTED, "No group selected" ); - return FALSE; - } - return TRUE; -} - -static void -findServ( const char *msgId, Str result ) -{ - const char *p, *pColon, *serv; - Str s, grp; - - Utl_cpyStr( result, "(unknown)" ); - if ( Db_contains( msgId ) ) - { - Utl_cpyStr( s, Db_xref( msgId ) ); - p = strtok( s, " \t" ); - if ( p ) - do - { - pColon = strstr( p, ":" ); - if ( pColon ) - { - Utl_cpyStrN( grp, p, pColon - p ); - serv = Grp_serv( grp ); - if ( Cfg_servIsPreferential( serv, result ) ) - Utl_cpyStr( result, serv ); - } - } - while ( ( p = strtok( NULL, " \t" ) ) ); - } -} - -static Bool -retrieveArt( const char *msgId ) -{ - Str serv; - - findServ( msgId, serv ); - if ( strcmp( serv, "(unknown)" ) == 0 - || strcmp( serv, GRP_LOCAL_SERVER_NAME ) == 0 ) - return FALSE; - if ( ! Client_connect( serv ) ) - return FALSE; - Client_retrieveArt( msgId ); - Client_disconnect(); - return TRUE; -} - -static Bool -checkNumb( int numb ) -{ - if ( ! testGrpSelected() ) - return FALSE; - if ( ! Cont_validNumb( numb ) ) - { - putStat( STAT_NO_SUCH_NUMB, "No such article" ); - return FALSE; - } - return TRUE; -} - -/* - Parse arguments for ARTICLE, BODY, HEAD, STAT commands. - Return message-ID and article number (0 if unknown). -*/ -static Bool -whichId( const char **msgId, int *numb, char *arg ) -{ - const Over *ov; - int n; - - if ( sscanf( arg, "%d", &n ) == 1 ) - { - if ( ! checkNumb( n ) ) - return FALSE; - serv.artPtr = n; - ov = Cont_get( n ); - *msgId = Ov_msgId( ov ); - *numb = n; - } - else if ( strcmp( arg, "" ) == 0 ) - { - if ( ! checkNumb( serv.artPtr ) ) - return FALSE; - ov = Cont_get( serv.artPtr ); - *msgId = Ov_msgId( ov ); - *numb = serv.artPtr; - } - else - { - *msgId = arg; - *numb = 0; - } - if ( ! Pseudo_isGeneralInfo( *msgId ) && ! Db_contains( *msgId ) ) - { - putStat( STAT_NO_SUCH_NUMB, "No such article" ); - return FALSE; - } - return TRUE; -} - -void -touchArticle( const char *msgId ) -{ - int stat = Db_stat( msgId ); - stat |= DB_INTERESTING; - Db_setStat( msgId, stat ); - Db_updateLastAccess( msgId ); -} - -static void -touchReferences( const char *msgId ) -{ - Str s; - int len; - char *p; - const char *ref = Db_ref( msgId ); - - while ( TRUE ) - { - p = s; - while ( *ref != '<' ) - if ( *(ref++) == '\0' ) - return; - len = 0; - while ( *ref != '>' ) - { - if ( *ref == '\0' || ++len >= MAXCHAR - 1 ) - return; - *(p++) = *(ref++); - } - *(p++) = '>'; - *p = '\0'; - if ( Db_contains( s ) ) - touchArticle( s ); - } -} - -static void -doBodyInDb( const char *msgId ) -{ - int stat; - Str serv; - - touchArticle( msgId ); - touchReferences( msgId ); - stat = Db_stat( msgId ); - if ( Online_true() && ( stat & DB_NOT_DOWNLOADED ) ) - { - retrieveArt( msgId ); - stat = Db_stat( msgId ); - } - if ( stat & DB_RETRIEVING_FAILED ) - { - Db_setStat( msgId, stat & ~DB_RETRIEVING_FAILED ); - putTxtBuf( Db_body( msgId ) ); - } - else if ( stat & DB_NOT_DOWNLOADED ) - { - findServ( msgId, serv ); - if ( Req_contains( serv, msgId ) ) - putTxtBuf( Pseudo_alreadyMarkedBody() ); - else if ( strcmp( serv, "(unknown)" ) != 0 && - strcmp( serv, GRP_LOCAL_SERVER_NAME ) != 0 && - Req_add( serv, msgId ) ) - putTxtBuf( Pseudo_markedBody() ); - else - putTxtBuf( Pseudo_markingFailedBody() ); - } - else - putTxtBuf( Db_body( msgId ) ); -} - -static Bool -doBody( char *arg, const Cmd *cmd ) -{ - const char *msgId; - int numb; - - if ( ! whichId( &msgId, &numb, arg ) ) - return TRUE; - putStat( STAT_BODY_FOLLOWS, "%ld %s Body", numb, msgId ); - if ( Pseudo_isGeneralInfo( msgId ) ) - putTxtBuf( Pseudo_generalInfoBody() ); - else - doBodyInDb( msgId ); - putEndOfTxt(); - noteInterest(); - return TRUE; -} - -static void -doHeadInDb( const char *msgId ) -{ - putTxtBuf( Db_header( msgId ) ); -} - -static Bool -doHead( char *arg, const Cmd *cmd ) -{ - const char *msgId; - int numb; - - if ( ! whichId( &msgId, &numb, arg ) ) - return TRUE; - putStat( STAT_HEAD_FOLLOWS, "%ld %s Head", numb, msgId ); - if ( Pseudo_isGeneralInfo( msgId ) ) - putTxtBuf( Pseudo_generalInfoHead() ); - else - doHeadInDb( msgId ); - putEndOfTxt(); - return TRUE; -} - -static void -doArtInDb( const char *msgId ) -{ - doHeadInDb( msgId ); - putTxtLn( "" ); - doBodyInDb( msgId ); -} - -static Bool -doArt( char *arg, const Cmd *cmd ) -{ - const char *msgId; - int numb; - - if ( ! whichId( &msgId, &numb, arg ) ) - return TRUE; - putStat( STAT_ART_FOLLOWS, "%ld %s Article", numb, msgId ); - if ( Pseudo_isGeneralInfo( msgId ) ) - { - putTxtBuf( Pseudo_generalInfoHead() ); - putTxtLn( "" ); - putTxtBuf( Pseudo_generalInfoBody() ); - } - else - doArtInDb( msgId ); - putEndOfTxt(); - noteInterest(); - return TRUE; -} - -static Bool -doHelp( char *arg, const Cmd *cmd ) -{ - unsigned int i; - - putStat( STAT_HELP_FOLLOWS, "Help" ); - putTxtBuf( "\nCommands:\n\n" ); - for ( i = 0; i < sizeof( commands ) / sizeof( commands[ 0 ] ); ++i ) - putTxtLn( "%s", commands[ i ].syntax ); - putEndOfTxt(); - return TRUE; -} - -static Bool -doIhave( char *arg, const Cmd *cmd ) -{ - putStat( STAT_ART_REJECTED, "Command not used" ); - return TRUE; -} - -static Bool -doLast( char *arg, const Cmd *cmd ) -{ - int n; - - if ( testGrpSelected() ) - { - n = serv.artPtr; - if ( ! Cont_validNumb( n ) ) - putStat( STAT_NO_ART_SELECTED, "No article selected" ); - else - { - while ( ! Cont_validNumb( --n ) && n >= Cont_first() ); - if ( ! Cont_validNumb( n ) ) - putStat( STAT_NO_PREV_ART, "No previous article" ); - else - { - putStat( STAT_ART_RETRIEVED, "%ld %s selected", - n, Ov_msgId( Cont_get( n ) ) ); - serv.artPtr = n; - } - } - } - return TRUE; -} - -static void -printGroups( const char *pat, void (*printProc)( Str, const char* ) ) -{ - Str line; - const char *g; - FILE *f; - sig_t lastHandler; - int ret; - - putStat( STAT_GRPS_FOLLOW, "Groups" ); - fflush( stdout ); - Log_dbg( "[S FLUSH]" ); - if ( Grp_exists( pat ) ) - { - (*printProc)( line, pat ); - if ( ! Prt_putTxtLn( line, stdout ) ) - Log_err( "Writing to stdout failed." ); - } - else - { - lastHandler = signal( SIGPIPE, SIG_IGN ); - f = popen( "sort", "w" ); - if ( f == NULL ) - { - Log_err( "Cannot open pipe to 'sort'" ); - if ( Grp_firstGrp( &g ) ) - do - if ( Wld_match( g, pat ) ) - { - (*printProc)( line, g ); - if ( ! Prt_putTxtLn( line, stdout ) ) - Log_err( "Writing to stdout failed." ); - } - while ( Grp_nextGrp( &g ) ); - } - else - { - if ( Grp_firstGrp( &g ) ) - do - if ( Wld_match( g, pat ) ) - { - (*printProc)( line, g ); - if ( ! Prt_putTxtLn( line, f ) ) - { - Log_err( "Writing to 'sort' pipe failed." ); - break; - } - } - while ( Grp_nextGrp( &g ) ); - ret = pclose( f ); - if ( ret != EXIT_SUCCESS ) - Log_err( "sort command returned %d", ret ); - fflush( stdout ); - Log_dbg( "[S FLUSH]" ); - signal( SIGPIPE, lastHandler ); - } - } - putEndOfTxt(); -} - -static void -printActiveTimes( Str result, const char *grp ) -{ - snprintf( result, MAXCHAR, "%s %ld", grp, Grp_created( grp ) ); -} - -static void -doListActiveTimes( const char *pat ) -{ - printGroups( pat, &printActiveTimes ); -} - -static void -printActive( Str result, const char *grp ) -{ - snprintf( result, MAXCHAR, "%s %d %d %c", - grp, Grp_last( grp ), Grp_first( grp ), Grp_postAllow( grp ) ); -} - -static void -doListActive( const char *pat ) -{ - printGroups( pat, &printActive ); -} - -static void -printNewsgrp( Str result, const char *grp ) -{ - snprintf( result, MAXCHAR, "%s %s", grp, Grp_dsc( grp ) ); -} - -static void -doListNewsgrps( const char *pat ) -{ - printGroups( pat, &printNewsgrp ); -} - -static void -putGrp( const char *name ) -{ - putTxtLn( "%s %lu %lu y", name, Grp_last( name ), Grp_first( name ) ); -} - -static void -doListOverFmt( void ) -{ - putStat( STAT_GRPS_FOLLOW, "Overview format" ); - putTxtBuf( "Subject:\n" - "From:\n" - "Date:\n" - "Message-ID:\n" - "References:\n" - "Bytes:\n" - "Lines:\n" ); - putEndOfTxt(); -} - -static void -doListExtensions( void ) -{ - putStat( STAT_CMD_OK, "Extensions" ); - putTxtBuf( " LISTGROUP\n" - " XOVER\n" ); - putEndOfTxt(); -} - -static Bool -doList( char *line, const Cmd *cmd ) -{ - Str s, arg; - const char *pat; - - if ( sscanf( line, "%s", s ) != 1 ) - doListActive( "*" ); - else - { - Utl_toLower( s ); - strcpy( arg, Utl_restOfLn( line, 1 ) ); - pat = Utl_stripWhiteSpace( arg ); - if ( pat[ 0 ] == '\0' ) - pat = "*"; - if ( strcmp( "active", s ) == 0 ) - doListActive( pat ); - else if ( strcmp( "overview.fmt", s ) == 0 ) - doListOverFmt(); - else if ( strcmp( "newsgroups", s ) == 0 ) - doListNewsgrps( pat ); - else if ( strcmp( "active.times", s ) == 0 ) - doListActiveTimes( pat ); - else if ( strcmp( "extensions", s ) == 0 ) - doListExtensions(); - else - putSyntax( cmd ); - } - return TRUE; -} - -static Bool -doListgrp( char *arg, const Cmd *cmd ) -{ - const Over *ov; - int first, last, i; - - if ( ! Grp_exists( arg ) ) - putStat( STAT_NO_SUCH_GRP, "No such group" ); - else - { - changeToGrp( arg ); - first = Cont_first(); - last = Cont_last(); - putStat( STAT_GRP_SELECTED, "Article list" ); - for ( i = first; i <= last; ++i ) - if ( ( ov = Cont_get( i ) ) ) - putTxtLn( "%lu", i ); - putEndOfTxt(); - } - return TRUE; -} - -static Bool -doMode( char *arg, const Cmd *cmd ) -{ - putStat( STAT_READY_POST_ALLOW, "Ok" ); - return TRUE; -} - -static unsigned long -getTimeInSeconds( unsigned int year, unsigned int mon, unsigned int day, - unsigned int hour, unsigned int min, unsigned int sec ) -{ - struct tm t = { 0 }; - - t.tm_year = year - 1900; - t.tm_mon = mon - 1; - t.tm_mday = day; - t.tm_hour = hour; - t.tm_min = min; - t.tm_sec = sec; - return mktime( &t ); -} - - -static Bool -doNewgrps( char *arg, const Cmd *cmd ) -{ - time_t t, now, lastUpdate; - unsigned int year, mon, day, hour, min, sec, cent, len; - const char *g; - Str date, timeofday, file; - - if ( sscanf( arg, "%s %s", date, timeofday ) != 2 ) - { - putSyntax( cmd ); - return TRUE; - } - len = strlen( date ); - switch ( len ) - { - case 6: - if ( sscanf( date, "%2u%2u%2u", &year, &mon, &day ) != 3 ) - { - putSyntax( cmd ); - return TRUE; - } - now = time( NULL ); - cent = 1900; - while ( now > getTimeInSeconds( cent + 100, 1, 1, 0, 0, 0 ) ) - cent += 100; - year += cent; - break; - case 8: - if ( sscanf( date, "%4u%2u%2u", &year, &mon, &day ) != 3 ) - { - putSyntax( cmd ); - return TRUE; - } - break; - default: - putSyntax( cmd ); - return TRUE; - } - if ( sscanf( timeofday, "%2u%2u%2u", &hour, &min, &sec ) != 3 ) - { - putSyntax( cmd ); - return TRUE; - } - if ( year < 1970 || mon == 0 || mon > 12 || day == 0 || day > 31 - || hour > 23 || min > 59 || sec > 60 ) - { - putSyntax( cmd ); - return TRUE; - } - snprintf( file, MAXCHAR, "%s/groupinfo.lastupdate", Cfg_spoolDir() ); - t = getTimeInSeconds( year, mon, day, hour, min, sec ); - putStat( STAT_NEW_GRP_FOLLOW, "New groups since %s", arg ); - - if ( ! Utl_getStamp( &lastUpdate, file ) || t <= lastUpdate ) - { - if ( Grp_firstGrp( &g ) ) - do - if ( Grp_created( g ) > t ) - putGrp( g ); - while ( Grp_nextGrp( &g ) ); - } - putEndOfTxt(); - return TRUE; -} - -static Bool -doNext( char *arg, const Cmd *cmd ) -{ - int n; - - if ( testGrpSelected() ) - { - n = serv.artPtr; - if ( ! Cont_validNumb( n ) ) - putStat( STAT_NO_ART_SELECTED, "No article selected" ); - else - { - while ( ! Cont_validNumb( ++n ) && n <= Cont_last() ); - if ( ! Cont_validNumb( n ) ) - putStat( STAT_NO_NEXT_ART, "No next article" ); - else - { - putStat( STAT_ART_RETRIEVED, "%ld %s selected", - n, Ov_msgId( Cont_get( n ) ) ); - serv.artPtr = n; - } - } - } - return TRUE; -} - -/* Cancel and return TRUE if need to send cancel message on to server. */ -static Bool -controlCancel( const char *cancelId ) -{ - return ( Ctrl_cancel( cancelId ) == CANCEL_NEEDS_MSG ); -} - -/* - It's a control message. Currently we only know about 'cancel' - messages; others are passed on for outside groups, and logged - as ignored for local groups. - */ -static Bool -handleControl( ItemList *control, ItemList *newsgroups, - const char *msgId, const DynStr *art ) -{ - const char *grp; - const char *op; - Bool err = FALSE; - Bool localDone = FALSE; - - op = Itl_first( control ); - if ( op == NULL ) - { - Log_err( "Malformed control line." ); - return TRUE; - } - else if ( strcasecmp( op, "cancel" ) == 0 ) - { - if ( controlCancel( Itl_next( control ) ) ) - localDone = TRUE; - else - return err; - } - - /* Pass on for outside groups. */ - for( grp = Itl_first( newsgroups ); - grp != NULL; - grp = Itl_next( newsgroups ) ) - { - if ( Grp_exists( grp ) && ! Grp_local( grp ) ) - { - if ( ! Out_add( Grp_serv( grp ), msgId, art ) ) - { - Log_err( "Cannot add posted article to outgoing directory" ); - err = TRUE; - } - break; - } - } - - if ( localDone ) - return err; - - /* Log 'can't do' for internal groups. */ - for( grp = Itl_first( newsgroups ); - grp != NULL; - grp = Itl_next( newsgroups ) ) - { - if ( Grp_exists( grp ) && Grp_local( grp ) ) - Log_inf( "Ignoring control '%s' for '%s'.", op, grp ); - } - - return err; -} - -static Bool -postArticle( ItemList *newsgroups, const char *msgId, const DynStr *art ) -{ - const char *grp; - Bool err; - Bool oneLocal; - - err = oneLocal = FALSE; - - /* Run round first doing all local groups. */ - for( grp = Itl_first( newsgroups ); - grp != NULL; - grp = Itl_next( newsgroups ) ) - { - if ( Grp_local( grp ) ) - { - if ( ! oneLocal ) - { - if ( ! Post_open( DynStr_str( art ) ) ) - { - err = TRUE; - break; - } - else - oneLocal = TRUE; - } - - if ( ! Post_add( grp ) ) - err = TRUE; - } - } - if ( oneLocal ) - Post_close(); - - /* Now look for a valid external group. */ - for( grp = Itl_first( newsgroups ); - grp != NULL; - grp = Itl_next( newsgroups ) ) - { - if ( Grp_exists( grp ) && ! Grp_local( grp ) ) - { - if ( ! Out_add( Grp_serv( grp ), msgId, art ) ) - { - Log_err( "Cannot add posted article to outgoing directory" ); - err = TRUE; - } - break; - } - } - - return err; -} - -static Bool -doPost( char *arg, const Cmd *cmd ) -{ - Bool err, replyToFound, dateFound, inHeader; - DynStr *s; - Str line, field, val, msgId, from; - const char* p; - ItemList * newsgroups, *control; - - /* - Get article and make following changes to the header: - - add/replace/cut Message-ID depending on config options - - add Reply-To with content of From, if missing - (some providers overwrite From field) - - rename X-Sender header to X-NOFFLE-X-Sender - (some providers want to insert their own X-Sender) - - For doing this, it is not necessary to parse multiple-line - headers. - */ - putStat( STAT_SEND_ART, "Continue (end with period)" ); - fflush( stdout ); - Log_dbg( "[S FLUSH]" ); - s = new_DynStr( 10000 ); - msgId[ 0 ] = '\0'; - from[ 0 ] = '\0'; - newsgroups = control = NULL; - replyToFound = dateFound = FALSE; - inHeader = TRUE; - while ( getTxtLn( line, &err ) ) - { - if ( inHeader ) - { - p = Utl_stripWhiteSpace( line ); - if ( *p == '\0' ) - { - inHeader = FALSE; - if ( from[ 0 ] == '\0' ) - Log_err( "Posted message has no From field" ); - if ( ! Cfg_removeMsgId() ) - { - if ( Cfg_replaceMsgId() ) - { - Prt_genMsgId( msgId, from, "NOFFLE" ); - Log_dbg( "Replacing Message-ID with '%s'", msgId ); - } - else if ( msgId[ 0 ] == '\0' ) - { - Prt_genMsgId( msgId, from, "NOFFLE" ); - - Log_inf( "Adding missing Message-ID '%s'", msgId ); - } - else if ( ! Prt_isValidMsgId( msgId ) ) - { - Log_ntc( "Replacing invalid Message-ID with '%s'", - msgId ); - Prt_genMsgId( msgId, from, "NOFFLE" ); - } - DynStr_app( s, "Message-ID: " ); - DynStr_appLn( s, msgId ); - } - if ( ! replyToFound && from[ 0 ] != '\0' ) - { - Log_dbg( "Adding Reply-To field to posted message." ); - DynStr_app( s, "Reply-To: " ); - DynStr_appLn( s, from ); - } - if ( ! dateFound ) - { - time_t t; - - time( &t ); - Utl_rfc822Date( t, val ); - DynStr_app( s, "Date: " ); - DynStr_appLn( s, val ); - } - DynStr_appLn( s, p ); - } - else if ( Prt_getField( field, val, p ) ) - { - if ( strcmp( field, "message-id" ) == 0 ) - strcpy( msgId, val ); - else if ( strcmp( field, "from" ) == 0 ) - { - strcpy( from, val ); - DynStr_appLn( s, p ); - } - else if ( strcmp( field, "newsgroups" ) == 0 ) - { - Utl_toLower( val ); - newsgroups = new_Itl ( val, " ," ); - DynStr_appLn( s, p ); - } - else if ( strcmp( field, "control" ) == 0 ) - { - control = new_Itl ( val, " " ); - DynStr_appLn( s, p ); - } - else if ( strcmp( field, "reply-to" ) == 0 ) - { - replyToFound = TRUE; - DynStr_appLn( s, p ); - } - else if ( strcmp( field, "date" ) == 0 ) - { - dateFound = TRUE; - DynStr_appLn( s, p ); - } - else if ( strcmp( field, "x-sender" ) == 0 ) - { - DynStr_app( s, "X-NOFFLE-X-Sender: " ); - DynStr_appLn( s, val ); - } - else - DynStr_appLn( s, p ); - } - else - DynStr_appLn( s, line ); - } - else - DynStr_appLn( s, line ); - } - if ( inHeader ) - Log_err( "Posted message has no body" ); - if ( ! err ) - { - if ( newsgroups == NULL || Itl_count( newsgroups ) == 0 ) - { - Log_err( "Posted message has no valid Newsgroups header field" ); - err = TRUE; - } - else - { - const char *grp; - Bool knownGrp = FALSE; - Bool postAllowedGrp = FALSE; - - /* Check at least one group is known. */ - for( grp = Itl_first( newsgroups ); - grp != NULL; - grp = Itl_next( newsgroups ) ) - { - if ( Grp_exists( grp ) ) - { - knownGrp = TRUE; - switch( Grp_postAllow( grp ) ) - { - case 'n': - break; - case 'm': - /* Can't post to moderated local groups. */ - postAllowedGrp = ! Grp_local( grp ); - break; - default: - postAllowedGrp = TRUE; - } - if ( postAllowedGrp ) - break; - } - } - - if ( ! knownGrp ) - { - - Log_err( "No known group in Newsgroups header field" ); - err = TRUE; - } - else if ( ! postAllowedGrp ) - { - - Log_err( "No group permits posting" ); - err = TRUE; - } - else - { - err = ( control == NULL ) - ? postArticle( newsgroups, msgId, s ) - : handleControl( control, newsgroups, msgId, s ); - } - } - } - if ( err ) - putStat( STAT_POST_FAILED, "Posting failed" ); - else - { - putStat( STAT_POST_OK, "Message posted" ); - if ( Online_true() ) - postArts(); - } - del_Itl( newsgroups ); - del_Itl( control ); - del_DynStr( s ); - return TRUE; -} - -static void -parseRange( const char *s, int *first, int *last, int *numb ) -{ - int r, i; - char* p; - Str t; - - Utl_cpyStr( t, s ); - p = Utl_stripWhiteSpace( t ); - r = sscanf( p, "%d-%d", first, last ); - if ( r < 1 ) - { - *first = serv.artPtr; - *last = serv.artPtr; - } - else if ( r == 1 ) - { - if ( p[ strlen( p ) - 1 ] == '-' ) - *last = Cont_last(); - else - *last = *first; - } - if ( *first < Cont_first() ) - *first = Cont_first(); - if ( *last > Cont_last() ) - *last = Cont_last(); - if ( *first > Cont_last() || *last < Cont_first() ) - *last = *first - 1; - *numb = 0; - for ( i = *first; i <= *last; ++i ) - if ( Cont_validNumb( i ) ) - ++(*numb); -} - -static Bool -doXhdr( char *arg, const Cmd *cmd ) -{ - int first, last, i, n, numb; - enum { SUBJ, FROM, DATE, MSG_ID, REF, BYTES, LINES } what; - const char *p; - const Over *ov; - Str whatStr; - - if ( ! testGrpSelected() ) - return TRUE; - if ( sscanf( arg, "%s", whatStr ) != 1 ) - { - putSyntax( cmd ); - return TRUE; - } - Utl_toLower( whatStr ); - if ( strcmp( whatStr, "subject" ) == 0 ) - what = SUBJ; - else if ( strcmp( whatStr, "from" ) == 0 ) - what = FROM; - else if ( strcmp( whatStr, "date" ) == 0 ) - what = DATE; - else if ( strcmp( whatStr, "message-id" ) == 0 ) - what = MSG_ID; - else if ( strcmp( whatStr, "references" ) == 0 ) - what = REF; - else if ( strcmp( whatStr, "bytes" ) == 0 ) - what = BYTES; - else if ( strcmp( whatStr, "lines" ) == 0 ) - what = LINES; - else - { - putStat( STAT_HEAD_FOLLOWS, "Unknown header (empty list follows)" ); - putEndOfTxt(); - return TRUE; - } - p = Utl_restOfLn( arg, 1 ); - parseRange( p, &first, &last, &numb ); - if ( numb == 0 ) - putStat( STAT_NO_ART_SELECTED, "No articles selected" ); - else - { - putStat( STAT_HEAD_FOLLOWS, "%s header %lu-%lu", - whatStr, first, last ) ; - for ( i = first; i <= last; ++i ) - if ( ( ov = Cont_get( i ) ) ) - { - n = Ov_numb( ov ); - switch ( what ) - { - case SUBJ: - putTxtLn( "%lu %s", n, Ov_subj( ov ) ); - break; - case FROM: - putTxtLn( "%lu %s", n, Ov_from( ov ) ); - break; - case DATE: - putTxtLn( "%lu %s", n, Ov_date( ov ) ); - break; - case MSG_ID: - putTxtLn( "%lu %s", n, Ov_msgId( ov ) ); - break; - case REF: - putTxtLn( "%lu %s", n, Ov_ref( ov ) ); - break; - case BYTES: - putTxtLn( "%lu %d", n, Ov_bytes( ov ) ); - break; - case LINES: - putTxtLn( "%lu %d", n, Ov_lines( ov ) ); - break; - default: - ASSERT( FALSE ); - } - } - putEndOfTxt(); - } - return TRUE; -} - -static Bool -doXpat( char *arg, const Cmd *cmd ) -{ - int first, last, i, n; - enum { SUBJ, FROM, DATE, MSG_ID, REF } what; - const Over *ov; - Str whatStr, pat; - - if ( ! testGrpSelected() ) - return TRUE; - if ( sscanf( arg, "%s %d-%d %s", whatStr, &first, &last, pat ) != 4 ) - { - if ( sscanf( arg, "%s %d- %s", whatStr, &first, pat ) == 3 ) - last = Cont_last(); - else if ( sscanf( arg, "%s %d %s", whatStr, &first, pat ) == 3 ) - last = first; - else - { - putSyntax( cmd ); - return TRUE; - } - } - Utl_toLower( whatStr ); - if ( strcmp( whatStr, "subject" ) == 0 ) - what = SUBJ; - else if ( strcmp( whatStr, "from" ) == 0 ) - what = FROM; - else if ( strcmp( whatStr, "date" ) == 0 ) - what = DATE; - else if ( strcmp( whatStr, "message-id" ) == 0 ) - what = MSG_ID; - else if ( strcmp( whatStr, "references" ) == 0 ) - what = REF; - else - { - putStat( STAT_HEAD_FOLLOWS, "invalid header (empty list follows)" ); - putEndOfTxt(); - return TRUE; - } - putStat( STAT_HEAD_FOLLOWS, "header" ) ; - for ( i = first; i <= last; ++i ) - if ( ( ov = Cont_get( i ) ) ) - { - n = Ov_numb( ov ); - switch ( what ) - { - case SUBJ: - if ( Wld_match( Ov_subj( ov ), pat ) ) - putTxtLn( "%lu %s", n, Ov_subj( ov ) ); - break; - case FROM: - if ( Wld_match( Ov_from( ov ), pat ) ) - putTxtLn( "%lu %s", n, Ov_from( ov ) ); - break; - case DATE: - if ( Wld_match( Ov_date( ov ), pat ) ) - putTxtLn( "%lu %s", n, Ov_date( ov ) ); - break; - case MSG_ID: - if ( Wld_match( Ov_msgId( ov ), pat ) ) - putTxtLn( "%lu %s", n, Ov_msgId( ov ) ); - break; - case REF: - if ( Wld_match( Ov_ref( ov ), pat ) ) - putTxtLn( "%lu %s", n, Ov_ref( ov ) ); - break; - default: - ASSERT( FALSE ); - } - } - putEndOfTxt(); - return TRUE; -} - -static Bool -doSlave( char *arg, const Cmd *cmd ) -{ - putStat( STAT_CMD_OK, "Ok" ); - return TRUE; -} - -static Bool -doStat( char *arg, const Cmd *cmd ) -{ - const char *msgId; - int numb; - - if ( ! whichId( &msgId, &numb, arg ) ) - return TRUE; - if ( numb > 0 ) - putStat( STAT_ART_RETRIEVED, "%ld %s selected", - numb, msgId ); - else - putStat( STAT_ART_RETRIEVED, "0 %s selected", msgId ); - return TRUE; -} - -static Bool -doQuit( char *arg, const Cmd *cmd ) -{ - putStat( STAT_GOODBYE, "Goodbye" ); - return FALSE; -} - -static Bool -doXOver( char *arg, const Cmd *cmd ) -{ - int first, last, i, n; - const Over *ov; - - if ( ! testGrpSelected() ) - return TRUE; - parseRange( arg, &first, &last, &n ); - if ( n == 0 ) - putStat( STAT_NO_ART_SELECTED, "No articles selected" ); - else - { - putStat( STAT_OVERS_FOLLOW, "Overview %ld-%ld", first, last ); - for ( i = first; i <= last; ++i ) - if ( ( ov = Cont_get( i ) ) ) - putTxtLn( "%lu\t%s\t%s\t%s\t%s\t%s\t%d\t%d\t", - Ov_numb( ov ), Ov_subj( ov ), Ov_from( ov ), - Ov_date( ov ), Ov_msgId( ov ), Ov_ref( ov ), - Ov_bytes( ov ), Ov_lines( ov ) ); - putEndOfTxt(); - } - return TRUE; -} - -static void -putFatal( const char *fmt, ... ) -{ - va_list ap; - Str s; - - va_start( ap, fmt ); - vsnprintf( s, MAXCHAR, fmt, ap ); - va_end( ap ); - Log_err( s ); - putStat( STAT_PROGRAM_FAULT, "%s", s ); - fflush( stdout ); - Log_dbg( "[S FLUSH]" ); -} - -/* Parse line, execute command and return FALSE, if it was the quit command. */ -static Bool -parseAndExecute( Str line ) -{ - unsigned int i, n; - Cmd *c; - Str s, arg; - Bool ret; - - if ( sscanf( line, "%s", s ) == 1 ) - { - Utl_toLower( s ); - strcpy( arg, Utl_restOfLn( line, 1 ) ); - n = sizeof( commands ) / sizeof( commands[ 0 ] ); - for ( i = 0, c = commands; i < n; ++i, ++c ) - if ( strcmp( c->name, s ) == 0 ) - { - ret = c->cmdProc( Utl_stripWhiteSpace( arg ), c ); - fflush( stdout ); - Log_dbg( "[S FLUSH]" ); - return ret; - } - } - putStat( STAT_NO_SUCH_CMD, "Command not recognized" ); - fflush( stdout ); - Log_dbg( "[S FLUSH]" ); - return TRUE; -} - -static void -putWelcome( void ) -{ - putStat( STAT_READY_POST_ALLOW, "NNTP server NOFFLE %s", - Cfg_version() ); - fflush( stdout ); - Log_dbg( "[S FLUSH]" ); -} - -static Bool -initServ( void ) -{ - ASSERT( ! serv.running ); - if ( ! Lock_openDatabases() ) - return FALSE; - serv.running = TRUE; - return TRUE; -} - -static void -closeServ( void ) -{ - ASSERT( serv.running ); - serv.running = FALSE; - Lock_closeDatabases(); -} - -void -Serv_run( void ) -{ - Bool done; - int r; - Str line; - struct timeval timeOut; - fd_set readSet; - - putWelcome(); - done = FALSE; - while ( ! done ) - { - FD_ZERO( &readSet ); - FD_SET( STDIN_FILENO, &readSet ); - /* Never hold lock more than 5 seconds (empirically good value, - avoids to close/open databases, if clients sends several - commands, but releases the lock often enough, for allowing - multiple persons to read news at the same time) */ - timeOut.tv_sec = 5; - timeOut.tv_usec = 0; - r = select( STDIN_FILENO + 1, &readSet, NULL, NULL, &timeOut ); - if ( r < 0 ) - done = TRUE; - else if ( r == 0 ) - { - if ( serv.running ) - closeServ(); - } - else /* ( r > 0 ) */ - { - if ( ! serv.running ) - { - if ( ! initServ() ) - { - putFatal( "Cannot init server" ); - done = TRUE; - } - } - if ( ! getLn( line ) ) - { - Log_inf( "Client disconnected. Terminating." ); - done = TRUE; - } - else if ( ! parseAndExecute( line ) ) - done = TRUE; - } - } - if ( serv.running ) - closeServ(); -}
--- a/server.h Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ -/* - server.h - - Be NNTP server on stdin/stdout. - - $Id: server.h 3 2000-01-04 11:35:42Z enz $ -*/ - -#ifndef SERV_H -#define SERV_H - -void Serv_run( void ); - -#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Makefile.am Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,36 @@ +# This Makefile.am is used by automake to generate the Makefile.in, +# which in turn will be used to generate the Makefile. + +bin_PROGRAMS = noffle + +noffle_SOURCES = \ +client.c client.h \ +common.h \ +configfile.c configfile.h \ +content.c content.h \ +control.c control.h \ +database.c database.h \ +dynamicstring.c dynamicstring.h \ +fetch.c fetch.h \ +fetchlist.c fetchlist.h \ +group.c group.h \ +itemlist.c itemlist.h \ +lock.c lock.h \ +log.c log.h \ +noffle.c \ +online.c online.h \ +outgoing.c outgoing.h \ +over.c over.h \ +post.c post.h \ +protocol.c protocol.h \ +pseudo.c pseudo.h \ +request.c request.h \ +server.c server.h \ +util.c util.h \ +wildmat.c wildmat.h + +noffle_LDADD = -lgdbm + +tags: + etags $(noffle_SOURCES) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Makefile.in Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,326 @@ +# Makefile.in generated automatically by automake 1.4 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +# This Makefile.am is used by automake to generate the Makefile.in, +# which in turn will be used to generate the Makefile. + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +CC = @CC@ +CONFIGFILE = @CONFIGFILE@ +DOCDIR = @DOCDIR@ +MAKEINFO = @MAKEINFO@ +PACKAGE = @PACKAGE@ +SPOOLDIR = @SPOOLDIR@ +VERSION = @VERSION@ + +bin_PROGRAMS = noffle + +noffle_SOURCES = client.c client.h common.h configfile.c configfile.h content.c content.h control.c control.h database.c database.h dynamicstring.c dynamicstring.h fetch.c fetch.h fetchlist.c fetchlist.h group.c group.h itemlist.c itemlist.h lock.c lock.h log.c log.h noffle.c online.c online.h outgoing.c outgoing.h over.c over.h post.c post.h protocol.c protocol.h pseudo.c pseudo.h request.c request.h server.c server.h util.c util.h wildmat.c wildmat.h + + +noffle_LDADD = -lgdbm +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = ../config.h +CONFIG_CLEAN_FILES = +PROGRAMS = $(bin_PROGRAMS) + + +DEFS = @DEFS@ -I. -I$(srcdir) -I.. +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +noffle_OBJECTS = client.o configfile.o content.o control.o database.o \ +dynamicstring.o fetch.o fetchlist.o group.o itemlist.o lock.o log.o \ +noffle.o online.o outgoing.o over.o post.o protocol.o pseudo.o \ +request.o server.o util.o wildmat.o +noffle_DEPENDENCIES = +noffle_LDFLAGS = +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ +DIST_COMMON = Makefile.am Makefile.in + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = tar +GZIP_ENV = --best +DEP_FILES = .deps/client.P .deps/configfile.P .deps/content.P \ +.deps/control.P .deps/database.P .deps/dynamicstring.P .deps/fetch.P \ +.deps/fetchlist.P .deps/group.P .deps/itemlist.P .deps/lock.P \ +.deps/log.P .deps/noffle.P .deps/online.P .deps/outgoing.P .deps/over.P \ +.deps/post.P .deps/protocol.P .deps/pseudo.P .deps/request.P \ +.deps/server.P .deps/util.P .deps/wildmat.P +SOURCES = $(noffle_SOURCES) +OBJECTS = $(noffle_OBJECTS) + +all: all-redirect +.SUFFIXES: +.SUFFIXES: .S .c .o .s +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status $(BUILT_SOURCES) + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + + +mostlyclean-binPROGRAMS: + +clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) + +distclean-binPROGRAMS: + +maintainer-clean-binPROGRAMS: + +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(bindir) + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + if test -f $$p; then \ + echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`"; \ + $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \ + else :; fi; \ + done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + list='$(bin_PROGRAMS)'; for p in $$list; do \ + rm -f $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \ + done + +.s.o: + $(COMPILE) -c $< + +.S.o: + $(COMPILE) -c $< + +mostlyclean-compile: + -rm -f *.o core *.core + +clean-compile: + +distclean-compile: + -rm -f *.tab.c + +maintainer-clean-compile: + +noffle: $(noffle_OBJECTS) $(noffle_DEPENDENCIES) + @rm -f noffle + $(LINK) $(noffle_LDFLAGS) $(noffle_OBJECTS) $(noffle_LDADD) $(LIBS) + +ID: $(HEADERS) $(SOURCES) $(LISP) + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + here=`pwd` && cd $(srcdir) \ + && mkid -f$$here/ID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS) + +mostlyclean-tags: + +clean-tags: + +distclean-tags: + -rm -f TAGS ID + +maintainer-clean-tags: + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +subdir = src + +distdir: $(DISTFILES) + here=`cd $(top_builddir) && pwd`; \ + top_distdir=`cd $(top_distdir) && pwd`; \ + distdir=`cd $(distdir) && pwd`; \ + cd $(top_srcdir) \ + && $(AUTOMAKE) --include-deps --build-dir=$$here --srcdir-name=$(top_srcdir) --output-dir=$$top_distdir --gnu src/Makefile + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done + +DEPS_MAGIC := $(shell mkdir .deps > /dev/null 2>&1 || :) + +-include $(DEP_FILES) + +mostlyclean-depend: + +clean-depend: + +distclean-depend: + -rm -rf .deps + +maintainer-clean-depend: + +%.o: %.c + @echo '$(COMPILE) -c $<'; \ + $(COMPILE) -Wp,-MD,.deps/$(*F).pp -c $< + @-cp .deps/$(*F).pp .deps/$(*F).P; \ + tr ' ' '\012' < .deps/$(*F).pp \ + | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \ + >> .deps/$(*F).P; \ + rm .deps/$(*F).pp + +%.lo: %.c + @echo '$(LTCOMPILE) -c $<'; \ + $(LTCOMPILE) -Wp,-MD,.deps/$(*F).pp -c $< + @-sed -e 's/^\([^:]*\)\.o[ ]*:/\1.lo \1.o :/' \ + < .deps/$(*F).pp > .deps/$(*F).P; \ + tr ' ' '\012' < .deps/$(*F).pp \ + | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \ + >> .deps/$(*F).P; \ + rm -f .deps/$(*F).pp +info-am: +info: info-am +dvi-am: +dvi: dvi-am +check-am: all-am +check: check-am +installcheck-am: +installcheck: installcheck-am +install-exec-am: install-binPROGRAMS +install-exec: install-exec-am + +install-data-am: +install-data: install-data-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-am +uninstall-am: uninstall-binPROGRAMS +uninstall: uninstall-am +all-am: Makefile $(PROGRAMS) +all-redirect: all-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: + $(mkinstalldirs) $(DESTDIR)$(bindir) + + +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-binPROGRAMS mostlyclean-compile \ + mostlyclean-tags mostlyclean-depend mostlyclean-generic + +mostlyclean: mostlyclean-am + +clean-am: clean-binPROGRAMS clean-compile clean-tags clean-depend \ + clean-generic mostlyclean-am + +clean: clean-am + +distclean-am: distclean-binPROGRAMS distclean-compile distclean-tags \ + distclean-depend distclean-generic clean-am + +distclean: distclean-am + +maintainer-clean-am: maintainer-clean-binPROGRAMS \ + maintainer-clean-compile maintainer-clean-tags \ + maintainer-clean-depend maintainer-clean-generic \ + distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-am + +.PHONY: mostlyclean-binPROGRAMS distclean-binPROGRAMS clean-binPROGRAMS \ +maintainer-clean-binPROGRAMS uninstall-binPROGRAMS install-binPROGRAMS \ +mostlyclean-compile distclean-compile clean-compile \ +maintainer-clean-compile tags mostlyclean-tags distclean-tags \ +clean-tags maintainer-clean-tags distdir mostlyclean-depend \ +distclean-depend clean-depend maintainer-clean-depend info-am info \ +dvi-am dvi check check-am installcheck-am installcheck install-exec-am \ +install-exec install-data-am install-data install-am install \ +uninstall-am uninstall all-redirect all-am all installdirs \ +mostlyclean-generic distclean-generic clean-generic \ +maintainer-clean-generic clean mostlyclean distclean maintainer-clean + + +tags: + etags $(noffle_SOURCES) + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/client.c Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,888 @@ +/* + client.c + + $Id: client.c 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#if HAVE_CONFIG_H +#include <config.h> +#endif + +#include "client.h" + +#include <stdio.h> +#include <arpa/inet.h> +#include <ctype.h> +#include <netdb.h> +#include <netinet/in.h> +#include <signal.h> +#include <stdarg.h> +#include <sys/socket.h> +#include <unistd.h> +#include "configfile.h" +#include "content.h" +#include "dynamicstring.h" +#include "group.h" +#include "log.h" +#include "over.h" +#include "protocol.h" +#include "pseudo.h" +#include "request.h" +#include "util.h" +#include "wildmat.h" + +/* + Some newsgroups names are reserved for server-specific or server + pseudo groups. We don't want to fetch them. For example, INN + keeps all its control messages in a 'control' hierarchy, and + used the "to." heirarchy for dark and mysterious purposes I think + are to do with newsfeeds. The recommended restrictions are documented + in C.Lindsay, "News Article Format", <draft-ietf-usefor-article-03.txt>. +*/ + +struct ForbiddenGroupName +{ + const char *pattern; + Bool match; +} forbiddenGroupNames[] = +{ + { "*.*", FALSE }, /* Single component */ + { "control.*", TRUE }, /* control.* groups */ + { "to.*", TRUE }, /* control.* groups */ + { "*.all", TRUE }, /* 'all' as a component */ + { "*.all.*", TRUE }, + { "all.*", TRUE }, + { "*.ctl", TRUE }, /* 'ctl' as a component */ + { "*.ctl.*", TRUE }, + { "ctl.*", TRUE } +}; + +struct +{ + FILE* in; /* Receiving socket from server */ + FILE* out; /* Sending socket to server */ + Str lastCmd; /* Last command line */ + Str lastStat; /* Response from server to last command */ + Str grp; /* Selected group */ + int rmtFirst; /* First article of current group at server */ + int rmtLast; /* Last article of current group at server */ + Bool auth; /* Authetication already done? */ + Str serv; /* Remote server name */ +} client = { NULL, NULL, "", "", "", 1, 0, FALSE, "" }; + +static void +logBreakDown( void ) +{ + Log_err( "Connection to remote server lost " + "(article numbers could be inconsistent)" ); +} + +static Bool +getLn( Str line ) +{ + Bool r; + + r = Prt_getLn( line, client.in ); + if ( ! r ) + logBreakDown(); + return r; +} + +static Bool +getTxtLn( Str line, Bool *err ) +{ + Bool r; + + r = Prt_getTxtLn( line, err, client.in ); + if ( *err ) + logBreakDown(); + return r; +} + +static void +putTxtBuf( const char *buf ) +{ + Prt_putTxtBuf( buf, client.out ); + fflush( client.out ); + Log_dbg( "[S FLUSH]" ); +} + +static void +putEndOfTxt( void ) +{ + Prt_putEndOfTxt( client.out ); + fflush( client.out ); + Log_dbg( "[S FLUSH]" ); +} + +static Bool +putCmd( const char *fmt, ... ) +{ + Bool err; + unsigned int n; + Str line; + va_list ap; + + va_start( ap, fmt ); + vsnprintf( line, MAXCHAR, fmt, ap ); + va_end( ap ); + strcpy( client.lastCmd, line ); + Log_dbg( "[S] %s", line ); + n = fprintf( client.out, "%s\r\n", line ); + fflush( client.out ); + Log_dbg( "[S FLUSH]" ); + err = ( n != strlen( line ) + 2 ); + if ( err ) + logBreakDown();; + return ! err; +} + +static Bool +putCmdNoFlush( const char *fmt, ... ) +{ + Bool err; + unsigned int n; + Str line; + va_list ap; + + va_start( ap, fmt ); + vsnprintf( line, MAXCHAR, fmt, ap ); + va_end( ap ); + strcpy( client.lastCmd, line ); + Log_dbg( "[S] %s", line ); + n = fprintf( client.out, "%s\r\n", line ); + err = ( n != strlen( line ) + 2 ); + if ( err ) + logBreakDown();; + return ! err; +} + +static int getStat( void ); + +static Bool +performAuth( void ) +{ + int stat; + Str user, pass; + + Cfg_authInfo( client.serv, user, pass ); + if ( strcmp( user, "" ) == 0 ) + { + Log_err( "No username for authentication set" ); + return FALSE; + } + putCmd( "AUTHINFO USER %s", user ); + stat = getStat(); + if ( stat == STAT_AUTH_ACCEPTED ) + return TRUE; + else if ( stat != STAT_MORE_AUTH_REQUIRED ) + { + Log_err( "Username rejected. Server stat: %s", client.lastStat ); + return FALSE; + } + if ( strcmp( pass, "" ) == 0 ) + { + Log_err( "No password for authentication set" ); + return FALSE; + } + putCmd( "AUTHINFO PASS %s", pass ); + stat = getStat(); + if ( stat != STAT_AUTH_ACCEPTED ) + { + Log_err( "Password rejected. Server status: %s", client.lastStat ); + return FALSE; + } + return TRUE; +} + +static int +getStat( void ) +{ + int result; + Str lastCmd; + + if ( ! getLn( client.lastStat ) ) + result = STAT_PROGRAM_FAULT; + else if ( sscanf( client.lastStat, "%d", &result ) != 1 ) + { + Log_err( "Invalid server status: %s", client.lastStat ); + result = STAT_PROGRAM_FAULT; + } + if ( result == STAT_AUTH_REQUIRED && ! client.auth ) + { + client.auth = TRUE; + strcpy( lastCmd, client.lastCmd ); + if ( performAuth() ) + { + putCmd( lastCmd ); + return getStat(); + } + } + return result; +} + +static void +connectAlarm( int sig ) +{ + return; +} + +static sig_t +installSignalHandler( int sig, sig_t handler ) +{ + struct sigaction act, oldAct; + + act.sa_handler = handler; + sigemptyset( &act.sa_mask ); + act.sa_flags = 0; + if ( sig == SIGALRM ) + act.sa_flags |= SA_INTERRUPT; + else + act.sa_flags |= SA_RESTART; + if ( sigaction( sig, &act, &oldAct ) < 0 ) + return SIG_ERR; + return oldAct.sa_handler; +} + +static Bool +connectWithTimeout( int sock, const struct sockaddr *servAddr, + socklen_t addrLen ) +{ + sig_t oldHandler; + int r, to; + + oldHandler = installSignalHandler( SIGALRM, connectAlarm ); + if ( oldHandler == SIG_ERR ) + { + Log_err( "client.c:connectWithTimeout: signal failed." ); + return FALSE; + } + to = Cfg_connectTimeout(); + if ( alarm( to ) != 0 ) + Log_err( "client.c:connectWithTimeout: Alarm was already set." ); + r = connect( sock, servAddr, addrLen ); + alarm( 0 ); + installSignalHandler( SIGALRM, oldHandler ); + return ( r >= 0 ); +} + +Bool +Client_connect( const char *serv ) +{ + unsigned short int port; + int sock, i; + unsigned int stat; + struct hostent *hp; + char *pStart, *pColon; + Str host, s; + struct sockaddr_in sIn; + + client.auth = FALSE; + Utl_cpyStr( s, serv ); + pStart = Utl_stripWhiteSpace( s ); + pColon = strstr( pStart, ":" ); + if ( pColon == NULL ) + { + strcpy( host, pStart ); + port = 119; + } + else + { + *pColon = '\0'; + strcpy( host, pStart ); + if ( sscanf( pColon + 1, "%hi", &port ) != 1 ) + { + Log_err( "Syntax error in server name: '%s'", serv ); + return FALSE;; + } + if ( port <= 0 || port > 65535 ) + { + Log_err( "Invalid port number %hi. Must be in [1, 65535]", port ); + return FALSE;; + } + } + memset( (void *)&sIn, 0, sizeof( sIn ) ); + hp = gethostbyname( host ); + if ( hp ) + { + for ( i = 0; (hp->h_addr_list)[ i ]; ++i ) + { + sIn.sin_family = hp->h_addrtype; + sIn.sin_port = htons( port ); + sIn.sin_addr = *( (struct in_addr *)hp->h_addr_list[ i ] ); + sock = socket( AF_INET, SOCK_STREAM, 0 ); + if ( sock < 0 ) + break; + if ( ! connectWithTimeout( sock, (struct sockaddr *)&sIn, + sizeof( sIn ) ) ) + { + close( sock ); + break; + } + if ( ! ( client.out = fdopen( sock, "w" ) ) + || ! ( client.in = fdopen( dup( sock ), "r" ) ) ) + { + if ( client.out != NULL ) + fclose( client.out ); + close( sock ); + break; + } + stat = getStat(); + if ( stat == STAT_READY_POST_ALLOW || + stat == STAT_READY_NO_POST_ALLOW ) + { + /* INN needs a MODE READER before it will permit POST. */ + putCmd( "MODE READER" ); + stat = getStat(); + } + switch( stat ) { + case STAT_READY_POST_ALLOW: + case STAT_READY_NO_POST_ALLOW: + Log_inf( "Connected to %s:%d", + inet_ntoa( sIn.sin_addr ), port ); + Utl_cpyStr( client.serv, serv ); + return TRUE; + default: + Log_err( "Bad server stat %d", stat ); + } + shutdown( fileno( client.out ), 0 ); + fclose( client.in ); + fclose( client.out ); + close( sock ); + } + } + return FALSE; +} + +static Bool +isForbiddenGroupName( const char *name ) +{ + int i; + + for ( i = 0; + i < sizeof( forbiddenGroupNames ) / + sizeof( struct ForbiddenGroupName ); + i++ ) + { + /* Negate result of Wld_match to ensure it is 1 or 0. */ + if ( forbiddenGroupNames[i].match != + ( ! Wld_match( name, forbiddenGroupNames[i].pattern ) ) ) + return TRUE; + } + + return FALSE; +} + +static void +processGrps( void ) +{ + char postAllow; + Bool err; + int first, last; + Str grp, line, file; + + while ( getTxtLn( line, &err ) && ! err ) + { + if ( sscanf( line, "%s %d %d %c", + grp, &last, &first, &postAllow ) != 4 ) + { + Log_err( "Unknown reply to LIST or NEWGROUPS: %s", line ); + continue; + } + if ( isForbiddenGroupName( grp ) ) + { + Log_inf( "Group %s forbidden", grp ); + continue; + } + if ( ! Grp_exists( grp ) ) + { + Log_inf( "Registering new group '%s'", grp ); + Grp_create( grp ); + Grp_setRmtNext( grp, first ); + Grp_setServ( grp, client.serv ); + Grp_setPostAllow( grp, postAllow ); + } + else + { + if ( Cfg_servIsPreferential( client.serv, Grp_serv( grp ) ) ) + { + Log_inf( "Changing server for '%s': '%s'->'%s'", + grp, Grp_serv( grp ), client.serv ); + Grp_setServ( grp, client.serv ); + Grp_setRmtNext( grp, first ); + Grp_setPostAllow( grp, postAllow ); + } + else + Log_dbg( "Group %s is already fetched from %s", + grp, Grp_serv( grp ) ); + + } + } + if ( ! err ) + { + snprintf( file, MAXCHAR, "%s/groupinfo.lastupdate", Cfg_spoolDir() ); + Utl_stamp( file ); + } +} + +void +Client_disconnect( void ) +{ + if ( putCmd( "QUIT" ) ) + getStat(); + fclose( client.in ); + fclose( client.out ); + client.in = client.out = NULL; +} + +Bool +Client_getGrps( void ) +{ + if ( ! putCmd( "LIST ACTIVE" ) ) + return FALSE; + if ( getStat() != STAT_GRPS_FOLLOW ) + { + Log_err( "LIST ACTIVE command failed: %s", client.lastStat ); + return FALSE; + } + processGrps(); + return TRUE; +} + +Bool +Client_getDsc( void ) +{ + Bool err; + Str name, line, dsc; + + Log_inf( "Querying group descriptions" ); + if ( ! putCmd( "LIST NEWSGROUPS" ) ) + return FALSE; + if ( getStat() != STAT_GRPS_FOLLOW ) + { + Log_err( "LIST NEWSGROUPS failed: %s", client.lastStat ); + return FALSE; + } + while ( getTxtLn( line, &err ) && ! err ) + { + if ( sscanf( line, "%s", name ) != 1 ) + { + Log_err( "Unknown reply to LIST NEWSGROUPS: %s", line ); + continue; + } + strcpy( dsc, Utl_restOfLn( line, 1 ) ); + if ( Grp_exists( name ) ) + { + Log_dbg( "Description of %s: %s", name, dsc ); + Grp_setDsc( name, dsc ); + } + } + return TRUE; +} + +Bool +Client_getCreationTimes( void ) +{ + Bool err; + Str name, line; + time_t t; + + Log_inf( "Querying group creation times" ); + if ( ! putCmd( "LIST ACTIVE.TIMES" ) ) + return FALSE; + if ( getStat() != STAT_GRPS_FOLLOW ) + { + Log_err( "LIST ACTIVE.TIMES failes: %s", client.lastStat ); + return FALSE; + } + while ( getTxtLn( line, &err ) && ! err ) + { + if ( sscanf( line, "%s %ld", name, &t ) != 2 ) + { + Log_err( "Unknown reply to LIST ACTIVE.TIMES: %s", line ); + continue; + } + if ( Grp_exists( name ) ) + { + Log_inf( "Creation time of %s: %ld", name, t ); + Grp_setCreated( name, t ); + } + } + return TRUE; +} + +Bool +Client_getNewgrps( const time_t *lastTime ) +{ + Str s; + const char *p; + + ASSERT( *lastTime > 0 ); + strftime( s, MAXCHAR, "%Y%m%d %H%M00", gmtime( lastTime ) ); + /* + Do not use century for working with old server software until 2000. + According to newest IETF draft, this is still valid after 2000. + (directly using %y in fmt string causes a Y2K compiler warning) + */ + p = s + 2; + if ( ! putCmd( "NEWGROUPS %s", p ) ) + return FALSE; + if ( getStat() != STAT_NEW_GRP_FOLLOW ) + { + Log_err( "NEWGROUPS command failed: %s", client.lastStat ); + return FALSE; + } + processGrps(); + return TRUE; +} + +static const char * +readField( Str result, const char *p ) +{ + size_t len; + char *r; + + if ( ! p ) + return NULL; + r = result; + *r = '\0'; + len = 0; + while ( *p != '\t' && *p != '\n' ) + { + if ( ! *p ) + return p; + *(r++) = *(p++); + ++len; + if ( len >= MAXCHAR - 1 ) + { + *r = '\0'; + Log_err( "Field in overview too long: %s", r ); + return ++p; + } + } + *r = '\0'; + return ++p; +} + +static Bool +parseOvLn( Str line, int *numb, Str subj, Str from, + Str date, Str msgId, Str ref, size_t *bytes, size_t *lines ) +{ + const char *p; + Str t; + + p = readField( t, line ); + if ( sscanf( t, "%d", numb ) != 1 ) + return FALSE; + p = readField( subj, p ); + p = readField( from, p ); + p = readField( date, p ); + p = readField( msgId, p ); + p = readField( ref, p ); + p = readField( t, p ); + *bytes = 0; + *lines = 0; + if ( sscanf( t, "%d", bytes ) != 1 ) + return TRUE; + p = readField( t, p ); + if ( sscanf( t, "%d", lines ) != 1 ) + return TRUE; + return TRUE; +} + +static const char* +nextXref( const char *pXref, Str grp, int *numb ) +{ + Str s; + const char *pColon, *src; + char *dst; + + src = pXref; + while ( *src && isspace( *src ) ) + ++src; + dst = s; + while ( *src && ! isspace( *src ) ) + *(dst++) = *(src++); + *dst = '\0'; + if ( strlen( s ) == 0 ) + return NULL; + pColon = strstr( s, ":" ); + if ( ! pColon || sscanf( pColon + 1, "%d", numb ) != 1 ) + { + Log_err( "Corrupt Xref at position '%s'", pXref ); + return NULL; + } + Utl_cpyStrN( grp, s, pColon - s ); + Log_dbg( "client.c: nextXref: grp '%s' numb %lu", grp, numb ); + return src; +} + +static Bool +needsMark( const char *ref ) +{ + Bool done = FALSE; + char *p; + Str msgId; + int stat, len; + time_t lastAccess, nowTime; + double limit; + + nowTime = time( NULL ); + limit = Cfg_threadFollowTime() * 24. * 3600.; + while ( ! done ) + { + p = msgId; + while ( *ref != '<' ) + if ( *(ref++) == '\0' ) + return FALSE; + len = 0; + while ( *ref != '>' ) + { + if ( *ref == '\0' || ++len >= MAXCHAR - 1 ) + return FALSE; + *(p++) = *(ref++); + } + *(p++) = '>'; + *p = '\0'; + if ( Db_contains( msgId ) ) + { + stat = Db_stat( msgId ); + lastAccess = Db_lastAccess( msgId ); + if ( ( stat & DB_INTERESTING ) + && difftime( nowTime, lastAccess ) <= limit ) + return TRUE; + } + } + return FALSE; +} + +static void +prepareEntry( Over *ov ) +{ + Str g, t; + const char *msgId, *p, *xref; + int n; + + msgId = Ov_msgId( ov ); + if ( Pseudo_isGeneralInfo( msgId ) ) + Log_dbg( "Skipping general info '%s'", msgId ); + else if ( Db_contains( msgId ) ) + { + xref = Db_xref( msgId ); + Log_dbg( "Entry '%s' already in db with Xref '%s'", msgId, xref ); + p = nextXref( xref, g, &n ); + if ( p == NULL ) + Log_err( "Overview with no group in Xref '%s'", msgId ); + else + { + /* TODO: This code block seems unnessesary. Can we remove it? */ + if ( Cfg_servIsPreferential( client.serv, Grp_serv( g ) ) ) + { + Log_dbg( "Changing first server for '%s' from '%s' to '%s'", + msgId, Grp_serv( g ), client.serv ); + snprintf( t, MAXCHAR, "%s:%d %s", + client.grp, Ov_numb( ov ), xref ); + Db_setXref( msgId, t ); + } + else + { + Log_dbg( "Adding '%s' to Xref of '%s'", g, msgId ); + snprintf( t, MAXCHAR, "%s %s:%d", + xref, client.grp, Ov_numb( ov ) ); + Db_setXref( msgId, t ); + } + } + } + else + { + Log_dbg( "Preparing '%s' in database", msgId ); + Db_prepareEntry( ov, client.grp, Ov_numb( ov ) ); + } +} + +Bool +Client_getOver( int rmtFirst, int rmtLast, FetchMode mode ) +{ + Bool err; + size_t bytes, lines; + int rmtNumb, oldLast, cntMarked; + Over *ov; + Str line, subj, from, date, msgId, ref; + + ASSERT( strcmp( client.grp, "" ) != 0 ); + if ( ! putCmd( "XOVER %lu-%lu", rmtFirst, rmtLast ) ) + return FALSE; + if ( getStat() != STAT_OVERS_FOLLOW ) + { + Log_err( "XOVER command failed: %s", client.lastStat ); + return FALSE; + } + Log_dbg( "Requesting overview for remote %lu-%lu", rmtFirst, rmtLast ); + oldLast = Cont_last(); + cntMarked = 0; + while ( getTxtLn( line, &err ) && ! err ) + { + if ( ! parseOvLn( line, &rmtNumb, subj, from, date, msgId, ref, + &bytes, &lines ) ) + Log_err( "Bad overview line: %s", line ); + else + { + ov = new_Over( subj, from, date, msgId, ref, bytes, lines ); + Cont_app( ov ); + prepareEntry( ov ); + if ( mode == FULL || ( mode == THREAD && needsMark( ref ) ) ) + { + Req_add( client.serv, msgId ); + ++cntMarked; + } + } + Grp_setRmtNext( client.grp, rmtNumb + 1 ); + } + if ( oldLast != Cont_last() ) + Log_inf( "Added %s %lu-%lu", client.grp, oldLast + 1, Cont_last() ); + Log_inf( "%u articles marked for download in %s", cntMarked, client.grp ); + return err; +} + +static void +retrievingFailed( const char* msgId, const char *reason ) +{ + int stat; + + Log_err( "Retrieving of %s failed: %s", msgId, reason ); + stat = Db_stat( msgId ); + Pseudo_retrievingFailed( msgId, reason ); + Db_setStat( msgId, stat | DB_RETRIEVING_FAILED ); +} + +static Bool +retrieveAndStoreArt( const char *msgId ) +{ + Bool err; + DynStr *s = NULL; + Str line; + + Log_inf( "Retrieving %s", msgId ); + s = new_DynStr( 5000 ); + while ( getTxtLn( line, &err ) && ! err ) + DynStr_appLn( s, line ); + if ( ! err ) + Db_storeArt( msgId, DynStr_str( s ) ); + else + retrievingFailed( msgId, "Connection broke down" ); + del_DynStr( s ); + return ! err; +} + +void +Client_retrieveArt( const char *msgId ) +{ + if ( ! Db_contains( msgId ) ) + { + Log_err( "Article '%s' not prepared in database. Skipping.", msgId ); + return; + } + if ( ! ( Db_stat( msgId ) & DB_NOT_DOWNLOADED ) ) + { + Log_inf( "Article '%s' already retrieved. Skipping.", msgId ); + return; + } + if ( ! putCmd( "ARTICLE %s", msgId ) ) + retrievingFailed( msgId, "Connection broke down" ); + else if ( getStat() != STAT_ART_FOLLOWS ) + retrievingFailed( msgId, client.lastStat ); + else + retrieveAndStoreArt( msgId ); +} + +void +Client_retrieveArtList( const char *list ) +{ + Str msgId; + DynStr *s; + const char *p; + + Log_inf( "Retrieving article list" ); + s = new_DynStr( strlen( list ) ); + p = list; + while ( ( p = Utl_getLn( msgId, p ) ) ) + if ( ! Db_contains( msgId ) ) + Log_err( "Skipping retrieving of %s (not prepared in database)", + msgId ); + else if ( ! ( Db_stat( msgId ) & DB_NOT_DOWNLOADED ) ) + Log_inf( "Skipping %s (already retrieved)", msgId ); + else if ( ! putCmdNoFlush( "ARTICLE %s", msgId ) ) + { + retrievingFailed( msgId, "Connection broke down" ); + del_DynStr( s ); + return; + } + else + DynStr_appLn( s, msgId ); + fflush( client.out ); + Log_dbg( "[S FLUSH]" ); + p = DynStr_str( s ); + while ( ( p = Utl_getLn( msgId, p ) ) ) + { + if ( getStat() != STAT_ART_FOLLOWS ) + retrievingFailed( msgId, client.lastStat ); + else if ( ! retrieveAndStoreArt( msgId ) ) + break; + } + del_DynStr( s ); +} + +Bool +Client_changeToGrp( const char* name ) +{ + unsigned int stat; + int estimatedNumb, first, last; + + if ( ! Grp_exists( name ) ) + return FALSE; + if ( ! putCmd( "GROUP %s", name ) ) + return FALSE; + if ( getStat() != STAT_GRP_SELECTED ) + return FALSE; + if ( sscanf( client.lastStat, "%u %d %d %d", + &stat, &estimatedNumb, &first, &last ) != 4 ) + { + Log_err( "Bad server response to GROUP: %s", client.lastStat ); + return FALSE; + } + Utl_cpyStr( client.grp, name ); + client.rmtFirst = first; + client.rmtLast = last; + return TRUE; +} + +void +Client_rmtFirstLast( int *first, int *last ) +{ + *first = client.rmtFirst; + *last = client.rmtLast; +} + +Bool +Client_postArt( const char *msgId, const char *artTxt, + Str errStr ) +{ + if ( ! putCmd( "POST" ) ) + return FALSE; + if ( getStat() != STAT_SEND_ART ) + { + Log_err( "Posting of %s not allowed: %s", msgId, client.lastStat ); + strcpy( errStr, client.lastStat ); + return FALSE; + } + putTxtBuf( artTxt ); + putEndOfTxt(); + if ( getStat() != STAT_POST_OK ) + { + Log_err( "Posting of %s failed: %s", msgId, client.lastStat ); + strcpy( errStr, client.lastStat ); + return FALSE; + } + Log_inf( "Posted %s (Status: %s)", msgId, client.lastStat ); + return TRUE; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/client.h Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,74 @@ +/* + client.h + + Noffle acting as client to other NNTP-servers + + $Id: client.h 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#ifndef CLIENT_H +#define CLIENT_H + +#include <time.h> +#include "common.h" +#include "database.h" +#include "fetchlist.h" + +/* Format of server name: <host>[:<port>] */ +Bool +Client_connect( const char *serv ); + +void +Client_disconnect( void ); + +Bool +Client_getGrps( void ); + +Bool +Client_getDsc( void ); + +Bool +Client_getCreationTimes( void ); + +Bool +Client_getNewgrps( const time_t *lastTime ); + +/* + Change to group <name> at server if it is also in current local grouplist. + Returns TRUE at success. +*/ +Bool +Client_changeToGrp( const Str name ); + +/* + Get overviews <rmtFirst> - <rmtLast> from server and append it + to the current content. For articles that are to be fetched due to FULL + or THREAD mode, store IDs in request database. +*/ +Bool +Client_getOver( int rmtFirst, int rmtLast, FetchMode mode ); + +/* + Retrieve full article text and store it into database. +*/ +void +Client_retrieveArt( const char *msgId ); + +/* + Same, but for a list of msgId's (new line after each msgId). + All ARTICLE commands are sent and then all answers read. +*/ +void +Client_retrieveArtList( const char *list ); + +/* + Store IDs of first and last article of group selected by + Client_changeToGroup at remote server. +*/ +void +Client_rmtFirstLast( int *first, int *last ); + +Bool +Client_postArt( const char *msgId, const char *artTxt, Str errStr ); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/common.h Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,33 @@ +/* + common.h + + Common declarations. + + $Id: common.h 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#ifndef COMMON_H +#define COMMON_H + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define FALSE 0 +#define TRUE !0 +#define MAXCHAR 2048 + +#ifdef DEBUG +#include <assert.h> +#define ASSERT( x ) \ + if ( ! ( x ) ) \ + Log_err( "ASSERTION FAILED: %s line %i", __FILE__, __LINE__ ); \ + assert( x ) +#else +#define ASSERT( x ) +#endif + +typedef int Bool; +typedef char Str[ MAXCHAR ]; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/configfile.c Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,400 @@ +/* + configfile.c + + The following macros must be set, when compiling this file: + CONFIGFILE + SPOOLDIR + VERSION + + $Id: configfile.c 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#if HAVE_CONFIG_H +#include <config.h> +#endif + +#include "configfile.h" + +#include <limits.h> +#include "log.h" +#include "util.h" + +typedef struct +{ + Str name; + Str user; + Str pass; +} +ServEntry; + +typedef struct +{ + Str pattern; + int days; +} +ExpireEntry; + +struct +{ + /* Compile time options */ + const char *spoolDir; + const char *version; + /* Options from the config file */ + int maxFetch; + int autoUnsubscribeDays; + int threadFollowTime; + int connectTimeout; + Bool autoSubscribe; + Bool autoUnsubscribe; + Bool removeMsgId; + Bool replaceMsgId; + Str autoSubscribeMode; + Str mailTo; + int defaultExpire; + int numServ; + int maxServ; + ServEntry *serv; + int servIdx; /* for server enumeration */ + int numExpire; + int maxExpire; + ExpireEntry *expire; + int expireIdx; +} config = +{ + SPOOLDIR, /* spoolDir */ + VERSION, /* version */ + 300, /* maxFetch */ + 30, /* autoUnsubscribeDays */ + 7, /* threadFollowTime */ + 30, /* connectTimeout */ + FALSE, /* autoSubscribe */ + FALSE, /* autoUnsubscribe */ + FALSE, /* removeMsgId */ + TRUE, /* replaceMsgId */ + "over", /* autoSubscribeMode */ + "", /* mailTo */ + 14, /* defaultExpire */ + 0, /* numServ */ + 0, /* maxServ */ + NULL, /* serv */ + 0, /* servIdx */ + 0, /* numExpire */ + 0, /* maxExpire */ + NULL, /* expire */ + 0 /* expireIdx */ +}; + +const char * Cfg_spoolDir( void ) { return config.spoolDir; } +const char * Cfg_version( void ) { return config.version; } + +int Cfg_maxFetch( void ) { return config.maxFetch; } +int Cfg_autoUnsubscribeDays( void ) { return config.autoUnsubscribeDays; } +int Cfg_threadFollowTime( void ) { return config.threadFollowTime; } +int Cfg_connectTimeout( void ) { return config.connectTimeout; } +Bool Cfg_autoUnsubscribe( void ) { return config.autoUnsubscribe; } +Bool Cfg_autoSubscribe( void ) { return config.autoSubscribe; } +Bool Cfg_removeMsgId( void ) { return config.removeMsgId; } +Bool Cfg_replaceMsgId( void ) { return config.replaceMsgId; } +const char * Cfg_autoSubscribeMode( void ) { + return config.autoSubscribeMode; } +const char * Cfg_mailTo( void ) { return config.mailTo; } +int Cfg_expire( void ) { return config.defaultExpire; } + +void +Cfg_beginServEnum( void ) +{ + config.servIdx = 0; +} + +Bool +Cfg_nextServ( Str name ) +{ + if ( config.servIdx >= config.numServ ) + return FALSE; + strcpy( name, config.serv[ config.servIdx ].name ); + ++config.servIdx; + return TRUE; +} + +static Bool +searchServ( const char *name, int *idx ) +{ + int i; + + for ( i = 0; i < config.numServ; ++i ) + if ( strcmp( name, config.serv[ i ].name ) == 0 ) + { + *idx = i; + return TRUE; + } + return FALSE; +} + +Bool +Cfg_servListContains( const char *name ) +{ + int idx; + + return searchServ( name, &idx ); +} + +Bool +Cfg_servIsPreferential( const char *name1, const char *name2 ) +{ + Bool exists1, exists2; + int idx1, idx2; + + exists1 = searchServ( name1, &idx1 ); + exists2 = searchServ( name2, &idx2 ); + if ( exists1 && exists2 ) + return ( idx1 < idx2 ); + if ( exists1 && ! exists2 ) + return TRUE; + /* ( ! exists1 && exists2 ) || ( ! exists1 && ! exists2 ) */ + return FALSE; +} + +void +Cfg_authInfo( const char *name, Str user, Str pass ) +{ + int idx; + + if ( searchServ( name, &idx ) ) + { + strcpy( user, config.serv[ idx ].user ); + strcpy( pass, config.serv[ idx ].pass ); + } + else + { + user[ 0 ] = '\0'; + pass[ 0 ] = '\0'; + } +} + +void +Cfg_beginExpireEnum( void ) +{ + config.expireIdx = 0; +} + +int +Cfg_nextExpire( Str pattern ) +{ + if ( config.expireIdx >= config.numExpire ) + return -1; + strcpy( pattern, config.expire[ config.expireIdx ].pattern ); + return config.expire[ config.expireIdx++ ].days; +} + +static void +logSyntaxErr( const char *line ) +{ + Log_err( "Syntax error in config file: %s", line ); +} + +static void +getBool( Bool *variable, const char *line ) +{ + Str value, name, lowerLn; + + strcpy( lowerLn, line ); + Utl_toLower( lowerLn ); + if ( sscanf( lowerLn, "%s %s", name, value ) != 2 ) + { + logSyntaxErr( line ); + return; + } + + if ( strcmp( value, "yes" ) == 0 ) + *variable = TRUE; + else if ( strcmp( value, "no" ) == 0 ) + *variable = FALSE; + else + Log_err( "Error in config file %s must be yes or no", name ); +} + +static void +getInt( int *variable, int min, int max, const char *line ) +{ + int value; + Str name; + + if ( sscanf( line, "%s %d", name, &value ) != 2 ) + { + logSyntaxErr( line ); + return; + } + if ( value < min || value > max ) + { + Log_err( "Range error in config file %s [%d,%d]", name, min, max ); + return; + } + *variable = value; +} + +static void +getStr( char *variable, const char *line ) +{ + Str dummy; + + if ( sscanf( line, "%s %s", dummy, variable ) != 2 ) + { + logSyntaxErr( line ); + return; + } +} + +static void +getServ( const char *line ) +{ + Str dummy; + int r, len; + ServEntry entry; + + entry.user[ 0 ] = '\0'; + entry.pass[ 0 ] = '\0'; + r = sscanf( line, "%s %s %s %s", + dummy, entry.name, entry.user, entry.pass ); + if ( r < 2 ) + { + logSyntaxErr( line ); + return; + } + len = strlen( entry.name ); + /* To make server name more definit, it is made lowercase and + port is removed, if it is the default port */ + if ( len > 4 && strcmp( entry.name + len - 4, ":119" ) == 0 ) + entry.name[ len - 4 ] = '\0'; + Utl_toLower( entry.name ); + + if ( config.maxServ < config.numServ + 1 ) + { + if ( ! ( config.serv = realloc( config.serv, + ( config.maxServ + 5 ) + * sizeof( ServEntry ) ) ) ) + { + Log_err( "Could not realloc server list" ); + exit( EXIT_FAILURE ); + } + config.maxServ += 5; + } + config.serv[ config.numServ++ ] = entry; +} + +static void +getExpire( const char *line ) +{ + Str dummy; + ExpireEntry entry; + int days; + + /* + The line is either "expire <num>" or "expire <pat> <num>". + The former updates the overall default. + */ + if ( sscanf( line, "%s %s %d", dummy, entry.pattern, &days ) != 3 ) + { + logSyntaxErr( line ); + return; + } + else + { + if ( days < 0 ) + { + Log_err( "Expire days error in '%s': must be integer > 0", + line, days ); + return; + } + + Utl_toLower( entry.pattern ); + entry.days = days; + + if ( config.maxExpire < config.numExpire + 1 ) + { + if ( ! ( config.expire = realloc( config.expire, + ( config.maxExpire + 5 ) + * sizeof( ExpireEntry ) ) ) ) + { + Log_err( "Could not realloc exipre list" ); + exit( EXIT_FAILURE ); + } + config.maxExpire += 5; + } + config.expire[ config.numExpire++ ] = entry; + } +} + +void +Cfg_read( void ) +{ + char *p; + FILE *f; + Str file, line, lowerLine, name, s; + + snprintf( file, MAXCHAR, CONFIGFILE ); + if ( ! ( f = fopen( file, "r" ) ) ) + { + Log_err( "Cannot read %s", file ); + return; + } + while ( fgets( line, MAXCHAR, f ) ) + { + p = Utl_stripWhiteSpace( line ); + Utl_stripComment( p ); + Utl_cpyStr( lowerLine, p ); + Utl_toLower( lowerLine ); + if ( *p == '\0' ) + continue; + if ( sscanf( p, "%s", name ) != 1 ) + Log_err( "Syntax error in %s: %s", file, line ); + else if ( strcmp( "max-fetch", name ) == 0 ) + getInt( &config.maxFetch, 0, INT_MAX, p ); + else if ( strcmp( "auto-unsubscribe-days", name ) == 0 ) + getInt( &config.autoUnsubscribe, -1, INT_MAX, p ); + else if ( strcmp( "thread-follow-time", name ) == 0 ) + getInt( &config.threadFollowTime, 0, INT_MAX, p ); + else if ( strcmp( "connect-timeout", name ) == 0 ) + getInt( &config.connectTimeout, 0, INT_MAX, p ); + else if ( strcmp( "default-expire", name ) == 0 ) + getInt( &config.defaultExpire, 0, INT_MAX, p ); + else if ( strcmp( "auto-subscribe", name ) == 0 ) + getBool( &config.autoSubscribe, p ); + else if ( strcmp( "auto-unsubscribe", name ) == 0 ) + getBool( &config.autoUnsubscribe, p ); + else if ( strcmp( "remove-messageid", name ) == 0 ) + getBool( &config.removeMsgId, p ); + else if ( strcmp( "replace-messageid", name ) == 0 ) + getBool( &config.replaceMsgId, p ); + else if ( strcmp( "auto-subscribe-mode", name ) == 0 ) + { + getStr( s, p ); + Utl_toLower( s ); + if ( strcmp( s, "full" ) != 0 + && strcmp( s, "thread" ) != 0 + && strcmp( s, "over" ) != 0 + && strcmp( s, "off" ) != 0 ) + { + Log_err( "Syntax error in config file: %s", line ); + return; + } + else + strcpy( config.autoSubscribeMode, s ); + } + else if ( strcmp( "server", name ) == 0 ) + /* Server needs line not p, + because password may contain uppercase */ + getServ( line ); + else if ( strcmp( "mail-to", name ) == 0 ) + getStr( config.mailTo, p ); + else if ( strcmp( "expire", name ) == 0 ) + getExpire( p ); + else + Log_err( "Unknown config option: %s", name ); + } + fclose( f ); + if ( ! config.numServ ) + { + Log_err( "Config file contains no server" ); + exit( EXIT_FAILURE ); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/configfile.h Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,53 @@ +/* + configfile.h + + Common declarations and handling of the configuration file. + + $Id: configfile.h 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#ifndef CONFIGFILE_H +#define CONFIGFILE_H + +#include "common.h" + +const char * Cfg_spoolDir( void ); +const char * Cfg_version( void ); + +int Cfg_maxFetch( void ); +int Cfg_autoUnsubscribeDays( void ); +int Cfg_threadFollowTime( void ); +int Cfg_connectTimeout( void ); +Bool Cfg_autoUnsubscribe( void ); +Bool Cfg_autoSubscribe( void ); +Bool Cfg_removeMsgId( void ); +Bool Cfg_replaceMsgId( void ); +const char * Cfg_autoSubscribeMode( void ); /* Can be: full, thread, over */ +const char * Cfg_mailTo( void ); + +/* Begin iteration through the server names */ +void Cfg_beginServEnum( void ); + +/* Save next server name in "name". Return TRUE if name has been was saved. + Return FALSE if there are no more server names. */ +Bool Cfg_nextServ( Str name ); + +Bool Cfg_servListContains( const char *name ); +/* Prefer server earlier in config file. Known servers are always preferential + to unknown servers. */ +Bool Cfg_servIsPreferential( const char *name1, const char *name2 ); +void Cfg_authInfo( const char *name, Str user, Str pass ); + +/* Begin iteration through expire entries. */ +void Cfg_beginExpireEnum( void ); + +/* Put next expire pattern in "pattern" and return its days count. + Return -1 if no more expire patterns. */ +int Cfg_nextExpire( Str pattern ); + +/* Return default expire days. */ +int Cfg_expire( void ); + +void Cfg_read( void ); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/content.c Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,266 @@ +/* + content.c + + $Id: content.c 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#if HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <dirent.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include "common.h" +#include "configfile.h" +#include "group.h" +#include "log.h" +#include "over.h" +#include "pseudo.h" +#include "util.h" + +struct +{ + DIR *dir; /* Directory for browsing through all + groups */ + int vecFirst; /* First article number in vector */ + int first; /* First live article number */ + int last; /* Last article number */ + unsigned int size; /* Number of overviews. */ + unsigned int max; /* Size of elem. */ + Over **elem; /* Ptr to array with ptrs to overviews. + NULL entries for non-existing article numbers + in group. */ + Str name; + Str file; +} cont = { NULL, 1, 1, 0, 0, 0, NULL, "", "" }; + +void +Cont_app( Over *ov ) +{ + if ( cont.max < cont.size + 1 ) + { + if ( ! ( cont.elem = realloc( cont.elem, + ( cont.max + 500 ) + * sizeof( cont.elem[ 0 ] ) ) ) ) + { + Log_err( "Could not realloc overview list" ); + exit( EXIT_FAILURE ); + } + cont.max += 500; + } + ASSERT( cont.vecFirst > 0 ); + if ( ov ) + Ov_setNumb( ov, cont.vecFirst + cont.size ); + cont.elem[ cont.size++ ] = ov; + cont.last = cont.vecFirst + cont.size - 1; +} + +Bool +Cont_validNumb( int n ) +{ + return ( n != 0 && n >= cont.first && n <= cont.last + && cont.elem[ n - cont.vecFirst ] ); +} + +void +Cont_delete( int n ) +{ + Over **ov; + + if ( ! Cont_validNumb( n ) ) + return; + ov = &cont.elem[ n - cont.vecFirst ]; + free( *ov ); + *ov = NULL; +} + +/* Remove all overviews from content. */ +static void +clearCont() +{ + int i; + + for ( i = 0; i < cont.size; ++i ) + del_Over( cont.elem[ i ] ); + cont.size = 0; +} + +static void +setupEmpty( const char *name ) +{ + cont.last = Grp_last( name ); + cont.first = cont.vecFirst = cont.last + 1; + ASSERT( cont.first > 0 ); +} + +/* Extend content list to size "cnt" and append NULL entries. */ +static void +extendCont( int cnt ) +{ + int i, n; + + if ( cont.size < cnt ) + { + n = cnt - cont.size; + for ( i = 0; i < n; ++i ) + Cont_app( NULL ); + } +} + +/* Discard all cached overviews, and read in the overviews of a new group + from its overviews file. */ +void +Cont_read( const char *name ) +{ + FILE *f; + Over *ov; + int numb; + Str line; + + /* Delete old overviews and make room for new ones. */ + cont.vecFirst = 0; + cont.first = 0; + cont.last = 0; + Utl_cpyStr( cont.name, name ); + clearCont(); + + /* read overviews from overview file and store them in the overviews + list */ + snprintf( cont.file, MAXCHAR, "%s/overview/%s", Cfg_spoolDir(), name ); + f = fopen( cont.file, "r" ); + if ( ! f ) + { + Log_dbg( "No group overview file: %s", cont.file ); + setupEmpty( name ); + return; + } + Log_dbg( "Reading %s", cont.file ); + while ( fgets( line, MAXCHAR, f ) ) + { + if ( ! ( ov = Ov_read( line ) ) ) + { + Log_err( "Overview corrupted in %s: %s", name, line ); + continue; + } + numb = Ov_numb( ov ); + if ( numb < cont.first ) + { + Log_err( "Wrong ordering in %s: %s", name, line ); + continue; + } + if ( cont.first == 0 ) + cont.first = cont.vecFirst = numb; + cont.last = numb; + extendCont( numb - cont.first + 1 ); + cont.elem[ numb - cont.first ] = ov; + } + fclose( f ); + + if ( cont.first == 0 ) + setupEmpty( name ); /* Corrupt overview file recovery */ +} + +void +Cont_write( void ) +{ + Bool anythingWritten; + int i; + FILE *f; + const Over *ov; + + + /* Move the first article no. to the first active article */ + while ( ! Cont_validNumb( cont.first ) && cont.first <= cont.last ) + ++cont.first; + + /* Save the overview */ + if ( ! ( f = fopen( cont.file, "w" ) ) ) + { + Log_err( "Could not open %s for writing", cont.file ); + return; + } + Log_dbg( "Writing %s (%lu)", cont.file, cont.size ); + anythingWritten = FALSE; + for ( i = 0; i < cont.size; ++i ) + { + if ( ( ov = cont.elem[ i ] ) ) + { + if ( ! Pseudo_isGeneralInfo( Ov_msgId( ov ) ) ) + { + if ( ! Ov_write( ov, f ) ) + { + Log_err( "Writing of overview line failed" ); + break; + } + else + anythingWritten = TRUE; + } + } + } + fclose( f ); + + /* + If empty, remove the overview file and set set first to one + beyond last to flag said emptiness. + */ + if ( ! anythingWritten ) + { + unlink( cont.file ); + cont.first = cont.last + 1; + } +} + +const Over * +Cont_get( int numb ) +{ + if ( ! Cont_validNumb( numb ) ) + return NULL; + return cont.elem[ numb - cont.vecFirst ]; +} + +int +Cont_first( void ) { return cont.first; } + +int +Cont_last( void ) { return cont.last; } + +const char * +Cont_grp( void ) { return cont.name; } + +Bool +Cont_nextGrp( Str result ) +{ + struct dirent *d; + + ASSERT( cont.dir ); + if ( ! ( d = readdir( cont.dir ) ) ) + { + cont.dir = NULL; + return FALSE; + } + if ( ! d->d_name ) + return FALSE; + Utl_cpyStr( result, d->d_name ); + result[ MAXCHAR - 1 ] = '\0'; + return TRUE; +} + +Bool +Cont_firstGrp( Str result ) +{ + Str name; + + snprintf( name, MAXCHAR, "%s/overview", Cfg_spoolDir() ); + if ( ! ( cont.dir = opendir( name ) ) ) + { + Log_err( "Cannot open %s", name ); + return FALSE; + } + Cont_nextGrp( result ); /* "." */ + Cont_nextGrp( result ); /* ".." */ + return Cont_nextGrp( result ); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/content.h Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,65 @@ +/* + content.h + + Contents of a newsgroup + - list of article overviews for selected group. + + The overviews of all articles of a group are stored in an overview file, + filename SPOOLDIR/overview/GROUPNAME. One entire overview file is read + and cached in memory, at a time. + + $Id: content.h 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#ifndef CONT_H +#define CONT_H + +#include "over.h" + +/* + Try to read overviews from overview file for group <grp>. + Fill with fake articles, if something goes wrong. +*/ +void +Cont_read( const char *grp ); + +/* + Append overview to current list and increment the current + group's last article counter. Ownership of the ptr is transfered + to content +*/ +void +Cont_app( Over *ov ); + +/* Write content */ +void +Cont_write( void ); + +Bool +Cont_validNumb( int numb ); + +const Over * +Cont_get( int numb ); + +void +Cont_delete( int numb ); + +int +Cont_first( void ); + +int +Cont_last( void ); + +const char * +Cont_grp( void ); + +Bool +Cont_nextGrp( Str result ); + +Bool +Cont_firstGrp( Str result ); + +void +Cont_expire( void ); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/control.c Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,74 @@ +/* + control.c + + $Id: control.c 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#include "control.h" +#include <stdio.h> +#include "common.h" +#include "content.h" +#include "database.h" +#include "group.h" +#include "itemlist.h" +#include "log.h" +#include "outgoing.h" + +int +Ctrl_cancel( const char *msgId ) +{ + ItemList *refs; + const char *ref; + Str server; + Bool seen = FALSE; + int res = CANCEL_OK; + + /* See if in outgoing and zap if so. */ + if ( Out_find( msgId, server ) ) + { + Out_remove( server, msgId ); + Log_inf( "'%s' cancelled from outgoing queue for '%s'.\n", + msgId, server ); + seen = TRUE; + } + + if ( ! Db_contains( msgId ) ) + { + Log_inf( "Cancel: '%s' not in database.", msgId ); + return seen ? CANCEL_OK : CANCEL_NO_SUCH_MSG; + } + + /* + Retrieve the Xrefs, remove from each group and then + remove from the database. + */ + refs = new_Itl( Db_xref( msgId ), " " ); + for( ref = Itl_first( refs ); ref != NULL; ref = Itl_next( refs ) ) + { + Str grp; + int no; + + if ( sscanf( ref, "%s:%d", grp, &no ) != 2 ) + break; + + if ( Grp_exists( grp ) ) + { + Cont_read( grp ); + Cont_delete( no ); + Cont_write(); + + if ( ! Grp_local( grp ) && ! seen ) + res = CANCEL_NEEDS_MSG; + + Log_dbg( "Removed '%s' from group '%s'.", msgId, grp ); + } + else + { + Log_inf( "Group '%s' in Xref for '%s' not found.", grp, msgId ); + } + } + del_Itl( refs ); + Db_delete( msgId ); + Log_inf( "Message '%s' cancelled.", msgId ); + return res; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/control.h Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,25 @@ +/* + control.h + + Control actions needed by server and command line. + + $Id: control.h 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#ifndef CONTROL_H +#define CONTROL_H + +#define CANCEL_OK 0 +#define CANCEL_NO_SUCH_MSG 1 +#define CANCEL_NEEDS_MSG 2 + +/* + Cancel a message. Return CANCEL_OK if completely cancelled, + CANCEL_NO_SUCH_MSG if no message with that ID exists, and + CANCEL_NEEDS_MSG if a 'cancel' message should be propagated upstream + to cancel the message elsewhere. + */ +int +Ctrl_cancel( const char *msgId ); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/database.c Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,684 @@ +/* + database.c + + $Id: database.c 49 2000-05-05 21:45:56Z uh1763 $ + + Uses GNU gdbm library. Using Berkeley db (included in libc6) was + cumbersome. It is based on Berkeley db 1.85, which has severe bugs + (e.g. it is not recommended to delete or overwrite entries with + overflow pages). +*/ + +#if HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include "database.h" +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <gdbm.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include "configfile.h" +#include "itemlist.h" +#include "log.h" +#include "protocol.h" +#include "util.h" +#include "wildmat.h" + +static struct Db +{ + GDBM_FILE dbf; + + /* Start string for Xref header line: "Xref: <host>" */ + Str xrefHost; + + /* Msg Id of presently loaded article, empty if none loaded */ + Str msgId; + + /* Status of loaded article */ + int stat; /* Flags */ + time_t lastAccess; + + /* Overview of loaded article */ + Str subj; + Str from; + Str date; + Str ref; + Str xref; + size_t bytes; + size_t lines; + + /* Article text (except for overview header lines) */ + DynStr *txt; + +} db = { NULL, "(unknown)", "", 0, 0, "", "", "", "", "", 0, 0, NULL }; + +static const char * +errMsg( void ) +{ + if ( errno != 0 ) + return strerror( errno ); + return gdbm_strerror( gdbm_errno ); +} + +Bool +Db_open( void ) +{ + Str name, host; + int flags; + + ASSERT( db.dbf == NULL ); + snprintf( name, MAXCHAR, "%s/data/articles.gdbm", Cfg_spoolDir() ); + flags = GDBM_WRCREAT | GDBM_FAST; + + if ( ! ( db.dbf = gdbm_open( name, 512, flags, 0644, NULL ) ) ) + { + Log_err( "Error opening %s for r/w (%s)", name, errMsg() ); + return FALSE; + } + Log_dbg( "%s opened for r/w", name ); + + if ( db.txt == NULL ) + db.txt = new_DynStr( 5000 ); + + gethostname( host, MAXCHAR ); + snprintf( db.xrefHost, MAXCHAR, "Xref: %s", host ); + + return TRUE; +} + +void +Db_close( void ) +{ + ASSERT( db.dbf ); + Log_dbg( "Closing database" ); + gdbm_close( db.dbf ); + db.dbf = NULL; + del_DynStr( db.txt ); + db.txt = NULL; + Utl_cpyStr( db.msgId, "" ); +} + +static Bool +loadArt( const char *msgId ) +{ + static void *dptr = NULL; + + datum key, val; + Str t = ""; + const char *p; + + ASSERT( db.dbf ); + + if ( strcmp( msgId, db.msgId ) == 0 ) + return TRUE; + + key.dptr = (void *)msgId; + key.dsize = strlen( msgId ) + 1; + if ( dptr != NULL ) + { + free( dptr ); + dptr = NULL; + } + val = gdbm_fetch( db.dbf, key ); + dptr = val.dptr; + if ( dptr == NULL ) + { + Log_dbg( "database.c loadArt: gdbm_fetch found no entry" ); + return FALSE; + } + + Utl_cpyStr( db.msgId, msgId ); + p = Utl_getLn( t, (char *)dptr ); + if ( ! p || sscanf( t, "%x", &db.stat ) != 1 ) + { + Log_err( "Entry in database '%s' is corrupt (status)", msgId ); + return FALSE; + } + p = Utl_getLn( t, p ); + if ( ! p || sscanf( t, "%lu", &db.lastAccess ) != 1 ) + { + Log_err( "Entry in database '%s' is corrupt (lastAccess)", msgId ); + return FALSE; + } + p = Utl_getLn( db.subj, p ); + p = Utl_getLn( db.from, p ); + p = Utl_getLn( db.date, p ); + p = Utl_getLn( db.ref, p ); + p = Utl_getLn( db.xref, p ); + if ( ! p ) + { + Log_err( "Entry in database '%s' is corrupt (overview)", msgId ); + return FALSE; + } + p = Utl_getLn( t, p ); + if ( ! p || sscanf( t, "%u", &db.bytes ) != 1 ) + { + Log_err( "Entry in database '%s' is corrupt (bytes)", msgId ); + return FALSE; + } + p = Utl_getLn( t, p ); + if ( ! p || sscanf( t, "%u", &db.lines ) != 1 ) + { + Log_err( "Entry in database '%s' is corrupt (lines)", msgId ); + return FALSE; + } + DynStr_clear( db.txt ); + DynStr_app( db.txt, p ); + return TRUE; +} + +static Bool +saveArt( void ) +{ + DynStr *s; + Str t = ""; + datum key, val; + + if ( strcmp( db.msgId, "" ) == 0 ) + return FALSE; + s = new_DynStr( 5000 ); + snprintf( t, MAXCHAR, "%x", db.stat ); + DynStr_appLn( s, t ); + snprintf( t, MAXCHAR, "%lu", db.lastAccess ); + DynStr_appLn( s, t ); + DynStr_appLn( s, db.subj ); + DynStr_appLn( s, db.from ); + DynStr_appLn( s, db.date ); + DynStr_appLn( s, db.ref ); + DynStr_appLn( s, db.xref ); + snprintf( t, MAXCHAR, "%u", db.bytes ); + DynStr_appLn( s, t ); + snprintf( t, MAXCHAR, "%u", db.lines ); + DynStr_appLn( s, t ); + DynStr_appDynStr( s, db.txt ); + + key.dptr = (void *)db.msgId; + key.dsize = strlen( db.msgId ) + 1; + val.dptr = (void *)DynStr_str( s ); + val.dsize = DynStr_len( s ) + 1; + if ( gdbm_store( db.dbf, key, val, GDBM_REPLACE ) != 0 ) + { + Log_err( "Could not store %s in database (%s)", errMsg() ); + return FALSE; + } + + del_DynStr( s ); + return TRUE; +} + +Bool +Db_prepareEntry( const Over *ov, const char *grp, int numb ) +{ + const char *msgId; + + ASSERT( db.dbf ); + ASSERT( ov ); + ASSERT( grp ); + + msgId = Ov_msgId( ov ); + Log_dbg( "Preparing entry %s", msgId ); + if ( Db_contains( msgId ) ) + Log_err( "Preparing article twice: %s", msgId ); + + db.stat = DB_NOT_DOWNLOADED; + db.lastAccess = time( NULL ); + + Utl_cpyStr( db.msgId, msgId ); + Utl_cpyStr( db.subj, Ov_subj( ov ) ); + Utl_cpyStr( db.from, Ov_from( ov ) ); + Utl_cpyStr( db.date, Ov_date( ov ) ); + Utl_cpyStr( db.ref, Ov_ref( ov ) ); + snprintf( db.xref, MAXCHAR, "%s:%i", grp, numb ); + db.bytes = Ov_bytes( ov ); + db.lines = Ov_lines( ov ); + + DynStr_clear( db.txt ); + + return saveArt(); +} + +Bool +Db_storeArt( const char *msgId, const char *artTxt ) +{ + Str line, lineEx, field, value; + const char *startPos; + + ASSERT( db.dbf ); + + Log_dbg( "Store article %s", msgId ); + if ( ! loadArt( msgId ) ) + { + Log_err( "Cannot find info about '%s' in database", msgId ); + return FALSE; + } + if ( ! ( db.stat & DB_NOT_DOWNLOADED ) ) + { + Log_err( "Trying to store already retrieved article '%s'", msgId ); + return FALSE; + } + db.stat &= ~DB_NOT_DOWNLOADED; + db.stat &= ~DB_RETRIEVING_FAILED; + db.lastAccess = time( NULL ); + + DynStr_clear( db.txt ); + + /* Read header */ + startPos = artTxt; + while ( TRUE ) + { + artTxt = Utl_getHeaderLn( lineEx, artTxt ); + if ( lineEx[ 0 ] == '\0' ) + { + DynStr_appLn( db.txt, lineEx ); + break; + } + /* Remove fields already in overview and handle x-noffle + headers correctly in case of cascading NOFFLEs */ + if ( Prt_getField( field, value, lineEx ) ) + { + if ( strcmp( field, "x-noffle-status" ) == 0 ) + { + if ( strstr( value, "NOT_DOWNLOADED" ) != 0 ) + db.stat |= DB_NOT_DOWNLOADED; + } + else if ( strcmp( field, "message-id" ) != 0 + && strcmp( field, "xref" ) != 0 + && strcmp( field, "references" ) != 0 + && strcmp( field, "subject" ) != 0 + && strcmp( field, "from" ) != 0 + && strcmp( field, "date" ) != 0 + && strcmp( field, "bytes" ) != 0 + && strcmp( field, "lines" ) != 0 + && strcmp( field, "x-noffle-lastaccess" ) != 0 ) + DynStr_appLn( db.txt, lineEx ); + } + } + + /* Read body */ + while ( ( artTxt = Utl_getLn( line, artTxt ) ) ) + if ( ! ( db.stat & DB_NOT_DOWNLOADED ) ) + DynStr_appLn( db.txt, line ); + + return saveArt(); +} + +void +Db_setStat( const char *msgId, int stat ) +{ + if ( loadArt( msgId ) ) + { + db.stat = stat; + saveArt(); + } +} + +void +Db_updateLastAccess( const char *msgId ) +{ + if ( loadArt( msgId ) ) + { + db.lastAccess = time( NULL ); + saveArt(); + } +} + +void +Db_setXref( const char *msgId, const char *xref ) +{ + if ( loadArt( msgId ) ) + { + Utl_cpyStr( db.xref, xref ); + saveArt(); + } +} + +/* Search best position for breaking a line */ +static const char * +searchBreakPos( const char *line, int wantedLength ) +{ + const char *lastSpace = NULL; + Bool lastWasSpace = FALSE; + int len = 0; + + while ( *line != '\0' ) + { + if ( isspace( *line ) ) + { + if ( len > wantedLength && lastSpace != NULL ) + return lastSpace; + if ( ! lastWasSpace ) + lastSpace = line; + lastWasSpace = TRUE; + } + else + lastWasSpace = FALSE; + ++len; + ++line; + } + if ( len > wantedLength && lastSpace != NULL ) + return lastSpace; + return line; +} + +/* Append header line by breaking long line into multiple lines */ +static void +appendLongHeader( DynStr *target, const char *field, const char *value ) +{ + const int wantedLength = 78; + const char *breakPos, *old; + int len; + + len = strlen( field ); + DynStr_appN( target, field, len ); + DynStr_appN( target, " ", 1 ); + old = value; + while ( isspace( *old ) ) + ++old; + breakPos = searchBreakPos( old, wantedLength - len - 1 ); + DynStr_appN( target, old, breakPos - old ); + if ( *breakPos == '\0' ) + { + DynStr_appN( target, "\n", 1 ); + return; + } + DynStr_appN( target, "\n ", 2 ); + while ( TRUE ) + { + old = breakPos; + while ( isspace( *old ) ) + ++old; + breakPos = searchBreakPos( old, wantedLength - 1 ); + DynStr_appN( target, old, breakPos - old ); + if ( *breakPos == '\0' ) + { + DynStr_appN( target, "\n", 1 ); + return; + } + DynStr_appN( target, "\n ", 2 ); + } +} + +const char * +Db_header( const char *msgId ) +{ + static DynStr *s = NULL; + + Str date, t; + int stat; + const char *p; + + if ( s == NULL ) + s = new_DynStr( 5000 ); + else + DynStr_clear( s ); + ASSERT( db.dbf ); + if ( ! loadArt( msgId ) ) + return NULL; + strftime( date, MAXCHAR, "%Y-%m-%d %H:%M:%S", + localtime( &db.lastAccess ) ); + stat = db.stat; + snprintf( t, MAXCHAR, + "Message-ID: %s\n" + "X-NOFFLE-Status:%s%s%s\n" + "X-NOFFLE-LastAccess: %s\n", + msgId, + stat & DB_INTERESTING ? " INTERESTING" : "", + stat & DB_NOT_DOWNLOADED ? " NOT_DOWNLOADED" : "", + stat & DB_RETRIEVING_FAILED ? " RETRIEVING_FAILED" : "", + date ); + DynStr_app( s, t ); + appendLongHeader( s, "Subject:", db.subj ); + appendLongHeader( s, "From:", db.from ); + appendLongHeader( s, "Date:", db.date ); + appendLongHeader( s, "References:", db.ref ); + DynStr_app( s, "Bytes: " ); + snprintf( t, MAXCHAR, "%u", db.bytes ); + DynStr_appLn( s, t ); + DynStr_app( s, "Lines: " ); + snprintf( t, MAXCHAR, "%u", db.lines ); + DynStr_appLn( s, t ); + appendLongHeader( s, db.xrefHost, db.xref ); + p = strstr( DynStr_str( db.txt ), "\n\n" ); + if ( ! p ) + DynStr_appDynStr( s, db.txt ); + else + DynStr_appN( s, DynStr_str( db.txt ), p - DynStr_str( db.txt ) + 1 ); + return DynStr_str( s ); +} + +const char * +Db_body( const char *msgId ) +{ + const char *p; + + if ( ! loadArt( msgId ) ) + return ""; + p = strstr( DynStr_str( db.txt ), "\n\n" ); + if ( ! p ) + return ""; + return ( p + 2 ); +} + +int +Db_stat( const char *msgId ) +{ + if ( ! loadArt( msgId ) ) + return 0; + return db.stat; +} + +time_t +Db_lastAccess( const char *msgId ) +{ + if ( ! loadArt( msgId ) ) + return -1; + return db.lastAccess; +} + +const char * +Db_ref( const char *msgId ) +{ + if ( ! loadArt( msgId ) ) + return ""; + return db.ref; +} + +const char * +Db_xref( const char *msgId ) +{ + if ( ! loadArt( msgId ) ) + return ""; + return db.xref; +} + +const char * +Db_from( const char *msgId ) +{ + if ( ! loadArt( msgId ) ) + return ""; + return db.from; +} + +const char * +Db_date( const char *msgId ) +{ + if ( ! loadArt( msgId ) ) + return ""; + return db.date; +} + +Bool +Db_contains( const char *msgId ) +{ + datum key; + + ASSERT( db.dbf ); + if ( strcmp( msgId, db.msgId ) == 0 ) + return TRUE; + key.dptr = (void*)msgId; + key.dsize = strlen( msgId ) + 1; + return gdbm_exists( db.dbf, key ); +} + +void +Db_delete( const char *msgId ) +{ + datum key; + + ASSERT( db.dbf ); + if ( strcmp( msgId, db.msgId ) == 0 ) + db.msgId[ 0 ] = '\0'; + key.dptr = (void*)msgId; + key.dsize = strlen( msgId ) + 1; + gdbm_delete( db.dbf, key ); +} + +static datum cursor = { NULL, 0 }; + +Bool +Db_first( const char** msgId ) +{ + ASSERT( db.dbf ); + if ( cursor.dptr != NULL ) + { + free( cursor.dptr ); + cursor.dptr = NULL; + } + cursor = gdbm_firstkey( db.dbf ); + *msgId = cursor.dptr; + return ( cursor.dptr != NULL ); +} + +Bool +Db_next( const char** msgId ) +{ + void *oldDptr = cursor.dptr; + + ASSERT( db.dbf ); + if ( cursor.dptr == NULL ) + return FALSE; + cursor = gdbm_nextkey( db.dbf, cursor ); + free( oldDptr ); + *msgId = cursor.dptr; + return ( cursor.dptr != NULL ); +} + +static int +calcExpireDays( const char *msgId ) +{ + const char *xref; + ItemList *refs; + const char *ref; + int res; + + xref = Db_xref( msgId ); + if ( xref[ 0 ] == '\0' ) + return -1; + + res = -1; + refs = new_Itl( xref, " :" ); + for ( ref = Itl_first( refs ); ref != NULL; ref = Itl_next( refs ) ) + { + Str pattern; + int days; + + Cfg_beginExpireEnum(); + while ( ( days = Cfg_nextExpire( pattern ) ) != -1 ) + if ( Wld_match( ref, pattern ) + && ( ( days > res && res != 0 ) || + days == 0 ) ) + { + res = days; + Log_dbg ( "Custom expiry %d for %s in group %s", + days, msgId, ref ); + break; + } + + Itl_next( refs ); /* Throw away group number */ + } + + if ( res == -1 ) + res = Cfg_expire(); + return res; +} + +Bool +Db_expire( void ) +{ + int cntDel, cntLeft, flags, expDays; + time_t nowTime, lastAccess; + const char *msgId; + Str name, tmpName; + GDBM_FILE tmpDbf; + datum key, val; + + if ( ! Db_open() ) + return FALSE; + snprintf( name, MAXCHAR, "%s/data/articles.gdbm", Cfg_spoolDir() ); + snprintf( tmpName, MAXCHAR, "%s/data/articles.gdbm.new", Cfg_spoolDir() ); + flags = GDBM_NEWDB | GDBM_FAST; + if ( ! ( tmpDbf = gdbm_open( tmpName, 512, flags, 0644, NULL ) ) ) + { + Log_err( "Error opening %s for read/write (%s)", errMsg() ); + Db_close(); + return FALSE; + } + Log_inf( "Expiring articles" ); + cntDel = 0; + cntLeft = 0; + nowTime = time( NULL ); + if ( Db_first( &msgId ) ) + do + { + expDays = calcExpireDays( msgId ); + lastAccess = Db_lastAccess( msgId ); + if ( expDays == -1 ) + Log_err( "Internal error: Failed expiry calculation on %s", + msgId ); + else if ( lastAccess == -1 ) + Log_err( "Internal error: Getting lastAccess of %s failed", + msgId ); + else if ( expDays > 0 + && difftime( nowTime, lastAccess ) > + ( (double) expDays * 24 * 3600 ) ) + { +#ifdef DEBUG + Str last, now; + + Utl_cpyStr( last, ctime( &lastAccess ) ); + last[ strlen( last ) - 1 ] = '\0'; + Utl_cpyStr( now, ctime( &nowTime ) ); + last[ strlen( now ) - 1 ] = '\0'; + Log_dbg( "Expiring %s: last access %s, time now %s", + msgId, last, now ); +#endif + ++cntDel; + } + else + { + ++cntLeft; + key.dptr = (void *)msgId; + key.dsize = strlen( msgId ) + 1; + + val = gdbm_fetch( db.dbf, key ); + if ( val.dptr != NULL ) + { + if ( gdbm_store( tmpDbf, key, val, GDBM_INSERT ) != 0 ) + Log_err( "Could not store %s in new database (%s)", + errMsg() ); + free( val.dptr ); + } + } + } + while ( Db_next( &msgId ) ); + Log_inf( "%lu articles deleted, %lu left", cntDel, cntLeft ); + gdbm_close( tmpDbf ); + Db_close(); + rename( tmpName, name ); + return TRUE; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/database.h Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,99 @@ +/* + database.h + + Article database. + + $Id: database.h 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#ifndef DB_H +#define DB_H + +#include <time.h> +#include "common.h" +#include "dynamicstring.h" +#include "over.h" + +/* Article status flags: */ +#define DB_INTERESTING 0x01 /* Was article ever tried to read? */ +#define DB_NOT_DOWNLOADED 0x02 /* Not fully downloaded */ +#define DB_RETRIEVING_FAILED 0x04 /* Retrieving of article failed */ + +/* Open database for r/w. Locking must be done by the caller! */ +Bool +Db_open( void ); + +void +Db_close( void ); + +/* + Creates an database entry for the article from the overview + information. Xref is replaced by grp:numb. +*/ +Bool +Db_prepareEntry( const Over *ov, const char *grp, int numb ); + +/* Store full article. Can only be used after Db_prepareEntry. */ +Bool +Db_storeArt( const char *msgId, const char *artTxt ); + +void +Db_setStat( const char *msgId, int stat ); + +void +Db_updateLastAccess( const char *msgId ); + +/* Xref header line without hostname */ +void +Db_setXref( const char *msgId, const char *xref ); + +const char * +Db_header( const char *msgId ); + +const char * +Db_body( const char *msgId ); + +int +Db_stat( const char *msgId ); + +/* Get last modification time of entry. Returns -1, if msgId non-existing. */ +time_t +Db_lastAccess( const char *msgId ); + +/* Value of the References header line */ +const char * +Db_ref( const char *msgId ); + +/* Value of the From header line */ +const char * +Db_from( const char *msgId ); + +/* Value of the Date header line */ +const char * +Db_date( const char *msgId ); + +/* Xref header line without hostname */ +const char * +Db_xref( const char *msgId ); + +Bool +Db_contains( const char *msgId ); + +/* Delete entry from database */ +void +Db_delete( const char *msgId ); + +Bool +Db_first( const char** msgId ); + +Bool +Db_next( const char** msgId ); + +/* + Expire all articles that have not been accessed for a number of + days determined by their group membership and noffle configuration. + */ +Bool +Db_expire( void ); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/dynamicstring.c Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,120 @@ +/* + dynamicstring.c + + $Id: dynamicstring.c 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#include "dynamicstring.h" + +#include <sys/types.h> +#include "log.h" + +struct DynStr +{ + size_t len; /* Current length (without trailing '\0') */ + size_t max; /* Max length that fits into buffer (incl. trailing '\0') */ + char *str; +}; + +static void +reallocStr( DynStr *self, size_t max ) +{ + if ( max <= self->max ) + return; + if ( ! ( self->str = (char *)realloc( self->str, max ) ) ) + { + Log_err( "Realloc of DynStr failed" ); + exit( EXIT_FAILURE ); + } + if ( self->max == 0 ) /* First allocation? */ + *(self->str) = '\0'; + self->max = max; +} + +DynStr * +new_DynStr( size_t reserve ) +{ + DynStr *s; + + if ( ! ( s = (DynStr *) malloc( sizeof( DynStr ) ) ) ) + { + Log_err( "Allocation of DynStr failed" ); + exit( EXIT_FAILURE ); + } + s->len = 0; + s->max = 0; + s->str = NULL; + if ( reserve > 0 ) + reallocStr( s, reserve + 1 ); + return s; +} + +void +del_DynStr( DynStr *self ) +{ + if ( ! self ) + return; + free( self->str ); + self->str = NULL; + free( self ); +} + +size_t +DynStr_len( const DynStr *self ) +{ + return self->len; +} + +const char * +DynStr_str( const DynStr *self ) +{ + return self->str; +} + +void +DynStr_app( DynStr *self, const char *s ) +{ + size_t len; + + len = strlen( s ); + if ( self->len + len + 1 > self->max ) + reallocStr( self, self->len * 2 + len + 1 ); + strcpy( self->str + self->len, s ); + self->len += len; +} + +void +DynStr_appDynStr( DynStr *self, const DynStr *s ) +{ + if ( self->len + s->len + 1 > self->max ) + reallocStr( self, self->len * 2 + s->len + 1 ); + memcpy( self->str + self->len, s->str, s->len + 1 ); + self->len += s->len; +} + +void +DynStr_appLn( DynStr *self, const char *s ) +{ + DynStr_app( self, s ); + DynStr_app( self, "\n" ); +} + +void +DynStr_appN( DynStr *self, const char *s, size_t n ) +{ + size_t len = self->len; + + if ( len + n + 1 > self->max ) + reallocStr( self, len * 2 + n + 1 ); + strncat( self->str + len, s, n ); + self->len = len + strlen( self->str + len ); +} + +void +DynStr_clear( DynStr *self ) +{ + self->len = 0; + if ( self->max > 0 ) + *(self->str) = '\0'; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/dynamicstring.h Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,54 @@ +/* + dynamicstring.h + + String utilities + + $Id: dynamicstring.h 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#ifndef DYNAMICSTRING_H +#define DYNAMICSTRING_H + +#include <sys/types.h> + +/* A dynamically growing string */ +struct DynStr; +typedef struct DynStr DynStr; + +/* Create new DynStr with given capacity */ +DynStr * +new_DynStr( size_t reserve ); + +/* Delete DynStr */ +void +del_DynStr( DynStr *self ); + +/* Return DynStr's length */ +size_t +DynStr_len( const DynStr *self ); + +/* Return DynStr's content ptr */ +const char * +DynStr_str( const DynStr *self ); + +/* append C-string to DynStr */ +void +DynStr_app( DynStr *self, const char *s ); + +/* append a DynStr to DynStr */ +void +DynStr_appDynStr( DynStr *self, const DynStr *s ); + +/* Append C-string + newline to DynStr */ +void +DynStr_appLn( DynStr *self, const char *s ); + +/* Append a maximum of n characters from C-string s to DynStr self */ +void +DynStr_appN( DynStr *self, const char *s, size_t n ); + +/* Truncate content of DynString to zero length */ +void +DynStr_clear( DynStr *self ); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/fetch.c Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,252 @@ +/* + fetch.c + + $Id: fetch.c 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#if HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include "fetch.h" +#include <errno.h> +#include <time.h> +#include <signal.h> +#include "client.h" +#include "configfile.h" +#include "content.h" +#include "dynamicstring.h" +#include "fetchlist.h" +#include "request.h" +#include "group.h" +#include "log.h" +#include "outgoing.h" +#include "protocol.h" +#include "pseudo.h" +#include "util.h" + +struct Fetch +{ + Bool ready; + Str serv; +} fetch = { FALSE, "" }; + +static Bool +connectToServ( const char *name ) +{ + Log_inf( "Fetch from '%s'", name ); + if ( ! Client_connect( name ) ) + { + Log_err( "Could not connect to %s", name ); + return FALSE; + } + return TRUE; +} + +void +Fetch_getNewGrps( void ) +{ + time_t t; + Str file; + + ASSERT( fetch.ready ); + snprintf( file, MAXCHAR, "%s/groupinfo.lastupdate", Cfg_spoolDir() ); + if ( ! Utl_getStamp( &t, file ) ) + { + Log_err( "Cannot read %s. Please run noffle --query groups", file ); + return; + } + Log_inf( "Updating groupinfo" ); + Client_getNewgrps( &t ); + Utl_stamp( file ); +} + +void +Fetch_getNewArts( const char *name, FetchMode mode ) +{ + int next, first, last, oldLast; + + if ( ! Client_changeToGrp( name ) ) + { + Log_err( "Could not change to group %s", name ); + return; + } + Cont_read( name ); + Client_rmtFirstLast( &first, &last ); + next = Grp_rmtNext( name ); + oldLast = Cont_last(); + if ( next == last + 1 ) + { + Log_inf( "No new articles in %s", name ); + Cont_write(); + Grp_setFirstLast( name, Cont_first(), Cont_last() ); + return; + } + if ( first == 0 && last == 0 ) + { + Log_inf( "No articles in %s", name ); + Cont_write(); + Grp_setFirstLast( name, Cont_first(), Cont_last() ); + return; + } + if ( next > last + 1 ) + { + Log_err( "Article number inconsistent (%s rmt=%lu-%lu, next=%lu)", + name, first, last, next ); + Pseudo_cntInconsistent( name, first, last, next ); + } + else if ( next < first ) + { + Log_inf( "Missing articles (%s first=%lu next=%lu)", + name, first, next ); + Pseudo_missArts( name, first, next ); + } + else + first = next; + if ( last - first > Cfg_maxFetch() ) + { + Log_ntc( "Cutting number of overviews to %lu", Cfg_maxFetch() ); + first = last - Cfg_maxFetch() + 1; + } + Log_inf( "Getting remote overviews %lu-%lu for group %s", + first, last, name ); + Client_getOver( first, last, mode ); + Cont_write(); + Grp_setFirstLast( name, Cont_first(), Cont_last() ); +} + +void +Fetch_updateGrps( void ) +{ + FetchMode mode; + int i, size; + const char* name; + + ASSERT( fetch.ready ); + Fetchlist_read(); + size = Fetchlist_size(); + for ( i = 0; i < size; ++i ) + { + Fetchlist_element( &name, &mode, i ); + if ( strcmp( Grp_serv( name ), fetch.serv ) == 0 ) + Fetch_getNewArts( name, mode ); + } +} + +void +Fetch_getReq_( void ) +{ + Str msgId; + DynStr *list; + const char *p; + int count = 0; + + ASSERT( fetch.ready ); + Log_dbg( "Retrieving articles marked for download" ); + list = new_DynStr( 10000 ); + if ( Req_first( fetch.serv, msgId ) ) + do + { + DynStr_appLn( list, msgId ); + if ( ++count % 20 == 0 ) /* Send max. 20 ARTICLE cmds at once */ + { + p = DynStr_str( list ); + Client_retrieveArtList( p ); + while ( ( p = Utl_getLn( msgId, p ) ) ) + Req_remove( fetch.serv, msgId ); + DynStr_clear( list ); + } + } + while ( Req_next( msgId ) ); + p = DynStr_str( list ); + Client_retrieveArtList( p ); + while ( ( p = Utl_getLn( msgId, p ) ) ) + Req_remove( fetch.serv, msgId ); + del_DynStr( list ); +} + +void +Fetch_postArts( void ) +{ + DynStr *s; + Str msgId, cmd, errStr, sender; + int ret; + const char *txt; + FILE *f; + sig_t lastHandler; + + s = new_DynStr( 10000 ); + if ( Out_first( fetch.serv, msgId, s ) ) + { + Log_inf( "Posting articles" ); + do + { + txt = DynStr_str( s ); + Out_remove( fetch.serv, msgId ); + if ( ! Client_postArt( msgId, txt, errStr ) ) + { + Utl_cpyStr( sender, Cfg_mailTo() ); + if ( strcmp( sender, "" ) == 0 + && ! Prt_searchHeader( txt, "SENDER", sender ) + && ! Prt_searchHeader( txt, "X-NOFFLE-X-SENDER", + sender ) /* see server.c */ + && ! Prt_searchHeader( txt, "FROM", sender ) ) + Log_err( "Article %s has no From/Sender/X-Sender field", + msgId ); + else + { + Log_ntc( "Return article to '%s' by mail", sender ); + snprintf( cmd, MAXCHAR, + "mail -s '[ NOFFLE: Posting failed ]' '%s'", + sender ); + lastHandler = signal( SIGPIPE, SIG_IGN ); + f = popen( cmd, "w" ); + if ( f == NULL ) + Log_err( "Invocation of '%s' failed (%s)", cmd, + strerror( errno ) ); + else + { + fprintf( f, + "\t[ NOFFLE: POSTING OF ARTICLE FAILED ]\n" + "\n" + "\t[ The posting of your article failed. ]\n" + "\t[ Reason of failure at remote server: ]\n" + "\n" + "\t[ %s ]\n" + "\n" + "\t[ Full article text has been appended. ]\n" + "\n" + "%s" + ".\n", + errStr, txt ); + ret = pclose( f ); + if ( ret != EXIT_SUCCESS ) + Log_err( "'%s' exit value %d", cmd, ret ); + signal( SIGPIPE, lastHandler ); + } + } + } + } + while ( Out_next( msgId, s ) ); + } + del_DynStr( s ); +} + +Bool +Fetch_init( const char *serv ) +{ + if ( ! connectToServ( serv ) ) + return FALSE; + Utl_cpyStr( fetch.serv, serv ); + fetch.ready = TRUE; + return TRUE; +} + +void +Fetch_close() +{ + Client_disconnect(); + fetch.ready = FALSE; + Log_inf( "Fetch from '%s' finished", fetch.serv ); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/fetch.h Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,38 @@ +/* + fetch.h + + Do the daily business by using client.c + + $Id: fetch.h 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#ifndef FETCH_H +#define FETCH_H + +#include "common.h" +#include "database.h" +#include "fetchlist.h" + +Bool +Fetch_init( const char *serv ); + +void +Fetch_close( void ); + +void +Fetch_getNewGrps( void ); + +void +Fetch_updateGrps( void ); + +void +Fetch_getReq_( void ); + +void +Fetch_postArts( void ); + +/* Get new articles in group "grp", using fetch mode "mode". */ +void +Fetch_getNewArts( const char *grp, FetchMode mode ); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/fetchlist.c Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,210 @@ +/* + fetchlist.c + + $Id: fetchlist.c 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#if HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include "fetchlist.h" +#include "configfile.h" +#include "log.h" +#include "util.h" + +struct Elem +{ + Str name; + FetchMode mode; +}; + +static struct Fetchlist +{ + struct Elem *elem; + int size; + int max; +} fetchlist = { NULL, 0, 0 }; + +static const char * +getFile( void ) +{ + static Str file; + snprintf( file, MAXCHAR, "%s/fetchlist", Cfg_spoolDir() ); + return file; +} + +static void +clearList( void ) +{ + fetchlist.size = 0; +} + +static int +compareElem( const void *elem1, const void *elem2 ) +{ + struct Elem* e1 = (struct Elem*)elem1; + struct Elem* e2 = (struct Elem*)elem2; + return strcmp( e1->name, e2->name ); +} + +static struct Elem * +searchElem( const char *name ) +{ + int i; + + for ( i = 0; i < fetchlist.size; ++i ) + if ( strcmp( name, fetchlist.elem[ i ].name ) == 0 ) + return &fetchlist.elem[ i ]; + return NULL; +} + +static void +appGrp( const char *name, FetchMode mode ) +{ + struct Elem elem; + + if ( fetchlist.max < fetchlist.size + 1 ) + { + if ( ! ( fetchlist.elem + = realloc( fetchlist.elem, + ( fetchlist.max + 50 ) + * sizeof( fetchlist.elem[ 0 ] ) ) ) ) + { + Log_err( "Could not realloc fetchlist" ); + exit( EXIT_FAILURE ); + } + fetchlist.max += 50; + } + strcpy( elem.name, name ); + elem.mode = mode; + fetchlist.elem[ fetchlist.size++ ] = elem; +} + +void +Fetchlist_read( void ) +{ + FILE *f; + const char *file = getFile(); + char *p; + FetchMode mode = OVER; + Bool valid; + int ret; + Str line, grp, modeStr; + + Log_dbg( "Reading %s", file ); + clearList(); + if ( ! ( f = fopen( file, "r" ) ) ) + { + Log_inf( "No file %s", file ); + return; + } + while ( fgets( line, MAXCHAR, f ) ) + { + p = Utl_stripWhiteSpace( line ); + if ( *p == '#' || *p == '\0' ) + continue; + ret = sscanf( p, "%s %s", grp, modeStr ); + valid = TRUE; + if ( ret < 1 || ret > 2 ) + valid = FALSE; + else if ( ret >= 2 ) + { + if ( strcmp( modeStr, "full" ) == 0 ) + mode = FULL; + else if ( strcmp( modeStr, "thread" ) == 0 ) + mode = THREAD; + else if ( strcmp( modeStr, "over" ) == 0 ) + mode = OVER; + else + valid = FALSE; + } + if ( ! valid ) + { + Log_err( "Invalid entry in %s: %s", file, line ); + continue; + } + appGrp( grp, mode ); + } + fclose( f ); +} + +Bool +Fetchlist_write( void ) +{ + int i; + FILE *f; + const char *file = getFile(); + const char *modeStr = ""; + + qsort( fetchlist.elem, fetchlist.size, sizeof( fetchlist.elem[ 0 ] ), + compareElem ); + if ( ! ( f = fopen( file, "w" ) ) ) + { + Log_err( "Could not open %s for writing", file ); + return FALSE; + } + for ( i = 0; i < fetchlist.size; ++i ) + { + switch ( fetchlist.elem[ i ].mode ) + { + case FULL: + modeStr = "full"; break; + case THREAD: + modeStr = "thread"; break; + case OVER: + modeStr = "over"; break; + } + fprintf( f, "%s %s\n", fetchlist.elem[ i ].name, modeStr ); + } + fclose( f ); + return TRUE; +} + +int +Fetchlist_size( void ) +{ + return fetchlist.size; +} + +Bool +Fetchlist_contains( const char *name ) +{ + return ( searchElem( name ) != NULL ); +} + +Bool +Fetchlist_element( const char **name, FetchMode *mode, int index ) +{ + if ( index < 0 || index >= fetchlist.size ) + return FALSE; + *name = fetchlist.elem[ index ].name; + *mode = fetchlist.elem[ index ].mode; + return TRUE; +} + +Bool +Fetchlist_add( const char *name, FetchMode mode ) +{ + struct Elem *elem = searchElem( name ); + if ( elem == NULL ) + { + appGrp( name, mode ); + return TRUE; + } + strcpy( elem->name, name ); + elem->mode = mode; + return FALSE; +} + +Bool +Fetchlist_remove( const char *name ) +{ + struct Elem *elem = searchElem( name ); + if ( elem == NULL ) + return FALSE; + *elem = fetchlist.elem[ fetchlist.size - 1 ]; + --fetchlist.size; + return TRUE; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/fetchlist.h Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,42 @@ +/* + fetchlist.h + + List of groups that are to be fetched presently. + + $Id: fetchlist.h 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#ifndef FETCHLIST_H +#define FETCHLIST_H + +#include "common.h" + +typedef enum { FULL, THREAD, OVER } FetchMode; + +void +Fetchlist_read( void ); + +/* Invalidates any indices (list is sorted by name before saving) */ +Bool +Fetchlist_write( void ); + +int +Fetchlist_size( void ); + +Bool +Fetchlist_contains( const char *name ); + +/* Get element number index. */ +Bool +Fetchlist_element( const char **name, FetchMode *mode, int index ); + +/* Add entry. Invalidates any indices. Returns TRUE if new entry, FALSE if + entry was overwritten. */ +Bool +Fetchlist_add( const char *name, FetchMode mode ); + +/* Remove entry. Invalidates any indices. Returns FALSE if not found. */ +Bool +Fetchlist_remove( const char *name ); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/group.c Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,375 @@ +/* + group.c + + The group database resides in groupinfo.gdbm and stores all we know about + the groups we know of. One database record is cached in the global struct + grp. Group information is transfered between the grp and the database by + loadGrp() and saveGrp(). This is done transparently. Access to the groups + database is done by group name, by the functions defined in group.h. + + $Id: group.c 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#if HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include "group.h" +#include <gdbm.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/stat.h> +#include "configfile.h" +#include "log.h" +#include "util.h" + +/* currently only used within grp */ +typedef struct +{ + int first; /* number of first article within group */ + int last; /* number of last article within group */ + int rmtNext; + time_t created; + time_t lastAccess; +} Entry; + +struct +{ + Str name; /* name of the group */ + Entry entry; /* more information about this group */ + Str serv; /* server the group resides on */ + Str dsc; /* description of the group */ + char postAllow; /* Posting status */ + GDBM_FILE dbf; +} grp = { "(no grp)", { 0, 0, 0, 0, 0 }, "", "", ' ', NULL }; + +/* + Note: postAllow should really go in Entry. But changing Entry would + make backwards group file format capability tricky, so it goes + where it is, and we test the length of the retrieved record to + determine if it exists. + + Someday if we really change the record format this should be tidied up. + */ + +static const char * +errMsg( void ) +{ + if ( errno != 0 ) + return strerror( errno ); + return gdbm_strerror( gdbm_errno ); +} + +Bool +Grp_open( void ) +{ + Str name; + int flags; + + ASSERT( grp.dbf == NULL ); + snprintf( name, MAXCHAR, "%s/data/groupinfo.gdbm", Cfg_spoolDir() ); + flags = GDBM_WRCREAT | GDBM_FAST; + if ( ! ( grp.dbf = gdbm_open( name, 512, flags, 0644, NULL ) ) ) + { + Log_err( "Error opening %s for r/w (%s)", errMsg() ); + return FALSE; + } + Log_dbg( "%s opened for r/w", name ); + return TRUE; +} + +void +Grp_close( void ) +{ + ASSERT( grp.dbf ); + Log_dbg( "Closing groupinfo" ); + gdbm_close( grp.dbf ); + grp.dbf = NULL; + Utl_cpyStr( grp.name, "" ); +} + +/* Load group info from gdbm-database into global struct grp */ +static Bool +loadGrp( const char *name ) +{ + const char *p; + datum key, val; + + ASSERT( grp.dbf ); + if ( strcmp( grp.name, name ) == 0 ) + return TRUE; + key.dptr = (void *)name; + key.dsize = strlen( name ) + 1; + val = gdbm_fetch( grp.dbf, key ); + if ( val.dptr == NULL ) + return FALSE; + grp.entry = *( (Entry *)val.dptr ); + p = val.dptr + sizeof( grp.entry ); + Utl_cpyStr( grp.serv, p ); + p += strlen( p ) + 1; + Utl_cpyStr( grp.dsc, p ); + p += strlen( p) + 1; + if ( p - val.dptr < val.dsize ) + grp.postAllow = p[ 0 ]; + else + grp.postAllow = 'y'; + Utl_cpyStr( grp.name, name ); + free( val.dptr ); + return TRUE; +} + +/* Save group info from global struct grp into gdbm-database */ +static void +saveGrp( void ) +{ + size_t lenServ, lenDsc, bufLen; + datum key, val; + void *buf; + char *p; + + ASSERT( grp.dbf ); + lenServ = strlen( grp.serv ); + lenDsc = strlen( grp.dsc ); + bufLen = sizeof( grp.entry ) + lenServ + lenDsc + 2 + sizeof( char ); + buf = malloc( bufLen ); + memcpy( buf, (void *)&grp.entry, sizeof( grp.entry ) ); + p = (char *)buf + sizeof( grp.entry ); + strcpy( p, grp.serv ); + p += lenServ + 1; + strcpy( p, grp.dsc ); + p += lenDsc + 1; + p[ 0 ] = grp.postAllow; + key.dptr = (void *)grp.name; + key.dsize = strlen( grp.name ) + 1; + val.dptr = buf; + val.dsize = bufLen; + if ( gdbm_store( grp.dbf, key, val, GDBM_REPLACE ) != 0 ) + Log_err( "Could not save group %s: %s", errMsg() ); + free( buf ); +} + +Bool +Grp_exists( const char *name ) +{ + datum key; + + ASSERT( grp.dbf ); + key.dptr = (void*)name; + key.dsize = strlen( name ) + 1; + return gdbm_exists( grp.dbf, key ); +} + +Bool +Grp_local( const char *name ) +{ + if ( ! loadGrp( name ) ) + return 0; + return ( strcmp( grp.serv, GRP_LOCAL_SERVER_NAME ) == 0 ); +} + +void +Grp_create( const char *name ) +{ + Utl_cpyStr( grp.name, name ); + Utl_cpyStr( grp.serv, "(unknown)" ); + grp.dsc[ 0 ] = '\0'; + grp.entry.first = 1; + grp.entry.last = 0; + grp.entry.rmtNext = 0; + grp.entry.created = 0; + grp.entry.lastAccess = 0; + grp.postAllow = 'y'; + saveGrp(); +} + +void +Grp_delete( const char *name ) +{ + datum key; + + ASSERT( grp.dbf ); + key.dptr = (void*)name; + key.dsize = strlen( name ) + 1; + gdbm_delete( grp.dbf, key ); +} + +const char * +Grp_dsc( const char *name ) +{ + if ( ! loadGrp( name ) ) + return NULL; + return grp.dsc; +} + +const char * +Grp_serv( const char *name ) +{ + static Str serv = ""; + + if ( ! loadGrp( name ) ) + return "[unknown grp]"; + if ( Cfg_servListContains( grp.serv ) + || Grp_local( name ) ) + Utl_cpyStr( serv, grp.serv ); + else + snprintf( serv, MAXCHAR, "[%s]", grp.serv ); + return serv; +} + +int +Grp_first( const char *name ) +{ + if ( ! loadGrp( name ) ) + return 0; + return grp.entry.first; +} + +int +Grp_last( const char *name ) +{ + if ( ! loadGrp( name ) ) + return 0; + return grp.entry.last; +} + +int +Grp_lastAccess( const char *name ) +{ + if ( ! loadGrp( name ) ) + return 0; + return grp.entry.lastAccess; +} + +int +Grp_rmtNext( const char *name ) +{ + if ( ! loadGrp( name ) ) + return 0; + return grp.entry.rmtNext; +} + +time_t +Grp_created( const char *name ) +{ + if ( ! loadGrp( name ) ) + return 0; + return grp.entry.created; +} + +char +Grp_postAllow( const char *name ) +{ + if ( ! loadGrp( name ) ) + return 0; + return grp.postAllow; +} + + +/* Replace group's description (only if value != ""). */ +void +Grp_setDsc( const char *name, const char *value ) +{ + if ( loadGrp( name ) ) + { + Utl_cpyStr( grp.dsc, value ); + saveGrp(); + } +} + +void +Grp_setLocal( const char *name ) +{ + Grp_setServ( name, GRP_LOCAL_SERVER_NAME ); +} + +void +Grp_setServ( const char *name, const char *value ) +{ + if ( loadGrp( name ) ) + { + Utl_cpyStr( grp.serv, value ); + saveGrp(); + } +} + +void +Grp_setCreated( const char *name, time_t value ) +{ + if ( loadGrp( name ) ) + { + grp.entry.created = value; + saveGrp(); + } +} + +void +Grp_setRmtNext( const char *name, int value ) +{ + if ( loadGrp( name ) ) + { + grp.entry.rmtNext = value; + saveGrp(); + } +} + +void +Grp_setLastAccess( const char *name, int value ) +{ + if ( loadGrp( name ) ) + { + grp.entry.lastAccess = value; + saveGrp(); + } +} + +void +Grp_setPostAllow( const char *name, char postAllow ) +{ + if ( loadGrp( name ) ) + { + grp.postAllow = postAllow; + saveGrp(); + } +} + +void +Grp_setFirstLast( const char *name, int first, int last ) +{ + if ( loadGrp( name ) ) + { + grp.entry.first = first; + grp.entry.last = last; + saveGrp(); + } +} + +static datum cursor = { NULL, 0 }; + +Bool +Grp_firstGrp( const char **name ) +{ + ASSERT( grp.dbf ); + if ( cursor.dptr != NULL ) + { + free( cursor.dptr ); + cursor.dptr = NULL; + } + cursor = gdbm_firstkey( grp.dbf ); + *name = cursor.dptr; + return ( cursor.dptr != NULL ); +} + +Bool +Grp_nextGrp( const char **name ) +{ + void *oldDptr = cursor.dptr; + + ASSERT( grp.dbf ); + if ( cursor.dptr == NULL ) + return FALSE; + cursor = gdbm_nextkey( grp.dbf, cursor ); + free( oldDptr ); + *name = cursor.dptr; + return ( cursor.dptr != NULL ); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/group.h Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,114 @@ +/* + group.h + + Groups database + + $Id: group.h 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#ifndef GRP_H +#define GRP_H + +#include <time.h> +#include "common.h" + +#define GRP_LOCAL_SERVER_NAME "(local)" + +/* open group database */ +Bool +Grp_open( void ); + +/* close group database */ +void +Grp_close( void ); + +/* does group exist? */ +Bool +Grp_exists( const char *name ); + +/* is it a local group? */ +Bool +Grp_local( const char *name ); + +/* create new group and save it in database */ +void +Grp_create( const char *name ); + +/* delete a group and its articles from the database. */ +void +Grp_delete( const char *name ); + +/* Get group description */ +const char * +Grp_dsc( const char *name ); + +/* Get server the group resides on */ +const char * +Grp_serv( const char *name ); + +/* + Get article number of the first article in the group + This number is a hint only, it is independent of the + real articles in content.c +*/ +int +Grp_first( const char *name ); + +/* + Get article number of the last article in the group + This number is a hint only, it is independent of the + real articles in content.c +*/ +int +Grp_last( const char *name ); + +int +Grp_lastAccess( const char *name ); + +int +Grp_rmtNext( const char *name ); + +time_t +Grp_created( const char *name ); + +char +Grp_postAllow( const char *name ); + +/* Replace group's description (only if value != ""). */ +void +Grp_setDsc( const char *name, const char *value ); + +void +Grp_setLocal( const char *name ); + +void +Grp_setServ( const char *name, const char *value ); + +void +Grp_setCreated( const char *name, time_t value ); + +void +Grp_setRmtNext( const char *name, int value ); + +void +Grp_setLastAccess( const char *name, int value ); + +void +Grp_setFirstLast( const char *name, int first, int last ); + +void +Grp_setPostAllow( const char *name, char postAllow ); + +/* Begin iterating trough the names of all groups. Store name of first + group (or NULL if there aren't any) in name. Returns whether there are + any groups. */ +Bool +Grp_firstGrp( const char **name ); + +/* Continue iterating trough the names of all groups. Store name of next + group (or NULL if there aren't any more) in name. Returns TRUE on + success, FALSE when there are no more groups. */ +Bool +Grp_nextGrp( const char **name ); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/itemlist.c Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,123 @@ +/* + itemlist.c + + $Id: itemlist.c 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#include "itemlist.h" +#include <ctype.h> +#include <string.h> +#include <stdlib.h> +#include "common.h" +#include "log.h" + +struct ItemList +{ + char *list; + char *separators; + char *next; + size_t count; +}; + +/* Make a new item list. */ +ItemList * +new_Itl( const char *list, const char *separators ) +{ + ItemList * res; + char *p; + Bool inItem; + + res = (ItemList *) malloc( sizeof( ItemList ) ); + if ( res == NULL ) + { + Log_err( "Malloc of ItemList failed." ); + exit( EXIT_FAILURE ); + } + + res->list = (char *) malloc ( strlen(list) + 2 ); + if ( res->list == NULL ) + { + Log_err( "Malloc of ItemList.list failed." ); + exit( EXIT_FAILURE ); + } + strcpy( res->list, list ); + + if ( ( res->separators = strdup( separators ) ) == NULL ) + { + Log_err( "Malloc of ItemList.separators failed." ); + exit( EXIT_FAILURE ); + } + + res->count = 0; + res->next = res->list; + + /* Separate items into strings and have final zero-length string. */ + for( p = res->list, inItem = FALSE; *p != '\0'; p++ ) + { + Bool isSep = ( strchr( separators, p[ 0 ] ) != NULL ); + + if ( inItem ) + { + if ( isSep ) + { + p[ 0 ] = '\0'; + inItem = FALSE; + res->count++; + } + } + else + { + if ( ! isSep ) + inItem = TRUE; + } + } + if ( inItem ) + res->count++; + p[ 1 ] = '\0'; + return res; +} + +/* Delete an item list. */ +void +del_Itl( ItemList *self ) +{ + if ( self == NULL ) + return; + free( self->list ); + free( self->separators ); + free( self ); +} + +/* Get first item. */ +const char * +Itl_first( ItemList *self) +{ + self->next = self->list; + return Itl_next( self ); +} + +/* Get next item or NULL. */ +const char * +Itl_next( ItemList *self ) +{ + const char *res = self->next; + + if ( res[ 0 ] == '\0' ) + return NULL; + + while ( strchr( self->separators, res[ 0 ] ) != NULL ) + res++; + + if ( res[ 0 ] == '\0' && res[ 1 ] == '\0' ) + return NULL; + + self->next += strlen( res ) + 1; + return res; +} + +/* Get count of items in list. */ +size_t +Itl_count( const ItemList *self ) +{ + return self->count; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/itemlist.h Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,38 @@ +/* + itemlist.h + + Copy a string wiht a list of separated items (as found in several + header lines) and provide a convenient way of accessing the + individual items. + + $Id: itemlist.h 49 2000-05-05 21:45:56Z uh1763 $ */ + +#ifndef ITEMLIST_H +#define ITEMLIST_H + +#include <sys/types.h> + +struct ItemList; +typedef struct ItemList ItemList; + +/* Make a new item list. */ +ItemList * +new_Itl( const char *list, const char *separators ); + +/* Delete an item list. */ +void +del_Itl( ItemList *self ); + +/* Get first item. */ +const char * +Itl_first( ItemList *self); + +/* Get next item or NULL. */ +const char * +Itl_next( ItemList *self ); + +/* Get count of items in list. */ +size_t +Itl_count( const ItemList *self ); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lock.c Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,131 @@ +/* + lock.c + + $Id: lock.c 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#if HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include "lock.h" +#include <errno.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <time.h> +#include <unistd.h> +#include "configfile.h" +#include "log.h" +#include "database.h" +#include "group.h" +#include "request.h" + +struct Lock +{ + int lockFd; + Str lockFile; +} lock = { -1, "" }; + + +#ifdef DEBUG +static Bool +testLock( void ) +{ + return ( lock.lockFd != -1 ); +} +#endif + +static Bool +waitLock( void ) +{ + int fd; + struct flock l; + + ASSERT( ! testLock() ); + Log_dbg( "Waiting for lock ..." ); + snprintf( lock.lockFile, MAXCHAR, "%s/lock/global", Cfg_spoolDir() ); + if ( ( fd = open( lock.lockFile, O_WRONLY | O_CREAT, 0644 ) ) < 0 ) + { + Log_err( "Cannot open %s (%s)", lock.lockFile, strerror( errno ) ); + return FALSE; + } + l.l_type = F_WRLCK; + l.l_start = 0; + l.l_whence = SEEK_SET; + l.l_len = 0; + if ( fcntl( fd, F_SETLKW, &l ) < 0 ) + { + Log_err( "Cannot lock %s: %s", lock.lockFile, strerror( errno ) ); + return FALSE; + } + lock.lockFd = fd; + Log_dbg( "Lock successful" ); + return TRUE; +} + +static void +releaseLock( void ) +{ + struct flock l; + + ASSERT( testLock() ); + l.l_type = F_UNLCK; + l.l_start = 0; + l.l_whence = SEEK_SET; + l.l_len = 0; + if ( fcntl( lock.lockFd, F_SETLK, &l ) < 0 ) + Log_err( "Cannot release %s: %s", lock.lockFile, + strerror( errno ) ); + close( lock.lockFd ); + lock.lockFd = -1; + Log_dbg( "Releasing lock" ); +} + + +/* Open all databases and set global lock. */ +Bool +Lock_openDatabases( void ) +{ + if ( ! waitLock() ) + { + Log_err( "Could not get write lock" ); + return FALSE; + } + if ( ! Db_open() ) + { + Log_err( "Could not open database" ); + releaseLock(); + return FALSE; + } + if ( ! Grp_open() ) + { + Log_err( "Could not open groupinfo" ); + Db_close(); + releaseLock(); + return FALSE; + } + if ( ! Req_open() ) + { + Log_err( "Could not initialize request database" ); + Grp_close(); + Db_close(); + releaseLock(); + return FALSE; + } + + return TRUE; +} + + +/* Close all databases and release global lock. */ +void +Lock_closeDatabases( void ) +{ + Grp_close(); + Db_close(); + Req_close(); + releaseLock(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lock.h Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,24 @@ +/* + lock.h + + Opening/Closing of the various databases: article overview database, + articla database, groups database, outgoing articles database, requests + database. Handles global lock. + + $Id: lock.h 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#ifndef LOCK_H +#define LOCK_H + +#include "common.h" + +/* Open all databases and set global lock. */ +Bool +Lock_openDatabases( void ); + +/* Close all databases and release global lock. */ +void +Lock_closeDatabases( void ); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/log.c Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,75 @@ +/* + log.c + + $Id: log.c 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#include <syslog.h> +#include <stdarg.h> +#include "common.h" + +#define MAXLENGTH 240 + +struct +{ + Bool interactive; +} log = { FALSE }; + +void +Log_init( Str name, Bool interactive, int facility ) +{ + int option = LOG_PID | LOG_CONS; + + log.interactive = interactive; + openlog( name, option, facility ); +} + +#define DO_LOG( LEVEL ) \ + va_list ap; \ + Str t; \ + \ + va_start( ap, fmt ); \ + vsnprintf( t, MAXCHAR, fmt, ap ); \ + if ( MAXLENGTH < MAXCHAR ) \ + t[ MAXLENGTH ] = '\0'; \ + syslog( LEVEL, "%s", t ); \ + if ( log.interactive ) \ + fprintf( stderr, "%s\n", t ); \ + va_end( ap ); + +void +Log_inf( const char *fmt, ... ) +{ + DO_LOG( LOG_INFO ); +} + +void +Log_err( const char *fmt, ... ) +{ + DO_LOG( LOG_ERR ); +} + +/* Ensure the condition "cond" is true; otherwise log an error and return 1 */ +int +Log_check(int cond, const char *fmt, ... ) +{ + if (!cond) { + DO_LOG( LOG_ERR ); + return 1; + } + return 0; +} + +void +Log_ntc( const char *fmt, ... ) +{ + DO_LOG( LOG_NOTICE ); +} + +void +Log_dbg( const char *fmt, ... ) +{ +#ifdef DEBUG + DO_LOG( LOG_DEBUG ); +#endif +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/log.h Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,43 @@ +/* + log.h + + Print log messages to syslog, stdout/stderr. + + $Id: log.h 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#ifndef LOG_H +#define LOG_H + +#include "common.h" + +/* + Initialise logging (required before using any log functions). + name: program name for syslog + interactive: print messages also to stderr/stdout + facility: like syslog +*/ +void +Log_init( Str name, Bool interactive, int facility ); + +/* Log level info */ +void +Log_inf( const char *fmt, ... ); + +/* Log level error */ +void +Log_err( const char *fmt, ... ); + +/* Check for cond being true. Otherwise log an error, and return 1. */ +int +Log_check(int cond, const char *fmt, ... ); + +/* Log level notice */ +void +Log_ntc( const char *fmt, ... ); + +/* Log only if DEBUG is defined. */ +void +Log_dbg( const char *fmt, ... ); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/noffle.c Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,798 @@ +/* + noffle.c + + Main program. Implements specified actions, but running as server, which + is done by Serv_run(), declared in server.h. + + Locking policy: lock access to databases while noffle is running, but + not as server. If noffle runs as server, locking is performed while + executing NNTP commands, but temporarily released if no new command is + received for some seconds (to allow multiple clients connect at the same + time). + + $Id: noffle.c 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#include <ctype.h> +#include <errno.h> +#include <getopt.h> +#include <signal.h> +#include <sys/resource.h> +#include <syslog.h> +#include <unistd.h> +#include "client.h" +#include "common.h" +#include "content.h" +#include "control.h" +#include "configfile.h" +#include "database.h" +#include "fetch.h" +#include "fetchlist.h" +#include "group.h" +#include "itemlist.h" +#include "log.h" +#include "online.h" +#include "outgoing.h" +#include "over.h" +#include "pseudo.h" +#include "util.h" +#include "server.h" +#include "request.h" +#include "lock.h" + +struct Noffle +{ + Bool queryGrps; + Bool queryDsc; + Bool queryTimes; + Bool interactive; +} noffle = { FALSE, FALSE, FALSE, TRUE }; + +static void +doArt( const char *msgId ) +{ + const char *id; + + if ( strcmp( msgId, "all" ) == 0 ) + { + if ( ! Db_first( &id ) ) + fprintf( stderr, "Database empty.\n" ); + else + do + { + printf( "From %s %s\n" + "%s\n" + "%s\n", + Db_from( id ), Db_date( id ), + Db_header( id ), + Db_body( id ) ); + } + while ( Db_next( &id ) ); + } + else + { + if ( ! Db_contains( msgId ) ) + fprintf( stderr, "Not in database.\n" ); + else + printf( "%s\n%s", Db_header( msgId ), Db_body( msgId ) ); + } +} + +static void +doCancel( const char *msgId ) +{ + switch( Ctrl_cancel( msgId ) ) + { + case CANCEL_NO_SUCH_MSG: + printf( "No such message '%s'.\n", msgId ); + break; + + case CANCEL_OK: + printf( "Message '%s' cancelled.\n", msgId ); + break; + + case CANCEL_NEEDS_MSG: + printf( "Message '%s' cancelled in local database only.\n", msgId ); + break; + } +} + +/* List articles requested from one particular server */ +static void +listRequested1( const char* serv ) +{ + Str msgid; + + if ( ! Req_first( serv, msgid ) ) + return; + do + printf( "%s %s\n", msgid, serv ); + while ( Req_next( msgid ) ); +} + +/* List requested articles. List for all servers if serv = "all" or serv = + NULL. */ +void +doRequested( const char *arg ) +{ + Str serv; + + if ( ! arg || ! strcmp( arg, "all" ) ) + { + Cfg_beginServEnum(); + while ( Cfg_nextServ( serv ) ) + listRequested1( serv ); + } + else + listRequested1( arg ); +} + + +static void +doDb( void ) +{ + const char *msgId; + + if ( ! Db_first( &msgId ) ) + fprintf( stderr, "Database empty.\n" ); + else + do + printf( "%s\n", msgId ); + while ( Db_next( &msgId ) ); +} + +static void +doFetch( void ) +{ + Str serv; + + Cfg_beginServEnum(); + while ( Cfg_nextServ( serv ) ) + if ( Fetch_init( serv ) ) + { + Fetch_postArts(); + + Fetch_getNewGrps(); + + /* Get overviews of new articles and store IDs of new articles + that are to be fetched becase of FULL or THREAD mode in the + request database. */ + Fetch_updateGrps(); + + /* get requested articles */ + Fetch_getReq_(); + + Fetch_close(); + } +} + +static void +doQuery( void ) +{ + Str serv; + + Cfg_beginServEnum(); + while ( Cfg_nextServ( serv ) ) + if ( Fetch_init( serv ) ) + { + if ( noffle.queryGrps ) + Client_getGrps(); + if ( noffle.queryDsc ) + Client_getDsc(); + if ( noffle.queryTimes ) + Client_getCreationTimes(); + Fetch_close(); + } +} + +/* Expire all overviews not in database */ +static void +expireContents( void ) +{ + const Over *ov; + int i; + int cntDel, cntLeft; + Str grp; + Bool autoUnsubscribe; + int autoUnsubscribeDays; + time_t now = time( NULL ), maxAge = 0; + const char *msgId; + + autoUnsubscribe = Cfg_autoUnsubscribe(); + autoUnsubscribeDays = Cfg_autoUnsubscribeDays(); + maxAge = Cfg_autoUnsubscribeDays() * 24 * 3600; + if ( ! Cont_firstGrp( grp ) ) + return; + Log_inf( "Expiring overviews not in database" ); + do + { + if ( ! Grp_exists( grp ) ) + Log_err( "Overview file for unknown group %s exists", grp ); + else + { + cntDel = cntLeft = 0; + Cont_read( grp ); + for ( i = Cont_first(); i <= Cont_last(); ++i ) + if ( ( ov = Cont_get( i ) ) ) + { + msgId = Ov_msgId( ov ); + if ( ! Db_contains( msgId ) ) + { + Cont_delete( i ); + ++cntDel; + } + else + ++cntLeft; + } + if ( ! Grp_local( grp ) + && autoUnsubscribe + && difftime( now, Grp_lastAccess( grp ) ) > maxAge ) + { + Log_ntc( "Auto-unsubscribing from %s after %d " + "days without access", + grp, autoUnsubscribeDays ); + Pseudo_autoUnsubscribed( grp, autoUnsubscribeDays ); + Fetchlist_read(); + Fetchlist_remove( grp ); + Fetchlist_write(); + } + Cont_write(); + Grp_setFirstLast( grp, Cont_first(), Cont_last() ); + Log_inf( "%ld overviews deleted from group %s, %ld left (%ld-%ld)", + cntDel, grp, cntLeft, Grp_first( grp ), Grp_last( grp ) ); + } + } + while ( Cont_nextGrp( grp ) ); +} + +static void +doExpire( void ) +{ + Db_close(); + Db_expire(); + if ( ! Db_open() ) + return; + expireContents(); +} + +static void +doCreateLocalGroup( const char * name ) +{ + Str grp; + + Utl_cpyStr( grp, name ); + Utl_toLower( grp ); + name = Utl_stripWhiteSpace( grp ); + + if ( Grp_exists( name ) ) + fprintf( stderr, "'%s' already exists.\n", name ); + else + { + Log_inf( "Creating new local group '%s'", name ); + Grp_create( name ); + Grp_setLocal( name ); + printf( "New local group '%s' created.\n", name ); + } +} + +static void +doDeleteLocalGroup( const char * name ) +{ + Str grp; + + Utl_cpyStr( grp, name ); + Utl_toLower( grp ); + name = Utl_stripWhiteSpace( grp ); + + if ( ! Grp_exists( name ) ) + fprintf( stderr, "'%s' does not exist.\n", name ); + else + { + int i; + + Log_inf( "Deleting group '%s'", name ); + + /* + Delete all articles that are only in the group. Check the + article Xref for more than one group. + */ + Cont_read( name ); + for ( i = Cont_first(); i <= Cont_last(); i++ ) + { + const Over *over; + Bool toDelete; + Str msgId; + + over = Cont_get( i ); + toDelete = TRUE; + if ( over != NULL ) + { + ItemList * xref; + + Utl_cpyStr( msgId, Ov_msgId( over ) ); + xref = new_Itl( Db_xref( msgId ), " " ); + if ( Itl_count( xref ) > 1 ) + toDelete = FALSE; + del_Itl( xref ); + } + Cont_delete( i ); + if ( toDelete ) + Db_delete( msgId ); + } + Cont_write(); + Grp_delete( name ); + printf( "Group '%s' deleted.\n", name ); + } +} + +static void +doList( void ) +{ + FetchMode mode; + int i, size; + const char *name, *modeStr = ""; + + Fetchlist_read(); + size = Fetchlist_size(); + if ( size == 0 ) + fprintf( stderr, "Fetch list is empty.\n" ); + else + for ( i = 0; i < size; ++i ) + { + Fetchlist_element( &name, &mode, i ); + switch ( mode ) + { + case FULL: + modeStr = "full"; break; + case THREAD: + modeStr = "thread"; break; + case OVER: + modeStr = "over"; break; + } + printf( "%s %s %s\n", name, Grp_serv( name ), modeStr ); + } +} + +/* A modify command. argc/argv start AFTER '-m'. */ +static Bool +doModify( const char *cmd, int argc, char **argv ) +{ + const char *grp; + + if ( argc < 2 ) + { + fprintf( stderr, "Insufficient arguments to -m\n" ); + return FALSE; + + } + else if ( strcmp( cmd, "desc" ) != 0 + && strcmp( cmd, "post" ) != 0 ) + { + fprintf( stderr, "Unknown argument -m %s\n", optarg ); + return FALSE; + } + + grp = argv[ 0 ]; + argv++; + argc--; + + if ( strcmp( cmd, "desc" ) == 0 ) + { + Str desc; + + Utl_cpyStr( desc, *( argv++ ) ); + while ( --argc > 0 ) + { + Utl_catStr( desc, " " ); + Utl_catStr( desc, *( argv++ ) ); + } + + Grp_setDsc( grp, desc ); + } + else + { + char c; + + if ( ! Grp_local( grp ) ) + { + fprintf( stderr, "%s is not a local group\n", grp ); + return FALSE; + } + + c = **argv; + if ( c == 'y' || c == 'm' || c == 'n' ) + Grp_setPostAllow( grp, c ); + else + { + fprintf( stderr, "Access must be 'y', 'n' or 'm'" ); + return FALSE; + } + } + + return TRUE; +} + +static void +doGrps( void ) +{ + const char *g; + Str dateLastAccess, dateCreated; + time_t lastAccess, created; + + if ( Grp_firstGrp( &g ) ) + do + { + lastAccess = Grp_lastAccess( g ); + created = Grp_created( g ); + ASSERT( lastAccess >= 0 ); + ASSERT( created >= 0 ); + strftime( dateLastAccess, MAXCHAR, "%Y-%m-%d %H:%M:%S", + localtime( &lastAccess ) ); + strftime( dateCreated, MAXCHAR, "%Y-%m-%d %H:%M:%S", + localtime( &created ) ); + printf( "%s\t%s\t%i\t%i\t%i\t%c\t%s\t%s\t%s\n", + g, Grp_serv( g ), Grp_first( g ), Grp_last( g ), + Grp_rmtNext( g ), Grp_postAllow( g ), dateCreated, + dateLastAccess, Grp_dsc( g ) ); + } + while ( Grp_nextGrp( &g ) ); +} + +static Bool +doSubscribe( const char *name, FetchMode mode ) +{ + if ( ! Grp_exists( name ) ) + { + fprintf( stderr, "%s is not available at remote servers.\n", name ); + return FALSE; + } + Fetchlist_read(); + if ( Fetchlist_add( name, mode ) ) + printf( "Adding %s to fetch list in %s mode.\n", + name, mode == FULL ? "full" : mode == THREAD ? + "thread" : "overview" ); + else + printf( "%s is already in fetch list. Mode is now: %s.\n", + name, mode == FULL ? "full" : mode == THREAD ? + "thread" : "overview" ); + if ( ! Fetchlist_write() ) + fprintf( stderr, "Could not save fetchlist.\n" ); + return TRUE; +} + +static void +doUnsubscribe( const char *name ) +{ + Fetchlist_read(); + if ( ! Fetchlist_remove( name ) ) + printf( "%s is not in fetch list.\n", name ); + else + printf( "%s removed from fetch list.\n", name ); + if ( ! Fetchlist_write() ) + fprintf( stderr, "Could not save fetchlist.\n" ); +} + +static void +printUsage( void ) +{ + static const char *msg = + "Usage: noffle <option>\n" + "Option is one of the following:\n" + " -a | --article <msg id>|all Show article(s) in database\n" + " -c | --cancel <msg id> Remove article from database\n" + " -C | --create <grp> Create a local group\n" + " -d | --database Show content of article database\n" + " -D | --delete <grp> Delete a group\n" + " -e | --expire Expire articles\n" + " -f | --fetch Get newsfeed from server/post articles\n" + " -g | --groups Show all groups available at server\n" + " -h | --help Show this text\n" + " -l | --list List groups on fetch list\n" + " -m | --modify desc <grp> <desc> Modify a group description\n" + " -m | --modify post <grp> (y|n) Modify posting status of a local group\n" + " -n | --online Switch to online mode\n" + " -o | --offline Switch to offline mode\n" + " -q | --query groups Get group list from server\n" + " -q | --query desc Get group descriptions from server\n" + " -q | --query times Get group creation times from server\n" + " -r | --server Run as server on stdin/stdout\n" + " -R | --requested List articles marked for download\n" + " -s | --subscribe-over <grp> Add group to fetch list (overview)\n" + " -S | --subscribe-full <grp> Add group to fetch list (full)\n" + " -t | --subscribe-thread <grp> Add group to fetch list (thread)\n" + " -u | --unsubscribe <grp> Remove group from fetch list\n" + " -v | --version Print version\n"; + fprintf( stderr, "%s", msg ); +} + +/* + Allow core files: Change core limit and change working directory + to spool directory, where news has write permissions. +*/ +static void +enableCorefiles() +{ + struct rlimit lim; + + if ( getrlimit( RLIMIT_CORE, &lim ) != 0 ) + { + Log_err( "Cannot get system core limit: %s", strerror( errno ) ); + return; + } + lim.rlim_cur = lim.rlim_max; + if ( setrlimit( RLIMIT_CORE, &lim ) != 0 ) + { + Log_err( "Cannot set system core limit: %s", strerror( errno ) ); + return; + } + Log_dbg( "Core limit set to %i", lim.rlim_max ); + if ( chdir( Cfg_spoolDir() ) != 0 ) + { + Log_err( "Cannot change to directory '%s'", Cfg_spoolDir() ); + return; + } + Log_dbg( "Changed to directory '%s'", Cfg_spoolDir() ); +} + +static Bool +initNoffle( Bool interactive ) +{ + Log_init( "noffle", interactive, LOG_NEWS ); + Cfg_read(); + Log_dbg( "NOFFLE version %s", Cfg_version() ); + noffle.interactive = interactive; + if ( interactive ) + if ( ! Lock_openDatabases() ) + return FALSE; + if ( ! interactive ) + enableCorefiles(); + return TRUE; +} + +static void +closeNoffle( void ) +{ + if ( noffle.interactive ) + Lock_closeDatabases(); +} + +static void +bugReport( int sig ) +{ + Log_err( "Received SIGSEGV. Please submit a bug report" ); + signal( SIGSEGV, SIG_DFL ); + raise( sig ); +} + +static void +logSignal( int sig ) +{ + const char *name; + Bool err = TRUE; + + switch ( sig ) + { + case SIGABRT: + name = "SIGABRT"; break; + case SIGFPE: + name = "SIGFPE"; break; + case SIGILL: + name = "SIGILL"; break; + case SIGINT: + name = "SIGINT"; break; + case SIGTERM: + name = "SIGTERM"; break; + case SIGPIPE: + name = "SIGPIPE"; err = FALSE; break; + default: + name = "?"; break; + } + if ( err ) + Log_err( "Received signal %i (%s). Aborting.", sig, name ); + else + Log_inf( "Received signal %i (%s). Aborting.", sig, name ); + signal( sig, SIG_DFL ); + raise( sig ); +} + +int main ( int argc, char **argv ) +{ + int c, result; + struct option longOptions[] = + { + { "article", required_argument, NULL, 'a' }, + { "cancel", required_argument, NULL, 'c' }, + { "create", required_argument, NULL, 'C' }, + { "database", no_argument, NULL, 'd' }, + { "delete", required_argument, NULL, 'D' }, + { "expire", no_argument, NULL, 'e' }, + { "fetch", no_argument, NULL, 'f' }, + { "groups", no_argument, NULL, 'g' }, + { "help", no_argument, NULL, 'h' }, + { "list", no_argument, NULL, 'l' }, + { "modify", required_argument, NULL, 'm' }, + { "offline", no_argument, NULL, 'o' }, + { "online", no_argument, NULL, 'n' }, + { "query", required_argument, NULL, 'q' }, + { "server", no_argument, NULL, 'r' }, + { "requested", no_argument, NULL, 'R' }, + { "subscribe-over", required_argument, NULL, 's' }, + { "subscribe-full", required_argument, NULL, 'S' }, + { "subscribe-thread", required_argument, NULL, 't' }, + { "unsubscribe", required_argument, NULL, 'u' }, + { "version", no_argument, NULL, 'v' }, + { NULL, 0, NULL, 0 } + }; + + signal( SIGSEGV, bugReport ); + signal( SIGABRT, logSignal ); + signal( SIGFPE, logSignal ); + signal( SIGILL, logSignal ); + signal( SIGINT, logSignal ); + signal( SIGTERM, logSignal ); + signal( SIGPIPE, logSignal ); + c = getopt_long( argc, argv, "a:c:C:dD:efghlm:onq:rRs:S:t:u:v", + longOptions, NULL ); + if ( ! initNoffle( c != 'r' ) ) + return EXIT_FAILURE; + result = EXIT_SUCCESS; + switch ( c ) + { + case 0: + /* Options that set a flag. */ + break; + case 'a': + if ( ! optarg ) + { + fprintf( stderr, "Option -a needs argument.\n" ); + result = EXIT_FAILURE; + } + else + doArt( optarg ); + break; + case 'c': + if ( ! optarg ) + { + fprintf( stderr, "Option -c needs argument.\n" ); + result = EXIT_FAILURE; + } + else + doCancel( optarg ); + break; + case 'C': + if ( ! optarg ) + { + fprintf( stderr, "Option -C needs argument.\n" ); + result = EXIT_FAILURE; + } + else + doCreateLocalGroup( optarg ); + break; + case 'd': + doDb(); + break; + case 'D': + if ( ! optarg ) + { + fprintf( stderr, "Option -D needs argument.\n" ); + result = EXIT_FAILURE; + } + else + doDeleteLocalGroup( optarg ); + break; + case 'e': + doExpire(); + break; + case 'f': + doFetch(); + break; + case 'g': + doGrps(); + break; + case -1: + case 'h': + printUsage(); + break; + case 'l': + doList(); + break; + case 'm': + if ( ! optarg ) + { + fprintf( stderr, "Option -m needs argument.\n" ); + result = EXIT_FAILURE; + } + else + if ( ! doModify( optarg, argc - optind, &argv[ optind ] ) ) + result = EXIT_FAILURE; + break; + case 'n': + if ( Online_true() ) + fprintf( stderr, "NOFFLE is already online\n" ); + else + Online_set( TRUE ); + break; + case 'o': + if ( ! Online_true() ) + fprintf( stderr, "NOFFLE is already offline\n" ); + else + Online_set( FALSE ); + break; + case 'q': + if ( ! optarg ) + { + fprintf( stderr, "Option -q needs argument.\n" ); + result = EXIT_FAILURE; + } + else + { + if ( strcmp( optarg, "groups" ) == 0 ) + noffle.queryGrps = TRUE; + else if ( strcmp( optarg, "desc" ) == 0 ) + noffle.queryDsc = TRUE; + else if ( strcmp( optarg, "times" ) == 0 ) + noffle.queryTimes = TRUE; + else + { + fprintf( stderr, "Unknown argument -q %s\n", optarg ); + result = EXIT_FAILURE; + } + doQuery(); + } + break; + case 'r': + Log_inf( "Starting as server" ); + Serv_run(); + break; + case 'R': + doRequested( optarg ); + break; + case 's': + if ( ! optarg ) + { + fprintf( stderr, "Option -s needs argument.\n" ); + result = EXIT_FAILURE; + } + else + result = doSubscribe( optarg, OVER ); + break; + case 'S': + if ( ! optarg ) + { + fprintf( stderr, "Option -S needs argument.\n" ); + result = EXIT_FAILURE; + } + else + doSubscribe( optarg, FULL ); + break; + case 't': + if ( ! optarg ) + { + fprintf( stderr, "Option -t needs argument.\n" ); + result = EXIT_FAILURE; + } + else + result = doSubscribe( optarg, THREAD ); + break; + case 'u': + if ( ! optarg ) + { + fprintf( stderr, "Option -u needs argument.\n" ); + result = EXIT_FAILURE; + } + else + doUnsubscribe( optarg ); + break; + case '?': + /* Error message already printed by getopt_long */ + result = EXIT_FAILURE; + break; + case 'v': + printf( "NNTP server NOFFLE, version %s.\n", Cfg_version() ); + break; + default: + abort(); /* Never reached */ + } + closeNoffle(); + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/online.c Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,62 @@ +/* + online.c + + $Id: online.c 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#if HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <unistd.h> +#include "common.h" +#include "configfile.h" +#include "log.h" + +static void +fileOnline( Str s ) +{ + snprintf( s, MAXCHAR, "%s/lock/online", Cfg_spoolDir() ); +} + +Bool +Online_true( void ) +{ + FILE *f; + Str file; + + fileOnline( file ); + if ( ! ( f = fopen( file, "r" ) ) ) + return FALSE; + fclose( f ); + return TRUE; +} + +void +Online_set( Bool value ) +{ + FILE *f; + Str file; + + fileOnline( file ); + if ( value ) + { + if ( ! ( f = fopen( file, "a" ) ) ) + { + Log_err( "Could not create %s", file ); + return; + } + fclose( f ); + Log_inf( "NOFFLE is now online" ); + } + else + { + if ( unlink( file ) != 0 ) + { + Log_err( "Cannot remove %s", file ); + return; + } + Log_inf( "NOFFLE is now offline" ); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/online.h Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,18 @@ +/* + online.h + + Online/offline status. + + $Id: online.h 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#ifndef ONLINE_H +#define ONLINE_H + +Bool +Online_true( void ); + +void +Online_set( Bool value ); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/outgoing.c Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,168 @@ +/* + outgoing.c + + $Id: outgoing.c 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#if HAVE_CONFIG_H +#include <config.h> +#endif + +#include "outgoing.h" + +#include <stdio.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <time.h> +#include <unistd.h> +#include "configfile.h" +#include "log.h" +#include "util.h" + +struct Outgoing +{ + DIR *dir; + Str serv; +} outgoing = { NULL, "" }; + +static void +fileOutgoing( Str file, const char *serv, const char *msgId ) +{ + snprintf( file, MAXCHAR, "%s/outgoing/%s/%s", + Cfg_spoolDir(), serv, msgId ); +} + +static void +createDir( const char *serv ) +{ + Str dir; + int r; + + snprintf( dir, MAXCHAR, "%s/outgoing/%s", Cfg_spoolDir(), serv ); + r = mkdir( dir, 0755 ); + if ( r != 0 ) + Log_dbg( "mkdir: %s", strerror( errno ) ); +} + +Bool +Out_add( const char *serv, const char *msgId, const DynStr *artTxt ) +{ + Str file; + FILE *f; + + fileOutgoing( file, serv, msgId ); + if ( ! ( f = fopen( file, "w" ) ) ) + { + createDir( serv ); + if ( ! ( f = fopen( file, "w" ) ) ) + { + Log_err( "Cannot open %s", file ); + return FALSE; + } + } + fprintf( f, "%s", DynStr_str( artTxt ) ); + fclose( f ); + return TRUE; +} + +Bool +Out_first( const char *serv, Str msgId, DynStr *artTxt ) +{ + Str file; + + snprintf( file, MAXCHAR, "%s/outgoing/%s", Cfg_spoolDir(), serv ); + if ( ! ( outgoing.dir = opendir( file ) ) ) + { + Log_dbg( "Cannot open %s", file ); + return FALSE; + } + Utl_cpyStr( outgoing.serv, serv ); + Out_next( NULL, NULL ); /* "." */ + Out_next( NULL, NULL ); /* ".." */ + return Out_next( msgId, artTxt ); +} + +Bool +Out_next( Str msgId, DynStr *artTxt ) +{ + struct dirent *d; + FILE *f; + Str file, line; + + ASSERT( outgoing.dir ); + if ( ! ( d = readdir( outgoing.dir ) ) ) + { + closedir( outgoing.dir ); + outgoing.dir = NULL; + return FALSE; + } + if ( artTxt == NULL ) + return ( d->d_name != NULL ); + fileOutgoing( file, outgoing.serv, d->d_name ); + if ( ! ( f = fopen( file, "r" ) ) ) + { + Log_err( "Cannot open %s for read", file ); + return FALSE; + } + DynStr_clear( artTxt ); + while ( fgets( line, MAXCHAR, f ) ) + DynStr_app( artTxt, line ); + Utl_cpyStr( msgId, d->d_name ); + fclose( f ); + return TRUE; +} + +void +Out_remove( const char *serv, const char *msgId ) +{ + Str file; + + fileOutgoing( file, serv, msgId ); + if ( unlink( file ) != 0 ) + Log_err( "Cannot remove %s", file ); +} + +Bool +Out_find( const char *msgId, Str server ) +{ + Str servdir; + DIR *d; + struct dirent *entry; + Bool res; + + + snprintf( servdir, MAXCHAR, "%s/outgoing", Cfg_spoolDir() ); + if ( ! ( d = opendir( servdir ) ) ) + { + Log_dbg( "Cannot open %s", servdir ); + return FALSE; + } + + readdir( d ); /* '.' */ + readdir( d ); /* '..' */ + + res = FALSE; + while ( ! res && ( entry = readdir( d ) ) != NULL ) + { + Str file; + struct stat s; + + fileOutgoing( file, entry->d_name, msgId ); + if ( stat( file, &s ) == 0 ) + { + res = TRUE; + Utl_cpyStr( server, entry->d_name ); + } + } + + closedir( d ); + return res; +} + + + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/outgoing.h Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,34 @@ +/* + outgoing.h + + Collection of posted articles. + + $Id: outgoing.h 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#ifndef OUT_H +#define OUT_H + +#include "common.h" +#include "dynamicstring.h" + +Bool +Out_add( const char *serv, const char *msgId, const DynStr *artTxt ); + +/* Start enumeration. Return TRUE on success. */ +Bool +Out_first( const char *serv, Str msgId, DynStr *artTxt ); + +/* Continue enumeration. Return TRUE on success. */ +Bool +Out_next( Str msgId, DynStr *s ); + +/* Delete article from outgoing collection */ +void +Out_remove( const char *serv, const char *msgId ); + +/* Find server for outgoing message. */ +Bool +Out_find( const char *msgId, Str server ); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/over.c Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,191 @@ +/* + over.c + + $Id: over.c 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#include <errno.h> +#include <time.h> +#include "configfile.h" +#include "content.h" +#include "database.h" +#include "fetchlist.h" +#include "log.h" +#include "util.h" +#include "protocol.h" +#include "pseudo.h" + +struct Over +{ + int numb; /* message number of the overviewed article */ + char *subj; + char *from; + char *date; + char *msgId; + char *ref; + size_t bytes; + size_t lines; + time_t time; +}; + +Over * +new_Over( const char *subj, const char *from, + const char *date, const char *msgId, char *ref, + size_t bytes, size_t lines ) +{ + Over *ov; + + if ( ! ( ov = (Over *)malloc( sizeof( Over ) ) ) ) + { + Log_err( "Cannot allocate Over" ); + exit( EXIT_FAILURE ); + } + ov->numb = 0; + Utl_allocAndCpy( &ov->subj, subj ); + Utl_allocAndCpy( &ov->from, from ); + Utl_allocAndCpy( &ov->date, date ); + Utl_allocAndCpy( &ov->msgId, msgId ); + Utl_allocAndCpy( &ov->ref, ref ); + ov->bytes = bytes; + ov->lines = lines; + return ov; +} + +void +del_Over( Over *self ) +{ + if ( ! self ) + return; + free( self->subj ); + self->subj = NULL; + free( self->from ); + self->from = NULL; + free( self->date ); + self->date = NULL; + free( self->msgId ); + self->msgId = NULL; + free( self->ref ); + self->ref = NULL; + free( self ); +} + +int +Ov_numb( const Over *self ) +{ + return self->numb; +} + +const char * +Ov_subj( const Over *self ) +{ + return self->subj; +} + +const char * +Ov_from( const Over *self ) +{ + return self->from; +} + +const char * +Ov_date( const Over *self ) +{ + return self->date; +} + +const char * +Ov_msgId( const Over *self ) +{ + return self->msgId; +} + +const char * +Ov_ref( const Over *self ) +{ + return self->ref; +} + +size_t +Ov_bytes( const Over *self ) +{ + return self->bytes; +} + +size_t +Ov_lines( const Over *self ) +{ + return self->lines; +} + +void +Ov_setNumb( Over *self, int numb ) +{ + self->numb = numb; +} + +Bool +Ov_write( const Over *self, FILE *f ) +{ + return ( fprintf( f, "%i\t%s\t%s\t%s\t%s\t%s\t%d\t%d\n", + self->numb, self->subj, + self->from, self->date, self->msgId, + self->ref, self->bytes, + self->lines ) > 0 ); +} + +static const char * +readField( Str result, const char *p ) +{ + size_t len; + char *r; + + if ( ! p ) + return NULL; + r = result; + *r = '\0'; + len = 0; + while ( *p != '\t' && *p != '\n' ) + { + if ( ! *p ) + return p; + *(r++) = *(p++); + ++len; + if ( len >= MAXCHAR - 1 ) + { + *r = '\0'; + Log_err( "Field in overview too long: %s", r ); + return ++p; + } + } + *r = '\0'; + return ++p; +} + +/* read Over-struct from line */ +Over * +Ov_read( char *line ) +{ + size_t bytes, lines; + const char *p; + Over *result; + int numb; + Str t, subj, from, date, msgId, ref; + + p = readField( t, line ); + if ( sscanf( t, "%i", &numb ) != 1 ) + return NULL; + p = readField( subj, p ); + p = readField( from, p ); + p = readField( date, p ); + p = readField( msgId, p ); + p = readField( ref, p ); + p = readField( t, p ); + if ( sscanf( t, "%d", &bytes ) != 1 ) + return NULL; + p = readField( t, p ); + if ( sscanf( t, "%d", &lines ) != 1 ) + return NULL; + result = new_Over( subj, from, date, msgId, ref, bytes, lines ); + Ov_setNumb( result, numb ); + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/over.h Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,70 @@ +/* + over.h + + Processing of single article overviews. Handling of overview files is in + content.c. An article overview contains important article properties, + such as date, from, subject. + + $Id: over.h 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#ifndef OVER_H +#define OVER_H + +#include <time.h> +#include "common.h" + +struct Over; +typedef struct Over Over; + +/* + Usual fields from overview databases. + Xref without hostname. +*/ +Over * +new_Over( const char *subj, const char *from, const char *date, + const char *msgId, char *ref, size_t bytes, size_t lines ); + + +/* free memory */ +void +del_Over( Over *self ); + +/* read Over-struct from line */ +Over * +Ov_read( char *line ); + +/* write struct Over to f as a line */ +Bool +Ov_write( const Over *self, FILE *f ); + +/* Access particular fields in struct over */ + +int +Ov_numb( const Over *self ); + +const char * +Ov_subj( const Over *self ); + +const char * +Ov_from( const Over *self ); + +const char * +Ov_date( const Over *self ); + +const char * +Ov_msgId( const Over *self ); + +const char * +Ov_ref( const Over *self ); + +size_t +Ov_bytes( const Over *self ); + +size_t +Ov_lines( const Over *self ); + +void +Ov_setNumb( Over *self, int numb ); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/post.c Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,155 @@ +/* + post.c + + $Id: post.c 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#if HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include "post.h" +#include <string.h> +#include "common.h" +#include "content.h" +#include "database.h" +#include "group.h" +#include "log.h" +#include "over.h" +#include "protocol.h" +#include "util.h" + +struct OverInfo +{ + Str subject; + Str from; + Str date; + Str msgId; + Str ref; + size_t bytes; + size_t lines; +}; + +struct Article +{ + const char * text; + Bool posted; + struct OverInfo over; +}; + +static struct Article article = { NULL, FALSE }; + +static void +getOverInfo( struct OverInfo * o ) +{ + const char *p = article.text; + Str line, field, value; + + o->bytes = strlen( p ); + + while( p != NULL ) + { + p = Utl_getHeaderLn( line, p ); + if ( line[ 0 ] == '\0' ) + break; + + /* Look for headers we need to stash. */ + if ( Prt_getField( field, value, line ) ) + { + if ( strcmp( field, "subject" ) == 0 ) + Utl_cpyStr( o->subject, value ); + else if ( strcmp ( field, "from" ) == 0 ) + Utl_cpyStr( o->from, value ); + else if ( strcmp ( field, "date" ) == 0 ) + Utl_cpyStr( o->date, value ); + else if ( strcmp ( field, "references" ) == 0 ) + Utl_cpyStr( o->ref, value ); + else if ( strcmp ( field, "message-id" ) == 0 ) + Utl_cpyStr( o->msgId, value ); + } + } + + /* Move to start of body and count lines. */ + for ( p++, o->lines = 0; *p != '\0'; p++ ) + if ( *p == '\n' ) + o->lines++; +} + +/* Register an article for posting. */ +Bool +Post_open( const char * text ) +{ + if ( article.text != NULL ) + { + Log_err( "Busy article in Post_open." ); + return FALSE; + } + + memset( &article.over, 0, sizeof( article.over ) ); + article.text = text; + getOverInfo( &article.over ); + + if ( Db_contains( article.over.msgId ) ) + { + Log_err( "Duplicate article %s.", article.over.msgId ); + return FALSE; + } + + return TRUE; +} + + +/* Add the article to a group. */ +Bool +Post_add ( const char * grp ) +{ + Over * over; + const char *msgId; + + over = new_Over( article.over.subject, + article.over.from, + article.over.date, + article.over.msgId, + article.over.ref, + article.over.bytes, + article.over.lines ); + + msgId = article.over.msgId; + + Cont_read( grp ); + Cont_app( over ); + Log_dbg( "Added message '%s' to group '%s'.", msgId, grp ); + + if ( !article.posted ) + { + Log_inf( "Added '%s' to database.", msgId ); + if ( ! Db_prepareEntry( over, Cont_grp(), Cont_last() ) + || ! Db_storeArt ( msgId, article.text ) ) + return FALSE; + article.posted = TRUE; + } + else + { + Str t; + const char *xref; + + xref = Db_xref( msgId ); + Log_dbg( "Adding '%s' to Xref of '%s'", grp, msgId ); + snprintf( t, MAXCHAR, "%s %s:%i", xref, grp, Ov_numb( over ) ); + Db_setXref( msgId, t ); + } + + Cont_write(); + Grp_setFirstLast( Cont_grp(), Cont_first(), Cont_last() ); + return TRUE; +} + +/* Done with article - tidy up. */ +void +Post_close( void ) +{ + article.text = NULL; + article.posted = FALSE; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/post.h Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,28 @@ +/* + post.h + + Take a single article received in its entirety without an overview + (i.e. received via at the server via a POST), and add it to the database + and (possibly multiple) group(s). + + $Id: post.h 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#ifndef POST_H +#define POST_H + +#include "common.h" + +/* Register an article for posting. */ +Bool +Post_open( const char * text ); + +/* Add the article to a group. */ +Bool +Post_add ( const char * grp ); + +/* Done with article - tidy up. */ +void +Post_close( void ); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocol.c Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,284 @@ +/* + protocol.c + + $Id: protocol.c 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#if HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <ctype.h> +#include <netdb.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include "common.h" +#include "dynamicstring.h" +#include "log.h" +#include "over.h" +#include "util.h" + +Bool +Prt_getLn( Str line, FILE *f ) +{ + size_t len; + + /* + We also accept lines ending with "\n" instead of "\r\n", some + clients wrongly send such lines. + */ + if ( ! fgets( line, MAXCHAR, f ) ) + { + Log_dbg( "Prt_getLine failed" ); + return FALSE; + } + len = strlen( line ); + if ( line[ len - 1 ] == '\n' ) + { + line[ len - 1 ] = '\0'; + if ( line[ len - 2 ] == '\r' ) + line[ len - 2 ] = '\0'; + } + Log_dbg( "[R] %s", line ); + return TRUE; +} + +Bool +Prt_getTxtLn( Str line, Bool *err, FILE *f ) +{ + Str buf; + + if ( ! Prt_getLn( buf, f ) ) + { + Log_err( "Cannot get text line" ); + *err = TRUE; + return FALSE; + } + *err = FALSE; + if ( buf[ 0 ] == '.' ) + { + if ( buf[ 1 ] == 0 ) + return FALSE; + else + strcpy( line, buf + 1 ); + } + else + strcpy( line, buf ); + return TRUE; +} + +Bool +Prt_putTxtLn( const char* line, FILE *f ) +{ + if ( line[ 0 ] == '.' ) + { + Log_dbg( "[S] .%s", line ); + return ( fprintf( f, ".%s\r\n", line ) == strlen( line ) + 3 ); + } + else + { + Log_dbg( "[S] %s", line ); + return ( fprintf( f, "%s\r\n", line ) == strlen( line ) + 2 ); + } +} + +Bool +Prt_putEndOfTxt( FILE *f ) +{ + Log_dbg( "[S] ." ); + return ( fprintf( f, ".\r\n" ) == 3 ); +} + +/* + Write text buffer of lines each ending with '\n'. + Replace '\n' by "\r\n". +*/ +Bool +Prt_putTxtBuf( const char *buf, FILE *f ) +{ + Str line; + const char *pBuf; + char *pLn; + + pBuf = buf; + pLn = line; + while ( *pBuf != '\0' ) + { + if ( *pBuf == '\n' ) + { + *pLn = '\0'; + if ( ! Prt_putTxtLn( line, f ) ) + return FALSE; + pLn = line; + ++pBuf; + } + else if ( pLn - line >= MAXCHAR - 1 ) + { + /* Put it out raw to prevent String overflow */ + Log_err( "Writing VERY long line" ); + *pLn = '\0'; + if ( fprintf( f, "%s", line ) != strlen( line ) ) + return FALSE; + pLn = line; + } + else + *(pLn++) = *(pBuf++); + } + return TRUE; +} + +Bool +Prt_getField( Str resultField, Str resultValue, const char* line ) +{ + char *dst; + const char *p; + Str lineLower, t; + + Utl_cpyStr( lineLower, line ); + Utl_toLower( lineLower ); + p = Utl_stripWhiteSpace( lineLower ); + dst = resultField; + while ( ! isspace( *p ) && *p != ':' && *p != '\0' ) + *(dst++) = *(p++); + *dst = '\0'; + while ( isspace( *p ) ) + ++p; + if ( *p == ':' ) + { + ++p; + strcpy( t, line + ( p - lineLower ) ); + p = Utl_stripWhiteSpace( t ); + strcpy( resultValue, p ); + return TRUE; + } + return FALSE; +} + +Bool +Prt_searchHeader( const char *artTxt, const char *which, Str result ) +{ + const char *src, *p; + char *dst; + Str line, whichLower, field; + int len; + + Utl_cpyStr( whichLower, which ); + Utl_toLower( whichLower ); + src = artTxt; + while ( TRUE ) + { + dst = line; + len = 0; + while ( *src != '\n' && len < MAXCHAR ) + { + if ( *src == '\0' ) + return FALSE; + *(dst++) = *(src++); + ++len; + } + if ( *src == '\n' ) + ++src; + *dst = '\0'; + p = Utl_stripWhiteSpace( line ); + if ( *p == '\0' ) + break; + if ( Prt_getField( field, result, line ) + && strcmp( field, whichLower ) == 0 ) + return TRUE; + } + return FALSE; +} + +static Bool +getFQDN( Str result ) +{ + struct hostent *myHostEnt; + struct utsname myName; + + if ( uname( &myName ) >= 0 + && ( myHostEnt = gethostbyname( myName.nodename ) ) ) + { + Utl_cpyStr( result, myHostEnt->h_name ); + return TRUE; + } + return FALSE; +} + +static void +getDomain( Str domain, const char *from ) +{ + const char *addTopLevel, *p1, *p2, *p, *domainStart; + Str myDomain; + + if ( getFQDN( myDomain ) ) + { + p = strstr( myDomain, "." ); + if ( p != NULL ) + domainStart = p + 1; + else + domainStart = myDomain; + } + else /* Take domain of From field */ + { + myDomain[ 0 ] = '\0'; + p1 = strstr( from, "@" ); + if ( p1 != NULL ) + { + p2 = strstr( p1, ">" ); + if ( p2 != NULL ) + Utl_cpyStrN( myDomain, p1 + 1, p2 - p1 - 1 ); + } + if ( myDomain[ 0 ] == '\0' ) + Utl_cpyStr( myDomain, "unknown" ); + domainStart = myDomain; + } + /* + If domain contains no dot (and is probably invalid anyway), + we add ".local", because some servers insist on domainnames with dot + in message ID. + */ + addTopLevel = strstr( domainStart, "." ) == NULL ? ".local" : ""; + snprintf( domain, MAXCHAR, "%s%s", myDomain, addTopLevel ); +} + +/* See RFC 850, section 2.1.7 */ +Bool +Prt_isValidMsgId( const char *msgId ) +{ + Str head, domain; + int len, headLen; + const char *p; + + len = strlen( msgId ); + p = strstr( msgId, "@" ); + if ( msgId[ 0 ] != '<' || msgId[ len - 1 ] != '>' || p == NULL ) + return FALSE; + strcpy( domain, p + 1 ); + domain[ strlen( domain ) - 1 ] = '\0'; + headLen = p - msgId - 1; + Utl_cpyStrN( head, msgId + 1, headLen ); + head[ headLen ] = '\0'; + /* + To do: check for special characters in head and domain (non-printable + or '@', '<', '>'). Maybe compare domain with a config option + and replace it by the config option, if not equal. + */ + if ( strstr( domain, "." ) == NULL ) + return FALSE; + return TRUE; +} + +void +Prt_genMsgId( Str msgId, const char *from, const char *suffix ) +{ + Str domain, date; + time_t t; + + getDomain( domain, from ); + time( &t ); + strftime( date, MAXCHAR, "%Y%m%d%H%M%S", gmtime( &t ) ); + srand( time( NULL ) ); + snprintf( msgId, MAXCHAR, "<%s.%X.%s@%s>", date, rand(), suffix, domain ); + ASSERT( Prt_isValidMsgId( msgId ) ); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocol.h Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,105 @@ +/* + protocol.h + + Functions related with the NNTP protocol which are useful for both + the server and the client. + + $Id: protocol.h 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#ifndef PRT_H +#define PRT_H + +#include "dynamicstring.h" +#include "over.h" + +#define STAT_HELP_FOLLOWS 100 +#define STAT_DEBUG_FOLLOWS 199 + +#define STAT_READY_POST_ALLOW 200 +#define STAT_READY_NO_POST_ALLOW 201 +#define STAT_CMD_OK 202 +#define STAT_GOODBYE 205 +#define STAT_GRP_SELECTED 211 +#define STAT_GRPS_FOLLOW 215 +#define STAT_ART_FOLLOWS 220 +#define STAT_HEAD_FOLLOWS 221 +#define STAT_BODY_FOLLOWS 222 +#define STAT_ART_RETRIEVED 223 +#define STAT_OVERS_FOLLOW 224 +#define STAT_NEW_GRP_FOLLOW 231 +#define STAT_POST_OK 240 +#define STAT_AUTH_ACCEPTED 281 + +#define STAT_SEND_ART 340 +#define STAT_MORE_AUTH_REQUIRED 381 + +#define STAT_NO_SUCH_GRP 411 +#define STAT_NO_GRP_SELECTED 412 +#define STAT_NO_ART_SELECTED 420 +#define STAT_NO_NEXT_ART 421 +#define STAT_NO_PREV_ART 422 +#define STAT_NO_SUCH_NUMB 423 +#define STAT_NO_SUCH_ID 430 +#define STAT_ART_REJECTED 437 +#define STAT_POST_FAILED 441 +#define STAT_AUTH_REQUIRED 480 +#define STAT_AUTH_REJECTED 482 + +#define STAT_NO_SUCH_CMD 500 +#define STAT_SYNTAX_ERR 501 +#define STAT_NO_PERMISSION 502 +#define STAT_PROGRAM_FAULT 503 + +/* + Read next line from f into Str, up to "\n" or "\r\n". Don't save "\n" + or "\r\n" in line. Terminate with '\0'. +*/ +Bool +Prt_getLn( Str line, FILE *f ); + +/* + Read a text line from server. Returns TRUE if line != ".", removes + leading '.' otherwise. +*/ +Bool +Prt_getTxtLn( Str line, Bool *err, FILE *f ); + +/* + Write text line to f. Escape "." at the beginning with another ".". + Terminate with "\r\n". +*/ +Bool +Prt_putTxtLn( const char* line, FILE *f ); + +/* + Write text buffer of lines each ending with '\n'. + Replace '\n' by "\r\n". +*/ +Bool +Prt_putTxtBuf( const char *buf, FILE *f ); + +/* + Write text-ending "."-line to f +*/ +Bool +Prt_putEndOfTxt( FILE *f ); + +/* + Splits line in field and value. Field is converted to lower-case. +*/ +Bool +Prt_getField( Str resultField, Str resultValue, const char* line ); + +/* Search header. Works only with single line headers (ignores continuation + lines */ +Bool +Prt_searchHeader( const char *artTxt, const char* which, Str result ); + +Bool +Prt_isValidMsgId( const char *msgId ); + +void +Prt_genMsgId( Str msgId, const char *from, const char *suffix ); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pseudo.c Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,335 @@ +/* + pseudo.c + + $Id: pseudo.c 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#if HAVE_CONFIG_H +#include <config.h> +#endif + +#include "pseudo.h" + +#include <stdio.h> +#include <time.h> +#include "common.h" +#include "configfile.h" +#include "content.h" +#include "database.h" +#include "group.h" +#include "log.h" +#include "protocol.h" +#include "util.h" + +Over * +genOv( const char *rawSubj, const char *rawBody, const char *suffix ) +{ + size_t bytes, lines; + time_t t; + Str subj, date, msgId; + + snprintf( subj, MAXCHAR, "[ %s ]", rawSubj ); + time( &t ); + Utl_rfc822Date( t, date ); + Prt_genMsgId( msgId, "", suffix ); + bytes = lines = 0; + while ( *rawBody ) + { + ++bytes; + if ( *rawBody == '\n' ) + ++lines; + ++rawBody; + } + return new_Over( subj, "news (\"[ NOFFLE ]\")" , date, msgId, "", + bytes, lines ); +} + +void +Pseudo_appGeneralInfo() +{ + Cont_app( genOv( "General info", Pseudo_generalInfoBody(), + "NOFFLE-GENERAL-INFO" ) ); +} + +Bool +Pseudo_isGeneralInfo( const char *msgId ) +{ + return ( strstr( msgId, "NOFFLE-GENERAL-INFO" ) != NULL ); +} + +const char * +Pseudo_generalInfoHead() +{ + static Str s; + + Over *ov; + + ov = genOv( "General info", Pseudo_generalInfoBody(), + "NOFFLE-GENERAL-INFO" ); + if ( ov ) + { + snprintf( s, MAXCHAR, + "Message-ID: %s\n" + "Subject: %s\n" + "From: %s\n" + "Date: %s\n" + "Bytes: %u\n" + "Lines: %u\n", + Ov_msgId( ov ), + Ov_subj( ov ), + Ov_from( ov ), + Ov_date( ov ), + Ov_bytes( ov ), + Ov_lines( ov ) ); + del_Over( ov ); + return s; + } + return NULL; +} + +const char * +Pseudo_generalInfoBody( void ) +{ + if ( Cfg_autoSubscribe() ) + return + "\n" + "\t[ NOFFLE INFO: General information ]\n" + "\n" + "\t[ This server is running NOFFLE, which is a NNTP server ]\n" + "\t[ optimized for low speed dial-up Internet connections. ]\n" + "\n" + "\t[ By reading this or any other article of this group, ]\n" + "\t[ NOFFLE has put it on its fetch list and will retrieve ]\n" + "\t[ articles next time it is online. ]\n" + "\n" + "\t[ If you have more questions about NOFFLE please talk ]\n" + "\t[ to your newsmaster or read the manual page for ]\n" + "\t[ \"noffle\". ]\n"; + else + return + "\n" + "\t[ NOFFLE INFO: General information ]\n" + "\n" + "\t[ This server is running NOFFLE, which is a NNTP server ]\n" + "\t[ optimized for low speed dial-up Internet connections. ]\n" + "\n" + "\t[ This group is presently not on the fetch list. You can ]\n" + "\t[ put groups on the fetch list by running the \"noffle\" ]\n" + "\t[ command on the computer where this server is running. ]\n" + "\n" + "\t[ If you have more questions about NOFFLE please talk ]\n" + "\t[ to your newsmaster or read the manual page for ]\n" + "\t[ \"noffle\". ]\n"; +} + +const char * +Pseudo_markedBody( void ) +{ + return + "\n" + "\t[ NOFFLE INFO: Marked for download ]\n" + "\n" + "\t[ The body of this article has been marked for download. ]\n"; +} + +const char * +Pseudo_alreadyMarkedBody( void ) +{ + return + "\n" + "\t[ NOFFLE INFO: Already marked for download ]\n" + "\n" + "\t[ The body of this article has already been marked ]\n" + "\t[ for download. ]\n"; +} + +const char * +Pseudo_markingFailedBody( void ) +{ + return + "\n" + "\t[ NOFFLE ERROR: Marking for download failed ]\n" + "\n" + "\t[ Sorry, I could not mark this article for download. ]\n" + "\t[ Either the database is corrupted, or I was unable to ]\n" + "\t[ get write access to the request directory. ]\n" + "\t[ Please contact your newsmaster to remove this problem. ]\n"; +} + +void +genPseudo( const char *rawSubj, const char* rawBody ) +{ + Over *ov; + DynStr *body = 0, *artTxt = 0; + + body = new_DynStr( 10000 ); + artTxt = new_DynStr( 10000 ); + DynStr_app( body, "\n\t[ NOFFLE INFO: " ); + DynStr_app( body, rawSubj ); + DynStr_app( body, " ]\n\n" ); + DynStr_app( body, "\t[ " ); + while( *rawBody ) + { + if ( *rawBody == '\n' ) + { + DynStr_app( body, " ]\n" ); + if ( *( rawBody + 1 ) == '\n' ) + { + DynStr_app( body, "\n\t[ " ); + ++rawBody; + } + else if ( *( rawBody + 1 ) != '\0' ) + DynStr_app( body, "\t[ " ); + } + else + DynStr_appN( body, rawBody, 1 ); + ++rawBody; + } + DynStr_appLn( body, "" ); + DynStr_appLn( artTxt, + "Comments: Pseudo article generated by news server NOFFLE" ); + DynStr_appLn( artTxt, "" ); + DynStr_appDynStr( artTxt, body ); + ov = genOv( rawSubj, DynStr_str( body ), "PSEUDO" ); + if ( body && artTxt && ov ) + { + Cont_app( ov ); + if ( Db_prepareEntry( ov, Cont_grp(), Cont_last() ) ) + Db_storeArt( Ov_msgId( ov ), DynStr_str( artTxt ) ); + Cont_write(); + Grp_setFirstLast( Cont_grp(), Cont_first(), Cont_last() ); + } + del_DynStr( body ); + del_DynStr( artTxt ); +} + +void +Pseudo_retrievingFailed( const char *msgId, const char *reason ) +{ + DynStr *artTxt = 0; + + if ( ! Db_contains( msgId ) ) + { + Log_err( "Article %s has no entry in database %s", msgId ); + return; + } + artTxt = new_DynStr( 10000 ); + DynStr_appLn( artTxt, + "Comments: Pseudo body generated by news server NOFFLE" ); + DynStr_appLn( artTxt, "" ); + DynStr_app( artTxt, + "\n" + "\t[ NOFFLE ERROR: Retrieving failed ]\n" + "\n" + "\t[ This article could not be retrieved. Maybe ]\n" + "\t[ it has already expired at the remote server ]\n" + "\t[ or it has been cancelled by its sender. See ]\n" + "\t[ the appended status line of the remote ]\n" + "\t[ server for more information. ]\n" + "\n" + "\t[ This message will disappear the next time ]\n" + "\t[ someone tries to read this article, so that ]\n" + "\t[ it can be marked for download again. ]\n" ); + DynStr_app( artTxt, "\n\t[ Remote server status: " ); + DynStr_app( artTxt, reason ); + DynStr_app( artTxt, " ]\n" ); + Db_storeArt( msgId, DynStr_str( artTxt ) ); + del_DynStr( artTxt ); +} + +void +Pseudo_cntInconsistent( const char *grp, int first, int last, int next ) +{ + DynStr *info; + Str s; + + info = new_DynStr( 10000 ); + if ( info ) + { + DynStr_app( info, + "This group's article counter is not \n" + "consistent Probably the remote news server\n" + "was changed or has reset its article counter\n" + "for this group. As a consequence there could\n" + "be some articles be duplicated in this group\n" ); + snprintf( s, MAXCHAR, "Group: %s", grp ); + DynStr_appLn( info, s ); + snprintf( s, MAXCHAR, "Remote first article number: %i", first ); + DynStr_appLn( info, s ); + snprintf( s, MAXCHAR, "Remote last article number: %i", last ); + DynStr_appLn( info, s ); + snprintf( s, MAXCHAR, "Remote next article number: %i", next ); + DynStr_appLn( info, s ); + genPseudo( "Article counter inconsistent", DynStr_str( info ) ); + } + del_DynStr( info ); +} + +void +Pseudo_missArts( const char *grp, int first, int next ) +{ + DynStr *info; + Str s; + + info = new_DynStr( 5000 ); + if ( info ) + { + DynStr_app( info, + "Some articles could not be retrieved from\n" + "the remote server, because it had already\n" + "deleted them.\n" + "If this group is on the fetch list, then\n" + "contact your newsmaster to ensure that\n" + "\"noffle\" is fetching news more frequently.\n" ); + snprintf( s, MAXCHAR, "Group: %s", grp ); + DynStr_appLn( info, s ); + snprintf( s, MAXCHAR, "Remote next article number: %i", next ); + DynStr_appLn( info, s ); + snprintf( s, MAXCHAR, "Remote first article number: %i", first ); + DynStr_appLn( info, s ); + genPseudo( "Missing articles", DynStr_str( info ) ); + del_DynStr( info ); + } +} + +void +Pseudo_autoUnsubscribed( const char *grp, int days ) +{ + DynStr *info; + Str s; + + info = new_DynStr( 10000 ); + if ( info ) + { + DynStr_app( info, + "NOFFLE has automatically unsubscribed this\n" + "group since it has not been accessed for\n" + "some time.\n" + "Re-subscribing is done either automatically\n" + "by NOFFLE (if configured) or by manually\n" + "running the 'noffle --subscribe' command\n" ); + snprintf( s, MAXCHAR, "Group: %s", grp ); + DynStr_appLn( info, s ); + snprintf( s, MAXCHAR, "Days without access: %i", days ); + DynStr_appLn( info, s ); + genPseudo( "Auto unsubscribed", DynStr_str( info ) ); + } + del_DynStr( info ); +} + +void +Pseudo_autoSubscribed() +{ + DynStr *info; + + info = new_DynStr( 10000 ); + if ( info ) + { + DynStr_app( info, + "NOFFLE has now automatically subscribed to\n" + "this group. It will fetch articles next time\n" + "it is online.\n" ); + genPseudo( "Auto subscribed", DynStr_str( info ) ); + } + del_DynStr( info ); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pseudo.h Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,64 @@ +/* + pseudo.h + + Handling of pseudo articles. + + $Id: pseudo.h 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#ifndef PSEUDO_H +#define PSEUDO_H + +#include "over.h" + +/* + General info is a special pseudo message for groups not on fetchlist. + It is never stored in database, but generated every time a content is read. + However the group counter is always increased. This ensures that there + is always at least 1 article visible (even if the user deletes it) for + using the auto-subscribe option. +*/ +Bool +Pseudo_isGeneralInfo( const char *msgId ); + +void +Pseudo_appGeneralInfo( void ); + +const char * +Pseudo_generalInfoHead( void ); + +const char * +Pseudo_generalInfoBody( void ); + + +const char * +Pseudo_markedBody( void ); + +const char * +Pseudo_alreadyMarkedBody( void ); + +const char * +Pseudo_markingFailedBody( void ); + +void +Pseudo_retrievingFailed( const char *msgId, const char *reason ); + + +/* + Other pseudo articles are stored in database and can contain dynamically + generated information about the failure. + */ + +void +Pseudo_cntInconsistent( const char *grp, int first, int last, int next ); + +void +Pseudo_missArts( const char *grp, int first, int next ); + +void +Pseudo_autoUnsubscribed( const char *grp, int days ); + +void +Pseudo_autoSubscribed( void ); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/request.c Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,410 @@ +/* + request.c + + Collection of articles that are marked for download. + + $Id: request.c 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#if HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include "request.h" +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <assert.h> +#include "configfile.h" +#include "log.h" +#include "util.h" + + +/* This struct keeps record of the message IDs that are to be fetched from + one particular host. Several of these are chained together via the + "next" pointer, if we have several servers. +*/ + +struct Reqserv; +typedef struct Reqserv Reqserv; + +struct Reqserv { + char* serv; /* Server the messages are to be requested + from */ + char** reql; /* List of message IDs of requested + messages. Some entries (that have been + deleted) may be NULL */ + int reql_length; /* Number of string pointers in reql, + including NULL entries */ + int reql_capacity; /* maximum number of string pointers reql + can hold */ + Bool dirty; /* whether the request list needs to be + rewritten to disk */ + Reqserv* next; /* next Reqserv in list */ + time_t mtime; /* last modification time of request file */ +}; + +/* List of servers */ +static Reqserv* reqserv = 0; + +/* sanity check */ +static Bool is_open = FALSE; + +/* for Req_first/Req_next */ +static char** iterator = 0; +static char** iterator_end = 0; + + +/* local functions */ +static Reqserv* newReqserv (const char* serv); +static Bool getReqserv (const char* serv, Reqserv** rsz); +static void fileRequest (Str file, const char *serv); +static char** searchMsgId (const Reqserv * rs, const char *msgId); +static void storeMsgId (Reqserv* rs, const char* msgId); +static Bool readRequestfile (const char* serv, Reqserv** rsz); +static time_t get_mtime (const char* serv); + +/* read modification time of request file */ +static time_t get_mtime(const char* serv) +{ + Str filename; + struct stat stat1; + + fileRequest(filename, serv); + stat(filename, &stat1); + return stat1.st_mtime; +} + + +/* create new Reqserv and queue it */ +static Reqserv* newReqserv(const char* serv) +{ + Reqserv* rs = (Reqserv*) malloc(sizeof(Reqserv)); + rs->serv = strcpy(malloc(strlen(serv)+1), serv); + rs->reql = 0; + rs->reql_length = 0; + rs->reql_capacity = 0; + rs->next = reqserv; + rs->dirty = FALSE; + rs->mtime = 0; + reqserv = rs; + return rs; +} + + +/* get Reqserv for given server, and save it in "rsz". Load from file as + necessary. Return TRUE on success. Otherwise log errors and return + FALSE. (details in errno) +*/ +static Bool getReqserv(const char* serv, Reqserv** rsz) +{ + Reqserv* rs; + for (rs = reqserv; rs; rs = rs->next) + if (!strcmp(serv, rs->serv)) { + *rsz = rs; + return TRUE; + } + return readRequestfile(serv, rsz); +} + + +/* Delete Reqserv from cache, if not up-to-date */ +static void +cleanupReqserv( void ) +{ + Reqserv *rs, *prev, *next; + + rs = reqserv; + prev = NULL; + while ( rs != NULL ) + { + ASSERT( ! rs->dirty ); + next = rs->next; + if ( get_mtime( rs->serv ) != rs->mtime ) + { + if ( prev != NULL ) + prev->next = next; + else + reqserv = next; + free( rs->serv ); + rs->serv = NULL; + free( rs->reql ); + rs->reql = NULL; + free( rs ); + } + prev = rs; + rs = next; + } +} + +/* Save name of file storing requests from server "serv" in "file" */ +static void fileRequest( Str file, const char *serv) +{ + snprintf( file, MAXCHAR, "%s/requested/%s", Cfg_spoolDir(), serv); +} + + +/* Search for msgid in Reqserv. Return pointer to list entry. Return 0 if + list does not contain msgid. */ +static char** searchMsgId(const Reqserv * rs, const char *msgId ) +{ + char** rz; + ASSERT(rs != 0); + + if (!rs->reql) + return 0; + + for (rz = rs->reql; rz < rs->reql + rs->reql_length; rz++) + if (*rz && !strcmp(*rz, msgId)) + return rz; + + return 0; +} + + +Bool +Req_contains(const char *serv, const char *msgId) +{ + Reqserv* rs; + ASSERT( is_open ); + if (getReqserv(serv, &rs) == FALSE) + return FALSE; + return searchMsgId(rs, msgId) ? TRUE : FALSE; +} + + +static void storeMsgId(Reqserv* rs, const char* msgId) +{ + char* msgid; + + if (searchMsgId(rs, msgId)) + /* already recorded */ + return; + + msgid = strcpy(malloc(strlen(msgId)+1), msgId); + + if (rs->reql_length >= rs->reql_capacity) { + int c1 = rs->reql_capacity*2 + 10; + rs->reql = (char**) realloc(rs->reql, c1*sizeof(char*)); + rs->reql_capacity = c1; + } + + *(rs->reql + rs->reql_length++) = msgid; + rs->dirty = TRUE; +} + + +/* Add request for message "msgIg" from server "serv". Return TRUE iff + successful. +*/ +Bool Req_add(const char *serv, const char *msgId) +{ + Reqserv* rs; + ASSERT( is_open ); + Log_dbg( "Marking %s on %s for download", msgId, serv ); + + if (getReqserv(serv, &rs) == FALSE) + return FALSE; + storeMsgId(rs, msgId); + return TRUE; +} + +static Bool +readLn( Str line, FILE* f ) +{ + size_t len; + + if ( ! fgets( line, MAXCHAR, f ) ) + return FALSE; + len = strlen( line ); + if ( line[ len - 1 ] == '\n' ) + line[ len - 1 ] = '\0'; + return TRUE; +} + +/* Read request file into new, non-queued Reqserv. Save new Reqserv in + "rsz" and return TRUE on success. Returns FALSE on failure, see errno. + If the file doesn't exist, an empty Reqserv is returned. +*/ +static Bool readRequestfile(const char* serv, Reqserv** rsz) +{ + Str filename; + Str line; + FILE* file; + Reqserv* rs; + + fileRequest(filename, serv); + Log_dbg("reading request file %s", filename); + + file = fopen(filename, "r"); + if (!file && (errno == ENOENT)) { + *rsz = newReqserv(serv); + (*rsz)->mtime = get_mtime(serv); + return TRUE; + } + if (Log_check(file != 0, + "could not open %s for reading: %s", + filename, strerror(errno))) + return FALSE; + + rs = *rsz = newReqserv(serv); + + while( readLn(line, file) == TRUE) { + char* line1 = Utl_stripWhiteSpace(line); + if (*line1) + storeMsgId(rs, line1); + } + + rs->dirty = FALSE; + + if (Log_check(fclose(file) != EOF, + "could not close %s properly: %s\n", + filename, strerror(errno))) + return FALSE; + + return TRUE; +} + + +/* Write out request file for given Reqserv. Return TRUE on success. If an + I/O error occurs, it is logged, and FALSE is returned. +*/ +static Bool writeRequestfile(Reqserv* rs) +{ + Str filename; + FILE* file; + char** z; + + fileRequest(filename, rs->serv); + Log_dbg("writing request file %s", filename); + + if (Log_check((file = fopen(filename, "w")) != 0, + "could not open %s for writing: %s", + filename, strerror(errno))) + return FALSE; + + if (rs->reql) + for (z = rs->reql; z < rs->reql+rs->reql_length; z++) + if (*z) { + if (Log_check( fputs(*z, file) != EOF + && fputs("\n", file) != EOF, + "write error: %s", strerror(errno))) + return FALSE; + } + + if (Log_check(fclose(file) != EOF, + "could not close %s properly: %s\n", + filename, strerror(errno))) + return FALSE; + + rs->dirty = FALSE; + rs->mtime = get_mtime(rs->serv); + + return TRUE; +} + + +void +Req_remove( const char *serv, const char *msgId ) +{ + Reqserv* rs; + char** z; + + ASSERT( is_open ); + Log_dbg("Req_remove(\"%s\", \"%s\")", serv, msgId); + + if (getReqserv(serv, &rs) == FALSE) + return; + + z = searchMsgId(rs, msgId); + if ( ! z ) + return; + + free(*z); + *z = 0; + rs->dirty = TRUE; +} + + +Bool +Req_first( const char *serv, Str msgId ) +{ + Reqserv* rs; + + ASSERT( is_open ); + ASSERT( !iterator && !iterator_end ); + + if (getReqserv(serv, &rs) == FALSE) + return FALSE; + + if (!rs->reql) + return FALSE; + + iterator = rs->reql - 1; + iterator_end = rs->reql + rs->reql_length; + + return Req_next(msgId); +} + + +Bool +Req_next( Str msgId ) +{ + ASSERT( is_open ); + ASSERT(iterator && iterator_end); + + if (iterator >= iterator_end) + return FALSE; + iterator++; + + while (iterator < iterator_end) { + if (!*iterator) + iterator++; + else { + Utl_cpyStr(msgId, *iterator); + return TRUE; + } + } + + iterator = iterator_end = 0; + return FALSE; +} + + +/* Get exclusive access to all request files. Maybe we already have had it, + and the cache is outdated. So we delete request files, which have + changed recently, from cache. These files will be reread on demand. +*/ +Bool +Req_open(void) +{ + Log_dbg("opening request database"); + ASSERT(is_open == FALSE); + cleanupReqserv(); + is_open = TRUE; + return TRUE; +} + + +/* Do not occupy the request files any longer. Write any changes to disk. + Return TRUE on success, FALSE if an IO error occurs. */ +void Req_close(void) +{ + Bool ret = TRUE; + Reqserv* rs; + Log_dbg("closing request database, writing changes to disk"); + ASSERT(is_open == TRUE); + + for (rs = reqserv; rs; rs = rs->next) { + if (rs->dirty == TRUE) { + if (!writeRequestfile(rs)) + ret = FALSE; + } + } + + is_open = FALSE; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/request.h Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,51 @@ +/* + request.h + + Collection of requested articles. + + $Id: request.h 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#ifndef REQ_H +#define REQ_H + +#include "common.h" + +/* Is request for message msgId from server serv already recorded? This + function has no error detection facility. On error, FALSE is returned. + Nevertheless, errors are logged. */ +Bool +Req_contains( const char *serv, const char *msgId ); + +/* Add request for message "msgId" from server "serv". Return TRUE if + successful. */ +Bool +Req_add( const char *serv, const char *msgId ); + +/* Remove request for message msgIg from server serv. This function does + not return any errors. Nevertheless, they are logged. */ +void +Req_remove( const char *serv, const char *msgId ); + +/* Begin iteration through all messages requested from one server. Return + TRUE if there are any requests. Save first message ID in msgId. On + error, it is logged, and FALSE is returned. +*/ +Bool +Req_first( const char *serv, Str msgId ); + +/* Continue iteration. Return TRUE on success, FALSE when there are no more + requests. Save message ID in msgId. On error, it is logged, and FALSE is + returned. */ +Bool +Req_next( Str msgId ); + +/* Get exclusive access to the request files. Refresh cache as necessary. */ +Bool +Req_open(void); + +/* Write changes to disk */ +void +Req_close(void); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server.c Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,1544 @@ +/* + server.c + + $Id: server.c 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#if HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include "server.h" +#include <ctype.h> +#include <signal.h> +#include <stdarg.h> +#include <sys/time.h> +#include <sys/types.h> +#include <time.h> +#include <unistd.h> +#include "client.h" +#include "common.h" +#include "configfile.h" +#include "content.h" +#include "control.h" +#include "database.h" +#include "dynamicstring.h" +#include "fetch.h" +#include "fetchlist.h" +#include "group.h" +#include "itemlist.h" +#include "lock.h" +#include "log.h" +#include "online.h" +#include "outgoing.h" +#include "post.h" +#include "protocol.h" +#include "pseudo.h" +#include "request.h" +#include "util.h" +#include "wildmat.h" + +struct +{ + Bool running; + int artPtr; + Str grp; /* selected group, "" if none */ +} serv = { FALSE, 0, "" }; + +typedef struct Cmd +{ + const char *name; + const char *syntax; + /* Returns false, if quit cmd */ + Bool (*cmdProc)( char *arg, const struct Cmd *cmd ); +} +Cmd; + +static Bool doArt( char *arg, const Cmd *cmd ); +static Bool doBody( char *arg, const Cmd *cmd ); +static Bool doGrp( char *arg, const Cmd *cmd ); +static Bool doHead( char *arg, const Cmd *cmd ); +static Bool doHelp( char *arg, const Cmd *cmd ); +static Bool doIhave( char *arg, const Cmd *cmd ); +static Bool doLast( char *arg, const Cmd *cmd ); +static Bool doList( char *arg, const Cmd *cmd ); +static Bool doListgrp( char *arg, const Cmd *cmd ); +static Bool doMode( char *arg, const Cmd *cmd ); +static Bool doNewgrps( char *arg, const Cmd *cmd ); +static Bool doNext( char *arg, const Cmd *cmd ); +static Bool doPost( char *arg, const Cmd *cmd ); +static Bool doSlave( char *arg, const Cmd *cmd ); +static Bool doStat( char *arg, const Cmd *cmd ); +static Bool doQuit( char *arg, const Cmd *cmd ); +static Bool doXhdr( char *arg, const Cmd *cmd ); +static Bool doXpat( char *arg, const Cmd *cmd ); +static Bool doXOver( char *arg, const Cmd *cmd ); +static Bool notImplemented( char *arg, const Cmd *cmd ); +static void putStat( unsigned int stat, const char *fmt, ... ); + +Cmd commands[] = +{ + { "article", "ARTICLE [msg-id|n]", &doArt }, + { "body", "BODY [msg-id|n]", &doBody }, + { "head", "HEAD [msg-id|n]", &doHead }, + { "group", "GROUP grp", &doGrp }, + { "help", "HELP", &doHelp }, + { "ihave", "IHAVE (ignored)", &doIhave }, + { "last", "LAST", &doLast }, + { "list", "LIST [ACTIVE [pat]]|ACTIVE.TIMES [pat]|" + "EXTENSIONS|NEWSGROUPS [pat]|OVERVIEW.FMT", &doList }, + { "listgroup", "LISTGROUP grp", &doListgrp }, + { "mode", "MODE (ignored)", &doMode }, + { "newgroups", "NEWGROUPS [xx]yymmdd hhmmss [GMT]", &doNewgrps }, + { "newnews", "NEWNEWS (not implemented)", ¬Implemented }, + { "next", "NEXT", &doNext }, + { "post", "POST", &doPost }, + { "quit", "QUIT", &doQuit }, + { "slave", "SLAVE (ignored)", &doSlave }, + { "stat", "STAT [msg-id|n]", &doStat }, + { "xhdr", "XHDR over-field [m[-[n]]]", &doXhdr }, + { "xpat", "XPAT over-field m[-[n]] pat", &doXpat }, + { "xover", "XOVER [m[-[n]]]", &doXOver } +}; + +/* + Notice interest in reading this group. + Automatically subscribe if option set in config file. +*/ +static void +noteInterest( void ) +{ + FetchMode mode; + + Grp_setLastAccess( serv.grp, time( NULL ) ); + if ( ! Grp_local ( serv.grp ) && Cfg_autoSubscribe() && ! Online_true() ) + { + Fetchlist_read(); + if ( ! Fetchlist_contains( serv.grp ) ) + { + if ( strcmp( Cfg_autoSubscribeMode(), "full" ) == 0 ) + mode = FULL; + else if ( strcmp( Cfg_autoSubscribeMode(), "thread" ) == 0 ) + mode = THREAD; + else + mode = OVER; + Fetchlist_add( serv.grp, mode ); + Fetchlist_write(); + Pseudo_autoSubscribed(); + } + } +} + +static void +putStat( unsigned int stat, const char *fmt, ... ) +{ + Str s, line; + va_list ap; + + ASSERT( stat <= 999 ); + va_start( ap, fmt ); + vsnprintf( s, MAXCHAR, fmt, ap ); + va_end( ap ); + snprintf( line, MAXCHAR, "%u %s", stat, s ); + Log_dbg( "[S] %s", line ); + printf( "%s\r\n", line ); +} + +static void +putTxtLn( const char *fmt, ... ) +{ + Str line; + va_list ap; + + va_start( ap, fmt ); + vsnprintf( line, MAXCHAR, fmt, ap ); + va_end( ap ); + Prt_putTxtLn( line, stdout ); +} + +static void +putTxtBuf( const char *buf ) +{ + if ( buf ) + Prt_putTxtBuf( buf, stdout ); +} + +static void +putEndOfTxt( void ) +{ + Prt_putEndOfTxt( stdout ); +} + +static void +putSyntax( const Cmd *cmd ) +{ + putStat( STAT_SYNTAX_ERR, "Syntax error. Usage: %s", cmd->syntax ); +} + +static Bool +getLn( Str line ) +{ + return Prt_getLn( line, stdin ); +} + +static Bool +getTxtLn( Str line, Bool *err ) +{ + return Prt_getTxtLn( line, err, stdin ); +} + +static Bool +notImplemented( char *arg, const Cmd *cmd ) +{ + putStat( STAT_NO_PERMISSION, "Command not implemented" ); + return TRUE; +} + +static void +checkNewArts( const char *grp ) +{ + if ( ! Online_true() + || strcmp( grp, serv.grp ) == 0 + || Grp_local( grp ) + || time( NULL ) - Grp_lastAccess( serv.grp ) < 1800 ) + return; + if ( Fetch_init( Grp_serv( grp ) ) ) + { + Fetch_getNewArts( grp, OVER ); + Fetch_close(); + } +} + +static void +postArts() +{ + Str serv; + + Cfg_beginServEnum(); + while ( Cfg_nextServ( serv ) ) + if ( Fetch_init( serv ) ) + { + Fetch_postArts(); + Fetch_close(); + } +} + +static void +readCont( const char *name ) +{ + Fetchlist_read(); + Cont_read( name ); + if ( ! Grp_local ( name ) + && ! Fetchlist_contains( name ) + && ! Online_true() ) + { + Pseudo_appGeneralInfo(); + Grp_setFirstLast( name, Cont_first(), Cont_last() ); + } +} + +static void +changeToGrp( const char *grp ) +{ + checkNewArts( grp ); + Utl_cpyStr( serv.grp, grp ); + readCont( grp ); + serv.artPtr = Cont_first(); +} + +static Bool +doGrp( char *arg, const Cmd *cmd ) +{ + int first, last, numb; + + if ( arg[ 0 ] == '\0' ) + putSyntax( cmd ); + else if ( ! Grp_exists( arg ) ) + putStat( STAT_NO_SUCH_GRP, "No such group" ); + else + { + changeToGrp( arg ); + first = Cont_first(); + last = Cont_last(); + if ( ( first == 0 && last == 0 ) + || first > last ) + first = last = numb = 0; + else + numb = last - first + 1; + putStat( STAT_GRP_SELECTED, "%lu %lu %lu %s selected", + numb, first, last, arg ); + } + return TRUE; +} + +static Bool +testGrpSelected( void ) +{ + if ( *serv.grp == '\0' ) + { + putStat( STAT_NO_GRP_SELECTED, "No group selected" ); + return FALSE; + } + return TRUE; +} + +static void +findServ( const char *msgId, Str result ) +{ + const char *p, *pColon, *serv; + Str s, grp; + + Utl_cpyStr( result, "(unknown)" ); + if ( Db_contains( msgId ) ) + { + Utl_cpyStr( s, Db_xref( msgId ) ); + p = strtok( s, " \t" ); + if ( p ) + do + { + pColon = strstr( p, ":" ); + if ( pColon ) + { + Utl_cpyStrN( grp, p, pColon - p ); + serv = Grp_serv( grp ); + if ( Cfg_servIsPreferential( serv, result ) ) + Utl_cpyStr( result, serv ); + } + } + while ( ( p = strtok( NULL, " \t" ) ) ); + } +} + +static Bool +retrieveArt( const char *msgId ) +{ + Str serv; + + findServ( msgId, serv ); + if ( strcmp( serv, "(unknown)" ) == 0 + || strcmp( serv, GRP_LOCAL_SERVER_NAME ) == 0 ) + return FALSE; + if ( ! Client_connect( serv ) ) + return FALSE; + Client_retrieveArt( msgId ); + Client_disconnect(); + return TRUE; +} + +static Bool +checkNumb( int numb ) +{ + if ( ! testGrpSelected() ) + return FALSE; + if ( ! Cont_validNumb( numb ) ) + { + putStat( STAT_NO_SUCH_NUMB, "No such article" ); + return FALSE; + } + return TRUE; +} + +/* + Parse arguments for ARTICLE, BODY, HEAD, STAT commands. + Return message-ID and article number (0 if unknown). +*/ +static Bool +whichId( const char **msgId, int *numb, char *arg ) +{ + const Over *ov; + int n; + + if ( sscanf( arg, "%d", &n ) == 1 ) + { + if ( ! checkNumb( n ) ) + return FALSE; + serv.artPtr = n; + ov = Cont_get( n ); + *msgId = Ov_msgId( ov ); + *numb = n; + } + else if ( strcmp( arg, "" ) == 0 ) + { + if ( ! checkNumb( serv.artPtr ) ) + return FALSE; + ov = Cont_get( serv.artPtr ); + *msgId = Ov_msgId( ov ); + *numb = serv.artPtr; + } + else + { + *msgId = arg; + *numb = 0; + } + if ( ! Pseudo_isGeneralInfo( *msgId ) && ! Db_contains( *msgId ) ) + { + putStat( STAT_NO_SUCH_NUMB, "No such article" ); + return FALSE; + } + return TRUE; +} + +void +touchArticle( const char *msgId ) +{ + int stat = Db_stat( msgId ); + stat |= DB_INTERESTING; + Db_setStat( msgId, stat ); + Db_updateLastAccess( msgId ); +} + +static void +touchReferences( const char *msgId ) +{ + Str s; + int len; + char *p; + const char *ref = Db_ref( msgId ); + + while ( TRUE ) + { + p = s; + while ( *ref != '<' ) + if ( *(ref++) == '\0' ) + return; + len = 0; + while ( *ref != '>' ) + { + if ( *ref == '\0' || ++len >= MAXCHAR - 1 ) + return; + *(p++) = *(ref++); + } + *(p++) = '>'; + *p = '\0'; + if ( Db_contains( s ) ) + touchArticle( s ); + } +} + +static void +doBodyInDb( const char *msgId ) +{ + int stat; + Str serv; + + touchArticle( msgId ); + touchReferences( msgId ); + stat = Db_stat( msgId ); + if ( Online_true() && ( stat & DB_NOT_DOWNLOADED ) ) + { + retrieveArt( msgId ); + stat = Db_stat( msgId ); + } + if ( stat & DB_RETRIEVING_FAILED ) + { + Db_setStat( msgId, stat & ~DB_RETRIEVING_FAILED ); + putTxtBuf( Db_body( msgId ) ); + } + else if ( stat & DB_NOT_DOWNLOADED ) + { + findServ( msgId, serv ); + if ( Req_contains( serv, msgId ) ) + putTxtBuf( Pseudo_alreadyMarkedBody() ); + else if ( strcmp( serv, "(unknown)" ) != 0 && + strcmp( serv, GRP_LOCAL_SERVER_NAME ) != 0 && + Req_add( serv, msgId ) ) + putTxtBuf( Pseudo_markedBody() ); + else + putTxtBuf( Pseudo_markingFailedBody() ); + } + else + putTxtBuf( Db_body( msgId ) ); +} + +static Bool +doBody( char *arg, const Cmd *cmd ) +{ + const char *msgId; + int numb; + + if ( ! whichId( &msgId, &numb, arg ) ) + return TRUE; + putStat( STAT_BODY_FOLLOWS, "%ld %s Body", numb, msgId ); + if ( Pseudo_isGeneralInfo( msgId ) ) + putTxtBuf( Pseudo_generalInfoBody() ); + else + doBodyInDb( msgId ); + putEndOfTxt(); + noteInterest(); + return TRUE; +} + +static void +doHeadInDb( const char *msgId ) +{ + putTxtBuf( Db_header( msgId ) ); +} + +static Bool +doHead( char *arg, const Cmd *cmd ) +{ + const char *msgId; + int numb; + + if ( ! whichId( &msgId, &numb, arg ) ) + return TRUE; + putStat( STAT_HEAD_FOLLOWS, "%ld %s Head", numb, msgId ); + if ( Pseudo_isGeneralInfo( msgId ) ) + putTxtBuf( Pseudo_generalInfoHead() ); + else + doHeadInDb( msgId ); + putEndOfTxt(); + return TRUE; +} + +static void +doArtInDb( const char *msgId ) +{ + doHeadInDb( msgId ); + putTxtLn( "" ); + doBodyInDb( msgId ); +} + +static Bool +doArt( char *arg, const Cmd *cmd ) +{ + const char *msgId; + int numb; + + if ( ! whichId( &msgId, &numb, arg ) ) + return TRUE; + putStat( STAT_ART_FOLLOWS, "%ld %s Article", numb, msgId ); + if ( Pseudo_isGeneralInfo( msgId ) ) + { + putTxtBuf( Pseudo_generalInfoHead() ); + putTxtLn( "" ); + putTxtBuf( Pseudo_generalInfoBody() ); + } + else + doArtInDb( msgId ); + putEndOfTxt(); + noteInterest(); + return TRUE; +} + +static Bool +doHelp( char *arg, const Cmd *cmd ) +{ + unsigned int i; + + putStat( STAT_HELP_FOLLOWS, "Help" ); + putTxtBuf( "\nCommands:\n\n" ); + for ( i = 0; i < sizeof( commands ) / sizeof( commands[ 0 ] ); ++i ) + putTxtLn( "%s", commands[ i ].syntax ); + putEndOfTxt(); + return TRUE; +} + +static Bool +doIhave( char *arg, const Cmd *cmd ) +{ + putStat( STAT_ART_REJECTED, "Command not used" ); + return TRUE; +} + +static Bool +doLast( char *arg, const Cmd *cmd ) +{ + int n; + + if ( testGrpSelected() ) + { + n = serv.artPtr; + if ( ! Cont_validNumb( n ) ) + putStat( STAT_NO_ART_SELECTED, "No article selected" ); + else + { + while ( ! Cont_validNumb( --n ) && n >= Cont_first() ); + if ( ! Cont_validNumb( n ) ) + putStat( STAT_NO_PREV_ART, "No previous article" ); + else + { + putStat( STAT_ART_RETRIEVED, "%ld %s selected", + n, Ov_msgId( Cont_get( n ) ) ); + serv.artPtr = n; + } + } + } + return TRUE; +} + +static void +printGroups( const char *pat, void (*printProc)( Str, const char* ) ) +{ + Str line; + const char *g; + FILE *f; + sig_t lastHandler; + int ret; + + putStat( STAT_GRPS_FOLLOW, "Groups" ); + fflush( stdout ); + Log_dbg( "[S FLUSH]" ); + if ( Grp_exists( pat ) ) + { + (*printProc)( line, pat ); + if ( ! Prt_putTxtLn( line, stdout ) ) + Log_err( "Writing to stdout failed." ); + } + else + { + lastHandler = signal( SIGPIPE, SIG_IGN ); + f = popen( "sort", "w" ); + if ( f == NULL ) + { + Log_err( "Cannot open pipe to 'sort'" ); + if ( Grp_firstGrp( &g ) ) + do + if ( Wld_match( g, pat ) ) + { + (*printProc)( line, g ); + if ( ! Prt_putTxtLn( line, stdout ) ) + Log_err( "Writing to stdout failed." ); + } + while ( Grp_nextGrp( &g ) ); + } + else + { + if ( Grp_firstGrp( &g ) ) + do + if ( Wld_match( g, pat ) ) + { + (*printProc)( line, g ); + if ( ! Prt_putTxtLn( line, f ) ) + { + Log_err( "Writing to 'sort' pipe failed." ); + break; + } + } + while ( Grp_nextGrp( &g ) ); + ret = pclose( f ); + if ( ret != EXIT_SUCCESS ) + Log_err( "sort command returned %d", ret ); + fflush( stdout ); + Log_dbg( "[S FLUSH]" ); + signal( SIGPIPE, lastHandler ); + } + } + putEndOfTxt(); +} + +static void +printActiveTimes( Str result, const char *grp ) +{ + snprintf( result, MAXCHAR, "%s %ld", grp, Grp_created( grp ) ); +} + +static void +doListActiveTimes( const char *pat ) +{ + printGroups( pat, &printActiveTimes ); +} + +static void +printActive( Str result, const char *grp ) +{ + snprintf( result, MAXCHAR, "%s %d %d %c", + grp, Grp_last( grp ), Grp_first( grp ), Grp_postAllow( grp ) ); +} + +static void +doListActive( const char *pat ) +{ + printGroups( pat, &printActive ); +} + +static void +printNewsgrp( Str result, const char *grp ) +{ + snprintf( result, MAXCHAR, "%s %s", grp, Grp_dsc( grp ) ); +} + +static void +doListNewsgrps( const char *pat ) +{ + printGroups( pat, &printNewsgrp ); +} + +static void +putGrp( const char *name ) +{ + putTxtLn( "%s %lu %lu y", name, Grp_last( name ), Grp_first( name ) ); +} + +static void +doListOverFmt( void ) +{ + putStat( STAT_GRPS_FOLLOW, "Overview format" ); + putTxtBuf( "Subject:\n" + "From:\n" + "Date:\n" + "Message-ID:\n" + "References:\n" + "Bytes:\n" + "Lines:\n" ); + putEndOfTxt(); +} + +static void +doListExtensions( void ) +{ + putStat( STAT_CMD_OK, "Extensions" ); + putTxtBuf( " LISTGROUP\n" + " XOVER\n" ); + putEndOfTxt(); +} + +static Bool +doList( char *line, const Cmd *cmd ) +{ + Str s, arg; + const char *pat; + + if ( sscanf( line, "%s", s ) != 1 ) + doListActive( "*" ); + else + { + Utl_toLower( s ); + strcpy( arg, Utl_restOfLn( line, 1 ) ); + pat = Utl_stripWhiteSpace( arg ); + if ( pat[ 0 ] == '\0' ) + pat = "*"; + if ( strcmp( "active", s ) == 0 ) + doListActive( pat ); + else if ( strcmp( "overview.fmt", s ) == 0 ) + doListOverFmt(); + else if ( strcmp( "newsgroups", s ) == 0 ) + doListNewsgrps( pat ); + else if ( strcmp( "active.times", s ) == 0 ) + doListActiveTimes( pat ); + else if ( strcmp( "extensions", s ) == 0 ) + doListExtensions(); + else + putSyntax( cmd ); + } + return TRUE; +} + +static Bool +doListgrp( char *arg, const Cmd *cmd ) +{ + const Over *ov; + int first, last, i; + + if ( ! Grp_exists( arg ) ) + putStat( STAT_NO_SUCH_GRP, "No such group" ); + else + { + changeToGrp( arg ); + first = Cont_first(); + last = Cont_last(); + putStat( STAT_GRP_SELECTED, "Article list" ); + for ( i = first; i <= last; ++i ) + if ( ( ov = Cont_get( i ) ) ) + putTxtLn( "%lu", i ); + putEndOfTxt(); + } + return TRUE; +} + +static Bool +doMode( char *arg, const Cmd *cmd ) +{ + putStat( STAT_READY_POST_ALLOW, "Ok" ); + return TRUE; +} + +static unsigned long +getTimeInSeconds( unsigned int year, unsigned int mon, unsigned int day, + unsigned int hour, unsigned int min, unsigned int sec ) +{ + struct tm t = { 0 }; + + t.tm_year = year - 1900; + t.tm_mon = mon - 1; + t.tm_mday = day; + t.tm_hour = hour; + t.tm_min = min; + t.tm_sec = sec; + return mktime( &t ); +} + + +static Bool +doNewgrps( char *arg, const Cmd *cmd ) +{ + time_t t, now, lastUpdate; + unsigned int year, mon, day, hour, min, sec, cent, len; + const char *g; + Str date, timeofday, file; + + if ( sscanf( arg, "%s %s", date, timeofday ) != 2 ) + { + putSyntax( cmd ); + return TRUE; + } + len = strlen( date ); + switch ( len ) + { + case 6: + if ( sscanf( date, "%2u%2u%2u", &year, &mon, &day ) != 3 ) + { + putSyntax( cmd ); + return TRUE; + } + now = time( NULL ); + cent = 1900; + while ( now > getTimeInSeconds( cent + 100, 1, 1, 0, 0, 0 ) ) + cent += 100; + year += cent; + break; + case 8: + if ( sscanf( date, "%4u%2u%2u", &year, &mon, &day ) != 3 ) + { + putSyntax( cmd ); + return TRUE; + } + break; + default: + putSyntax( cmd ); + return TRUE; + } + if ( sscanf( timeofday, "%2u%2u%2u", &hour, &min, &sec ) != 3 ) + { + putSyntax( cmd ); + return TRUE; + } + if ( year < 1970 || mon == 0 || mon > 12 || day == 0 || day > 31 + || hour > 23 || min > 59 || sec > 60 ) + { + putSyntax( cmd ); + return TRUE; + } + snprintf( file, MAXCHAR, "%s/groupinfo.lastupdate", Cfg_spoolDir() ); + t = getTimeInSeconds( year, mon, day, hour, min, sec ); + putStat( STAT_NEW_GRP_FOLLOW, "New groups since %s", arg ); + + if ( ! Utl_getStamp( &lastUpdate, file ) || t <= lastUpdate ) + { + if ( Grp_firstGrp( &g ) ) + do + if ( Grp_created( g ) > t ) + putGrp( g ); + while ( Grp_nextGrp( &g ) ); + } + putEndOfTxt(); + return TRUE; +} + +static Bool +doNext( char *arg, const Cmd *cmd ) +{ + int n; + + if ( testGrpSelected() ) + { + n = serv.artPtr; + if ( ! Cont_validNumb( n ) ) + putStat( STAT_NO_ART_SELECTED, "No article selected" ); + else + { + while ( ! Cont_validNumb( ++n ) && n <= Cont_last() ); + if ( ! Cont_validNumb( n ) ) + putStat( STAT_NO_NEXT_ART, "No next article" ); + else + { + putStat( STAT_ART_RETRIEVED, "%ld %s selected", + n, Ov_msgId( Cont_get( n ) ) ); + serv.artPtr = n; + } + } + } + return TRUE; +} + +/* Cancel and return TRUE if need to send cancel message on to server. */ +static Bool +controlCancel( const char *cancelId ) +{ + return ( Ctrl_cancel( cancelId ) == CANCEL_NEEDS_MSG ); +} + +/* + It's a control message. Currently we only know about 'cancel' + messages; others are passed on for outside groups, and logged + as ignored for local groups. + */ +static Bool +handleControl( ItemList *control, ItemList *newsgroups, + const char *msgId, const DynStr *art ) +{ + const char *grp; + const char *op; + Bool err = FALSE; + Bool localDone = FALSE; + + op = Itl_first( control ); + if ( op == NULL ) + { + Log_err( "Malformed control line." ); + return TRUE; + } + else if ( strcasecmp( op, "cancel" ) == 0 ) + { + if ( controlCancel( Itl_next( control ) ) ) + localDone = TRUE; + else + return err; + } + + /* Pass on for outside groups. */ + for( grp = Itl_first( newsgroups ); + grp != NULL; + grp = Itl_next( newsgroups ) ) + { + if ( Grp_exists( grp ) && ! Grp_local( grp ) ) + { + if ( ! Out_add( Grp_serv( grp ), msgId, art ) ) + { + Log_err( "Cannot add posted article to outgoing directory" ); + err = TRUE; + } + break; + } + } + + if ( localDone ) + return err; + + /* Log 'can't do' for internal groups. */ + for( grp = Itl_first( newsgroups ); + grp != NULL; + grp = Itl_next( newsgroups ) ) + { + if ( Grp_exists( grp ) && Grp_local( grp ) ) + Log_inf( "Ignoring control '%s' for '%s'.", op, grp ); + } + + return err; +} + +static Bool +postArticle( ItemList *newsgroups, const char *msgId, const DynStr *art ) +{ + const char *grp; + Bool err; + Bool oneLocal; + + err = oneLocal = FALSE; + + /* Run round first doing all local groups. */ + for( grp = Itl_first( newsgroups ); + grp != NULL; + grp = Itl_next( newsgroups ) ) + { + if ( Grp_local( grp ) ) + { + if ( ! oneLocal ) + { + if ( ! Post_open( DynStr_str( art ) ) ) + { + err = TRUE; + break; + } + else + oneLocal = TRUE; + } + + if ( ! Post_add( grp ) ) + err = TRUE; + } + } + if ( oneLocal ) + Post_close(); + + /* Now look for a valid external group. */ + for( grp = Itl_first( newsgroups ); + grp != NULL; + grp = Itl_next( newsgroups ) ) + { + if ( Grp_exists( grp ) && ! Grp_local( grp ) ) + { + if ( ! Out_add( Grp_serv( grp ), msgId, art ) ) + { + Log_err( "Cannot add posted article to outgoing directory" ); + err = TRUE; + } + break; + } + } + + return err; +} + +static Bool +doPost( char *arg, const Cmd *cmd ) +{ + Bool err, replyToFound, dateFound, inHeader; + DynStr *s; + Str line, field, val, msgId, from; + const char* p; + ItemList * newsgroups, *control; + + /* + Get article and make following changes to the header: + - add/replace/cut Message-ID depending on config options + - add Reply-To with content of From, if missing + (some providers overwrite From field) + - rename X-Sender header to X-NOFFLE-X-Sender + (some providers want to insert their own X-Sender) + + For doing this, it is not necessary to parse multiple-line + headers. + */ + putStat( STAT_SEND_ART, "Continue (end with period)" ); + fflush( stdout ); + Log_dbg( "[S FLUSH]" ); + s = new_DynStr( 10000 ); + msgId[ 0 ] = '\0'; + from[ 0 ] = '\0'; + newsgroups = control = NULL; + replyToFound = dateFound = FALSE; + inHeader = TRUE; + while ( getTxtLn( line, &err ) ) + { + if ( inHeader ) + { + p = Utl_stripWhiteSpace( line ); + if ( *p == '\0' ) + { + inHeader = FALSE; + if ( from[ 0 ] == '\0' ) + Log_err( "Posted message has no From field" ); + if ( ! Cfg_removeMsgId() ) + { + if ( Cfg_replaceMsgId() ) + { + Prt_genMsgId( msgId, from, "NOFFLE" ); + Log_dbg( "Replacing Message-ID with '%s'", msgId ); + } + else if ( msgId[ 0 ] == '\0' ) + { + Prt_genMsgId( msgId, from, "NOFFLE" ); + + Log_inf( "Adding missing Message-ID '%s'", msgId ); + } + else if ( ! Prt_isValidMsgId( msgId ) ) + { + Log_ntc( "Replacing invalid Message-ID with '%s'", + msgId ); + Prt_genMsgId( msgId, from, "NOFFLE" ); + } + DynStr_app( s, "Message-ID: " ); + DynStr_appLn( s, msgId ); + } + if ( ! replyToFound && from[ 0 ] != '\0' ) + { + Log_dbg( "Adding Reply-To field to posted message." ); + DynStr_app( s, "Reply-To: " ); + DynStr_appLn( s, from ); + } + if ( ! dateFound ) + { + time_t t; + + time( &t ); + Utl_rfc822Date( t, val ); + DynStr_app( s, "Date: " ); + DynStr_appLn( s, val ); + } + DynStr_appLn( s, p ); + } + else if ( Prt_getField( field, val, p ) ) + { + if ( strcmp( field, "message-id" ) == 0 ) + strcpy( msgId, val ); + else if ( strcmp( field, "from" ) == 0 ) + { + strcpy( from, val ); + DynStr_appLn( s, p ); + } + else if ( strcmp( field, "newsgroups" ) == 0 ) + { + Utl_toLower( val ); + newsgroups = new_Itl ( val, " ," ); + DynStr_appLn( s, p ); + } + else if ( strcmp( field, "control" ) == 0 ) + { + control = new_Itl ( val, " " ); + DynStr_appLn( s, p ); + } + else if ( strcmp( field, "reply-to" ) == 0 ) + { + replyToFound = TRUE; + DynStr_appLn( s, p ); + } + else if ( strcmp( field, "date" ) == 0 ) + { + dateFound = TRUE; + DynStr_appLn( s, p ); + } + else if ( strcmp( field, "x-sender" ) == 0 ) + { + DynStr_app( s, "X-NOFFLE-X-Sender: " ); + DynStr_appLn( s, val ); + } + else + DynStr_appLn( s, p ); + } + else + DynStr_appLn( s, line ); + } + else + DynStr_appLn( s, line ); + } + if ( inHeader ) + Log_err( "Posted message has no body" ); + if ( ! err ) + { + if ( newsgroups == NULL || Itl_count( newsgroups ) == 0 ) + { + Log_err( "Posted message has no valid Newsgroups header field" ); + err = TRUE; + } + else + { + const char *grp; + Bool knownGrp = FALSE; + Bool postAllowedGrp = FALSE; + + /* Check at least one group is known. */ + for( grp = Itl_first( newsgroups ); + grp != NULL; + grp = Itl_next( newsgroups ) ) + { + if ( Grp_exists( grp ) ) + { + knownGrp = TRUE; + switch( Grp_postAllow( grp ) ) + { + case 'n': + break; + case 'm': + /* Can't post to moderated local groups. */ + postAllowedGrp = ! Grp_local( grp ); + break; + default: + postAllowedGrp = TRUE; + } + if ( postAllowedGrp ) + break; + } + } + + if ( ! knownGrp ) + { + + Log_err( "No known group in Newsgroups header field" ); + err = TRUE; + } + else if ( ! postAllowedGrp ) + { + + Log_err( "No group permits posting" ); + err = TRUE; + } + else + { + err = ( control == NULL ) + ? postArticle( newsgroups, msgId, s ) + : handleControl( control, newsgroups, msgId, s ); + } + } + } + if ( err ) + putStat( STAT_POST_FAILED, "Posting failed" ); + else + { + putStat( STAT_POST_OK, "Message posted" ); + if ( Online_true() ) + postArts(); + } + del_Itl( newsgroups ); + del_Itl( control ); + del_DynStr( s ); + return TRUE; +} + +static void +parseRange( const char *s, int *first, int *last, int *numb ) +{ + int r, i; + char* p; + Str t; + + Utl_cpyStr( t, s ); + p = Utl_stripWhiteSpace( t ); + r = sscanf( p, "%d-%d", first, last ); + if ( r < 1 ) + { + *first = serv.artPtr; + *last = serv.artPtr; + } + else if ( r == 1 ) + { + if ( p[ strlen( p ) - 1 ] == '-' ) + *last = Cont_last(); + else + *last = *first; + } + if ( *first < Cont_first() ) + *first = Cont_first(); + if ( *last > Cont_last() ) + *last = Cont_last(); + if ( *first > Cont_last() || *last < Cont_first() ) + *last = *first - 1; + *numb = 0; + for ( i = *first; i <= *last; ++i ) + if ( Cont_validNumb( i ) ) + ++(*numb); +} + +static Bool +doXhdr( char *arg, const Cmd *cmd ) +{ + int first, last, i, n, numb; + enum { SUBJ, FROM, DATE, MSG_ID, REF, BYTES, LINES } what; + const char *p; + const Over *ov; + Str whatStr; + + if ( ! testGrpSelected() ) + return TRUE; + if ( sscanf( arg, "%s", whatStr ) != 1 ) + { + putSyntax( cmd ); + return TRUE; + } + Utl_toLower( whatStr ); + if ( strcmp( whatStr, "subject" ) == 0 ) + what = SUBJ; + else if ( strcmp( whatStr, "from" ) == 0 ) + what = FROM; + else if ( strcmp( whatStr, "date" ) == 0 ) + what = DATE; + else if ( strcmp( whatStr, "message-id" ) == 0 ) + what = MSG_ID; + else if ( strcmp( whatStr, "references" ) == 0 ) + what = REF; + else if ( strcmp( whatStr, "bytes" ) == 0 ) + what = BYTES; + else if ( strcmp( whatStr, "lines" ) == 0 ) + what = LINES; + else + { + putStat( STAT_HEAD_FOLLOWS, "Unknown header (empty list follows)" ); + putEndOfTxt(); + return TRUE; + } + p = Utl_restOfLn( arg, 1 ); + parseRange( p, &first, &last, &numb ); + if ( numb == 0 ) + putStat( STAT_NO_ART_SELECTED, "No articles selected" ); + else + { + putStat( STAT_HEAD_FOLLOWS, "%s header %lu-%lu", + whatStr, first, last ) ; + for ( i = first; i <= last; ++i ) + if ( ( ov = Cont_get( i ) ) ) + { + n = Ov_numb( ov ); + switch ( what ) + { + case SUBJ: + putTxtLn( "%lu %s", n, Ov_subj( ov ) ); + break; + case FROM: + putTxtLn( "%lu %s", n, Ov_from( ov ) ); + break; + case DATE: + putTxtLn( "%lu %s", n, Ov_date( ov ) ); + break; + case MSG_ID: + putTxtLn( "%lu %s", n, Ov_msgId( ov ) ); + break; + case REF: + putTxtLn( "%lu %s", n, Ov_ref( ov ) ); + break; + case BYTES: + putTxtLn( "%lu %d", n, Ov_bytes( ov ) ); + break; + case LINES: + putTxtLn( "%lu %d", n, Ov_lines( ov ) ); + break; + default: + ASSERT( FALSE ); + } + } + putEndOfTxt(); + } + return TRUE; +} + +static Bool +doXpat( char *arg, const Cmd *cmd ) +{ + int first, last, i, n; + enum { SUBJ, FROM, DATE, MSG_ID, REF } what; + const Over *ov; + Str whatStr, pat; + + if ( ! testGrpSelected() ) + return TRUE; + if ( sscanf( arg, "%s %d-%d %s", whatStr, &first, &last, pat ) != 4 ) + { + if ( sscanf( arg, "%s %d- %s", whatStr, &first, pat ) == 3 ) + last = Cont_last(); + else if ( sscanf( arg, "%s %d %s", whatStr, &first, pat ) == 3 ) + last = first; + else + { + putSyntax( cmd ); + return TRUE; + } + } + Utl_toLower( whatStr ); + if ( strcmp( whatStr, "subject" ) == 0 ) + what = SUBJ; + else if ( strcmp( whatStr, "from" ) == 0 ) + what = FROM; + else if ( strcmp( whatStr, "date" ) == 0 ) + what = DATE; + else if ( strcmp( whatStr, "message-id" ) == 0 ) + what = MSG_ID; + else if ( strcmp( whatStr, "references" ) == 0 ) + what = REF; + else + { + putStat( STAT_HEAD_FOLLOWS, "invalid header (empty list follows)" ); + putEndOfTxt(); + return TRUE; + } + putStat( STAT_HEAD_FOLLOWS, "header" ) ; + for ( i = first; i <= last; ++i ) + if ( ( ov = Cont_get( i ) ) ) + { + n = Ov_numb( ov ); + switch ( what ) + { + case SUBJ: + if ( Wld_match( Ov_subj( ov ), pat ) ) + putTxtLn( "%lu %s", n, Ov_subj( ov ) ); + break; + case FROM: + if ( Wld_match( Ov_from( ov ), pat ) ) + putTxtLn( "%lu %s", n, Ov_from( ov ) ); + break; + case DATE: + if ( Wld_match( Ov_date( ov ), pat ) ) + putTxtLn( "%lu %s", n, Ov_date( ov ) ); + break; + case MSG_ID: + if ( Wld_match( Ov_msgId( ov ), pat ) ) + putTxtLn( "%lu %s", n, Ov_msgId( ov ) ); + break; + case REF: + if ( Wld_match( Ov_ref( ov ), pat ) ) + putTxtLn( "%lu %s", n, Ov_ref( ov ) ); + break; + default: + ASSERT( FALSE ); + } + } + putEndOfTxt(); + return TRUE; +} + +static Bool +doSlave( char *arg, const Cmd *cmd ) +{ + putStat( STAT_CMD_OK, "Ok" ); + return TRUE; +} + +static Bool +doStat( char *arg, const Cmd *cmd ) +{ + const char *msgId; + int numb; + + if ( ! whichId( &msgId, &numb, arg ) ) + return TRUE; + if ( numb > 0 ) + putStat( STAT_ART_RETRIEVED, "%ld %s selected", + numb, msgId ); + else + putStat( STAT_ART_RETRIEVED, "0 %s selected", msgId ); + return TRUE; +} + +static Bool +doQuit( char *arg, const Cmd *cmd ) +{ + putStat( STAT_GOODBYE, "Goodbye" ); + return FALSE; +} + +static Bool +doXOver( char *arg, const Cmd *cmd ) +{ + int first, last, i, n; + const Over *ov; + + if ( ! testGrpSelected() ) + return TRUE; + parseRange( arg, &first, &last, &n ); + if ( n == 0 ) + putStat( STAT_NO_ART_SELECTED, "No articles selected" ); + else + { + putStat( STAT_OVERS_FOLLOW, "Overview %ld-%ld", first, last ); + for ( i = first; i <= last; ++i ) + if ( ( ov = Cont_get( i ) ) ) + putTxtLn( "%lu\t%s\t%s\t%s\t%s\t%s\t%d\t%d\t", + Ov_numb( ov ), Ov_subj( ov ), Ov_from( ov ), + Ov_date( ov ), Ov_msgId( ov ), Ov_ref( ov ), + Ov_bytes( ov ), Ov_lines( ov ) ); + putEndOfTxt(); + } + return TRUE; +} + +static void +putFatal( const char *fmt, ... ) +{ + va_list ap; + Str s; + + va_start( ap, fmt ); + vsnprintf( s, MAXCHAR, fmt, ap ); + va_end( ap ); + Log_err( s ); + putStat( STAT_PROGRAM_FAULT, "%s", s ); + fflush( stdout ); + Log_dbg( "[S FLUSH]" ); +} + +/* Parse line, execute command and return FALSE, if it was the quit command. */ +static Bool +parseAndExecute( Str line ) +{ + unsigned int i, n; + Cmd *c; + Str s, arg; + Bool ret; + + if ( sscanf( line, "%s", s ) == 1 ) + { + Utl_toLower( s ); + strcpy( arg, Utl_restOfLn( line, 1 ) ); + n = sizeof( commands ) / sizeof( commands[ 0 ] ); + for ( i = 0, c = commands; i < n; ++i, ++c ) + if ( strcmp( c->name, s ) == 0 ) + { + ret = c->cmdProc( Utl_stripWhiteSpace( arg ), c ); + fflush( stdout ); + Log_dbg( "[S FLUSH]" ); + return ret; + } + } + putStat( STAT_NO_SUCH_CMD, "Command not recognized" ); + fflush( stdout ); + Log_dbg( "[S FLUSH]" ); + return TRUE; +} + +static void +putWelcome( void ) +{ + putStat( STAT_READY_POST_ALLOW, "NNTP server NOFFLE %s", + Cfg_version() ); + fflush( stdout ); + Log_dbg( "[S FLUSH]" ); +} + +static Bool +initServ( void ) +{ + ASSERT( ! serv.running ); + if ( ! Lock_openDatabases() ) + return FALSE; + serv.running = TRUE; + return TRUE; +} + +static void +closeServ( void ) +{ + ASSERT( serv.running ); + serv.running = FALSE; + Lock_closeDatabases(); +} + +void +Serv_run( void ) +{ + Bool done; + int r; + Str line; + struct timeval timeOut; + fd_set readSet; + + putWelcome(); + done = FALSE; + while ( ! done ) + { + FD_ZERO( &readSet ); + FD_SET( STDIN_FILENO, &readSet ); + /* Never hold lock more than 5 seconds (empirically good value, + avoids to close/open databases, if clients sends several + commands, but releases the lock often enough, for allowing + multiple persons to read news at the same time) */ + timeOut.tv_sec = 5; + timeOut.tv_usec = 0; + r = select( STDIN_FILENO + 1, &readSet, NULL, NULL, &timeOut ); + if ( r < 0 ) + done = TRUE; + else if ( r == 0 ) + { + if ( serv.running ) + closeServ(); + } + else /* ( r > 0 ) */ + { + if ( ! serv.running ) + { + if ( ! initServ() ) + { + putFatal( "Cannot init server" ); + done = TRUE; + } + } + if ( ! getLn( line ) ) + { + Log_inf( "Client disconnected. Terminating." ); + done = TRUE; + } + else if ( ! parseAndExecute( line ) ) + done = TRUE; + } + } + if ( serv.running ) + closeServ(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server.h Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,14 @@ +/* + server.h + + Be NNTP server on stdin/stdout. + + $Id: server.h 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#ifndef SERV_H +#define SERV_H + +void Serv_run( void ); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/util.c Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,255 @@ +/* + util.c + + $Id: util.c 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#include "util.h" +#include <errno.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <time.h> +#include <unistd.h> +#include "configfile.h" +#include "log.h" +#include "wildmat.h" + +static const char * +nextWhiteSpace( const char *p ) +{ + while ( *p && ! isspace( *p ) ) + ++p; + return p; +} + +static const char * +nextNonWhiteSpace( const char *p ) +{ + while ( *p && isspace( *p ) ) + ++p; + return p; +} + +const char * +Utl_restOfLn( const char *line, unsigned int token ) +{ + unsigned int i; + const char *p; + + p = line; + for ( i = 0; i < token; ++i ) + { + p = nextNonWhiteSpace( p ); + p = nextWhiteSpace( p ); + } + p = nextNonWhiteSpace( p ); + return p; +} + +const char * +Utl_getLn( Str result, const char *pos ) +{ + int len = 0; + const char *p = pos; + + if ( ! p ) + return NULL; + while ( *p != '\n' ) + { + if ( *p == '\0' ) + { + if ( len > 0 ) + Log_err( "Line not terminated by newline: '%s'", pos ); + return NULL; + } + *(result++) = *(p++); + ++len; + if ( len >= MAXCHAR - 1 ) + { + *result = '\0'; + Log_err( "Utl_getLn: line too long: %s", result ); + return ++p; + } + } + *result = '\0'; + return ++p; + +} + +const char * +Utl_ungetLn( const char *str, const char *p ) +{ + if ( str == p ) + return FALSE; + --p; + if ( *p != '\n' ) + { + Log_dbg( "Utl_ungetLn: not at beginning of line" ); + return NULL; + } + --p; + while ( TRUE ) + { + if ( p == str ) + return p; + if ( *p == '\n' ) + return p + 1; + --p; + } +} + +const char * +Utl_getHeaderLn( Str result, const char *p ) +{ + const char * res = Utl_getLn( result, p ); + + /* Look for followon line if this isn't a blank line. */ + if ( res != NULL && !isspace( result[ 0 ] ) ) + while ( res != NULL && res[ 0 ] != '\n' && isspace( res[ 0 ] ) ) + { + Str nextLine; + const char *here; + char *next; + + here = res; + res = Utl_getLn( nextLine, res ); + next = Utl_stripWhiteSpace( nextLine ); + + if ( next[ 0 ] != '\0' ) + { + Utl_catStr( result, " " ); + Utl_catStr( result, next ); + } + else + { + res = here; + break; + } + } + + return res; +} + +void +Utl_toLower( Str line ) +{ + char *p; + + p = line; + while ( *p ) + { + *p = tolower( *p ); + ++p; + } +} + +char * +Utl_stripWhiteSpace( char *line ) +{ + char *p; + + while ( isspace( *line ) ) + ++line; + p = line + strlen( line ) - 1; + while ( isspace( *p ) ) + { + *p = '\0'; + --p; + } + return line; +} + +void +Utl_stripComment( char *line ) +{ + for ( ; *line != '\0'; line++ ) + if ( *line =='#' ) + { + *line = '\0'; + break; + } +} + +void +Utl_cpyStr( Str dst, const char *src ) +{ + dst[ 0 ] = '\0'; + strncat( dst, src, MAXCHAR ); +} + +void +Utl_cpyStrN( Str dst, const char *src, size_t n ) +{ + if ( n > MAXCHAR ) + n = MAXCHAR; + dst[ 0 ] = '\0'; + strncat( dst, src, n ); +} + +void +Utl_catStr( Str dst, const char *src ) +{ + strncat( dst, src, MAXCHAR - strlen( dst ) ); +} + +void +Utl_catStrN( Str dst, const char *src, size_t n ) +{ + if ( n > MAXCHAR - strlen( dst ) ) + n = MAXCHAR - strlen( dst ); + strncat( dst, src, n ); +} + +void +Utl_stamp( Str file ) +{ + FILE *f; + time_t t; + + time( &t ); + if ( ! ( f = fopen( file, "w" ) ) ) + { + Log_err( "Could not open %s for writing (%s)", + file, strerror( errno ) ); + return; + } + fprintf( f, "%lu\n", t ); + fclose( f ); +} + +Bool +Utl_getStamp( time_t *result, Str file ) +{ + FILE *f; + + if ( ! ( f = fopen( file, "r" ) ) ) + return FALSE; + if ( fscanf( f, "%lu", result ) != 1 ) + { + Log_err( "File %s corrupted", file ); + fclose( f ); + return FALSE; + } + fclose( f ); + return TRUE; +} + +void +Utl_rfc822Date( time_t t, Str res ) +{ + strftime( res, MAXCHAR,"%a, %d %b %Y %H:%M:%S %z", localtime( &t ) ); +} + +void +Utl_allocAndCpy( char **dst, const char *src ) +{ + int len = strlen( src ); + if ( ! ( *dst = (char *)malloc( len + 1 ) ) ) + { + Log_err( "Cannot allocate string with length %lu", strlen( src ) ); + exit( EXIT_FAILURE ); + } + memcpy( *dst, src, len + 1 ); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/util.h Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,84 @@ +/* + util.h + + Miscellaneous helper functions. + + $Id: util.h 49 2000-05-05 21:45:56Z uh1763 $ +*/ + +#ifndef UTL_H +#define UTL_H + +#include <time.h> +#include "common.h" + +/* + Find first non-whitespace character after <token> tokens in string <line>. + Return pointer to end of string, if parsing failed. +*/ +const char * +Utl_restOfLn( const char *line, unsigned int token ); + +void +Utl_toLower( Str line ); + +/* + Read a line from string. + Return NULL if pos == NULL or no more line to read +*/ +const char * +Utl_getLn( Str result, const char *p ); + +/* + Go back to last line. +*/ +const char * +Utl_ungetLn( const char *str, const char *p ); + +/* + Read a header line from string. Reads continuation lines if + necessary. Return NULL if pos == NULL or no more line to read +*/ +const char * +Utl_getHeaderLn( Str result, const char *p ); + +/* + Strip white spaces from left and right side. + Return pointer to new start. This is within line. +*/ +char * +Utl_stripWhiteSpace( char *line ); + +/* Strip comment from a line. Comments start with '#'. */ +void +Utl_stripComment( char *line ); + +/* Write timestamp into <file>. */ +void +Utl_stamp( Str file ); + +/* Get time stamp from <file> */ +Bool +Utl_getStamp( time_t *result, Str file ); + +/* Put RFC822-compliant date string into res. */ +void +Utl_rfc822Date( time_t t, Str res ); + +void +Utl_cpyStr( Str dst, const char *src ); + +void +Utl_cpyStrN( Str dst, const char *src, size_t n ); + +void +Utl_catStr( Str dst, const char *src ); + +void +Utl_catStrN( Str dst, const char *src, size_t n ); + +/* String allocation and copying. */ +void +Utl_allocAndCpy( char **dst, const char *src ); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/wildmat.c Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,206 @@ +/* + wildmat.c + + Taken from the INN 2.2 distribution and slightly altered to fit the + Noffle environment. Changes are: + o Rename wildmat() to Wld_match(). + o Adjust includes. + + $Id: wildmat.c 49 2000-05-05 21:45:56Z uh1763 $ + + The entire INN distribution is covered by the following copyright + notice. As this file originated in the INN distribution is it + subject to the conditions of this notice. + + Copyright 1991 Rich Salz. + All rights reserved. + $Revision: 49 $ + + Redistribution and use in any form are permitted provided that the + following restrictions are are met: + 1. Source distributions must retain this entire copyright notice + and comment. + 2. Binary distributions must include the acknowledgement ``This + product includes software developed by Rich Salz'' in the + documentation or other materials provided with the + distribution. This must not be represented as an endorsement + or promotion without specific prior written permission. + 3. The origin of this software must not be misrepresented, either + by explicit claim or by omission. Credits must appear in the + source and documentation. + 4. Altered versions must be plainly marked as such in the source + and documentation and must not be misrepresented as being the + original software. + THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +*/ + +/* $Revision: 49 $ +** +** Do shell-style pattern matching for ?, \, [], and * characters. +** Might not be robust in face of malformed patterns; e.g., "foo[a-" +** could cause a segmentation violation. It is 8bit clean. +** +** Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986. +** Rich $alz is now <rsalz@osf.org>. +** April, 1991: Replaced mutually-recursive calls with in-line code +** for the star character. +** +** Special thanks to Lars Mathiesen <thorinn@diku.dk> for the ABORT code. +** This can greatly speed up failing wildcard patterns. For example: +** pattern: -*-*-*-*-*-*-12-*-*-*-m-*-*-* +** text 1: -adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1 +** text 2: -adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1 +** Text 1 matches with 51 calls, while text 2 fails with 54 calls. Without +** the ABORT code, it takes 22310 calls to fail. Ugh. The following +** explanation is from Lars: +** The precondition that must be fulfilled is that DoMatch will consume +** at least one character in text. This is true if *p is neither '*' nor +** '\0'.) The last return has ABORT instead of FALSE to avoid quadratic +** behaviour in cases like pattern "*a*b*c*d" with text "abcxxxxx". With +** FALSE, each star-loop has to run to the end of the text; with ABORT +** only the last one does. +** +** Once the control of one instance of DoMatch enters the star-loop, that +** instance will return either TRUE or ABORT, and any calling instance +** will therefore return immediately after (without calling recursively +** again). In effect, only one star-loop is ever active. It would be +** possible to modify the code to maintain this context explicitly, +** eliminating all recursive calls at the cost of some complication and +** loss of clarity (and the ABORT stuff seems to be unclear enough by +** itself). I think it would be unwise to try to get this into a +** released version unless you have a good test data base to try it out +** on. +*/ +#include <stdio.h> +#include <sys/types.h> +#include "common.h" +#include "log.h" + +#define ABORT (-1) + +/* What character marks an inverted character class? */ +#define NEGATE_CLASS '^' + /* Is "*" a common pattern? */ +#define OPTIMIZE_JUST_STAR + /* Do tar(1) matching rules, which ignore a trailing slash? */ +#undef MATCH_TAR_PATTERN + + +/* +** Match text and p, return TRUE, FALSE, or ABORT. +*/ +static int DoMatch(const char *text, const char *p) +{ + int last; + int matched; + int reverse; + + for ( ; *p; text++, p++) { + if (*text == '\0' && *p != '*') + return ABORT; + switch (*p) { + case '\\': + /* Literal match with following character. */ + p++; + /* FALLTHROUGH */ + default: + if (*text != *p) + return FALSE; + continue; + case '?': + /* Match anything. */ + continue; + case '*': + while (*++p == '*') + /* Consecutive stars act just like one. */ + continue; + if (*p == '\0') + /* Trailing star matches everything. */ + return TRUE; + while (*text) + if ((matched = DoMatch(text++, p)) != FALSE) + return matched; + return ABORT; + case '[': + reverse = p[1] == NEGATE_CLASS ? TRUE : FALSE; + if (reverse) + /* Inverted character class. */ + p++; + matched = FALSE; + if (p[1] == ']' || p[1] == '-') + if (*++p == *text) + matched = TRUE; + for (last = *p; *++p && *p != ']'; last = *p) + /* This next line requires a good C compiler. */ + if (*p == '-' && p[1] != ']' + ? *text <= *++p && *text >= last : *text == *p) + matched = TRUE; + if (matched == reverse) + return FALSE; + continue; + } + } + +#ifdef MATCH_TAR_PATTERN + if (*text == '/') + return TRUE; +#endif /* MATCH_TAR_ATTERN */ + return *text == '\0'; +} + + +/* +** User-level routine. Returns TRUE or FALSE. +*/ +Bool +Wld_match(const char *text, const char *pattern) +{ +#ifdef OPTIMIZE_JUST_STAR + if (pattern[0] == '*' && pattern[1] == '\0') + return TRUE; +#endif /* OPTIMIZE_JUST_STAR */ + return DoMatch(text, pattern) == TRUE; +} + + + +#if defined(WILDMAT_TEST) + +/* Yes, we use gets not fgets. Sue me. */ +extern char *gets(); + + +int +main() +{ + char p[80]; + char text[80]; + + printf("Wildmat tester. Enter pattern, then strings to test.\n"); + printf("A blank line gets prompts for a new pattern; a blank pattern\n"); + printf("exits the program.\n"); + + for ( ; ; ) { + printf("\nEnter pattern: "); + (void)fflush(stdout); + if (gets(p) == NULL || p[0] == '\0') + break; + for ( ; ; ) { + printf("Enter text: "); + (void)fflush(stdout); + if (gets(text) == NULL) + exit(0); + if (text[0] == '\0') + /* Blank line; go back and get a new pattern. */ + break; + printf(" %s\n", Wld_match(text, p) ? "YES" : "NO"); + } + } + + exit(0); + /* NOTREACHED */ +} +#endif /* defined(TEST) */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/wildmat.h Fri May 05 22:45:56 2000 +0100 @@ -0,0 +1,18 @@ +/* + wildmat.h + + Noffle header file for wildmat. + + $Id: wildmat.h 49 2000-05-05 21:45:56Z uh1763 $ + */ + +#ifndef WILDMAT_H +#define WILDMAT_H + +/* + See if test is matched by pattern p. Return TRUE if so. + */ +Bool +Wld_match(const char *text, const char *pattern); + +#endif
--- a/util.c Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,255 +0,0 @@ -/* - util.c - - $Id: util.c 44 2000-05-05 07:23:15Z enz $ -*/ - -#include "util.h" -#include <errno.h> -#include <ctype.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <time.h> -#include <unistd.h> -#include "config.h" -#include "log.h" -#include "wildmat.h" - -static const char * -nextWhiteSpace( const char *p ) -{ - while ( *p && ! isspace( *p ) ) - ++p; - return p; -} - -static const char * -nextNonWhiteSpace( const char *p ) -{ - while ( *p && isspace( *p ) ) - ++p; - return p; -} - -const char * -Utl_restOfLn( const char *line, unsigned int token ) -{ - unsigned int i; - const char *p; - - p = line; - for ( i = 0; i < token; ++i ) - { - p = nextNonWhiteSpace( p ); - p = nextWhiteSpace( p ); - } - p = nextNonWhiteSpace( p ); - return p; -} - -const char * -Utl_getLn( Str result, const char *pos ) -{ - int len = 0; - const char *p = pos; - - if ( ! p ) - return NULL; - while ( *p != '\n' ) - { - if ( *p == '\0' ) - { - if ( len > 0 ) - Log_err( "Line not terminated by newline: '%s'", pos ); - return NULL; - } - *(result++) = *(p++); - ++len; - if ( len >= MAXCHAR - 1 ) - { - *result = '\0'; - Log_err( "Utl_getLn: line too long: %s", result ); - return ++p; - } - } - *result = '\0'; - return ++p; - -} - -const char * -Utl_ungetLn( const char *str, const char *p ) -{ - if ( str == p ) - return FALSE; - --p; - if ( *p != '\n' ) - { - Log_dbg( "Utl_ungetLn: not at beginning of line" ); - return NULL; - } - --p; - while ( TRUE ) - { - if ( p == str ) - return p; - if ( *p == '\n' ) - return p + 1; - --p; - } -} - -const char * -Utl_getHeaderLn( Str result, const char *p ) -{ - const char * res = Utl_getLn( result, p ); - - /* Look for followon line if this isn't a blank line. */ - if ( res != NULL && !isspace( result[ 0 ] ) ) - while ( res != NULL && res[ 0 ] != '\n' && isspace( res[ 0 ] ) ) - { - Str nextLine; - const char *here; - char *next; - - here = res; - res = Utl_getLn( nextLine, res ); - next = Utl_stripWhiteSpace( nextLine ); - - if ( next[ 0 ] != '\0' ) - { - Utl_catStr( result, " " ); - Utl_catStr( result, next ); - } - else - { - res = here; - break; - } - } - - return res; -} - -void -Utl_toLower( Str line ) -{ - char *p; - - p = line; - while ( *p ) - { - *p = tolower( *p ); - ++p; - } -} - -char * -Utl_stripWhiteSpace( char *line ) -{ - char *p; - - while ( isspace( *line ) ) - ++line; - p = line + strlen( line ) - 1; - while ( isspace( *p ) ) - { - *p = '\0'; - --p; - } - return line; -} - -void -Utl_stripComment( char *line ) -{ - for ( ; *line != '\0'; line++ ) - if ( *line =='#' ) - { - *line = '\0'; - break; - } -} - -void -Utl_cpyStr( Str dst, const char *src ) -{ - dst[ 0 ] = '\0'; - strncat( dst, src, MAXCHAR ); -} - -void -Utl_cpyStrN( Str dst, const char *src, size_t n ) -{ - if ( n > MAXCHAR ) - n = MAXCHAR; - dst[ 0 ] = '\0'; - strncat( dst, src, n ); -} - -void -Utl_catStr( Str dst, const char *src ) -{ - strncat( dst, src, MAXCHAR - strlen( dst ) ); -} - -void -Utl_catStrN( Str dst, const char *src, size_t n ) -{ - if ( n > MAXCHAR - strlen( dst ) ) - n = MAXCHAR - strlen( dst ); - strncat( dst, src, n ); -} - -void -Utl_stamp( Str file ) -{ - FILE *f; - time_t t; - - time( &t ); - if ( ! ( f = fopen( file, "w" ) ) ) - { - Log_err( "Could not open %s for writing (%s)", - file, strerror( errno ) ); - return; - } - fprintf( f, "%lu\n", t ); - fclose( f ); -} - -Bool -Utl_getStamp( time_t *result, Str file ) -{ - FILE *f; - - if ( ! ( f = fopen( file, "r" ) ) ) - return FALSE; - if ( fscanf( f, "%lu", result ) != 1 ) - { - Log_err( "File %s corrupted", file ); - fclose( f ); - return FALSE; - } - fclose( f ); - return TRUE; -} - -void -Utl_rfc822Date( time_t t, Str res ) -{ - strftime( res, MAXCHAR,"%a, %d %b %Y %H:%M:%S %z", localtime( &t ) ); -} - -void -Utl_allocAndCpy( char **dst, const char *src ) -{ - int len = strlen( src ); - if ( ! ( *dst = (char *)malloc( len + 1 ) ) ) - { - Log_err( "Cannot allocate string with length %lu", strlen( src ) ); - exit( EXIT_FAILURE ); - } - memcpy( *dst, src, len + 1 ); -}
--- a/util.h Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,84 +0,0 @@ -/* - util.h - - Miscellaneous helper functions. - - $Id: util.h 44 2000-05-05 07:23:15Z enz $ -*/ - -#ifndef UTL_H -#define UTL_H - -#include <time.h> -#include "common.h" - -/* - Find first non-whitespace character after <token> tokens in string <line>. - Return pointer to end of string, if parsing failed. -*/ -const char * -Utl_restOfLn( const char *line, unsigned int token ); - -void -Utl_toLower( Str line ); - -/* - Read a line from string. - Return NULL if pos == NULL or no more line to read -*/ -const char * -Utl_getLn( Str result, const char *p ); - -/* - Go back to last line. -*/ -const char * -Utl_ungetLn( const char *str, const char *p ); - -/* - Read a header line from string. Reads continuation lines if - necessary. Return NULL if pos == NULL or no more line to read -*/ -const char * -Utl_getHeaderLn( Str result, const char *p ); - -/* - Strip white spaces from left and right side. - Return pointer to new start. This is within line. -*/ -char * -Utl_stripWhiteSpace( char *line ); - -/* Strip comment from a line. Comments start with '#'. */ -void -Utl_stripComment( char *line ); - -/* Write timestamp into <file>. */ -void -Utl_stamp( Str file ); - -/* Get time stamp from <file> */ -Bool -Utl_getStamp( time_t *result, Str file ); - -/* Put RFC822-compliant date string into res. */ -void -Utl_rfc822Date( time_t t, Str res ); - -void -Utl_cpyStr( Str dst, const char *src ); - -void -Utl_cpyStrN( Str dst, const char *src, size_t n ); - -void -Utl_catStr( Str dst, const char *src ); - -void -Utl_catStrN( Str dst, const char *src, size_t n ); - -/* String allocation and copying. */ -void -Utl_allocAndCpy( char **dst, const char *src ); - -#endif
--- a/wildmat.c Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,206 +0,0 @@ -/* - wildmat.c - - Taken from the INN 2.2 distribution and slightly altered to fit the - Noffle environment. Changes are: - o Rename wildmat() to Wld_match(). - o Adjust includes. - - $Id: wildmat.c 44 2000-05-05 07:23:15Z enz $ - - The entire INN distribution is covered by the following copyright - notice. As this file originated in the INN distribution is it - subject to the conditions of this notice. - - Copyright 1991 Rich Salz. - All rights reserved. - $Revision: 44 $ - - Redistribution and use in any form are permitted provided that the - following restrictions are are met: - 1. Source distributions must retain this entire copyright notice - and comment. - 2. Binary distributions must include the acknowledgement ``This - product includes software developed by Rich Salz'' in the - documentation or other materials provided with the - distribution. This must not be represented as an endorsement - or promotion without specific prior written permission. - 3. The origin of this software must not be misrepresented, either - by explicit claim or by omission. Credits must appear in the - source and documentation. - 4. Altered versions must be plainly marked as such in the source - and documentation and must not be misrepresented as being the - original software. - THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED - WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - -*/ - -/* $Revision: 44 $ -** -** Do shell-style pattern matching for ?, \, [], and * characters. -** Might not be robust in face of malformed patterns; e.g., "foo[a-" -** could cause a segmentation violation. It is 8bit clean. -** -** Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986. -** Rich $alz is now <rsalz@osf.org>. -** April, 1991: Replaced mutually-recursive calls with in-line code -** for the star character. -** -** Special thanks to Lars Mathiesen <thorinn@diku.dk> for the ABORT code. -** This can greatly speed up failing wildcard patterns. For example: -** pattern: -*-*-*-*-*-*-12-*-*-*-m-*-*-* -** text 1: -adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1 -** text 2: -adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1 -** Text 1 matches with 51 calls, while text 2 fails with 54 calls. Without -** the ABORT code, it takes 22310 calls to fail. Ugh. The following -** explanation is from Lars: -** The precondition that must be fulfilled is that DoMatch will consume -** at least one character in text. This is true if *p is neither '*' nor -** '\0'.) The last return has ABORT instead of FALSE to avoid quadratic -** behaviour in cases like pattern "*a*b*c*d" with text "abcxxxxx". With -** FALSE, each star-loop has to run to the end of the text; with ABORT -** only the last one does. -** -** Once the control of one instance of DoMatch enters the star-loop, that -** instance will return either TRUE or ABORT, and any calling instance -** will therefore return immediately after (without calling recursively -** again). In effect, only one star-loop is ever active. It would be -** possible to modify the code to maintain this context explicitly, -** eliminating all recursive calls at the cost of some complication and -** loss of clarity (and the ABORT stuff seems to be unclear enough by -** itself). I think it would be unwise to try to get this into a -** released version unless you have a good test data base to try it out -** on. -*/ -#include <stdio.h> -#include <sys/types.h> -#include "common.h" -#include "log.h" - -#define ABORT (-1) - -/* What character marks an inverted character class? */ -#define NEGATE_CLASS '^' - /* Is "*" a common pattern? */ -#define OPTIMIZE_JUST_STAR - /* Do tar(1) matching rules, which ignore a trailing slash? */ -#undef MATCH_TAR_PATTERN - - -/* -** Match text and p, return TRUE, FALSE, or ABORT. -*/ -static int DoMatch(const char *text, const char *p) -{ - int last; - int matched; - int reverse; - - for ( ; *p; text++, p++) { - if (*text == '\0' && *p != '*') - return ABORT; - switch (*p) { - case '\\': - /* Literal match with following character. */ - p++; - /* FALLTHROUGH */ - default: - if (*text != *p) - return FALSE; - continue; - case '?': - /* Match anything. */ - continue; - case '*': - while (*++p == '*') - /* Consecutive stars act just like one. */ - continue; - if (*p == '\0') - /* Trailing star matches everything. */ - return TRUE; - while (*text) - if ((matched = DoMatch(text++, p)) != FALSE) - return matched; - return ABORT; - case '[': - reverse = p[1] == NEGATE_CLASS ? TRUE : FALSE; - if (reverse) - /* Inverted character class. */ - p++; - matched = FALSE; - if (p[1] == ']' || p[1] == '-') - if (*++p == *text) - matched = TRUE; - for (last = *p; *++p && *p != ']'; last = *p) - /* This next line requires a good C compiler. */ - if (*p == '-' && p[1] != ']' - ? *text <= *++p && *text >= last : *text == *p) - matched = TRUE; - if (matched == reverse) - return FALSE; - continue; - } - } - -#ifdef MATCH_TAR_PATTERN - if (*text == '/') - return TRUE; -#endif /* MATCH_TAR_ATTERN */ - return *text == '\0'; -} - - -/* -** User-level routine. Returns TRUE or FALSE. -*/ -Bool -Wld_match(const char *text, const char *pattern) -{ -#ifdef OPTIMIZE_JUST_STAR - if (pattern[0] == '*' && pattern[1] == '\0') - return TRUE; -#endif /* OPTIMIZE_JUST_STAR */ - return DoMatch(text, pattern) == TRUE; -} - - - -#if defined(WILDMAT_TEST) - -/* Yes, we use gets not fgets. Sue me. */ -extern char *gets(); - - -int -main() -{ - char p[80]; - char text[80]; - - printf("Wildmat tester. Enter pattern, then strings to test.\n"); - printf("A blank line gets prompts for a new pattern; a blank pattern\n"); - printf("exits the program.\n"); - - for ( ; ; ) { - printf("\nEnter pattern: "); - (void)fflush(stdout); - if (gets(p) == NULL || p[0] == '\0') - break; - for ( ; ; ) { - printf("Enter text: "); - (void)fflush(stdout); - if (gets(text) == NULL) - exit(0); - if (text[0] == '\0') - /* Blank line; go back and get a new pattern. */ - break; - printf(" %s\n", Wld_match(text, p) ? "YES" : "NO"); - } - } - - exit(0); - /* NOTREACHED */ -} -#endif /* defined(TEST) */
--- a/wildmat.h Fri May 05 21:26:14 2000 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ -/* - wildmat.h - - Noffle header file for wildmat. - - $Id: wildmat.h 44 2000-05-05 07:23:15Z enz $ - */ - -#ifndef WILDMAT_H -#define WILDMAT_H - -/* - See if test is matched by pattern p. Return TRUE if so. - */ -Bool -Wld_match(const char *text, const char *pattern); - -#endif