Index: ossp-pkg/lmtp2nntp/Makefile.in RCS File: /v/ossp/cvs/ossp-pkg/lmtp2nntp/Makefile.in,v rcsdiff -q -kk '-r1.30' '-r1.31' -u '/v/ossp/cvs/ossp-pkg/lmtp2nntp/Makefile.in,v' 2>/dev/null --- Makefile.in 2001/12/11 12:02:26 1.30 +++ Makefile.in 2001/12/31 11:09:52 1.31 @@ -44,9 +44,9 @@ POD2MAN = pod2man PROG = lmtp2nntp -HDRS = lmtp2nntp.h daemon.h lmtp.h nntp.h argz.h shpat_match.h msg.h -SRCS = lmtp2nntp.c daemon.c lmtp.c nntp.c argz.c shpat_match.c msg.c version.c -OBJS = lmtp2nntp.o daemon.o lmtp.o nntp.o argz.o shpat_match.o msg.o version.o +HDRS = lmtp2nntp_global.h lmtp2nntp_daemon.h lmtp2nntp_lmtp.h lmtp2nntp_nntp.h lmtp2nntp_argz.h lmtp2nntp_shpat.h lmtp2nntp_msg.h +SRCS = lmtp2nntp_main.c lmtp2nntp_daemon.c lmtp2nntp_lmtp.c lmtp2nntp_nntp.c lmtp2nntp_argz.c lmtp2nntp_shpat.c lmtp2nntp_msg.c lmtp2nntp_version.c +OBJS = lmtp2nntp_main.o lmtp2nntp_daemon.o lmtp2nntp_lmtp.o lmtp2nntp_nntp.o lmtp2nntp_argz.o lmtp2nntp_shpat.o lmtp2nntp_msg.o lmtp2nntp_version.o SUBDIRS = @SUBDIR_STR@ @SUBDIR_L2@ @SUBDIR_SA@ @SUBDIR_VAR@ @@ -77,8 +77,8 @@ $(CC) $(LDFLAGS) -o $(PROG) $(OBJS) $(LIBS) lmtp2nntp.8: lmtp2nntp.pod - VS=`$(SHTOOL) version -lc -dshort version.c`; \ - VL=`$(SHTOOL) version -lc -dlong version.c`; \ + VS=`$(SHTOOL) version -lc -dshort lmtp2nntp_version.c`; \ + VL=`$(SHTOOL) version -lc -dlong lmtp2nntp_version.c`; \ $(POD2MAN) --section=8 --center="mail to news gateway" \ --release="$$VS" --date="$$VL" \ lmtp2nntp.pod >lmtp2nntp.8 @@ -111,13 +111,13 @@ -rm -f shtool new-version: - @$(SHTOOL) version -lc -n lmtp2nntp -p lmtp2nntp_ -e version.c; \ - V=`$(SHTOOL) version -lc -dlong version.c`; \ + @$(SHTOOL) version -lc -n lmtp2nntp -p lmtp2nntp_ -e lmtp2nntp_version.c; \ + V=`$(SHTOOL) version -lc -dlong lmtp2nntp_version.c`; \ sed -e "s/Version .*(.*)/Version $$V/g" README.n && mv README.n README dist: distclean @$(SHTOOL) fixperm -v .; \ - V=`$(SHTOOL) version -lc -dshort version.c`; \ + V=`$(SHTOOL) version -lc -dshort lmtp2nntp_version.c`; \ $(SHTOOL) tarball -o lmtp2nntp-$${V}.tar.gz -d lmtp2nntp-$${V} -u ossp -g ossp \ -e 'CVS,\.cvsignore,\.[ao],^\.,/\.,autogen.sh,*.tar.gz,*.bak,TODO' -c 'gzip --best' . @@ -138,11 +138,3 @@ $(OBJS): Makefile # AUTOMATICALLY GENERATED DEPENDENCY LIST - DO NOT EDIT -lmtp2nntp.o: lmtp2nntp.c argz.h shpat_match.h daemon.h lmtp2nntp.h config.h lmtp.h nntp.h msg.h version.c -daemon.o: daemon.c config.h daemon.h -lmtp.o: lmtp.c lmtp.h config.h -nntp.o: nntp.c nntp.h msg.h lmtp2nntp.h config.h -argz.o: argz.c argz.h config.h -shpat_match.o: shpat_match.c config.h shpat_match.h -msg.o: msg.c msg.h lmtp2nntp.h config.h argz.h -version.o: version.c version.c Index: ossp-pkg/lmtp2nntp/README RCS File: /v/ossp/cvs/ossp-pkg/lmtp2nntp/README,v rcsdiff -q -kk '-r1.14' '-r1.15' -u '/v/ossp/cvs/ossp-pkg/lmtp2nntp/README,v' 2>/dev/null --- README 2001/12/11 11:53:11 1.14 +++ README 2001/12/31 11:09:52 1.15 @@ -7,7 +7,7 @@ |_| |_| OSSP lmtp2nntp - Mail to News Gateway - Version 1.2a1 (06-Dec-2001) + Version 1.2a2 (31-Dec-2001) ABSTRACT Index: ossp-pkg/lmtp2nntp/argz.c RCS File: /v/ossp/cvs/ossp-pkg/lmtp2nntp/Attic/argz.c,v co -q -kk -p'1.3' '/v/ossp/cvs/ossp-pkg/lmtp2nntp/Attic/argz.c,v' | diff -u - /dev/null -L'ossp-pkg/lmtp2nntp/argz.c' 2>/dev/null --- ossp-pkg/lmtp2nntp/argz.c +++ /dev/null 2024-05-08 22:22:11.000000000 +0200 @@ -1,429 +0,0 @@ -/* -** Copyright (c) 2001 The OSSP Project -** Copyright (c) 2001 Cable & Wireless Deutschland -** -** This file is part of OSSP lmtp2nntp, an LMTP speaking local -** mailer which forwards mails as Usenet news articles via NNTP. -** It can be found at http://www.ossp.org/pkg/lmtp2nntp/. -** -** 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.0 of the License, 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 file; if not, write to the Free Software -** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -** USA, or contact the OSSP project . -** -** argz.h: Zero-Terminated Argument Vector library -*/ - -/* Routines for dealing with '\0' separated arg vectors. - Copyright (C) 1996, 1997 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Contributed by Ulrich Drepper , 1996. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - The GNU C Library 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with the GNU C Library; see the file COPYING.LIB. If not, - write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ - -#include -#include -#include - -#include "argz.h" - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif -#if defined(HAVE_DMALLOC_H) && defined(DMALLOC) -#include "dmalloc.h" -#endif - -/* Find the length of STRING, but scan at most MAXLEN characters. - If no '\0' terminator is found in that many characters, return MAXLEN. */ -static size_t my_strnlen(const char *string, size_t maxlen) -{ - const char *end = memchr(string, '\0', maxlen); - return end ? (size_t) (end - string) : maxlen; -} - -static char *my_strndup(const char *s, size_t n) -{ - size_t len = my_strnlen(s, n); - char *new = malloc(len + 1); - - if (new == NULL) - return NULL; - new[len] = '\0'; - return (char *)memcpy(new, s, len); -} - -/* Copy SRC to DEST, returning the address of the terminating '\0' in DEST. */ -static char *my_stpcpy(char *dest, const char *src) -{ - register char *d = dest; - register const char *s = src; - - do { - *d++ = *s; - } while (*s++ != '\0'); - return d - 1; -} - -int argz_add_sep(char **argz, size_t * argz_len, const char *string, int delim) -{ - size_t nlen = strlen(string) + 1; - - if (nlen > 1) { - const char *rp; - char *wp; - - *argz = (char *)realloc(*argz, *argz_len + nlen); - if (*argz == NULL) - return ENOMEM; - - wp = *argz + *argz_len; - rp = string; - do { - if (*rp == delim) { - if (wp > *argz && wp[-1] != '\0') - *wp++ = '\0'; - else - --nlen; - } - else - *wp++ = *rp; - } while (*rp++ != '\0'); - - *argz_len += nlen; - } - - return 0; -} - -/* Add BUF, of length BUF_LEN to the argz vector in ARGZ & ARGZ_LEN. */ -int argz_append(char **argz, size_t * argz_len, const char *buf, size_t buf_len) -{ - size_t new_argz_len = *argz_len + buf_len; - char *new_argz = realloc(*argz, new_argz_len); - if (new_argz) { - memcpy(new_argz + *argz_len, buf, buf_len); - *argz = new_argz; - *argz_len = new_argz_len; - return 0; - } - else - return ENOMEM; -} - -/* Add STR to the argz vector in ARGZ & ARGZ_LEN. This should be moved into - * argz.c in libshouldbelibc. */ -int argz_add(char **argz, size_t * argz_len, const char *str) -{ - return argz_append(argz, argz_len, str, strlen(str) + 1); -} - -/* Returns the number of strings in ARGZ. */ -size_t argz_count(const char *argz, size_t len) -{ - size_t count = 0; - while (len > 0) { - size_t part_len = strlen(argz); - argz += part_len + 1; - len -= part_len + 1; - count++; - } - return count; -} - -/* Make a '\0' separated arg vector from a unix argv vector, returning it in - * ARGZ, and the total length in LEN. If a memory allocation error occurs, - * ENOMEM is returned, otherwise 0. */ -int argz_create(char *const argv[], char **argz, size_t * len) -{ - int argc; - size_t tlen = 0; - char *const *ap; - char *p; - - for (argc = 0; argv[argc] != NULL; ++argc) - tlen += strlen(argv[argc]) + 1; - - if (tlen == 0) - *argz = NULL; - else { - *argz = malloc(tlen); - if (*argz == NULL) - return ENOMEM; - - for (p = *argz, ap = argv; *ap; ++ap, ++p) - p = my_stpcpy(p, *ap); - } - *len = tlen; - - return 0; -} - -int argz_create_sep(const char *string, int delim, char **argz, size_t * len) -{ - size_t nlen = strlen(string) + 1; - - if (nlen > 1) { - const char *rp; - char *wp; - - *argz = (char *)malloc(nlen); - if (*argz == NULL) - return ENOMEM; - - rp = string; - wp = *argz; - do - if (*rp == delim) { - if (wp > *argz && wp[-1] != '\0') - *wp++ = '\0'; - else - --nlen; - } - else - *wp++ = *rp; - while (*rp++ != '\0'); - - if (nlen == 0) { - free(*argz); - *argz = NULL; - *len = 0; - } - - *len = nlen; - } - else { - *argz = NULL; - *len = 0; - } - - return 0; -} - -/* Delete ENTRY from ARGZ & ARGZ_LEN, if any. */ -void argz_delete(char **argz, size_t * argz_len, char *entry) -{ - if (entry) - /* Get rid of the old value for NAME. */ - { - size_t entry_len = strlen(entry) + 1; - *argz_len -= entry_len; - memmove(entry, entry + entry_len, *argz_len - (entry - *argz)); - if (*argz_len == 0) { - free(*argz); - *argz = 0; - } - } -} - -/* Puts pointers to each string in ARGZ, plus a terminating 0 element, into - * ARGV, which must be large enough to hold them all. */ -void argz_extract(const char *argz, size_t len, char **argv) -{ - while (len > 0) { - size_t part_len = strlen(argz); - *argv++ = (char *)argz; - argz += part_len + 1; - len -= part_len + 1; - } - *argv = 0; -} - -/* Insert ENTRY into ARGZ & ARGZ_LEN before BEFORE, which should be an - * existing entry in ARGZ; if BEFORE is NULL, ENTRY is appended to the end. - * Since ARGZ's first entry is the same as ARGZ, argz_insert (ARGZ, ARGZ_LEN, - * ARGZ, ENTRY) will insert ENTRY at the beginning of ARGZ. If BEFORE is not - * in ARGZ, EINVAL is returned, else if memory can't be allocated for the new - * ARGZ, ENOMEM is returned, else 0. */ -int argz_insert(char **argz, size_t * argz_len, char *before, const char *entry) -{ - if (!before) - return argz_add(argz, argz_len, entry); - - if (before < *argz || before >= *argz + *argz_len) - return EINVAL; - - if (before > *argz) - /* Make sure before is actually the beginning of an entry. */ - while (before[-1]) - before--; - - { - size_t after_before = *argz_len - (before - *argz); - size_t entry_len = strlen(entry) + 1; - size_t new_argz_len = *argz_len + entry_len; - char *new_argz = realloc(*argz, new_argz_len); - - if (new_argz) { - before = new_argz + (before - *argz); - memmove(before + entry_len, before, after_before); - memmove(before, entry, entry_len); - *argz = new_argz; - *argz_len = new_argz_len; - return 0; - } - else - return ENOMEM; - } -} - -char *argz_next(const char *argz, size_t argz_len, const char *entry) -{ - if (entry) { - if (entry < argz + argz_len) - entry = strchr(entry, '\0') + 1; - - return entry >= argz + argz_len ? NULL : (char *)entry; - } - else if (argz_len > 0) - return (char *)argz; - else - return NULL; -} - -/* Append BUF, of length BUF_LEN to *TO, of length *TO_LEN, reallocating and - * updating *TO & *TO_LEN appropriately. If an allocation error occurs, - * *TO's old value is freed, and *TO is set to 0. */ -static void -str_append(char **to, size_t * to_len, const char *buf, const size_t buf_len) -{ - size_t new_len = *to_len + buf_len; - char *new_to = realloc(*to, new_len + 1); - - if (new_to) { - /* *((char *)__mempcpy(new_to + *to_len, buf, buf_len)) = '\0'; */ - memcpy(new_to + *to_len, buf, buf_len); - *(new_to + *to_len + buf_len) = '\0'; - *to = new_to; - *to_len = new_len; - } - else { - free(*to); - *to = 0; - } -} - -/* Replace any occurrences of the string STR in ARGZ with WITH, reallocating - * ARGZ as necessary. If REPLACE_COUNT is non-zero, *REPLACE_COUNT will be - * incremented by number of replacements performed. */ -int argz_replace(char **argz, size_t * argz_len, const char *str, - const char *with, unsigned *replace_count) -{ - int err = 0; - - if (str && *str) { - char *arg = 0; - char *src = *argz; - size_t src_len = *argz_len; - char *dst = 0; - size_t dst_len = 0; - int delayed_copy = 1; /* True while we've avoided copying - * anything. */ - size_t str_len = strlen(str), with_len = strlen(with); - - while (!err && (arg = argz_next(src, src_len, arg))) { - char *match = strstr(arg, str); - if (match) { - char *from = match + str_len; - size_t to_len = match - arg; - char *to = my_strndup(arg, to_len); - - while (to && from) { - str_append(&to, &to_len, with, with_len); - if (to) { - match = strstr(from, str); - if (match) { - str_append(&to, &to_len, from, match - from); - from = match + str_len; - } - else { - str_append(&to, &to_len, from, strlen(from)); - from = 0; - } - } - } - - if (to) { - if (delayed_copy) - /* We avoided copying SRC to DST until we found a - * match; now that we've done so, copy everything - * from the start of SRC. */ - { - if (arg > src) - err = - argz_append(&dst, &dst_len, src, - (arg - src)); - delayed_copy = 0; - } - if (!err) - err = argz_add(&dst, &dst_len, to); - free(to); - } - else - err = ENOMEM; - - if (replace_count) - (*replace_count)++; - } - else if (!delayed_copy) - err = argz_add(&dst, &dst_len, arg); - } - - if (!err) { - if (!delayed_copy) - /* We never found any instances of str. */ - { - if (src) - free(src); - *argz = dst; - *argz_len = dst_len; - } - } - else if (dst_len > 0) - free(dst); - } - - return err; -} - -/* Make '\0' separated arg vector ARGZ printable by converting all the '\0's - * except the last into the character SEP. */ -void argz_stringify(char *argz, size_t len, int sep) -{ - if (len > 0) { - while (1) { - size_t part_len = my_strnlen(argz, len); - argz += part_len; - len -= part_len; - if (len-- <= 1) /* includes final '\0' we want to stop - * at */ - break; - *argz++ = sep; - } - } - return; -} - Index: ossp-pkg/lmtp2nntp/argz.h RCS File: /v/ossp/cvs/ossp-pkg/lmtp2nntp/Attic/argz.h,v co -q -kk -p'1.2' '/v/ossp/cvs/ossp-pkg/lmtp2nntp/Attic/argz.h,v' | diff -u - /dev/null -L'ossp-pkg/lmtp2nntp/argz.h' 2>/dev/null --- ossp-pkg/lmtp2nntp/argz.h +++ /dev/null 2024-05-08 22:22:11.000000000 +0200 @@ -1,66 +0,0 @@ -/* -** Copyright (c) 2001 The OSSP Project -** Copyright (c) 2001 Cable & Wireless Deutschland -** -** This file is part of OSSP lmtp2nntp, an LMTP speaking local -** mailer which forwards mails as Usenet news articles via NNTP. -** It can be found at http://www.ossp.org/pkg/lmtp2nntp/. -** -** 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.0 of the License, 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 file; if not, write to the Free Software -** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -** USA, or contact the OSSP project . -** -** argz.c: Zero-Terminated Argument Vector library (API) -*/ - -/* Routines for dealing with '\0' separated arg vectors. - Copyright (C) 1996, 1997 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Contributed by Ulrich Drepper , 1996. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - The GNU C Library 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with the GNU C Library; see the file COPYING.LIB. If not, - write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ - -#ifndef _ARGZ_H_ -#define _ARGZ_H_ - -#include - -extern int argz_create (char *const _argv[], char **_argz, size_t * _len); -extern int argz_create_sep(const char *_string, int _sep, char **_argz, size_t * _len); -extern size_t argz_count (const char *_argz, size_t _len); -extern void argz_extract (const char *_argz, size_t _len, char **_argv); -extern void argz_stringify (char *_argz, size_t _len, int _sep); -extern int argz_append (char **_argz, size_t * _argz_len, const char *_buf, size_t _buf_len); -extern int argz_add (char **_argz, size_t * _argz_len, const char *_str); -extern int argz_add_sep (char **_argz, size_t * _argz_len, const char *_string, int _delim); -extern void argz_delete (char **_argz, size_t * _argz_len, char *_entry); -extern int argz_insert (char **_argz, size_t * _argz_len, char *_before, const char *_entry); -extern int argz_replace (char **_argz, size_t * _argz_len, const char *_str, const char *_with, unsigned int *_replace_count); -extern char *argz_next (const char *_argz, size_t _argz_len, const char *_entry); - -#endif /* _ARGZ_H_ */ - Index: ossp-pkg/lmtp2nntp/autogen.sh RCS File: /v/ossp/cvs/ossp-pkg/lmtp2nntp/Attic/autogen.sh,v rcsdiff -q -kk '-r1.10' '-r1.11' -u '/v/ossp/cvs/ossp-pkg/lmtp2nntp/Attic/autogen.sh,v' 2>/dev/null --- autogen.sh 2001/12/31 10:23:09 1.10 +++ autogen.sh 2001/12/31 11:09:52 1.11 @@ -64,16 +64,16 @@ shtoolize -q all # perform same operation in subdirs -echo "===> str (autogen.sh)" -(cd str && ./devtool autogen) -echo "<=== str" -echo "===> l2 (devtool)" -(cd l2 && ./devtool autogen) -echo "<=== l2" -echo "===> sa (devtool)" -(cd sa && ./devtool autogen) -echo "<=== sa" -echo "===> var (devtool)" -(cd var && ./devtool autogen) -echo "<=== var" +echo "===> lib_str (autogen.sh)" +(cd lib_str && ./devtool autogen) +echo "<=== lib_str" +echo "===> lib_l2 (devtool)" +(cd lib_l2 && ./devtool autogen) +echo "<=== lib_l2" +echo "===> lib_sa (devtool)" +(cd lib_sa && ./devtool autogen) +echo "<=== lib_sa" +echo "===> lib_var (devtool)" +(cd lib_var && ./devtool autogen) +echo "<=== lib_var" Index: ossp-pkg/lmtp2nntp/configure.ac RCS File: /v/ossp/cvs/ossp-pkg/lmtp2nntp/configure.ac,v rcsdiff -q -kk '-r1.16' '-r1.17' -u '/v/ossp/cvs/ossp-pkg/lmtp2nntp/configure.ac,v' 2>/dev/null --- configure.ac 2001/12/11 12:02:26 1.16 +++ configure.ac 2001/12/31 11:09:52 1.17 @@ -27,7 +27,7 @@ dnl # standard preamble AC_PREREQ(2.52) AC_REVISION(1.0) -AC_INIT(lmtp2nntp.c) +AC_INIT(lmtp2nntp_main.c) dnl # announce our version AC_DIVERT_PUSH(NOTICE) @@ -52,10 +52,10 @@ AC_CHECK_EXTLIB([OSSP Str], str, str_parse, str.h, [SUBDIR_STR=""], - [SUBDIR_STR="str" - CPPFLAGS="$CPPFLAGS -Istr" - CFLAGS="$CFLAGS -Istr" - LDFLAGS="$LDFLAGS -Lstr/.libs" + [SUBDIR_STR="lib_str" + CPPFLAGS="$CPPFLAGS -Ilib_str" + CFLAGS="$CFLAGS -Ilib_str" + LDFLAGS="$LDFLAGS -Llib_str/.libs" LIBS_EXTRA="$LIBS_EXTRA -lstr"]) AC_SUBST(SUBDIR_STR) @@ -63,10 +63,10 @@ AC_CHECK_EXTLIB([OSSP L2], l2, l2_stream_log, l2.h, [SUBDIR_L2=""], - [SUBDIR_L2="l2" - CPPFLAGS="$CPPFLAGS -Il2" - CFLAGS="$CFLAGS -Il2" - LDFLAGS="$LDFLAGS -Ll2/.libs" + [SUBDIR_L2="lib_l2" + CPPFLAGS="$CPPFLAGS -Ilib_l2" + CFLAGS="$CFLAGS -Ilib_l2" + LDFLAGS="$LDFLAGS -Llib_l2/.libs" LIBS_EXTRA="$LIBS_EXTRA -ll2"]) AC_SUBST(SUBDIR_L2) @@ -74,21 +74,21 @@ AC_CHECK_EXTLIB([OSSP SA], sa, sa_create, sa.h, [SUBDIR_SA=""], - [SUBDIR_SA="sa" - CPPFLAGS="$CPPFLAGS -Isa" - CFLAGS="$CFLAGS -Isa" - LDFLAGS="$LDFLAGS -Lsa/.libs" + [SUBDIR_SA="lib_sa" + CPPFLAGS="$CPPFLAGS -Ilib_sa" + CFLAGS="$CFLAGS -Ilib_sa" + LDFLAGS="$LDFLAGS -Llib_sa/.libs" LIBS_EXTRA="$LIBS_EXTRA -lsa"]) AC_SUBST(SUBDIR_SA) dnl # check for VAR library -AC_CHECK_EXTLIB([OSSP VAR], +AC_CHECK_EXTLIB([OSSP Var], var, var_expand, var.h, [SUBDIR_VAR=""], - [SUBDIR_VAR="var" - CPPFLAGS="$CPPFLAGS -Ivar" - CFLAGS="$CFLAGS -Ivar" - LDFLAGS="$LDFLAGS -Lvar/.libs" + [SUBDIR_VAR="lib_var" + CPPFLAGS="$CPPFLAGS -Ilib_var" + CFLAGS="$CFLAGS -Ilib_var" + LDFLAGS="$LDFLAGS -Llib_var/.libs" LIBS_EXTRA="$LIBS_EXTRA -lvar"]) AC_SUBST(SUBDIR_VAR) Index: ossp-pkg/lmtp2nntp/daemon.c RCS File: /v/ossp/cvs/ossp-pkg/lmtp2nntp/Attic/daemon.c,v co -q -kk -p'1.4' '/v/ossp/cvs/ossp-pkg/lmtp2nntp/Attic/daemon.c,v' | diff -u - /dev/null -L'ossp-pkg/lmtp2nntp/daemon.c' 2>/dev/null --- ossp-pkg/lmtp2nntp/daemon.c +++ /dev/null 2024-05-08 22:22:11.000000000 +0200 @@ -1,122 +0,0 @@ -/* -** daemon.c -- daemonize current process -** Copyright (c) 1999-2000 Ralf S. Engelschall, All Rights Reserved. -** -** See "Unix Programming Frequently Asked Questions": -** http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC10 -*/ - -#include "config.h" - -#include -#include -#include -#include -#include -#ifdef HAVE_TERMIOS_H -#include -#endif - -#include "daemon.h" - -int daemonize(void) -{ - int fd; - int rc; - - /* - * if we are started from init, - * no need to become daemon. - */ - if (getppid() == 1) - return 0; - - /* - * Ignore tty related signals - */ -#ifdef SIGTTOU - signal(SIGTTOU, SIG_IGN); -#endif -#ifdef SIGTTIN - signal(SIGTTIN, SIG_IGN); -#endif -#ifdef SIGTSTP - signal(SIGTSTP, SIG_IGN); -#endif - - /* - * fork so the parent can exit, this returns control to the command line - * or shell invoking your program. This step is required so that the new - * process is guaranteed not to be a process group leader (The next step, - * setsid, would fail if you're a process group leader). - */ - rc = fork(); - switch (rc) { - case -1: return -1; - case 0: break; - default: _exit(0); /* exit original process */ - } - - /* - * setsid to become a process group and session group leader. Since a - * controlling terminal is associated with a session, and this new session - * has not yet acquired a controlling terminal our process now has no - * controlling terminal, which is a Good Thing for daemons. - */ -#ifdef HAVE_SETSID - if (setsid() == -1) - return -1; -#else - if (setpgid(0, getpid()) == -1) - return -1; -#ifndef _PATH_TTY -#define _PATH_TTY "/dev/tty" -#endif - if ((fd = open(_PATH_TTY, O_RDWR)) == -1) - return -1; - ioctl(fd, TIOCNOTTY, NULL); - close(fd); -#endif - - /* - * fork again so the parent, (the session group leader), can exit. This - * means that we, as a non-session group leader, can never regain a - * controlling terminal. - */ - rc = fork(); - switch (rc) { - case -1: return -1; - case 0: break; - default: _exit(0); /* exit original process */ - } - - /* - * chdir("/") to ensure that our process doesn't keep any directory in - * use. Failure to do this could make it so that an administrator couldn't - * unmount a filesystem, because it was our current directory. - * [Equivalently, we could change to any directory containing files - * important to the daemon's operation.] - */ - chdir("/"); - - /* - * give us complete control over the permissions of anything we write. We - * don't know what umask we may have inherited. [This step is optional] - */ - umask(0); - - /* - * close fds 0, 1, and 2. This releases the standard in, out, and error we - * inherited from our parent process. We have no way of knowing where - * these fds might have been redirected to. - */ - if ((fd = open("/dev/null", O_RDWR, 0)) != -1) { - dup2(fd, STDIN_FILENO); - dup2(fd, STDOUT_FILENO); - dup2(fd, STDERR_FILENO); - if (fd > 2) - close(fd); - } - return 0; -} - Index: ossp-pkg/lmtp2nntp/daemon.h RCS File: /v/ossp/cvs/ossp-pkg/lmtp2nntp/Attic/daemon.h,v co -q -kk -p'1.2' '/v/ossp/cvs/ossp-pkg/lmtp2nntp/Attic/daemon.h,v' | diff -u - /dev/null -L'ossp-pkg/lmtp2nntp/daemon.h' 2>/dev/null --- ossp-pkg/lmtp2nntp/daemon.h +++ /dev/null 2024-05-08 22:22:11.000000000 +0200 @@ -1,6 +0,0 @@ -#ifndef _DAEMON_H_ -#define _DAEMON_H_ - -int daemonize(void); - -#endif /* _DAEMON_H_ */ Index: ossp-pkg/lmtp2nntp/lmtp.c RCS File: /v/ossp/cvs/ossp-pkg/lmtp2nntp/Attic/lmtp.c,v co -q -kk -p'1.23' '/v/ossp/cvs/ossp-pkg/lmtp2nntp/Attic/lmtp.c,v' | diff -u - /dev/null -L'ossp-pkg/lmtp2nntp/lmtp.c' 2>/dev/null --- ossp-pkg/lmtp2nntp/lmtp.c +++ /dev/null 2024-05-08 22:22:11.000000000 +0200 @@ -1,490 +0,0 @@ -/* -** Copyright (c) 2001 The OSSP Project -** Copyright (c) 2001 Cable & Wireless Deutschland -** -** This file is part of OSSP lmtp2nntp, an LMTP speaking local -** mailer which forwards mails as Usenet news articles via NNTP. -** It can be found at http://www.ossp.org/pkg/lmtp2nntp/. -** -** 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.0 of the License, 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 file; if not, write to the Free Software -** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -** USA, or contact the OSSP project . -** -** lmtp.c: Local Mail Transfer Protocol (LMTP) server library -*/ - -#include -#include -#include -#include -#include -#include - -#include "lmtp.h" - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif -#if defined(HAVE_DMALLOC_H) && defined(DMALLOC) -#include "dmalloc.h" -#endif - -#ifndef NUL -#define NUL '\0' -#endif - -/* maximum LMTP protocol line length */ -#define LMTP_LINE_MAXLEN 1024 - -/* maximum number of verbs/callbacks to be registered */ -#define LMTP_MAXVERBS 32 - -typedef struct { - int rl_cnt; - char *rl_bufptr; - char rl_buf[LMTP_LINE_MAXLEN]; -} lmtp_readline_t; - -typedef struct { - char *verb; - lmtp_cb_t cb; - void *ctx; -} lmtp_dispatch_t; - -struct lmtp_st { - lmtp_io_t io; /* select, read, write functions */ - lmtp_readline_t rl; /* a function to read in a single line */ - lmtp_dispatch_t **dispatch; /* LMTP commands to be dispatched */ -}; - -ssize_t lmtp_fd_read(void *_ctx, void *buf, size_t buflen) -{ - lmtp_fd_t *ctx = (lmtp_fd_t *)_ctx; - return read(ctx->fd, buf, buflen); -} - -ssize_t lmtp_fd_write(void *_ctx, const void *buf, size_t buflen) -{ - lmtp_fd_t *ctx = (lmtp_fd_t *)_ctx; - return write(ctx->fd, buf, buflen); -} - -static int verbindex(lmtp_t *lmtp, char *verb) -{ - /* returns the index of the verb or -1 if verb not registered */ - int i; - - for (i = 0; (i < LMTP_MAXVERBS) && (lmtp->dispatch[i] != NULL); i++) - if (strcasecmp(lmtp->dispatch[i]->verb, verb) == 0) - return i; - return -1; -} - -static lmtp_rc_t lmtp_cb_default(lmtp_t *lmtp, lmtp_io_t *io, lmtp_req_t *req, void *ctx) -{ - lmtp_res_t res; - lmtp_rc_t rc = LMTP_OK; - - res.statuscode = "500"; - res.dsncode = "5.5.1"; - res.statusmsg = "Command unrecognized."; - lmtp_response(lmtp, &res); - return rc; -} - -lmtp_t *lmtp_create(lmtp_io_t *io) -{ - /* create a lmtp structure allocating memory for it and initializing it. - * A lmtp_cb_default() callback is registered for the default "" verb. - * The _rfd_ and _wfd_ args are passed to the read(), write() - * functions and must have meaning for them. If _io_ is NULL, - * the system io functions are used. You can provide an _io_ structure - * and specify alternate functions. Ommiting one or more functions inside - * the _io_ structure by NULLing it causes use of the system default - * function. - */ - lmtp_t *lmtp; - - if ((lmtp = (lmtp_t *)malloc(sizeof(lmtp_t))) == NULL) - return NULL; - - if (io == NULL) - return NULL; - - lmtp->io.ctx = io->ctx; - lmtp->io.select = io->select; - lmtp->io.read = io->read; - lmtp->io.write = io->write; - - lmtp->rl.rl_cnt = 0; - lmtp->rl.rl_bufptr = NULL; - lmtp->rl.rl_buf[0] = NUL; - - if ((lmtp->dispatch = (lmtp_dispatch_t **)malloc(sizeof(void *)*LMTP_MAXVERBS)) == NULL) - return NULL; - lmtp->dispatch[0] = NULL; - - lmtp_register(lmtp, "", lmtp_cb_default, NULL, NULL, NULL); - - return lmtp; -} - -void lmtp_destroy(lmtp_t *lmtp) -{ - int i; - - if (lmtp == NULL) - return; - - for (i = 0; (i < LMTP_MAXVERBS) && (lmtp->dispatch[i] != NULL); i++) { - free(lmtp->dispatch[i]->verb); /* lmtp_register() */ - free(lmtp->dispatch[i]); /* lmtp_register() */ - } - free(lmtp->dispatch); /* lmtp_create() */ - free(lmtp); /* lmtp_create() */ - return; -} - -lmtp_rc_t lmtp_readline(lmtp_t *lmtp, char *buf, size_t buflen) -{ - /* read a line (characters until NL) from input stream */ - size_t n; - char c; - lmtp_readline_t *rl = &lmtp->rl; - - if (lmtp == NULL) - return LMTP_ERR_ARG; - for (n = 0; n < buflen-1;) { - - /* fetch one character (but read more) */ - if (rl->rl_cnt <= 0) { - do { - rl->rl_cnt = lmtp->io.read(lmtp->io.ctx, rl->rl_buf, LMTP_LINE_MAXLEN); - } while (rl->rl_cnt == -1 && errno == EINTR); - if (rl->rl_cnt == -1) - return LMTP_ERR_SYSTEM; - if (rl->rl_cnt == 0) - return LMTP_EOF; - rl->rl_bufptr = rl->rl_buf; - } - - /* act on fetched character */ - rl->rl_cnt--; - c = *rl->rl_bufptr++; - if (c == '\r') - continue; /* skip copying CR */ - if (c == '\n') - break; /* end of line */ - buf[n++] = c; /* output char into given buffer */ - - } - buf[n] = NUL; /* string termination */ - if (n == (buflen-1)) - return LMTP_ERR_OVERFLOW; - return LMTP_OK; -} - -lmtp_rc_t lmtp_readmsg(lmtp_t *lmtp, char **cppBuf, size_t maxlen) -{ - /* read lines until end of message, unescape dots. - * on success, returns a buffer which has to be free(3)d by the caller - * - * NOTE: the underlying lmtp_readline() already reduces any - * CR/LF combination to a string terminating zero. Callers of this - * function must assume multiline messages have lines terminated - * with NL only. - * - * RFC0821 "Simple Mail Transfer Protocol" [excerpt] - * 4.5.2. TRANSPARENCY - * When a line of mail text is received by the receiver-SMTP it checks - * the line. If the line is composed of a single period it is the end of - * mail. If the first character is a period and there are other - * characters on the line, the first character is deleted. - */ - lmtp_rc_t rc = LMTP_OK; - char *cpBuf; /* buffer as a whole */ - char *cpBufrealloc;/* buffer before realloc */ - char *cpPtr; /* write cursor */ - char *cpLine; /* start of the current line (see offsetline) */ - size_t nBuf; /* size of buffer, doubled through realloc until maximum reached */ - size_t offset; /* required when cpBuf changed through realloc */ - size_t offsetline; /* memorizing start of line when reallocing in the middle of a line */ - - for (nBuf = 4096; nBuf > maxlen; nBuf = nBuf >> 1); - if ((cpBuf = (char *)malloc(nBuf)) == NULL) - return LMTP_ERR_MEM; - *cppBuf = cpBuf; /* tell caller about the buffer */ - cpPtr = cpBuf; /* initialize write cursor */ - cpLine = cpBuf; /* initialize start of line */ - while (1) { - rc = lmtp_readline(lmtp, cpPtr, nBuf-(cpPtr-cpBuf)); - if (rc == LMTP_ERR_OVERFLOW) { - if (nBuf == maxlen) - return LMTP_ERR_OVERFLOW; - offset = nBuf-1; /* write cursor offset is end of buffer */ - offsetline = cpLine - cpBuf; /* remember start of line offset */ - nBuf *= 2; /* increase buffer */ - if (nBuf > maxlen) - nBuf = maxlen; /* but don't exceed maximum */ - if ((cpBufrealloc = (char *)realloc(cpBuf, nBuf)) == NULL) { - free(cpBuf); - return LMTP_ERR_MEM; - } - cpBuf = cpBufrealloc; - *cppBuf = cpBuf; /* tell caller about the new buffer */ - cpPtr = cpBuf + offset; /* recover write cursor */ - cpLine = cpBuf + offsetline; /* recover start of line */ - } - else if (rc == LMTP_OK) { - if (strcmp(cpLine, ".") == 0) { /* dot alone is end of message */ - *cpLine = NUL; /* hide dot from caller */ - break; - } - if (*cpLine == '.') /* escaped dot */ - memmove(cpLine, cpLine+1, strlen(cpLine+1)+1); - cpPtr += strlen(cpPtr); /* write cursor to the end */ - *cpPtr++ = '\n'; /* artifical NL */ - *cpPtr = NUL; /* artifical end of string */ - cpLine = cpPtr; /* start of line */ - } - else break; /* rc == LMTP_ERR* */ - } - return rc; -} - -lmtp_rc_t lmtp_request(lmtp_t *lmtp, lmtp_req_t *req) -{ - /* reads a line and attaches the buffer to req->msg; - * pulls the verb out and attaches the verb to req->verb; - * - * LMTP_OK req->msg set, req->verb set means normal operation - * LMTP_OK req->msg set, req->verb "" means no verb seen - * LMTP_EOF req->msg set, req->verb NULL means eof - * LMTP_ERR_OVERFLOW req->msg set, req->verb NULL means buf overflow - * LMTP_ERR_SYSTEM req->msg set, req->verb NULL means system error - * - * RFC0821 "Simple Mail Transfer Protocol" [excerpts] - * 4.1.1. COMMAND SEMANTICS - * The command codes themselves are alphabetic characters terminated by - * if parameters follow and otherwise. - * 4.1.2. COMMAND SYNTAX - * ::= the space character (ASCII code 32) - */ - lmtp_rc_t rc; - char *verb; - int verblen; - int i; - - req->verb = NULL; - if ((req->msg = (char *)malloc(LMTP_LINE_MAXLEN)) == (char *)NULL) - return LMTP_ERR_MEM; - if ((rc = lmtp_readline(lmtp, req->msg, LMTP_LINE_MAXLEN)) != LMTP_OK) - return rc; - for (i = 0; (i < LMTP_MAXVERBS) && (lmtp->dispatch[i] != NULL); i++) { - if ((verb = lmtp->dispatch[i]->verb) != NULL) { /* skip NULL verb */ - if ((verblen = strlen(verb)) == 0) - continue; /* skip "" verb */ - if ( (strlen(req->msg) >= verblen) - && (strncasecmp(req->msg, verb, verblen) == 0) - && ( (req->msg[verblen] == NUL) - || (req->msg[verblen] == ' ') ) ) { - req->verb = verb; - return LMTP_OK; - } - } - } - req->verb = ""; - return LMTP_OK; -} - -lmtp_rc_t lmtp_response(lmtp_t *lmtp, lmtp_res_t *res) -{ - /* write the status message. For multiline status messages it is - * neccessary to repeat the status and dsn codes for every line with a - * dash after the status for every line but the last one - */ - lmtp_rc_t rc = LMTP_OK; - int rv; - int dash; - int len; - char *cpS; - char *cpE; - char formatbuf[LMTP_LINE_MAXLEN]; - - if ( strlen(res->statuscode) != 3 - || !isdigit((int)res->statuscode[0]) - || !isdigit((int)res->statuscode[1]) - || !isdigit((int)res->statuscode[2])) - return LMTP_ERR_ARG; - - if (res->dsncode != NULL) { - if ( (strlen(res->dsncode) != 5) - || !isdigit((int)res->dsncode[0]) - || (res->dsncode[1] != '.') - || !isdigit((int)res->dsncode[2]) - || (res->dsncode[3] != '.') - || !isdigit((int)res->dsncode[4]) - || (res->dsncode[0] != res->statuscode[0])) - return LMTP_ERR_ARG; - } - - cpS = res->statusmsg; - for (dash = 1; dash == 1; ) { - if ((cpE = strchr(cpS, '\n')) == NULL) { - cpE = cpS+strlen(cpS); - dash = 0; - } - if (res->dsncode != NULL) - len = sprintf(formatbuf, "%3.3s%c%5.5s ", res->statuscode, dash ? '-' : ' ', res->dsncode); - else - len = sprintf(formatbuf, "%3.3s%c", res->statuscode, dash ? '-' : ' '); - if ((len + cpE - cpS + 2) > sizeof(formatbuf)) { /* status + line + '\r\n' does not fit into formatbuf */ - dash = 1; - if ((cpE = cpS + sizeof(formatbuf) - 2 - len) <= cpS) /* no space for line at all */ - return LMTP_ERR_ARG; - } - strncpy(formatbuf+len, cpS, cpE-cpS); - len += (cpE-cpS); - formatbuf[len++] = '\r'; - formatbuf[len++] = '\n'; - do { - rv = lmtp->io.write(lmtp->io.ctx, formatbuf, len); - } while (rv == -1 && errno == EINTR); - if (rv == -1) - return LMTP_ERR_SYSTEM; - cpS = cpE; - if (*cpS == '\n') - cpS++; - } - return rc; -} - -char *lmtp_error(lmtp_rc_t rc) -{ - /* get an error message matching the given lmtp_rc_t code usually - * returned by a previously called function - */ - char *str; - str = "LMTP: errorcode has no description"; - if (rc == LMTP_OK ) str = "LMTP: no error"; - else if (rc == LMTP_EOF ) str = "LMTP: eof"; - else if (rc == LMTP_ERR_SYSTEM ) str = "LMTP: see errno"; - else if (rc == LMTP_ERR_MEM ) str = "LMTP: dynamic memory allocation failed"; - else if (rc == LMTP_ERR_OVERFLOW) str = "LMTP: static allocated memory exhausted"; - else if (rc == LMTP_ERR_ARG ) str = "LMTP: invalid arg was passed to function"; - else if (rc == LMTP_ERR_UNKNOWN ) str = "LMTP: guru meditation"; - return str; -} - -lmtp_rc_t lmtp_register(lmtp_t *lmtp, char *verb, lmtp_cb_t cb, void *ctx, lmtp_cb_t *oldcb, void **oldctx) -{ - /* For _lmtp_ structure, register a _verb_ and associate a callback - * function _cb_ to it. A context can be specified which will be passed - * to the callback function for every call. Consider the context being - * user data. The library itself does not care about the context except - * passing it along. If the verb was registered previously, the - * registration is replaced and if _oldcb_ and/or _oldctx_ is given, the - * previous registration is returned. Calling the previously registered - * callbacks from within the newly registered callback effectively allows - * hooking or chaining to a previous registered callback. The _ctx_, - * _oldcb_ and _oldctx_ are optional and might be passed as NULL in case - * you don't care. Setting _cb_ to NULL means to check only for a - * previous registration; - */ - lmtp_rc_t rc = LMTP_OK; - int overload = 0; /* overload (replacement) detected has to return old oldcb - and/or oldctx, no overload requires growth of dispatch table */ - int i; - - if (cb == NULL) { /* checking for existing callback only */ - i = verbindex(lmtp, verb); - if (oldcb != NULL) - *oldcb = (i == -1) ? NULL : lmtp->dispatch[i]->cb; - if (oldctx != NULL) - *oldctx = (i == -1) ? NULL : lmtp->dispatch[i]->ctx; - return LMTP_OK; - } - - for (i = 0; lmtp->dispatch[i] != NULL; i++) { - if (strcasecmp(verb, lmtp->dispatch[i]->verb) == 0) { - overload = 1; - if (oldcb != NULL) - *oldcb = lmtp->dispatch[i]->cb; - if (oldctx != NULL) - *oldctx = lmtp->dispatch[i]->ctx; - break; - } - } - if (i > LMTP_MAXVERBS-2) - return LMTP_ERR_OVERFLOW; - - if (!overload) { - if ((lmtp->dispatch[i] = - (lmtp_dispatch_t *)malloc(sizeof(lmtp_dispatch_t))) == NULL) - return LMTP_ERR_MEM; - lmtp->dispatch[i+1] = NULL; - if (oldcb != NULL) - *oldcb = NULL; - if (oldctx != NULL) - *oldctx = NULL; - } - - lmtp->dispatch[i]->verb = strdup(verb); - lmtp->dispatch[i]->cb = cb; - lmtp->dispatch[i]->ctx = ctx; - - return rc; -} - -lmtp_rc_t lmtp_loop(lmtp_t *lmtp) -{ - /* Print a welcome message then execute a request/ dispatch loop until - * request signals no more data. Each request is checked to contain a - * registered verb and if a verb is found the correspondig callback is - * executed. The lmtp_create() function usually cares to register a - * default callback in order to handle unregistered verbs. The psoudoverb - * for default is the empty string "" and the callback for this verb can - * be overridden. - */ - lmtp_rc_t rc = LMTP_OK; - lmtp_req_t req; - lmtp_res_t res; - char *verb; - int i; - - req.verb = ""; - req.msg = NULL; - - res.statuscode = "220"; - res.dsncode = NULL; - res.statusmsg = "LMTP Service ready."; - if ((rc = lmtp_response(lmtp, &res)) != LMTP_OK) - return rc; - - while ((rc = lmtp_request(lmtp, &req)) == LMTP_OK) { - verb = req.verb; - if ((i = verbindex(lmtp, verb)) != -1) { - rc = lmtp->dispatch[i]->cb(lmtp, &lmtp->io, &req, lmtp->dispatch[i]->ctx); - if (req.msg != NULL) - free(req.msg); - req.verb = ""; - req.msg = NULL; - - if (rc != LMTP_OK) - break; - } - } - return rc; -} - Index: ossp-pkg/lmtp2nntp/lmtp.h RCS File: /v/ossp/cvs/ossp-pkg/lmtp2nntp/Attic/lmtp.h,v co -q -kk -p'1.11' '/v/ossp/cvs/ossp-pkg/lmtp2nntp/Attic/lmtp.h,v' | diff -u - /dev/null -L'ossp-pkg/lmtp2nntp/lmtp.h' 2>/dev/null --- ossp-pkg/lmtp2nntp/lmtp.h +++ /dev/null 2024-05-08 22:22:11.000000000 +0200 @@ -1,85 +0,0 @@ -/* -** Copyright (c) 2001 The OSSP Project -** Copyright (c) 2001 Cable & Wireless Deutschland -** -** This file is part of OSSP lmtp2nntp, an LMTP speaking local -** mailer which forwards mails as Usenet news articles via NNTP. -** It can be found at http://www.ossp.org/pkg/lmtp2nntp/. -** -** 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.0 of the License, 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 file; if not, write to the Free Software -** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -** USA, or contact the OSSP project . -** -** lmtp.h: Local Mail Transfer Protocol (LMTP) server library (API) -*/ - -#ifndef __LMTP_H__ -#define __LMTP_H__ - -#include -#include -#include -#include - -struct lmtp_st; -typedef struct lmtp_st lmtp_t; - -typedef struct { - void *ctx; - int (*select)(void *, fd_set *, fd_set *, fd_set *, struct timeval *); - ssize_t (*read)(void *, void *, size_t); - ssize_t (*write)(void *, const void *, size_t); -} lmtp_io_t; - -typedef struct { - char *verb; - char *msg; -} lmtp_req_t; - -typedef struct { - char *statuscode; - char *dsncode; - char *statusmsg; -} lmtp_res_t; - -typedef enum { - LMTP_OK, - LMTP_EOF, - LMTP_ERR_SYSTEM, - LMTP_ERR_MEM, - LMTP_ERR_OVERFLOW, - LMTP_ERR_ARG, - LMTP_ERR_UNKNOWN -} lmtp_rc_t; - -typedef struct { - int fd; -} lmtp_fd_t; - -typedef lmtp_rc_t (*lmtp_cb_t)(lmtp_t *, lmtp_io_t *, lmtp_req_t *, void *); - -lmtp_t *lmtp_create (lmtp_io_t *); -void lmtp_destroy (lmtp_t *); -lmtp_rc_t lmtp_readline(lmtp_t *, char *, size_t); -lmtp_rc_t lmtp_readmsg (lmtp_t *, char **, size_t); -lmtp_rc_t lmtp_request (lmtp_t *, lmtp_req_t *); -lmtp_rc_t lmtp_response(lmtp_t *, lmtp_res_t *); -char *lmtp_error (lmtp_rc_t); -lmtp_rc_t lmtp_register(lmtp_t *, char *, lmtp_cb_t, void *, lmtp_cb_t *, void **); -lmtp_rc_t lmtp_loop (lmtp_t *); -ssize_t lmtp_fd_read (void *, void *, size_t); -ssize_t lmtp_fd_write(void *, const void *, size_t); - -#endif /* __LMTP_H__ */ - Index: ossp-pkg/lmtp2nntp/lmtp2nntp.c RCS File: /v/ossp/cvs/ossp-pkg/lmtp2nntp/Attic/lmtp2nntp.c,v co -q -kk -p'1.100' '/v/ossp/cvs/ossp-pkg/lmtp2nntp/Attic/lmtp2nntp.c,v' | diff -u - /dev/null -L'ossp-pkg/lmtp2nntp/lmtp2nntp.c' 2>/dev/null --- ossp-pkg/lmtp2nntp/lmtp2nntp.c +++ /dev/null 2024-05-08 22:22:11.000000000 +0200 @@ -1,2373 +0,0 @@ -/* -** Copyright (c) 2001 The OSSP Project -** Copyright (c) 2001 Cable & Wireless Deutschland -** -** This file is part of OSSP lmtp2nntp, an LMTP speaking local -** mailer which forwards mails as Usenet news articles via NNTP. -** It can be found at http://www.ossp.org/pkg/lmtp2nntp/. -** -** 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.0 of the License, 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 file; if not, write to the Free Software -** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -** USA, or contact the OSSP project . -** -** lmtp2nntp.c: LMTP to NNTP main procedure -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* third party */ -#include "str.h" -#include "argz.h" -#include "shpat_match.h" -#include "l2.h" -#include "var.h" -#include "daemon.h" - -/* library version check (compile-time) */ -#define L2_VERSION_HEX_REQ 0x001200 -#define L2_VERSION_STR_REQ "0.1.0" -#define STR_VERSION_HEX_REQ 0x009206 -#define STR_VERSION_STR_REQ "0.9.6" -#ifdef L2_VERSION_HEX -#if L2_VERSION_HEX < L2_VERSION_HEX_REQ -#error "require a newer version of OSSP L2" -#endif -#endif -#ifdef STR_VERSION_HEX -#if STR_VERSION_HEX < STR_VERSION_HEX_REQ -#error "require a newer version of OSSP Str" -#endif -#endif - -/* own headers */ -#include "lmtp2nntp.h" -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif -#if defined(HAVE_DMALLOC_H) && defined(DMALLOC) -#include "dmalloc.h" -#endif -#include "lmtp.h" -#include "nntp.h" -#include "sa.h" -#include "msg.h" -#define _VERSION_C_AS_HEADER_ -#include "version.c" -#undef _VERSION_C_AS_HEADER_ - -#ifndef FALSE -#define FALSE (1 != 1) -#endif -#ifndef TRUE -#define TRUE (!FALSE) -#endif - -#ifndef NUL -#define NUL '\0' -#endif - -#define ERR_EXECUTION 1 -#define ERR_DELIVERY -2 - -#define STDSTRLEN 512 -#define MAXNEWSSERVICES 16 -#define MAXACLS 32 - -static lmtp_rc_t lmtp_cb_lhlo(lmtp_t *lmtp, lmtp_io_t *io, lmtp_req_t *req, void *ctx); -static lmtp_rc_t lmtp_cb_mail(lmtp_t *lmtp, lmtp_io_t *io, lmtp_req_t *req, void *ctx); -static lmtp_rc_t lmtp_cb_rcpt(lmtp_t *lmtp, lmtp_io_t *io, lmtp_req_t *req, void *ctx); -static lmtp_rc_t lmtp_cb_data(lmtp_t *lmtp, lmtp_io_t *io, lmtp_req_t *req, void *ctx); -static lmtp_rc_t lmtp_cb_noop(lmtp_t *lmtp, lmtp_io_t *io, lmtp_req_t *req, void *ctx); -static lmtp_rc_t lmtp_cb_rset(lmtp_t *lmtp, lmtp_io_t *io, lmtp_req_t *req, void *ctx); -static lmtp_rc_t lmtp_cb_quit(lmtp_t *lmtp, lmtp_io_t *io, lmtp_req_t *req, void *ctx); - -static int helo_rfc0821domain(char *msg, char **domain); -static int helo_rfc1035domain(char *msg, char **domain); - -struct session { - int lhlo_seen; - char *lhlo_domain; -}; - -static void catchsignal(int sig, ...); -static void initsession(struct session *session); -static void resetsession(struct session *session); -int groupmatch(char *, size_t, char *); - -struct ns { - char *h; /* host */ - char *p; /* port */ - sa_addr_t *saa; /* socket address abstraction */ - sa_t *sa; /* socket abstraction */ - nntp_t *nntp; - nntp_rc_t rc; - l2_channel_t *l2; -}; - -struct acl { - char *acl; - int not; - sa_addr_t *saa; - size_t prefixlen; -}; - -typedef struct { - l2_context_t ctx; - char *progname; - char *option_logfile; - int option_groupmode; - int option_operationmode; - char *option_operationmodefakestatus; - char *option_operationmodefakedsn; - int option_maxmessagesize; - char *azHeaderValuePairs; - size_t asHeaderValuePairs; - int option_timeout_lmtp_accept; - int option_timeout_lmtp_read; - int option_timeout_lmtp_write; - int option_timeout_nntp_connect; - int option_timeout_nntp_read; - int option_timeout_nntp_write; - char *option_mailfrom; - char *option_restrictheader; - unsigned int option_levelmask; - char *option_pidfile; - int option_killflag; - uid_t option_uid; - int option_daemon; - int option_aclc; - struct acl option_acl[MAXACLS]; - int option_veryverbose; - int option_childsmax; - int active_childs; - l2_env_t *l2_env; - l2_channel_t *l2; - sa_addr_t *saaAltio; - sa_t *saAltio; - char *cpBindh; - char *cpBindp; - sa_addr_t *saaBind; - sa_t *saBind; - sa_addr_t *saaIO; - sa_t *saIO; - int fdIOi; - int fdIOo; - int nsc; - struct ns ns[MAXNEWSSERVICES]; - char *azGroupargs; - size_t asGroupargs; - struct session session; - msg_t *msg; - struct utsname uname; -} lmtp2nntp_t; - -static var_config_t ctx_lookup_cfg = { - '$', /* varinit */ - '{', /* startdelim */ - '}', /* enddelim */ - '[', /* startindex */ - ']', /* endindex */ - '#', /* current_index */ - '\\', /* escape */ - "a-zA-Z0-9_.-" /* namechars */ -}; - -static var_rc_t ctx_lookup( - void *_ctx, - const char *var_ptr, size_t var_len, int var_idx, - const char **val_ptr, size_t *val_len, size_t *val_size) -{ - lmtp2nntp_t *ctx = (lmtp2nntp_t *)_ctx; - const char *name; - size_t len; - size_t n; - char *cp; - var_rc_t rc; - - log2(ctx, DEBUG, "lookup variable \"%s\" (%d)", - var_ptr /* FIXME: NUL-termination? */, var_len); - rc = VAR_ERR_UNDEFINED_VARIABLE; - if (strncasecmp(var_ptr, "lmtp2nntp.version", var_len) == 0) { - } - else if (strncasecmp(var_ptr, "os.name", var_len) == 0) { - } - else if (strncasecmp(var_ptr, "os.version", var_len) == 0) { - } - else if (var_len > 8 && strncasecmp(var_ptr, "msg.hdr.", 8) == 0) { - name = var_ptr + 8; - len = strlen(name); - if (ctx == NULL) - return VAR_ERR_UNDEFINED_VARIABLE; - if (ctx->msg == NULL) - return VAR_ERR_UNDEFINED_VARIABLE; - if (ctx->msg->azHeaders == NULL) - return VAR_ERR_UNDEFINED_VARIABLE; - cp = NULL; - while ((cp = argz_next(ctx->msg->azHeaders, ctx->msg->asHeaders, cp)) != NULL) { - char *cpVar, *cpVal; - int nVar, nVal; - cpVar = cp; - nVar = strlen(cpVar); - if ((cp = argz_next(ctx->msg->azHeaders, ctx->msg->asHeaders, cp)) == NULL) - break; - cpVal = cp; - nVal = strlen(cpVal); - if (len == (nVar-1) && strncasecmp(cpVar, name, len) == 0 && cpVar[len] == ':') { - *val_ptr = cpVal; - *val_len = nVal; - *val_size = 0; - rc = VAR_OK; - } - } - } - if (rc == VAR_OK) - log4(ctx, DEBUG, "lookup variable \"%s\" (%d) ok: result is \"%s\" (%d)", - var_ptr /* FIXME: NUL-termination? */, var_len, *val_ptr, *val_len); - else - log3(ctx, DEBUG, "lookup variable \"%s\" (%d) failed: %s", - var_ptr /* FIXME: NUL-termination? */, var_len, var_strerror(rc)); - return rc; -} - - -static void lmtp_gfs_ns(struct ns *); -static void lmtp_gfs_lhlo(lmtp2nntp_t *); -static void lmtp_gfs_rset(lmtp2nntp_t *); -static void lmtp_gfs_quit(lmtp2nntp_t *); - -enum { - GROUPMODE_ARG, - GROUPMODE_ENVELOPE, - GROUPMODE_HEADER -}; - -enum { - OPERATIONMODE_FAKE, - OPERATIONMODE_POST, - OPERATIONMODE_FEED -}; - -/* - * print usage information - */ -static void usage(char *command) -{ - /* use - * perl ) { if(m/\/\*POD (.*) .*\*\//) { $_=$1; s/.<(.*?)>/$1/g ; print "\"$_ \"\n" };}' - * to pull the USAGE string out of this source - */ - fprintf(stderr, - "USAGE: %s " - "[-C childsmax] " - "[-D] " - "[-K] " - "[-P pidfile] " - "[-V] " - "[-a addr/mask[,addr/mask[,...]] " - "[-b addr[:port]|-|path[:perms]] " - "[-c addr[:port]] " - "[-d addr[:port][,addr[:port], ...]] " - "[-g groupmode] " - "[-h header:value] " - "[-l level[:logfile]] " - "[-m mailfrom] " - "[-n nodename] " - "[-o operationmode] " - "[-r restrictheader] " - "[-s size] " - "[-t name=sec[,name=sec[,...]] " - "[-u uid] " - "[-v] " - "newsgroup [newsgroup ...] " - "\n", - command); - return; -} - -static ssize_t hook_lmtp_read(void *_ctx, void *buf, size_t nbytes) -{ - lmtp2nntp_t *ctx = (lmtp2nntp_t *)_ctx; - ssize_t rc; - size_t n; - sa_rc_t rv; - - if (ctx->saIO != NULL) { - if ((rv = sa_read(ctx->saIO, buf, nbytes, &n)) != SA_OK) - rc = -1; - else - rc = (ssize_t)n; - } - else - rc = read(ctx->fdIOi, buf, nbytes); - if (rc == -1) - log0(ctx, TRACE, "LMTP read error: %m"); - else - log3(ctx, TRACE, "LMTP %5d << \"%{text}D\"", rc, buf, rc); - log1(ctx, DEBUG, "hook_lmtp_read() return, rc=%d", rc); - return rc; -} - -static ssize_t hook_lmtp_write(void *_ctx, const void *buf, size_t nbytes) -{ - lmtp2nntp_t *ctx = (lmtp2nntp_t *)_ctx; - ssize_t rc; - size_t n; - sa_rc_t rv; - - log3(ctx, TRACE, "LMTP %5d >> \"%{text}D\"", nbytes, buf, nbytes); - if (ctx->saIO != NULL) { - if ((rv = sa_write(ctx->saIO, buf, nbytes, &n)) != SA_OK) - rc = -1; - else - rc = (ssize_t)n; - } - else - rc = write(ctx->fdIOo, buf, nbytes); - if (rc == -1) - log0(ctx, TRACE, "LMTP write error: %m"); - return rc; -} - -static ssize_t hook_nntp_read(void *_ctx, void *buf, size_t nbytes) -{ - struct ns *ctx = (struct ns *)_ctx; - ssize_t rc; - size_t n; - sa_rc_t rv; - - if ((rv = sa_read(ctx->sa, buf, nbytes, &n)) != SA_OK) - rc = -1; - else - rc = (ssize_t)n; - if (rc == -1) - log0(ctx, TRACE, "NNTP read error: %m"); - else - log3(ctx, TRACE, "NNTP %5d << \"%{text}D\"", rc, buf, rc); - return rc; -} - -static ssize_t hook_nntp_write(void *_ctx, const void *buf, size_t nbytes) -{ - struct ns *ctx = (struct ns *)_ctx; - ssize_t rc; - size_t n; - sa_rc_t rv; - - log3(ctx, TRACE, "NNTP %5d >> \"%{text}D\"", nbytes, buf, nbytes); - if ((rv = sa_write(ctx->sa, buf, nbytes, &n)) != SA_OK) - rc = -1; - else - rc = (ssize_t)n; - if (rc == -1) - log0(ctx, TRACE, "NNTP write error: %m"); - return rc; -} - -static l2_result_t -formatter_prefix(l2_context_t *_ctx, const char id, const char *param, - char *bufptr, size_t bufsize, size_t *buflen, va_list *ap) -{ - lmtp2nntp_t *ctx = (lmtp2nntp_t *)_ctx->vp; - - if ((ctx->msg != NULL) && (ctx->msg->cpFid != NULL)) { - sprintf(bufptr, "%s: ", ctx->msg->cpFid); - *buflen = strlen(bufptr); - } - else - *buflen = 0; - return L2_OK; -} - -static l2_result_t -formatter_errno(l2_context_t *_ctx, const char id, const char *param, - char *bufptr, size_t bufsize, size_t *buflen, va_list *ap) -{ - sprintf(bufptr, "(%d) %s", errno, strerror(errno)); - *buflen = strlen(bufptr); - return L2_OK; -} - -static void catchsignal(int sig, ...) -{ - va_list ap; - static lmtp2nntp_t *ctx = NULL; - pid_t pid; - - if(sig == 0) { - va_start(ap, sig); - if ((ctx = va_arg(ap, lmtp2nntp_t *)) == NULL) - exit(ERR_EXECUTION); - log0(ctx, TRACE, "catching and logging signals now"); - va_end(ap); - return; - } - if (ctx != NULL) { - switch (sig) { - case SIGCHLD: - log1(ctx, NOTICE, "caught signal %d - wait for child", sig); - pid = wait(NULL); - ctx->active_childs--; - log2(ctx, NOTICE, "caught signal %d - child [%ld] terminated", sig, (long)pid); - return; - case SIGUSR1: - log1(ctx, NOTICE, "caught signal %d - flush logging stream", sig); - l2_channel_flush(ctx->l2); - return; - case SIGHUP: - case SIGINT: - case SIGQUIT: - log1(ctx, NOTICE, "caught signal %d - exit - no more logging", sig); - break; - default: - log1(ctx, PANIC, "CAUGHT SIGNAL %d - EXIT - NO MORE LOGGING", sig); - } - l2_channel_destroy(ctx->l2); - l2_env_destroy(ctx->l2_env); - } - exit(ERR_EXECUTION); -} - - -int main(int argc, char **argv) -{ - int rc; - lmtp_t *lmtp = NULL; - lmtp_io_t lmtp_io; - lmtp2nntp_t *ctx = NULL; - int bOk; - int i; /* general purpose scratch int, index ... */ - char *cp; /* general purpose character pointer */ - char *azHosts; - size_t asHosts; - char *azTimeout; - size_t asTimeout; - char *azACL; - size_t asACL; - char *cpHost; - char *cpPort; - pid_t pid; - FILE *fd; - char *cpName; - char *cpValue; - int nValue; - char *cpAddr; - char *cpPrefixLen; - struct passwd *sPasswd; - char *cpHeadername; - char *cpHeadervalue; - - /* drop effective uid/gid priviledges */ - seteuid(getuid()); - setegid(getgid()); - - /* library version check (run-time) */ - if (l2_version.v_hex < L2_VERSION_HEX_REQ) { - fprintf(stderr, "require OSSP L2 >= %s, found %s\n", L2_VERSION_STR_REQ, L2_VERSION_STR); - CU(ERR_EXECUTION); - } - if (str_version.v_hex < STR_VERSION_HEX_REQ) { - fprintf(stderr, "require OSSP Str >= %s, found %s\n", STR_VERSION_STR_REQ, STR_VERSION_STR); - CU(ERR_EXECUTION); - } - - /* create application context */ - if ((ctx = (lmtp2nntp_t *)malloc(sizeof(lmtp2nntp_t))) == NULL) - CU(ERR_EXECUTION); - ctx->ctx.vp = ctx; - ctx->progname = strdup(argv[0]); - ctx->option_logfile = NULL; - ctx->option_groupmode = GROUPMODE_ARG; - ctx->option_operationmode = OPERATIONMODE_FAKE; - ctx->option_operationmodefakestatus = "553"; /* Requested action not taken: mailbox name not allowed */ - ctx->option_operationmodefakedsn = "5.7.1"; /* Delivery not authorized, message refused */ - ctx->option_maxmessagesize = 8 * 1024 * 1024; - ctx->azHeaderValuePairs = NULL; - ctx->asHeaderValuePairs = 0; - ctx->option_timeout_lmtp_accept = 0; - ctx->option_timeout_lmtp_read = 10; - ctx->option_timeout_lmtp_write = 10; - ctx->option_timeout_nntp_connect = 360; - ctx->option_timeout_nntp_read = 60; - ctx->option_timeout_nntp_write = 60; - ctx->option_mailfrom = NULL; - ctx->option_restrictheader = NULL; - ctx->option_levelmask = L2_LEVEL_NONE; - ctx->option_pidfile = NULL; - ctx->option_killflag = FALSE; - ctx->option_uid = getuid(); - ctx->option_daemon = FALSE; - ctx->option_veryverbose = FALSE; - ctx->option_childsmax = 10; - ctx->active_childs = 0; - ctx->l2_env = NULL; - ctx->l2 = NULL; - ctx->saaAltio = NULL; - ctx->saAltio = NULL; - ctx->cpBindh = NULL; - ctx->cpBindp = NULL; - ctx->saaBind = NULL; - ctx->saBind = NULL; - ctx->nsc = 0; - for (i=0; i < MAXNEWSSERVICES; i++) { - ctx->ns[i].h = NULL; - ctx->ns[i].p = NULL; - ctx->ns[i].saa = NULL; - ctx->ns[i].sa = NULL; - ctx->ns[i].nntp = NULL; - ctx->ns[i].rc = LMTP_ERR_UNKNOWN; - ctx->ns[i].l2 = NULL; - } - ctx->option_aclc = 0; - for (i = 0; i < MAXACLS; i++) { - ctx->option_acl[i].acl = NULL; - ctx->option_acl[i].not = FALSE; - ctx->option_acl[i].saa = NULL; - ctx->option_acl[i].prefixlen = 0; - } - ctx->azGroupargs = NULL; - ctx->asGroupargs = 0; - initsession(&ctx->session); - ctx->msg = NULL; - if (uname(&ctx->uname) == -1) { - fprintf(stderr, "%s:Error: uname failed \"%s\"\n", ctx->progname, strerror(errno)); - CU(ERR_EXECUTION); - } - - /*POD B */ - - /* use - * perl ) { if(m/\/\*POD (.*) .*\*\//) { $_=$1; print "$_\n" };}' - * to pull the POD SYNOPSIS header directly out of this source - */ - - /* read in the arguments */ - while ((i = getopt(argc, argv, "C:DKP:Va:b:c:d:g:h:l:m:n:o:r:s:t:u:v")) != -1) { - switch (i) { - case 'C': /*POD [B<-C> I] */ - ctx->option_childsmax = atoi(optarg); - if (ctx->option_childsmax <= 0) { - fprintf(stderr, "%s:Error: Invalid number (%d) to option -C\n", ctx->progname, ctx->option_childsmax); - CU(ERR_EXECUTION); - } - break; - case 'D': /*POD [B<-D>] */ - ctx->option_daemon = TRUE; - break; - case 'K': /*POD [B<-K>] */ - ctx->option_killflag = TRUE; - break; - case 'P': /*POD [B<-P> I] */ - ctx->option_pidfile = strdup(optarg); - break; - case 'V': /*POD [B<-V>] */ - ctx->option_veryverbose = TRUE; - break; - case 'a': /*POD [B<-a> I/I[,I/I[,...]] */ - if (argz_create_sep(optarg, ',', &azACL, &asACL) != 0) - CU(ERR_EXECUTION); - cp = NULL; - while ((cp = argz_next(azACL, asACL, cp)) != NULL) { - if (ctx->option_aclc >= MAXACLS) { - fprintf(stderr, "%s:Error: Too many ACL (%d) using option -a\n", ctx->progname, ctx->option_aclc); - CU(ERR_EXECUTION); - } - ctx->option_acl[ctx->option_aclc].acl = strdup(cp); - if (cp[0] == '!') { - ctx->option_acl[ctx->option_aclc].not = TRUE; - cpAddr = strdup(cp+1); - } - else { - cpAddr = strdup(cp); - } - if ((cpPrefixLen = strrchr(cpAddr, '/')) != NULL) - *cpPrefixLen++ = NUL; - else - cpPrefixLen = "-1"; - ctx->option_acl[ctx->option_aclc].prefixlen = atoi(cpPrefixLen); - if ((rc = sa_addr_create(&ctx->option_acl[ctx->option_aclc].saa)) != SA_OK) { - fprintf(stderr, "%s:Error: Creating address failed for -a option (%d)\n", - ctx->progname, rc); - } - if ((rc = sa_addr_u2a(ctx->option_acl[ctx->option_aclc].saa, "inet://%s:0", cpAddr)) != SA_OK) { - fprintf(stderr, "%s:Error: Parsing host address failed for \"%s:0\" (%d)\n", - ctx->progname, cpAddr, rc); - CU(ERR_EXECUTION); - } - ctx->option_aclc++; - free(cpAddr); - } - free(azACL); - break; - case 'b': /*POD [B<-b> I[I<:port>]|C<->|I[:perms]] */ - if (strcmp(optarg, "-") != 0) { - if ((rc = sa_create(&ctx->saAltio)) != SA_OK) { - fprintf(stderr, "%s:Error: Creating TCP socket failed for \"%s\": %s\n", - ctx->progname, optarg, strerror(errno)); - CU(ERR_EXECUTION); - } - if ((rc = sa_addr_create(&ctx->saaAltio)) != SA_OK) { - fprintf(stderr, "%s:Error: Creating address failed for -b option (%d)\n", - ctx->progname, rc); - } - if (optarg[0] == '/') { - char *cpPath; - char *cpPerm; - int nPerm; - int n; - - cpPath = strdup(optarg); - cpPerm = NULL; - nPerm = -1; - if ((cpPerm = strrchr(cpPath, ':')) != NULL) { - *cpPerm++ = '\0'; - nPerm = 0; - for (i = 0; i < 4 && cpPerm[i] != '\0'; i++) { - if (!isdigit((int)cpPerm[i])) { - nPerm = -1; - break; - } - n = cpPerm[i] - '0'; - if (n > 7) { - nPerm = -1; - break; - } - nPerm = ((nPerm << 3) | n); - } - if (nPerm == -1 || cpPerm[i] != '\0') { - fprintf(stderr, "%s:Error: Invalid permissions \"%s\"\n", ctx->progname, cpPerm); - CU(ERR_EXECUTION); - } - } - if ((rc = sa_addr_u2a(ctx->saaAltio, "unix:%s", cpPath)) != SA_OK) { - fprintf(stderr, "%s:Error: Parsing alternate IO guessing UNIX domain socket failed for \"%s\" (%d)\n", - ctx->progname, cpPath, rc); - CU(ERR_EXECUTION); - } - if ((rc = sa_bind(ctx->saAltio, ctx->saaAltio)) != SA_OK) { - fprintf(stderr, "%s:Error: Bind failed for \"%s\": %s\n", - ctx->progname, cpPath, strerror(errno)); - CU(ERR_EXECUTION); - } - if (nPerm != -1) { - if (chmod(cpPath, nPerm) == -1) { - fprintf(stderr, "%s:Error: chmod failed for \"%s\": %s\n", ctx->progname, cpPath, strerror(errno)); - CU(ERR_EXECUTION); - } - } - if (getuid() == 0 && getuid() != ctx->option_uid) { - if (chown(cpPath, ctx->option_uid, -1) == -1) { - fprintf(stderr, "%s:Error: chown failed for \"%s\": %s\n", ctx->progname, cpPath, strerror(errno)); - CU(ERR_EXECUTION); - } - } - free(cpPath); - } - else { - if ((rc = sa_addr_u2a(ctx->saaAltio, "inet://%s", optarg)) != SA_OK) { - fprintf(stderr, "%s:Error: Parsing alternate IO guessing INET socket failed for \"%s\" (%d)\n", - ctx->progname, optarg, rc); - CU(ERR_EXECUTION); - } - if ((rc = sa_bind(ctx->saAltio, ctx->saaAltio)) != SA_OK) { - fprintf(stderr, "%s:Error: Bind failed for \"%s\": %s\n", - ctx->progname, optarg, strerror(errno)); - CU(ERR_EXECUTION); - } - } - if ((rc = sa_listen(ctx->saAltio, -1)) != SA_OK) { - fprintf(stderr, "%s:Error: Listen to failed for \"%s\": %s\n", - ctx->progname, optarg, strerror(errno)); - CU(ERR_EXECUTION); - } - } - break; - case 'c': /*POD [B<-c> I[I<:port>]] */ - ctx->cpBindh = strdup(optarg); - if ((ctx->cpBindp = strrchr(ctx->cpBindh, ':')) != NULL) { - *ctx->cpBindp++ = NUL; - ctx->cpBindp = strdup(ctx->cpBindp); - } - else - ctx->cpBindp = strdup("0"); - if ((rc = sa_addr_create(&ctx->saaBind)) != SA_OK) { - fprintf(stderr, "%s:Error: Creating address failed for -c option (%d)\n", - ctx->progname, rc); - } - if ((rc = sa_addr_u2a(ctx->saaBind, "inet://%s:%s", ctx->cpBindh, ctx->cpBindp)) != SA_OK) { - fprintf(stderr, "%s:Error: Parsing bind address failed for \"%s:%s\" (%d)\n", - ctx->progname, ctx->cpBindh, ctx->cpBindp, rc); - CU(ERR_EXECUTION); - } - break; - case 'd': /*POD [B<-d> I[I<:port>][,I[I<:port>], ...]] */ - if (argz_create_sep(optarg, ',', &azHosts, &asHosts) != 0) - CU(ERR_EXECUTION); - cp = NULL; - while ((cp = argz_next(azHosts, asHosts, cp)) != NULL) { - if (ctx->nsc >= MAXNEWSSERVICES) { - fprintf(stderr, "%s:Error: Too many services (%d) using option -d\n", ctx->progname, ctx->nsc); - CU(ERR_EXECUTION); - } - cpHost = strdup(cp); - if ((cpPort = strrchr(cpHost, ':')) != NULL) { - *cpPort++ = NUL; - cpPort = strdup(cpPort); - } - else - cpPort = strdup("nntp"); - ctx->ns[ctx->nsc].h = cpHost; - ctx->ns[ctx->nsc].p = cpPort; - if ((rc = sa_addr_create(&ctx->ns[ctx->nsc].saa)) != SA_OK) { - fprintf(stderr, "%s:Error: Creating address failed for -d option (%d)\n", - ctx->progname, rc); - } - if ((rc = sa_addr_u2a(ctx->ns[ctx->nsc].saa, "inet://%s:%s", - ctx->ns[ctx->nsc].h, ctx->ns[ctx->nsc].p)) != SA_OK) { - fprintf(stderr, "%s:Error: Parsing host address failed for \"%s:%s\" (%d)\n", - ctx->progname, ctx->ns[ctx->nsc].h, ctx->ns[ctx->nsc].p, rc); - CU(ERR_EXECUTION); - } - if ((rc = sa_create(&ctx->ns[ctx->nsc].sa)) != SA_OK) { - fprintf(stderr, "%s:Error: Creating TCP socket failed for \"%s:%s\": %s\n", - ctx->progname, ctx->ns[ctx->nsc].h, ctx->ns[ctx->nsc].p, strerror(errno)); - CU(ERR_EXECUTION); - } - ctx->ns[ctx->nsc].nntp = NULL; - ctx->nsc++; - } - free(azHosts); - break; - case 'g': /*POD [B<-g> I] */ - if (strcasecmp(optarg, "arg") == 0) - ctx->option_groupmode = GROUPMODE_ARG; - else if (strcasecmp(optarg, "envelope") == 0) - ctx->option_groupmode = GROUPMODE_ENVELOPE; - else if (strcasecmp(optarg, "header") == 0) - ctx->option_groupmode = GROUPMODE_HEADER; - else { - fprintf(stderr, "%s:Error: Invalid mode \"%s\" to option -g\n", ctx->progname, optarg); - CU(ERR_EXECUTION); - } - break; - case 'h': /*POD [B<-h> I
:] */ - cpHeadername = strdup(optarg); - if ((cp = strchr(cpHeadername, ':')) == NULL) { - free(cpHeadername); - fprintf(stderr, "%s:Error: header \"%s\" for -h option not terminated with colon\n", - ctx->progname, cpHeadername); - CU(ERR_EXECUTION); - } - cp++; - if (*cp == NUL) { - free(cpHeadername); - fprintf(stderr, "%s:Error: header \"%s\" for -h option has no value\n", - ctx->progname, cpHeadername); - CU(ERR_EXECUTION); - } - cpHeadervalue = strdup(cp); - *cp = NUL; - argz_add(&ctx->azHeaderValuePairs, &ctx->asHeaderValuePairs, cpHeadername); - argz_add(&ctx->azHeaderValuePairs, &ctx->asHeaderValuePairs, cpHeadervalue); - free(cpHeadervalue); - free(cpHeadername); - break; - case 'l': /*POD [B<-l> I[:I]] */ - if ((cp = strrchr(optarg, ':')) != NULL) { - *cp++ = NUL; - if (*cp == NUL) { - fprintf(stderr, "%s:Error: empty logfile to option -l\n", ctx->progname); - CU(ERR_EXECUTION); - } - else - ctx->option_logfile = strdup(cp); - } - else - ctx->option_logfile = strdup("logfile"); - - if (l2_util_s2l(optarg, strlen(optarg), ',', &ctx->option_levelmask) != L2_OK) { - fprintf(stderr, "%s:Error: invalid level \"%s\" to option -l\n", ctx->progname, optarg); - CU(ERR_EXECUTION); - } - ctx->option_levelmask = L2_LEVEL_UPTO(ctx->option_levelmask); - break; - case 'm': /*POD [B<-m> I] */ - ctx->option_mailfrom = strdup(optarg); - /* protect ourselfs from the substitution of backreferences. - * Missing varargs would cause segfaults. Rewrite capturing - * brackets to clustering syntax. Use poor man's s///g - * simulator as current str library doesn't support global - * substitution */ - while (str_parse(ctx->option_mailfrom, "s/(.*?)\\((?!\\?:)(.*)/$1(?:$2/", &cp) > 0) { - free(ctx->option_mailfrom); - ctx->option_mailfrom = cp; - } - if (str_parse("<>", ctx->option_mailfrom) == -1) { - fprintf(stderr, "%s:Error: illegal regex \"%s\" to option -m.\n", ctx->progname, ctx->option_mailfrom); - CU(ERR_EXECUTION); - } - break; - case 'n': /*POD [B<-n> I] */ - if (strlen(optarg) > sizeof(ctx->uname.nodename)-1) { - fprintf(stderr, "%s:Error: nodename \"%s\" to long to option -n.\n", ctx->progname, optarg); - CU(ERR_EXECUTION); - } - strcpy(ctx->uname.nodename, optarg); - break; - case 'o': /*POD [B<-o> I] */ - if (strcasecmp(optarg, "post") == 0) - ctx->option_operationmode = OPERATIONMODE_POST; - else if (strcasecmp(optarg, "feed") == 0) - ctx->option_operationmode = OPERATIONMODE_FEED; - else { - if (strlen(optarg) != 9) { - fprintf(stderr, "%s:Error: Invalid format or length \"%s\" to option -o\n", ctx->progname, optarg); - CU(ERR_EXECUTION); - } - - if (optarg[3] != '/') { - fprintf(stderr, "%s:Error: Invalid format or missing slash \"%s\" to option -o\n", ctx->progname, optarg); - CU(ERR_EXECUTION); - } - - optarg[3] = NUL; - ctx->option_operationmodefakestatus = &optarg[0]; - ctx->option_operationmodefakedsn = &optarg[4]; - - if ( strlen(ctx->option_operationmodefakestatus) != 3 - || !isdigit((int)ctx->option_operationmodefakestatus[0]) - || !isdigit((int)ctx->option_operationmodefakestatus[1]) - || !isdigit((int)ctx->option_operationmodefakestatus[2])) { - fprintf(stderr, "%s:Error: Invalid status in format \"%s\" to option -o\n", ctx->progname, optarg); - CU(ERR_EXECUTION); - } - - if ( (strlen(ctx->option_operationmodefakedsn) != 5) - || !isdigit((int)ctx->option_operationmodefakedsn[0]) - || (ctx->option_operationmodefakedsn[1] != '.') - || !isdigit((int)ctx->option_operationmodefakedsn[2]) - || (ctx->option_operationmodefakedsn[3] != '.') - || !isdigit((int)ctx->option_operationmodefakedsn[4]) - || (ctx->option_operationmodefakedsn[0] != ctx->option_operationmodefakestatus[0])) { - fprintf(stderr, "%s:Error: Invalid dsn in format \"%s\" to option -o\n", ctx->progname, optarg); - CU(ERR_EXECUTION); - } - } - break; - case 'r': /*POD [B<-r> I] */ - ctx->option_restrictheader = strdup(optarg); - /* protect ourselfs from the substitution of backreferences. - * Missing varargs would cause segfaults. Rewrite capturing - * brackets to clustering syntax. Use poor man's s///g - * simulator as current str library doesn't support global - * substitution */ - while (str_parse(ctx->option_restrictheader, "s/(.*?)\\((?!\\?:)(.*)/$1(?:$2/", &cp) > 0) { - free(ctx->option_restrictheader); - ctx->option_restrictheader = cp; - } - if (str_parse("<>", ctx->option_restrictheader) == -1) { - fprintf(stderr, "%s:Error: illegal regex \"%s\" to option -r.\n", ctx->progname, ctx->option_restrictheader); - CU(ERR_EXECUTION); - } - break; - case 's': /*POD [B<-s> I] */ - ctx->option_maxmessagesize = atoi(optarg); - if(ctx->option_maxmessagesize < 64) { - fprintf(stderr, "%s:Error: maximum message size is unacceptable small.\n", ctx->progname); - CU(ERR_EXECUTION); - } - break; - case 't': /*POD [B<-t> I=I[,I=I[,...]] */ - if (argz_create_sep(optarg, ',', &azTimeout, &asTimeout) != 0) - CU(ERR_EXECUTION); - cp = NULL; - while ((cp = argz_next(azTimeout, asTimeout, cp)) != NULL) { - cpName = strdup(cp); - if ((cpValue = strrchr(cpName, '=')) == NULL) { - fprintf(stderr, "%s:Error: comma-seperated argument %s to option -t have to be name=value.\n", ctx->progname, cp); - CU(ERR_EXECUTION); - } - *cpValue++ = NUL; - nValue = atoi(cpValue); - if (nValue < 0) { - fprintf(stderr, "%s:Error: timeout %s=%d to option -t must be a positive integer.\n", ctx->progname, cpName, nValue); - CU(ERR_EXECUTION); - } - if (strcmp(cpName, "lmtp") == 0) { - ctx->option_timeout_lmtp_accept = nValue; - ctx->option_timeout_lmtp_read = nValue; - ctx->option_timeout_lmtp_write = nValue; - } - else if (strcmp(cpName, "lmtp:accept") == 0) - ctx->option_timeout_lmtp_accept = nValue; - else if (strcmp(cpName, "lmtp:read") == 0) - ctx->option_timeout_lmtp_read = nValue; - else if (strcmp(cpName, "lmtp:write") == 0) - ctx->option_timeout_lmtp_write = nValue; - else if (strcmp(cpName, "nntp") == 0) { - ctx->option_timeout_nntp_connect = nValue; - ctx->option_timeout_nntp_read = nValue; - ctx->option_timeout_nntp_write = nValue; - } - else if (strcmp(cpName, "nntp:connect") == 0) - ctx->option_timeout_nntp_connect = nValue; - else if (strcmp(cpName, "nntp:read") == 0) - ctx->option_timeout_nntp_read = nValue; - else if (strcmp(cpName, "nntp:write") == 0) - ctx->option_timeout_nntp_write = nValue; - else { - fprintf(stderr, "%s:Error: unknown timeout %s to option -t.\n", ctx->progname, cpName); - CU(ERR_EXECUTION); - } - free(cpName); - } - free(azTimeout); - break; - case 'u': /*POD [B<-u> I] */ - if (isdigit((int)optarg[0])) { - if ((sPasswd = getpwuid((uid_t)atoi(optarg))) == NULL) { - fprintf(stderr, "%s:Error: uid \"%s\" not found for -u option.\n", ctx->progname, optarg); - CU(ERR_EXECUTION); - } - } - else { - if ((sPasswd = getpwnam(optarg)) == NULL) { - fprintf(stderr, "%s:Error: loginname \"%s\" not found for -u option.\n", ctx->progname, optarg); - CU(ERR_EXECUTION); - } - } - ctx->option_uid = sPasswd->pw_uid; - break; - case 'v': /*POD [B<-v>] (version)*/ - fprintf(stdout, "%s\n", lmtp2nntp_version.v_gnu); - CU(0); - break; - case '?': - default: - usage(ctx->progname); - CU(ERR_EXECUTION); - } - } - /*POD I [I ...] */ - for (i = optind; i < argc; i++) { - argz_add(&ctx->azGroupargs, &ctx->asGroupargs, argv[i]); - } - - /* if no positive ACL exists (option -a) add a wildcard match-all for IPv4 and IPv6 */ - bOk = FALSE; - for (i = 0; i < ctx->option_aclc; i++) { - if (!ctx->option_acl[i].not) { - bOk = TRUE; - break; - } - } - if (!bOk) { - if (ctx->option_aclc >= MAXACLS) { - fprintf(stderr, "%s:Error: Too many ACL (%d) using option -a (no space for additional fake IPv4 ACL)\n", ctx->progname, ctx->option_aclc); - CU(ERR_EXECUTION); - } - ctx->option_acl[ctx->option_aclc].acl = "0.0.0.0"; - ctx->option_acl[ctx->option_aclc].not = FALSE; - ctx->option_acl[ctx->option_aclc].prefixlen = 0; - if ((rc = sa_addr_create(&ctx->option_acl[ctx->option_aclc].saa)) != SA_OK) { - fprintf(stderr, "%s:Error: Creating fake address failed for -a option (%d)\n", - ctx->progname, rc); - } - if ((rc = sa_addr_u2a(ctx->option_acl[ctx->option_aclc].saa, "inet://%s:0", ctx->option_acl[ctx->option_aclc].acl)) != SA_OK) { - fprintf(stderr, "%s:Error: Parsing host address failed for \"%s:0\" (%s)\n", - ctx->progname, ctx->option_acl[ctx->option_aclc].acl, - sa_error(rc)); - CU(ERR_EXECUTION); - } - ctx->option_aclc++; - } - if (!bOk) { - if (ctx->option_aclc >= MAXACLS) { - fprintf(stderr, "%s:Error: Too many ACL (%d) using option -a (no space for additional fake IPv6 ACL)\n", ctx->progname, ctx->option_aclc); - CU(ERR_EXECUTION); - } - ctx->option_acl[ctx->option_aclc].acl = "[::]"; - ctx->option_acl[ctx->option_aclc].not = FALSE; - ctx->option_acl[ctx->option_aclc].prefixlen = 0; - if ((rc = sa_addr_create(&ctx->option_acl[ctx->option_aclc].saa)) != SA_OK) { - fprintf(stderr, "%s:Error: Creating fake address failed for -a option (%d)\n", - ctx->progname, rc); - } - if ((rc = sa_addr_u2a(ctx->option_acl[ctx->option_aclc].saa, "inet://%s:0", ctx->option_acl[ctx->option_aclc].acl)) != SA_OK) { - fprintf(stderr, "%s:Error: Parsing host address failed for \"%s:0\" (%s)\n", - ctx->progname, ctx->option_acl[ctx->option_aclc].acl, - sa_error(rc)); - CU(ERR_EXECUTION); - } - ctx->option_aclc++; - } - - if (getuid() != ctx->option_uid) { - if (setuid(ctx->option_uid) == -1) { - fprintf(stderr, "%s:Error: Setting UID to %d failed: %s\n", - ctx->progname, ctx->option_uid, strerror(errno)); - CU(ERR_EXECUTION); - } - } - - /* create L2 environment */ - if (l2_env_create(&ctx->l2_env) != L2_OK) { - fprintf(stderr, "%s:Error: failed to create L2 environment\n", ctx->progname); - CU(ERR_EXECUTION); - } - - /* register custom L2 formatters */ - if (l2_env_formatter(ctx->l2_env, 'P', formatter_prefix, &ctx->ctx) != L2_OK) { - fprintf(stderr, "%s:Error: logging failed to register prefix formatter\n", ctx->progname); - CU(ERR_EXECUTION); - } - if (l2_env_formatter(ctx->l2_env, 'D', l2_util_fmt_dump, NULL) != L2_OK) { - fprintf(stderr, "%s:Error: logging failed to register dump formatter\n", ctx->progname); - CU(ERR_EXECUTION); - } - if (l2_env_formatter(ctx->l2_env, 'S', l2_util_fmt_string, NULL) != L2_OK) { - fprintf(stderr, "%s:Error: logging failed to register string formatter\n", ctx->progname); - CU(ERR_EXECUTION); - } - if (l2_env_formatter(ctx->l2_env, 'm', formatter_errno, NULL) != L2_OK) { - fprintf(stderr, "%s:Error: logging failed to register errno formatter\n", ctx->progname); - CU(ERR_EXECUTION); - } - - /* create channel stream */ - if (ctx->option_levelmask != L2_LEVEL_NONE && ctx->option_logfile != NULL) { - if (ctx->option_veryverbose) - rc = l2_spec(&ctx->l2, ctx->l2_env, - "prefix(prefix=\"%%b %%d %%H:%%M:%%S <%%L> lmtp2nntp[%%P]: \",timezone=local)" - " -> buffer(size=65536)" - " -> file(path=%s,append=1,perm=%d)", - ctx->option_logfile, 0644); - else - rc = l2_spec(&ctx->l2, ctx->l2_env, - "prefix(prefix=\"%%b %%d %%H:%%M:%%S <%%L> lmtp2nntp[%%P]: \",timezone=local)" - " -> file(path=%s,append=1,perm=%d)", - ctx->option_logfile, 0644); - if (rc != L2_OK) { - fprintf(stderr, "%s:Error: logging failed to create stream\n", ctx->progname); - CU(ERR_EXECUTION); - } - if (l2_channel_levels(ctx->l2, ctx->option_levelmask, L2_LEVEL_NONE) != L2_OK) { - fprintf(stderr, "%s:Error: logging failed to set global logging level\n", ctx->progname); - CU(ERR_EXECUTION); - } - if (l2_channel_open(ctx->l2) != L2_OK) { - fprintf(stderr, "%s:Error: logging failed to open channel stream\n", ctx->progname); - CU(ERR_EXECUTION); - } - } - - /* from this point on logging is up and running and fprintf(stderr, ...) - * should not be used in the remainder of the code - */ - - log1(ctx, NOTICE, "startup, version %s", lmtp2nntp_version.v_gnu); - if (ctx->option_veryverbose) - log0(ctx, NOTICE, "logging very verbose (unbuffered)"); - - if ((ctx->option_pidfile != NULL) && ctx->option_killflag) { - if ((fd = fopen(ctx->option_pidfile, "r")) == NULL) - log1(ctx, ERROR, "cannot open pidfile \"%s\" for reading %m", ctx->option_pidfile); - else { - if (fscanf(fd, "%d\n", &pid) != 1) { - fclose(fd); - log1(ctx, ERROR, "cannot extract pid from pidfile \"%s\"", ctx->option_pidfile); - } - else { - fclose(fd); - log1(ctx, TRACE, "going to kill pid[%d]", pid); - if (kill(pid, SIGHUP) == -1) - log1(ctx, ERROR, "killing pid[%d] failed %m", pid); - if (unlink(ctx->option_pidfile) == -1) - log1(ctx, ERROR, "unlinking pidfile \"%s\" failed %m", ctx->option_pidfile); - } - } - CU(0); - } - - catchsignal(0, ctx); - signal(SIGCHLD, (void(*)())catchsignal); - signal(SIGHUP, (void(*)())catchsignal); - signal(SIGINT, (void(*)())catchsignal); - signal(SIGQUIT, (void(*)())catchsignal); - signal(SIGILL, (void(*)())catchsignal); - signal(SIGBUS, (void(*)())catchsignal); - signal(SIGSEGV, (void(*)())catchsignal); - signal(SIGSYS, (void(*)())catchsignal); - signal(SIGTERM, (void(*)())catchsignal); - signal(SIGUSR1, (void(*)())catchsignal); - signal(SIGUSR2, SIG_IGN ); - - /* loop for LMTP protocol with support for alternate io through daemon */ - if (ctx->saAltio == NULL) { - /* initialize LMTP context */ - ctx->fdIOi = STDIN_FILENO; - ctx->fdIOo = STDOUT_FILENO; - lmtp_io.ctx = ctx; - lmtp_io.read = hook_lmtp_read; - lmtp_io.write = hook_lmtp_write; - if ((lmtp = lmtp_create(&lmtp_io)) == NULL) { - log0(ctx, ERROR, "Unable to initialize LMTP library\n"); - CU(ERR_EXECUTION); - } - /* RFC0821, 4.5.1. MINIMUM IMPLEMENTATION - * In order to make SMTP workable, the following minimum implementation - * is required for all receivers: [...] - * RFC0821, 4.1.2. COMMAND SYNTAX - * - * Verb Parameter - * ----+------------------------------- - * HELO - * MAIL FROM: - * RCPT TO: - * DATA - * RSET - * NOOP - * QUIT - */ - lmtp_register(lmtp, "LHLO", lmtp_cb_lhlo, ctx, NULL, NULL); - lmtp_register(lmtp, "MAIL", lmtp_cb_mail, ctx, NULL, NULL); - lmtp_register(lmtp, "RCPT", lmtp_cb_rcpt, ctx, NULL, NULL); - lmtp_register(lmtp, "DATA", lmtp_cb_data, ctx, NULL, NULL); - lmtp_register(lmtp, "RSET", lmtp_cb_rset, ctx, NULL, NULL); - lmtp_register(lmtp, "NOOP", lmtp_cb_noop, ctx, NULL, NULL); - lmtp_register(lmtp, "QUIT", lmtp_cb_quit, ctx, NULL, NULL); - lmtp_loop(lmtp); - lmtp_gfs_quit(ctx); - lmtp_gfs_lhlo(ctx); - lmtp_destroy(lmtp); - } else { - pid = getpid(); - if (ctx->option_daemon) { - daemonize(); - log1(ctx, NOTICE, "daemonized, previous pid[%d]", pid); - } - if (ctx->option_pidfile != NULL) { - if ((fd = fopen(ctx->option_pidfile, "w+")) == NULL) - log1(ctx, ERROR, "cannot open pidfile \"%s\" for writing %m", ctx->option_pidfile); - else { - fprintf(fd, "%d\n", getpid()); - fclose(fd); - } - } - - sa_timeout(ctx->saAltio, SA_TIMEOUT_ALL, 0, 0); - sa_timeout(ctx->saAltio, SA_TIMEOUT_ACCEPT, ctx->option_timeout_lmtp_accept, 0); - sa_timeout(ctx->saAltio, SA_TIMEOUT_READ, ctx->option_timeout_lmtp_read, 0); - sa_timeout(ctx->saAltio, SA_TIMEOUT_WRITE, ctx->option_timeout_lmtp_write, 0); - while (1) { - while (ctx->active_childs >= ctx->option_childsmax) { - log1(ctx, ERROR, "maximum number of childs (%d) reached - waiting (1s)", ctx->option_childsmax); - sleep(1); - } - - if ((rc = sa_accept(ctx->saAltio, &ctx->saaIO, &ctx->saIO)) != SA_OK) { - if (rc == SA_ERR_SYS) - log3(ctx, ERROR, "accept failed: %s: (%d) %s", sa_error(rc), errno, strerror(errno)); - else - log1(ctx, ERROR, "accept failed: %s", sa_error(rc)); - sleep(10); - continue; - } - - /* Access Control List */ - bOk = FALSE; - /* check positive matches */ - for (i = 0; i < ctx->option_aclc; i++) { - char *cpA1; - char *cpA2; - if (ctx->option_acl[i].not) - continue; - sa_addr_a2u(ctx->option_acl[i].saa, &cpA1); - sa_addr_a2u(ctx->saaIO, &cpA2); - if (sa_addr_match(ctx->saaIO, ctx->option_acl[i].saa, ctx->option_acl[i].prefixlen) == SA_OK) { - log4(ctx, TRACE, "positive/inclusive ACL \"%s\" (%s/%d) matches %s: YES (stop comparison)", ctx->option_acl[i].acl, cpA1, ctx->option_acl[i].prefixlen, cpA2); - bOk = TRUE; - break; - } - else - log4(ctx, TRACE, "positive/inclusive ACL \"%s\" (%s/%d) matches %s: NO", ctx->option_acl[i].acl, cpA1, ctx->option_acl[i].prefixlen, cpA2); - free(cpA1); - free(cpA2); - } - /* check negative matches */ - for (i = 0; i < ctx->option_aclc; i++) { - char *cpA1; - char *cpA2; - if (!ctx->option_acl[i].not) - continue; - sa_addr_a2u(ctx->option_acl[i].saa, &cpA1); - sa_addr_a2u(ctx->saaIO, &cpA2); - if (sa_addr_match(ctx->saaIO, ctx->option_acl[i].saa, ctx->option_acl[i].prefixlen) == SA_OK) { - log4(ctx, TRACE, "negative/exclusive ACL \"%s\" (not %s/%d) matches %s: YES (stop comparison)", ctx->option_acl[i].acl, cpA1, ctx->option_acl[i].prefixlen, cpA2); - bOk = FALSE; - break; - } - else { - log4(ctx, TRACE, "negative/exclusive ACL \"%s\" (not %s/%d) matches %s: NO", ctx->option_acl[i].acl, cpA1, ctx->option_acl[i].prefixlen, cpA2); - } - } - if (bOk) { - char *cpA; - sa_addr_a2u(ctx->saaIO, &cpA); - log1(ctx, TRACE, "connection from %s accepted due to ACL", cpA); - free(cpA); - } - else { - char *cpA; - sa_addr_a2u(ctx->saaIO, &cpA); - log1(ctx, ERROR, "connection from %s refused due to ACL", cpA); - free(cpA); - sa_destroy(ctx->saIO); - sa_addr_destroy(ctx->saaIO); - continue; - } - - /* logging buffer must be empty before fork otherwise content is - * duplicated and written twice on next flush */ - l2_channel_flush(ctx->l2); - pid = fork(); - if (pid == -1) { - log0(ctx, ERROR, "daemon cannot spawn child %m"); - continue; - } - if (pid != 0) { - log1(ctx, INFO, "daemon forked process, new child pid[%d]", pid); - ctx->active_childs++; - sa_destroy(ctx->saIO); - sa_addr_destroy(ctx->saaIO); - continue; - } - log1(ctx, NOTICE, "startup new child process, parent pid[%d]", getppid()); - - /* child must close listening socket */ - sa_destroy(ctx->saAltio); - ctx->saAltio = NULL; /* prevent cleanup from free'ing this again */ - - /* initialize LMTP context */ - lmtp_io.ctx = ctx; - lmtp_io.read = hook_lmtp_read; - lmtp_io.write = hook_lmtp_write; - if ((lmtp = lmtp_create(&lmtp_io)) == NULL) { - log0(ctx, ERROR, "Unable to initialize LMTP library\n"); - CU(ERR_EXECUTION); - } - /* RFC0821, 4.5.1. MINIMUM IMPLEMENTATION - * In order to make SMTP workable, the following minimum implementation - * is required for all receivers: [...] - * RFC0821, 4.1.2. COMMAND SYNTAX - * - * Verb Parameter - * ----+------------------------------- - * HELO - * MAIL FROM: - * RCPT TO: - * DATA - * RSET - * NOOP - * QUIT - */ - lmtp_register(lmtp, "LHLO", lmtp_cb_lhlo, ctx, NULL, NULL); - lmtp_register(lmtp, "MAIL", lmtp_cb_mail, ctx, NULL, NULL); - lmtp_register(lmtp, "RCPT", lmtp_cb_rcpt, ctx, NULL, NULL); - lmtp_register(lmtp, "DATA", lmtp_cb_data, ctx, NULL, NULL); - lmtp_register(lmtp, "RSET", lmtp_cb_rset, ctx, NULL, NULL); - lmtp_register(lmtp, "NOOP", lmtp_cb_noop, ctx, NULL, NULL); - lmtp_register(lmtp, "QUIT", lmtp_cb_quit, ctx, NULL, NULL); - lmtp_loop(lmtp); - lmtp_gfs_quit(ctx); - lmtp_gfs_lhlo(ctx); - lmtp_destroy(lmtp); - CU(0); - } - } - CU(0); - - /* graceful shutdown */ - CUS: - log0(ctx, NOTICE, "graceful shutdown shortly before exit - no more logging"); - l2_channel_destroy(ctx->l2); - l2_env_destroy(ctx->l2_env); - if (ctx->saAltio) - sa_destroy(ctx->saAltio); - if (ctx->saaAltio) - sa_addr_destroy(ctx->saaAltio); - if (ctx->option_restrictheader != NULL) - free(ctx->option_restrictheader); - if (ctx->azHeaderValuePairs != NULL) - free(ctx->azHeaderValuePairs); - if (ctx->option_pidfile != NULL) - free(ctx->option_pidfile); - if (ctx->option_logfile != NULL) - free(ctx->option_logfile); - if (ctx->progname != NULL) - free(ctx->progname); - if (ctx->azGroupargs != NULL) - free(ctx->azGroupargs); - if (ctx != NULL) - free(ctx); - str_parse(NULL, NULL); - - return rc; -} - -static void resetsession(struct session *session) -{ - if (session->lhlo_domain != NULL) - free(session->lhlo_domain); - initsession(session); - return; -} - -static void initsession(struct session *session) -{ - session->lhlo_seen = FALSE; - session->lhlo_domain = NULL; - return; -} - -static lmtp_rc_t lmtp_cb_lhlo(lmtp_t *lmtp, lmtp_io_t *io, lmtp_req_t *req, void *_ctx) -{ - /* - * RFC0821 [excerpt] 4.1. SMTP COMMANDS - * 4.1.1. COMMAND SEMANTICS, HELO - * This command and an OK reply to it confirm that both the sender-SMTP - * and the receiver-SMTP are in the initial state, that is, there is no - * transaction in progress and all state tables and buffers are cleared. - * - * The first command in a session must be the HELO command. The HELO - * command may be used later in a session as well. If the HELO command - * argument is not acceptable a 501 failure reply must be returned and - * the receiver-SMTP must stay in the same state. - * - * If the transaction beginning command argument is not acceptable a 501 - * failure reply must be returned and the receiver-SMTP must stay in the - * same state. If the commands in a transaction are out of order a 503 - * failure reply must be returned and the receiver-SMTP must stay in the - * same state. - * - * HELO - */ - lmtp2nntp_t *ctx = (lmtp2nntp_t *)_ctx; - nntp_rc_t rc; - lmtp_res_t res; - char str[STDSTRLEN]; - int bOk; - int i; - nntp_io_t nntp_io; - - log1(ctx, INFO, "LMTP service executing LHLO command < %s", req->msg); - - /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 503 Bad sequence of commands - * RFC1893 2. Status Codes 5.X.X Permanent Failure - * RFC1893 3.5 Network and Routing Status X.0.0 Other undefined Status - */ - log0(ctx, TRACE, "checking for duplicate LHLO"); - if (ctx->session.lhlo_seen) { - res.statuscode = "503"; - res.dsncode = "5.0.0"; - res.statusmsg = "Duplicate LHLO."; - CU(LMTP_OK); - } - - /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 501 Syntax error in parameters or arguments - * RFC1893 2. Status Codes 5.X.X Permanent Failure - * RFC1893 3.5 Network and Routing Status X.0.0 Other undefined Status - */ - log0(ctx, TRACE, "checking domain to match either RFC0821 or RFC1035 syntax"); - if (! ( helo_rfc0821domain(req->msg, &ctx->session.lhlo_domain) > 0 - || helo_rfc1035domain(req->msg, &ctx->session.lhlo_domain) > 0)) { - res.statuscode = "501"; - res.dsncode = "5.0.0"; - res.statusmsg = "Please identify yourself. Domain must match RFC0821/RFC1035."; - CU(LMTP_OK); - } - - /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 451 Requested action aborted: local error in processing - * RFC1893 2. Status Codes 4.X.X Persistent Transient Failure - * RFC1893 3.5 Network and Routing Status X.3.5 System incorrectly configured - */ - if (ctx->option_operationmode != OPERATIONMODE_FAKE) { - log0(ctx, TRACE, "check if at least one NNTP service was successfully configured"); - if (ctx->nsc == 0) { - res.statuscode = "451"; - res.dsncode = "4.3.5"; - res.statusmsg = "No valid NNTP services configured."; - CU(LMTP_OK); - } - } - - log0(ctx, TRACE, "try to establish a session to any configured NNTP services"); - if (ctx->option_operationmode == OPERATIONMODE_FAKE) - log0(ctx, NOTICE, "NNTP running in fake mode, network connections will be executed but result is ignored"); - i = 0; - do { - log1(ctx, DEBUG, "trying ns[%d]", i); - bOk = TRUE; - log2(ctx, TRACE, "try %s:%s", ctx->ns[i].h, ctx->ns[i].p); - - ctx->ns[i].l2 = ctx->l2; - - if (bOk && (ctx->saaBind != NULL)) { - log2(ctx, DEBUG, "bind local socket to %s:%s", ctx->cpBindh, ctx->cpBindp); - if (sa_bind(ctx->ns[i].sa, ctx->saaBind) != SA_OK) { - bOk = FALSE; - log2(ctx, ERROR, "binding NNTP client to local address %s:%s failed, %m", ctx->cpBindh, ctx->cpBindp); - } - } - - sa_timeout(ctx->ns[i].sa, SA_TIMEOUT_ALL, 0, 0); - sa_timeout(ctx->ns[i].sa, SA_TIMEOUT_CONNECT, ctx->option_timeout_nntp_connect, 0); - sa_timeout(ctx->ns[i].sa, SA_TIMEOUT_READ, ctx->option_timeout_nntp_read, 0); - sa_timeout(ctx->ns[i].sa, SA_TIMEOUT_WRITE, ctx->option_timeout_nntp_read, 0); - - if (bOk) { - log0(ctx, DEBUG, "connect"); - if (sa_connect(ctx->ns[i].sa, ctx->ns[i].saa) != SA_OK) { - bOk = FALSE; - log2(ctx, WARNING, "connect to %s:%s failed, %m", - ctx->ns[i].h, ctx->ns[i].p); - } - } - - if (bOk) { - log0(ctx, DEBUG, "nntp_create"); - nntp_io.ctx = &ctx->ns[i]; - nntp_io.read = hook_nntp_read; - nntp_io.write = hook_nntp_write; - if ((ctx->ns[i].nntp = nntp_create(&nntp_io)) == NULL) { - bOk = FALSE; - log0(ctx, ERROR, "creation of NNTP context failed"); - } - } - - if (bOk) { - log0(ctx, DEBUG, "nntp_init"); - if ((rc = nntp_init(ctx->ns[i].nntp)) != NNTP_OK) { - bOk = FALSE; - log2(ctx, ERROR, "initialization of NNTP context failed, (%d) %s", rc, nntp_error(rc)); - } - } - - if (bOk) { - log2(ctx, INFO, "NNTP session to %s:%s successfully established", ctx->ns[i].h, ctx->ns[i].p); - i++; - } - else { - log2(ctx, WARNING, "NNTP session establishment to %s:%s failed", ctx->ns[i].h, ctx->ns[i].p); - log1(ctx, DEBUG, "removing ns[%d] from list", i); - lmtp_gfs_ns(&ctx->ns[i]); - if (i < --ctx->nsc) { - memcpy(&ctx->ns[i], &ctx->ns[i+1], (ctx->nsc - i ) * sizeof(struct ns)); - } - } - } while (i < ctx->nsc); - - if (ctx->option_operationmode == OPERATIONMODE_FAKE) - log1(ctx, NOTICE, "NNTP running in fake mode, network connections successfully established=%d but ignored", ctx->nsc); - else - { - /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 421 Service not available - * RFC1893 2. Status Codes 4.X.X Persistent Transient Failure - * RFC1893 3.5 Network and Routing Status X.4.1 No answer from host - */ - log0(ctx, DEBUG, "check if at least one NNTP session successfully established"); - if (ctx->nsc == 0) { - log0(ctx, ERROR, "no NNTP session established"); - res.statuscode = "421"; - res.dsncode = "4.4.1"; - res.statusmsg = "No NNTP session established."; - CU(LMTP_OK); - } - } - - ctx->session.lhlo_seen = TRUE; - - /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 250 Requested mail action okay, completed - */ - str_format(str, sizeof(str), - "%s Hello %s, pleased to meet you.\n" /* RFC2821 4.1.1.1 */ - "ENHANCEDSTATUSCODES\n" /* RFC2034 */ - "DSN\n" /* RFC1894 */ - "PIPELINING\n" /* RFC1854 */ - "8BITMIME", /* RFC1652 */ - ctx->uname.nodename, - ctx->session.lhlo_domain); - res.statuscode = "250"; - res.dsncode = NULL; /* DSN not used for greeting */ - res.statusmsg = str; - CU(LMTP_OK); - - CUS: - lmtp_response(lmtp, &res); - return rc; -} - -static void lmtp_gfs_ns(struct ns *ns) -{ - if (ns->nntp != NULL) { - nntp_destroy(ns->nntp); - ns->nntp = NULL; - } - if (ns->sa != NULL) { - sa_destroy(ns->sa); - ns->sa = NULL; - } - if (ns->saa != NULL) { - sa_addr_destroy(ns->saa); - ns->saa = NULL; - } - if (ns->p != NULL) { - free(ns->p); - ns->p = NULL; - } - if (ns->h != NULL) { - free(ns->h); - ns->h = NULL; - } -} - -static void lmtp_gfs_lhlo(lmtp2nntp_t *ctx) -{ - int i; - - log0(ctx, TRACE, "LMTP service LHLO command - graceful shutdown"); - - for (i = 0; i < ctx->nsc; i++) - lmtp_gfs_ns(&ctx->ns[i]); - - if (ctx->option_mailfrom != NULL) - free(ctx->option_mailfrom); - if (ctx->cpBindh != NULL) - free(ctx->cpBindh); - if (ctx->cpBindp != NULL) - free(ctx->cpBindp); - if (ctx->saBind != NULL) - sa_destroy(ctx->saBind); - if (ctx->saaBind != NULL) - sa_addr_destroy(ctx->saaBind); -} - -static int helo_rfc0821domain(char *msg, char **domain) -{ - int rc; - - rc = str_parse(msg, - "^.+ (" - /* - ## - ## The mega Perl regular expression below is generated - ## with the following Perl program. This is only possible - ## because the given grammar is Chomsky-3 (right or left - ## linear grammar, but noth both). - ## - - # BNF grammar for according to RFC0821: - # ::= one, two, or three digits representing a decimal integer value in the range 0 through 255 - # ::= any one of the 52 alphabetic characters A through Z in upper case and a through z in lower case - # ::= any one of the ten digits 0 through 9 - # ::= | | "-" - # ::= | - # ::= | - # ::= "." "." "." - # ::= | - # ::= - # ::= | "#" | "[" "]" - # ::= | "." - # - # corresponding Perl regular expression ($domain) - $snum = "(?:[0-9]|[0-9]{2}|[0-1][0-9]{2}|2[0-4][0-9]|25[0-5])"; - $d = "[0-9]"; - $a = "[A-Za-z]"; - $let_dig_hyp = "(?:$a|$d|-)"; - $let_dig = "(?:$a|$d)"; - $ldh_str = "${let_dig_hyp}+"; - $dotnum = "$snum\\.$snum\\.$snum\\.$snum"; - $number = "$d+"; - $name = "$a$ldh_str$let_dig"; - $element = "(?:$name|#$number|\\[$dotnum\\])"; - $domain = "(?:$element\.)*$element"; - # - # translate into C string block suitable for passing to the Perl - # Compatible Regular Expressions (PCRE) based string library Str. - my $cregex = $domain; - $cregex .= "\n"; - $cregex =~ s|\\|\\\\|sg; - $cregex =~ s|(.{17})|$1\n|sg; - $cregex =~ s|([^\n]+)\n|"$1"\n|sg; - $cregex =~ s|\n\n|\n|sg; - print "$cregex"; - */ - - "(?:(?:[A-Za-z](?:[A-Za-z]|[0-9]|-)+(?:[A-Za-z]|[0-9])|#[0-9]+|\\[(?:[0" - "-9]|[0-9]{2}|[0-1][0-9]{2}|2[0-4][0-9]|25[0-5])\\.(?:[0-9]|[0-9]{2}|[0" - "-1][0-9]{2}|2[0-4][0-9]|25[0-5])\\.(?:[0-9]|[0-9]{2}|[0-1][0-9]{2}|2[0" - "-4][0-9]|25[0-5])\\.(?:[0-9]|[0-9]{2}|[0-1][0-9]{2}|2[0-4][0-9]|25[0-5" - "])\\]).)*(?:[A-Za-z](?:[A-Za-z]|[0-9]|-)+(?:[A-Za-z]|[0-9])|#[0-9]+|\\" - "[(?:[0-9]|[0-9]{2}|[0-1][0-9]{2}|2[0-4][0-9]|25[0-5])\\.(?:[0-9]|[0-9]" - "{2}|[0-1][0-9]{2}|2[0-4][0-9]|25[0-5])\\.(?:[0-9]|[0-9]{2}|[0-1][0-9]{" - "2}|2[0-4][0-9]|25[0-5])\\.(?:[0-9]|[0-9]{2}|[0-1][0-9]{2}|2[0-4][0-9]|" - "25[0-5])\\])" - - ")$", domain); - return rc; -} - -static int helo_rfc1035domain(char *msg, char **domain) -{ - int rc; - - rc = str_parse(msg, - "^.+ (" - /* - ## - ## The mega Perl regular expression below is generated - ## with the following Perl program. This is only possible - ## because the given grammar is Chomsky-3 (right or left - ## linear grammar, but noth both). - ## - - # BNF grammar for according to RFC1035: - # ::= any one of the 52 alphabetic characters A through Z in upper case and a through z in lower case - # ::= any one of the ten digits 0 through 9 - # ::= | - # ::= | "-" - # ::= | - #
:] */ + cpHeadername = strdup(optarg); + if ((cp = strchr(cpHeadername, ':')) == NULL) { + free(cpHeadername); + fprintf(stderr, "%s:Error: header \"%s\" for -h option not terminated with colon\n", + ctx->progname, cpHeadername); + CU(ERR_EXECUTION); + } + cp++; + if (*cp == NUL) { + free(cpHeadername); + fprintf(stderr, "%s:Error: header \"%s\" for -h option has no value\n", + ctx->progname, cpHeadername); + CU(ERR_EXECUTION); + } + cpHeadervalue = strdup(cp); + *cp = NUL; + argz_add(&ctx->azHeaderValuePairs, &ctx->asHeaderValuePairs, cpHeadername); + argz_add(&ctx->azHeaderValuePairs, &ctx->asHeaderValuePairs, cpHeadervalue); + free(cpHeadervalue); + free(cpHeadername); + break; + case 'l': /*POD [B<-l> I[:I]] */ + if ((cp = strrchr(optarg, ':')) != NULL) { + *cp++ = NUL; + if (*cp == NUL) { + fprintf(stderr, "%s:Error: empty logfile to option -l\n", ctx->progname); + CU(ERR_EXECUTION); + } + else + ctx->option_logfile = strdup(cp); + } + else + ctx->option_logfile = strdup("logfile"); + + if (l2_util_s2l(optarg, strlen(optarg), ',', &ctx->option_levelmask) != L2_OK) { + fprintf(stderr, "%s:Error: invalid level \"%s\" to option -l\n", ctx->progname, optarg); + CU(ERR_EXECUTION); + } + ctx->option_levelmask = L2_LEVEL_UPTO(ctx->option_levelmask); + break; + case 'm': /*POD [B<-m> I] */ + ctx->option_mailfrom = strdup(optarg); + /* protect ourselfs from the substitution of backreferences. + * Missing varargs would cause segfaults. Rewrite capturing + * brackets to clustering syntax. Use poor man's s///g + * simulator as current str library doesn't support global + * substitution */ + while (str_parse(ctx->option_mailfrom, "s/(.*?)\\((?!\\?:)(.*)/$1(?:$2/", &cp) > 0) { + free(ctx->option_mailfrom); + ctx->option_mailfrom = cp; + } + if (str_parse("<>", ctx->option_mailfrom) == -1) { + fprintf(stderr, "%s:Error: illegal regex \"%s\" to option -m.\n", ctx->progname, ctx->option_mailfrom); + CU(ERR_EXECUTION); + } + break; + case 'n': /*POD [B<-n> I] */ + if (strlen(optarg) > sizeof(ctx->uname.nodename)-1) { + fprintf(stderr, "%s:Error: nodename \"%s\" to long to option -n.\n", ctx->progname, optarg); + CU(ERR_EXECUTION); + } + strcpy(ctx->uname.nodename, optarg); + break; + case 'o': /*POD [B<-o> I] */ + if (strcasecmp(optarg, "post") == 0) + ctx->option_operationmode = OPERATIONMODE_POST; + else if (strcasecmp(optarg, "feed") == 0) + ctx->option_operationmode = OPERATIONMODE_FEED; + else { + if (strlen(optarg) != 9) { + fprintf(stderr, "%s:Error: Invalid format or length \"%s\" to option -o\n", ctx->progname, optarg); + CU(ERR_EXECUTION); + } + + if (optarg[3] != '/') { + fprintf(stderr, "%s:Error: Invalid format or missing slash \"%s\" to option -o\n", ctx->progname, optarg); + CU(ERR_EXECUTION); + } + + optarg[3] = NUL; + ctx->option_operationmodefakestatus = &optarg[0]; + ctx->option_operationmodefakedsn = &optarg[4]; + + if ( strlen(ctx->option_operationmodefakestatus) != 3 + || !isdigit((int)ctx->option_operationmodefakestatus[0]) + || !isdigit((int)ctx->option_operationmodefakestatus[1]) + || !isdigit((int)ctx->option_operationmodefakestatus[2])) { + fprintf(stderr, "%s:Error: Invalid status in format \"%s\" to option -o\n", ctx->progname, optarg); + CU(ERR_EXECUTION); + } + + if ( (strlen(ctx->option_operationmodefakedsn) != 5) + || !isdigit((int)ctx->option_operationmodefakedsn[0]) + || (ctx->option_operationmodefakedsn[1] != '.') + || !isdigit((int)ctx->option_operationmodefakedsn[2]) + || (ctx->option_operationmodefakedsn[3] != '.') + || !isdigit((int)ctx->option_operationmodefakedsn[4]) + || (ctx->option_operationmodefakedsn[0] != ctx->option_operationmodefakestatus[0])) { + fprintf(stderr, "%s:Error: Invalid dsn in format \"%s\" to option -o\n", ctx->progname, optarg); + CU(ERR_EXECUTION); + } + } + break; + case 'r': /*POD [B<-r> I] */ + ctx->option_restrictheader = strdup(optarg); + /* protect ourselfs from the substitution of backreferences. + * Missing varargs would cause segfaults. Rewrite capturing + * brackets to clustering syntax. Use poor man's s///g + * simulator as current str library doesn't support global + * substitution */ + while (str_parse(ctx->option_restrictheader, "s/(.*?)\\((?!\\?:)(.*)/$1(?:$2/", &cp) > 0) { + free(ctx->option_restrictheader); + ctx->option_restrictheader = cp; + } + if (str_parse("<>", ctx->option_restrictheader) == -1) { + fprintf(stderr, "%s:Error: illegal regex \"%s\" to option -r.\n", ctx->progname, ctx->option_restrictheader); + CU(ERR_EXECUTION); + } + break; + case 's': /*POD [B<-s> I] */ + ctx->option_maxmessagesize = atoi(optarg); + if(ctx->option_maxmessagesize < 64) { + fprintf(stderr, "%s:Error: maximum message size is unacceptable small.\n", ctx->progname); + CU(ERR_EXECUTION); + } + break; + case 't': /*POD [B<-t> I=I[,I=I[,...]] */ + if (argz_create_sep(optarg, ',', &azTimeout, &asTimeout) != 0) + CU(ERR_EXECUTION); + cp = NULL; + while ((cp = argz_next(azTimeout, asTimeout, cp)) != NULL) { + cpName = strdup(cp); + if ((cpValue = strrchr(cpName, '=')) == NULL) { + fprintf(stderr, "%s:Error: comma-seperated argument %s to option -t have to be name=value.\n", ctx->progname, cp); + CU(ERR_EXECUTION); + } + *cpValue++ = NUL; + nValue = atoi(cpValue); + if (nValue < 0) { + fprintf(stderr, "%s:Error: timeout %s=%d to option -t must be a positive integer.\n", ctx->progname, cpName, nValue); + CU(ERR_EXECUTION); + } + if (strcmp(cpName, "lmtp") == 0) { + ctx->option_timeout_lmtp_accept = nValue; + ctx->option_timeout_lmtp_read = nValue; + ctx->option_timeout_lmtp_write = nValue; + } + else if (strcmp(cpName, "lmtp:accept") == 0) + ctx->option_timeout_lmtp_accept = nValue; + else if (strcmp(cpName, "lmtp:read") == 0) + ctx->option_timeout_lmtp_read = nValue; + else if (strcmp(cpName, "lmtp:write") == 0) + ctx->option_timeout_lmtp_write = nValue; + else if (strcmp(cpName, "nntp") == 0) { + ctx->option_timeout_nntp_connect = nValue; + ctx->option_timeout_nntp_read = nValue; + ctx->option_timeout_nntp_write = nValue; + } + else if (strcmp(cpName, "nntp:connect") == 0) + ctx->option_timeout_nntp_connect = nValue; + else if (strcmp(cpName, "nntp:read") == 0) + ctx->option_timeout_nntp_read = nValue; + else if (strcmp(cpName, "nntp:write") == 0) + ctx->option_timeout_nntp_write = nValue; + else { + fprintf(stderr, "%s:Error: unknown timeout %s to option -t.\n", ctx->progname, cpName); + CU(ERR_EXECUTION); + } + free(cpName); + } + free(azTimeout); + break; + case 'u': /*POD [B<-u> I] */ + if (isdigit((int)optarg[0])) { + if ((sPasswd = getpwuid((uid_t)atoi(optarg))) == NULL) { + fprintf(stderr, "%s:Error: uid \"%s\" not found for -u option.\n", ctx->progname, optarg); + CU(ERR_EXECUTION); + } + } + else { + if ((sPasswd = getpwnam(optarg)) == NULL) { + fprintf(stderr, "%s:Error: loginname \"%s\" not found for -u option.\n", ctx->progname, optarg); + CU(ERR_EXECUTION); + } + } + ctx->option_uid = sPasswd->pw_uid; + break; + case 'v': /*POD [B<-v>] (version)*/ + fprintf(stdout, "%s\n", lmtp2nntp_version.v_gnu); + CU(0); + break; + case '?': + default: + usage(ctx->progname); + CU(ERR_EXECUTION); + } + } + /*POD I [I ...] */ + for (i = optind; i < argc; i++) { + argz_add(&ctx->azGroupargs, &ctx->asGroupargs, argv[i]); + } + + /* if no positive ACL exists (option -a) add a wildcard match-all for IPv4 and IPv6 */ + bOk = FALSE; + for (i = 0; i < ctx->option_aclc; i++) { + if (!ctx->option_acl[i].not) { + bOk = TRUE; + break; + } + } + if (!bOk) { + if (ctx->option_aclc >= MAXACLS) { + fprintf(stderr, "%s:Error: Too many ACL (%d) using option -a (no space for additional fake IPv4 ACL)\n", ctx->progname, ctx->option_aclc); + CU(ERR_EXECUTION); + } + ctx->option_acl[ctx->option_aclc].acl = "0.0.0.0"; + ctx->option_acl[ctx->option_aclc].not = FALSE; + ctx->option_acl[ctx->option_aclc].prefixlen = 0; + if ((rc = sa_addr_create(&ctx->option_acl[ctx->option_aclc].saa)) != SA_OK) { + fprintf(stderr, "%s:Error: Creating fake address failed for -a option (%d)\n", + ctx->progname, rc); + } + if ((rc = sa_addr_u2a(ctx->option_acl[ctx->option_aclc].saa, "inet://%s:0", ctx->option_acl[ctx->option_aclc].acl)) != SA_OK) { + fprintf(stderr, "%s:Error: Parsing host address failed for \"%s:0\" (%s)\n", + ctx->progname, ctx->option_acl[ctx->option_aclc].acl, + sa_error(rc)); + CU(ERR_EXECUTION); + } + ctx->option_aclc++; + } + if (!bOk) { + if (ctx->option_aclc >= MAXACLS) { + fprintf(stderr, "%s:Error: Too many ACL (%d) using option -a (no space for additional fake IPv6 ACL)\n", ctx->progname, ctx->option_aclc); + CU(ERR_EXECUTION); + } + ctx->option_acl[ctx->option_aclc].acl = "[::]"; + ctx->option_acl[ctx->option_aclc].not = FALSE; + ctx->option_acl[ctx->option_aclc].prefixlen = 0; + if ((rc = sa_addr_create(&ctx->option_acl[ctx->option_aclc].saa)) != SA_OK) { + fprintf(stderr, "%s:Error: Creating fake address failed for -a option (%d)\n", + ctx->progname, rc); + } + if ((rc = sa_addr_u2a(ctx->option_acl[ctx->option_aclc].saa, "inet://%s:0", ctx->option_acl[ctx->option_aclc].acl)) != SA_OK) { + fprintf(stderr, "%s:Error: Parsing host address failed for \"%s:0\" (%s)\n", + ctx->progname, ctx->option_acl[ctx->option_aclc].acl, + sa_error(rc)); + CU(ERR_EXECUTION); + } + ctx->option_aclc++; + } + + if (getuid() != ctx->option_uid) { + if (setuid(ctx->option_uid) == -1) { + fprintf(stderr, "%s:Error: Setting UID to %d failed: %s\n", + ctx->progname, ctx->option_uid, strerror(errno)); + CU(ERR_EXECUTION); + } + } + + /* create L2 environment */ + if (l2_env_create(&ctx->l2_env) != L2_OK) { + fprintf(stderr, "%s:Error: failed to create L2 environment\n", ctx->progname); + CU(ERR_EXECUTION); + } + + /* register custom L2 formatters */ + if (l2_env_formatter(ctx->l2_env, 'P', formatter_prefix, &ctx->ctx) != L2_OK) { + fprintf(stderr, "%s:Error: logging failed to register prefix formatter\n", ctx->progname); + CU(ERR_EXECUTION); + } + if (l2_env_formatter(ctx->l2_env, 'D', l2_util_fmt_dump, NULL) != L2_OK) { + fprintf(stderr, "%s:Error: logging failed to register dump formatter\n", ctx->progname); + CU(ERR_EXECUTION); + } + if (l2_env_formatter(ctx->l2_env, 'S', l2_util_fmt_string, NULL) != L2_OK) { + fprintf(stderr, "%s:Error: logging failed to register string formatter\n", ctx->progname); + CU(ERR_EXECUTION); + } + if (l2_env_formatter(ctx->l2_env, 'm', formatter_errno, NULL) != L2_OK) { + fprintf(stderr, "%s:Error: logging failed to register errno formatter\n", ctx->progname); + CU(ERR_EXECUTION); + } + + /* create channel stream */ + if (ctx->option_levelmask != L2_LEVEL_NONE && ctx->option_logfile != NULL) { + if (ctx->option_veryverbose) + rc = l2_spec(&ctx->l2, ctx->l2_env, + "prefix(prefix=\"%%b %%d %%H:%%M:%%S <%%L> lmtp2nntp[%%P]: \",timezone=local)" + " -> buffer(size=65536)" + " -> file(path=%s,append=1,perm=%d)", + ctx->option_logfile, 0644); + else + rc = l2_spec(&ctx->l2, ctx->l2_env, + "prefix(prefix=\"%%b %%d %%H:%%M:%%S <%%L> lmtp2nntp[%%P]: \",timezone=local)" + " -> file(path=%s,append=1,perm=%d)", + ctx->option_logfile, 0644); + if (rc != L2_OK) { + fprintf(stderr, "%s:Error: logging failed to create stream\n", ctx->progname); + CU(ERR_EXECUTION); + } + if (l2_channel_levels(ctx->l2, ctx->option_levelmask, L2_LEVEL_NONE) != L2_OK) { + fprintf(stderr, "%s:Error: logging failed to set global logging level\n", ctx->progname); + CU(ERR_EXECUTION); + } + if (l2_channel_open(ctx->l2) != L2_OK) { + fprintf(stderr, "%s:Error: logging failed to open channel stream\n", ctx->progname); + CU(ERR_EXECUTION); + } + } + + /* from this point on logging is up and running and fprintf(stderr, ...) + * should not be used in the remainder of the code + */ + + log1(ctx, NOTICE, "startup, version %s", lmtp2nntp_version.v_gnu); + if (ctx->option_veryverbose) + log0(ctx, NOTICE, "logging very verbose (unbuffered)"); + + if ((ctx->option_pidfile != NULL) && ctx->option_killflag) { + if ((fd = fopen(ctx->option_pidfile, "r")) == NULL) + log1(ctx, ERROR, "cannot open pidfile \"%s\" for reading %m", ctx->option_pidfile); + else { + if (fscanf(fd, "%d\n", &pid) != 1) { + fclose(fd); + log1(ctx, ERROR, "cannot extract pid from pidfile \"%s\"", ctx->option_pidfile); + } + else { + fclose(fd); + log1(ctx, TRACE, "going to kill pid[%d]", pid); + if (kill(pid, SIGHUP) == -1) + log1(ctx, ERROR, "killing pid[%d] failed %m", pid); + if (unlink(ctx->option_pidfile) == -1) + log1(ctx, ERROR, "unlinking pidfile \"%s\" failed %m", ctx->option_pidfile); + } + } + CU(0); + } + + catchsignal(0, ctx); + signal(SIGCHLD, (void(*)())catchsignal); + signal(SIGHUP, (void(*)())catchsignal); + signal(SIGINT, (void(*)())catchsignal); + signal(SIGQUIT, (void(*)())catchsignal); + signal(SIGILL, (void(*)())catchsignal); + signal(SIGBUS, (void(*)())catchsignal); + signal(SIGSEGV, (void(*)())catchsignal); + signal(SIGSYS, (void(*)())catchsignal); + signal(SIGTERM, (void(*)())catchsignal); + signal(SIGUSR1, (void(*)())catchsignal); + signal(SIGUSR2, SIG_IGN ); + + /* loop for LMTP protocol with support for alternate io through daemon */ + if (ctx->saAltio == NULL) { + /* initialize LMTP context */ + ctx->fdIOi = STDIN_FILENO; + ctx->fdIOo = STDOUT_FILENO; + lmtp_io.ctx = ctx; + lmtp_io.read = hook_lmtp_read; + lmtp_io.write = hook_lmtp_write; + if ((lmtp = lmtp_create(&lmtp_io)) == NULL) { + log0(ctx, ERROR, "Unable to initialize LMTP library\n"); + CU(ERR_EXECUTION); + } + /* RFC0821, 4.5.1. MINIMUM IMPLEMENTATION + * In order to make SMTP workable, the following minimum implementation + * is required for all receivers: [...] + * RFC0821, 4.1.2. COMMAND SYNTAX + * + * Verb Parameter + * ----+------------------------------- + * HELO + * MAIL FROM: + * RCPT TO: + * DATA + * RSET + * NOOP + * QUIT + */ + lmtp_register(lmtp, "LHLO", lmtp_cb_lhlo, ctx, NULL, NULL); + lmtp_register(lmtp, "MAIL", lmtp_cb_mail, ctx, NULL, NULL); + lmtp_register(lmtp, "RCPT", lmtp_cb_rcpt, ctx, NULL, NULL); + lmtp_register(lmtp, "DATA", lmtp_cb_data, ctx, NULL, NULL); + lmtp_register(lmtp, "RSET", lmtp_cb_rset, ctx, NULL, NULL); + lmtp_register(lmtp, "NOOP", lmtp_cb_noop, ctx, NULL, NULL); + lmtp_register(lmtp, "QUIT", lmtp_cb_quit, ctx, NULL, NULL); + lmtp_loop(lmtp); + lmtp_gfs_quit(ctx); + lmtp_gfs_lhlo(ctx); + lmtp_destroy(lmtp); + } else { + pid = getpid(); + if (ctx->option_daemon) { + daemonize(); + log1(ctx, NOTICE, "daemonized, previous pid[%d]", pid); + } + if (ctx->option_pidfile != NULL) { + if ((fd = fopen(ctx->option_pidfile, "w+")) == NULL) + log1(ctx, ERROR, "cannot open pidfile \"%s\" for writing %m", ctx->option_pidfile); + else { + fprintf(fd, "%d\n", getpid()); + fclose(fd); + } + } + + sa_timeout(ctx->saAltio, SA_TIMEOUT_ALL, 0, 0); + sa_timeout(ctx->saAltio, SA_TIMEOUT_ACCEPT, ctx->option_timeout_lmtp_accept, 0); + sa_timeout(ctx->saAltio, SA_TIMEOUT_READ, ctx->option_timeout_lmtp_read, 0); + sa_timeout(ctx->saAltio, SA_TIMEOUT_WRITE, ctx->option_timeout_lmtp_write, 0); + while (1) { + while (ctx->active_childs >= ctx->option_childsmax) { + log1(ctx, ERROR, "maximum number of childs (%d) reached - waiting (1s)", ctx->option_childsmax); + sleep(1); + } + + if ((rc = sa_accept(ctx->saAltio, &ctx->saaIO, &ctx->saIO)) != SA_OK) { + if (rc == SA_ERR_SYS) + log3(ctx, ERROR, "accept failed: %s: (%d) %s", sa_error(rc), errno, strerror(errno)); + else + log1(ctx, ERROR, "accept failed: %s", sa_error(rc)); + sleep(10); + continue; + } + + /* Access Control List */ + bOk = FALSE; + /* check positive matches */ + for (i = 0; i < ctx->option_aclc; i++) { + char *cpA1; + char *cpA2; + if (ctx->option_acl[i].not) + continue; + sa_addr_a2u(ctx->option_acl[i].saa, &cpA1); + sa_addr_a2u(ctx->saaIO, &cpA2); + if (sa_addr_match(ctx->saaIO, ctx->option_acl[i].saa, ctx->option_acl[i].prefixlen) == SA_OK) { + log4(ctx, TRACE, "positive/inclusive ACL \"%s\" (%s/%d) matches %s: YES (stop comparison)", ctx->option_acl[i].acl, cpA1, ctx->option_acl[i].prefixlen, cpA2); + bOk = TRUE; + break; + } + else + log4(ctx, TRACE, "positive/inclusive ACL \"%s\" (%s/%d) matches %s: NO", ctx->option_acl[i].acl, cpA1, ctx->option_acl[i].prefixlen, cpA2); + free(cpA1); + free(cpA2); + } + /* check negative matches */ + for (i = 0; i < ctx->option_aclc; i++) { + char *cpA1; + char *cpA2; + if (!ctx->option_acl[i].not) + continue; + sa_addr_a2u(ctx->option_acl[i].saa, &cpA1); + sa_addr_a2u(ctx->saaIO, &cpA2); + if (sa_addr_match(ctx->saaIO, ctx->option_acl[i].saa, ctx->option_acl[i].prefixlen) == SA_OK) { + log4(ctx, TRACE, "negative/exclusive ACL \"%s\" (not %s/%d) matches %s: YES (stop comparison)", ctx->option_acl[i].acl, cpA1, ctx->option_acl[i].prefixlen, cpA2); + bOk = FALSE; + break; + } + else { + log4(ctx, TRACE, "negative/exclusive ACL \"%s\" (not %s/%d) matches %s: NO", ctx->option_acl[i].acl, cpA1, ctx->option_acl[i].prefixlen, cpA2); + } + } + if (bOk) { + char *cpA; + sa_addr_a2u(ctx->saaIO, &cpA); + log1(ctx, TRACE, "connection from %s accepted due to ACL", cpA); + free(cpA); + } + else { + char *cpA; + sa_addr_a2u(ctx->saaIO, &cpA); + log1(ctx, ERROR, "connection from %s refused due to ACL", cpA); + free(cpA); + sa_destroy(ctx->saIO); + sa_addr_destroy(ctx->saaIO); + continue; + } + + /* logging buffer must be empty before fork otherwise content is + * duplicated and written twice on next flush */ + l2_channel_flush(ctx->l2); + pid = fork(); + if (pid == -1) { + log0(ctx, ERROR, "daemon cannot spawn child %m"); + continue; + } + if (pid != 0) { + log1(ctx, INFO, "daemon forked process, new child pid[%d]", pid); + ctx->active_childs++; + sa_destroy(ctx->saIO); + sa_addr_destroy(ctx->saaIO); + continue; + } + log1(ctx, NOTICE, "startup new child process, parent pid[%d]", getppid()); + + /* child must close listening socket */ + sa_destroy(ctx->saAltio); + ctx->saAltio = NULL; /* prevent cleanup from free'ing this again */ + + /* initialize LMTP context */ + lmtp_io.ctx = ctx; + lmtp_io.read = hook_lmtp_read; + lmtp_io.write = hook_lmtp_write; + if ((lmtp = lmtp_create(&lmtp_io)) == NULL) { + log0(ctx, ERROR, "Unable to initialize LMTP library\n"); + CU(ERR_EXECUTION); + } + /* RFC0821, 4.5.1. MINIMUM IMPLEMENTATION + * In order to make SMTP workable, the following minimum implementation + * is required for all receivers: [...] + * RFC0821, 4.1.2. COMMAND SYNTAX + * + * Verb Parameter + * ----+------------------------------- + * HELO + * MAIL FROM: + * RCPT TO: + * DATA + * RSET + * NOOP + * QUIT + */ + lmtp_register(lmtp, "LHLO", lmtp_cb_lhlo, ctx, NULL, NULL); + lmtp_register(lmtp, "MAIL", lmtp_cb_mail, ctx, NULL, NULL); + lmtp_register(lmtp, "RCPT", lmtp_cb_rcpt, ctx, NULL, NULL); + lmtp_register(lmtp, "DATA", lmtp_cb_data, ctx, NULL, NULL); + lmtp_register(lmtp, "RSET", lmtp_cb_rset, ctx, NULL, NULL); + lmtp_register(lmtp, "NOOP", lmtp_cb_noop, ctx, NULL, NULL); + lmtp_register(lmtp, "QUIT", lmtp_cb_quit, ctx, NULL, NULL); + lmtp_loop(lmtp); + lmtp_gfs_quit(ctx); + lmtp_gfs_lhlo(ctx); + lmtp_destroy(lmtp); + CU(0); + } + } + CU(0); + + /* graceful shutdown */ + CUS: + log0(ctx, NOTICE, "graceful shutdown shortly before exit - no more logging"); + l2_channel_destroy(ctx->l2); + l2_env_destroy(ctx->l2_env); + if (ctx->saAltio) + sa_destroy(ctx->saAltio); + if (ctx->saaAltio) + sa_addr_destroy(ctx->saaAltio); + if (ctx->option_restrictheader != NULL) + free(ctx->option_restrictheader); + if (ctx->azHeaderValuePairs != NULL) + free(ctx->azHeaderValuePairs); + if (ctx->option_pidfile != NULL) + free(ctx->option_pidfile); + if (ctx->option_logfile != NULL) + free(ctx->option_logfile); + if (ctx->progname != NULL) + free(ctx->progname); + if (ctx->azGroupargs != NULL) + free(ctx->azGroupargs); + if (ctx != NULL) + free(ctx); + str_parse(NULL, NULL); + + return rc; +} + +static void resetsession(struct session *session) +{ + if (session->lhlo_domain != NULL) + free(session->lhlo_domain); + initsession(session); + return; +} + +static void initsession(struct session *session) +{ + session->lhlo_seen = FALSE; + session->lhlo_domain = NULL; + return; +} + +static lmtp_rc_t lmtp_cb_lhlo(lmtp_t *lmtp, lmtp_io_t *io, lmtp_req_t *req, void *_ctx) +{ + /* + * RFC0821 [excerpt] 4.1. SMTP COMMANDS + * 4.1.1. COMMAND SEMANTICS, HELO + * This command and an OK reply to it confirm that both the sender-SMTP + * and the receiver-SMTP are in the initial state, that is, there is no + * transaction in progress and all state tables and buffers are cleared. + * + * The first command in a session must be the HELO command. The HELO + * command may be used later in a session as well. If the HELO command + * argument is not acceptable a 501 failure reply must be returned and + * the receiver-SMTP must stay in the same state. + * + * If the transaction beginning command argument is not acceptable a 501 + * failure reply must be returned and the receiver-SMTP must stay in the + * same state. If the commands in a transaction are out of order a 503 + * failure reply must be returned and the receiver-SMTP must stay in the + * same state. + * + * HELO + */ + lmtp2nntp_t *ctx = (lmtp2nntp_t *)_ctx; + nntp_rc_t rc; + lmtp_res_t res; + char str[STDSTRLEN]; + int bOk; + int i; + nntp_io_t nntp_io; + + log1(ctx, INFO, "LMTP service executing LHLO command < %s", req->msg); + + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 503 Bad sequence of commands + * RFC1893 2. Status Codes 5.X.X Permanent Failure + * RFC1893 3.5 Network and Routing Status X.0.0 Other undefined Status + */ + log0(ctx, TRACE, "checking for duplicate LHLO"); + if (ctx->session.lhlo_seen) { + res.statuscode = "503"; + res.dsncode = "5.0.0"; + res.statusmsg = "Duplicate LHLO."; + CU(LMTP_OK); + } + + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 501 Syntax error in parameters or arguments + * RFC1893 2. Status Codes 5.X.X Permanent Failure + * RFC1893 3.5 Network and Routing Status X.0.0 Other undefined Status + */ + log0(ctx, TRACE, "checking domain to match either RFC0821 or RFC1035 syntax"); + if (! ( helo_rfc0821domain(req->msg, &ctx->session.lhlo_domain) > 0 + || helo_rfc1035domain(req->msg, &ctx->session.lhlo_domain) > 0)) { + res.statuscode = "501"; + res.dsncode = "5.0.0"; + res.statusmsg = "Please identify yourself. Domain must match RFC0821/RFC1035."; + CU(LMTP_OK); + } + + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 451 Requested action aborted: local error in processing + * RFC1893 2. Status Codes 4.X.X Persistent Transient Failure + * RFC1893 3.5 Network and Routing Status X.3.5 System incorrectly configured + */ + if (ctx->option_operationmode != OPERATIONMODE_FAKE) { + log0(ctx, TRACE, "check if at least one NNTP service was successfully configured"); + if (ctx->nsc == 0) { + res.statuscode = "451"; + res.dsncode = "4.3.5"; + res.statusmsg = "No valid NNTP services configured."; + CU(LMTP_OK); + } + } + + log0(ctx, TRACE, "try to establish a session to any configured NNTP services"); + if (ctx->option_operationmode == OPERATIONMODE_FAKE) + log0(ctx, NOTICE, "NNTP running in fake mode, network connections will be executed but result is ignored"); + i = 0; + do { + log1(ctx, DEBUG, "trying ns[%d]", i); + bOk = TRUE; + log2(ctx, TRACE, "try %s:%s", ctx->ns[i].h, ctx->ns[i].p); + + ctx->ns[i].l2 = ctx->l2; + + if (bOk && (ctx->saaBind != NULL)) { + log2(ctx, DEBUG, "bind local socket to %s:%s", ctx->cpBindh, ctx->cpBindp); + if (sa_bind(ctx->ns[i].sa, ctx->saaBind) != SA_OK) { + bOk = FALSE; + log2(ctx, ERROR, "binding NNTP client to local address %s:%s failed, %m", ctx->cpBindh, ctx->cpBindp); + } + } + + sa_timeout(ctx->ns[i].sa, SA_TIMEOUT_ALL, 0, 0); + sa_timeout(ctx->ns[i].sa, SA_TIMEOUT_CONNECT, ctx->option_timeout_nntp_connect, 0); + sa_timeout(ctx->ns[i].sa, SA_TIMEOUT_READ, ctx->option_timeout_nntp_read, 0); + sa_timeout(ctx->ns[i].sa, SA_TIMEOUT_WRITE, ctx->option_timeout_nntp_read, 0); + + if (bOk) { + log0(ctx, DEBUG, "connect"); + if (sa_connect(ctx->ns[i].sa, ctx->ns[i].saa) != SA_OK) { + bOk = FALSE; + log2(ctx, WARNING, "connect to %s:%s failed, %m", + ctx->ns[i].h, ctx->ns[i].p); + } + } + + if (bOk) { + log0(ctx, DEBUG, "nntp_create"); + nntp_io.ctx = &ctx->ns[i]; + nntp_io.read = hook_nntp_read; + nntp_io.write = hook_nntp_write; + if ((ctx->ns[i].nntp = nntp_create(&nntp_io)) == NULL) { + bOk = FALSE; + log0(ctx, ERROR, "creation of NNTP context failed"); + } + } + + if (bOk) { + log0(ctx, DEBUG, "nntp_init"); + if ((rc = nntp_init(ctx->ns[i].nntp)) != NNTP_OK) { + bOk = FALSE; + log2(ctx, ERROR, "initialization of NNTP context failed, (%d) %s", rc, nntp_error(rc)); + } + } + + if (bOk) { + log2(ctx, INFO, "NNTP session to %s:%s successfully established", ctx->ns[i].h, ctx->ns[i].p); + i++; + } + else { + log2(ctx, WARNING, "NNTP session establishment to %s:%s failed", ctx->ns[i].h, ctx->ns[i].p); + log1(ctx, DEBUG, "removing ns[%d] from list", i); + lmtp_gfs_ns(&ctx->ns[i]); + if (i < --ctx->nsc) { + memcpy(&ctx->ns[i], &ctx->ns[i+1], (ctx->nsc - i ) * sizeof(struct ns)); + } + } + } while (i < ctx->nsc); + + if (ctx->option_operationmode == OPERATIONMODE_FAKE) + log1(ctx, NOTICE, "NNTP running in fake mode, network connections successfully established=%d but ignored", ctx->nsc); + else + { + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 421 Service not available + * RFC1893 2. Status Codes 4.X.X Persistent Transient Failure + * RFC1893 3.5 Network and Routing Status X.4.1 No answer from host + */ + log0(ctx, DEBUG, "check if at least one NNTP session successfully established"); + if (ctx->nsc == 0) { + log0(ctx, ERROR, "no NNTP session established"); + res.statuscode = "421"; + res.dsncode = "4.4.1"; + res.statusmsg = "No NNTP session established."; + CU(LMTP_OK); + } + } + + ctx->session.lhlo_seen = TRUE; + + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 250 Requested mail action okay, completed + */ + str_format(str, sizeof(str), + "%s Hello %s, pleased to meet you.\n" /* RFC2821 4.1.1.1 */ + "ENHANCEDSTATUSCODES\n" /* RFC2034 */ + "DSN\n" /* RFC1894 */ + "PIPELINING\n" /* RFC1854 */ + "8BITMIME", /* RFC1652 */ + ctx->uname.nodename, + ctx->session.lhlo_domain); + res.statuscode = "250"; + res.dsncode = NULL; /* DSN not used for greeting */ + res.statusmsg = str; + CU(LMTP_OK); + + CUS: + lmtp_response(lmtp, &res); + return rc; +} + +static void lmtp_gfs_ns(struct ns *ns) +{ + if (ns->nntp != NULL) { + nntp_destroy(ns->nntp); + ns->nntp = NULL; + } + if (ns->sa != NULL) { + sa_destroy(ns->sa); + ns->sa = NULL; + } + if (ns->saa != NULL) { + sa_addr_destroy(ns->saa); + ns->saa = NULL; + } + if (ns->p != NULL) { + free(ns->p); + ns->p = NULL; + } + if (ns->h != NULL) { + free(ns->h); + ns->h = NULL; + } +} + +static void lmtp_gfs_lhlo(lmtp2nntp_t *ctx) +{ + int i; + + log0(ctx, TRACE, "LMTP service LHLO command - graceful shutdown"); + + for (i = 0; i < ctx->nsc; i++) + lmtp_gfs_ns(&ctx->ns[i]); + + if (ctx->option_mailfrom != NULL) + free(ctx->option_mailfrom); + if (ctx->cpBindh != NULL) + free(ctx->cpBindh); + if (ctx->cpBindp != NULL) + free(ctx->cpBindp); + if (ctx->saBind != NULL) + sa_destroy(ctx->saBind); + if (ctx->saaBind != NULL) + sa_addr_destroy(ctx->saaBind); +} + +static int helo_rfc0821domain(char *msg, char **domain) +{ + int rc; + + rc = str_parse(msg, + "^.+ (" + /* + ## + ## The mega Perl regular expression below is generated + ## with the following Perl program. This is only possible + ## because the given grammar is Chomsky-3 (right or left + ## linear grammar, but noth both). + ## + + # BNF grammar for according to RFC0821: + # ::= one, two, or three digits representing a decimal integer value in the range 0 through 255 + # ::= any one of the 52 alphabetic characters A through Z in upper case and a through z in lower case + # ::= any one of the ten digits 0 through 9 + # ::= | | "-" + # ::= | + # ::= | + # ::= "." "." "." + # ::= | + # ::= + # ::= | "#" | "[" "]" + # ::= | "." + # + # corresponding Perl regular expression ($domain) + $snum = "(?:[0-9]|[0-9]{2}|[0-1][0-9]{2}|2[0-4][0-9]|25[0-5])"; + $d = "[0-9]"; + $a = "[A-Za-z]"; + $let_dig_hyp = "(?:$a|$d|-)"; + $let_dig = "(?:$a|$d)"; + $ldh_str = "${let_dig_hyp}+"; + $dotnum = "$snum\\.$snum\\.$snum\\.$snum"; + $number = "$d+"; + $name = "$a$ldh_str$let_dig"; + $element = "(?:$name|#$number|\\[$dotnum\\])"; + $domain = "(?:$element\.)*$element"; + # + # translate into C string block suitable for passing to the Perl + # Compatible Regular Expressions (PCRE) based string library Str. + my $cregex = $domain; + $cregex .= "\n"; + $cregex =~ s|\\|\\\\|sg; + $cregex =~ s|(.{17})|$1\n|sg; + $cregex =~ s|([^\n]+)\n|"$1"\n|sg; + $cregex =~ s|\n\n|\n|sg; + print "$cregex"; + */ + + "(?:(?:[A-Za-z](?:[A-Za-z]|[0-9]|-)+(?:[A-Za-z]|[0-9])|#[0-9]+|\\[(?:[0" + "-9]|[0-9]{2}|[0-1][0-9]{2}|2[0-4][0-9]|25[0-5])\\.(?:[0-9]|[0-9]{2}|[0" + "-1][0-9]{2}|2[0-4][0-9]|25[0-5])\\.(?:[0-9]|[0-9]{2}|[0-1][0-9]{2}|2[0" + "-4][0-9]|25[0-5])\\.(?:[0-9]|[0-9]{2}|[0-1][0-9]{2}|2[0-4][0-9]|25[0-5" + "])\\]).)*(?:[A-Za-z](?:[A-Za-z]|[0-9]|-)+(?:[A-Za-z]|[0-9])|#[0-9]+|\\" + "[(?:[0-9]|[0-9]{2}|[0-1][0-9]{2}|2[0-4][0-9]|25[0-5])\\.(?:[0-9]|[0-9]" + "{2}|[0-1][0-9]{2}|2[0-4][0-9]|25[0-5])\\.(?:[0-9]|[0-9]{2}|[0-1][0-9]{" + "2}|2[0-4][0-9]|25[0-5])\\.(?:[0-9]|[0-9]{2}|[0-1][0-9]{2}|2[0-4][0-9]|" + "25[0-5])\\])" + + ")$", domain); + return rc; +} + +static int helo_rfc1035domain(char *msg, char **domain) +{ + int rc; + + rc = str_parse(msg, + "^.+ (" + /* + ## + ## The mega Perl regular expression below is generated + ## with the following Perl program. This is only possible + ## because the given grammar is Chomsky-3 (right or left + ## linear grammar, but noth both). + ## + + # BNF grammar for according to RFC1035: + # ::= any one of the 52 alphabetic characters A through Z in upper case and a through z in lower case + # ::= any one of the ten digits 0 through 9 + # ::= | + # ::= | "-" + # ::= | + #