diff --new-file -u -r nullmailer-1.00RC7/Makefile.in nullmailer-lq-1.00RC7/Makefile.in --- nullmailer-1.00RC7/Makefile.in Thu Feb 20 01:41:12 2003 +++ nullmailer-lq-1.00RC7/Makefile.in Mon Feb 3 22:18:33 2003 @@ -366,7 +366,9 @@ install-root: chown nullmail $(DESTDIR)$(localstatedir)/* chown nullmail $(DESTDIR)$(sbindir)/nullmailer-queue + chown nullmail $(DESTDIR)$(sbindir)/nullmailer-localqueue chmod u+s $(DESTDIR)$(sbindir)/nullmailer-queue + chmod u+s $(DESTDIR)$(sbindir)/nullmailer-localqueue chown nullmail $(DESTDIR)$(bindir)/mailq chmod u+s $(DESTDIR)$(bindir)/mailq diff --new-file -u -r nullmailer-1.00RC7/README.localqueue nullmailer-lq-1.00RC7/README.localqueue --- nullmailer-1.00RC7/README.localqueue Wed Dec 31 19:00:00 1969 +++ nullmailer-lq-1.00RC7/README.localqueue Thu Feb 20 01:39:49 2003 @@ -0,0 +1,28 @@ +nullmailer-lq +Simple relay-only mail transport agent w/ localqueue support +http://www.basher584.org/basher584/nullmail-lq/ +Version 1.00RC7-lq +2003-02-20 + +This is a patched version of nullmailer to support delivery of local mail +to an admin mailbox in mbox format. + +Nullmailer is a sendmail/qmail/etc replacement MTA for hosts which +relay to a fixed set of smart relays. It is designed to be simple to +configure, secure, and easily extend-able. + +Please see the README file for more information about the nullmailer +program. + +See website for specific changes and more inforamtion: +http://www.basher584.org/basher584/nullmail-lq/ + +src/localqueue.cc is Copyright(C) 2003 Benic Systems, and may be copied +according to the the GNU GENERAL PUBLIC LICENSE (GPL) Version 2 or a later +version. A copy of this license is included with this package. This +package comes with no warranty of any kind. + +queue.cc is from the nullmailer package and Copyright(C) 1999-2003 Bruce +Guenter + +See the README file for the rest of the Copyright information. diff --new-file -u -r nullmailer-1.00RC7/lib/defines.h nullmailer-lq-1.00RC7/lib/defines.h --- nullmailer-1.00RC7/lib/defines.h Thu Feb 20 01:41:12 2003 +++ nullmailer-lq-1.00RC7/lib/defines.h Mon Feb 3 22:18:33 2003 @@ -9,5 +9,6 @@ extern const char* PROTOCOL_DIR; extern const char* BIN_DIR; extern const char* SBIN_DIR; +extern const char* ADMINFILE; #endif /* NULLMAILER__DEFINES__H__ */ diff --new-file -u -r nullmailer-1.00RC7/lib/make_defines.sh nullmailer-lq-1.00RC7/lib/make_defines.sh --- nullmailer-1.00RC7/lib/make_defines.sh Thu Feb 20 01:41:12 2003 +++ nullmailer-lq-1.00RC7/lib/make_defines.sh Thu Feb 20 00:17:57 2003 @@ -7,3 +7,4 @@ echo "const char* PROTOCOL_DIR=\"$3/\";" >>defines.cc echo "const char* BIN_DIR=\"$4/\";" >>defines.cc echo "const char* SBIN_DIR=\"$5/\";" >>defines.cc +echo "const char* ADMINFILE=\"/home/nullmail/mail/inbox\";" >>defines.cc diff --new-file -u -r nullmailer-1.00RC7/nullmailer-1.00RC7.spec nullmailer-lq-1.00RC7/nullmailer-1.00RC7.spec --- nullmailer-1.00RC7/nullmailer-1.00RC7.spec Thu Feb 20 01:41:12 2003 +++ nullmailer-lq-1.00RC7/nullmailer-1.00RC7.spec Thu Feb 20 00:43:33 2003 @@ -1,13 +1,13 @@ -Name: nullmailer +Name: nullmailer-lq Summary: Simple relay-only mail transport agent -Version: 1.00RC7 +Version: 1.00RC7lq Release: 1 Copyright: GPL Group: Networking/Daemons -Source: http://em.ca/~bruceg/nullmailer/archive/%{version}/nullmailer-%{version}.tar.gz +Source: http://www.basher584.org/basher584/nullmail-lq/nullmailer-lq-1.00RC7.tar.gz BuildRoot: /tmp/nullmailer-root -URL: http://em.ca/~bruceg/nullmailer/ -Packager: Bruce Guenter +URL: http://www.basher584.org/basher584/nullmail-lq/ +Packager: Basher584 Provides: smtpdaemon Conflicts: sendmail Conflicts: qmail @@ -15,8 +15,10 @@ PreReq: shadow-utils %description -Nullmailer is a mail transport agent designed to only relay all its -messages through a fixed set of "upstream" hosts. It is also designed +Nullmailer-localqueue is a patched version of Bruce Guenter's nullmailer +program that delivers all local mail to a local mbox. +Nullmailer is a mail transport agent designed to only relay all +its messages through a fixed set of "upstream" hosts. It is also designed to be secure. %prep @@ -87,6 +89,7 @@ /usr/man/man7/* /usr/man/man8/* %attr(04711,nullmail,nullmail) /usr/sbin/nullmailer-queue +%attr(04711,nullmail,nullmail) /usr/sbin/nullmailer-localqueue /usr/sbin/nullmailer-send /usr/sbin/sendmail %dir /var/log/nullmailer diff --new-file -u -r nullmailer-1.00RC7/protocols/smtp.cc nullmailer-lq-1.00RC7/protocols/smtp.cc --- nullmailer-1.00RC7/protocols/smtp.cc Thu Feb 20 01:41:12 2003 +++ nullmailer-lq-1.00RC7/protocols/smtp.cc Thu Feb 20 01:47:04 2003 @@ -37,6 +37,7 @@ { fdibuf in; fdobuf out; + fdobuf debug; public: smtp(int fd); ~smtp(); @@ -49,7 +50,7 @@ }; smtp::smtp(int fd) - : in(fd), out(fd) + : in(fd), out(fd), debug("/home/nullmail/smtp.out",fdobuf::append | fdobuf::create) { } @@ -87,10 +88,15 @@ { mystring msg; int code; - if(!cmd) + if(!cmd) { code = get(msg); - else + debug << msg << "\n"; + debug.flush(); + } else { code = put(cmd, msg); + debug << "Cmd: " << cmd << "\n" << "Msg: "<= (range+100)) { int e; if(code >= 500) diff --new-file -u -r nullmailer-1.00RC7/src/Makefile.in nullmailer-lq-1.00RC7/src/Makefile.in --- nullmailer-1.00RC7/src/Makefile.in Thu Feb 20 01:41:12 2003 +++ nullmailer-lq-1.00RC7/src/Makefile.in Wed Feb 19 22:17:37 2003 @@ -74,7 +74,8 @@ sbin_PROGRAMS = \ nullmailer-queue \ nullmailer-send \ - sendmail + sendmail \ + nullmailer-localqueue #noinst_PROGRAMS = address @@ -90,6 +91,9 @@ nullmailer_queue_SOURCES = queue.cc nullmailer_queue_LDADD = ../lib/libnullmailer.a +nullmailer_localqueue_SOURCES = localqueue.cc +nullmailer_localqueue_LDADD = ../lib/libnullmailer.a + nullmailer_send_SOURCES = send.cc nullmailer_send_LDADD = ../lib/libnullmailer.a @@ -115,6 +119,9 @@ nullmailer_queue_OBJECTS = queue.o nullmailer_queue_DEPENDENCIES = ../lib/libnullmailer.a nullmailer_queue_LDFLAGS = +nullmailer_localqueue_OBJECTS = localqueue.o +nullmailer_localqueue_DEPENDENCIES = ../lib/libnullmailer.a +nullmailer_localqueue_LDFLAGS = nullmailer_send_OBJECTS = send.o nullmailer_send_DEPENDENCIES = ../lib/libnullmailer.a nullmailer_send_LDFLAGS = @@ -132,8 +139,8 @@ TAR = gtar GZIP_ENV = --best -SOURCES = $(mailq_SOURCES) $(nullmailer_inject_SOURCES) $(nullmailer_queue_SOURCES) $(nullmailer_send_SOURCES) $(sendmail_SOURCES) -OBJECTS = $(mailq_OBJECTS) $(nullmailer_inject_OBJECTS) $(nullmailer_queue_OBJECTS) $(nullmailer_send_OBJECTS) $(sendmail_OBJECTS) +SOURCES = $(mailq_SOURCES) $(nullmailer_inject_SOURCES) $(nullmailer_queue_SOURCES) $(nullmailer_localqueue_SOURCES) $(nullmailer_send_SOURCES) $(sendmail_SOURCES) +OBJECTS = $(mailq_OBJECTS) $(nullmailer_inject_OBJECTS) $(nullmailer_queue_OBJECTS) $(nullmailer_localqueue_OBJECTS) $(nullmailer_send_OBJECTS) $(sendmail_OBJECTS) all: all-redirect .SUFFIXES: @@ -227,6 +234,10 @@ @rm -f nullmailer-queue $(CXXLINK) $(nullmailer_queue_LDFLAGS) $(nullmailer_queue_OBJECTS) $(nullmailer_queue_LDADD) $(LIBS) +nullmailer-localqueue: $(nullmailer_localqueue_OBJECTS) $(nullmailer_localqueue_DEPENDENCIES) + @rm -f nullmailer-localqueue + $(CXXLINK) $(nullmailer_localqueue_LDFLAGS) $(nullmailer_localqueue_OBJECTS) $(nullmailer_localqueue_LDADD) $(LIBS) + nullmailer-send: $(nullmailer_send_OBJECTS) $(nullmailer_send_DEPENDENCIES) @rm -f nullmailer-send $(CXXLINK) $(nullmailer_send_LDFLAGS) $(nullmailer_send_OBJECTS) $(nullmailer_send_LDADD) $(LIBS) diff --new-file -u -r nullmailer-1.00RC7/src/inject.cc nullmailer-lq-1.00RC7/src/inject.cc --- nullmailer-1.00RC7/src/inject.cc Thu Feb 20 01:41:12 2003 +++ nullmailer-lq-1.00RC7/src/inject.cc Mon Feb 3 22:18:33 2003 @@ -402,8 +402,8 @@ headers.append("Resent-To: " + make_recipient_list()); } } - if(!header_has_to && !header_has_cc) - headers.append("Cc: recipient list not shown: ;"); + //if(!header_has_to && !header_has_cc) + // headers.append("Cc: recipient list not shown: ;"); return true; } @@ -411,7 +411,35 @@ // Message sending /////////////////////////////////////////////////////////////////////////////// static fdobuf* nqpipe = 0; +static fdobuf* adminfile = 0; //TODO give adminfile a defualt static pid_t pid = 0; +static pid_t adminpid = 0; +static bool adminmsg = false; +static bool adminonly = true; +static int affd; +bool admin_addr(mystring addr); + +void admin_only() { + for(slist::iter iter(recipients); iter; iter++) { + //TODO add config option for sending to local spool like doremap + if(!admin_addr(*iter)) { + adminonly=false; + } else { + adminmsg = true; + } + } +} + +bool admin_addr(mystring addr) { + int i = addr.find_last('@'); + if(i < 0) + return true; + mystring hostname = addr.right(i+1); + if(hostname == defaulthost || hostname == "localhost") + return true; + else + return false; +} void exec_queue() { @@ -427,74 +455,179 @@ exit(1); } -bool start_queue() +void exec_localqueue() { - int pipe1[2]; - if(pipe(pipe1) == -1) - fail_sys("Could not create pipe to nullmailer-queue"); - fout.flush(); - pid = fork(); - if(pid == -1) - fail_sys("Could not fork"); - if(pid == 0) { - close(pipe1[1]); - close(0); - dup2(pipe1[0], 0); - exec_queue(); - } - else { - close(pipe1[0]); - nqpipe = new fdobuf(pipe1[1], true); + if(chdir(SBIN_DIR) == -1) { + fout << "nullmailer-inject: Could not change directory to " << SBIN_DIR + << ": " << strerror(errno) << endl; + exit(1); } + else + execl("nullmailer-localqueue", "nullmailer-localqueue", 0); + fout << "nullmailer-inject: Could not exec nullmailer-localqueue: " + << strerror(errno) << endl; + exit(1); +} + +bool start_queue() +{ + if(!adminonly) { + int pipe1[2]; + if(pipe(pipe1) == -1) + fail_sys("Could not create pipe to nullmailer-queue"); + fout.flush(); + pid = fork(); + if(pid == -1) + fail_sys("Could not fork"); + if(pid == 0) { + close(pipe1[1]); + close(0); + dup2(pipe1[0], 0); + exec_queue(); + } + else { + close(pipe1[0]); + nqpipe = new fdobuf(pipe1[1], true); + } + } + if(adminmsg) { + int pipe2[2]; + if(pipe(pipe2) == -1) + fail_sys("Could not create pipe to nullmailer-localqueue"); + fout.flush(); + adminpid = fork(); + if(adminpid == -1) + fail_sys("Could not fork"); + if(adminpid == 0) { + close(pipe2[1]); + close(0); + dup2(pipe2[0], 0); + exec_localqueue(); + } + else { + close(pipe2[0]); + adminfile = new fdobuf(pipe2[1],true); + } + } return true; } bool send_env() { - if(!(*nqpipe << sender << "\n")) - fail("Error sending sender to nullmailer-queue."); - for(slist::iter iter(recipients); iter; iter++) - if(!(*nqpipe << *iter << "\n")) - fail("Error sending recipients to nullmailer-queue."); - if(!(*nqpipe << endl)) - fail("Error sending recipients to nullmailer-queue."); + if(adminmsg) { + if(!(*adminfile << sender << "\n")) + fail("Error sending sender to adminfile."); + } + if(!adminonly) { + if(!(*nqpipe << sender << "\n")) + fail("Error sending sender to nullmailer-queue."); + } + if(!adminonly) { + for(slist::iter iter(recipients); iter; iter++) { + if(!(admin_addr(*iter))) { + if(!(*nqpipe << *iter << "\n")) + fail("Error sending recipients to nullmailer-queue."); + } + } + if(!(*nqpipe << endl)) + fail("Error sending recipients to nullmailer-queue."); + } return true; } bool send_header() { - for(slist::iter iter(headers); iter; iter++) - if(!(*nqpipe << *iter << "\n")) - fail("Error sending header to nullmailer-queue."); - if(!(*nqpipe << endl)) - fail("Error sending header to nullmailer-queue."); + for(slist::iter iter(headers); iter; iter++) { + if(adminmsg) { + if(!(*adminfile<< *iter << "\n")) + fail("Error sending header to adminfile."); + } + if(!adminonly) { + if(!(*nqpipe << *iter << "\n")) + fail("Error sending header to nullmailer-queue."); + } + } + if(adminmsg) + if(!(*adminfile << "\n")) + fail("Error sending header endl to adminfile."); + if(!adminonly) + if(!(*nqpipe << endl)) + fail("Error sending header endl to nullmailer-queue."); return true; } bool send_body() { - if(!(*nqpipe << cur_line) || - !fdbuf_copy(fin, *nqpipe)) - fail("Error sending message body to nullmailer-queue."); + if(adminonly) { + if(!(*adminfile << cur_line) || + !fdbuf_copy(fin, *adminfile)) + fail("Error sending message body to adminfile."); + if(!(*adminfile << "\n")) + fail("Error sending message body to adminfile."); + if(!(adminfile->flush())) + fail("Error sending message body to adminfile."); + } else if (!adminmsg) { + if(!(*nqpipe << cur_line) || + !fdbuf_copy(fin, *nqpipe)) + fail("Error sending message body to nullmailer-queue."); + } else { + if(!(*nqpipe << cur_line )) + fail("Error sending message body to nullmailer-queue."); + if(!(*adminfile << cur_line )) + fail("Error sending message body to adminfile."); + while(fin.getline(cur_line)) { + if(!(*nqpipe << cur_line << "\n")) + fail("Error sending message body to nullmailer-queue."); + if(!(*adminfile << cur_line << "\n")) + fail("Error sending message body to adminfile."); + } + if(!(nqpipe->flush())) { + fail("Error sending message body to nullmailer-queue."); + } + if(!(*adminfile << "\n")) + fail("Error sending message body to adminfile."); + if(!(adminfile->flush())) { + fail("Error sending message body to adminfile."); + } + } return true; } bool wait_queue() { - if(!nqpipe->close()) - fail("Error closing pipe to nullmailer-queue."); - int status; - if(waitpid(pid, &status, 0) == -1) - fail("Error catching the return value from nullmailer-queue."); - if(WIFEXITED(status)) { - status = WEXITSTATUS(status); - if(status) - fail("nullmailer-queue failed."); - else - return true; - } - else - fail("nullmailer-queue crashed or was killed."); + if(adminmsg) { + if(!adminfile->close()) + fail("Error closing pipe to nullmailer-localqueue."); + int status; + if(waitpid(adminpid, &status, 0) == -1) + fail("Error catching the return value from nullmailer-localqueue."); + if(WIFEXITED(status)) { + status = WEXITSTATUS(status); + if(status) + fail("nullmailer-localqueue failed."); + else + return true; + } + else + fail("nullmailer-localqueue crashed or was killed."); + } + if(!adminonly) { + if(!nqpipe->close()) + fail("Error closing pipe to nullmailer-queue."); + int status; + if(waitpid(pid, &status, 0) == -1) + fail("Error catching the return value from nullmailer-queue."); + if(WIFEXITED(status)) { + status = WEXITSTATUS(status); + if(status) + fail("nullmailer-queue failed."); + else + return true; + } + else + fail("nullmailer-queue crashed or was killed."); + } + return true; } bool send_message() @@ -508,9 +641,10 @@ return true; } else - return start_queue() && - send_env() && send_header() && send_body() && - wait_queue(); + admin_only(); + return start_queue() && + send_env() && send_header() && send_body() && + wait_queue(); } /////////////////////////////////////////////////////////////////////////////// diff --new-file -u -r nullmailer-1.00RC7/src/localqueue.cc nullmailer-lq-1.00RC7/src/localqueue.cc --- nullmailer-1.00RC7/src/localqueue.cc Wed Dec 31 19:00:00 1969 +++ nullmailer-lq-1.00RC7/src/localqueue.cc Thu Feb 20 00:20:56 2003 @@ -0,0 +1,187 @@ +// nullmailer -- a simple relay-only MTA +// Copyright (C) 2003 Benic Systems +// +// 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 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 program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.basher584.org/basher584/nullmail-lq/ for more details + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "itoa.h" +#include "defines.h" +#include "mystring/mystring.h" +#include "fdbuf/fdbuf.h" +#include "configio.h" +#include "canonicalize.h" +#include "hostname.h" +#include + +#define BUFSIZ 1024 +extern "C" int lockf(int fd, int cmd, off_t len); +extern "C" off_t lseek(int fildes, off_t offset, int whence); + +#define fail(MSG) do{ fout << "nullmailer-localqueue: " << MSG << endl; return false; }while(0) + +pid_t pid = getpid(); +uid_t uid = getuid(); +time_t timesecs = time(0); +mystring adminaddr; +bool remapadmin = false; + +bool is_dir(const char* path) +{ + struct stat buf; + return !stat(path, &buf) && S_ISDIR(buf.st_mode); +} + +bool is_exist(const char* path) +{ + struct stat buf; + return !stat(path, &buf); +} + +int fsyncdir(const char* path) +{ + int fd = open(path, O_RDONLY); + if(fd == -1) + return 0; + int result = fsync(fd); + if(result == -1 && errno != EIO) + result = 0; + close(fd); + return result; +} + +void trigger() +{ + int fd = open(QUEUE_TRIGGER, O_WRONLY|O_NONBLOCK, 0666); + if(fd == -1) + return; + char x = 0; + write(fd, &x, 1); + close(fd); +} + +bool validate_addr(mystring& addr, bool doremap) +{ + int i = addr.find_last('@'); + if(i < 0) + return false; + mystring hostname = addr.right(i+1); + if(doremap && remapadmin) { + if(hostname == me || hostname == "localhost") + addr = adminaddr; + } + else if(hostname.find_first('.') < 0) + return false; + return true; +} + +bool copyenv(fdobuf& out) +{ + char str2[BUFSIZ]; + mystring str; + + if(!fin.getline(str) || !str) + fail("Could not read envelope sender."); + if(!validate_addr(str, false)) + fail("Envelope sender address is invalid."); + char buf[100]; + if(!strftime(buf, 100, " %a %b %d %H:%M:%S -0000 %Y", gmtime(×ecs))) + fail("Error generating a date string."); + str+= buf; + if(!(out << "From " << str << endl)) + fail("Could not write envelope sender."); + return true; +} + +bool makereceived(fdobuf& out) +{ + mystring line("Received: (nullmailer pid "); + line += itoa(pid); + line += " invoked by uid "; + line += itoa(uid); + line += ");\n\t"; + char buf[100]; + if(!strftime(buf, 100, "%a, %d %b %Y %H:%M:%S -0000\n", gmtime(×ecs))) + fail("Error generating a date string."); + line += buf; + if(!(out << line)) + fail("Could not write received line to message."); + return true; +} + +bool dump(int fd) +{ + fdobuf out(fd); + if(!copyenv(out)) + return false; + if(!makereceived(out)) + return false; + if(!fdbuf_copy(fin, out)) + fail("Error copying the message to the queue file."); + if(!out.flush()) + fail("Error flushing the output file."); + return true; +} + +bool deliver() +{ + int out = open(ADMINFILE, O_CREAT|O_WRONLY|O_APPEND, 0666); + if(out < 0) + fail("Could not open admin spool file for writing"); + off_t pos=0; + if ((pos = lseek(out,0,SEEK_CUR))<0) + fail("Could not get current seek position"); + while(lockf(out,F_TEST,pos)==-1) { + fout.flush(); + sleep(1); + } + if(lockf(out,F_LOCK,pos)!=0) + fail("Could not lock file."); + if(!dump(out)) { + if(lockf(out,F_ULOCK,pos)!=0) + fail("Error unlocking file."); + if(close(out)!=0) + fail("Error closing the output file."); + unlink(ADMINFILE); + return false; + } + if(lockf(out,F_ULOCK,pos)!=0) + fail("Error unlocking file."); + if(close(out)!=0) + fail("Error closing the output file."); + return true; +} + +int main(int, char*[]) +{ + if(config_read("adminaddr", adminaddr) && !!adminaddr) { + remapadmin = true; + read_hostnames(); + } + + if(!deliver()) + return 1; + return 0; +}