From f1bd9f55fe77ddbdac0970f8bb67736cabaaf9af Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sat, 12 Dec 2009 13:58:54 -0500 Subject: [PATCH] Imported stock netqmail-1.06 sources. --- BIN.Makefile | 24 + BIN.README | 19 + BLURB | 43 + BLURB2 | 26 + BLURB3 | 93 + BLURB4 | 44 + CHANGES | 1484 ++++++++++++ COPYRIGHT | 33 + FAQ | 709 ++++++ FILES | 434 ++++ INSTALL | 1 + INSTALL.alias | 40 + INSTALL.ctl | 38 + INSTALL.ids | 72 + INSTALL.maildir | 59 + INSTALL.mbox | 53 + INSTALL.vsm | 50 + INTERNALS | 156 ++ Makefile | 2141 +++++++++++++++++ PIC.local2alias | 37 + PIC.local2ext | 41 + PIC.local2local | 40 + PIC.local2rem | 38 + PIC.local2virt | 44 + PIC.nullclient | 38 + PIC.relaybad | 8 + PIC.relaygood | 33 + PIC.rem2local | 36 + README | 295 +++ REMOVE.binmail | 16 + REMOVE.sendmail | 28 + SECURITY | 131 + SENDMAIL | 76 + SYSDEPS | 17 + TARGETS | 387 +++ TEST.deliver | 82 + TEST.receive | 41 + THANKS | 338 +++ THOUGHTS | 418 ++++ TODO | 23 + UPGRADE | 66 + VERSION | 1 + addresses.5 | 260 ++ alloc.3 | 62 + alloc.c | 32 + alloc.h | 8 + alloc_re.c | 17 + auto-gid.c | 51 + auto-int.c | 40 + auto-int8.c | 40 + auto-str.c | 44 + auto-uid.c | 51 + auto_break.h | 6 + auto_patrn.h | 6 + auto_qmail.h | 6 + auto_spawn.h | 6 + auto_split.h | 6 + auto_uids.h | 16 + auto_usera.h | 6 + binm1+df.sh | 11 + binm1.sh | 10 + binm2+df.sh | 11 + binm2.sh | 10 + binm3+df.sh | 11 + binm3.sh | 10 + bouncesaying.1 | 71 + bouncesaying.c | 41 + byte.h | 13 + byte_chr.c | 20 + byte_copy.c | 14 + byte_cr.c | 16 + byte_diff.c | 16 + byte_rchr.c | 23 + byte_zero.c | 13 + case.3 | 100 + case.h | 13 + case_diffb.c | 21 + case_diffs.c | 19 + case_lowerb.c | 14 + case_lowers.c | 12 + case_starts.c | 18 + cdb.3 | 62 + cdb.h | 12 + cdb_hash.c | 16 + cdb_seek.c | 94 + cdb_unpack.c | 12 + cdbmake.h | 35 + cdbmake_add.c | 118 + cdbmake_hash.c | 10 + cdbmake_pack.c | 11 + cdbmss.c | 65 + cdbmss.h | 16 + chkshsgr.c | 9 + chkspawn.c | 48 + coe.3 | 25 + coe.c | 8 + coe.h | 6 + commands.c | 40 + commands.h | 12 + condredirect.1 | 63 + condredirect.c | 85 + conf-break | 9 + conf-cc | 3 + conf-groups | 6 + conf-ld | 3 + conf-patrn | 6 + conf-qmail | 11 + conf-spawn | 5 + conf-split | 3 + conf-users | 15 + config-fast.sh | 30 + config.sh | 64 + constmap.c | 114 + constmap.h | 20 + control.c | 130 + control.h | 10 + date822fmt.c | 29 + date822fmt.h | 7 + datemail.sh | 1 + datetime.3 | 73 + datetime.c | 55 + datetime.h | 20 + datetime_un.c | 35 + direntry.3 | 36 + direntry.h1 | 8 + direntry.h2 | 8 + dns.c | 398 +++ dns.h | 14 + dnscname.c | 25 + dnsdoe.c | 16 + dnsdoe.h | 6 + dnsfq.c | 32 + dnsip.c | 34 + dnsmxip.c | 40 + dnsptr.c | 27 + dot-qmail.9 | 394 +++ elq.sh | 1 + env.3 | 31 + env.c | 113 + env.h | 17 + envelopes.5 | 231 ++ envread.c | 30 + error.3 | 45 + error.c | 95 + error.h | 23 + error_str.3 | 19 + error_str.c | 276 +++ error_temp.3 | 27 + error_temp.c | 80 + except.1 | 33 + except.c | 37 + exit.h | 6 + extra.h | 7 + fd.h | 7 + fd_copy.3 | 44 + fd_copy.c | 13 + fd_move.3 | 41 + fd_move.c | 11 + fifo.c | 10 + fifo.h | 6 + fifo_make.3 | 24 + find-systype.sh | 144 ++ fmt.h | 25 + fmt_str.c | 12 + fmt_strn.c | 12 + fmt_uint.c | 6 + fmt_uint0.c | 10 + fmt_ulong.c | 13 + fmtqfn.c | 24 + fmtqfn.h | 8 + forgeries.7 | 104 + fork.h1 | 7 + fork.h2 | 7 + forward.1 | 24 + forward.c | 60 + gen_alloc.h | 7 + gen_allocdefs.h | 34 + getln.3 | 51 + getln.c | 20 + getln.h | 7 + getln2.3 | 64 + getln2.c | 31 + gfrom.c | 10 + gfrom.h | 6 + headerbody.c | 87 + headerbody.h | 6 + hfield.c | 125 + hfield.h | 38 + hier.c | 252 ++ home+df.sh | 9 + home.sh | 7 + hostname.c | 17 + idedit.c | 147 ++ install-big.c | 285 +++ install.c | 164 ++ instcheck.c | 108 + ip.c | 53 + ip.h | 11 + ipalloc.c | 7 + ipalloc.h | 14 + ipme.c | 100 + ipme.h | 12 + ipmeprint.c | 24 + lock.h | 8 + lock_ex.c | 11 + lock_exnb.c | 11 + lock_un.c | 11 + maildir.5 | 239 ++ maildir.c | 108 + maildir.h | 12 + maildir2mbox.1 | 53 + maildir2mbox.c | 162 ++ maildirmake.1 | 15 + maildirmake.c | 24 + maildirwatch.1 | 23 + maildirwatch.c | 125 + mailsubj.1 | 38 + mailsubj.sh | 7 + make-compile.sh | 1 + make-load.sh | 2 + make-makelib.sh | 16 + mbox.5 | 235 ++ myctime.c | 37 + myctime.h | 6 + ndelay.c | 13 + ndelay.h | 7 + ndelay_off.c | 13 + newfield.c | 68 + newfield.h | 12 + now.3 | 14 + now.c | 8 + now.h | 8 + old-patches/README | 3 + old-patches/netqmail-1.04.patch | 414 ++++ old-patches/qmail-isoc.patch | 362 +++ open.h | 10 + open_append.c | 6 + open_excl.c | 6 + open_read.c | 6 + open_trunc.c | 6 + open_write.c | 6 + other-patches/README | 51 + other-patches/checkpassword-0.90.errno.patch | 12 + other-patches/daemontools-0.76.errno.patch | 12 + other-patches/djbdns-1.05.errno.patch | 12 + other-patches/dot-forward-0.71.errno.patch | 12 + other-patches/fastforward-0.51.errno.patch | 22 + other-patches/mess822-0.58.errno.patch | 33 + other-patches/qmailanalog-0.70.errno.patch | 87 + other-patches/serialmail-0.75.errno.patch | 23 + other-patches/ucspi-tcp-0.88.a_record.patch | 64 + other-patches/ucspi-tcp-0.88.errno.patch | 12 + .../ucspi-tcp-0.88.nodefaultrbl.patch | 28 + pinq.sh | 1 + predate.c | 116 + preline.1 | 57 + preline.c | 90 + prioq.c | 58 + prioq.h | 15 + proc+df.sh | 9 + proc.sh | 7 + prot.c | 21 + prot.h | 7 + qail.sh | 1 + qbiff.1 | 31 + qbiff.c | 113 + qlx.h | 18 + qmail-clean.8 | 13 + qmail-clean.c | 98 + qmail-command.8 | 149 ++ qmail-control.9 | 78 + qmail-getpw.9 | 114 + qmail-getpw.c | 88 + qmail-header.5 | 332 +++ qmail-inject.8 | 309 +++ qmail-inject.c | 773 ++++++ qmail-limits.9 | 30 + qmail-local.8 | 99 + qmail-local.c | 698 ++++++ qmail-log.5 | 266 ++ qmail-lspawn.8 | 46 + qmail-lspawn.c | 234 ++ qmail-newmrh.9 | 41 + qmail-newmrh.c | 70 + qmail-newu.9 | 43 + qmail-newu.c | 137 ++ qmail-pop3d.8 | 43 + qmail-pop3d.c | 305 +++ qmail-popup.8 | 65 + qmail-popup.c | 183 ++ qmail-pw2u.9 | 241 ++ qmail-pw2u.c | 312 +++ qmail-qmqpc.8 | 29 + qmail-qmqpc.c | 159 ++ qmail-qmqpd.8 | 25 + qmail-qmqpd.c | 174 ++ qmail-qmtpd.8 | 32 + qmail-qmtpd.c | 268 +++ qmail-qread.8 | 24 + qmail-qread.c | 175 ++ qmail-qstat.8 | 18 + qmail-qstat.sh | 7 + qmail-queue.8 | 161 ++ qmail-queue.c | 254 ++ qmail-remote.8 | 205 ++ qmail-remote.c | 427 ++++ qmail-rspawn.8 | 21 + qmail-rspawn.c | 103 + qmail-send.9 | 246 ++ qmail-send.c | 1612 +++++++++++++ qmail-showctl.8 | 12 + qmail-showctl.c | 306 +++ qmail-smtpd.8 | 179 ++ qmail-smtpd.c | 421 ++++ qmail-start.9 | 94 + qmail-start.c | 120 + qmail-tcpok.8 | 24 + qmail-tcpok.c | 35 + qmail-tcpto.8 | 30 + qmail-tcpto.c | 85 + qmail-upq.sh | 14 + qmail-users.9 | 113 + qmail.7 | 68 + qmail.c | 136 ++ qmail.h | 24 + qreceipt.1 | 33 + qreceipt.c | 131 + qsmhook.c | 137 ++ qsutil.c | 46 + qsutil.h | 12 + quote.c | 83 + quote.h | 8 + rcpthosts.c | 60 + rcpthosts.h | 7 + readsubdir.c | 49 + readsubdir.h | 20 + readwrite.h | 7 + received.c | 71 + received.h | 6 + remoteinfo.c | 77 + remoteinfo.h | 6 + scan.h | 27 + scan_8long.c | 11 + scan_ulong.c | 11 + seek.h | 15 + seek_cur.c | 7 + seek_end.c | 7 + seek_set.c | 7 + seek_trunc.c | 5 + select.h1 | 8 + select.h2 | 9 + sendmail.c | 162 ++ sgetopt.3 | 28 + sgetopt.c | 54 + sgetopt.h | 21 + sig.h | 43 + sig_alarm.c | 7 + sig_block.c | 40 + sig_bug.c | 17 + sig_catch.c | 18 + sig_child.c | 7 + sig_hup.c | 7 + sig_misc.c | 17 + sig_pause.c | 14 + sig_pipe.c | 5 + sig_term.c | 7 + slurpclose.c | 19 + slurpclose.h | 6 + spawn.c | 260 ++ splogger.8 | 60 + splogger.c | 72 + str.h | 14 + str_chr.c | 19 + str_cpy.c | 16 + str_diff.c | 17 + str_diffn.c | 18 + str_len.c | 15 + str_rchr.c | 22 + str_start.c | 15 + stralloc.3 | 160 ++ stralloc.h | 21 + stralloc_arts.c | 12 + stralloc_cat.c | 9 + stralloc_catb.c | 15 + stralloc_cats.c | 10 + stralloc_copy.c | 9 + stralloc_eady.c | 6 + stralloc_opyb.c | 14 + stralloc_opys.c | 10 + stralloc_pend.c | 5 + strerr.h | 80 + strerr_die.c | 37 + strerr_sys.c | 12 + subfd.h | 15 + subfderr.c | 7 + subfdin.c | 13 + subfdins.c | 13 + subfdout.c | 7 + subfdouts.c | 7 + subgetopt.3 | 357 +++ subgetopt.c | 79 + subgetopt.h | 24 + substdi.c | 91 + substdio.c | 15 + substdio.h | 47 + substdio_copy.c | 18 + substdo.c | 108 + tcp-env.1 | 67 + tcp-env.c | 129 + tcp-environ.5 | 62 + tcpto.c | 165 ++ tcpto.h | 8 + tcpto_clean.c | 20 + timeoutconn.c | 59 + timeoutconn.h | 6 + timeoutread.c | 22 + timeoutread.h | 6 + timeoutwrite.c | 22 + timeoutwrite.h | 6 + token822.c | 513 ++++ token822.h | 37 + trigger.c | 41 + trigger.h | 8 + triggerpull.c | 16 + triggerpull.h | 6 + trycpp.c | 7 + trydrent.c | 8 + tryflock.c | 8 + trylsock.c | 4 + trymkffo.c | 7 + trynpbg1.c | 26 + tryrsolv.c | 4 + trysalen.c | 11 + trysgact.c | 10 + trysgprm.c | 10 + tryshsgr.c | 14 + trysysel.c | 8 + trysyslog.c | 9 + tryulong32.c | 11 + tryvfork.c | 4 + trywaitp.c | 7 + uint32.h1 | 6 + uint32.h2 | 6 + wait.3 | 93 + wait.h | 14 + wait_nohang.c | 12 + wait_pid.c | 39 + warn-auto.sh | 2 + warn-shsgr | 3 + 449 files changed, 32673 insertions(+) create mode 100644 BIN.Makefile create mode 100644 BIN.README create mode 100644 BLURB create mode 100644 BLURB2 create mode 100644 BLURB3 create mode 100644 BLURB4 create mode 100644 CHANGES create mode 100644 COPYRIGHT create mode 100644 FAQ create mode 100644 FILES create mode 100644 INSTALL create mode 100644 INSTALL.alias create mode 100644 INSTALL.ctl create mode 100644 INSTALL.ids create mode 100644 INSTALL.maildir create mode 100644 INSTALL.mbox create mode 100644 INSTALL.vsm create mode 100644 INTERNALS create mode 100644 Makefile create mode 100644 PIC.local2alias create mode 100644 PIC.local2ext create mode 100644 PIC.local2local create mode 100644 PIC.local2rem create mode 100644 PIC.local2virt create mode 100644 PIC.nullclient create mode 100644 PIC.relaybad create mode 100644 PIC.relaygood create mode 100644 PIC.rem2local create mode 100644 README create mode 100644 REMOVE.binmail create mode 100644 REMOVE.sendmail create mode 100644 SECURITY create mode 100644 SENDMAIL create mode 100644 SYSDEPS create mode 100644 TARGETS create mode 100644 TEST.deliver create mode 100644 TEST.receive create mode 100644 THANKS create mode 100644 THOUGHTS create mode 100644 TODO create mode 100644 UPGRADE create mode 100644 VERSION create mode 100644 addresses.5 create mode 100644 alloc.3 create mode 100644 alloc.c create mode 100644 alloc.h create mode 100644 alloc_re.c create mode 100644 auto-gid.c create mode 100644 auto-int.c create mode 100644 auto-int8.c create mode 100644 auto-str.c create mode 100644 auto-uid.c create mode 100644 auto_break.h create mode 100644 auto_patrn.h create mode 100644 auto_qmail.h create mode 100644 auto_spawn.h create mode 100644 auto_split.h create mode 100644 auto_uids.h create mode 100644 auto_usera.h create mode 100644 binm1+df.sh create mode 100644 binm1.sh create mode 100644 binm2+df.sh create mode 100644 binm2.sh create mode 100644 binm3+df.sh create mode 100644 binm3.sh create mode 100644 bouncesaying.1 create mode 100644 bouncesaying.c create mode 100644 byte.h create mode 100644 byte_chr.c create mode 100644 byte_copy.c create mode 100644 byte_cr.c create mode 100644 byte_diff.c create mode 100644 byte_rchr.c create mode 100644 byte_zero.c create mode 100644 case.3 create mode 100644 case.h create mode 100644 case_diffb.c create mode 100644 case_diffs.c create mode 100644 case_lowerb.c create mode 100644 case_lowers.c create mode 100644 case_starts.c create mode 100644 cdb.3 create mode 100644 cdb.h create mode 100644 cdb_hash.c create mode 100644 cdb_seek.c create mode 100644 cdb_unpack.c create mode 100644 cdbmake.h create mode 100644 cdbmake_add.c create mode 100644 cdbmake_hash.c create mode 100644 cdbmake_pack.c create mode 100644 cdbmss.c create mode 100644 cdbmss.h create mode 100644 chkshsgr.c create mode 100644 chkspawn.c create mode 100644 coe.3 create mode 100644 coe.c create mode 100644 coe.h create mode 100644 commands.c create mode 100644 commands.h create mode 100644 condredirect.1 create mode 100644 condredirect.c create mode 100644 conf-break create mode 100644 conf-cc create mode 100644 conf-groups create mode 100644 conf-ld create mode 100644 conf-patrn create mode 100644 conf-qmail create mode 100644 conf-spawn create mode 100644 conf-split create mode 100644 conf-users create mode 100644 config-fast.sh create mode 100644 config.sh create mode 100644 constmap.c create mode 100644 constmap.h create mode 100644 control.c create mode 100644 control.h create mode 100644 date822fmt.c create mode 100644 date822fmt.h create mode 100644 datemail.sh create mode 100644 datetime.3 create mode 100644 datetime.c create mode 100644 datetime.h create mode 100644 datetime_un.c create mode 100644 direntry.3 create mode 100644 direntry.h1 create mode 100644 direntry.h2 create mode 100644 dns.c create mode 100644 dns.h create mode 100644 dnscname.c create mode 100644 dnsdoe.c create mode 100644 dnsdoe.h create mode 100644 dnsfq.c create mode 100644 dnsip.c create mode 100644 dnsmxip.c create mode 100644 dnsptr.c create mode 100644 dot-qmail.9 create mode 100644 elq.sh create mode 100644 env.3 create mode 100644 env.c create mode 100644 env.h create mode 100644 envelopes.5 create mode 100644 envread.c create mode 100644 error.3 create mode 100644 error.c create mode 100644 error.h create mode 100644 error_str.3 create mode 100644 error_str.c create mode 100644 error_temp.3 create mode 100644 error_temp.c create mode 100644 except.1 create mode 100644 except.c create mode 100644 exit.h create mode 100644 extra.h create mode 100644 fd.h create mode 100644 fd_copy.3 create mode 100644 fd_copy.c create mode 100644 fd_move.3 create mode 100644 fd_move.c create mode 100644 fifo.c create mode 100644 fifo.h create mode 100644 fifo_make.3 create mode 100644 find-systype.sh create mode 100644 fmt.h create mode 100644 fmt_str.c create mode 100644 fmt_strn.c create mode 100644 fmt_uint.c create mode 100644 fmt_uint0.c create mode 100644 fmt_ulong.c create mode 100644 fmtqfn.c create mode 100644 fmtqfn.h create mode 100644 forgeries.7 create mode 100644 fork.h1 create mode 100644 fork.h2 create mode 100644 forward.1 create mode 100644 forward.c create mode 100644 gen_alloc.h create mode 100644 gen_allocdefs.h create mode 100644 getln.3 create mode 100644 getln.c create mode 100644 getln.h create mode 100644 getln2.3 create mode 100644 getln2.c create mode 100644 gfrom.c create mode 100644 gfrom.h create mode 100644 headerbody.c create mode 100644 headerbody.h create mode 100644 hfield.c create mode 100644 hfield.h create mode 100644 hier.c create mode 100644 home+df.sh create mode 100644 home.sh create mode 100644 hostname.c create mode 100644 idedit.c create mode 100644 install-big.c create mode 100644 install.c create mode 100644 instcheck.c create mode 100644 ip.c create mode 100644 ip.h create mode 100644 ipalloc.c create mode 100644 ipalloc.h create mode 100644 ipme.c create mode 100644 ipme.h create mode 100644 ipmeprint.c create mode 100644 lock.h create mode 100644 lock_ex.c create mode 100644 lock_exnb.c create mode 100644 lock_un.c create mode 100644 maildir.5 create mode 100644 maildir.c create mode 100644 maildir.h create mode 100644 maildir2mbox.1 create mode 100644 maildir2mbox.c create mode 100644 maildirmake.1 create mode 100644 maildirmake.c create mode 100644 maildirwatch.1 create mode 100644 maildirwatch.c create mode 100644 mailsubj.1 create mode 100644 mailsubj.sh create mode 100644 make-compile.sh create mode 100644 make-load.sh create mode 100644 make-makelib.sh create mode 100644 mbox.5 create mode 100644 myctime.c create mode 100644 myctime.h create mode 100644 ndelay.c create mode 100644 ndelay.h create mode 100644 ndelay_off.c create mode 100644 newfield.c create mode 100644 newfield.h create mode 100644 now.3 create mode 100644 now.c create mode 100644 now.h create mode 100644 old-patches/README create mode 100644 old-patches/netqmail-1.04.patch create mode 100644 old-patches/qmail-isoc.patch create mode 100644 open.h create mode 100644 open_append.c create mode 100644 open_excl.c create mode 100644 open_read.c create mode 100644 open_trunc.c create mode 100644 open_write.c create mode 100644 other-patches/README create mode 100644 other-patches/checkpassword-0.90.errno.patch create mode 100644 other-patches/daemontools-0.76.errno.patch create mode 100644 other-patches/djbdns-1.05.errno.patch create mode 100644 other-patches/dot-forward-0.71.errno.patch create mode 100644 other-patches/fastforward-0.51.errno.patch create mode 100644 other-patches/mess822-0.58.errno.patch create mode 100644 other-patches/qmailanalog-0.70.errno.patch create mode 100644 other-patches/serialmail-0.75.errno.patch create mode 100644 other-patches/ucspi-tcp-0.88.a_record.patch create mode 100644 other-patches/ucspi-tcp-0.88.errno.patch create mode 100644 other-patches/ucspi-tcp-0.88.nodefaultrbl.patch create mode 100644 pinq.sh create mode 100644 predate.c create mode 100644 preline.1 create mode 100644 preline.c create mode 100644 prioq.c create mode 100644 prioq.h create mode 100644 proc+df.sh create mode 100644 proc.sh create mode 100644 prot.c create mode 100644 prot.h create mode 100644 qail.sh create mode 100644 qbiff.1 create mode 100644 qbiff.c create mode 100644 qlx.h create mode 100644 qmail-clean.8 create mode 100644 qmail-clean.c create mode 100644 qmail-command.8 create mode 100644 qmail-control.9 create mode 100644 qmail-getpw.9 create mode 100644 qmail-getpw.c create mode 100644 qmail-header.5 create mode 100644 qmail-inject.8 create mode 100644 qmail-inject.c create mode 100644 qmail-limits.9 create mode 100644 qmail-local.8 create mode 100644 qmail-local.c create mode 100644 qmail-log.5 create mode 100644 qmail-lspawn.8 create mode 100644 qmail-lspawn.c create mode 100644 qmail-newmrh.9 create mode 100644 qmail-newmrh.c create mode 100644 qmail-newu.9 create mode 100644 qmail-newu.c create mode 100644 qmail-pop3d.8 create mode 100644 qmail-pop3d.c create mode 100644 qmail-popup.8 create mode 100644 qmail-popup.c create mode 100644 qmail-pw2u.9 create mode 100644 qmail-pw2u.c create mode 100644 qmail-qmqpc.8 create mode 100644 qmail-qmqpc.c create mode 100644 qmail-qmqpd.8 create mode 100644 qmail-qmqpd.c create mode 100644 qmail-qmtpd.8 create mode 100644 qmail-qmtpd.c create mode 100644 qmail-qread.8 create mode 100644 qmail-qread.c create mode 100644 qmail-qstat.8 create mode 100644 qmail-qstat.sh create mode 100644 qmail-queue.8 create mode 100644 qmail-queue.c create mode 100644 qmail-remote.8 create mode 100644 qmail-remote.c create mode 100644 qmail-rspawn.8 create mode 100644 qmail-rspawn.c create mode 100644 qmail-send.9 create mode 100644 qmail-send.c create mode 100644 qmail-showctl.8 create mode 100644 qmail-showctl.c create mode 100644 qmail-smtpd.8 create mode 100644 qmail-smtpd.c create mode 100644 qmail-start.9 create mode 100644 qmail-start.c create mode 100644 qmail-tcpok.8 create mode 100644 qmail-tcpok.c create mode 100644 qmail-tcpto.8 create mode 100644 qmail-tcpto.c create mode 100644 qmail-upq.sh create mode 100644 qmail-users.9 create mode 100644 qmail.7 create mode 100644 qmail.c create mode 100644 qmail.h create mode 100644 qreceipt.1 create mode 100644 qreceipt.c create mode 100644 qsmhook.c create mode 100644 qsutil.c create mode 100644 qsutil.h create mode 100644 quote.c create mode 100644 quote.h create mode 100644 rcpthosts.c create mode 100644 rcpthosts.h create mode 100644 readsubdir.c create mode 100644 readsubdir.h create mode 100644 readwrite.h create mode 100644 received.c create mode 100644 received.h create mode 100644 remoteinfo.c create mode 100644 remoteinfo.h create mode 100644 scan.h create mode 100644 scan_8long.c create mode 100644 scan_ulong.c create mode 100644 seek.h create mode 100644 seek_cur.c create mode 100644 seek_end.c create mode 100644 seek_set.c create mode 100644 seek_trunc.c create mode 100644 select.h1 create mode 100644 select.h2 create mode 100644 sendmail.c create mode 100644 sgetopt.3 create mode 100644 sgetopt.c create mode 100644 sgetopt.h create mode 100644 sig.h create mode 100644 sig_alarm.c create mode 100644 sig_block.c create mode 100644 sig_bug.c create mode 100644 sig_catch.c create mode 100644 sig_child.c create mode 100644 sig_hup.c create mode 100644 sig_misc.c create mode 100644 sig_pause.c create mode 100644 sig_pipe.c create mode 100644 sig_term.c create mode 100644 slurpclose.c create mode 100644 slurpclose.h create mode 100644 spawn.c create mode 100644 splogger.8 create mode 100644 splogger.c create mode 100644 str.h create mode 100644 str_chr.c create mode 100644 str_cpy.c create mode 100644 str_diff.c create mode 100644 str_diffn.c create mode 100644 str_len.c create mode 100644 str_rchr.c create mode 100644 str_start.c create mode 100644 stralloc.3 create mode 100644 stralloc.h create mode 100644 stralloc_arts.c create mode 100644 stralloc_cat.c create mode 100644 stralloc_catb.c create mode 100644 stralloc_cats.c create mode 100644 stralloc_copy.c create mode 100644 stralloc_eady.c create mode 100644 stralloc_opyb.c create mode 100644 stralloc_opys.c create mode 100644 stralloc_pend.c create mode 100644 strerr.h create mode 100644 strerr_die.c create mode 100644 strerr_sys.c create mode 100644 subfd.h create mode 100644 subfderr.c create mode 100644 subfdin.c create mode 100644 subfdins.c create mode 100644 subfdout.c create mode 100644 subfdouts.c create mode 100644 subgetopt.3 create mode 100644 subgetopt.c create mode 100644 subgetopt.h create mode 100644 substdi.c create mode 100644 substdio.c create mode 100644 substdio.h create mode 100644 substdio_copy.c create mode 100644 substdo.c create mode 100644 tcp-env.1 create mode 100644 tcp-env.c create mode 100644 tcp-environ.5 create mode 100644 tcpto.c create mode 100644 tcpto.h create mode 100644 tcpto_clean.c create mode 100644 timeoutconn.c create mode 100644 timeoutconn.h create mode 100644 timeoutread.c create mode 100644 timeoutread.h create mode 100644 timeoutwrite.c create mode 100644 timeoutwrite.h create mode 100644 token822.c create mode 100644 token822.h create mode 100644 trigger.c create mode 100644 trigger.h create mode 100644 triggerpull.c create mode 100644 triggerpull.h create mode 100644 trycpp.c create mode 100644 trydrent.c create mode 100644 tryflock.c create mode 100644 trylsock.c create mode 100644 trymkffo.c create mode 100644 trynpbg1.c create mode 100644 tryrsolv.c create mode 100644 trysalen.c create mode 100644 trysgact.c create mode 100644 trysgprm.c create mode 100644 tryshsgr.c create mode 100644 trysysel.c create mode 100644 trysyslog.c create mode 100644 tryulong32.c create mode 100644 tryvfork.c create mode 100644 trywaitp.c create mode 100644 uint32.h1 create mode 100644 uint32.h2 create mode 100644 wait.3 create mode 100644 wait.h create mode 100644 wait_nohang.c create mode 100644 wait_pid.c create mode 100644 warn-auto.sh create mode 100644 warn-shsgr diff --git a/BIN.Makefile b/BIN.Makefile new file mode 100644 index 0000000..f46c537 --- /dev/null +++ b/BIN.Makefile @@ -0,0 +1,24 @@ +SHELL=/bin/sh + +# Files are edited in the installation directory, then copied. +# There are 40 arguments to idedit after the filename, +# showing the positions of each byte in the following ten ints: +# uida, uidd, uidl, uido, uidp, uidq, uidr, uids, gidq, gidn. +# Normal little-endian positions are n n+1 n+2 ... n+39 for some n. +# Normal big-endian positions are n+3 n+2 n+1 n n+7 ... n+36 for some n. + +setup: + mkdir /var/qmail + ./idedit install-big XXX + ./idedit qmail-lspawn XXX + ./idedit qmail-queue XXX + ./idedit qmail-rspawn XXX + ./idedit qmail-showctl XXX + ./idedit qmail-start XXX + ./install-big + cp /var/qmail/boot/binm1+df /var/qmail/rc + chmod 755 /var/qmail/rc + echo '|fastforward -d /etc/aliases.cdb' > /var/qmail/alias/.qmail-default + chmod 644 /var/qmail/alias/.qmail-default + hostname | grep -q '\.' + ./config-fast `hostname` diff --git a/BIN.README b/BIN.README new file mode 100644 index 0000000..6beeee1 --- /dev/null +++ b/BIN.README @@ -0,0 +1,19 @@ +Like any other piece of software (and information generally), qmail +comes with NO WARRANTY. + +Configuration: The qmail home directory is /var/qmail. (This must be a +local directory, not shared among machines. Under Linux, make sure that +all mail-handling filesystems are mounted with synchronous metadata.) +The user-ext delimiter is -. The silent concurrency limit is 120. The +queue subdirectory split is 23. + +To install: + # make setup + +To set up qmail to receive and deliver mail, follow the instructions in +/var/qmail/doc/fastforward/ALIASES, and then start at step 9 of +/var/qmail/doc/INSTALL. + +Compilation environment: Oops, the package creator forgot to edit this! +He's supposed to list his OS version, compiler version, hardware, and +name. diff --git a/BLURB b/BLURB new file mode 100644 index 0000000..7bcfb3f --- /dev/null +++ b/BLURB @@ -0,0 +1,43 @@ +qmail is a secure, reliable, efficient, simple message transfer agent. +It is meant as a replacement for the entire sendmail-binmail system on +typical Internet-connected UNIX hosts. + +Secure: Security isn't just a goal, but an absolute requirement. Mail +delivery is critical for users; it cannot be turned off, so it must be +completely secure. (This is why I started writing qmail: I was sick of +the security holes in sendmail and other MTAs.) + +Reliable: qmail's straight-paper-path philosophy guarantees that a +message, once accepted into the system, will never be lost. qmail also +supports maildir, a new, super-reliable user mailbox format. Maildirs, +unlike mbox files and mh folders, won't be corrupted if the system +crashes during delivery. Even better, not only can a user safely read +his mail over NFS, but any number of NFS clients can deliver mail to him +at the same time. + +Efficient: On a Pentium under BSD/OS, qmail can easily sustain 200000 +local messages per day---that's separate messages injected and delivered +to mailboxes in a real test! Although remote deliveries are inherently +limited by the slowness of DNS and SMTP, qmail overlaps 20 simultaneous +deliveries by default, so it zooms quickly through mailing lists. (This +is why I finished qmail: I had to get a big mailing list set up.) + +Simple: qmail is vastly smaller than any other Internet MTA. Some +reasons why: (1) Other MTAs have separate forwarding, aliasing, and +mailing list mechanisms. qmail has one simple forwarding mechanism that +lets users handle their own mailing lists. (2) Other MTAs offer a +spectrum of delivery modes, from fast+unsafe to slow+queued. qmail-send +is instantly triggered by new items in the queue, so the qmail system +has just one delivery mode: fast+queued. (3) Other MTAs include, in +effect, a specialized version of inetd that watches the load average. +qmail's design inherently limits the machine load, so qmail-smtpd can +safely run from your system's inetd. + +Replacement for sendmail: qmail supports host and user masquerading, +full host hiding, virtual domains, null clients, list-owner rewriting, +relay control, double-bounce recording, arbitrary RFC 822 address lists, +cross-host mailing list loop detection, per-recipient checkpointing, +downed host backoffs, independent message retry schedules, etc. In +short, it's up to speed on modern MTA features. qmail also includes a +drop-in ``sendmail'' wrapper so that it will be used transparently by +your current UAs. diff --git a/BLURB2 b/BLURB2 new file mode 100644 index 0000000..1a98d0e --- /dev/null +++ b/BLURB2 @@ -0,0 +1,26 @@ +Mailing list management is one of qmail's strengths. Notable features: + +* qmail lets each user handle his own mailing lists. The delivery +instructions for user-whatever go into ~user/.qmail-whatever. + +* qmail makes it really easy to set up mailing list owners. If the user +touches ~user/.qmail-whatever-owner, all bounces will come back to him. + +* qmail supports VERPs, which permit completely reliable automated +bounce handling for mailing lists of any size. + +* SPEED---qmail blasts through mailing lists an order of magnitude +faster than sendmail. For example, one message was successfully +delivered to 150 hosts around the world in just 70 seconds, with qmail's +out-of-the-box configuration. + +* qmail automatically prevents mailing list loops, even across hosts. + +* qmail allows inconceivably gigantic mailing lists. No random limits. + +* qmail handles aliasing and forwarding with the same simple mechanism. +For example, Postmaster is controlled by ~alias/.qmail-postmaster. This +means that cross-host loop detection also applies to aliases. + +* qmail supports the ezmlm mailing list manager, which easily and +automatically handles bounces, subscription requests, and archives. diff --git a/BLURB3 b/BLURB3 new file mode 100644 index 0000000..5f8af6d --- /dev/null +++ b/BLURB3 @@ -0,0 +1,93 @@ +Here are some of qmail's features. + +Setup: +* automatic adaptation to your UNIX variant---no configuration needed +* AIX, BSD/OS, FreeBSD, HP/UX, Irix, Linux, OSF/1, SunOS, Solaris, and more +* automatic per-host configuration (config, config-fast) +* quick installation---no big list of decisions to make + +Security: +* clear separation between addresses, files, and programs +* minimization of setuid code (qmail-queue) +* minimization of root code (qmail-start, qmail-lspawn) +* five-way trust partitioning---security in depth +* optional logging of one-way hashes, entire contents, etc. (QUEUE_EXTRA) + +Message construction (qmail-inject): +* RFC 822, RFC 1123 +* full support for address groups +* automatic conversion of old-style address lists to RFC 822 format +* sendmail hook for compatibility with current user agents +* header line length limited only by memory +* host masquerading (control/defaulthost) +* user masquerading ($MAILUSER, $MAILHOST) +* automatic Mail-Followup-To creation ($QMAILMFTFILE) + +SMTP service (qmail-smtpd): +* RFC 821, RFC 1123, RFC 1651, RFC 1652, RFC 1854 +* 8-bit clean +* 931/1413/ident/TAP callback (tcp-env) +* relay control---stop unauthorized relaying by outsiders (control/rcpthosts) +* no interference between relay control and forwarding +* tcpd hook---reject SMTP connections from known abusers +* automatic recognition of local IP addresses +* per-buffer timeouts +* hop counting + +Queue management (qmail-send): +* instant handling of messages added to queue +* parallelism limit (control/concurrencyremote, control/concurrencylocal) +* split queue directory---no slowdown when queue gets big +* quadratic retry schedule---old messages tried less often +* independent message retry schedules +* automatic safe queueing---no loss of mail if system crashes +* automatic per-recipient checkpointing +* automatic queue cleanups (qmail-clean) +* queue viewing (qmail-qread) +* detailed delivery statistics (qmailanalog, available separately) + +Bounces (qmail-send): +* QSBMF bounce messages---both machine-readable and human-readable +* HCMSSC support---language-independent RFC 1893 error codes +* double bounces sent to postmaster + +Routing by domain (qmail-send): +* any number of names for local host (control/locals) +* any number of virtual domains (control/virtualdomains) +* domain wildcards (control/virtualdomains) +* configurable percent hack support (control/percenthack) +* UUCP hook + +SMTP delivery (qmail-remote): +* RFC 821, RFC 974, RFC 1123 +* 8-bit clean +* automatic downed host backoffs +* artificial routing---smarthost, localnet, mailertable (control/smtproutes) +* per-buffer timeouts +* passive SMTP queue---perfect for SLIP/PPP (serialmail, available separately) + +Forwarding and mailing lists (qmail-local): +* address wildcards (.qmail-default, .qmail-foo-default, etc.) +* sendmail .forward compatibility (dot-forward, available separately) +* fast forwarding databases (fastforward, available separately) +* sendmail /etc/aliases compatibility (fastforward/newaliases) +* mailing list owners---automatically divert bounces and vacation messages +* VERPs---automatic recipient identification for mailing list bounces +* Delivered-To---automatic loop prevention, even across hosts +* automatic mailing list management (ezmlm, available separately) + +Local delivery (qmail-local): +* user-controlled address hierarchy---fred controls fred-anything +* mbox delivery +* reliable NFS delivery (maildir) +* user-controlled program delivery: procmail etc. (qmail-command) +* optional new-mail notification (qbiff) +* optional NRUDT return receipts (qreceipt) +* conditional filtering (condredirect, bouncesaying) + +POP3 service (qmail-popup, qmail-pop3d): +* RFC 1939 +* UIDL support +* TOP support +* APOP hook +* modular password checking (checkpassword, available separately) diff --git a/BLURB4 b/BLURB4 new file mode 100644 index 0000000..6c4eaac --- /dev/null +++ b/BLURB4 @@ -0,0 +1,44 @@ +qmail's modular, lightweight design and sensible queue management make +it the fastest available message transfer agent. Here's how it stacks up +against the competition in five different speed measurements. + +* Scheduling: I sent a message to 8192 ``trash'' recipients on my home +machine. All the deliveries were done in a mere 78 seconds---a rate of +over 9 million deliveries a day! Compare this to the speed advertised +for Zmailer's scheduling: 1.1 million deliveries a day on a +SparcStation-10/50. (My home machine is a 16MB Pentium-100 under BSD/OS, +with the default qmail configuration. qmail's logs were piped through +accustamp and written to disk as usual.) + +* Local mailing lists: When qmail is delivering a message to a mailbox, +it physically writes the message to disk before it announces success--- +that way, mail doesn't get lost if the power goes out. I tried sending a +message to 1024 local mailboxes on the same disk on my home machine; all +the deliveries were done in 25.5 seconds. That's more than 3.4 million +deliveries a day! Sending 1024 copies to a _single_ mailbox was just as +fast. Compare these figures to Zmailer's advertised rate for throwing +recipients away without even delivering the message---only 0.48 million +per day on the SparcStation. + +* Mailing lists with remote recipients: qmail uses the same delivery +strategy that makes LSOFT's LSMTP so fast for outgoing mailing lists--- +you choose how many parallel SMTP connections you want to run, and qmail +runs exactly that many. Of course, performance varies depending on how +far away your recipients are. The advantage of qmail over other packages +is its smallness: for example, one Linux user is running 60 simultaneous +connections, without swapping, on a machine with just 16MB of memory! + +* Separate local messages: What LSOFT doesn't tell you about LSMTP is +how many _separate_ messages it can handle in a day. Does it get bogged +down as the queue fills up? On my home machine, I disabled qmail's +deliveries and then sent 5000 separate messages to one recipient. The +messages were all safely written to the queue disk in 23 minutes, with +no slowdown as the queue filled up. After I reenabled deliveries, all +the messages were delivered to the recipient's mailbox in under 12 +minutes. End-to-end rate: more than 200000 individual messages a day! + +* Overall performance: What really matters is how well qmail performs +with your mail load. Red Hat Software found one day that their mail hub, +a 48MB Pentium running sendmail 8.7, was running out of steam at 70000 +messages a day. They shifted the load to qmail---on a _smaller_ machine, +a 16MB 486/66---and now they're doing fine. diff --git a/CHANGES b/CHANGES new file mode 100644 index 0000000..bb20670 --- /dev/null +++ b/CHANGES @@ -0,0 +1,1484 @@ +20071130 version: netqmail 1.06 +20071130 legal: qmail-1.03 is now in the public domain +20051103 doc: dot-qmail.9 updated for changed (19980613) conf-patrn default, tnx ADM +20040121 version: netqmail 1.05 +20040121 code: qmail-smtpd is protected from exceedingly long (eg 2GB) + header lines +20040121 code: qmail_lspawn, qmail-newmrh, qmail-newu, and qmail-rspawn + are protected from misbehaving on hosts where the size of an + integer is not the same as the size of a character pointer + (eg 64 bit hosts with 32 bit ints) +20031027 version: netqmail 1.04. +20031027 doc: INSTALL points to http://lifewithqmail.org/lwq.html +20031027 doc: qmail.7 identifies installation as netqmail and points to + http://qmail.org/ +20031027 doc: qmail-queue.8 adds explanation of $QMAILQUEUE +20031027 doc: qmail-log.5 adds reference to errors from $QMAILQUEUE script +20031027 doc: FAQ also points to http://cr.yp.to/qmail/faq.html and + http://qmail.org/ +20031027 code: qmail-smtpd identifies itself as netqmail +20031027 code: if $QMAILQUEUE is set, it's invoked instead of qmail-queue +20031024 code: changed errno from int to #include. +20031024 code: fixed .qmail parsing bug. +20031024 code: recognize 0.0.0.0 as a local address. +20031024 code: sendmail's -f flag now overrides environment variables. +19980615 version: qmail 1.03. +19980614 doc: eliminated BIN.setup in favor of a web page. +19980614 code: added other auto* to qmail-showctl output. +19980614 doc: added pointer to immhf.html in qmail-header.5. +19980614 doc: added note to TEST.receive about SMTP command format. +19980614 doc: added FAQ 5.6 on qmail-qmqpd. +19980614 code: removed unused variables in idedit.c. +19980613 code: changed conf-patrn to 002. +19980613 doc: moved SENDMAIL lower in INSTALL. +19980612 code: added install-big. +19980612 code: added BIN.Makefile. +19980612 doc: added BIN.README, BIN.setup. +19980612 code: switched to new install. +19980611 code: added idedit. +19980611 doc: added FAQ 1.3 on $QMAILMFTFILE. +19980611 doc: used bouncesaying in FAQ 5.5. +19980611 code: added except. +19980611 code: added bouncesaying. +19980611 code: allowed unbracketed IP addresses in dns_ipplus() and + dns_mxip(). +19980611 code: allowed spaces after colon in non-bracketed addresses in + qmail-smtpd. +19980611 doc: cleaned up UPGRADE. +19980528 bug: qmail-smtpd skips first character in rcpthosts() call. + tnx NND. impact: qmail-smtpd crashes on empty address; and it + allows relaying to ""@any.host. fix: use addr.s. +19980515 doc: expanded flock discussion in INSTALL.mbox. +19980515 doc: eliminated flock warning from INSTALL.maildir. +19980515 doc: split REMOVE.binmail out of INSTALL. +19980515 doc: split REMOVE.sendmail out of INSTALL. +19980515 doc: split TEST.deliver and TEST.receive out of INSTALL and + UPGRADE. +19980515 doc: integrated INSTALL.boot into INSTALL. +19980515 code: cleaned up final output in qmail-qmqpd.c. +19980514 doc: updated procmail notes in INSTALL.mbox. tnx JRM. +19980514 doc: changed FAQ 4.4 to point to INSTALL.mbox for procmail. + tnx JRM. +19980514 code: separated HELO and EHLO; single-line response for HELO. + tnx to various people. +19980430 version: qmail 1.02. +19980430 doc: updated SECURITY. +19980430 doc: fixed FAQ 4.9. tnx KB. +19980430 code: changed quote2() to avoid quoting <>. +19980429 code: changed quote_need() to quote empty local parts. tnx HHO. +19980428 doc: added status notes to INSTALL and UPGRADE. +19980428 code: skip setting environment in sendmail.c if PROTO is set. +19980428 code: eliminated recipientmap. +19980428 code: added virtual users to qmail-send.c. tnx RN. +19980428 code: eliminated domain from rewrite() in qmail-send.c. +19980428 code: added binm1, binm1+df, binm2, binm2+df, binm3, binm3+df. +19980428 doc: eliminated most Mailbox references from INSTALL, UPGRADE. +19980428 code: added config-fast. +19980428 code: renamed qmail-config as config. +19980428 code: supported QMAILMFTFILE in qmail-inject.c. +19980428 code: recognized Mail-Followup-To in hfield.c. +19980428 code: replaced rwrecip() with rwappend() in qmail-inject.c. +19980428 code: cleaned up doheaderfile() in qmail-inject.c. +19980426 code: eliminated -type test from qmail-qstat to speed it up. + tnx FT. +19980421 doc: eliminated remove-rcpthosts comments from FAQ. +19980421 doc: updated FAQ 4.3 to point to Russ Allbery's FAQ. +19980421 doc: took account of /var/qmail/boot in INSTALL, UPGRADE, and + INSTALL.vsm. +19980421 code: added /var/qmail/boot, with home, home+df, proc, proc+df. +19980421 doc: skipped make and make man in INSTALL. +19980420 doc: cleaned up mbox description in SENDMAIL. +19980420 code: changed QMQP port to official port 628. +19980402 doc: updated qmsmac references to fastforward. +19980402 doc: replaced qmail-upgrade man page with doc/SENDMAIL. +19980402 code: added qmqpservers output to qmail-showctl. +19980402 code: added qmail-qmqpd. +19980402 code: added qmail-qmqpc. +19980304 code: eliminated del_saywhynoexit in qmail-send.c. +19980304 code: eliminated concurrencynodel in qmail-send.c. +19980222 code: added status() to qmail-send.c. +19980222 code: added concurrencyused to qmail-send.c. +19980128 doc: added note to qmail-getpw.9 about ETXTBSY. +19980127 code: eliminated err_seenmail() in qmail-smtpd.c. tnx PO. +19980126 doc: used $DEFAULT in FAQ where possible. +19980126 code: added DEFAULT in qmail-local. +19980126 code: added -/ to qmail-pw2u. +19980126 code: revamped qmeopen() as qmesearch() with more sensible + semantics, separating dash from ext cleanly. +19980126 code: split qmeexists() out of qmeopen() in qmail-local.c. +19980126 code: introduced safeext in qmail-local.c. +19980126 code: changed ~alias to mode 2755, to put files into group + qmail rather than group nofiles under System V. +19980126 doc: switched to /var/qmail/rc in INSTALL*, UPGRADE, FAQ. +19980126 code: added rc. +19980119 doc: added .qmail creation warning to condredirect.1. +19980118 code: made auto_uids.c creation atomic in Makefile. tnx HHO. +19980118 doc: added PIC.*. +19980117 portability problem: Solaris 2.5.1 incorrectly converts + O_NDELAY into O_NONBLOCK for sockets, so that ndelay_off() + fails to undo ndelay_on(). impact: none, since all the network + readers here use select() via timeoutread(). fix: use + O_NONBLOCK if it is defined. +19980115 code: reformatted qmail-qmtpd.c. +19980115 doc: changed tcpcontrol references in FAQ. +19980115 doc: documented morercpthosts in qmail-qmtpd.9. +19980115 code: eliminated unused datetime in qmail-qmtpd.c. +19980115 code: eliminated sigalrm() in qmail-qmtpd.c. +19980115 code: used rcpthosts() in qmail-smtpd.c, qmail-qmtpd.c. +19980115 code: introduced rcpthosts.c. +19980115 code: added morercpthosts.cdb support to qmail-showctl. +19980115 code: added morercpthosts support to qmail-showctl. +19980115 code: do_lst now returns file-exists in qmail-showctl. +19980112 doc: documented morercpthosts in qmail-smtpd.9. +19980112 code: added qmail-newmrh. +19980112 code: used commands.c in qmail-popup. +19980112 code: used commands.c in qmail-pop3d. +19980112 code: introduced fakehelo in qmail-smtpd. +19980112 code: moved flagbarf setting out of bmfcheck(). +19980112 code: allowed more address misformatting in qmail-smtpd. +19980112 code: eliminated qmail@pobox.com help address in qmail-smtpd. +19980112 code: reorganized qmail-smtpd. +19980112 code: reformatted qmail-smtpd. +19980112 code: used commands.c in qmail-smtpd. +19980112 code: switched from 0 to "" for no arg in commands(). +19980112 code: added commands.c. +19971230 doc: added -s to FreeBSD commands in INSTALL.ids. tnx TM. +19971224 doc: added pointer to qmail pictures in README. +19971223 doc: added note in FAQ about qmail-pop3d using maildir. +19971219 code: added HOST2, HOST3, HOST4. +19971219 code: renamed extx as x in qmail-local.c. +19971219 doc: partitioned qmail-command.0. +19971219 doc: updated FAQ 4.3 to point to newer majordomo patches. +19971219 doc: eliminated qlist2 from FAQ. +19971219 doc: eliminated qlist discussion from SECURITY. +19971219 code: moved qlist, qlist2 to separate package. +19971213 doc: added FAQ 4.10 on qmail-users generally. +19971213 doc: added FAQ 4.9 on dealing with NFS outages. +19971031 doc: added Linux and FreeBSD commands to INSTALL.ids. tnx TM. +19971026 doc: added note about smtplf in qmail-smtpd.8. tnx S2S. +19971014 doc: some tweaks to THOUGHTS. +19971012 doc: used MAILER-DAEMON in UUCP example in INSTALL. +19971003 code: eliminated dataline and getln() from qmail-remote.c. +19971003 code: revamped blast() in qmail-remote.c. +19971002 doc: added FAQ entries for .forward and /etc/aliases. +19971002 doc: rewrote INSTALL.mbox and INSTALL.vsm. +19971002 doc: renamed INSTALL.qsmhook as INSTALL.vsm. +19971002 doc: emphasized the qmail-popup argv0 in FAQ. +19971001 doc: added dot-forward note to BLURB3. +19971001 doc: added more configuration notes to qmail-upgrade.9. +19971001 doc: added note in INSTALL.qsmhook about dot-forward. +19970930 code: token822_parse() now supports backslash as a quoting + character in atoms. +19970929 doc: suggested symbolic links in INSTALL.mbox. +19970925 doc: added note to INTERNALS about bounce stability. +19970925 doc: added section to THOUGHTS discussing CNAME lookups. +19970925 code: qmail-remote no longer does CNAME lookup on sender. tnx + C2F. +19970923 portability problem: under SCO OSR5, splogger needs socket + libraries. impact: couldn't compile. fix: socket.lib. tnx RB. +19970906 portability problem: under RISC/OS, Mail invokes sendmail -bm. + impact: can't send mail using Mail on RISC/OS. fix: ignore -bm. + tnx NW. +19970813 code: implemented databytes in qmail-qmtpd. +19970813 code: implemented databytes. tnx M4S for sample code. +19970813 code: replaced execvp() with execv() for sh in qmail-local. +19970813 doc: said in qmail-control.9 that recipientmap allows comments. +19970813 code: used strerr in qmail-local.c. +19970813 code: changed timeoutread(), timeoutwrite() interface. +19970813 code: eliminated shutdown() in timeoutread(), timeoutwrite(). +19970813 code: revamped I/O in qmail-smtpd.c. +19970813 code: used timeoutread(), timeoutwrite() in qmail-smtpd.c. +19970813 code: simplified getcontrol() logic in qmail-remote.c; some + out-of-memory messages are now cannot-read-control messages. +19970813 code: eliminated scan_nbblong(). +19970813 code: reformatted qmail-remote.c. +19970813 code: renamed flaganyrecipok as flagbother in qmail-remote.c. +19970813 code: integrated status report into quit() in qmail-remote.c. +19970813 code: revamped smtpcode() in qmail-remote.c. +19970813 code: added flagcritical in qmail-remote.c. eliminates + possible-duplicate warning if dot has not yet been sent. +19970813 code: revamped I/O in qmail-remote.c. +19970813 code: quit immediately after sending QUIT in qmail-remote.c. +19970813 code: made many more globals in qmail-remote.c. +19970813 code: switched qmail-remote.c from subfdin to home-grown. +19970813 code: switched qmail-remote.c from subfdout to subfdoutsmall. +19970813 code: added LAST support to qmail-pop3d. +19970812 code: changed qmail_close() success return from 0 to "". +19970812 code: revamped I/O in qmail-qmtpd.c. +19970812 code: added qmail-tcpok. +19970812 code: used strerr in maildirmake.c. +19970812 code: reformatted maildirmake.c. +19970812 code: printed qp in condredirect.c. +19970812 code: printed qqx in condredirect.c. +19970812 code: revamped I/O in condredirect.c. +19970812 code: reformatted condredirect.c. +19970812 code: used strerr in preline.c. +19970812 code: revamped I/O in preline.c. +19970812 code: reformatted preline.c. +19970812 code: printed qp in forward.c. +19970812 code: printed qqx in forward.c. +19970812 code: revamped I/O in forward.c. +19970812 code: used strerr in forward.c. +19970812 code: reformatted forward.c. +19970812 code: used strerr in predate.c. +19970812 code: forced failure in qmail-qmtpd if no recipients; saves + time for qmail-send. +19970812 code: added smtpd() to sendmail.c. +19970812 code: added mailq() to sendmail.c. +19970812 code: added die_usage() to sendmail.c. +19970812 code: reformatted sendmail.c. +19970812 code: used byte_zero() in qmail-popup.c. +19970812 code: reformatted qmail-popup.c. +19970812 code: eliminated unused header files in qmail-popup.c. +19970812 code: changed I/O system in qmail-popup.c to match qmail-pop3d. +19970812 doc: pointed people to the mailing list in INSTALL and UPGRADE. +19970810 code: added TXTBSY check to qmail-getpw.c. this gives vendors + the opportunity to make getpwnam() reliable. +19970810 code: moved non-deleted messages from new/ to cur/ in + qmail-pop3d. tnx to various people. +19970810 code: introduced list() in qmail-pop3d.c. +19970810 code: reformatted qmail-pop3d.c. +19970810 code: merged dataline and newname into line in qmail-pop3d.c. +19970810 code: chopped filenames in qmail-pop3d at colons for UIDL. tnx + to various people. +19970810 code: eliminated printint(), printlong() in qmail-pop3d.c. +19970810 code: revamped I/O in qmail-pop3d.c. +19970810 code: used timeoutread(), timeoutwrite() in qmail-pop3d.c. +19970810 code: eliminated die_prot() in qmail-pop3d.c. +19970810 code: eliminated unused header files in qmail-pop3d.c. +19970810 code: switched qmail-pop3d to use maildir.c. tnx MD. +19970809 code: added uid/gid printing to qmail-showctl. tnx PGF. +19970808 code: switched control.c from scan_nbblong to scan_ulong. +19970808 code: cleaned up wait_pid to use waitpid() when possible, and + to support at least one extra child otherwise. +19970807 code: in qmail-smtpd, treat long envelope addresses as a syntax + error, instead of waiting for qmail-queue to reject them. +19970803 code: changed condredirect, forward, qlist, qmail-inject, + qmail-local, qmail-qmtpd, qmail-send, qmail-smtpd, qreceipt for + new qmail_close() interface. +19970803 code: revised qmail_close() to handle qmail-queue exit codes. +19970802 doc: documented SMTP-related exit codes in qmail-queue.8. +19970802 doc: documented qmail-queue exit codes in qmail-queue.8. +19970802 code: revamped qmail-queue exit codes. +19970802 doc: noted linking restrictions in qmail-queue.8. +19970802 doc: rewrote INSTALL.mbox. +19970802 doc: split INSTALL.maildir off of INSTALL.mbox. +19970802 code: added /var/qmail/doc/ creation to qmail-hier. +19970802 doc: added ezmlm note to FAQ. +19970802 doc: replaced qlist blurbs with ezmlm blurbs in BLURB*. +19970802 doc: added various notes to qmail-start.9. +19970728 doc: eliminated RFC*. +19970714 doc: added daemontools notes to FAQ. +19970714 code: eliminated ESMTP parameter syntax checking. +19970701 doc: changed ``forwarded'' to ``resent'' in qmail-header.5. +19970629 code: reformatted constmap.c. +19970628 code: changed straynewline() message in qmail-smtpd.c to point + to http://pobox.com/~djb/smtplf.html. tnx RDM. +19970609 doc: added preline to vacation example in dot-qmail.9. tnx C2S. +19970421 code: cleaned up slurpclose to handle interrupts. +19970421 code: set qmail-popup to mode 711. tnx MD. +19970421 doc: fixed qmail-local -n example in dot-qmail.9. +19970415 version: qmail 1.01. +19970414 doc: tightened up qmail-upgrade.7. +19970414 code: rewrote rewrite(). +19970414 code: implemented recipientmap. suggested by RDM. +19970414 doc: auto-configured qmail home directory in qmail-control.5, + qmail-newu.8, qmail-pw2u.8, qmail-start.8, qmail-users.5. +19970414 port: Solaris needs socket libs for gethostname. impact: can't + compile under Solaris. fix: use socket.lib for qmail-local. +19970412 code: introduced stralloc_starts. +19970412 code: introduced str_equal. +19970412 code: introduced str_start. +19970412 code: introduced byte_equal. +19970412 code: made an optional aliasempty arg for qmail-start. +19970412 code: made an aliasempty arg for qmail-lspawn. +19970412 code: changed ALIAS_EMPTY to an arg for qmail-local. +19970412 port: UnixWare returns >0 for SIOCGIFCONF. impact: ipme fails + under UnixWare. fix: check for >=0, not =0. tnx JD. +19970412 port: DGUX does not have ranlib. impact: can't compile under + DGUX. fix: added dgux line to make-makelib. tnx HWM. +19970412 code: changed maildir library to skip any filename beginning + with dot. tnx SP. +19970412 doc: added FAQ entry about aliases with dots. +19970412 doc: clarified in qmail-inject.8 that default envelope sender + is the same as _default_ From address. +19970411 code: renamed qmail-makectl as qmail-config. +19970411 code: renamed qmail-alias as qmail-local. +19970411 code: switched from signal library to sig library. +19970411 code: switched from qqtalk library to qmail library. +19970411 code: switched from getline library to getln library. +19970411 code: massive library cleanups. +19970411 code: revamped autoconfiguration system. +19970411 code: revamped configuration interface. +19970411 code: eliminated qmail-home. +19970411 code: eliminated tokenize. +19970220 qmail 1.00. +19970219 change: various documentation tweaks. +19970218 change: updated THOUGHTS. +19970218 change: talked about SPAWN_NUMD in FAQ. tnx EC. +19970210 change: noted in maildir.5 that readers should skip any name + starting with a dot. tnx SP. +19970209 change: added note to splogger.8 about reliability. tnx BT. +19970209 change: added section to FAQ on slow sendmail switch. tnx BT. +19970206 change: added section to FAQ about dtcm. tnx PJG. +19970206 change: tweaked maildir.5. +19970201 change: added MH spost note to FAQ. tnx TU. +19970131 change: reorganized FAQ. +19970131 change: added web references to FAQ. +19970124 change: tweaked qmail-upgrade man page. +19970120 qmail 0.96, gamma. +19970120 change: removed various try* in auto-configuration. +19970120 bug: qmail-inject fails to quote argument addresses. impact: + addresses containing special characters could be misinterpreted + or rejected. tnx C2F. fix: use quote2(). +19970120 portability problem: ESIX puts syslog() and openlog() into + -lgen. impact: can't compile under ESIX. fix: put -lgen into + LIBS for unix_sv. tnx RN. +19961221 qmail 0.95, gamma. +19961218 change: added various try* to TARGETS. tnx SA. +19961216 change: clarified in qmail-send.8 that virtualdomains does not + apply to domains listed in locals. +19961216 change: slurpclose() now closes fd on out-of-memory. makes it + more widely applicable. +19961215 change: replaced elm instructions in INSTALL.mbox with an + explanation of what source change to make. tnx AB. +19961212 portability problem: under NEWS-OS, time_t needs sys/types.h. + impact: couldn't compile under NEWS-OS. fix: include + sys/types.h in predate.c. tnx TU. +19961211 change: used timeoutread, timeoutwrite in remoteinfo(). tnx + REB. +19961210 portability problem: apparently some SGIs produce a systype of + irix64. impact: couldn't compile on those systems. fix: handle + irix64 in make-cmds. tnx M3S. +19961208 change: added note to maildir2mbox.1 about mbox locking. +19961208 qmail 0.94, gamma. +19961207 change: added QMAILDEFAULTDOMAIN, QMAILDEFAULTHOST, + QMAILIDHOST, QMAILPLUSDOMAIN. tnx BTW. +19961206 cleanup: readsubdir() protects itself against name overflow, + rather than depending on caller. +19961204 change: changed FAQ 7.3 to prohibit fixup relaying. +19961203 change: added note to FAQ about possibly having to put a space + before "$SENDER" for uux. tnx FW. +19961202 change: added FAQ entry on QUEUE_EXTRA. +19961202 change: added FAQ entry on backups. tnx DP. +19961202 change: added note to INSTALL.mbox about qpopper. tnx VV. +19961201 change: replaced logger with splogger in FAQ 5.1. tnx FPL. +19961201 change: used netmask example for tcpcontrol in FAQ. tnx FPL. +19961201 change: added note to README about the mailing list. tnx FPL. +19961201 change: documented rcpthosts wildcards. tnx RN. +19961201 change: added note to FAQ about making mailx use datemail. +19961201 change: added datemail. function requested by several people; + approach suggested by TG. +19961201 change: added predate. +19961129 change: added QUEUE_EXTRA, QUEUE_EXTRALEN. +19961129 change: qmail-remote bounces messages with partial final lines. +19961129 change: added atomcheck() to quote crappy atoms. +19961129 change: revised atomok() to let atoms deal with more crap. +19961127 change: qmail-send adds note to deferral if flagdying. tnx TG. +19961127 change: split off maildirbounce, maildir2qmtp, and maildir2smtp + into a separate serialmail package. +19961126 change: eliminated beta success reports from README. +19961124 change: forced res_query() return value to fit inside incoming + buffer size. allegedly there are buggy versions of res_query() + that don't guarantee this. +19961122 qmail 0.93, gamma. +19961122 change: allowed empty arg list in forward. +19961121 change: qmail-smtpd now uses unknown (like qmail-qmtpd) rather + than dying if environment variables are not set. +19961121 cleanup: reorganized helo handling in qmail-smtpd.c. +19961121 cleanup: eliminated newfield_rec. +19961121 cleanup: introduced DATE822FMT. used it in received.c. +19961121 cleanup: introduced received.c. used it in qmail-qmtpd.c, + qmail-smtpd.c. +19961121 change: qmail-smtpd now generates a new timestamp for each + message. tnx PJG. +19961121 cleanup: used stralloc in newfield. +19961121 cleanup: eliminated newfield_cc. +19961121 change: eliminated hfield_mort(). +19961119 change: added 2-minute damper on tcpto. +19961118 change: wrote defaults for readfile controls in showctl. +19961117 change: control_readfile() now allows comments and blank lines. + tnx LW. +19961117 change: qmail-start sets logger gid to GID_NOFILES. +19961117 bug: ipme_init() uses a fixed-length buffer for SIOCGIFCONF. + impact: qmail-smtpd and qmail-remote will die if there are too + many local IP addresses. tnx MD. fix: ipme_init() now + dynamically allocates space, up to 200000 bytes, as long as + SIOCGIFCONF keeps failing. note that this is a very widespread + bug; it's in amd, exim, mrouted, named, nntpd, rarpd, sendmail, + tcpdump, timed, xntpd, and probably dozens more programs. +19961117 portability problem: on BSD 4.4 and various other systems, + SIOCGIFCONF will truncate long lists and return success. + impact: on those systems, qmail-smtpd and qmail-remote will + miss some local IP addresses if there are too many. fix: + ipme_init() now checks whether there is enough space left in + the buffer for another ifreq, plus 64 bytes JIC. yuck. +19961117 change: ipmeprint now flushes only at end. +19961117 cleanup: introduced subfdinsmall. used it in qmail-clean.c, + qmail-qmtpd.c. +19961117 cleanup: introduced subfdoutsmall. used it in hostname.c, + printbreak.c, printnumd.c, printsplit.c, qmail-alias.c, + qmail-clean.c, qmail-getpw.c, qmail-qmtpd.c, maildir2smtp.c, + maildir2qmtp.c. +19961117 change: moved subfderr buf up to 256 characters. +19961117 change: added maildirbounce. tnx MD, TG. +19961116 change: maildir2smtp and maildir2qmtp now print filenames for + permanent bounces. tnx MD. +19961116 change: in SECURITY, ``eleven most recent sendmail security + holes, five'' -> ``twelve most recent sendmail security holes, + six.'' +19961116 change: rewrote qmail-showctl more professionally. +19961115 change: added several tests to find-systype.sh. this will + affect many systypes. +19961115 change: qmail-alias now treats most exit codes as soft errors. +19961115 change: revamped exit codes everywhere for 0, 100, 111. +19961114 change: added splogger. +19961114 portability problem: Sun's cc recognizes sqrt() as a builtin, + even if math.h is not included and sqrt is defined statically. + yuck. impact: when qmail is compiled with Sun's cc, next-retry + times are all screwed up. tnx PJG. fix: my sqrt() is now called + squareroot(). +19961114 change: dns_ip() now recognizes [1.2.3.4]. tnx DS. +19961112 change: enabled x option in sendmail. tnx DS. +19961111 change: added SIGHUP handling to qmail-send. suggested by many + people. +19961111 bug: control routines returned incorrect codes for some + out-of-memory conditions. impact: none; conditions cannot + happen with sane control files. fix: return -1. +19961111 change: added SIGALRM handling to qmail-send. suggested by many + people. +19961111 change: eliminated flagnobreak (-b/-B) from qmail-pw2u. +19961111 change: qmail-getpw now allows hyphens inside usernames. +19961111 change: added users/append to qmail-pw2u. tnx G2A. +19961111 change: added badmailfrom to qmail-smtpd. requested by several + people. +19961110 change: replaced elm instructions in INSTALL.mbox with a simple + note to set incomingfolders in elm.rc. tnx AB. +19961110 change: replaced ``owner hack'' with ``variable envelope + return paths'' throughout the documentation. tnx DS. +19961110 change: qmail-setup installs man pages as well as cat pages. +19961110 change: renamed qmail-newuser as qmail-newu. tnx G2A. +19961110 change: renamed qmail-pw2user as qmail-pw2u. tnx G2A. +19961105 change: set path in INSTALL.boot. tnx TJH. +19961105 change: noted in qmail-smtpd.8 that addresses without @ are + always allowed through. +19961105 change: indicated at various spots in FAQ that rcpthosts has to + be updated. suggested by various people. +19961105 change: indicated at various spots in FAQ that qmail has to be + restarted. suggested by various people. +19961029 change: fixed typo in maildir2qmtp.1. tnx BG. +19961026 qmail 0.92, gamma. +19961026 bug: qmail-getpw did not 0-terminate usernames. tnx CF. impact: + qmail-getpw would crash on some systems, deferring local + deliveries. fix: 0-terminate. +19961025 cleanup: renamed auto-hassgprm.h to hassgprm.h. +19961025 cleanup: renamed auto-hassgact.h to hassgact.h. +19961024 change: replaced qmail-alias.0 with dot-qmail.0 in + INSTALL.alias. tnx MW. +19961022 change: switched uids as early as possible in qmail-start.c. +19961022 change: in SECURITY, ``ten most recent sendmail security + holes, five'' -> ``eleven most recent sendmail security holes, + five.'' +19961022 change: quote_need() now treats non-ASCII characters the same + way as control characters. +19961022 change: added version and home page to qmail.7. +19961022 cleanup: introduced slurpclose.c. used it in qmail-alias.c, + qmail-lspawn.c. +19961021 portability problem: AT&T NCR boxes need stdio.h before + arpa/nameser.h. impact: dns.c would not compile. fix: include + stdio.h. tnx HS. +19961021 change: added AIX section to INSTALL.ids. tnx SSB. +19961021 change: added qmail-pw2user. +19961020 change: added qmail-pw2user.8. +19961020 change: qmail-alias now dies soft on EACCES/EPERM for .qmail. +19961020 change: eliminated root comments from INSTALL.qsmhook. +19961020 change: various improvements in FAQ. +19961017 change: added QLX_ROOT. +19961017 change: renamed hosts in FAQ. tnx SLB. +19961017 change: in dot-qmail.5, documented envnoathost effects. tnx RN. +19961017 change: revamped addresses.5. +19961017 change: added stripvdomprepend() for better bounces. tnx PT. +19961012 portability problem: under HP-UX 9, can't setgroups() to 65537. + impact: couldn't compile under HP-UX 9. fix: use 0 instead of + 65537 in chkshsgr.c. tnx HHO. +19961008 change: added several qlx codes. +19961008 cleanup: eliminated qlx from qmail-alias. +19961008 change: qmail-lspawn runs qmail-getpw as UID_PW. +19961008 change: added qmail-newuser. +19961008 change: added cdb support to qmail-lspawn. +19961008 change: integrated cdb. +19961007 change: added qmail-users.5. +19961007 change: eliminated usermap. +19961007 cleanup: switched execvp to execv in sendmail, qmail-lspawn. +19961007 change: used qmail-getpw in qmail-lspawn. +19961007 change: renamed LSPAWN_USERLEN as GETPW_USERLEN. +19961007 change: added qmail-getpw. +19961007 change: created users subdirectory of CONF_HOME. +19961007 change: fixed typo in FAQ. tnx J1B. +19961006 change: replaced subfdout with a small ss in qmail-alias. +19961006 change: reduced qmail-alias buffer sizes to 1024. +19961003 change: added note to maildir2smtp.0 about maildirmake. tnx SM. +19961003 bug: if ipme_init() returned -1, qmail-remote would continue, + blindly assuming that all addresses are local. impact: on + systems with too many aliases, all remote deliveries fail. tnx + MD. fix: qmail-remote now dies with temp_oserr() on any result + other than 1. +19961003 portability problem: all pre-4.9.4 versions of bind barf, + badly, on CNAME queries to lame servers. what a crappy system. + even if the resolver doesn't barf, the next name server down + the line may barf. impact: qmail can't get mail through to + domains that are (1) lame and (2) running old versions of bind. + fix: never, ever, do a CNAME query. dns_cname() now does an ANY + query instead. this, like sendmail's analogous procedure, is + unreliable when a CNAME is mixed with other records. +19961001 cleanup: switched to libfd in qmail-start.c. +19960929 cleanup: renamed auto-hasmkffo.h to hasmkffo.h. +19960928 cleanup: reorganized qmail-start.c. +19960928 cleanup: used libfd in preline.c, qmail-lspawn.c, + qmail-popup.c, qmail-rspawn.c, qmail-start.c, qqtalk.c, + qsmhook.c. +19960928 cleanup: added libfd. +19960927 change: in SECURITY, ``nine most recent sendmail security + holes, four'' -> ``ten most recent sendmail security holes, + five.'' +19960926 change: added tcpcontrol notes to FAQ. +19960926 change: qmail-smtpd now immediately closes connection, with a + warning message dedicated to Solaris, if stray newlines show up + in the incoming data. +19960926 change: added INSTALL.boot. +19960926 portability problem: on systems that can handle IP interface + aliases (i.e., on sa_len systems), SIOCGIFADDR returns the + primary address for an alias. impact: ipme_init() did not + include alias addresses. fix: ipme_init() avoids SIOCGIFADDR on + sa_len systems; on these systems, the address we want is + already in ifr. tnx DM. +19960926 change: qmail-alias kills itself if locking takes longer than + 30 seconds. +19960926 change: qmail-pop3d no longer moves messages. tnx RS. +19960924 change: added note to FAQ about descriptors limit. tnx RD. +19960922 change: open_trunc() now uses 644. +19960922 change: qmail-setup now does umask(077). +19960922 change: maildir2mbox now does umask(077). +19960922 change: moved subfderr buf up to 64 characters. +19960920 change: in SECURITY, ``eight most recent sendmail security + holes, three'' -> ``nine most recent sendmail security holes, + four.'' +19960920 portability problem: init run commands are subject to job + control signals under more systems than HP-UX. impact: on some + systems (e.g., Solaris), qmail daemons would be killed. fix: + INSTALL now tells everybody to use csh -cf. +19960920 change: added queue-run section to FAQ. +19960920 change: in pine-crashing question in FAQ, added -oem and -oi, + so that change will work with the real sendmail too. +19960919 change: added CNAME section to FAQ. tnx to various people. +19960919 change: eliminated QQX_EXECHARD and QQT_EXECHARD. this means + that all qmail-queue invocation failures are now soft, even + things like EPERM. +19960919 change: replaced ``No such address'' with ``Sorry, no mailbox + here by that name.'' tnx G2A. +19960919 change: qmail-remote now includes host name in no-such-host + messages. tnx G2A. +19960919 change: replaced ``Temporarily unable to canonicalize address'' + with ``CNAME lookup failed temporarily.'' +19960918 change: improved an error message in qmail-alias.c. tnx TG. +19960918 change: added SHELL=/bin/sh to Makefile. tnx JL. +19960916 change: reorganized INSTALL.ids a bit. +19960916 change: ``from smtpd'' is now ``from network''. +19960916 change: SMTPD is now DAEMON. +19960916 change: qmail-start sets logger uid to UID_LOG. tnx JLH. +19960916 change: added CONF_USERL. +19960916 change: iaafmt() now puts a dot on in-addr.arpa. +19960915 change: added UPGRADE. suggested by several people. +19960915 change: added qsutil error messages to qmail-log.5. +19960915 change: qsutil error messages are now alerts. +19960915 portability problem: on some systems, logger appears to use + syslog(pri,buf) instead of syslog(pri,"%s",buf). tnx JC. + impact: logger could barf or crash if fed messages containing + %. an attacker could easily cause a crash, eliminating qmail's + logs. fix: % is no longer considered safe for logs. +19960912 cleanup: split seek.c into seek_*.c. +19960912 cleanup: replaced seek_to() with seek_set(). +19960912 cleanup: introduced libseek.a. +19960907 cleanup: split case.c into case_*.c. +19960907 cleanup: introduced libcase.a. +19960907 cleanup: split wait.c into wait_*.c. +19960907 cleanup: introduced libwait.a. +19960907 cleanup: renamed auto-haswaitp.h to haswaitp.h. +19960907 cleanup: split open.c into open_*.c. +19960907 cleanup: introduced libopen.a. +19960904 change: added generic pointer to qmail-control.5. tnx HW. +19960904 change: rewrote rcpthosts section in FAQ. tnx HW. +19960904 change: added organization section to FAQ. tnx HW. +19960902 qmail 0.91, gamma. +19960902 change: control_readfile() can now handle partial lines. tnx + JDHB. +19960902 change: eliminated non-fqdn note from FAQ. next version of + tcpserver will use DNS directly. +19960902 change: qlist now uses NEWSENDER, not SENDER. +19960902 change: qmail-pop3d no longer obtains a lock. tnx RS. +19960902 change: put }g on all seds in Makefile. +19960831 change: noted in qmail-control.5 that comments are not allowed + in control files. tnx J2B. +19960829 change: used double union in alloc.c. tnx ME. +19960829 change: replaced semicolon with colon for smtproutes port. +19960829 change: in INSTALL, put make man just before make setup. +19960829 change: changed a few qmail messages into alerts. +19960829 cleanup: renamed datetime_gmt as datetime_tai. +19960829 change: added note to UUCP question that some UUCP software + doesn't want preline -f. tnx SB. +19960829 change: added question 2.4 to FAQ on SLIP/PPP. +19960828 change: replaced owner- with owner-@host-@[] in qmail-inject. +19690828 change: replaced owner- with owner-@host-@[] in qmail-alias. +19960828 change: replaced owner- with owner-@host-@[] in injectbounce(). +19960828 change: replaced owner- with owner-@host-@[] in senderadd() for + owner hack. +19960828 change: qmail-inject -n now prints Return-Path. +19960825 cleanup: revised ending code in token_addrlist(). +19960825 change: tokenize now uses linelen 0 for unparse. +19960825 change: if linelen is 0 in token822_unparse, no length limit. +19960825 change: added LINELEN macro to qmail-inject for unparse. +19960825 change: token822_unparse now takes linelen argument. (leaving + two spaces on the right before linelen.) +19960824 cleanup: renamed token as token822. +19960822 portability problem: under NEWS-OS, /bin/mail and /usr/ucb/mail + invoke sendmail with -E and -J options. tnx TU. impact: + couldn't send mail with those programs. fix: accept opts, + including _optional_ args. ugh. +19960821 change: sendmail now quits if invoked as newaliases. tnx TU. +19960821 portability problem: under NEWS-OS, dirent.h needs sys/types.h. + tnx TU. this POSIX violation also appears in some versions of + FreeBSD. impact: couldn't compile under NEWS-OS. fix: include + sys/types.h in direntry.h* and trydrent.c. [sigh] +19960821 change: added concurrencyremote question to FAQ. +19960821 change: added chkspawn. +19960821 change: moved default SPAWN_NUMD up to 120. +19960818 change: allowed ;port in smtproutes. tnx AL. +19960818 change: introduced port in qmail-remote.c. +19960818 change: qmail-queue records qp in Received lines. 2 lines of + code. tnx ME. +19960818 change: in SECURITY, ``seven most recent sendmail security + holes'' -> ``eight most recent sendmail security holes.'' +19960818 change: qmail-pop3d now appends an extra blank line to every + message, for compatibility with popper. some clients can't + deal with the right thing, unfortunately. tnx FPL. +19960818 change: added qmail-tcpto. +19960818 change: eliminated cc -posix for NeXTs. tnx SA. +19960818 change: eliminated loadfifo. tnx SA. +19960818 change: integrated auto-configured fifo.c code from SA. +19960817 change: put SYSDEPS into a more reasonable order. +19960813 change: indicated possibility of duplication when qmail-remote + gets a dead connection after DATA. tnx ME. +19960813 change: documented qmail-inject environment variables. +19960813 change: supported per-recipient owner hack in qmail-inject. +19960813 change: supported per-message owner hack in qmail-inject. +19960813 change: introduced hackedruser into qmail-inject. +19960813 change: introduced QMAILRUSER, QMAILRHOST. +19960812 change: added QMAILINJECT option to allow address-comment form. +19960812 change: made name-address form the default in qmail-inject. +19960812 change: added QMAILINJECT options f and m to delete From and + Message-ID on input. tnx LL. +19960812 change: added QMAILINJECT environment variable. +19960812 change: added QMAILHOST, QMAILUSER, QMAILNAME to override + MAILHOST, MAILUSER, MAILNAME. tnx MG. +19960812 change: added qmail-showctl. +19960812 portability problem: under Solaris 2.4 and possibly other + systems, the linker does not give generic alignment to an array + of 4096 chars. tnx JP. impact: some subset of the programs + would (reliably) die with a bus error; in the Solaris case, + maildir2mbox. fix: redefine space in alloc.c to be aligned. +19960812 change: qmail-remote no longer does CNAME lookups if there's an + artificial SMTP route. tnx ME. +19960812 change: added flagcname arg to addrmangle() in qmail-remote. +19960812 cleanup: moved host/relayhost processing earlier in + qmail-remote. +19960812 change: qmail-remote stops before DATA if no RCPTs were + successful. tnx JLH. +19960812 change: rewrote rcpthosts explanation in FAQ. +19960811 change: added qmail-log.5. +19960811 change: introduced ALIAS_PATERNALISM. configurability requested + by several people. +19960811 change: eliminated go-writability test for qmeox(). the alleged + value of paternalism is nonexistent if nobody even notices + you're doing it. +19960811 change: in qbiff, changed no-/-allowed to no-/-at-beginning, + no-dots-allowed, must-be-nonempty. tnx MD. +19960811 change: in mbox.5, discouraged mail readers from looking for + From_ lines only after blank lines. too much crap in the world. +19960811 change: added subject line to qreceipt success notices. +19960811 change: added subject line to qmail-send bounce messages. +19960811 change: qmail-alias now expects dash arg. this finally gives + lspawn complete control over the local -> ~user/.qmail-ext map. +19960811 change: qmail-lspawn passes dash arg to qmail-alias. +19960811 change: reorganized qlist acknowledgment format. again. +19960811 change: documented EXT, EXT2, EXT3, EXT4. tnx BB. +19960810 change: qmail-makectl now copies locals to rcpthosts. should be + a better default. suggested by TK. +19960805 portability problem: new makefile generator put in tabs again. + sigh. impact: couldn't compile under some systems. fix: same as + before. tnx TG. +19960804 change: added tcpserver instructions to FAQ. +19960804 change: reorganized FAQ server instructions into a new section. +19960801 qmail 0.90, gamma. +19960801 change: qmail-qmtpd now supports rcpthosts, RELAYCLIENT. +19960731 change: default NUMD is now 29. this prepares for weird systems + where getpwnam() needs more than one descriptor (but the + descriptor limit is still 64! ... you never know), and for + possible future getpwnam() replacements. +19960731 change: popped subfderr buffer up to 32 characters. made sure + that everybody flushed subfderr as necessary. +19960731 change: maildir2qmtp now prints filenames and responses. +19960731 change: maildir2smtp now prints filenames it's trying and + relevant portion of SMTP responses. +19960731 change: used succwrite() in maildir2smtp, maildir2qmtp. + simplifies code quite a bit. +19960731 change: qmail-remote's blast() checks sooner for write errors. +19960731 change: added better -e option to sendmail. tnx TG. +19960731 change: added maildir2qmtp. +19960730 cleanup: eliminated die_nomem() in maildir2smtp.c. +19960730 change: dns_cname now pretends that "foo." is a CNAME for "foo" + to give the desired behavior for people who misuse DNS and + violate RFC 822. tnx RN. +19960730 change: dns_cname now tests for empty names and ] on every + loop. +19960730 change: used LSPAWN_BREAK in qmail-send.c for usermap. +19960730 change: updated header example in qmail-header.5. +19960730 change: added printbreak. auto-configured BREAK in dot-qmail.5, + qmail-lspawn.7, qmail-send.8, qmail-upgrade.7, qlist2. +19960730 change: added printnumd. auto-configured NUMD in qmail-send.8, + qmail-limits.8. +19960730 change: added printsplit. auto-configured split in qmail-upq. +19960730 change: added dot-qmail.5. +19960730 change: qmail-smtpd now treats HELO as including RSET. +19960730 change: added moreinfo to qlist usage message. +19960729 change: improved an error message in qmail-alias. +19960729 cleanup: merged qmeox(), qmeodx(). +19960729 bug: failure to stat .qmail-owner was not an error. impact: if + stat failed temporarily (e.g., because of NFS), .qmail-owner + would be incorrectly ignored, so outgoing message would have + wrong envelope sender. fix: qmail-alias does temp_nfsqmail() if + stat() returns a temporary error. +19960729 change: added RFCOWNER. +19960729 change: added qmtpd setup question to FAQ. +19960729 change: added qmail-qmtpd. +19960728 change: revamped maildir2smtp error messages. +19960728 change: revamped maildirwatch error messages. +19960728 change: revamped maildir2mbox error messages. +19960728 change: used strerr in maildir_scan(). +19960728 change: used strerr in maildir_chdir(). +19960728 change: introduced strerr. +19960728 bug: the new tcp-env tried to read from an ndelay socket. + impact: TCPREMOTEINFO would always be empty. fix: unset ndelay + in remoteinfo.c. +19960728 bug: if maildir2smtp saw a permanent failure after MAIL, it + failed to do RSET. impact: all further messages would be + rejected at the MAIL stage. fix: maildir2smtp now always does + RSET. tnx JW. +19960728 cleanup: qmail-alias now applies lowercase and dot-to-colon + conversion directly to dashext, leaving everything else alone. + this works since all .qmail access is factored through dashext. +19960727 portability problem: under NeXTStep, -posix is almost entirely + broken. impact: qmail daemons would dump core under NeXTStep. + fix: turn off -posix, except for loading qmail-setup, which + needs mkfifo(); NeXT, bless them, didn't put mkfifo() into the + C library where it belongs. this requires a new make command, + namely loadfifo. +19960727 change: all characters 33-126 are now considered safe for logs. + tnx MD. +19960727 cleanup: eliminated qp variable from mailforward(). +19960727 cleanup: maildirwatch.c includes headerbody.h. +19960727 cleanup: eliminated match from maildirwatch.c. +19960727 cleanup: eliminated code variable from maildir2smtp.c:doit(). +19960727 cleanup: maildir2smtp.c includes scan.h. +19960727 cleanup: maildir.c includes str.h. +19960727 cleanup: qmail-popup.c now includes exit.h. +19960727 cleanup: qmail-pop3d.c now includes exit.h. +19960727 cleanup: eliminated path from qmail-start.c. +19960727 cleanup: eliminated birthplusnn from nextretry(). +19960727 cleanup: eliminated r from timeoutconn(). +19960727 cleanup: tcpto.c now includes byte.h. +19960727 cleanup: spawn.c now declares initialize(). +19960727 cleanup: qmail-lspawn.c now includes str.h, byte.h. +19960727 cleanup: qmail-inject.c now includes quote.h. +19960727 change: qmail-check now checks separately for group + readability and other readability. +19960727 bug: maildir2smtp didn't check flagehlo in PIPELINING parsing. + impact: a server that said PIPELINING at any point, not just + EHLO, would receive pipelined data. fix: check flagehlo. +19960727 bug: readsubdir was calling pause(). impact: if a subdirectory + was removed, qmail-send would hang. fix: use rs->pause(). +19960727 change: used error_str in qmail-qread. +19960727 change: qmail-qread now looks for local/remote open errors. +19960727 cleanup: added warn() in qmail-qread.c. +19960727 change: qmail-qread now exits 111 for temporary errors. +19960727 change: used error_str in qmail-setup. +19960727 change: introduced error_str. +19960727 change: replaced qmail-check with make check in INSTALL. +19960727 change: added check target to Makefile. +19960727 change: replaced qmail-setup with make setup in INSTALL. +19960727 change: indirected fake targets through do- targets. +19960727 change: added setup target to Makefile. +19960727 change: qmail-makectl now makes sure that defaultdomain has + at least one dot. e.g., enteract.com -> enteract.com, not com. +19960726 bug: quote() failed to quote commas. impact: addresses + containing commas would not have been quoted correctly for + Return-Path or for SMTP MAIL FROM. fix: quote commas. +19960726 change: sendmail now mentions qmail-qread, not qmail-mailq. +19960726 change: qmail-alias now expects ext arg. this eliminates + LSPAWN_BREAK from qmail-alias and gives lspawn almost complete + control over the local -> ~user/.qmail-ext transformation. the + exception is that qmail-alias always uses ~user/.qmail, + ignoring ext, if local is the same as user. +19960726 change: qmail-lspawn passes ext to qmail-alias. +19960726 change: alloc() now uses up a 4K space before calling malloc(). +19960726 change: ipalloc allocation base is now 10. 100 was silly. +19960726 change: stralloc allocation base is now 30. +19960726 change: injectbounce() now supports the owner hack. +19960726 change: qmail-smtpd no longer requires HELO. tnx K1J. +19960726 cleanup: replaced makereceived() with dohelo(). +19960726 change: qmail-smtpd is back to 555 for syntax errors. +19960725 change: qmail-alias now supports the owner hack. tnx to RN for + prodding me to look at this problem. +19960725 change: senderadd() now supports the owner hack. +19960725 cleanup: split off senderadd(). +19960725 change: added pine-crashing note to FAQ. +19960725 change: added procmail config.h note to INSTALL.mbox. +19960725 change: added elm TMPDIR note to INSTALL.mbox. +19960725 change: added pine.conf note to INSTALL.mbox. +19960724 change: added fixup note to FAQ. +19960724 change: qmail-inject now exits 111 for temporary errors. +19960724 change: qmail-smtpd now appends RELAYCLIENT to incoming + recipient domain names. +19960724 cleanup: moved relayclient out of qmail-smtpd's addrallowed() + into caller. +19960724 change: added rcpthosts wildcards. +19960724 change: added clean target to Makefile. +19960723 change: added virtualdomains exceptions. +19960722 change: added BLURB4. +19960722 change: added BLURB3. +19960722 change: eliminated smarthost and localnet. +19960722 change: incorporated relaymap, contributed by LW. renamed it + as smtproutes. +19960722 change: qmail-popup now supports APOP. suggested by BG, who + distributed similar changes. +19960722 change: qmail-popup now sends APOP timestamp to checkpassword. +19960722 cleanup: in qmail-popup, split off doanddie(). +19960722 change: qmail-popup now prints APOP timestamp in banner. +19960722 change: added hostname argument to qmail-popup. +19960722 cleanup: in qmail-popup, split out() into out(), outflush(). +19960722 cleanup: in qmail-popup, introduced pop3_greet(). +19960721 portability problem: under Unisys SVR4, hostname is not in the + usual path. impact: qmail-makectl fails. fix: added hostname + command here, used it in qmail-makectl. +19960721 portability problem: on some sysctl-based systems, apparently + gethostname() doesn't write anything if the output buffer is + too small. it should write a truncated name. impact: if anyone + has a hostname longer than 64 characters, maildirs could get up + to 64 characters of garbage, rather than a truncated hostname. + fix: qmail-alias now does *host = 0 before calling gethostname. +19960721 change: updated FAQ examples from qsmhook to preline. +19960721 change: added preline. +19960721 change: qsmhook now uses signal_init, signal_uninit. +19960721 change: qsmhook now checks specifically for empty args. +19960721 change: documented mbox. +19960721 change: added EXT, EXT2, EXT3, EXT4. +19960721 change: added LAST response to qmail-pop3d, always returning + OK 0. tnx RN. +19960721 change: added qmail home page to README. +19960721 change: added HELP response to qmail-smtpd. tnx RN. +19960720 change: expanded, vertically, the qmail-inject error message + for unparseable header fields. +19960720 change: logo is now dolphin. tnx CEJ. +19960719 qmail 0.76, beta. +19960719 change: used LSPAWN_BREAK in qmail-alias for deciding how to + handle extensions. this should produce better behavior in the + (unsupported) case that LSPAWN_BREAK is not a hyphen. +19960719 bug: qmail-smtpd didn't check for null arg on MAIL, RCPT. + impact: qmail-smtpd would deref 0 and crash. fix: qmail-smtpd + now gives syntax error on null arg. +19960719 change: documented UFLINE in qmail-command.8. tnx TG. +19960718 change: added maildir2smtp. +19960718 cleanup: introduced maildir.c. used it in maildir2mbox.c, + maildirwatch.c. +19960718 change: added maildirwatch. +19960718 cleanup: maildir2mbox now sets up pq2 as it is deleting from + pq, rather than simultaneously with pq. +19960718 change: added H_DELIVEREDTO. +19960718 portability problem: Unisys requires -lsocket -lnsl. impact: + couldn't compile under Unisys. fix: added unix_sv section to + make-cmds.sh. tnx TVP. +19960718 change: added unix_sv section in find-systype. tnx TVP. +19960717 change: qmail-alias now appends newline if .qmail does not end + with a newline. tnx MC. +19960717 change: qmail-alias now defers delivery for a blank line only + if it is the first line of the file. handles user behavior + described by MC of putting many newlines at end of file. +19960717 bug: qmail-inject looked for dots in user part, not just host + part, when deciding whether to use defaultdomain. impact: the + address joe.bloggs@here didn't have defaultdomain added. fix: + qmail-inject now stops at the @. +19960717 change: updated INSTALL.alias to mention qmsmac. +19960717 change: syntax error code for SMTP is now 501. +19960717 change: added -e option to sendmail. tnx TG. +19960716 change: changed ~alias files to .qmail-local, not .qmaillocal. + suggested by many people. +19960716 change: redid qmail-alias/qmail-lspawn interface. +19960716 change: replaced EXTENSION, USEREXT with LOCAL. +19960716 change: qmail-queue now removes intd, mess upon error, as long + as it doesn't time out. suggested by BB et al. +19960716 change: added flagmademess, flagmadeintd to qmail-queue.c. +19960716 cleanup: changed todofd to intdfd in qmail-queue.c. +19960716 cleanup: added cleanup() to qmail-queue.c. +19960716 change: added timeout to tcp-env.c, default 30 seconds. +19960716 change: remoteinfo_get() now uses timeoutconn(). +19960715 change: added procmail config.h note to FAQ. +19960704 change: qmail-upgrade.7 now warns administrators that ~alias + generally doesn't apply to addresses starting with a user name. +19960703 change: added echo \c note to FAQ. tnx PJG. +19960702 change: qmail-smtpd now accepts HELO without an argument. + tnx K1J, J1B. +19960627 change: qmail-lspawn.8 now mentions that qmail-lspawn doesn't + set up supplementary groups. tnx TG. +19960625 portability problem: under Linux, read(,,0) doesn't do proper + error slippage. impact: timeoutconn() would always report + success; if a connection failed, qmail-remote would report a + greeting failure and skip all further MX records. tnx ME. fix: + timeoutconn() now uses getpeername() to check for success. +19960625 change: qmail-smtpd now mentions disk full for QQT_WRITE. +19960625 change: qmail-inject now mentions disk full for QQT_WRITE. +19960622 change: if RELAYCLIENT is set, qmail-smtpd skips rcpthosts. +19960609 change: updated INSTALL for current SMTP responses. +19960607 change: clarified INSTALL.qsmhook examples. tnx S1R. +19960607 change: added subject parsing to qlist.c. tnx RN. +19960607 cleanup: used case_diffb in qlist.c. +19960607 change: added extra log information to INSTALL examples. +19960606 change: added -Pn to uucp line in FAQ. tnx DWS. +19960605 portability problem: under Solaris, /usr/bin/groups incorrectly + reports your groups in /etc/group, rather than the results of + getgroups(). tnx MD, PJG. impact: test #19 in INSTALL fails. + fix: added special note to test #19 (sigh) about Solaris. +19960605 change: improved maildir setup commands in INSTALL.mbox. +19960605 change: on success, qmail-alias logs forwarding qp. 9 lines + extra code. +19960605 change: qmail-send logs qp for bounce. 6 lines extra code. +19960605 change: qmail-smtpd includes qp in its response when it accepts + a message. 7 lines extra code. requested by MD and others. +19960605 change: added qqtalk_qp. +19960605 change: qmail-send now logs uid and qp from todo file. 14 lines + extra code. +19960605 change: qmail-queue now records uid and qp in u and p lines + in todo file. 7 lines extra code. +19960605 change: improved qmail-alias x-bit error messages. +19960605 change: newline in log is now converted to /, not underscore. +19960604 change: when it accepts a message, qmail-smtpd includes the + local time in its 250 response. +19960604 change: on success, qmail-alias prints delivery counts, + file+forward+program. +19960603 change: qmail-remote now reports IP address on success. tnx MD. +19960603 change: qmail-send now logs success and failure reports, not + just deferral reports. +19960603 change: added netbsd section in find-systype, same as bsd.os + section. this will affect netbsd-* systypes. tnx MBS. +19960530 qmail 0.75, beta. +19960528 change: added qmail.7. tnx MD. +19960525 change: added qmail-pop3d. tnx RN. +19960525 change: added qmail-popup. tnx RN. +19960525 change: added elm filter section to FAQ. tnx GB. +19960502 portability problem: on many systems, select() on an + almost-full pipe incorrectly says writable. tnx ME for running + into this and helping track it down. impact: if qmail-send + writes a pipeful to qmail-lspawn or qmail-rspawn before they + can react (because of high concurrency, high load, or long + addresses), it will receive an incorrect -1/EAGAIN, and will + conclude that spawn died. sysadmin will have to restart qmail, + and messages will be duplicated. fix: in qmail-send.c, + busy-loop if write() to spawn returns any error other than + EPIPE. +19960501 bug: qmail-alias treated NAMETOOLONG and NOTDIR as temporary + errors. impact: qmail-alias never looked for -default; even if + mail was destined to bounce, it would have to time out first. + fix: qmail-alias now uses error_temp(). +19960430 bug: qmail-smtpd treated qq crash as permanent error. impact: + if somebody actively kills qq, mail will be incorrectly + bounced. tnx SS. fix: qmail-smtpd now treats only TOOLONG and + EXECHARD as permanent errors. +19960430 cleanup: eliminated QQT_TTY from qqtalk.h. +19960428 change: added ``warning: '' before trouble-marking message. +19960428 change: added percenthack. requested by GB. +19960428 cleanup: switched to auto-generated Makefile. +19960428 cleanup: switched to auto-generated .o dependencies. +19960428 cleanup: eliminated fmt.o, scan.o from Makefile. +19960428 portability problem: under HP-UX 10, the rc pgrp is sent HUP + when rc finishes. tnx BG. impact: the qmail daemons are killed + when rc finishes. fix: added special note in INSTALL (sigh) to + use csh -cf. +19960427 cleanup: added PORT_SMTP in qmail-remote.c. +19960427 cleanup: introduced timeoutwrite.c. used it in qmail-remote.c. +19960427 cleanup: introduced timeoutread.c. used it in qmail-remote.c. +19960427 cleanup: introduced timeoutconn.c. used it in qmail-remote.c. +19960427 change: added timeoutconnect. default: 60 seconds. +19960427 change: added pop3d instructions to FAQ. tnx RN. +19960427 change: eliminated env manipulation from qmail-start. tnx BG. +19960427 change: headerbody now ends header, inserting blank line, if + first line of a header field doesn't pass hfield_valid. tnx TG. +19960427 change: headerbody now prepends MBOX-Line: to any header line + starting From_. this lets qmail-inject work with elm's bounce. + tnx OR, K1J, et al. +19960426 change: added moreinfo arg to qlist and qlist2. +19960426 change: added signal_uncatchchild() to qmail-send.c. tnx BG. + now, if sysadmin sets SIGCHLD to SIG_IGN before invoking + qmail-send [sigh], qmail-send won't screw up bounce messages. +19960426 change: dns_cname now checks whether last character is ], + rather than whether first character is [, for quick return. +19960426 cleanup: glue is now global in dns.c. +19960426 cleanup: qmail-remote no longer does stralloc_0 for host and + canonhost. +19960426 change: dns_mxip no longer rejects [foo].bar. +19960426 change: dns_mxip no longer requires for bracket that input + be 0-terminated. +19960426 change: qmail-start can now take logger as an argument. +19960426 change: qmail-start now invokes qmail-send in foreground (as + parent of other processes). +19960426 change: added mailsubj. tnx GAW. +19960426 portability problem: under some systems, can't lock read-only + file. impact: maildir2mbox would always fail on those systems. + fix: maildir2mbox now opens a separate lock fd. tnx BG. +19960426 cleanup: removed unnecessary #!/bin/sh and # AUTO from mctl.sh. +19960426 change: added qmail-qstat. +19960426 change: added qmail-qread.8. +19960426 change: renamed qmail-mailq as qmail-qread. +19960419 change: qmail-alias now defers delivery rather than skipping + blank lines in .qmail. +19960419 change: in qmail-lspawn.c, lowercased name before getpwnam(). + really getpwnam() should do this, but oh well. +19960419 change: added username to qmail-lspawn.c, with LSPAWN_USERLEN + in conf-unusual.h. names longer than LSPAWN_USERLEN will skip + getpwnam(). +19960419 change: if qlist doesn't see any cmds, it presumes that you + meant to subscribe. +19960419 change: reorganized qlist acknowledgment format. +19960415 change: reorganized and rewrote FAQ. +19960415 change: renamed HOWTO as FAQ. +19960414 change: in qmail-alias, converted extension to lowercase just + before qmeopen(), qmeox() calls. thus EXTENSION and USEREXT and + RECIPIENT will preserve case passed by qmail-lspawn, while + .qmailext lookups will not. +19960414 change: removed case_lowers(r) from qmail-lspawn.c. tnx JLH. +19960414 change: moved extension . -> : conversion to just before + qmeopen(), qmeox() calls in qmail-alias.c. thus EXTENSION and + USEREXT and RECIPIENT will preserve dots. +19960414 change: qsmhook -x now does case-independent comparison. +19960413 change: added procmail instructions to HOWTO. +19960409 bug: qmail-alias does not check for newlines when it generates + Return-Path. impact: resulting Return-Path header field will be + illegal, if sender address contains newline followed by + something other than whitespace. fix: qmail-alias now replaces + newline with underscore in rpline. +19960409 change: added leaf UUCP description to HOWTO. tnx J2K. +19960409 change: added -B option to sendmail. tnx OR. +19960409 change: qlist now makes lists unwritable (after renaming from + .qtemp to .qmail). tnx MLH. +19960409 change: added flagdtline to qsmhook.c, based on -l option. +19960409 change: added PIPELINING declaration to qmail-smtpd. tnx JGM. +19960409 change: qmail-smtpd now flushes output instantly after DATA, + QUIT, HELO, EHLO, NOOP, VRFY, or any 502. +19960409 change: qmail-smtpd now flushes output upon read() and death. +19960409 change: qmail-smtpd no longer flushes output in out(). +19960409 change: increased qmail-smtpd outbuf size from 128 to 512. +19960409 cleanup: in qmail-smtpd, eliminated ssinit() in favor of FDBUF. +19960409 bug: qmail-alias produced aliasfoo-owner rather than foo-owner + as envelope sender for ~alias/.qmailfoo. tnx DS. impact: wrong + envelope sender whenever ~alias/.qmailfoo-owner existed. fix: + qmail-alias now checks for hyphen at beginning of extension. +19960409 change: added _ESMTP to end of 220. tnx JLH. +19960409 change: moved out("\r\n") out of smtp_greet() into callers. + this improves the flushing behavior on 221. +19960328 qmail 0.74, beta. +19960326 change: changed subdirectory split from 32 to 23. +19960326 portability problem: some versions of make don't understand + that a line with just a tab is blank. impact: couldn't compile + under those systems. fix: eliminated extra tab from Makefile. + tnx TG. +19960325 change: added qmail-mailq. +19960325 change: introduced readsubdir. +19960325 change: qmail-setup makes split; qmail-check checks split. +19960325 change: used split in qmail-send, qmail-clean, qmail-queue + for mess, info, local, remote. +19960325 change: fmtqfn now supports split queue subdirectories. +19960325 cleanup: eliminated cat2s(). +19960325 cleanup: introduced fmtqfn.c. used it in qmail-queue.c, + qmail-send.c, qmail-clean.c. +19960325 change: in protocol between qmail-clean and qmail-send, now + using intd/ instead of mess/. +19960325 change: qmail-queue.c and triggerpull.c now work inside queue + subdirectory. +19960325 change: spawn.c now checks whether message is a regular file. +19960325 change: spawn.c now allows slashes in messid except at + beginning. +19960325 cleanup: introduced fnmake_split in qmail-send.c. +19960325 cleanup: eliminated strnum in qmail-send.c in favor of + fnmake_{info,todo,mess,chanaddr} and fnmake2_bounce. +19960325 cleanup: introduced strnum3 in qmail-send.c for the logging + uses of strnum. +19960325 cleanup: in qmail-send.c, getinfo() now takes id argument. +19960325 cleanup: qmail-send.c now preallocates space for fn, fn2. +19960325 change: time zone is now -0000 instead of +0000. encouraging + DRUMS to use this as an i-don't-know-the-local-time indicator. +19960324 change: qmail-rspawn.c now calls tcpto_clean(). +19960324 cleanup: spawn.c now calls initialize(). +19960324 change: qmail-setup makes lock/tcpto; qmail-check checks it. +19960324 change: qmail-remote now quickly skips connect() to a host that + seems to be down. tnx BP for pressuring me to get this done. +19960323 change: in qmail-alias.8, renamed mboxg as mboxrd. tnx RD. + idea was popularized by RD in June 1995. +19960322 cleanup: eliminated subfd_init(). +19960322 change: qbiff now removes the word Subject. +19960322 change: now /bin/true instead of /dev/null in the generic + INSTALL.ids instructions. tnx JPR. +19960322 change: added hfield_skipname(). tnx RN. +19960322 bug: qmail-inject did not check whether USER needed quoting. + impact: if USER had weird characters, the From address would + generally be wrong, unless the user manually set up MAILUSER + with proper quoting. fix: qmail-inject sets up a quoted-string + if necessary. +19960322 cleanup: separated out quote_need() in quote.c. +19960322 cleanup: added stralloc_catb.c. used it in qmail-alias.c, + qmail-send.c. +19960322 change: qmail-send now uses a quadratic retry schedule from + birth of each message. this also eliminates clustering. +19960322 cleanup: separated out nextretry() in qmail-send.c. +19960322 change: qmail-remote now passes all non-@ addresses through + without comment, not just <> and <#>. +19960322 change: replaced # test with anything@[] test in qmail-inject. +19960322 change: replaced # with #@[] in qlist.c, qmail-alias.c, + qmail-send.c, qreceipt.c. +19960322 change: qmail-lspawn no longer discards messages to <#>. +19960322 cleanup: in qlist, used str_diff for <> and <#> tests. +19960322 change: qmail-alias is now back to testing envelope sender for + <> and <#>, rather than things without an @. +19960321 change: added 8BITMIME support to qmail-smtpd. +19960321 change: added ESMTP support to qmail-smtpd. +19960318 change: used NEWSENDER in place of SENDER for |forward. +19960318 change: added NEWSENDER. +19960318 change: added HCMSSC support to qmail-alias.c. +19960318 change: added HCMSSC support to spawn.c. +19960318 change: added HCMSSC support to qmail-remote.c. +19960318 change: added HCMSSC support to qmail-smtpd.c. +19960317 portability problem: SCO requires -lsocket -lnsl. impact: + couldn't compile under SCO. fix: added SCO section in + make-cmds.sh. tnx JPR. note that this is for OSR 5; 3.2v4.2 + will need more fixes, and old 3.2 is basically hopeless. +19960317 bug: newfield_datemake would leave newfield_date alone if it + was already initialized, even though qmail-send calls + newfield_datemake anew for each bounce. impact: bounce messages + would usually have an incorrect Date field. fix: redid + newfield_datemake to update newfield_date each time. +19960317 change: allowed . and @ in 822 phrases; 822 doesn't allow them, + but they do show up. tnx to the DRUMS group. +19960317 change: replaced GMT with +0000 in date822fmt.c. this confuses + a few versions of getdate(), but the DRUMS group is going to + outlaw GMT, not just recommend against it as in 1123. +19960317 change: redefined ALIAS_EMPTY to take advantage of . for file + deliveries. tnx RN. +19960317 change: qmail-alias now allows . as well as / to start file + deliveries. tnx RN. +19960317 change: qmail-alias now dies (soft) if .qmail is writable to + others, rather than silently ignoring it. +19960317 change: qmail-alias now dies (soft) if flagforwardonly is + violated, rather than silently ignoring the bad instructions. +19960317 change: qmail-alias now ignores x bit on empty .qmail files. +19960317 bug: if RCPT gave 4xx and DATA gave 5xx, qmail-rspawn would + incorrectly assign a permanent failure to that recipient. + impact: in that case, mail would be incorrectly bounced. fix: + remove orr > 0 test from qmail-rspawn.c. +19960310 change: tcp-env now uses signal_uninit(). [sigh] +19960310 change: tcp-env now specifically unsets HOST and INFO if they + are not applicable. just trying to make it more widely usable. +19960310 cleanup: used byte_* in remoteinfo.c, ipme.c, tcp-env.c. +19960310 cleanup: added readwrite.h, eliminated sys.h. +19960310 cleanup: included byte.h in qmail-send.c. +19960310 cleanup: eliminated i and j from forward.c's main(). +19960310 cleanup: eliminated wstat from qlist.c. +19960310 cleanup: eliminated die_nomem() parameter in qmail-setup.c. +19960310 cleanup: eliminated i from qmail-remote's addrmangle(). +19960310 cleanup: added exit.h. +19960310 cleanup: split ipalloc.c off of ip.c. +19960310 cleanup: added fmt_strn.c, eliminated fmt_strncpy.c. +19960310 change: reorganized INSTALL to do some pre-upgrade tests. + tnx RN. +19960310 change: reordered steps in upgrade procedure in INSTALL. +19960308 change: eliminated ownership test in qmail-alias.c. tnx DS. +19960304 change: in SECURITY, ``six most recent sendmail security + holes'' -> ``seven most recent sendmail security holes.'' +19960303 qmail 0.73, beta. +19960303 change: added SYSDEPS. +19960303 cleanup: revamped select.h autoconfiguration. +19960303 cleanup: revamped fork.h autoconfiguration. +19960303 cleanup: revamped direntry.h autoconfiguration. target is now + direntry.h; auto-hasdrent.h is gone. +19960303 change: tryflock.c now includes , for consistency + with lock.c. may affect portability. +19960302 portability problem: under BSDI, can't set sticky on normal + files. dorks. impact: the new qlist doesn't work under BSDI; + be glad I test things before release. fix: qmail-alias and + qlist now use executable instead of sticky. +19960302 change: gfrom now quotes >From and >>From etc. as well as From; + in other words, I'm switching from mbox format to mboxg format. +19960302 cleanup: added gfrom.c. used it in qmail-alias.c, maildir2mbox.c. +19960302 change: addbounce() now substitutes \n\n -> \n/ in reports, + and \n -> _ in recips. thus bounces can now be reliably parsed. +19960302 change: if qmail-send had trouble reading the original message + or the list of addresses for a bounce, it used to give up and + send a bounce with "Oh no! I had trouble reading the rest of + your message" or some such. now it aborts the bounce attempt + and tries again later. +19960302 cleanup: added qqtalk_fail(). used it in qmail-alias.c, + qmail-smtpd.c. +19960302 bug: if mailforward() had trouble reading message (e.g., + because of an I/O error), it marked an error but kept reading. + impact: could loop forever. fix: upon error, break. +19960302 change: maildir2mbox now scans (restrictively) for return-path. +19960302 change: qbiff now prints subject and body, up to 74 chars. +19960302 change: added H_SUBJECT to hfield. +19960302 change: qbiff now puts TO before FROM. +19960301 cleanup: added fmt_str.c. used it in many places. +19960301 change: qmail-send now says something if you've told it to exit + but it's waiting for some deliveries. tnx RN. +19960301 change: qmail-alias -n now continues (with warning) if home + directory is sticky. tnx RN. +19960301 change: improved usage messages in qmail-alias.c. tnx RN. +19960301 change: put limit on length of addresses in qlist. +19960301 change: added exit 99 support to qmail-alias. tnx RN. +19960301 change: qmail-alias now exits immediately on temporary or + permanent error. rewrote section in qmail-alias.8 accordingly. +19960301 cleanup: eliminated flagsuccesses from qmail-alias.c. +19960301 change: added usermap. +19960301 bug: failure to append to mbox was a permanent error. impact: + if mbox was temporarily unopenable (e.g., because fds were + low), mail would be incorrectly bounced. fix: failure is now + temporary. tnx DS. +19960229 change: qmail-alias now preserves any envelope sender that + doesn't contain an @, not just <> and <#>. +19960229 cleanup: revamped byte_* interface. +19960229 cleanup: renamed str_cpy as str_copy. +19960229 cleanup: added str_chr.c. used it in qbiff.c, qmail-smtpd.c. +19960229 cleanup: added str_rchr.c. used it in qmail-send.c, quote.c, + qmail-remote.c. +19960229 cleanup: added byte_rchr.c. used it in qmail-smtpd.c, spawn.c. +19960229 cleanup: used USEREXT instead of RECIPIENT in qsmhook.c. +19960229 cleanup: used USEREXT instead of RECIPIENT in qbiff.c. +19960229 cleanup: removed j and k from rewrite() in qmail-send.c. +19960229 portability problem: under HP-UX 10 and Solaris 2.5, can't + setgroups()/setgid() to the system's nogroup/nobody gid. dorks. + impact: inetd chokes, so all SMTP connections are rejected; and + ``alias'' mail, including postmaster, bounces. fix: in + INSTALL.ids, set up a separate powerless gid (tentatively + ``nofiles'') for qmaild and alias. tnx DS and PJG. +19960229 change: qreceipt now uses qqtalk rather than qmail-inject. +19960229 change: qlist now uses qqtalk rather than qmail-inject. +19960229 change: incorporated qmail-setup patch from RN for better + error messages. +19960228 change: added LSPAWN_BREAK in conf-unusual.h; used it in + lspawn.c. configurability requested by PJG. +19960228 portability problem: on several systems, including everything + from DEC, select() on a pipe reader returns 1 if there aren't + any writers yet. pointed out by DS. impact: qmail-send chewed + up lots of CPU time. fix: trigger_set() now opens the pipe for + writing after opening it for reading. also added trynpbg1; on + working systems, no point in wasting the extra fd. +19960228 change: qmail-alias uses .qmail sticky bit for forwardonly. +19960228 change: qlist now sets sticky bit on .qmail file. +19960228 change: un-documented +list. +19960228 portability problem: on HP-UX and possibly other systems, the + supplementary group list does not include the gid. pointed out + by DS. impact: on those systems, tryshsgr could incorrectly set + hasshsgr; this would prevent qmail-send from running. fix: if + tryshsgr sees that getgroups() returns 0, now it actively sets + up a supplementary group list. added chkshsgr to make sure the + setgroups() will work. +19960227 cleanup: eliminated GETSHORT in dns.c in favor of getshort(). +19960227 cleanup: deleted h->len < 3 test from qlist.c:dobody. tnx RN. +19960227 change: replaced ~ with $HOME in INSTALL.mbox. +19960227 change: added note about setgid-mail bits to INSTALL.mbox. +19960227 change: added forward.1. +19960227 change: modified forward to allow multiple addresses. +19960227 change: modified forward to take an entire address, not just a + hostname. +19960227 change: renamed qrelay as forward. +19960227 change: added USEREXT support to qmail-alias. +19960227 change: added -F to sendmail. the need for this was pointed + out by RN. +19960227 change: added 2 bytes of slop in alloc(). +19960227 bug: received_setup() was not allowing space for the final \0. + impact: none; the line length is always between 65 and 75 + characters, which gives at least 45 characters of slop with + existing malloc() implementations. fix: leave space. tnx NH. + note that the bug here is really in fmt_strncpy, which was + written before i was truly free of the curse of libc.a. +19960227 change: added ALIAS_EMPTY in conf-unusual.h; used it in + qmail-alias.c. tnx PJG. +19960227 change: added SPAWN_NUMD in conf-unusual.h; used it in spawn.c. +19960227 change: added conf-unusual.h. +19960227 cleanup: replaced sizeof(short) with 2 in dns.c. +19960227 portability problem: on an Alpha, long is 64 bits. pointed out + by DS. impact: address lookups produced incorrect results on an + Alpha; qmail-makectl and qmail-remote failed. fix: replaced + sizeof(long) with 4 in dns.c. +19960227 portability problem: on an Alpha, bzero() is declared properly + via sys/time.h. impact: couldn't compile on an Alpha. fix: + removed bzero() declaration from select.h. tnx DS. +19960227 portability problem: under SCO, sys/file.h is not protected. + impact: couldn't compile under SCO. fix: include sys/types.h in + lock.c. tnx RN. +19960219 change: added some .qmail-list hints to qlist.1. +19960219 change: added +list support to qmail-alias. +19960215 change: added THANKS. +19960212 bug: foo was not initialized in qrelay.c. impact: depends on + the machine; on some machines, no effect; on other machines, + guaranteed core dump. fix: initialized foo. tnx DS. +19960209 qmail 0.72, beta. +19960209 change: qmail-alias now replaces dot, not slash, with colon. + also, qmeopen() makes sure that .qmail file is S_IFREG; I hope + this doesn't cause portability problems. +19960209 change: added success-reporting procedure to INSTALL. +19960208 change: added VERSION. +19960208 change: added qlist2. +19960208 change: revamped qlist interface. tnx RN. +19960208 change: improved an error message in qlist.c. +19960208 change: added qrelay. added relay section to HOWTO. tnx DS. +19960208 cleanup: included substdio.h in qqtalk.h. +19960207 bug: prioq_delmin() wasn't guaranteeing heap structure on the + last element. impact: scheduled passes could have been delayed, + conceivably as long as half an hour. fix: prioq_delmin() now + checks when it can safely move the last element. +19960207 change: added maildirmake.1, maildir2mbox.1. +19960206 change: revised logo paragraph in THOUGHTS. +19960206 change: replaced nowhere.org with nowhere.mil in examples. + nowhere.org is a real domain... [sigh] +19960206 change: added qreceipt.1. +19960206 portability problem: IRIX doesn't have vfork. pointed out by + DS. impact: couldn't compile under IRIX. fix: added fork.h, + tryvfork.c. +19960206 portability problem: IRIX doesn't have ranlib. pointed out by + DS. impact: couldn't compile under IRIX. fix: added IRIX + section in make-cmds.sh. +19960205 cleanup: removed warning from substdio_copy() documentation; in + fact, substdio_copy() can be used safely on a fed substdio. +19960205 change: added qbiff.1. +19960204 change: implemented localnet. removed relevant paragraph from + THOUGHTS. tnx IS. +19960204 change: in qmail-remote.8, explained the dangers of smarthost. + tnx IS. +19960204 change: implemented virtualdomains wildcards. tnx JLH. +19960203 change: qmail-send now handles virtualdomains _after_ locals. + updated INSTALL.qsmhook appropriately. +19960203 change: added note to INSTALL.alias about ~ftp, ~www, ~uucp + being owned by root. +19960130 cleanup: in qlist.c, renamed flagremoved as flagwasthere. +19960130 bug: qmail-send did not pay attention to flagexitasap in + pass_dochan(). impact: qmail-send would happily start new + deliveries even if it wanted to exit. fix: qmail-send now + returns immediately in pass_selprep() and pass_dochan() if + flagexitasap. +19960130 change: in qlist.c and qlist.1, renamed ext as list. +19960130 change: in qlist.c and qlist.1, renamed manager as owner. +19960129 qmail 0.71, beta. +19960129 change: mentioned djb-qmailbeta in README. tnx MWE. +19960129 change: added a note to INSTALL.mbox making clear that + Mailbox is in mbox format. tnx MWE. +19960129 change: qlist now warns you if it didn't see any cmds. tnx RN. +19960129 change: incorporated qlist patch from RN to refuse double subs. +19960129 change: added qlist.1, contributed by RN. mangled it a bit. +19960129 bug: comment was not allowed in ``phrase (comment) ''; + pointed out by RN. impact: some correct address lists could be + mis-parsed by qmail-inject or qlist. fix: token.c now allows + TOKEN_COMMENT in the appropriate scan. +19960128 change: added a logo paragraph to THOUGHTS. +19960127 change: implemented rcpthosts. +19960127 change: split up some uses of putflush in qmail-remote, + qmail-smtpd, spawn.c. eliminated NODELAY and corresponding + paragraph in THOUGHTS. +19960127 change: added quote2(). used it in qmail-alias, qmail-send, + qreceipt. now addresses are properly quoted in the From, To, + and internal Return-Path of bounces; the From and To of + receipts; and the Return-Path/RPLINE of delivered messages. + removed relevant paragraph from THOUGHTS. +19960127 change: in RFCLOOPS, documented fact that Delivered-To address + is conventionally not quoted. +19960127 change: knocked default SMTP timeouts down to 20 minutes. +19960127 change: added INSTALL.ids. tnx RN. +19960127 change: in INSTALL, noted that nogroup should already exist. +19960127 bug: pass_selprep checked pqchan[c] even if pass[c].id. impact: + qmail-send wasted CPU time whenever more than one message was + waiting on a blocked channel. fix: pass_selprep now checks + !pass[c].id. +19960127 bug: programs invoked from qmail-alias were immune to SIGPIPE. + impact: a delivery pipeline such as |yes|head -1000 would loop + forever, since yes does not check for write errors. fix: added + signal_uninit(). used it before execvp in qmail-alias. [sigh] +19960127 cleanup: added date822fmt.c. used it in newfield.c, qmail-queue. +19960127 cleanup: added fmt_uint0.c. used it in myctime.c, newfield.c, + qmail-queue. +19960127 cleanup: added dnsdoe.c. used it in dnscname, dnsfq, dnsip, + dnsmxip, dnsptr. +19960127 cleanup: eliminated temp from dnsfq.c. +19960127 bug: gen_allocdefs was making assumptions incompatible with the + alloc_re interface. impact: qmail-send would dump core if you + ran out of memory. fix: changed alloc_re interface. +19960126 portability problem: some versions of Linux don't have + net/route.h. pointed out by RN. impact: couldn't compile under + those versions. fix: ipme.c no longer includes net/route.h; + hopefully this won't cause new portability problems. +19960126 change: added chmod instructions to INSTALL and INSTALL.alias. + tnx RN. +19960126 change: INSTALL now refers to the traditional sendmail spot + (/usr/lib), not the BSD 4.4 spot (/usr/sbin). tnx RN. +19960126 change: make auto-uids.h now creates auto-uids.h.tmp first. + thus, if someone disobeys the installation instructions, and + his make fails to remove targets upon error, he'll still be + okay. tnx RN. +19960126 change: added forgeries.7. +19960125 cleanup: eliminated flagverbose, flagmetoo in sendmail. +19960125 cleanup: added substdio_copy.c. used it at several spots. +19960125 cleanup: added constmap.c. qmail-send now uses constmap for + locals and virtualdomains. this will speed things up: no + problem now to have thousands of virtual domains. removed + relevant paragraph from THOUGHTS. +19960125 change: added linux section in find-systype. this will affect + linux-* systypes. tnx RN for relevant info. +19960124 change: added -od, -oe, -p, -f to sendmail. the need for + these was pointed out by TN. +19960124 bug: qmail-smtpd was reading from descriptor 1. impact: none; + in normal use, both 0 and 1 point to the network. fix: changed + 1 to 0. +19960124 bug: qmail-alias treated any .qmail open failure as permanent. + impact: if a .qmail file was temporarily unopenable (e.g., + because of NFS), it was incorrectly ignored. fix: qmail-alias + now dies QLX_SOFT on any open failure other than ENOENT. +19960124 change: added freebsd section in find-systype, same as bsd.os + section. this will affect freebsd-* systypes. +19960124 cleanup: find-systype now immediately converts sys to lowercase. +19960124 change: qmail-setup now copies man pages into /var/qmail/man; + qmail-check checks /var/qmail/man. using .0 style, which might + cause trouble on various machines, but better than not trying. +19960124 change: in qmail-remote.c, changed perm_control to temp_control + (and D to Z, thanks); thus failure to read control files (e.g., + because of permissions) is now a temporary error. +19960124 bug: in qmail-remote.c, temp_chdir() used D, not Z. impact: if + chdir() to CONF_HOME failed (e.g., because of NFS), message + would be bounced. fix: changed D to Z. +19960124 change: reorganized README. +19960124 portability problem: Linux has the fifo kernel bug that I had + hoped I'd never run into. impact: messages under Linux (and any + other systems with this bug) were picked up only in sweeps, not + instantly. fix: triggerpull.c now writes a byte (non-blocking) + to the fifo. updated INTERNALS accordingly. +19960124 bug: in qmail-remote.c, if quit() saw a remote write error, it + would call writeerr() even though a message report had already + been produced. impact: the mess report would include an extra + ``ZConnected but communications failed,'' which was confusing + to humans. fix: quit() now simply skips the wait-for-QUIT + smtpcode() upon write error. +19960124 portability problem: Linux does not have SIGSYS or SIGEMT. + impact: couldn't compile under Linux. fix: added appropriate + ifdefs in signal.c. +19960124 qmail 0.70, beta. diff --git a/COPYRIGHT b/COPYRIGHT new file mode 100644 index 0000000..3f36a91 --- /dev/null +++ b/COPYRIGHT @@ -0,0 +1,33 @@ +netqmail-1.04 +------------- +No copyright is claimed by the distributors of netqmail for changes from +qmail 1.03 to netqmail 1.04. +NOTE: netqmail 1.04 is a community-assembled distribution of qmail from +the official qmail-1.03.tar.gz and patches approved by the community. +D. J. Bernstein did not participate in, nor has he been asked to approve +of this distribution. + +netqmail-1.05 +------------- +James Craig Burley claims copyright on the qmail-isoc patch. See the file + + old-patches/qmail-isoc.patch + +for details on James' copyright claim and distribution license. + +James' patch has been combined with the original netqmail-1.04 patch +and the result incorporated into a unified netqmail-1.05 patch. + +Apart from James' copyrights, no other copyright is claimed by the +distributors of netqmail for changes from qmail 1.03 to netqmail 1.05. + +NOTE: netqmail 1.05 is a community-assembled distribution of qmail from +the official qmail-1.03.tar.gz and patches approved by the community. +D. J. Bernstein did not participate in, nor has he been asked to approve +of this distribution. + +netqmail-1.06 +------------- +The same copyright information as netqmail-1.05 applies to netqmail-1.06 +with the addition of D. J. Bernstein's dedication of qmail to the public +domain. diff --git a/FAQ b/FAQ new file mode 100644 index 0000000..ab24b64 --- /dev/null +++ b/FAQ @@ -0,0 +1,709 @@ +See http://cr.yp.to/qmail/faq.html for newer FAQs not included in this +document, and http://qmail.org/ for qmail community contributions. + +1. Controlling the appearance of outgoing messages +1.1. How do I set up host masquerading? +1.2. How do I set up user masquerading? +1.3. How do I set up Mail-Followup-To automatically? + +2. Routing outgoing messages +2.1. How do I send local messages to another host? +2.2. How do I set up a null client? +2.3. How do I send outgoing mail through UUCP? +2.4. How do I set up a separate queue for a SLIP/PPP link? +2.5. How do I deal with ``CNAME lookup failed temporarily''? + +3. Routing incoming messages by host +3.1. How do I receive mail for another host name? +3.2. How do I set up a virtual domain? +3.3. How do I set up several virtual domains for one user? + +4. Routing incoming messages by user +4.1. How do I forward unrecognized usernames to another host? +4.2. How do I set up a mailing list? +4.3. How do I use majordomo with qmail? +4.4. How do I use procmail with qmail? +4.5. How do I use elm's filter with qmail? +4.6. How do I create aliases with dots? +4.7. How do I use sendmail's .forward files with qmail? +4.8. How do I use sendmail's /etc/aliases with qmail? +4.9. How do I make qmail defer messages during NFS or NIS outages? +4.10. How do I change which account controls an address? + +5. Setting up servers +5.1. How do I run qmail-smtpd under tcpserver? +5.2. How do I set up qmail-qmtpd? +5.3. How do I set up qmail-pop3d? +5.4. How do I allow selected clients to use this host as a relay? +5.5. How do I fix up messages from broken SMTP clients? +5.6. How do I set up qmail-qmqpd? + +6. Configuring MUAs to work with qmail +6.1. How do I make BSD mail generate a Date with the local time zone? +6.2. How do I make pine work with qmail? +6.3. How do I make MH work with qmail? +6.4. How do I stop Sun's dtcm from hanging? + +7. Managing the mail system +7.1. How do I safely stop qmail-send? +7.2. How do I manually run the queue? +7.3. How do I rejuvenate a message? +7.4. How do I organize a big network? +7.5. How do I back up and restore the queue disk? +7.6. How do I run a supervised copy of qmail? +7.7. How do I avoid syslog? + +8. Miscellany +8.1. How do I tell qmail to do more deliveries at once? +8.2. How do I keep a copy of all incoming and outgoing mail messages? +8.3. How do I switch slowly from sendmail to qmail? + + + +1. Controlling the appearance of outgoing messages + + +1.1. How do I set up host masquerading? All the users on this host, +zippy.af.mil, are users on af.mil. When joe sends a message to fred, the +message should say ``From: joe@af.mil'' and ``To: fred@af.mil'', without +``zippy'' anywhere. + +Answer: echo af.mil > /var/qmail/control/defaulthost; chmod 644 +/var/qmail/control/defaulthost. + + +1.2. How do I set up user masquerading? I'd like my own From lines to +show boss@af.mil rather than god@heaven.af.mil. + +Answer: Add MAILHOST=af.mil and MAILUSER=boss to your environment. To +override From lines supplied by your MUA, add QMAILINJECT=f to your +environment. + + +1.3. How do I set up Mail-Followup-To automatically? When I send a +message to the sos@heaven.af.mil mailing list, I'd like to include +``Mail-Followup-To: sos@heaven.af.mil''. + +Answer: Add QMAILMFTFILE=$HOME/.lists to your environment, and put +sos@heaven.af.mil into ~/.lists. + + + +2. Routing outgoing messages + + +2.1. How do I send local messages to another host? All the mail for +af.mil should be delivered to our disk server, pokey.af.mil. I've set up +an MX from af.mil to pokey.af.mil, but when a user on the af.mil host +sends a message to boss@af.mil, af.mil tries to deliver it locally. How +do I stop that? + +Answer: Remove af.mil from /var/qmail/control/locals. If qmail-send is +running, give it a HUP. Make sure the MX is set up properly before you +do this. Also make sure that pokey can receive mail for af.mil---see +question 3.1. + + +2.2. How do I set up a null client? I'd like zippy.af.mil to +send all mail to bigbang.af.mil. + +Answer: echo :bigbang.af.mil > /var/qmail/control/smtproutes; +chmod 644 /var/qmail/control/smtproutes. Disable local delivery as in +question 2.1. Turn off qmail-smtpd in /etc/inetd.conf. + + +2.3. How do I send outgoing mail through UUCP? I need qmail to send all +outgoing mail via UUCP to my upstream UUCP site, gonzo. + +Answer: Put + + :alias-uucp + +into control/virtualdomains and + + |preline -df /usr/bin/uux - -r -gC + -a"${SENDER:-MAILER-DAEMON}" gonzo!rmail "($DEFAULT@$HOST)" + +(all on one line) into ~alias/.qmail-uucp-default. (For some UUCP +software you will need to use -d instead of -df.) If qmail-send is +running, give it a HUP. + + +2.4. How do I set up a separate queue for a SLIP/PPP link? + +Answer: Use serialmail (http://pobox.com/~djb/serialmail.html). + + +2.5. How do I deal with ``CNAME lookup failed temporarily''? The log +showed that a message was deferred for this reason. Why is qmail doing +CNAME lookups, anyway? + +Answer: The SMTP standard does not permit aliased hostnames, so qmail +has to do a CNAME lookup in DNS for every recipient host. If the +relevant DNS server is down, qmail defers the message. It will try again +soon. + + + +3. Routing incoming messages by host + + +3.1. How do I receive mail for another host name? I'd like our disk +server, pokey.af.mil, to receive mail addressed to af.mil. I've set up +an MX from af.mil to pokey.af.mil, but how do I get pokey to treat +af.mil as a name for the local host? + +Answer: Add af.mil to /var/qmail/control/locals and to +/var/qmail/control/rcpthosts. If qmail-send is running, give it a HUP +(or do svc -h /var/run/qmail if qmail is supervised). + + +3.2. How do I set up a virtual domain? I'd like any mail for +nowhere.mil, including root@nowhere.mil and postmaster@nowhere.mil and +so on, to be delivered to Bob. I've set up the MX already. + +Answer: Put + + nowhere.mil:bob + +into control/virtualdomains. Add nowhere.mil to control/rcpthosts. If +qmail-send is running, give it a HUP (or do svc -h /var/run/qmail if +qmail is supervised). + +Now mail for whatever@nowhere.mil will be delivered locally to +bob-whatever. Bob can set up ~bob/.qmail-default to catch all the +possible addresses, ~bob/.qmail-info to catch info@nowhere.mil, etc. + + +3.3. How do I set up several virtual domains for one user? Bob wants +another virtual domain, everywhere.org, but he wants to handle +nowhere.mil users and everywhere.org users differently. How can we do +that without setting up a second account? + +Answer: Put two lines into control/virtualdomains: + + nowhere.mil:bob-nowhere + everywhere.org:bob-everywhere + +Add nowhere.mil and everywhere.org to control/rcpthosts. If qmail-send +is running, give it a HUP (or do svc -h /var/run/qmail if qmail is +supervised). + +Now Bob can set up separate .qmail-nowhere-* and everywhere-* files. He +can even set up .qmail-nowhere-default and .qmail-everywhere-default. + + + +4. Routing incoming messages by user + + +4.1. How do I forward unrecognized usernames to another host? I'd like +to set up a LUSER_RELAY pointing at bigbang.af.mil. + +Answer: Put + + | forward "$LOCAL"@bigbang.af.mil + +into ~alias/.qmail-default. + + +4.2. How do I set up a mailing list? I'd like me-sos@my.host.name to be +forwarded to a bunch of people. + +Answer: Put a list of addresses into ~me/.qmail-sos, one per line. Then +incoming mail for me-sos will be forwarded to each of those addresses. +You should also touch ~me/.qmail-sos-owner so that bounces come back to +you rather than the original sender. + +Alternative: ezmlm (http://pobox.com/~djb/ezmlm.html) is a modern +mailing list manager, supporting automatic subscriptions, confirmations, +archives, fully automatic bounce handling (including warnings to +subscribers saying which messages they've missed), and more. + + +4.3. How do I use majordomo with qmail? + +Answer: See ftp://ftp.eyrie.org/pub/software/majordomo/mjqmail and +http://www.qmail.org for various methods. majordomo 2.0 is expected to +support qmail directly. + +Beware that majordomo's lists are not crashproof. + + + +4.4. How do I use procmail with qmail? + +Answer: Put + + | preline procmail + +into ~/.qmail. You'll have to use a full path for procmail unless +procmail is in the system's startup PATH. Note that procmail will try to +deliver to /var/spool/mail/$USER by default; to change this, see +INSTALL.mbox. + + +4.5. How do I use elm's filter with qmail? + +Answer: Put + + | preline filter + +into ~/.qmail. You'll have to use a full path for filter unless filter +is in the system's startup PATH. + + +4.6. How do I create aliases with dots? I tried setting up +~alias/.qmail-P.D.Q.Bach, but it doesn't do anything. + +Answer: Use .qmail-p:d:q:bach. Dots are converted to colons, and +uppercase is converted to lowercase. + + +4.7. How do I use sendmail's .forward files with qmail? + +Answer: Install the dot-forward package +(http://pobox.com/~djb/dot-forward.html). + + +4.8. How do I use sendmail's /etc/aliases with qmail? + +Answer: Install the fastforward package +(http://pobox.com/~djb/fastforward.html). + + +4.9. How do I make qmail defer messages during NFS or NIS outages? If +~joe suddenly disappears, I'd like mail for joe to be deferred. + +Answer: Build a qmail-users database, so that qmail no longer checks +home directories and the password database. This takes three steps. +First, put your complete user list (including local and NIS passwords) +into /var/qmail/users/passwd. Second, run + + # qmail-pw2u -h < /var/qmail/users/passwd > /var/qmail/users/assign + +Here -h means that every user must have a home directory; if you happen +to run qmail-pw2u during an NFS outage, it will print an error message +and stop. Third, run + + # qmail-newu + +Make sure to rebuild the database whenever you change your user list. + + +4.10. How do I change which account controls an address? I set up +~alias/.qmail-www, but qmail is looking at ~www/.qmail instead. + +Answer: If you do + + # chown root ~www + +then qmail will no longer consider www to be a user; see qmail-getpw.0. +For more precise control over address assignments, see qmail-users.0. + + + +5. Setting up servers + + +5.1. How do I run qmail-smtpd under tcpserver? inetd is barfing at high +loads, cutting off service for ten-minute stretches. I'd also like +better connection logging. + +Answer: First, install the tcpserver program, part of the ucspi-tcp +package (http://pobox.com/~djb/ucspi-tcp.html). Second, remove the smtp +line from /etc/inetd.conf, and put the line + + tcpserver -u 7770 -g 2108 0 smtp /var/qmail/bin/qmail-smtpd & + +into your system startup files. Replace 7770 with your qmaild uid, and +replace 2108 with your nofiles gid. Don't forget the &. The change will +take effect at your next reboot. + +By default, tcpserver allows at most 40 simultaneous qmail-smtpd +processes. To raise this limit to 400, use tcpserver -c 400. To keep +track of who's connecting and for how long, run (on two lines) + + tcpserver -v -u 7770 -g 2108 0 smtp /var/qmail/bin/qmail-smtpd \ + 2>&1 | /var/qmail/bin/splogger smtpd 3 & + + +5.2. How do I set up qmail-qmtpd? + +Answer: Two steps. First, put a + + qmtp 209/tcp + +line into /etc/services. Second, put (all on one line) + + qmtp stream tcp nowait qmaild + /var/qmail/bin/tcp-env tcp-env /var/qmail/bin/qmail-qmtpd + +into /etc/inetd.conf, and give inetd a HUP. + +If you have tcpserver installed, skip the inetd step, and set up + + tcpserver -u 7770 -g 2108 0 qmtp /var/qmail/bin/qmail-qmtpd & + +replacing 7770 and 2108 with the qmaild uid and nofiles gid. See +question 5.1 for more details on tcpserver. + + +5.3. How do I set up qmail-pop3d? My old POP server works with mbox +delivery; I'd like to switch to maildir delivery. + +Answer: Four steps. First, install the checkpassword program +(http://pobox.com/~djb/checkpwd.html). Second, make sure you have a + + pop3 110/tcp + +line in /etc/services. Third, put (all on one line, including +qmail-popup twice) + + pop3 stream tcp nowait root + /var/qmail/bin/qmail-popup qmail-popup + YOURHOST /bin/checkpassword /var/qmail/bin/qmail-pop3d Maildir + +into /etc/inetd.conf, and give inetd a HUP; replace YOURHOST with your +host's fully qualified domain name. Fourth, set up Maildir delivery for +any user who wants to read mail via POP. + +If you have tcpserver installed, skip the inetd step, and set up (on two +lines) + + tcpserver 0 pop3 /var/qmail/bin/qmail-popup YOURHOST \ + /bin/checkpassword /var/qmail/bin/qmail-pop3d Maildir & + +replacing YOURHOST with your host's fully qualified domain name. See +question 5.1 for more details on tcpserver. + +Security note: pop3d should be used only within a secure network; +otherwise an eavesdropper can steal passwords. + + +5.4. How do I allow selected clients to use this host as a relay? I see +that qmail-smtpd rejects messages to any host not listed in +control/rcpthosts. + +Answer: Three steps. First, install tcp-wrappers, available separately, +including hosts_options. Second, change your qmail-smtpd line in +inetd.conf to + + smtp stream tcp nowait qmaild /usr/local/bin/tcpd + /var/qmail/bin/tcp-env /var/qmail/bin/qmail-smtpd + +(all on one line) and give inetd a HUP. Third, in tcpd's hosts.allow, +make a line setting the environment variable RELAYCLIENT to the empty +string for the selected clients: + + tcp-env: 1.2.3.4, 1.2.3.5: setenv = RELAYCLIENT + +Here 1.2.3.4 and 1.2.3.5 are the clients' IP addresses. qmail-smtpd +ignores control/rcpthosts when RELAYCLIENT is set. (It also appends +RELAYCLIENT to each envelope recipient address. See question 5.5 for an +application.) + +Alternative procedure, if you are using tcpserver 0.80 or above: Create +/etc/tcp.smtp containing + + 1.2.3.6:allow,RELAYCLIENT="" + 127.:allow,RELAYCLIENT="" + +to allow clients with IP addresses 1.2.3.6 and 127.*. Run + + tcprules /etc/tcp.smtp.cdb /etc/tcp.smtp.tmp < /etc/tcp.smtp + +Finally, insert + + -x /etc/tcp.smtp.cdb + +after tcpserver in your qmail-smtpd invocation. + + +5.5. How do I fix up messages from broken SMTP clients? + +Answer: Three steps. First, put + + | bouncesaying 'Permission denied' [ "@$HOST" != "@fixme" ] + | qmail-inject -f "$SENDER" -- "$DEFAULT" + +into ~alias/.qmail-fixup-default. Second, put + + fixme:fixup + +into /var/qmail/control/virtualdomains, and give qmail-send a HUP. +Third, follow the procedure in question 5.4, but set RELAYCLIENT to the +string ``@fixme'': + + tcp-env: 1.2.3.6, 1.2.3.7: setenv = RELAYCLIENT @fixme + +Here 1.2.3.6 and 1.2.3.7 are the clients' IP addresses. If you are using +tcpserver instead of inetd and tcpd, put + + 1.2.3.6:allow,RELAYCLIENT="@fixme" + 1.2.3.7:allow,RELAYCLIENT="@fixme" + +into /etc/tcp.smtp, and run tcprules as in question 5.4. + + +5.6. How do I set up qmail-qmqpd? I'd like to allow fast queueing of +outgoing mail from authorized clients. + +Answer: Make sure you have installed tcpserver 0.80 or above. Create +/etc/qmqp.tcp in tcprules format to allow connections from authorized +hosts. For example, if queueing is allowed from 1.2.3.*: + + 1.2.3.:allow + :deny + +Convert /etc/qmqp.tcp to /etc/qmqp.cdb: + + tcprules /etc/qmqp.cdb /etc/qmqp.tmp < /etc/qmqp.tcp + +Finally, set up + + tcpserver -x /etc/qmqp.cdb -u 7770 -g 2108 0 628 /var/qmail/bin/qmail-qmqpd & + +replacing 7770 and 2108 with the qmaild uid and nofiles gid. See +question 5.1 for more details on tcpserver. + + + +6. Configuring MUAs to work with qmail + + +6.1. How do I make BSD mail generate a Date with the local time zone? +When I send mail, I'd rather use the local time zone than GMT, since +some MUAs don't know how to display Date in the receiver's time zone. + +Answer: Put + + set sendmail=/var/qmail/bin/datemail + +into your .mailrc or your system-wide Mail.rc. Beware that BSD mail is +neither secure nor reliable. + + +6.2. How do I make pine work with qmail? + +Answer: Put + + sendmail-path=/usr/lib/sendmail -oem -oi -t + +into /usr/local/lib/pine.conf. (This will work with sendmail too.) +Beware that pine is neither secure nor reliable. + + +6.3. How do I make MH work with qmail? + +Answer: Put + + postproc: /usr/mh/lib/spost + +into each user's .mh_profile. (This will work with sendmail too.) Beware +that MH is neither secure nor reliable. + + +6.4. How do I stop Sun's dtcm from hanging? + +Answer: There is a novice programming error in dtcm, known as ``failure +to close the output side of the pipe in the child.'' Sun has, at the +time of this writing, not yet provided a patch. Sorry. + + + +7. Managing the mail system + + +7.1. How do I safely stop qmail-send? Back when we were running +sendmail, it was always tricky to kill sendmail without risking the loss +of current deliveries; what should I do with qmail-send? + +Answer: Go ahead and kill the qmail-send process. It will shut down +cleanly. Wait for ``exiting'' to show up in the log. To restart qmail, +run /var/qmail/rc the same way it is run from your system boot scripts, +with the proper PATH, resource limits, etc. + +Alternative, if qmail is supervised: svc -t /var/run/qmail. The +supervise process will kill qmail, wait for it to stop, and restart it. +Use -d instead of -t if you don't want qmail to restart automatically; +to manually restart it, use -u. + + +7.2. How do I manually run the queue? I'd like qmail to try delivering +all the remote messages right now. + +Answer: Give the qmail-send process an ALRM. (Do svc -a /var/run/qmail +if qmail is supervised.) + +You may want to run qmail-tcpok first, to guarantee that qmail-remote +will try all addresses. Normally, if an address fails repeatedly, +qmail-remote leaves it alone for an hour. + + +7.3. How do I rejuvenate a message? Somebody broke into Eric's computer +again; it's going to be down for at least another two days. I know Eric +has been expecting an important message---in fact, I see it sitting here +in /var/qmail/queue/mess/15/26902. It's been in the queue for six days; +how can I make sure it isn't bounced tomorrow? + +Answer: Just touch /var/qmail/queue/info/15/26902. (This is the only +form of queue modification that's safe while qmail is running.) + + +7.4. How do I organize a big network? I have a lot of machines, and I +don't know where to start. + +Answer: First, choose the domain name where your users will receive +mail. This is normally the shortest domain name you control. If you are +in charge of *.movie.edu, you can use addresses like joe@movie.edu. + +Second, choose the machine that will know what to do with different +users at movie.edu. Set up a host name in DNS for this machine: + + mailhost.movie.edu IN A 1.2.3.4 + 4.3.2.1.in-addr.arpa IN PTR mailhost.movie.edu + +Here 1.2.3.4 is the IP address of that machine. + +Third, make a list of machines where mail should end up. For example, if +mail for Bob should end up on Bob's workstation, put Bob's workstation +onto the list. For each of these machines, set up a host name in DNS: + + bobshost.movie.edu IN A 1.2.3.7 + 7.3.2.1.in-addr.arpa IN PTR bobshost.movie.edu + +Fourth, install qmail on bobshost.movie.edu. qmail will automatically +configure itself to accept messages for bob@bobshost.movie.edu and +deliver them to ~bob/Mailbox on bobshost. Do the same for the other +machines where mail should end up. + +Fifth, install qmail on mailhost.movie.edu. Put + + movie.edu:alias-movie + +into control/virtualdomains on mailhost. Then forward bob@movie.edu to +bob@bobshost.movie.edu, by putting + + bob@bobshost.movie.edu + +into ~alias/.qmail-movie-bob. Do the same for other users. + +Sixth, put movie.edu into control/rcpthosts on mailhost.movie.edu, so +that mailhost.movie.edu will accept messages for users at movie.edu. + +Seventh, set up an MX record in DNS to deliver movie.edu messages to +mailhost: + + movie.edu IN MX 10 mailhost.movie.edu + +Eighth, on all your machines, put movie.edu into control/defaulthost. + + +7.5. How do I back up and restore the queue disk? + +Answer: You can't. + +One difficulty is that you can't get a consistent snapshot of the queue +while qmail-send is running. Another difficulty is that messages in the +queue must have filenames that match their inode numbers. + +However, the big problem is that backups---even twice-daily backups--- +are far too unreliable for mail. If your disk dies, there will be very +little overlap between the messages saved in the last backup and the +messages that were lost. + +There are several ways to add real reliability to a mail server. Battery +backups will keep your server alive, letting you park the disk to avoid +a head crash, when the power goes out. Solid-state disks have their own +battery backups. RAID boxes let you replace dead disks without losing +any data. + + +7.6. How do I run a supervised copy of qmail? svc sounds useful. + +Answer: Install daemontools (http://pobox.com/~djb/daemontools.html). +Create a /var/run/qmail directory. Change + + /var/qmail/rc + +to + + supervise /var/run/qmail /var/qmail/rc + +in your boot scripts. Make sure that supervise is in the startup PATH. +Now you can use svc to stop or restart qmail, and svstat to check +whether qmail is running. + + +7.7. How do I avoid syslog? It chews up a lot of CPU time and isn't +reliable. + +Answer: Install daemontools (http://pobox.com/~djb/daemontools.html). +Make a /var/log/qmail directory, owned by qmaill, mode 2700. Do + + qmail-start ./Mailbox /usr/local/bin/accustamp \ + | setuser qmaill /usr/local/bin/cyclog /var/log/qmail & + +in /var/qmail/rc. + +If you are logging tcpserver connections, make a /var/log/smtpd +directory, and use cyclog /var/log/smtpd for tcpserver. You shouldn't +run several copies of cyclog with the same log directory. + +By default, cyclog keeps 10 automatically rotated log files, each +containing up to 100KB of log data. To keep 20 files with 1MB each, use +cyclog -s 1000000 -n 20. + + + +8. Miscellany + + +8.1. How do I tell qmail to do more deliveries at once? It's running +only 20 parallel qmail-remote processes. + +Answer: Decide how many deliveries you want to allow at once. Put that +number into control/concurrencyremote. Restart qmail-send as in question +7.1. If your system has resource limits, make sure you set the +descriptors limit to at least double the concurrency plus 5; otherwise +you'll get lots of unnecessary deferrals whenever a big burst of mail +shows up. Note that qmail also imposes a compile-time concurrency limit, +120 by default; this is set in conf-spawn. + + +8.2. How do I keep a copy of all incoming and outgoing mail messages? + +Answer: Set QUEUE_EXTRA to "Tlog\0" and QUEUE_EXTRALEN to 5 in extra.h. +Recompile qmail. Put ./msg-log into ~alias/.qmail-log. + +You can also use QUEUE_EXTRA to, e.g., record the Message-ID of every +message: run + + | awk '/^$/ { exit } /^[mM][eE][sS][sS][aA][gG][eE]-/ { print }' + +from ~alias/.qmail-log. + + +8.3. How do I switch slowly from sendmail to qmail? I'm thinking of +moving the heaven.af.mil network over to qmail, but first I'd like to +give my users a chance to try out qmail without affecting current +sendmail deliveries. We're using NFS. + +Answer: Find a host in your network, say pc.heaven.af.mil, that isn't +running an SMTP server. (If addresses at pc.heaven.af.mil are used, you +should already have an MX pointing pc.heaven.af.mil to your mail hub.) + +Set up a new MX record pointing lists.heaven.af.mil to pc.heaven.af.mil. +Install qmail on pc.heaven.af.mil. Replace pc with lists in the control +files. Make the qmail man pages available on all your machines. + +Now tell your users about qmail. A user can forward joe@heaven.af.mil to +joe@lists.heaven.af.mil to get ~/Mailbox delivery; he can set up .qmail +files; he can start running his own mailing lists @lists.heaven.af.mil. + +When you're ready to turn sendmail off, you can set up pc.heaven.af.mil +as your new mail hub. Add heaven.af.mil to control/locals, and change +the heaven.af.mil MX to point to pc.heaven.af.mil. Make sure you leave +lists.heaven.af.mil in control/locals so that transition addresses will +continue to work. diff --git a/FILES b/FILES new file mode 100644 index 0000000..0f70971 --- /dev/null +++ b/FILES @@ -0,0 +1,434 @@ +BLURB +BLURB2 +BLURB3 +BLURB4 +README +FAQ +INSTALL +INSTALL.alias +INSTALL.ctl +INSTALL.ids +INSTALL.maildir +INSTALL.mbox +INSTALL-1.03 +INSTALL.vsm +REMOVE.sendmail +REMOVE.binmail +TEST.deliver +TEST.receive +UPGRADE +THOUGHTS +TODO +THANKS +CHANGES +SECURITY +INTERNALS +SENDMAIL +PIC.local2alias +PIC.local2ext +PIC.local2local +PIC.local2rem +PIC.local2virt +PIC.nullclient +PIC.relaybad +PIC.relaygood +PIC.rem2local +FILES +VERSION +SYSDEPS +TARGETS +Makefile +BIN.README +BIN.Makefile +idedit.c +conf-break +auto_break.h +conf-spawn +auto_spawn.h +chkspawn.c +conf-split +auto_split.h +conf-patrn +auto_patrn.h +conf-users +conf-groups +auto_uids.h +auto_usera.h +extra.h +addresses.5 +except.1 +bouncesaying.1 +condredirect.1 +dot-qmail.9 +envelopes.5 +forgeries.7 +forward.1 +maildir2mbox.1 +maildirmake.1 +maildirwatch.1 +mailsubj.1 +mbox.5 +preline.1 +qbiff.1 +qmail-clean.8 +qmail-command.8 +qmail-control.9 +qmail-getpw.9 +qmail-header.5 +qmail-inject.8 +qmail-limits.9 +qmail-local.8 +qmail-log.5 +qmail-lspawn.8 +qmail-newmrh.9 +qmail-newu.9 +qmail-pop3d.8 +qmail-popup.8 +qmail-pw2u.9 +qmail-qmqpc.8 +qmail-qmqpd.8 +qmail-qmtpd.8 +qmail-qread.8 +qmail-qstat.8 +qmail-queue.8 +qmail-remote.8 +qmail-rspawn.8 +qmail-send.9 +qmail-showctl.8 +qmail-smtpd.8 +qmail-start.9 +qmail-tcpok.8 +qmail-tcpto.8 +qmail-users.9 +qmail.7 +qreceipt.1 +splogger.8 +tcp-env.1 +config.sh +config-fast.sh +qmail-clean.c +qmail-getpw.c +qmail-inject.c +qmail-local.c +qmail-lspawn.c +qmail-newmrh.c +qmail-newu.c +qmail-pop3d.c +qmail-popup.c +qmail-pw2u.c +qmail-qmqpc.c +qmail-qmqpd.c +qmail-qmtpd.c +qmail-qread.c +qmail-qstat.sh +qmail-queue.c +qmail-remote.c +qmail-rspawn.c +qmail-send.c +qmail-showctl.c +qmail-smtpd.c +qmail-start.c +qmail-tcpok.c +qmail-tcpto.c +spawn.c +dnscname.c +dnsfq.c +dnsip.c +dnsmxip.c +dnsptr.c +hostname.c +ipmeprint.c +tcp-env.c +sendmail.c +qreceipt.c +qsmhook.c +qbiff.c +forward.c +preline.c +predate.c +except.c +bouncesaying.c +condredirect.c +maildirmake.c +maildir2mbox.c +maildirwatch.c +splogger.c +qail.sh +elq.sh +pinq.sh +qmail-upq.sh +datemail.sh +mailsubj.sh +qlx.h +rcpthosts.h +rcpthosts.c +commands.h +commands.c +dnsdoe.h +dnsdoe.c +fmtqfn.h +fmtqfn.c +gfrom.h +gfrom.c +myctime.h +myctime.c +newfield.h +newfield.c +qsutil.h +qsutil.c +readsubdir.h +readsubdir.c +received.h +received.c +tcpto.h +tcpto.c +tcpto_clean.c +trigger.h +trigger.c +triggerpull.h +triggerpull.c +trynpbg1.c +trysyslog.c +conf-cc +conf-ld +home.sh +home+df.sh +proc.sh +proc+df.sh +binm1.sh +binm2.sh +binm3.sh +binm1+df.sh +binm2+df.sh +binm3+df.sh +find-systype.sh +make-compile.sh +make-load.sh +make-makelib.sh +trycpp.c +warn-auto.sh +auto-str.c +auto-int.c +auto-int8.c +auto-gid.c +auto-uid.c +hier.c +install.c +instcheck.c +install-big.c +alloc.3 +alloc.h +alloc.c +alloc_re.c +case.3 +case.h +case_diffb.c +case_diffs.c +case_lowerb.c +case_lowers.c +case_starts.c +cdb.3 +cdb.h +cdb_hash.c +cdb_seek.c +cdb_unpack.c +cdbmake.h +cdbmake_add.c +cdbmake_hash.c +cdbmake_pack.c +cdbmss.h +cdbmss.c +coe.3 +coe.h +coe.c +fd.h +fd_copy.3 +fd_copy.c +fd_move.3 +fd_move.c +fifo_make.3 +fifo.h +fifo.c +trymkffo.c +fork.h1 +fork.h2 +tryvfork.c +now.3 +now.h +now.c +open.h +open_append.c +open_excl.c +open_read.c +open_trunc.c +open_write.c +seek.h +seek_cur.c +seek_end.c +seek_set.c +seek_trunc.c +conf-qmail +auto_qmail.h +qmail.h +qmail.c +gen_alloc.h +gen_allocdefs.h +stralloc.3 +stralloc.h +stralloc_eady.c +stralloc_pend.c +stralloc_copy.c +stralloc_opyb.c +stralloc_opys.c +stralloc_cat.c +stralloc_catb.c +stralloc_cats.c +stralloc_arts.c +strerr.h +strerr_sys.c +strerr_die.c +substdio.h +substdio.c +substdi.c +substdo.c +substdio_copy.c +subfd.h +subfderr.c +subfdouts.c +subfdout.c +subfdins.c +subfdin.c +readwrite.h +exit.h +timeoutconn.h +timeoutconn.c +timeoutread.h +timeoutread.c +timeoutwrite.h +timeoutwrite.c +remoteinfo.h +remoteinfo.c +uint32.h1 +uint32.h2 +tryulong32.c +wait.3 +wait.h +wait_pid.c +wait_nohang.c +trywaitp.c +sig.h +sig_alarm.c +sig_block.c +sig_catch.c +sig_pause.c +sig_pipe.c +sig_child.c +sig_term.c +sig_hup.c +sig_misc.c +sig_bug.c +trysgact.c +trysgprm.c +env.3 +env.h +env.c +envread.c +byte.h +byte_chr.c +byte_copy.c +byte_cr.c +byte_diff.c +byte_rchr.c +byte_zero.c +str.h +str_chr.c +str_cpy.c +str_diff.c +str_diffn.c +str_len.c +str_rchr.c +str_start.c +lock.h +lock_ex.c +lock_exnb.c +lock_un.c +tryflock.c +getln.3 +getln.h +getln.c +getln2.3 +getln2.c +sgetopt.3 +sgetopt.h +sgetopt.c +subgetopt.3 +subgetopt.h +subgetopt.c +error.3 +error_str.3 +error_temp.3 +error.h +error.c +error_str.c +error_temp.c +fmt.h +fmt_str.c +fmt_strn.c +fmt_uint.c +fmt_uint0.c +fmt_ulong.c +scan.h +scan_ulong.c +scan_8long.c +slurpclose.h +slurpclose.c +quote.h +quote.c +hfield.h +hfield.c +headerbody.h +headerbody.c +token822.h +token822.c +control.h +control.c +datetime.3 +datetime.h +datetime.c +datetime_un.c +prioq.h +prioq.c +date822fmt.h +date822fmt.c +dns.h +dns.c +trylsock.c +tryrsolv.c +ip.h +ip.c +ipalloc.h +ipalloc.c +select.h1 +select.h2 +trysysel.c +ndelay.h +ndelay.c +ndelay_off.c +direntry.3 +direntry.h1 +direntry.h2 +trydrent.c +prot.h +prot.c +chkshsgr.c +warn-shsgr +tryshsgr.c +ipme.h +ipme.c +trysalen.c +maildir.5 +maildir.h +maildir.c +tcp-environ.5 +constmap.h +constmap.c diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..b2da6b1 --- /dev/null +++ b/INSTALL @@ -0,0 +1 @@ +See http://lifewithqmail.org/lwq.html diff --git a/INSTALL.alias b/INSTALL.alias new file mode 100644 index 0000000..672365a --- /dev/null +++ b/INSTALL.alias @@ -0,0 +1,40 @@ +qmail lets each user control all addresses of the form user-anything. +Addresses that don't start with a username are controlled by a special +user, alias. Delivery instructions for foo go into ~alias/.qmail-foo; +delivery instructions for user-foo go into ~user/.qmail-foo. See +dot-qmail.0 for the full story. + +qmail doesn't have any built-in support for /etc/aliases. If you have a +big /etc/aliases and you'd like to keep it, install the fastforward +package, available separately. /etc/aliases should already include the +aliases discussed below---Postmaster, MAILER-DAEMON, and root. + +If you don't have a big /etc/aliases, you'll find it easier to use +qmail's native alias mechanism. Here's a checklist of aliases you should +set up right now. + +* Postmaster. You're not an Internet citizen if this address doesn't +work. Simply touch (and chmod 644) ~alias/.qmail-postmaster; any mail +for Postmaster will be delivered to ~alias/Mailbox. + +* MAILER-DAEMON. Not required, but users sometimes respond to bounce +messages. Touch (and chmod 644) ~alias/.qmail-mailer-daemon. + +* root. Under qmail, root never receives mail. Your system may generate +mail messages to root every night; if you don't have an alias for root, +those messages will bounce. (They'll end up double-bouncing to the +postmaster.) Set up an alias for root in ~alias/.qmail-root. .qmail +files are similar to .forward files, but beware that they are strictly +line-oriented---see dot-qmail.0 for details. + +* Other non-user accounts. Under qmail, non-user accounts don't get +mail; ``user'' means a non-root account that owns ~account. Set up +aliases for any non-user accounts that normally receive mail. + +Note that special accounts such as ftp, www, and uucp should always have +home directories owned by root. + +* Default. If you want, you can touch ~alias/.qmail-default to catch +everything else. Beware: this will also catch typos and other addresses +that should probably be bounced instead. It won't catch addresses that +start with a user name---the user can set up his own ~/.qmail-default. diff --git a/INSTALL.ctl b/INSTALL.ctl new file mode 100644 index 0000000..00ce689 --- /dev/null +++ b/INSTALL.ctl @@ -0,0 +1,38 @@ +As you've seen, qmail has essentially no pre-compilation configuration. +You should never have to recompile it unless you want to change the +qmail home directory, usernames, or uids. + +qmail does allow quite a bit of easy post-installation configuration. If +you care how your machine greets other machines via SMTP, for example, +you can put an appropriate line into /var/qmail/control/smtpgreeting. + +But this is all optional---if control/smtpgreeting doesn't exist, qmail +will do something reasonable by default. You shouldn't worry much about +configuration right now. You can always come back and tune things later. + +There's one big exception. You MUST tell qmail your hostname. Just run +the config-fast script: + + # ./config-fast your.full.host.name + +config-fast puts your.full.host.name into control/me. It also puts it +into control/locals and control/rcpthosts, so that qmail will accept +mail for your.full.host.name. + +You can instead use the config script, which looks up your host name in +DNS: + + # ./config + +config also looks up your local IP addresses in DNS to decide which +hosts to accept mail for. + +(Why doesn't qmail do these lookups on the fly? This was a deliberate +design decision. qmail does all its local functions---header rewriting, +checking if a recipient is local, etc.---without talking to the network. +The point is that qmail can continue accepting and delivering local mail +even if your network connection goes down.) + +Next, read through FAQ for information on setting up optional features +like masquerading. If you really want to learn right now what all the +configuration possibilities are, see qmail-control.0. diff --git a/INSTALL.ids b/INSTALL.ids new file mode 100644 index 0000000..a50e10d --- /dev/null +++ b/INSTALL.ids @@ -0,0 +1,72 @@ +Here's how to set up the qmail groups and the qmail users. + +On some systems there are commands that make this easy. Solaris and +Linux: + + # groupadd nofiles + # useradd -g nofiles -d /var/qmail/alias alias + # useradd -g nofiles -d /var/qmail qmaild + # useradd -g nofiles -d /var/qmail qmaill + # useradd -g nofiles -d /var/qmail qmailp + # groupadd qmail + # useradd -g qmail -d /var/qmail qmailq + # useradd -g qmail -d /var/qmail qmailr + # useradd -g qmail -d /var/qmail qmails + +FreeBSD 2.2: + + # pw groupadd nofiles + # pw useradd alias -g nofiles -d /var/qmail/alias -s /nonexistent + # pw useradd qmaild -g nofiles -d /var/qmail -s /nonexistent + # pw useradd qmaill -g nofiles -d /var/qmail -s /nonexistent + # pw useradd qmailp -g nofiles -d /var/qmail -s /nonexistent + # pw groupadd qmail + # pw useradd qmailq -g qmail -d /var/qmail -s /nonexistent + # pw useradd qmailr -g qmail -d /var/qmail -s /nonexistent + # pw useradd qmails -g qmail -d /var/qmail -s /nonexistent + +BSDI 2.0: + + # addgroup nofiles + # adduser -g nofiles -H/var/qmail/alias -G,,, -s/dev/null -P'*' alias + # adduser -g nofiles -H/var/qmail -G,,, -s/dev/null -P'*' qmaild + # adduser -g nofiles -H/var/qmail -G,,, -s/dev/null -P'*' qmaill + # adduser -g nofiles -H/var/qmail -G,,, -s/dev/null -P'*' qmailp + # addgroup qmail + # adduser -g qmail -H/var/qmail -G,,, -s/dev/null -P'*' qmailq + # adduser -g qmail -H/var/qmail -G,,, -s/dev/null -P'*' qmailr + # adduser -g qmail -H/var/qmail -G,,, -s/dev/null -P'*' qmails + +AIX: + + # mkgroup -A nofiles + # mkuser pgrp=nofiles home=/var/qmail/alias shell=/bin/true alias + # mkuser pgrp=nofiles home=/var/qmail shell=/bin/true qmaild + # mkuser pgrp=nofiles home=/var/qmail shell=/bin/true qmaill + # mkuser pgrp=nofiles home=/var/qmail shell=/bin/true qmailp + # mkgroup -A qmail + # mkuser pgrp=qmail home=/var/qmail shell=/bin/true qmailq + # mkuser pgrp=qmail home=/var/qmail shell=/bin/true qmailr + # mkuser pgrp=qmail home=/var/qmail shell=/bin/true qmails + +On other systems, you will have to edit /etc/group and /etc/passwd +manually. First add two new lines to /etc/group, something like + + qmail:*:2107: + nofiles:*:2108: + +where 2107 and 2108 are different from the other gids in /etc/group. +Next (using vipw) add six new lines to /etc/passwd, something like + + alias:*:7790:2108::/var/qmail/alias:/bin/true + qmaild:*:7791:2108::/var/qmail:/bin/true + qmaill:*:7792:2108::/var/qmail:/bin/true + qmailp:*:7793:2108::/var/qmail:/bin/true + qmailq:*:7794:2107::/var/qmail:/bin/true + qmailr:*:7795:2107::/var/qmail:/bin/true + qmails:*:7796:2107::/var/qmail:/bin/true + +where 7790 through 7796 are _new_ uids, 2107 is the qmail gid, and 2108 +is the nofiles gid. Make sure you use the nofiles gid for qmaild, +qmaill, qmailp, and alias, and the qmail gid for qmailq, qmailr, and +qmails. diff --git a/INSTALL.maildir b/INSTALL.maildir new file mode 100644 index 0000000..72373aa --- /dev/null +++ b/INSTALL.maildir @@ -0,0 +1,59 @@ +This file points out some reasons that you might want to switch from +mbox format to a new format, maildir. + + +1. The trouble with mbox + +The mbox format---the format of ~user/Mailbox, understood by BSD Mail +and lots of other MUAs---is inherently unreliable. + +Think about it: what happens if the system crashes while a program is +appending a new message to ~user/Mailbox? The message will be truncated. +Even worse, if it was truncated in the middle of a line, it will end up +being merged with the next message! Sure, the mailer understands that it +wasn't successful, so it'll try delivering the message again later, but +it can't fix your corrupted mbox. + +Other formats, such as mh folders, are just as unreliable. + +qmail supports maildir, a crashproof format for incoming mail messages. +maildir is fast and easy for MUAs to use. Even better, maildir works +wonders over NFS---see below. + +I don't want to cram maildir down people's throats, so it's not the +default. Nevertheless, I encourage you to start asking for maildir +versions of your favorite MUAs, and to switch over to maildir as soon as +you can. + + +2. Sun's Network F_ail_u_re System + +Anyone who tells you that mail can be safely delivered in mbox format +over NFS is pulling your leg---as explained above, mbox format is +inherently unreliable even on a single machine. + +Anyway, NFS is the most unreliable computing environment ever invented, +and qmail doesn't even pretend to support mbox over NFS. + +You should switch to maildir, which works fine over NFS without any +locking. You can safely read your mail over NFS if it's in maildir +format. Any number of machines can deliver mail to you at the same time. +(On the other hand, for efficiency, it's better to get NFS out of the +picture---your mail should be delivered on the server that contains your +home directory.) + +Here's how to set up qmail to use maildir for your incoming mail: + + % maildirmake $HOME/Maildir + % echo ./Maildir/ > ~/.qmail + +Make sure you include the trailing slash on Maildir/. + +The system administrator can set up Maildir as the default for everybody +by creating a maildir in the new-user template directory and replacing +./Mailbox with ./Maildir/ in /var/qmail/rc. + +Until your MUA supports maildir, you'll probably want to convert maildir +format to (gaaack) mbox format. I've supplied a maildir2mbox utility +that does the trick, along with some tiny qail and elq and pinq wrappers +that call maildir2mbox before calling Mail or elm or pine. diff --git a/INSTALL.mbox b/INSTALL.mbox new file mode 100644 index 0000000..93ca16c --- /dev/null +++ b/INSTALL.mbox @@ -0,0 +1,53 @@ +The qmail package includes a local delivery agent, qmail-local, which +provides user-controlled mailing lists, cross-host alias loop detection, +and many other important qmail features. + +There's one important difference between qmail-local and binmail: +qmail-local delivers mail by default into ~user/Mailbox, rather than +/var/spool/mail/user. It uses mbox format, with lockf locking on systems +that don't have flock (HP/UX, Solaris), and flock locking otherwise. + +This file explains how to switch your system to ~user/Mailbox. You +aren't required to do this; for further discussion of /var/spool/mail, +and an explanation of how to continue using binmail for local +deliveries, see INSTALL.vsm. + +The basic procedure for switching to ~user/Mailbox is simple: + + * Move each /var/spool/mail/user to ~user/Mailbox. For safety, do + this in single-user mode. + + * As root, set up a symbolic link from /var/spool/mail/user to + ~user/Mailbox for each user. /var/spool/mail should be mode 1777, + so users will not be able to accidentally remove these links. + +A few mail programs are unable to handle symbolic links, so you will +have to configure them to look at ~user/Mailbox directly: + + * procmail: Change SYSTEM_MBOX in config.h and recompile; or, with + recent versions, define MAILSPOOLHOME in src/authenticate.c. + +An alternative to symbolic links is hlfsd. Consult the documentation for +hlfsd if it is included in your operating system. + +If /var/spool/mail is large, you can gain extra speed by configuring +all your mail software to look at ~user/Mailbox directly: + + * Most MUAs: Put ``setenv MAIL $HOME/Mailbox'' in your system-wide + .cshrc and ``MAIL=$HOME/Mailbox; export MAIL'' in your system-wide + .profile. + + * elm: Change "mailbox" to "Mailbox" around line 388 of newmbox.c and + recompile. (elm looks at $MAIL, but without this change elm will + fail if two users try to read mail simultaneously.) + + * pine: Put ``inbox-path=Mailbox'' in your system-wide pine.conf. + (For pine versions more recent than 3.91, see also FAQ 6.2.) + + * qpopper 2.2: Change /.mail to /Mailbox in pop_dropcopy.c and + recompile with -DHOMEDIRMAIL in CFLAGS. + +Some vendors, in a misguided attempt to solve the security problems of +/var/spool/mail, have made all their mail software setgid mail. After +you move the mailboxes, you can---and, for security, should---remove +those setgid-mail bits. diff --git a/INSTALL.vsm b/INSTALL.vsm new file mode 100644 index 0000000..cf6a6cc --- /dev/null +++ b/INSTALL.vsm @@ -0,0 +1,50 @@ +UNIX has traditionally delivered mail into a central spool directory, +/var/spool/mail. (The original name was /usr/spool/mail; some systems +now use /var/mail.) There are two basic problems with /var/spool/mail: + + * It's slow. On systems with thousands of users, /var/spool/mail has + thousands of entries. A few UNIX systems support fast operations on + large directories, but most don't. + + * It's insecure. Writing code that works safely in a world-writable + directory is not easy. See, for example, CERT advisory 95:02. + +These may not be problems at your site, so you may want to leave your +mailboxes in /var/spool/mail. + +This file explains several ways that you can configure qmail to use +existing /var/spool/mail delivery tools. Please note that I do not vouch +for the security or reliability of any of those tools. + + +1. What to configure + +The qmail system is started from /var/qmail/rc with + + qmail-start ./Mailbox splogger qmail + +The first argument to qmail-start, ./Mailbox, is the default delivery +instruction. You can change it to run a program such as binmail or +procmail. (See dot-qmail.0 for the format of delivery instructions.) + + +2. Using procmail + +You may already have installed procmail for mail filtering. procmail +delivers to /var/spool/mail by default. + +To set up qmail to use procmail, simply copy /var/qmail/boot/proc to +/var/qmail/rc. + +Note that procmail must be in your system's boot PATH; if it isn't, you +will have edit /var/qmail/rc to include the full path. + + +3. Using sendmail's delivery agent + +sendmail uses binmail to deliver to /var/spool/mail. binmail is shipped +with the operating system as /bin/mail or /usr/libexec/mail.local. + +There is some variation in binmail syntax among systems. The most common +interfaces are shown in /var/qmail/boot/binm1, /var/qmail/boot/binm2, +and /var/qmail/boot/binm3. diff --git a/INTERNALS b/INTERNALS new file mode 100644 index 0000000..e668ae8 --- /dev/null +++ b/INTERNALS @@ -0,0 +1,156 @@ +1. Overview + +Here's the data flow in the qmail suite: + + qmail-smtpd --- qmail-queue --- qmail-send --- qmail-rspawn --- qmail-remote + / | \ +qmail-inject _/ qmail-clean \_ qmail-lspawn --- qmail-local + +Every message is added to a central queue directory by qmail-queue. +qmail-queue is invoked as needed, usually by qmail-inject for locally +generated messages, qmail-smtpd for messages received through SMTP, +qmail-local for forwarded messages, or qmail-send for bounce messages. + +Every message is then delivered by qmail-send, in cooperation with +qmail-lspawn and qmail-rspawn, and cleaned up by qmail-clean. These four +programs are long-running daemons. + +The queue is designed to be crashproof, provided that the underlying +filesystem is crashproof. All cleanups are handled by qmail-send and +qmail-clean without human intervention. See section 6 for more details. + + +2. Queue structure + +Each message in the queue is identified by a unique number, let's say +457. The queue is organized into several directories, each of which may +contain files related to message 457: + + mess/457: the message + todo/457: the envelope: where the message came from, where it's going + intd/457: the envelope, under construction by qmail-queue + info/457: the envelope sender address, after preprocessing + local/457: local envelope recipient addresses, after preprocessing + remote/457: remote envelope recipient addresses, after preprocessing + bounce/457: permanent delivery errors + +Here are all possible states for a message. + means a file exists; - +means it does not exist; ? means it may or may not exist. + + S1. -mess -intd -todo -info -local -remote -bounce + S2. +mess -intd -todo -info -local -remote -bounce + S3. +mess +intd -todo -info -local -remote -bounce + S4. +mess ?intd +todo ?info ?local ?remote -bounce (queued) + S5. +mess -intd -todo +info ?local ?remote ?bounce (preprocessed) + +Guarantee: If mess/457 exists, it has inode number 457. + + +3. How messages enter the queue + +To add a message to the queue, qmail-queue first creates a file in a +separate directory, pid/, with a unique name. The filesystem assigns +that file a unique inode number. qmail-queue looks at that number, say +457. By the guarantee above, message 457 must be in state S1. + +qmail-queue renames pid/whatever as mess/457, moving to S2. It writes +the message to mess/457. It then creates intd/457, moving to S3, and +writes the envelope information to intd/457. + +Finally qmail-queue creates a new link, todo/457, for intd/457, moving +to S4. At that instant the message has been successfully queued, and +qmail-queue leaves it for further handling by qmail-send. + +qmail-queue starts a 24-hour timer before touching any files, and +commits suicide if the timer expires. + + +4. How queued messages are preprocessed + +Once a message has been queued, qmail-send must decide which recipients +are local and which recipients are remote. It may also rewrite some +recipient addresses. + +When qmail-send notices todo/457, it knows that message 457 is in S4. It +removes info/457, local/457, and remote/457 if they exist. Then it reads +through todo/457. It creates info/457, possibly local/457, and possibly +remote/457. When it is done, it removes intd/457. The message is still +in S4 at this point. Finally qmail-send removes todo/457, moving to S5. +At that instant the message has been successfully preprocessed. + + +5. How preprocessed messages are delivered + +Messages at S5 are handled as follows. Each address in local/457 and +remote/457 is marked either NOT DONE or DONE. + + DONE: The message was successfully delivered, or the last delivery + attempt met with permanent failure. Either way, qmail-send + should not attempt further delivery to this address. + + NOT DONE: If there have been any delivery attempts, they have all + met with temporary failure. Either way, qmail-send should + try delivery in the future. + +qmail-send may at its leisure try to deliver a message to a NOT DONE +address. If the message is successfully delivered, qmail-send marks the +address as DONE. If the delivery attempt meets with permanent failure, +qmail-send first appends a note to bounce/457, creating bounce/457 if +necessary; then it marks the address as DONE. Note that bounce/457 is +not crashproof. + +qmail-send may handle bounce/457 at any time, as follows: it (1) injects +a new bounce message, created from bounce/457 and mess/457; (2) deletes +bounce/457. + +When all addresses in local/457 are DONE, qmail-send deletes local/457. +Same for remote/457. + +When local/457 and remote/457 are gone, qmail-send eliminates the +message, as follows. First, if bounce/457 exists, qmail-send handles it +as described above. Once bounce/457 is definitely gone, qmail-send +deletes info/457, moving to S2, and finally mess/457, moving to S1. + + +6. Cleanups + +If the computer crashes while qmail-queue is trying to queue a message, +or while qmail-send is eliminating a message, the message may be left in +state S2 or S3. + +When qmail-send sees a message in state S2 or S3---other than one +it is currently eliminating!---where mess/457 is more than 36 hours old, +it deletes intd/457 if that exists, then deletes mess/457. Note that any +qmail-queue handling the message must be dead. + +Similarly, when qmail-send sees a file in the pid/ directory that is +more than 36 hours old, it deletes it. + +Cleanups are not necessary if the computer crashes while qmail-send is +delivering a message. At worst a message may be delivered twice. (There +is no way for a distributed mail system to eliminate the possibility of +duplication. What if an SMTP connection is broken just before the server +acknowledges successful receipt of the message? The client must assume +the worst and send the message again. Similarly, if the computer crashes +just before qmail-send marks a message as DONE, the new qmail-send must +assume the worst and send the message again. The usual solutions in the +database literature---e.g., keeping log files---amount to saying that +it's the recipient's computer's job to discard duplicate messages.) + + +7. Further notes + +Currently info/457 serves two purposes: first, it records the envelope +sender; second, its modification time is used to decide when a message +has been in the queue too long. In the future info/457 may store more +information. Any non-backwards-compatible changes will be identified by +version numbers. + +When qmail-queue has successfully placed a message into the queue, it +pulls a trigger offered by qmail-send. Here is the current triggering +mechanism: lock/trigger is a named pipe. Before scanning todo/, +qmail-send opens lock/trigger O_NDELAY for reading. It then selects for +readability on lock/trigger. qmail-queue pulls the trigger by writing a +byte O_NDELAY to lock/trigger. This makes lock/trigger readable and +wakes up qmail-send. Before scanning todo/ again, qmail-send closes and +reopens lock/trigger. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0f0e31a --- /dev/null +++ b/Makefile @@ -0,0 +1,2141 @@ +# Don't edit Makefile! Use conf-* for configuration. + +SHELL=/bin/sh + +default: it + +addresses.0: \ +addresses.5 + nroff -man addresses.5 > addresses.0 + +alloc.a: \ +makelib alloc.o alloc_re.o + ./makelib alloc.a alloc.o alloc_re.o + +alloc.o: \ +compile alloc.c alloc.h error.h + ./compile alloc.c + +alloc_re.o: \ +compile alloc_re.c alloc.h byte.h + ./compile alloc_re.c + +auto-ccld.sh: \ +conf-cc conf-ld warn-auto.sh + ( cat warn-auto.sh; \ + echo CC=\'`head -1 conf-cc`\'; \ + echo LD=\'`head -1 conf-ld`\' \ + ) > auto-ccld.sh + +auto-gid: \ +load auto-gid.o substdio.a error.a str.a fs.a + ./load auto-gid substdio.a error.a str.a fs.a + +auto-gid.o: \ +compile auto-gid.c subfd.h substdio.h substdio.h readwrite.h exit.h \ +scan.h fmt.h + ./compile auto-gid.c + +auto-int: \ +load auto-int.o substdio.a error.a str.a fs.a + ./load auto-int substdio.a error.a str.a fs.a + +auto-int.o: \ +compile auto-int.c substdio.h readwrite.h exit.h scan.h fmt.h + ./compile auto-int.c + +auto-int8: \ +load auto-int8.o substdio.a error.a str.a fs.a + ./load auto-int8 substdio.a error.a str.a fs.a + +auto-int8.o: \ +compile auto-int8.c substdio.h readwrite.h exit.h scan.h fmt.h + ./compile auto-int8.c + +auto-str: \ +load auto-str.o substdio.a error.a str.a + ./load auto-str substdio.a error.a str.a + +auto-str.o: \ +compile auto-str.c substdio.h readwrite.h exit.h + ./compile auto-str.c + +auto-uid: \ +load auto-uid.o substdio.a error.a str.a fs.a + ./load auto-uid substdio.a error.a str.a fs.a + +auto-uid.o: \ +compile auto-uid.c subfd.h substdio.h substdio.h readwrite.h exit.h \ +scan.h fmt.h + ./compile auto-uid.c + +auto_break.c: \ +auto-str conf-break + ./auto-str auto_break \ + "`head -1 conf-break`" > auto_break.c + +auto_break.o: \ +compile auto_break.c + ./compile auto_break.c + +auto_patrn.c: \ +auto-int8 conf-patrn + ./auto-int8 auto_patrn `head -1 conf-patrn` > auto_patrn.c + +auto_patrn.o: \ +compile auto_patrn.c + ./compile auto_patrn.c + +auto_qmail.c: \ +auto-str conf-qmail + ./auto-str auto_qmail `head -1 conf-qmail` > auto_qmail.c + +auto_qmail.o: \ +compile auto_qmail.c + ./compile auto_qmail.c + +auto_spawn.c: \ +auto-int conf-spawn + ./auto-int auto_spawn `head -1 conf-spawn` > auto_spawn.c + +auto_spawn.o: \ +compile auto_spawn.c + ./compile auto_spawn.c + +auto_split.c: \ +auto-int conf-split + ./auto-int auto_split `head -1 conf-split` > auto_split.c + +auto_split.o: \ +compile auto_split.c + ./compile auto_split.c + +auto_uids.c: \ +auto-uid auto-gid conf-users conf-groups + ( ./auto-uid auto_uida `head -1 conf-users` \ + &&./auto-uid auto_uidd `head -2 conf-users | tail -1` \ + &&./auto-uid auto_uidl `head -3 conf-users | tail -1` \ + &&./auto-uid auto_uido `head -4 conf-users | tail -1` \ + &&./auto-uid auto_uidp `head -5 conf-users | tail -1` \ + &&./auto-uid auto_uidq `head -6 conf-users | tail -1` \ + &&./auto-uid auto_uidr `head -7 conf-users | tail -1` \ + &&./auto-uid auto_uids `head -8 conf-users | tail -1` \ + &&./auto-gid auto_gidq `head -1 conf-groups` \ + &&./auto-gid auto_gidn `head -2 conf-groups | tail -1` \ + ) > auto_uids.c.tmp && mv auto_uids.c.tmp auto_uids.c + +auto_uids.o: \ +compile auto_uids.c + ./compile auto_uids.c + +auto_usera.c: \ +auto-str conf-users + ./auto-str auto_usera `head -1 conf-users` > auto_usera.c + +auto_usera.o: \ +compile auto_usera.c + ./compile auto_usera.c + +binm1: \ +binm1.sh conf-qmail + cat binm1.sh \ + | sed s}QMAIL}"`head -1 conf-qmail`"}g \ + > binm1 + chmod 755 binm1 + +binm1+df: \ +binm1+df.sh conf-qmail + cat binm1+df.sh \ + | sed s}QMAIL}"`head -1 conf-qmail`"}g \ + > binm1+df + chmod 755 binm1+df + +binm2: \ +binm2.sh conf-qmail + cat binm2.sh \ + | sed s}QMAIL}"`head -1 conf-qmail`"}g \ + > binm2 + chmod 755 binm2 + +binm2+df: \ +binm2+df.sh conf-qmail + cat binm2+df.sh \ + | sed s}QMAIL}"`head -1 conf-qmail`"}g \ + > binm2+df + chmod 755 binm2+df + +binm3: \ +binm3.sh conf-qmail + cat binm3.sh \ + | sed s}QMAIL}"`head -1 conf-qmail`"}g \ + > binm3 + chmod 755 binm3 + +binm3+df: \ +binm3+df.sh conf-qmail + cat binm3+df.sh \ + | sed s}QMAIL}"`head -1 conf-qmail`"}g \ + > binm3+df + chmod 755 binm3+df + +bouncesaying: \ +load bouncesaying.o strerr.a error.a substdio.a str.a wait.a + ./load bouncesaying strerr.a error.a substdio.a str.a \ + wait.a + +bouncesaying.0: \ +bouncesaying.1 + nroff -man bouncesaying.1 > bouncesaying.0 + +bouncesaying.o: \ +compile bouncesaying.c fork.h strerr.h error.h wait.h sig.h exit.h + ./compile bouncesaying.c + +byte_chr.o: \ +compile byte_chr.c byte.h + ./compile byte_chr.c + +byte_copy.o: \ +compile byte_copy.c byte.h + ./compile byte_copy.c + +byte_cr.o: \ +compile byte_cr.c byte.h + ./compile byte_cr.c + +byte_diff.o: \ +compile byte_diff.c byte.h + ./compile byte_diff.c + +byte_rchr.o: \ +compile byte_rchr.c byte.h + ./compile byte_rchr.c + +byte_zero.o: \ +compile byte_zero.c byte.h + ./compile byte_zero.c + +case.a: \ +makelib case_diffb.o case_diffs.o case_lowerb.o case_lowers.o \ +case_starts.o + ./makelib case.a case_diffb.o case_diffs.o case_lowerb.o \ + case_lowers.o case_starts.o + +case_diffb.o: \ +compile case_diffb.c case.h + ./compile case_diffb.c + +case_diffs.o: \ +compile case_diffs.c case.h + ./compile case_diffs.c + +case_lowerb.o: \ +compile case_lowerb.c case.h + ./compile case_lowerb.c + +case_lowers.o: \ +compile case_lowers.c case.h + ./compile case_lowers.c + +case_starts.o: \ +compile case_starts.c case.h + ./compile case_starts.c + +cdb.a: \ +makelib cdb_hash.o cdb_unpack.o cdb_seek.o + ./makelib cdb.a cdb_hash.o cdb_unpack.o cdb_seek.o + +cdb_hash.o: \ +compile cdb_hash.c cdb.h uint32.h + ./compile cdb_hash.c + +cdb_seek.o: \ +compile cdb_seek.c cdb.h uint32.h + ./compile cdb_seek.c + +cdb_unpack.o: \ +compile cdb_unpack.c cdb.h uint32.h + ./compile cdb_unpack.c + +cdbmake.a: \ +makelib cdbmake_pack.o cdbmake_hash.o cdbmake_add.o + ./makelib cdbmake.a cdbmake_pack.o cdbmake_hash.o \ + cdbmake_add.o + +cdbmake_add.o: \ +compile cdbmake_add.c cdbmake.h alloc.h uint32.h + ./compile cdbmake_add.c + +cdbmake_hash.o: \ +compile cdbmake_hash.c cdbmake.h uint32.h + ./compile cdbmake_hash.c + +cdbmake_pack.o: \ +compile cdbmake_pack.c cdbmake.h uint32.h + ./compile cdbmake_pack.c + +cdbmss.o: \ +compile cdbmss.c readwrite.h seek.h alloc.h cdbmss.h cdbmake.h \ +uint32.h substdio.h + ./compile cdbmss.c + +check: \ +it man + ./instcheck + +chkshsgr: \ +load chkshsgr.o + ./load chkshsgr + +chkshsgr.o: \ +compile chkshsgr.c exit.h + ./compile chkshsgr.c + +chkspawn: \ +load chkspawn.o substdio.a error.a str.a fs.a auto_spawn.o + ./load chkspawn substdio.a error.a str.a fs.a auto_spawn.o + +chkspawn.o: \ +compile chkspawn.c substdio.h subfd.h substdio.h fmt.h select.h \ +exit.h auto_spawn.h + ./compile chkspawn.c + +clean: \ +TARGETS + rm -f `cat TARGETS` + +coe.o: \ +compile coe.c coe.h + ./compile coe.c + +commands.o: \ +compile commands.c commands.h substdio.h stralloc.h gen_alloc.h str.h \ +case.h + ./compile commands.c + +compile: \ +make-compile warn-auto.sh systype + ( cat warn-auto.sh; ./make-compile "`cat systype`" ) > \ + compile + chmod 755 compile + +condredirect: \ +load condredirect.o qmail.o strerr.a fd.a sig.a wait.a seek.a env.a \ +substdio.a error.a str.a fs.a auto_qmail.o + ./load condredirect qmail.o strerr.a fd.a sig.a wait.a \ + seek.a env.a substdio.a error.a str.a fs.a auto_qmail.o + +condredirect.0: \ +condredirect.1 + nroff -man condredirect.1 > condredirect.0 + +condredirect.o: \ +compile condredirect.c sig.h readwrite.h exit.h env.h error.h fork.h \ +wait.h seek.h qmail.h substdio.h strerr.h substdio.h fmt.h + ./compile condredirect.c + +config: \ +warn-auto.sh config.sh conf-qmail conf-break conf-split + cat warn-auto.sh config.sh \ + | sed s}QMAIL}"`head -1 conf-qmail`"}g \ + | sed s}BREAK}"`head -1 conf-break`"}g \ + | sed s}SPLIT}"`head -1 conf-split`"}g \ + > config + chmod 755 config + +config-fast: \ +warn-auto.sh config-fast.sh conf-qmail conf-break conf-split + cat warn-auto.sh config-fast.sh \ + | sed s}QMAIL}"`head -1 conf-qmail`"}g \ + | sed s}BREAK}"`head -1 conf-break`"}g \ + | sed s}SPLIT}"`head -1 conf-split`"}g \ + > config-fast + chmod 755 config-fast + +constmap.o: \ +compile constmap.c constmap.h alloc.h case.h + ./compile constmap.c + +control.o: \ +compile control.c readwrite.h open.h getln.h stralloc.h gen_alloc.h \ +substdio.h error.h control.h alloc.h scan.h + ./compile control.c + +date822fmt.o: \ +compile date822fmt.c datetime.h fmt.h date822fmt.h + ./compile date822fmt.c + +datemail: \ +warn-auto.sh datemail.sh conf-qmail conf-break conf-split + cat warn-auto.sh datemail.sh \ + | sed s}QMAIL}"`head -1 conf-qmail`"}g \ + | sed s}BREAK}"`head -1 conf-break`"}g \ + | sed s}SPLIT}"`head -1 conf-split`"}g \ + > datemail + chmod 755 datemail + +datetime.a: \ +makelib datetime.o datetime_un.o + ./makelib datetime.a datetime.o datetime_un.o + +datetime.o: \ +compile datetime.c datetime.h + ./compile datetime.c + +datetime_un.o: \ +compile datetime_un.c datetime.h + ./compile datetime_un.c + +direntry.h: \ +compile trydrent.c direntry.h1 direntry.h2 + ( ./compile trydrent.c >/dev/null 2>&1 \ + && cat direntry.h2 || cat direntry.h1 ) > direntry.h + rm -f trydrent.o + +dns.lib: \ +tryrsolv.c compile load socket.lib dns.o ipalloc.o ip.o stralloc.a \ +alloc.a error.a fs.a str.a + ( ( ./compile tryrsolv.c && ./load tryrsolv dns.o \ + ipalloc.o ip.o stralloc.a alloc.a error.a fs.a str.a \ + -lresolv `cat socket.lib` ) >/dev/null 2>&1 \ + && echo -lresolv || exit 0 ) > dns.lib + rm -f tryrsolv.o tryrsolv + +dns.o: \ +compile dns.c ip.h ipalloc.h ip.h gen_alloc.h fmt.h alloc.h str.h \ +stralloc.h gen_alloc.h dns.h case.h + ./compile dns.c + +dnscname: \ +load dnscname.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \ +substdio.a error.a str.a fs.a dns.lib socket.lib + ./load dnscname dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \ + alloc.a substdio.a error.a str.a fs.a `cat dns.lib` `cat \ + socket.lib` + +dnscname.o: \ +compile dnscname.c substdio.h subfd.h substdio.h stralloc.h \ +gen_alloc.h dns.h dnsdoe.h readwrite.h exit.h + ./compile dnscname.c + +dnsdoe.o: \ +compile dnsdoe.c substdio.h subfd.h substdio.h exit.h dns.h dnsdoe.h + ./compile dnsdoe.c + +dnsfq: \ +load dnsfq.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \ +substdio.a error.a str.a fs.a dns.lib socket.lib + ./load dnsfq dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \ + alloc.a substdio.a error.a str.a fs.a `cat dns.lib` `cat \ + socket.lib` + +dnsfq.o: \ +compile dnsfq.c substdio.h subfd.h substdio.h stralloc.h gen_alloc.h \ +dns.h dnsdoe.h ip.h ipalloc.h ip.h gen_alloc.h exit.h + ./compile dnsfq.c + +dnsip: \ +load dnsip.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \ +substdio.a error.a str.a fs.a dns.lib socket.lib + ./load dnsip dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \ + alloc.a substdio.a error.a str.a fs.a `cat dns.lib` `cat \ + socket.lib` + +dnsip.o: \ +compile dnsip.c substdio.h subfd.h substdio.h stralloc.h gen_alloc.h \ +dns.h dnsdoe.h ip.h ipalloc.h ip.h gen_alloc.h exit.h + ./compile dnsip.c + +dnsmxip: \ +load dnsmxip.o dns.o dnsdoe.o ip.o ipalloc.o now.o stralloc.a alloc.a \ +substdio.a error.a str.a fs.a dns.lib socket.lib + ./load dnsmxip dns.o dnsdoe.o ip.o ipalloc.o now.o \ + stralloc.a alloc.a substdio.a error.a str.a fs.a `cat \ + dns.lib` `cat socket.lib` + +dnsmxip.o: \ +compile dnsmxip.c substdio.h subfd.h substdio.h stralloc.h \ +gen_alloc.h fmt.h dns.h dnsdoe.h ip.h ipalloc.h ip.h gen_alloc.h \ +now.h datetime.h exit.h + ./compile dnsmxip.c + +dnsptr: \ +load dnsptr.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \ +substdio.a error.a str.a fs.a dns.lib socket.lib + ./load dnsptr dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \ + alloc.a substdio.a error.a str.a fs.a `cat dns.lib` `cat \ + socket.lib` + +dnsptr.o: \ +compile dnsptr.c substdio.h subfd.h substdio.h stralloc.h gen_alloc.h \ +str.h scan.h dns.h dnsdoe.h ip.h exit.h + ./compile dnsptr.c + +dot-qmail.0: \ +dot-qmail.5 + nroff -man dot-qmail.5 > dot-qmail.0 + +dot-qmail.5: \ +dot-qmail.9 conf-break conf-spawn + cat dot-qmail.9 \ + | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \ + | sed s}BREAK}"`head -1 conf-break`"}g \ + | sed s}SPAWN}"`head -1 conf-spawn`"}g \ + > dot-qmail.5 + +elq: \ +warn-auto.sh elq.sh conf-qmail conf-break conf-split + cat warn-auto.sh elq.sh \ + | sed s}QMAIL}"`head -1 conf-qmail`"}g \ + | sed s}BREAK}"`head -1 conf-break`"}g \ + | sed s}SPLIT}"`head -1 conf-split`"}g \ + > elq + chmod 755 elq + +env.a: \ +makelib env.o envread.o + ./makelib env.a env.o envread.o + +env.o: \ +compile env.c str.h alloc.h env.h + ./compile env.c + +envelopes.0: \ +envelopes.5 + nroff -man envelopes.5 > envelopes.0 + +envread.o: \ +compile envread.c env.h str.h + ./compile envread.c + +error.a: \ +makelib error.o error_str.o error_temp.o + ./makelib error.a error.o error_str.o error_temp.o + +error.o: \ +compile error.c error.h + ./compile error.c + +error_str.o: \ +compile error_str.c error.h + ./compile error_str.c + +error_temp.o: \ +compile error_temp.c error.h + ./compile error_temp.c + +except: \ +load except.o strerr.a error.a substdio.a str.a wait.a + ./load except strerr.a error.a substdio.a str.a wait.a + +except.0: \ +except.1 + nroff -man except.1 > except.0 + +except.o: \ +compile except.c fork.h strerr.h wait.h error.h exit.h + ./compile except.c + +fd.a: \ +makelib fd_copy.o fd_move.o + ./makelib fd.a fd_copy.o fd_move.o + +fd_copy.o: \ +compile fd_copy.c fd.h + ./compile fd_copy.c + +fd_move.o: \ +compile fd_move.c fd.h + ./compile fd_move.c + +fifo.o: \ +compile fifo.c hasmkffo.h fifo.h + ./compile fifo.c + +find-systype: \ +find-systype.sh auto-ccld.sh + cat auto-ccld.sh find-systype.sh > find-systype + chmod 755 find-systype + +fmt_str.o: \ +compile fmt_str.c fmt.h + ./compile fmt_str.c + +fmt_strn.o: \ +compile fmt_strn.c fmt.h + ./compile fmt_strn.c + +fmt_uint.o: \ +compile fmt_uint.c fmt.h + ./compile fmt_uint.c + +fmt_uint0.o: \ +compile fmt_uint0.c fmt.h + ./compile fmt_uint0.c + +fmt_ulong.o: \ +compile fmt_ulong.c fmt.h + ./compile fmt_ulong.c + +fmtqfn.o: \ +compile fmtqfn.c fmtqfn.h fmt.h auto_split.h + ./compile fmtqfn.c + +forgeries.0: \ +forgeries.7 + nroff -man forgeries.7 > forgeries.0 + +fork.h: \ +compile load tryvfork.c fork.h1 fork.h2 + ( ( ./compile tryvfork.c && ./load tryvfork ) >/dev/null \ + 2>&1 \ + && cat fork.h2 || cat fork.h1 ) > fork.h + rm -f tryvfork.o tryvfork + +forward: \ +load forward.o qmail.o strerr.a alloc.a fd.a wait.a sig.a env.a \ +substdio.a error.a str.a fs.a auto_qmail.o + ./load forward qmail.o strerr.a alloc.a fd.a wait.a sig.a \ + env.a substdio.a error.a str.a fs.a auto_qmail.o + +forward.0: \ +forward.1 + nroff -man forward.1 > forward.0 + +forward.o: \ +compile forward.c sig.h readwrite.h exit.h env.h qmail.h substdio.h \ +strerr.h substdio.h fmt.h + ./compile forward.c + +fs.a: \ +makelib fmt_str.o fmt_strn.o fmt_uint.o fmt_uint0.o fmt_ulong.o \ +scan_ulong.o scan_8long.o + ./makelib fs.a fmt_str.o fmt_strn.o fmt_uint.o fmt_uint0.o \ + fmt_ulong.o scan_ulong.o scan_8long.o + +getln.a: \ +makelib getln.o getln2.o + ./makelib getln.a getln.o getln2.o + +getln.o: \ +compile getln.c substdio.h byte.h stralloc.h gen_alloc.h getln.h + ./compile getln.c + +getln2.o: \ +compile getln2.c substdio.h stralloc.h gen_alloc.h byte.h getln.h + ./compile getln2.c + +getopt.a: \ +makelib subgetopt.o sgetopt.o + ./makelib getopt.a subgetopt.o sgetopt.o + +gfrom.o: \ +compile gfrom.c str.h gfrom.h + ./compile gfrom.c + +hasflock.h: \ +tryflock.c compile load + ( ( ./compile tryflock.c && ./load tryflock ) >/dev/null \ + 2>&1 \ + && echo \#define HASFLOCK 1 || exit 0 ) > hasflock.h + rm -f tryflock.o tryflock + +hasmkffo.h: \ +trymkffo.c compile load + ( ( ./compile trymkffo.c && ./load trymkffo ) >/dev/null \ + 2>&1 \ + && echo \#define HASMKFIFO 1 || exit 0 ) > hasmkffo.h + rm -f trymkffo.o trymkffo + +hasnpbg1.h: \ +trynpbg1.c compile load open.h open.a fifo.h fifo.o select.h + ( ( ./compile trynpbg1.c \ + && ./load trynpbg1 fifo.o open.a && ./trynpbg1 ) \ + >/dev/null 2>&1 \ + && echo \#define HASNAMEDPIPEBUG1 1 || exit 0 ) > \ + hasnpbg1.h + rm -f trynpbg1.o trynpbg1 + +hassalen.h: \ +trysalen.c compile + ( ./compile trysalen.c >/dev/null 2>&1 \ + && echo \#define HASSALEN 1 || exit 0 ) > hassalen.h + rm -f trysalen.o + +hassgact.h: \ +trysgact.c compile load + ( ( ./compile trysgact.c && ./load trysgact ) >/dev/null \ + 2>&1 \ + && echo \#define HASSIGACTION 1 || exit 0 ) > hassgact.h + rm -f trysgact.o trysgact + +hassgprm.h: \ +trysgprm.c compile load + ( ( ./compile trysgprm.c && ./load trysgprm ) >/dev/null \ + 2>&1 \ + && echo \#define HASSIGPROCMASK 1 || exit 0 ) > hassgprm.h + rm -f trysgprm.o trysgprm + +hasshsgr.h: \ +chkshsgr warn-shsgr tryshsgr.c compile load + ./chkshsgr || ( cat warn-shsgr; exit 1 ) + ( ( ./compile tryshsgr.c \ + && ./load tryshsgr && ./tryshsgr ) >/dev/null 2>&1 \ + && echo \#define HASSHORTSETGROUPS 1 || exit 0 ) > \ + hasshsgr.h + rm -f tryshsgr.o tryshsgr + +haswaitp.h: \ +trywaitp.c compile load + ( ( ./compile trywaitp.c && ./load trywaitp ) >/dev/null \ + 2>&1 \ + && echo \#define HASWAITPID 1 || exit 0 ) > haswaitp.h + rm -f trywaitp.o trywaitp + +headerbody.o: \ +compile headerbody.c stralloc.h gen_alloc.h substdio.h getln.h \ +hfield.h headerbody.h + ./compile headerbody.c + +hfield.o: \ +compile hfield.c hfield.h + ./compile hfield.c + +hier.o: \ +compile hier.c auto_qmail.h auto_split.h auto_uids.h fmt.h fifo.h + ./compile hier.c + +home: \ +home.sh conf-qmail + cat home.sh \ + | sed s}QMAIL}"`head -1 conf-qmail`"}g \ + > home + chmod 755 home + +home+df: \ +home+df.sh conf-qmail + cat home+df.sh \ + | sed s}QMAIL}"`head -1 conf-qmail`"}g \ + > home+df + chmod 755 home+df + +hostname: \ +load hostname.o substdio.a error.a str.a dns.lib socket.lib + ./load hostname substdio.a error.a str.a `cat dns.lib` \ + `cat socket.lib` + +hostname.o: \ +compile hostname.c substdio.h subfd.h substdio.h readwrite.h exit.h + ./compile hostname.c + +idedit: \ +load idedit.o strerr.a substdio.a error.a str.a fs.a wait.a open.a \ +seek.a + ./load idedit strerr.a substdio.a error.a str.a fs.a \ + wait.a open.a seek.a + +idedit.o: \ +compile idedit.c readwrite.h exit.h scan.h fmt.h strerr.h open.h \ +seek.h fork.h + ./compile idedit.c + +install: \ +load install.o fifo.o hier.o auto_qmail.o auto_split.o auto_uids.o \ +strerr.a substdio.a open.a error.a str.a fs.a + ./load install fifo.o hier.o auto_qmail.o auto_split.o \ + auto_uids.o strerr.a substdio.a open.a error.a str.a fs.a + +install-big: \ +load install-big.o fifo.o install.o auto_qmail.o auto_split.o \ +auto_uids.o strerr.a substdio.a open.a error.a str.a fs.a + ./load install-big fifo.o install.o auto_qmail.o \ + auto_split.o auto_uids.o strerr.a substdio.a open.a error.a \ + str.a fs.a + +install-big.o: \ +compile install-big.c auto_qmail.h auto_split.h auto_uids.h fmt.h \ +fifo.h + ./compile install-big.c + +install.o: \ +compile install.c substdio.h strerr.h error.h open.h readwrite.h \ +exit.h + ./compile install.c + +instcheck: \ +load instcheck.o fifo.o hier.o auto_qmail.o auto_split.o auto_uids.o \ +strerr.a substdio.a error.a str.a fs.a + ./load instcheck fifo.o hier.o auto_qmail.o auto_split.o \ + auto_uids.o strerr.a substdio.a error.a str.a fs.a + +instcheck.o: \ +compile instcheck.c strerr.h error.h readwrite.h exit.h + ./compile instcheck.c + +ip.o: \ +compile ip.c fmt.h scan.h ip.h + ./compile ip.c + +ipalloc.o: \ +compile ipalloc.c alloc.h gen_allocdefs.h ip.h ipalloc.h ip.h \ +gen_alloc.h + ./compile ipalloc.c + +ipme.o: \ +compile ipme.c hassalen.h byte.h ip.h ipalloc.h ip.h gen_alloc.h \ +stralloc.h gen_alloc.h ipme.h ip.h ipalloc.h + ./compile ipme.c + +ipmeprint: \ +load ipmeprint.o ipme.o ip.o ipalloc.o stralloc.a alloc.a substdio.a \ +error.a str.a fs.a socket.lib + ./load ipmeprint ipme.o ip.o ipalloc.o stralloc.a alloc.a \ + substdio.a error.a str.a fs.a `cat socket.lib` + +ipmeprint.o: \ +compile ipmeprint.c subfd.h substdio.h substdio.h ip.h ipme.h ip.h \ +ipalloc.h ip.h gen_alloc.h exit.h + ./compile ipmeprint.c + +it: \ +qmail-local qmail-lspawn qmail-getpw qmail-remote qmail-rspawn \ +qmail-clean qmail-send qmail-start splogger qmail-queue qmail-inject \ +predate datemail mailsubj qmail-upq qmail-showctl qmail-newu \ +qmail-pw2u qmail-qread qmail-qstat qmail-tcpto qmail-tcpok \ +qmail-pop3d qmail-popup qmail-qmqpc qmail-qmqpd qmail-qmtpd \ +qmail-smtpd sendmail tcp-env qmail-newmrh config config-fast dnscname \ +dnsptr dnsip dnsmxip dnsfq hostname ipmeprint qreceipt qsmhook qbiff \ +forward preline condredirect bouncesaying except maildirmake \ +maildir2mbox maildirwatch qail elq pinq idedit install-big install \ +instcheck home home+df proc proc+df binm1 binm1+df binm2 binm2+df \ +binm3 binm3+df + +load: \ +make-load warn-auto.sh systype + ( cat warn-auto.sh; ./make-load "`cat systype`" ) > load + chmod 755 load + +lock.a: \ +makelib lock_ex.o lock_exnb.o lock_un.o + ./makelib lock.a lock_ex.o lock_exnb.o lock_un.o + +lock_ex.o: \ +compile lock_ex.c hasflock.h lock.h + ./compile lock_ex.c + +lock_exnb.o: \ +compile lock_exnb.c hasflock.h lock.h + ./compile lock_exnb.c + +lock_un.o: \ +compile lock_un.c hasflock.h lock.h + ./compile lock_un.c + +maildir.0: \ +maildir.5 + nroff -man maildir.5 > maildir.0 + +maildir.o: \ +compile maildir.c prioq.h datetime.h gen_alloc.h env.h stralloc.h \ +gen_alloc.h direntry.h datetime.h now.h datetime.h str.h maildir.h \ +strerr.h + ./compile maildir.c + +maildir2mbox: \ +load maildir2mbox.o maildir.o prioq.o now.o myctime.o gfrom.o lock.a \ +getln.a env.a open.a strerr.a stralloc.a alloc.a substdio.a error.a \ +str.a fs.a datetime.a + ./load maildir2mbox maildir.o prioq.o now.o myctime.o \ + gfrom.o lock.a getln.a env.a open.a strerr.a stralloc.a \ + alloc.a substdio.a error.a str.a fs.a datetime.a + +maildir2mbox.0: \ +maildir2mbox.1 + nroff -man maildir2mbox.1 > maildir2mbox.0 + +maildir2mbox.o: \ +compile maildir2mbox.c readwrite.h prioq.h datetime.h gen_alloc.h \ +env.h stralloc.h gen_alloc.h subfd.h substdio.h substdio.h getln.h \ +error.h open.h lock.h gfrom.h str.h exit.h myctime.h maildir.h \ +strerr.h + ./compile maildir2mbox.c + +maildirmake: \ +load maildirmake.o strerr.a substdio.a error.a str.a + ./load maildirmake strerr.a substdio.a error.a str.a + +maildirmake.0: \ +maildirmake.1 + nroff -man maildirmake.1 > maildirmake.0 + +maildirmake.o: \ +compile maildirmake.c strerr.h exit.h + ./compile maildirmake.c + +maildirwatch: \ +load maildirwatch.o hfield.o headerbody.o maildir.o prioq.o now.o \ +getln.a env.a open.a strerr.a stralloc.a alloc.a substdio.a error.a \ +str.a + ./load maildirwatch hfield.o headerbody.o maildir.o \ + prioq.o now.o getln.a env.a open.a strerr.a stralloc.a \ + alloc.a substdio.a error.a str.a + +maildirwatch.0: \ +maildirwatch.1 + nroff -man maildirwatch.1 > maildirwatch.0 + +maildirwatch.o: \ +compile maildirwatch.c getln.h substdio.h subfd.h substdio.h prioq.h \ +datetime.h gen_alloc.h stralloc.h gen_alloc.h str.h exit.h hfield.h \ +readwrite.h open.h headerbody.h maildir.h strerr.h + ./compile maildirwatch.c + +mailsubj: \ +warn-auto.sh mailsubj.sh conf-qmail conf-break conf-split + cat warn-auto.sh mailsubj.sh \ + | sed s}QMAIL}"`head -1 conf-qmail`"}g \ + | sed s}BREAK}"`head -1 conf-break`"}g \ + | sed s}SPLIT}"`head -1 conf-split`"}g \ + > mailsubj + chmod 755 mailsubj + +mailsubj.0: \ +mailsubj.1 + nroff -man mailsubj.1 > mailsubj.0 + +make-compile: \ +make-compile.sh auto-ccld.sh + cat auto-ccld.sh make-compile.sh > make-compile + chmod 755 make-compile + +make-load: \ +make-load.sh auto-ccld.sh + cat auto-ccld.sh make-load.sh > make-load + chmod 755 make-load + +make-makelib: \ +make-makelib.sh auto-ccld.sh + cat auto-ccld.sh make-makelib.sh > make-makelib + chmod 755 make-makelib + +makelib: \ +make-makelib warn-auto.sh systype + ( cat warn-auto.sh; ./make-makelib "`cat systype`" ) > \ + makelib + chmod 755 makelib + +man: \ +qmail-local.0 qmail-lspawn.0 qmail-getpw.0 qmail-remote.0 \ +qmail-rspawn.0 qmail-clean.0 qmail-send.0 qmail-start.0 splogger.0 \ +qmail-queue.0 qmail-inject.0 mailsubj.0 qmail-showctl.0 qmail-newu.0 \ +qmail-pw2u.0 qmail-qread.0 qmail-qstat.0 qmail-tcpto.0 qmail-tcpok.0 \ +qmail-pop3d.0 qmail-popup.0 qmail-qmqpc.0 qmail-qmqpd.0 qmail-qmtpd.0 \ +qmail-smtpd.0 tcp-env.0 qmail-newmrh.0 qreceipt.0 qbiff.0 forward.0 \ +preline.0 condredirect.0 bouncesaying.0 except.0 maildirmake.0 \ +maildir2mbox.0 maildirwatch.0 qmail.0 qmail-limits.0 qmail-log.0 \ +qmail-control.0 qmail-header.0 qmail-users.0 dot-qmail.0 \ +qmail-command.0 tcp-environ.0 maildir.0 mbox.0 addresses.0 \ +envelopes.0 forgeries.0 + +mbox.0: \ +mbox.5 + nroff -man mbox.5 > mbox.0 + +myctime.o: \ +compile myctime.c datetime.h fmt.h myctime.h + ./compile myctime.c + +ndelay.a: \ +makelib ndelay.o ndelay_off.o + ./makelib ndelay.a ndelay.o ndelay_off.o + +ndelay.o: \ +compile ndelay.c ndelay.h + ./compile ndelay.c + +ndelay_off.o: \ +compile ndelay_off.c ndelay.h + ./compile ndelay_off.c + +newfield.o: \ +compile newfield.c fmt.h datetime.h stralloc.h gen_alloc.h \ +date822fmt.h newfield.h stralloc.h + ./compile newfield.c + +now.o: \ +compile now.c datetime.h now.h datetime.h + ./compile now.c + +open.a: \ +makelib open_append.o open_excl.o open_read.o open_trunc.o \ +open_write.o + ./makelib open.a open_append.o open_excl.o open_read.o \ + open_trunc.o open_write.o + +open_append.o: \ +compile open_append.c open.h + ./compile open_append.c + +open_excl.o: \ +compile open_excl.c open.h + ./compile open_excl.c + +open_read.o: \ +compile open_read.c open.h + ./compile open_read.c + +open_trunc.o: \ +compile open_trunc.c open.h + ./compile open_trunc.c + +open_write.o: \ +compile open_write.c open.h + ./compile open_write.c + +pinq: \ +warn-auto.sh pinq.sh conf-qmail conf-break conf-split + cat warn-auto.sh pinq.sh \ + | sed s}QMAIL}"`head -1 conf-qmail`"}g \ + | sed s}BREAK}"`head -1 conf-break`"}g \ + | sed s}SPLIT}"`head -1 conf-split`"}g \ + > pinq + chmod 755 pinq + +predate: \ +load predate.o datetime.a strerr.a sig.a fd.a wait.a substdio.a \ +error.a str.a fs.a + ./load predate datetime.a strerr.a sig.a fd.a wait.a \ + substdio.a error.a str.a fs.a + +predate.o: \ +compile predate.c datetime.h fork.h wait.h fd.h fmt.h strerr.h \ +substdio.h subfd.h substdio.h readwrite.h exit.h + ./compile predate.c + +preline: \ +load preline.o strerr.a fd.a wait.a sig.a env.a getopt.a substdio.a \ +error.a str.a + ./load preline strerr.a fd.a wait.a sig.a env.a getopt.a \ + substdio.a error.a str.a + +preline.0: \ +preline.1 + nroff -man preline.1 > preline.0 + +preline.o: \ +compile preline.c fd.h sgetopt.h subgetopt.h readwrite.h strerr.h \ +substdio.h exit.h fork.h wait.h env.h sig.h error.h + ./compile preline.c + +prioq.o: \ +compile prioq.c alloc.h gen_allocdefs.h prioq.h datetime.h \ +gen_alloc.h + ./compile prioq.c + +proc: \ +proc.sh conf-qmail + cat proc.sh \ + | sed s}QMAIL}"`head -1 conf-qmail`"}g \ + > proc + chmod 755 proc + +proc+df: \ +proc+df.sh conf-qmail + cat proc+df.sh \ + | sed s}QMAIL}"`head -1 conf-qmail`"}g \ + > proc+df + chmod 755 proc+df + +prot.o: \ +compile prot.c hasshsgr.h prot.h + ./compile prot.c + +qail: \ +warn-auto.sh qail.sh conf-qmail conf-break conf-split + cat warn-auto.sh qail.sh \ + | sed s}QMAIL}"`head -1 conf-qmail`"}g \ + | sed s}BREAK}"`head -1 conf-break`"}g \ + | sed s}SPLIT}"`head -1 conf-split`"}g \ + > qail + chmod 755 qail + +qbiff: \ +load qbiff.o headerbody.o hfield.o getln.a env.a open.a stralloc.a \ +alloc.a substdio.a error.a str.a + ./load qbiff headerbody.o hfield.o getln.a env.a open.a \ + stralloc.a alloc.a substdio.a error.a str.a + +qbiff.0: \ +qbiff.1 + nroff -man qbiff.1 > qbiff.0 + +qbiff.o: \ +compile qbiff.c readwrite.h stralloc.h gen_alloc.h substdio.h subfd.h \ +substdio.h open.h byte.h str.h headerbody.h hfield.h env.h exit.h + ./compile qbiff.c + +qmail-clean: \ +load qmail-clean.o fmtqfn.o now.o getln.a sig.a stralloc.a alloc.a \ +substdio.a error.a str.a fs.a auto_qmail.o auto_split.o + ./load qmail-clean fmtqfn.o now.o getln.a sig.a stralloc.a \ + alloc.a substdio.a error.a str.a fs.a auto_qmail.o \ + auto_split.o + +qmail-clean.0: \ +qmail-clean.8 + nroff -man qmail-clean.8 > qmail-clean.0 + +qmail-clean.o: \ +compile qmail-clean.c readwrite.h sig.h now.h datetime.h str.h \ +direntry.h getln.h stralloc.h gen_alloc.h substdio.h subfd.h \ +substdio.h byte.h scan.h fmt.h error.h exit.h fmtqfn.h auto_qmail.h + ./compile qmail-clean.c + +qmail-command.0: \ +qmail-command.8 + nroff -man qmail-command.8 > qmail-command.0 + +qmail-control.0: \ +qmail-control.5 + nroff -man qmail-control.5 > qmail-control.0 + +qmail-control.5: \ +qmail-control.9 conf-break conf-spawn + cat qmail-control.9 \ + | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \ + | sed s}BREAK}"`head -1 conf-break`"}g \ + | sed s}SPAWN}"`head -1 conf-spawn`"}g \ + > qmail-control.5 + +qmail-getpw: \ +load qmail-getpw.o case.a substdio.a error.a str.a fs.a auto_break.o \ +auto_usera.o + ./load qmail-getpw case.a substdio.a error.a str.a fs.a \ + auto_break.o auto_usera.o + +qmail-getpw.0: \ +qmail-getpw.8 + nroff -man qmail-getpw.8 > qmail-getpw.0 + +qmail-getpw.8: \ +qmail-getpw.9 conf-break conf-spawn + cat qmail-getpw.9 \ + | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \ + | sed s}BREAK}"`head -1 conf-break`"}g \ + | sed s}SPAWN}"`head -1 conf-spawn`"}g \ + > qmail-getpw.8 + +qmail-getpw.o: \ +compile qmail-getpw.c readwrite.h substdio.h subfd.h substdio.h \ +error.h exit.h byte.h str.h case.h fmt.h auto_usera.h auto_break.h \ +qlx.h + ./compile qmail-getpw.c + +qmail-header.0: \ +qmail-header.5 + nroff -man qmail-header.5 > qmail-header.0 + +qmail-inject: \ +load qmail-inject.o headerbody.o hfield.o newfield.o quote.o now.o \ +control.o date822fmt.o constmap.o qmail.o case.a fd.a wait.a open.a \ +getln.a sig.a getopt.a datetime.a token822.o env.a stralloc.a alloc.a \ +substdio.a error.a str.a fs.a auto_qmail.o + ./load qmail-inject headerbody.o hfield.o newfield.o \ + quote.o now.o control.o date822fmt.o constmap.o qmail.o \ + case.a fd.a wait.a open.a getln.a sig.a getopt.a datetime.a \ + token822.o env.a stralloc.a alloc.a substdio.a error.a \ + str.a fs.a auto_qmail.o + +qmail-inject.0: \ +qmail-inject.8 + nroff -man qmail-inject.8 > qmail-inject.0 + +qmail-inject.o: \ +compile qmail-inject.c sig.h substdio.h stralloc.h gen_alloc.h \ +subfd.h substdio.h sgetopt.h subgetopt.h getln.h alloc.h str.h fmt.h \ +hfield.h token822.h gen_alloc.h control.h env.h gen_alloc.h \ +gen_allocdefs.h error.h qmail.h substdio.h now.h datetime.h exit.h \ +quote.h headerbody.h auto_qmail.h newfield.h stralloc.h constmap.h + ./compile qmail-inject.c + +qmail-limits.0: \ +qmail-limits.7 + nroff -man qmail-limits.7 > qmail-limits.0 + +qmail-limits.7: \ +qmail-limits.9 conf-break conf-spawn + cat qmail-limits.9 \ + | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \ + | sed s}BREAK}"`head -1 conf-break`"}g \ + | sed s}SPAWN}"`head -1 conf-spawn`"}g \ + > qmail-limits.7 + +qmail-local: \ +load qmail-local.o qmail.o quote.o now.o gfrom.o myctime.o \ +slurpclose.o case.a getln.a getopt.a sig.a open.a seek.a lock.a fd.a \ +wait.a env.a stralloc.a alloc.a strerr.a substdio.a error.a str.a \ +fs.a datetime.a auto_qmail.o auto_patrn.o socket.lib + ./load qmail-local qmail.o quote.o now.o gfrom.o myctime.o \ + slurpclose.o case.a getln.a getopt.a sig.a open.a seek.a \ + lock.a fd.a wait.a env.a stralloc.a alloc.a strerr.a \ + substdio.a error.a str.a fs.a datetime.a auto_qmail.o \ + auto_patrn.o `cat socket.lib` + +qmail-local.0: \ +qmail-local.8 + nroff -man qmail-local.8 > qmail-local.0 + +qmail-local.o: \ +compile qmail-local.c readwrite.h sig.h env.h byte.h exit.h fork.h \ +open.h wait.h lock.h seek.h substdio.h getln.h strerr.h subfd.h \ +substdio.h sgetopt.h subgetopt.h alloc.h error.h stralloc.h \ +gen_alloc.h fmt.h str.h now.h datetime.h case.h quote.h qmail.h \ +substdio.h slurpclose.h myctime.h gfrom.h auto_patrn.h + ./compile qmail-local.c + +qmail-log.0: \ +qmail-log.5 + nroff -man qmail-log.5 > qmail-log.0 + +qmail-lspawn: \ +load qmail-lspawn.o spawn.o prot.o slurpclose.o coe.o sig.a wait.a \ +case.a cdb.a fd.a open.a stralloc.a alloc.a substdio.a error.a str.a \ +fs.a auto_qmail.o auto_uids.o auto_spawn.o + ./load qmail-lspawn spawn.o prot.o slurpclose.o coe.o \ + sig.a wait.a case.a cdb.a fd.a open.a stralloc.a alloc.a \ + substdio.a error.a str.a fs.a auto_qmail.o auto_uids.o \ + auto_spawn.o + +qmail-lspawn.0: \ +qmail-lspawn.8 + nroff -man qmail-lspawn.8 > qmail-lspawn.0 + +qmail-lspawn.o: \ +compile qmail-lspawn.c fd.h wait.h prot.h substdio.h stralloc.h \ +gen_alloc.h scan.h exit.h fork.h error.h cdb.h uint32.h case.h \ +slurpclose.h auto_qmail.h auto_uids.h qlx.h + ./compile qmail-lspawn.c + +qmail-newmrh: \ +load qmail-newmrh.o cdbmss.o getln.a open.a cdbmake.a seek.a case.a \ +stralloc.a alloc.a strerr.a substdio.a error.a str.a auto_qmail.o + ./load qmail-newmrh cdbmss.o getln.a open.a cdbmake.a \ + seek.a case.a stralloc.a alloc.a strerr.a substdio.a \ + error.a str.a auto_qmail.o + +qmail-newmrh.0: \ +qmail-newmrh.8 + nroff -man qmail-newmrh.8 > qmail-newmrh.0 + +qmail-newmrh.8: \ +qmail-newmrh.9 conf-break conf-spawn + cat qmail-newmrh.9 \ + | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \ + | sed s}BREAK}"`head -1 conf-break`"}g \ + | sed s}SPAWN}"`head -1 conf-spawn`"}g \ + > qmail-newmrh.8 + +qmail-newmrh.o: \ +compile qmail-newmrh.c strerr.h stralloc.h gen_alloc.h substdio.h \ +getln.h exit.h readwrite.h open.h auto_qmail.h cdbmss.h cdbmake.h \ +uint32.h substdio.h + ./compile qmail-newmrh.c + +qmail-newu: \ +load qmail-newu.o cdbmss.o getln.a open.a seek.a cdbmake.a case.a \ +stralloc.a alloc.a substdio.a error.a str.a auto_qmail.o + ./load qmail-newu cdbmss.o getln.a open.a seek.a cdbmake.a \ + case.a stralloc.a alloc.a substdio.a error.a str.a \ + auto_qmail.o + +qmail-newu.0: \ +qmail-newu.8 + nroff -man qmail-newu.8 > qmail-newu.0 + +qmail-newu.8: \ +qmail-newu.9 conf-break conf-spawn + cat qmail-newu.9 \ + | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \ + | sed s}BREAK}"`head -1 conf-break`"}g \ + | sed s}SPAWN}"`head -1 conf-spawn`"}g \ + > qmail-newu.8 + +qmail-newu.o: \ +compile qmail-newu.c stralloc.h gen_alloc.h subfd.h substdio.h \ +getln.h substdio.h cdbmss.h cdbmake.h uint32.h substdio.h exit.h \ +readwrite.h open.h error.h case.h auto_qmail.h + ./compile qmail-newu.c + +qmail-pop3d: \ +load qmail-pop3d.o commands.o case.a timeoutread.o timeoutwrite.o \ +maildir.o prioq.o now.o env.a strerr.a sig.a open.a getln.a \ +stralloc.a alloc.a substdio.a error.a str.a fs.a socket.lib + ./load qmail-pop3d commands.o case.a timeoutread.o \ + timeoutwrite.o maildir.o prioq.o now.o env.a strerr.a sig.a \ + open.a getln.a stralloc.a alloc.a substdio.a error.a str.a \ + fs.a `cat socket.lib` + +qmail-pop3d.0: \ +qmail-pop3d.8 + nroff -man qmail-pop3d.8 > qmail-pop3d.0 + +qmail-pop3d.o: \ +compile qmail-pop3d.c commands.h sig.h getln.h stralloc.h gen_alloc.h \ +substdio.h alloc.h open.h prioq.h datetime.h gen_alloc.h scan.h fmt.h \ +str.h exit.h maildir.h strerr.h readwrite.h timeoutread.h \ +timeoutwrite.h + ./compile qmail-pop3d.c + +qmail-popup: \ +load qmail-popup.o commands.o timeoutread.o timeoutwrite.o now.o \ +case.a fd.a sig.a wait.a stralloc.a alloc.a substdio.a error.a str.a \ +fs.a socket.lib + ./load qmail-popup commands.o timeoutread.o timeoutwrite.o \ + now.o case.a fd.a sig.a wait.a stralloc.a alloc.a \ + substdio.a error.a str.a fs.a `cat socket.lib` + +qmail-popup.0: \ +qmail-popup.8 + nroff -man qmail-popup.8 > qmail-popup.0 + +qmail-popup.o: \ +compile qmail-popup.c commands.h fd.h sig.h stralloc.h gen_alloc.h \ +substdio.h alloc.h wait.h str.h byte.h now.h datetime.h fmt.h exit.h \ +readwrite.h timeoutread.h timeoutwrite.h + ./compile qmail-popup.c + +qmail-pw2u: \ +load qmail-pw2u.o constmap.o control.o open.a getln.a case.a getopt.a \ +stralloc.a alloc.a substdio.a error.a str.a fs.a auto_usera.o \ +auto_break.o auto_qmail.o + ./load qmail-pw2u constmap.o control.o open.a getln.a \ + case.a getopt.a stralloc.a alloc.a substdio.a error.a str.a \ + fs.a auto_usera.o auto_break.o auto_qmail.o + +qmail-pw2u.0: \ +qmail-pw2u.8 + nroff -man qmail-pw2u.8 > qmail-pw2u.0 + +qmail-pw2u.8: \ +qmail-pw2u.9 conf-break conf-spawn + cat qmail-pw2u.9 \ + | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \ + | sed s}BREAK}"`head -1 conf-break`"}g \ + | sed s}SPAWN}"`head -1 conf-spawn`"}g \ + > qmail-pw2u.8 + +qmail-pw2u.o: \ +compile qmail-pw2u.c substdio.h readwrite.h subfd.h substdio.h \ +sgetopt.h subgetopt.h control.h constmap.h stralloc.h gen_alloc.h \ +fmt.h str.h scan.h open.h error.h getln.h auto_break.h auto_qmail.h \ +auto_usera.h + ./compile qmail-pw2u.c + +qmail-qmqpc: \ +load qmail-qmqpc.o slurpclose.o timeoutread.o timeoutwrite.o \ +timeoutconn.o ip.o control.o auto_qmail.o sig.a ndelay.a open.a \ +getln.a substdio.a stralloc.a alloc.a error.a str.a fs.a socket.lib + ./load qmail-qmqpc slurpclose.o timeoutread.o \ + timeoutwrite.o timeoutconn.o ip.o control.o auto_qmail.o \ + sig.a ndelay.a open.a getln.a substdio.a stralloc.a alloc.a \ + error.a str.a fs.a `cat socket.lib` + +qmail-qmqpc.0: \ +qmail-qmqpc.8 + nroff -man qmail-qmqpc.8 > qmail-qmqpc.0 + +qmail-qmqpc.o: \ +compile qmail-qmqpc.c substdio.h getln.h readwrite.h exit.h \ +stralloc.h gen_alloc.h slurpclose.h error.h sig.h ip.h timeoutconn.h \ +timeoutread.h timeoutwrite.h auto_qmail.h control.h fmt.h + ./compile qmail-qmqpc.c + +qmail-qmqpd: \ +load qmail-qmqpd.o received.o now.o date822fmt.o qmail.o auto_qmail.o \ +env.a substdio.a sig.a error.a wait.a fd.a str.a datetime.a fs.a + ./load qmail-qmqpd received.o now.o date822fmt.o qmail.o \ + auto_qmail.o env.a substdio.a sig.a error.a wait.a fd.a \ + str.a datetime.a fs.a + +qmail-qmqpd.0: \ +qmail-qmqpd.8 + nroff -man qmail-qmqpd.8 > qmail-qmqpd.0 + +qmail-qmqpd.o: \ +compile qmail-qmqpd.c auto_qmail.h qmail.h substdio.h received.h \ +sig.h substdio.h readwrite.h exit.h now.h datetime.h fmt.h env.h + ./compile qmail-qmqpd.c + +qmail-qmtpd: \ +load qmail-qmtpd.o rcpthosts.o control.o constmap.o received.o \ +date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a open.a \ +getln.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a \ +str.a fs.a auto_qmail.o + ./load qmail-qmtpd rcpthosts.o control.o constmap.o \ + received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \ + datetime.a open.a getln.a sig.a case.a env.a stralloc.a \ + alloc.a substdio.a error.a str.a fs.a auto_qmail.o + +qmail-qmtpd.0: \ +qmail-qmtpd.8 + nroff -man qmail-qmtpd.8 > qmail-qmtpd.0 + +qmail-qmtpd.o: \ +compile qmail-qmtpd.c stralloc.h gen_alloc.h substdio.h qmail.h \ +substdio.h now.h datetime.h str.h fmt.h env.h sig.h rcpthosts.h \ +auto_qmail.h readwrite.h control.h received.h + ./compile qmail-qmtpd.c + +qmail-qread: \ +load qmail-qread.o fmtqfn.o readsubdir.o date822fmt.o datetime.a \ +open.a getln.a stralloc.a alloc.a substdio.a error.a str.a fs.a \ +auto_qmail.o auto_split.o + ./load qmail-qread fmtqfn.o readsubdir.o date822fmt.o \ + datetime.a open.a getln.a stralloc.a alloc.a substdio.a \ + error.a str.a fs.a auto_qmail.o auto_split.o + +qmail-qread.0: \ +qmail-qread.8 + nroff -man qmail-qread.8 > qmail-qread.0 + +qmail-qread.o: \ +compile qmail-qread.c stralloc.h gen_alloc.h substdio.h subfd.h \ +substdio.h fmt.h str.h getln.h fmtqfn.h readsubdir.h direntry.h \ +auto_qmail.h open.h datetime.h date822fmt.h readwrite.h error.h \ +exit.h + ./compile qmail-qread.c + +qmail-qstat: \ +warn-auto.sh qmail-qstat.sh conf-qmail conf-break conf-split + cat warn-auto.sh qmail-qstat.sh \ + | sed s}QMAIL}"`head -1 conf-qmail`"}g \ + | sed s}BREAK}"`head -1 conf-break`"}g \ + | sed s}SPLIT}"`head -1 conf-split`"}g \ + > qmail-qstat + chmod 755 qmail-qstat + +qmail-qstat.0: \ +qmail-qstat.8 + nroff -man qmail-qstat.8 > qmail-qstat.0 + +qmail-queue: \ +load qmail-queue.o triggerpull.o fmtqfn.o now.o date822fmt.o \ +datetime.a seek.a ndelay.a open.a sig.a alloc.a substdio.a error.a \ +str.a fs.a auto_qmail.o auto_split.o auto_uids.o + ./load qmail-queue triggerpull.o fmtqfn.o now.o \ + date822fmt.o datetime.a seek.a ndelay.a open.a sig.a \ + alloc.a substdio.a error.a str.a fs.a auto_qmail.o \ + auto_split.o auto_uids.o + +qmail-queue.0: \ +qmail-queue.8 + nroff -man qmail-queue.8 > qmail-queue.0 + +qmail-queue.o: \ +compile qmail-queue.c readwrite.h sig.h exit.h open.h seek.h fmt.h \ +alloc.h substdio.h datetime.h now.h datetime.h triggerpull.h extra.h \ +auto_qmail.h auto_uids.h date822fmt.h fmtqfn.h + ./compile qmail-queue.c + +qmail-remote: \ +load qmail-remote.o control.o constmap.o timeoutread.o timeoutwrite.o \ +timeoutconn.o tcpto.o now.o dns.o ip.o ipalloc.o ipme.o quote.o \ +ndelay.a case.a sig.a open.a lock.a seek.a getln.a stralloc.a alloc.a \ +substdio.a error.a str.a fs.a auto_qmail.o dns.lib socket.lib + ./load qmail-remote control.o constmap.o timeoutread.o \ + timeoutwrite.o timeoutconn.o tcpto.o now.o dns.o ip.o \ + ipalloc.o ipme.o quote.o ndelay.a case.a sig.a open.a \ + lock.a seek.a getln.a stralloc.a alloc.a substdio.a error.a \ + str.a fs.a auto_qmail.o `cat dns.lib` `cat socket.lib` + +qmail-remote.0: \ +qmail-remote.8 + nroff -man qmail-remote.8 > qmail-remote.0 + +qmail-remote.o: \ +compile qmail-remote.c sig.h stralloc.h gen_alloc.h substdio.h \ +subfd.h substdio.h scan.h case.h error.h auto_qmail.h control.h dns.h \ +alloc.h quote.h ip.h ipalloc.h ip.h gen_alloc.h ipme.h ip.h ipalloc.h \ +gen_alloc.h gen_allocdefs.h str.h now.h datetime.h exit.h constmap.h \ +tcpto.h readwrite.h timeoutconn.h timeoutread.h timeoutwrite.h + ./compile qmail-remote.c + +qmail-rspawn: \ +load qmail-rspawn.o spawn.o tcpto_clean.o now.o coe.o sig.a open.a \ +seek.a lock.a wait.a fd.a stralloc.a alloc.a substdio.a error.a str.a \ +auto_qmail.o auto_uids.o auto_spawn.o + ./load qmail-rspawn spawn.o tcpto_clean.o now.o coe.o \ + sig.a open.a seek.a lock.a wait.a fd.a stralloc.a alloc.a \ + substdio.a error.a str.a auto_qmail.o auto_uids.o \ + auto_spawn.o + +qmail-rspawn.0: \ +qmail-rspawn.8 + nroff -man qmail-rspawn.8 > qmail-rspawn.0 + +qmail-rspawn.o: \ +compile qmail-rspawn.c fd.h wait.h substdio.h exit.h fork.h error.h \ +tcpto.h + ./compile qmail-rspawn.c + +qmail-send: \ +load qmail-send.o qsutil.o control.o constmap.o newfield.o prioq.o \ +trigger.o fmtqfn.o quote.o now.o readsubdir.o qmail.o date822fmt.o \ +datetime.a case.a ndelay.a getln.a wait.a seek.a fd.a sig.a open.a \ +lock.a stralloc.a alloc.a substdio.a error.a str.a fs.a auto_qmail.o \ +auto_split.o env.a + ./load qmail-send qsutil.o control.o constmap.o newfield.o \ + prioq.o trigger.o fmtqfn.o quote.o now.o readsubdir.o \ + qmail.o date822fmt.o datetime.a case.a ndelay.a getln.a \ + wait.a seek.a fd.a sig.a open.a lock.a stralloc.a alloc.a \ + substdio.a error.a str.a fs.a auto_qmail.o auto_split.o env.a + +qmail-send.0: \ +qmail-send.8 + nroff -man qmail-send.8 > qmail-send.0 + +qmail-send.8: \ +qmail-send.9 conf-break conf-spawn + cat qmail-send.9 \ + | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \ + | sed s}BREAK}"`head -1 conf-break`"}g \ + | sed s}SPAWN}"`head -1 conf-spawn`"}g \ + > qmail-send.8 + +qmail-send.o: \ +compile qmail-send.c readwrite.h sig.h direntry.h control.h select.h \ +open.h seek.h exit.h lock.h ndelay.h now.h datetime.h getln.h \ +substdio.h alloc.h error.h stralloc.h gen_alloc.h str.h byte.h fmt.h \ +scan.h case.h auto_qmail.h trigger.h newfield.h stralloc.h quote.h \ +qmail.h substdio.h qsutil.h prioq.h datetime.h gen_alloc.h constmap.h \ +fmtqfn.h readsubdir.h direntry.h + ./compile qmail-send.c + +qmail-showctl: \ +load qmail-showctl.o auto_uids.o control.o open.a getln.a stralloc.a \ +alloc.a substdio.a error.a str.a fs.a auto_qmail.o auto_break.o \ +auto_patrn.o auto_spawn.o auto_split.o + ./load qmail-showctl auto_uids.o control.o open.a getln.a \ + stralloc.a alloc.a substdio.a error.a str.a fs.a \ + auto_qmail.o auto_break.o auto_patrn.o auto_spawn.o \ + auto_split.o + +qmail-showctl.0: \ +qmail-showctl.8 + nroff -man qmail-showctl.8 > qmail-showctl.0 + +qmail-showctl.o: \ +compile qmail-showctl.c substdio.h subfd.h substdio.h exit.h fmt.h \ +str.h control.h constmap.h stralloc.h gen_alloc.h direntry.h \ +auto_uids.h auto_qmail.h auto_break.h auto_patrn.h auto_spawn.h \ +auto_split.h + ./compile qmail-showctl.c + +qmail-smtpd: \ +load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \ +timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \ +date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \ +open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \ +fs.a auto_qmail.o socket.lib + ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \ + timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \ + received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \ + datetime.a getln.a open.a sig.a case.a env.a stralloc.a \ + alloc.a substdio.a error.a str.a fs.a auto_qmail.o `cat \ + socket.lib` + +qmail-smtpd.0: \ +qmail-smtpd.8 + nroff -man qmail-smtpd.8 > qmail-smtpd.0 + +qmail-smtpd.o: \ +compile qmail-smtpd.c sig.h readwrite.h stralloc.h gen_alloc.h \ +substdio.h alloc.h auto_qmail.h control.h received.h constmap.h \ +error.h ipme.h ip.h ipalloc.h ip.h gen_alloc.h ip.h qmail.h \ +substdio.h str.h fmt.h scan.h byte.h case.h env.h now.h datetime.h \ +exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h + ./compile qmail-smtpd.c + +qmail-start: \ +load qmail-start.o prot.o fd.a auto_uids.o + ./load qmail-start prot.o fd.a auto_uids.o + +qmail-start.0: \ +qmail-start.8 + nroff -man qmail-start.8 > qmail-start.0 + +qmail-start.8: \ +qmail-start.9 conf-break conf-spawn + cat qmail-start.9 \ + | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \ + | sed s}BREAK}"`head -1 conf-break`"}g \ + | sed s}SPAWN}"`head -1 conf-spawn`"}g \ + > qmail-start.8 + +qmail-start.o: \ +compile qmail-start.c fd.h prot.h exit.h fork.h auto_uids.h + ./compile qmail-start.c + +qmail-tcpok: \ +load qmail-tcpok.o open.a lock.a strerr.a substdio.a error.a str.a \ +auto_qmail.o + ./load qmail-tcpok open.a lock.a strerr.a substdio.a \ + error.a str.a auto_qmail.o + +qmail-tcpok.0: \ +qmail-tcpok.8 + nroff -man qmail-tcpok.8 > qmail-tcpok.0 + +qmail-tcpok.o: \ +compile qmail-tcpok.c strerr.h substdio.h lock.h open.h readwrite.h \ +auto_qmail.h exit.h + ./compile qmail-tcpok.c + +qmail-tcpto: \ +load qmail-tcpto.o ip.o now.o open.a lock.a substdio.a error.a str.a \ +fs.a auto_qmail.o + ./load qmail-tcpto ip.o now.o open.a lock.a substdio.a \ + error.a str.a fs.a auto_qmail.o + +qmail-tcpto.0: \ +qmail-tcpto.8 + nroff -man qmail-tcpto.8 > qmail-tcpto.0 + +qmail-tcpto.o: \ +compile qmail-tcpto.c substdio.h subfd.h substdio.h auto_qmail.h \ +fmt.h ip.h lock.h error.h exit.h datetime.h now.h datetime.h + ./compile qmail-tcpto.c + +qmail-upq: \ +warn-auto.sh qmail-upq.sh conf-qmail conf-break conf-split + cat warn-auto.sh qmail-upq.sh \ + | sed s}QMAIL}"`head -1 conf-qmail`"}g \ + | sed s}BREAK}"`head -1 conf-break`"}g \ + | sed s}SPLIT}"`head -1 conf-split`"}g \ + > qmail-upq + chmod 755 qmail-upq + +qmail-users.0: \ +qmail-users.5 + nroff -man qmail-users.5 > qmail-users.0 + +qmail-users.5: \ +qmail-users.9 conf-break conf-spawn + cat qmail-users.9 \ + | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \ + | sed s}BREAK}"`head -1 conf-break`"}g \ + | sed s}SPAWN}"`head -1 conf-spawn`"}g \ + > qmail-users.5 + +qmail.0: \ +qmail.7 + nroff -man qmail.7 > qmail.0 + +qmail.o: \ +compile qmail.c substdio.h readwrite.h wait.h exit.h fork.h fd.h \ +qmail.h substdio.h auto_qmail.h + ./compile qmail.c + +qreceipt: \ +load qreceipt.o headerbody.o hfield.o quote.o token822.o qmail.o \ +getln.a fd.a wait.a sig.a env.a stralloc.a alloc.a substdio.a error.a \ +str.a auto_qmail.o + ./load qreceipt headerbody.o hfield.o quote.o token822.o \ + qmail.o getln.a fd.a wait.a sig.a env.a stralloc.a alloc.a \ + substdio.a error.a str.a auto_qmail.o + +qreceipt.0: \ +qreceipt.1 + nroff -man qreceipt.1 > qreceipt.0 + +qreceipt.o: \ +compile qreceipt.c sig.h env.h substdio.h stralloc.h gen_alloc.h \ +subfd.h substdio.h getln.h alloc.h str.h hfield.h token822.h \ +gen_alloc.h error.h gen_alloc.h gen_allocdefs.h headerbody.h exit.h \ +open.h quote.h qmail.h substdio.h + ./compile qreceipt.c + +qsmhook: \ +load qsmhook.o sig.a case.a fd.a wait.a getopt.a env.a stralloc.a \ +alloc.a substdio.a error.a str.a + ./load qsmhook sig.a case.a fd.a wait.a getopt.a env.a \ + stralloc.a alloc.a substdio.a error.a str.a + +qsmhook.o: \ +compile qsmhook.c fd.h stralloc.h gen_alloc.h readwrite.h sgetopt.h \ +subgetopt.h wait.h env.h byte.h str.h alloc.h exit.h fork.h case.h \ +subfd.h substdio.h error.h substdio.h sig.h + ./compile qsmhook.c + +qsutil.o: \ +compile qsutil.c stralloc.h gen_alloc.h readwrite.h substdio.h \ +qsutil.h + ./compile qsutil.c + +quote.o: \ +compile quote.c stralloc.h gen_alloc.h str.h quote.h + ./compile quote.c + +rcpthosts.o: \ +compile rcpthosts.c cdb.h uint32.h byte.h open.h error.h control.h \ +constmap.h stralloc.h gen_alloc.h rcpthosts.h + ./compile rcpthosts.c + +readsubdir.o: \ +compile readsubdir.c readsubdir.h direntry.h fmt.h scan.h str.h \ +auto_split.h + ./compile readsubdir.c + +received.o: \ +compile received.c fmt.h qmail.h substdio.h now.h datetime.h \ +datetime.h date822fmt.h received.h + ./compile received.c + +remoteinfo.o: \ +compile remoteinfo.c byte.h substdio.h ip.h fmt.h timeoutconn.h \ +timeoutread.h timeoutwrite.h remoteinfo.h + ./compile remoteinfo.c + +scan_8long.o: \ +compile scan_8long.c scan.h + ./compile scan_8long.c + +scan_ulong.o: \ +compile scan_ulong.c scan.h + ./compile scan_ulong.c + +seek.a: \ +makelib seek_cur.o seek_end.o seek_set.o seek_trunc.o + ./makelib seek.a seek_cur.o seek_end.o seek_set.o \ + seek_trunc.o + +seek_cur.o: \ +compile seek_cur.c seek.h + ./compile seek_cur.c + +seek_end.o: \ +compile seek_end.c seek.h + ./compile seek_end.c + +seek_set.o: \ +compile seek_set.c seek.h + ./compile seek_set.c + +seek_trunc.o: \ +compile seek_trunc.c seek.h + ./compile seek_trunc.c + +select.h: \ +compile trysysel.c select.h1 select.h2 + ( ./compile trysysel.c >/dev/null 2>&1 \ + && cat select.h2 || cat select.h1 ) > select.h + rm -f trysysel.o trysysel + +sendmail: \ +load sendmail.o env.a getopt.a alloc.a substdio.a error.a str.a \ +auto_qmail.o + ./load sendmail env.a getopt.a alloc.a substdio.a error.a \ + str.a auto_qmail.o + +sendmail.o: \ +compile sendmail.c sgetopt.h subgetopt.h substdio.h subfd.h \ +substdio.h alloc.h auto_qmail.h exit.h env.h str.h + ./compile sendmail.c + +setup: \ +it man + ./install + +sgetopt.o: \ +compile sgetopt.c substdio.h subfd.h substdio.h sgetopt.h subgetopt.h \ +subgetopt.h + ./compile sgetopt.c + +shar: \ +FILES BLURB BLURB2 BLURB3 BLURB4 README FAQ INSTALL INSTALL.alias \ +INSTALL.ctl INSTALL.ids INSTALL.maildir INSTALL.mbox INSTALL.vsm \ +REMOVE.sendmail REMOVE.binmail TEST.deliver TEST.receive UPGRADE \ +THOUGHTS TODO THANKS CHANGES SECURITY INTERNALS SENDMAIL \ +PIC.local2alias PIC.local2ext PIC.local2local PIC.local2rem \ +PIC.local2virt PIC.nullclient PIC.relaybad PIC.relaygood \ +PIC.rem2local FILES VERSION SYSDEPS TARGETS Makefile BIN.README \ +BIN.Makefile BIN.setup idedit.c conf-break auto_break.h conf-spawn \ +auto_spawn.h chkspawn.c conf-split auto_split.h conf-patrn \ +auto_patrn.h conf-users conf-groups auto_uids.h auto_usera.h extra.h \ +addresses.5 except.1 bouncesaying.1 condredirect.1 dot-qmail.9 \ +envelopes.5 forgeries.7 forward.1 maildir2mbox.1 maildirmake.1 \ +maildirwatch.1 mailsubj.1 mbox.5 preline.1 qbiff.1 qmail-clean.8 \ +qmail-command.8 qmail-control.9 qmail-getpw.9 qmail-header.5 \ +qmail-inject.8 qmail-limits.9 qmail-local.8 qmail-log.5 \ +qmail-lspawn.8 qmail-newmrh.9 qmail-newu.9 qmail-pop3d.8 \ +qmail-popup.8 qmail-pw2u.9 qmail-qmqpc.8 qmail-qmqpd.8 qmail-qmtpd.8 \ +qmail-qread.8 qmail-qstat.8 qmail-queue.8 qmail-remote.8 \ +qmail-rspawn.8 qmail-send.9 qmail-showctl.8 qmail-smtpd.8 \ +qmail-start.9 qmail-tcpok.8 qmail-tcpto.8 qmail-users.9 qmail.7 \ +qreceipt.1 splogger.8 tcp-env.1 config.sh config-fast.sh \ +qmail-clean.c qmail-getpw.c qmail-inject.c qmail-local.c \ +qmail-lspawn.c qmail-newmrh.c qmail-newu.c qmail-pop3d.c \ +qmail-popup.c qmail-pw2u.c qmail-qmqpc.c qmail-qmqpd.c qmail-qmtpd.c \ +qmail-qread.c qmail-qstat.sh qmail-queue.c qmail-remote.c \ +qmail-rspawn.c qmail-send.c qmail-showctl.c qmail-smtpd.c \ +qmail-start.c qmail-tcpok.c qmail-tcpto.c spawn.c dnscname.c dnsfq.c \ +dnsip.c dnsmxip.c dnsptr.c hostname.c ipmeprint.c tcp-env.c \ +sendmail.c qreceipt.c qsmhook.c qbiff.c forward.c preline.c predate.c \ +except.c bouncesaying.c condredirect.c maildirmake.c maildir2mbox.c \ +maildirwatch.c splogger.c qail.sh elq.sh pinq.sh qmail-upq.sh \ +datemail.sh mailsubj.sh qlx.h rcpthosts.h rcpthosts.c commands.h \ +commands.c dnsdoe.h dnsdoe.c fmtqfn.h fmtqfn.c gfrom.h gfrom.c \ +myctime.h myctime.c newfield.h newfield.c qsutil.h qsutil.c \ +readsubdir.h readsubdir.c received.h received.c tcpto.h tcpto.c \ +tcpto_clean.c trigger.h trigger.c triggerpull.h triggerpull.c \ +trynpbg1.c trysyslog.c conf-cc conf-ld home.sh home+df.sh proc.sh \ +proc+df.sh binm1.sh binm2.sh binm3.sh binm1+df.sh binm2+df.sh \ +binm3+df.sh find-systype.sh make-compile.sh make-load.sh \ +make-makelib.sh trycpp.c warn-auto.sh auto-str.c auto-int.c \ +auto-int8.c auto-gid.c auto-uid.c hier.c install.c instcheck.c \ +install-big.c alloc.3 alloc.h alloc.c alloc_re.c case.3 case.h \ +case_diffb.c case_diffs.c case_lowerb.c case_lowers.c case_starts.c \ +cdb.3 cdb.h cdb_hash.c cdb_seek.c cdb_unpack.c cdbmake.h \ +cdbmake_add.c cdbmake_hash.c cdbmake_pack.c cdbmss.h cdbmss.c coe.3 \ +coe.h coe.c fd.h fd_copy.3 fd_copy.c fd_move.3 fd_move.c fifo_make.3 \ +fifo.h fifo.c trymkffo.c fork.h1 fork.h2 tryvfork.c now.3 now.h now.c \ +open.h open_append.c open_excl.c open_read.c open_trunc.c \ +open_write.c seek.h seek_cur.c seek_end.c seek_set.c seek_trunc.c \ +conf-qmail auto_qmail.h qmail.h qmail.c gen_alloc.h gen_allocdefs.h \ +stralloc.3 stralloc.h stralloc_eady.c stralloc_pend.c stralloc_copy.c \ +stralloc_opyb.c stralloc_opys.c stralloc_cat.c stralloc_catb.c \ +stralloc_cats.c stralloc_arts.c strerr.h strerr_sys.c strerr_die.c \ +substdio.h substdio.c substdi.c substdo.c substdio_copy.c subfd.h \ +subfderr.c subfdouts.c subfdout.c subfdins.c subfdin.c readwrite.h \ +exit.h timeoutconn.h timeoutconn.c timeoutread.h timeoutread.c \ +timeoutwrite.h timeoutwrite.c remoteinfo.h remoteinfo.c uint32.h1 \ +uint32.h2 tryulong32.c wait.3 wait.h wait_pid.c wait_nohang.c \ +trywaitp.c sig.h sig_alarm.c sig_block.c sig_catch.c sig_pause.c \ +sig_pipe.c sig_child.c sig_term.c sig_hup.c sig_misc.c sig_bug.c \ +trysgact.c trysgprm.c env.3 env.h env.c envread.c byte.h byte_chr.c \ +byte_copy.c byte_cr.c byte_diff.c byte_rchr.c byte_zero.c str.h \ +str_chr.c str_cpy.c str_diff.c str_diffn.c str_len.c str_rchr.c \ +str_start.c lock.h lock_ex.c lock_exnb.c lock_un.c tryflock.c getln.3 \ +getln.h getln.c getln2.3 getln2.c sgetopt.3 sgetopt.h sgetopt.c \ +subgetopt.3 subgetopt.h subgetopt.c error.3 error_str.3 error_temp.3 \ +error.h error.c error_str.c error_temp.c fmt.h fmt_str.c fmt_strn.c \ +fmt_uint.c fmt_uint0.c fmt_ulong.c scan.h scan_ulong.c scan_8long.c \ +slurpclose.h slurpclose.c quote.h quote.c hfield.h hfield.c \ +headerbody.h headerbody.c token822.h token822.c control.h control.c \ +datetime.3 datetime.h datetime.c datetime_un.c prioq.h prioq.c \ +date822fmt.h date822fmt.c dns.h dns.c trylsock.c tryrsolv.c ip.h ip.c \ +ipalloc.h ipalloc.c select.h1 select.h2 trysysel.c ndelay.h ndelay.c \ +ndelay_off.c direntry.3 direntry.h1 direntry.h2 trydrent.c prot.h \ +prot.c chkshsgr.c warn-shsgr tryshsgr.c ipme.h ipme.c trysalen.c \ +maildir.5 maildir.h maildir.c tcp-environ.5 constmap.h constmap.c + shar -m `cat FILES` > shar + chmod 400 shar + +sig.a: \ +makelib sig_alarm.o sig_block.o sig_catch.o sig_pause.o sig_pipe.o \ +sig_child.o sig_hup.o sig_term.o sig_bug.o sig_misc.o + ./makelib sig.a sig_alarm.o sig_block.o sig_catch.o \ + sig_pause.o sig_pipe.o sig_child.o sig_hup.o sig_term.o \ + sig_bug.o sig_misc.o + +sig_alarm.o: \ +compile sig_alarm.c sig.h + ./compile sig_alarm.c + +sig_block.o: \ +compile sig_block.c sig.h hassgprm.h + ./compile sig_block.c + +sig_bug.o: \ +compile sig_bug.c sig.h + ./compile sig_bug.c + +sig_catch.o: \ +compile sig_catch.c sig.h hassgact.h + ./compile sig_catch.c + +sig_child.o: \ +compile sig_child.c sig.h + ./compile sig_child.c + +sig_hup.o: \ +compile sig_hup.c sig.h + ./compile sig_hup.c + +sig_misc.o: \ +compile sig_misc.c sig.h + ./compile sig_misc.c + +sig_pause.o: \ +compile sig_pause.c sig.h hassgprm.h + ./compile sig_pause.c + +sig_pipe.o: \ +compile sig_pipe.c sig.h + ./compile sig_pipe.c + +sig_term.o: \ +compile sig_term.c sig.h + ./compile sig_term.c + +slurpclose.o: \ +compile slurpclose.c stralloc.h gen_alloc.h readwrite.h slurpclose.h \ +error.h + ./compile slurpclose.c + +socket.lib: \ +trylsock.c compile load + ( ( ./compile trylsock.c && \ + ./load trylsock -lsocket -lnsl ) >/dev/null 2>&1 \ + && echo -lsocket -lnsl || exit 0 ) > socket.lib + rm -f trylsock.o trylsock + +spawn.o: \ +compile chkspawn spawn.c sig.h wait.h substdio.h byte.h str.h \ +stralloc.h gen_alloc.h select.h exit.h alloc.h coe.h open.h error.h \ +auto_qmail.h auto_uids.h auto_spawn.h + ./chkspawn + ./compile spawn.c + +splogger: \ +load splogger.o substdio.a error.a str.a fs.a syslog.lib socket.lib + ./load splogger substdio.a error.a str.a fs.a `cat \ + syslog.lib` `cat socket.lib` + +splogger.0: \ +splogger.8 + nroff -man splogger.8 > splogger.0 + +splogger.o: \ +compile splogger.c error.h substdio.h subfd.h substdio.h exit.h str.h \ +scan.h fmt.h + ./compile splogger.c + +str.a: \ +makelib str_len.o str_diff.o str_diffn.o str_cpy.o str_chr.o \ +str_rchr.o str_start.o byte_chr.o byte_rchr.o byte_diff.o byte_copy.o \ +byte_cr.o byte_zero.o + ./makelib str.a str_len.o str_diff.o str_diffn.o str_cpy.o \ + str_chr.o str_rchr.o str_start.o byte_chr.o byte_rchr.o \ + byte_diff.o byte_copy.o byte_cr.o byte_zero.o + +str_chr.o: \ +compile str_chr.c str.h + ./compile str_chr.c + +str_cpy.o: \ +compile str_cpy.c str.h + ./compile str_cpy.c + +str_diff.o: \ +compile str_diff.c str.h + ./compile str_diff.c + +str_diffn.o: \ +compile str_diffn.c str.h + ./compile str_diffn.c + +str_len.o: \ +compile str_len.c str.h + ./compile str_len.c + +str_rchr.o: \ +compile str_rchr.c str.h + ./compile str_rchr.c + +str_start.o: \ +compile str_start.c str.h + ./compile str_start.c + +stralloc.a: \ +makelib stralloc_eady.o stralloc_pend.o stralloc_copy.o \ +stralloc_opys.o stralloc_opyb.o stralloc_cat.o stralloc_cats.o \ +stralloc_catb.o stralloc_arts.o + ./makelib stralloc.a stralloc_eady.o stralloc_pend.o \ + stralloc_copy.o stralloc_opys.o stralloc_opyb.o \ + stralloc_cat.o stralloc_cats.o stralloc_catb.o \ + stralloc_arts.o + +stralloc_arts.o: \ +compile stralloc_arts.c byte.h str.h stralloc.h gen_alloc.h + ./compile stralloc_arts.c + +stralloc_cat.o: \ +compile stralloc_cat.c byte.h stralloc.h gen_alloc.h + ./compile stralloc_cat.c + +stralloc_catb.o: \ +compile stralloc_catb.c stralloc.h gen_alloc.h byte.h + ./compile stralloc_catb.c + +stralloc_cats.o: \ +compile stralloc_cats.c byte.h str.h stralloc.h gen_alloc.h + ./compile stralloc_cats.c + +stralloc_copy.o: \ +compile stralloc_copy.c byte.h stralloc.h gen_alloc.h + ./compile stralloc_copy.c + +stralloc_eady.o: \ +compile stralloc_eady.c alloc.h stralloc.h gen_alloc.h \ +gen_allocdefs.h + ./compile stralloc_eady.c + +stralloc_opyb.o: \ +compile stralloc_opyb.c stralloc.h gen_alloc.h byte.h + ./compile stralloc_opyb.c + +stralloc_opys.o: \ +compile stralloc_opys.c byte.h str.h stralloc.h gen_alloc.h + ./compile stralloc_opys.c + +stralloc_pend.o: \ +compile stralloc_pend.c alloc.h stralloc.h gen_alloc.h \ +gen_allocdefs.h + ./compile stralloc_pend.c + +strerr.a: \ +makelib strerr_sys.o strerr_die.o + ./makelib strerr.a strerr_sys.o strerr_die.o + +strerr_die.o: \ +compile strerr_die.c substdio.h subfd.h substdio.h exit.h strerr.h + ./compile strerr_die.c + +strerr_sys.o: \ +compile strerr_sys.c error.h strerr.h + ./compile strerr_sys.c + +subfderr.o: \ +compile subfderr.c readwrite.h substdio.h subfd.h substdio.h + ./compile subfderr.c + +subfdin.o: \ +compile subfdin.c readwrite.h substdio.h subfd.h substdio.h + ./compile subfdin.c + +subfdins.o: \ +compile subfdins.c readwrite.h substdio.h subfd.h substdio.h + ./compile subfdins.c + +subfdout.o: \ +compile subfdout.c readwrite.h substdio.h subfd.h substdio.h + ./compile subfdout.c + +subfdouts.o: \ +compile subfdouts.c readwrite.h substdio.h subfd.h substdio.h + ./compile subfdouts.c + +subgetopt.o: \ +compile subgetopt.c subgetopt.h + ./compile subgetopt.c + +substdi.o: \ +compile substdi.c substdio.h byte.h error.h + ./compile substdi.c + +substdio.a: \ +makelib substdio.o substdi.o substdo.o subfderr.o subfdout.o \ +subfdouts.o subfdin.o subfdins.o substdio_copy.o + ./makelib substdio.a substdio.o substdi.o substdo.o \ + subfderr.o subfdout.o subfdouts.o subfdin.o subfdins.o \ + substdio_copy.o + +substdio.o: \ +compile substdio.c substdio.h + ./compile substdio.c + +substdio_copy.o: \ +compile substdio_copy.c substdio.h + ./compile substdio_copy.c + +substdo.o: \ +compile substdo.c substdio.h str.h byte.h error.h + ./compile substdo.c + +syslog.lib: \ +trysyslog.c compile load + ( ( ./compile trysyslog.c && \ + ./load trysyslog -lgen ) >/dev/null 2>&1 \ + && echo -lgen || exit 0 ) > syslog.lib + rm -f trysyslog.o trysyslog + +systype: \ +find-systype trycpp.c + ./find-systype > systype + +tcp-env: \ +load tcp-env.o dns.o remoteinfo.o timeoutread.o timeoutwrite.o \ +timeoutconn.o ip.o ipalloc.o case.a ndelay.a sig.a env.a getopt.a \ +stralloc.a alloc.a substdio.a error.a str.a fs.a dns.lib socket.lib + ./load tcp-env dns.o remoteinfo.o timeoutread.o \ + timeoutwrite.o timeoutconn.o ip.o ipalloc.o case.a ndelay.a \ + sig.a env.a getopt.a stralloc.a alloc.a substdio.a error.a \ + str.a fs.a `cat dns.lib` `cat socket.lib` + +tcp-env.0: \ +tcp-env.1 + nroff -man tcp-env.1 > tcp-env.0 + +tcp-env.o: \ +compile tcp-env.c sig.h stralloc.h gen_alloc.h str.h env.h fmt.h \ +scan.h subgetopt.h ip.h dns.h byte.h remoteinfo.h exit.h case.h + ./compile tcp-env.c + +tcp-environ.0: \ +tcp-environ.5 + nroff -man tcp-environ.5 > tcp-environ.0 + +tcpto.o: \ +compile tcpto.c tcpto.h open.h lock.h seek.h now.h datetime.h ip.h \ +byte.h datetime.h readwrite.h + ./compile tcpto.c + +tcpto_clean.o: \ +compile tcpto_clean.c tcpto.h open.h substdio.h readwrite.h + ./compile tcpto_clean.c + +timeoutconn.o: \ +compile timeoutconn.c ndelay.h select.h error.h readwrite.h ip.h \ +byte.h timeoutconn.h + ./compile timeoutconn.c + +timeoutread.o: \ +compile timeoutread.c timeoutread.h select.h error.h readwrite.h + ./compile timeoutread.c + +timeoutwrite.o: \ +compile timeoutwrite.c timeoutwrite.h select.h error.h readwrite.h + ./compile timeoutwrite.c + +token822.o: \ +compile token822.c stralloc.h gen_alloc.h alloc.h str.h token822.h \ +gen_alloc.h gen_allocdefs.h + ./compile token822.c + +trigger.o: \ +compile trigger.c select.h open.h trigger.h hasnpbg1.h + ./compile trigger.c + +triggerpull.o: \ +compile triggerpull.c ndelay.h open.h triggerpull.h + ./compile triggerpull.c + +uint32.h: \ +tryulong32.c compile load uint32.h1 uint32.h2 + ( ( ./compile tryulong32.c && ./load tryulong32 && \ + ./tryulong32 ) >/dev/null 2>&1 \ + && cat uint32.h2 || cat uint32.h1 ) > uint32.h + rm -f tryulong32.o tryulong32 + +wait.a: \ +makelib wait_pid.o wait_nohang.o + ./makelib wait.a wait_pid.o wait_nohang.o + +wait_nohang.o: \ +compile wait_nohang.c haswaitp.h + ./compile wait_nohang.c + +wait_pid.o: \ +compile wait_pid.c error.h haswaitp.h + ./compile wait_pid.c diff --git a/PIC.local2alias b/PIC.local2alias new file mode 100644 index 0000000..75cff56 --- /dev/null +++ b/PIC.local2alias @@ -0,0 +1,37 @@ + Original message: + + To: help + Hi. + +qmail-inject Fill in the complete envelope and header: + + | (envelope) from joe@heaven.af.mil to help@heaven.af.mil + | From: joe@heaven.af.mil + | To: help@heaven.af.mil + | + | Hi. + V + +qmail-queue Store message safely on disk. + Trigger qmail-send. + | + V + +qmail-send Look at envelope recipient, help@heaven.af.mil. + | Is heaven.af.mil in locals? Yes. + | Deliver locally to help@heaven.af.mil. + V + +qmail-lspawn ./Mailbox + + | Look at mailbox name, help. + | Is help listed in qmail-users? No. + | Is there a help account? No. + | Give control of the message to alias. + | Run qmail-local. + V + +qmail-local alias ~alias help - help heaven.af.mil joe@heaven.af.mil ./Mailbox + + Does ~alias/.qmail-help exist? Yes: "john". + Forward message to john. diff --git a/PIC.local2ext b/PIC.local2ext new file mode 100644 index 0000000..a8bf644 --- /dev/null +++ b/PIC.local2ext @@ -0,0 +1,41 @@ + Original message: + + To: fred-sos + Hi. + +qmail-inject Fill in the complete envelope and header: + + | (envelope) from joe@heaven.af.mil to fred-sos@heaven.af.mil + | From: joe@heaven.af.mil + | To: fred-sos@heaven.af.mil + | + | Hi. + V + +qmail-queue Store message safely on disk. + Trigger qmail-send. + | + V + +qmail-send Look at envelope recipient, fred-sos@heaven.af.mil. + | Is heaven.af.mil in locals? Yes. + | Deliver locally to fred-sos@heaven.af.mil. + V + +qmail-lspawn ./Mailbox + + | Look at mailbox name, fred-sos. + | Is fred-sos listed in qmail-users? No. + | Is there a fred-sos account? No. + | Is there a fred account? Yes. + | Is fred's uid nonzero? Yes. + | Is ~fred visible to the qmailp user? Yes. + | Is ~fred owned by fred? Yes. + | Give control of the message to fred. + | Run qmail-local. + V + +qmail-local fred ~fred fred-sos - sos heaven.af.mil joe@heaven.af.mil ./Mailbox + + Does ~fred/.qmail-sos exist? Yes: "./Extramail". + Write message to ./Extramail in mbox format. diff --git a/PIC.local2local b/PIC.local2local new file mode 100644 index 0000000..3a067e0 --- /dev/null +++ b/PIC.local2local @@ -0,0 +1,40 @@ + Original message: + + To: fred + Hi. + +qmail-inject Fill in the complete envelope and header: + + | (envelope) from joe@heaven.af.mil to fred@heaven.af.mil + | From: joe@heaven.af.mil + | To: fred@heaven.af.mil + | + | Hi. + V + +qmail-queue Store message safely on disk. + Trigger qmail-send. + | + V + +qmail-send Look at envelope recipient, fred@heaven.af.mil. + | Is heaven.af.mil in locals? Yes. + | Deliver locally to fred@heaven.af.mil. + V + +qmail-lspawn ./Mailbox + + | Look at mailbox name, fred. + | Is fred listed in qmail-users? No. + | Is there a fred account? Yes. + | Is fred's uid nonzero? Yes. + | Is ~fred visible to the qmailp user? Yes. + | Is ~fred owned by fred? Yes. + | Give control of the message to fred. + | Run qmail-local. + V + +qmail-local fred ~fred fred '' '' heaven.af.mil joe@heaven.af.mil ./Mailbox + + Does ~fred/.qmail exist? No. + Write message to ./Mailbox in mbox format. diff --git a/PIC.local2rem b/PIC.local2rem new file mode 100644 index 0000000..6857af5 --- /dev/null +++ b/PIC.local2rem @@ -0,0 +1,38 @@ + Original message: + + To: bill@irs.gov + Hi. + +qmail-inject Fill in the complete envelope and header: + + | (envelope) from joe@heaven.af.mil to bill@irs.gov + | From: joe@heaven.af.mil + | To: bill@irs.gov + | + | Hi. + V + +qmail-queue Store message safely on disk. + Trigger qmail-send. + | + V + +qmail-send Look at envelope recipient, bill@irs.gov. + | Is irs.gov in locals? No. + | Is bill@irs.gov in virtualdomains? No. + | Is irs.gov in virtualdomains? No. + | Is .gov in virtualdomains? No. + | Deliver remotely to bill@irs.gov. + V + +qmail-rspawn Run qmail-remote. + + | + V + +qmail-remote Look at host name, irs.gov. + Is irs.gov listed in smtproutes? No. + Look up DNS MX/A for irs.gov and connect to it by SMTP: + + MAIL FROM: + RCPT TO: diff --git a/PIC.local2virt b/PIC.local2virt new file mode 100644 index 0000000..60f80c8 --- /dev/null +++ b/PIC.local2virt @@ -0,0 +1,44 @@ + Original message: + + To: dude@tommy.gov + Hi. + +qmail-inject Fill in the complete envelope and header: + + | (envelope) from joe@heaven.af.mil to dude@tommy.gov + | From: joe@heaven.af.mil + | To: dude@tommy.gov + | + | Hi. + V + +qmail-queue Store message safely on disk. + Trigger qmail-send. + | + V + +qmail-send Look at envelope recipient, dude@tommy.gov. + | Is tommy.gov in locals? No. + | Is dude@tommy.gov in virtualdomains? No. + | Is tommy.gov in virtualdomains? Yes: "tommy.gov:fred". + | Deliver locally to fred-dude@tommy.gov. + V + +qmail-lspawn ./Mailbox + + | Look at mailbox name, fred-dude. + | Is fred-dude listed in qmail-users? No. + | Is there a fred-dude account? No. + | Is there a fred account? Yes. + | Is fred's uid nonzero? Yes. + | Is ~fred visible to the qmailp user? Yes. + | Is ~fred owned by fred? Yes. + | Give control of the message to fred. + | Run qmail-local. + V + +qmail-local fred ~fred fred-dude - dude tommy.gov joe@heaven.af.mil ./Mailbox + + Does ~fred/.qmail-dude exist? No. + Does ~fred/.qmail-default exist? Yes: "./Mail.tommy". + Write message to ./Mail.tommy in mbox format. diff --git a/PIC.nullclient b/PIC.nullclient new file mode 100644 index 0000000..a90d7cb --- /dev/null +++ b/PIC.nullclient @@ -0,0 +1,38 @@ + Original message: + + To: bill@irs.gov + Hi. + +qmail-inject Fill in the complete envelope and header: + + | (envelope) from joe@heaven.af.mil to bill@irs.gov + | From: joe@heaven.af.mil + | To: bill@irs.gov + | + | Hi. + V + +qmail-queue Store message safely on disk. + Trigger qmail-send. + | + V + +qmail-send Look at envelope recipient, bill@irs.gov. + | Is irs.gov in locals? No. + | Is bill@irs.gov in virtualdomains? No. + | Is irs.gov in virtualdomains? No. + | Is .gov in virtualdomains? No. + | Deliver remotely to bill@irs.gov. + V + +qmail-rspawn Run qmail-remote. + + | + V + +qmail-remote Look at host name, irs.gov. + Is irs.gov listed in smtproutes? Yes: ":bigbang.af.mil". + Look up DNS A for bigbang.af.mil and connect by SMTP: + + MAIL FROM: + RCPT TO: diff --git a/PIC.relaybad b/PIC.relaybad new file mode 100644 index 0000000..513f74f --- /dev/null +++ b/PIC.relaybad @@ -0,0 +1,8 @@ +qmail-smtpd Receive message by SMTP from another host: + + MAIL FROM: + RCPT TO: + + Is $RELAYCLIENT set? No. + Is irs.gov in rcpthosts? No. + Reject RCPT. diff --git a/PIC.relaygood b/PIC.relaygood new file mode 100644 index 0000000..0d62fa9 --- /dev/null +++ b/PIC.relaygood @@ -0,0 +1,33 @@ +qmail-smtpd Receive message by SMTP from another host: + + | MAIL FROM: + | RCPT TO: + | + | Is $RELAYCLIENT set? Yes: "". + | Accept RCPT. + V + +qmail-queue Store message safely on disk. + Trigger qmail-send. + | + V + +qmail-send Look at envelope recipient, bill@irs.gov. + | Is irs.gov in locals? No. + | Is bill@irs.gov in virtualdomains? No. + | Is irs.gov in virtualdomains? No. + | Is .gov in virtualdomains? No. + | Deliver remotely to bill@irs.gov. + V + +qmail-rspawn Run qmail-remote. + + | + V + +qmail-remote Look at host name, irs.gov. + Is irs.gov listed in smtproutes? No. + Look up DNS MX/A for irs.gov and connect to it by SMTP: + + MAIL FROM: + RCPT TO: diff --git a/PIC.rem2local b/PIC.rem2local new file mode 100644 index 0000000..62fe61a --- /dev/null +++ b/PIC.rem2local @@ -0,0 +1,36 @@ +qmail-smtpd Receive message by SMTP from another host: + + | MAIL FROM: + | RCPT TO: + | + | Is $RELAYCLIENT set? No. + | Is heaven.af.mil in rcpthosts? Yes. + | Accept RCPT. + V + +qmail-queue Store message safely on disk. + Trigger qmail-send. + | + V + +qmail-send Look at envelope recipient, joe@heaven.af.mil. + | Is heaven.af.mil in locals? Yes. + | Deliver locally to joe@heaven.af.mil. + V + +qmail-lspawn ./Mailbox + + | Look at mailbox name, joe. + | Is joe listed in qmail-users? No. + | Is there a joe account? Yes. + | Is joe's uid nonzero? Yes. + | Is ~joe visible to the qmailp user? Yes. + | Is ~joe owned by joe? Yes. + | Give control of the message to joe. + | Run qmail-local. + V + +qmail-local joe ~joe joe '' '' heaven.af.mil bill@irs.gov ./Mailbox + + Does ~joe/.qmail exist? No. + Write message to ./Mailbox in mbox format. diff --git a/README b/README new file mode 100644 index 0000000..e282597 --- /dev/null +++ b/README @@ -0,0 +1,295 @@ +netqmail 1.06 +20071130 +James Craig Burley claims copyright on the qmail-isoc patch. For +more details, see COPYRIGHT. + +Apart from James' copyrights, no other copyright is claimed by the +distributors of netqmail for changes from qmail 1.03 to netqmail 1.05. +Daniel J. Bernstein has abandoned copyright for qmail 1.03. +D. J. Bernstein did not participate in, nor has he been asked to +approve of this distribution. + +With this distribution, we thank Daniel Bernstein for his dedication +of qmail to the public domain. We have taken advantage of this event +to remove the need to apply a patch. We will distribute a Netqmail 1.07 +shortly which includes enhancements. + +Netqmail 1.06 is produced by this motley krewe: + +Russ Nelson +Charles Cazabon +Dave Sill +Peter Samuel +Henning Brauer +Andrew Richards +Richard Lyons +John Levine +Scott Gifford +Kyle Wheeler +Wayne Marshall + +qmail is a secure, reliable, efficient, simple message transfer agent. +It is meant as a replacement for the entire sendmail-binmail system on +typical Internet-connected UNIX hosts. See BLURB, BLURB2, BLURB3, and +BLURB4 for more detailed advertisements. + +INSTALL says how to set up and test qmail. If you're upgrading from a +previous version, read UPGRADE instead. + +See PIC.* for some ``end-to-end'' pictures of mail flowing through the +qmail system. + +See http://pobox.com/~djb/qmail.html for other qmail-related software +and a pointer to the qmail mailing list. + +Other documentation: http://pobox.com/~djb/proto.html shows solutions to +several Internet mail problems; many of these solutions are implemented +in qmail. CHANGES and THANKS show how qmail has changed since it was +first released. SECURITY, INTERNALS, THOUGHTS, and TODO record many of +the qmail design decisions. + +The rest of this file is a list of systypes where various versions of +qmail have been reported to work. 0.96 was the final gamma version; 1.00 +had exactly the same code as 0.96. To see your systype, make systype; +cat systype. + +1.00: a.ux-3.0-svr2-:-:-:mc68030-:- (tnx RF) +1.01: aix-3-2-:-:-:000000406300-:- (tnx DG) +1.01: aix-3-2-:-:-:000011216700-:- (tnx JLB) +1.01: aix-4-1-:-:-:000041574c00-:- (tnx M2H) +1.01: aix-4-1-:-:-:000088581000-:- (tnx HJB) +1.01: aix-4-1-:-:-:002b51134c00-:- (tnx MP) +1.00: aix-4-1-:-:-:00910033a000-:- (tnx KJJ) +1.01: aix-4-2-:-:-:000055247900-:- (tnx JLB) +1.01: aix-4-2-:-:-:000062295800-:- (tnx TD) +1.01: aix-4-2-:-:-:000136094c00-:- (tnx T2U) +1.00: aix-4-2-:-:-:000205254600-:- (tnx MGM) +1.01: aix-4-2-:-:-:005255bc4c00-:- (tnx DS) +1.01: aix-4-2-:-:-:006030944c00-:- +1.01: bsd.386-1.1-0-:i386-:-:i386-:- (tnx T2M) +1.01: bsd.os-2.0-:i386-:-:pentium-:- (tnx MSS) +1.01: bsd.os-2.0.1-:i386-:-:i486-:- (tnx KR) +0.96: bsd.os-2.1-:i386-:-:-:- (tnx DAR) +1.00: bsd.os-2.1-:i386-:-:i486-:- (tnx RJC) +0.96: bsd.os-2.1-:i386-:-:pentium-:- (tnx UO) +1.01: bsd.os-3.0-:i386-:-:-:- (tnx VU) +1.01: bsd.os-3.0-:i386-:-:pentium-:- (tnx RJO) +1.01: bsd.os-3.1-:i386-:-:pentium-:- (tnx ABC) +1.01: bsd.os-3.1-:i386-:-:pentium.ii-:- (tnx UO) +0.96: dgux-5.4r2.01-generic-:-:-:aviion-:- (tnx HWM) +1.01: freebsd-2.1.0-release-:i386-:-:i486-dx-:- (tnx VV) +1.01: freebsd-2.1.0-release-:i386-:-:i486.dx2-:- (tnx JLB) +1.00: freebsd-2.1.0-release-:i386-:-:i486dx-:- (tnx chrisj=???) +1.01: freebsd-2.1.0-release-:i386-:-:pentium.735\90.or.815\100-:- (tnx MBS) +1.01: freebsd-2.1.5-release-:i386-:-:i486-dx-:- (tnx B1F) +0.96: freebsd-2.1.5-release-:i386-:-:i486dx-:- (tnx FN) +1.01: freebsd-2.1.5-release-:i386-:-:unknown.-:- (tnx BMF) +1.00: freebsd-2.1.6-release-:i386-:-:-:- (tnx TM) +0.96: freebsd-2.1.6-release-:i386-:-:Pentium-Pro.150-:- (tnx CH) +1.01: freebsd-2.1.6-release-:i386-:-:cy486dlc-:- (tnx M3H) +0.96: freebsd-2.1.6.1-release-:i386-:-:pentium.735\90.or.815\100-:- (tnx MF) +1.01: freebsd-2.1.7-release-:i386-:-:i486-dx-:- (tnx AAF) +1.00: freebsd-2.1.7-release-:i386-:-:pentium.735\90.or.815\100-:- (tnx JBB) +1.01: freebsd-2.1.7-release-:i386-:-:pentium.815\100-:- (tnx B1F) +1.01: freebsd-2.2-970422-releng-:i386-:-:-:- (tnx TM) +1.00: freebsd-2.2-release-:i386-:-:-:- (tnx MT) +1.01: freebsd-2.2-stable-:i386-:-:cyrix.5x86-:- (tnx A2B) +1.01: freebsd-2.2-stable-:i386-:-:pentium-:- (tnx gary@systemics=???) +1.01: freebsd-2.2.1-release-:i386-:-:-:- (tnx M2R) +1.01: freebsd-2.2.1-release-:i386-:-:i486-dx-:- (tnx PGR) +1.00: freebsd-2.2.1-release-:i386-:-:i486.dx2-:- (tnx BR) +1.01: freebsd-2.2.1-release-:i386-:-:pentium-:- (tnx REB) +1.01: freebsd-2.2.1-release-:i386-:-:pentium.pro-:- (tnx JS) +1.01: freebsd-2.2.2-release-:i386-:-:amd.am5x86.write-through-:- (tnx AGB) +1.01: freebsd-2.2.2-release-:i386-:-:i486-dx-:- (tnx A2L) +1.01: freebsd-2.2.2-release-:i386-:-:i486.dx2-:- (tnx D3S) +1.01: freebsd-2.2.2-release-:i386-:-:pentium-:- (tnx B2F) +1.01: freebsd-2.2.2-release-:i386-:-:pentium.pro-:- (tnx M2G) +1.01: freebsd-2.2.5-release-:i386-:-:i486-dx-:- (tnx R2N) +1.01: freebsd-2.2.5-release-:i386-:-:i486.dx2-:- (tnx AY) +1.01: freebsd-2.2.5-release-:i386-:-:pentium.pro-:- (tnx AI) +1.01: freebsd-2.2.5-stable-:i386-:-:i486.dx2-:- (tnx JK) +1.01: freebsd-2.2.5-stable-:i386-:-:pentium-:- (tnx root@defiant=???) +1.01: freebsd-2.2.6-release-:i386-:-:-:- (tnx TM) +1.01: freebsd-2.2.6-release-:i386-:-:amd.am5x86.write-through-:- (tnx root@skully=???) +1.00: freebsd-3.0-970209-snap-:i386-:-:-:- (tnx YF) +1.01: freebsd-3.0-970428-snap-:i386-:-:pentium-:- (tnx M3S) +1.01: freebsd-3.0-970807-snap-:i386-:-:amd.k6-:- (tnx KMD) +1.01: freebsd-3.0-980309-snap-:i386-:-:pentium-:- (tnx MM) +1.01: freebsd-3.0-current-:i386-:-:pentium-:- (tnx KB) +1.01: hp-ux-a.09.05-a-:-:-:9000.712-:- (tnx SV) +1.01: hp-ux-a.09.07-a-:-:-:9000.712-:- (tnx LB) +1.00: hp-ux-b.09.00-a-:-:-:9000.360-:- (tnx VV) +1.01: hp-ux-b.10.20-a-:-:-:9000.755-:- (tnx BCK) +1.01: irix-5.3-11091812-:-:-:ip22-:- (tnx JL) +1.01: irix-6.2-03131015-:-:-:ip22-:- (tnx DS) +1.01: irix64-6.2-03131016-:-:-:ip19-:- (tnx AH) +1.01: irix64-6.2-06101031-:-:-:ip28-:- (tnx DB) +1.01: linux-1.2.13-:i386-:-:i486-:- (tnx RF) +1.01: linux-1.2.13-:i386-:-:pentium-:- (tnx MEE) +1.01: linux-1.99.4-:i386-:-:pentium-:- (tnx C2H) +1.01: linux-2.0.0-:i386-:-:i486-:- (tnx kragen@gentle=???) +1.01: linux-2.0.0-:i386-:-:pentium-:- (tnx MJD) +1.01: linux-2.0.6-:i386-:-:pentium-:- +1.00: linux-2.0.6-:i386-:-:ppro-:- (tnx MR) +1.01: linux-2.0.7-:i386-:-:i486-:- (tnx TLM) +1.01: linux-2.0.9-:i386-:-:i486-:- (tnx VBM) +0.96: linux-2.0.13-:i386-:-:pentium-:- (tnx BW) +1.01: linux-2.0.15-:i386-:-:i486-:- (tnx JCD) +1.01: linux-2.0.18-:i386-:-:i486-:- (tnx tk@avalon=???) +1.01: linux-2.0.18-:i386-:-:pentium-:- (tnx root@webtvchat=???) +1.00: linux-2.0.22-:i386-:-:pentium-:- (tnx MDI) +1.00: linux-2.0.23-:i386-:-:i486-:- (tnx B2L) +1.01: linux-2.0.24-:i386-:-:i486-:- (tnx GLM) +1.00: linux-2.0.24-:i386-:-:pentium-:- (tnx VV) +0.96: linux-2.0.25-:i386-:-:i486-:- (tnx BDB) +1.01: linux-2.0.25-:i386-:-:pentium-:- (tnx KA) +0.93: linux-2.0.26-:i386-:-:i486-:- (tnx blynch@texas=???) +1.01: linux-2.0.26-:i386-:-:pentium-:- (tnx robbie@opus=???) +1.00: linux-2.0.27-:-:-:sparc-:- (tnx SVD) +1.00: linux-2.0.27-:i386-:-:i386-:- (tnx ECG) +1.01: linux-2.0.27-:i386-:-:i486-:- (tnx BN) +1.01: linux-2.0.27-:i386-:-:pentium-:- (tnx EK) +1.01: linux-2.0.27-:i386-:-:ppro-:- (tnx L3L) +1.01: linux-2.0.28-:i386-:-:i486-:- (tnx AAF) +1.00: linux-2.0.28-:i386-:-:pentium-:- (tnx root@duggy=???) +1.01: linux-2.0.28-:i386-:-:ppro-:- (tnx S3T) +1.01: linux-2.0.28-osfmach3-:-:-:ppc-:- (tnx CG) +1.01: linux-2.0.29-:alpha-:-:alpha-:- (tnx MB) +1.01: linux-2.0.29-:i386-:-:i386-:- (tnx AJK) +1.01: linux-2.0.29-:i386-:-:i486-:- (tnx FPL) +1.01: linux-2.0.29-:i386-:-:pentium-:- (tnx FW) +1.00: linux-2.0.29-:i386-:-:ppro-:- (tnx MMM) +1.01: linux-2.0.30-:-:-:sparc-:- (tnx J2P) +1.01: linux-2.0.30-:alpha-:-:alpha-:- (tnx WS) +1.01: linux-2.0.30-:i386-:-:i386-:- (tnx OK) +1.00: linux-2.0.30-:i386-:-:i486-:- (tnx KUT) +1.01: linux-2.0.30-:i386-:-:i486-:- (tnx PK) +1.01: linux-2.0.30-:i386-:-:pentium-:- (tnx AV) +1.00: linux-2.0.30-:i386-:-:ppro-:- (tnx root@gate=???) +1.01: linux-2.0.30-osfmach3-:-:-:ppc-:- (tnx PTW) +1.01: linux-2.0.30u11-:i386-:-:pentium-:- (tnx JTB) +1.01: linux-2.0.31-:i386-:-:i486-:- (tnx SAE) +1.01: linux-2.0.31-:i386-:-:pentium-:- (tnx B3W) +1.01: linux-2.0.31-:i386-:-:ppro-:- (tnx JAK) +1.01: linux-2.0.32-:-:-:ie86-:- (tnx root@vmlinuz=???) +1.01: linux-2.0.32-:alpha-:-:alpha-:- (tnx NR) +1.01: linux-2.0.32-:i386-:-:i486-:- (tnx SC) +1.01: linux-2.0.32-:i386-:-:pentium-:- (tnx HT) +1.01: linux-2.0.32-:i386-:-:ppro-:- (tnx RK) +1.01: linux-2.0.33-:i386-:-:i486-:- (tnx RAB) +1.01: linux-2.0.33-:i386-:-:pentium-:- (tnx AF) +1.01: linux-2.0.33-:i386-:-:ppro-:- (tnx B2W) +1.01: linux-2.1.9-:i386-:-:i486-:- (tnx SJB) +1.01: linux-2.1.10-:i386-:-:i486-:- (tnx JB) +0.96: linux-2.1.13-:i386-:-:i486-:- (tnx ML) +0.96: linux-2.1.14-:i386-:-:pentium-:- (tnx SCW) +0.96: linux-2.1.23-:i386-:-:pentium-:- (tnx JF) +1.01: linux-2.1.24-:-:-:ppc-:- (tnx meta=???) +0.96: linux-2.1.25-:i386-:-:i486-:- (tnx JBF) +0.96: linux-2.1.25-:i386-:-:pentium-:- (tnx UO) +1.00: linux-2.1.26-:i386-:-:i486-:- (tnx DK) +1.00: linux-2.1.27-:i386-:-:pentium-:- (tnx JF) +1.01: linux-2.1.28-:i386-:-:i486-:- (tnx HDG) +1.00: linux-2.1.28-:i386-:-:pentium-:- (tnx RGS) +1.00: linux-2.1.29-:i386-:-:i486-:- (tnx SJW) +1.01: linux-2.1.35-:i386-:-:pentium-:- (tnx JF) +1.01: linux-2.1.36-:i386-:-:i486-:- (tnx ML) +1.01: linux-2.1.42-:i386-:-:i486-:- (tnx wtanaka=???) +1.01: linux-2.1.46-:i386-:-:pentium-:- (tnx VR) +1.01: linux-2.1.51-:i386-:-:pentium-:- (tnx KO) +1.01: linux-2.1.61-:i386-:-:i486-:- (tnx RO) +1.01: linux-2.1.65-:i386-:-:i486-:- (tnx F2T) +1.01: linux-2.1.71-:i386-:-:ppro-:- (tnx MJG) +1.01: linux-2.1.78-:i386-:-:pentium-:- (tnx AS) +1.01: linux-2.1.82-:i386-:-:pentium-:- (tnx AY) +1.01: linux-2.1.85-:i386-:-:pentium-:- (tnx PJH) +1.00: machten-4-0.4-:-:-:powerpc-:- (tnx RAM) +1.01: netbsd-1.1-:i386-:-:pentium.(genuineintel.586-class.cpu)-:- (tnx GL) +1.01: netbsd-1.2-:hp300-:-:-:- (tnx ML) +1.01: netbsd-1.2-:i386-:-:i486dx.(genuineintel.486-class.cpu)-:- (tnx T2K) +0.96: netbsd-1.2-:i386-:-:pentium.(genuineintel.586-class.cpu)-:- (tnx GH) +1.01: netbsd-1.2.1-:mac68k-:-:apple.macintosh.se/30..(68030)-:- (tnx HM) +1.01: netbsd-1.2.1-:sparc-:-:fmi,mb86904.@.110.mhz,.on-chip.fpu-:- (tnx ZU) +0.96: netbsd-1.2c-:pmax-:-:-:- (tnx JLW) +1.01: netbsd-1.3-:hp300-:-:hp.9000/433.(33mhz.mc68040.cpu+mmu+fpu,.4k.on-chip.physical.i/d.caches)-:- (tnx TB) +1.01: netbsd-1.3.1-:sun3-:-:sun.3/60-:- (tnx MBS) +1.01: netbsd-1.3_alpha-:i386-:-:intel.pentium.(p54c).(586-class)-:- (tnx GL) +1.01: nextstep-3.1-:mc680x0-:-:68040-:- (tnx JRY) +1.01: nextstep-3.3-:hppa-:-:7100lc-:- +1.01: nextstep-3.3-:i386-:-:pentium-:- (tnx HM) +1.01: nextstep-3.3-:mc680x0-:-:68040-:- (tnx WEB) +1.01: nextstep-4.1-:mc680x0-:-:68040-:- (tnx FN) +1.00: openbsd-2.0-hoth#0-:openbsd.i386-:-:i386-:- (tnx MBS) +1.00: openbsd-2.0-mr_potatoe_head#2-:openbsd.i386-:-:i386-:- (tnx JJMK) +0.96: openbsd-2.0-puma#1-:openbsd.m68k-:-:mac68k-:- (tnx AKB) +1.01: openbsd-2.1-asgard#1-:openbsd.i386-:-:i386-:- (tnx ETT) +1.01: openbsd-2.1-generic#71-:openbsd.sparc-:-:sparc-:- (tnx MMM2) +1.01: openbsd-2.1-katana#2-:openbsd.i386-:-:i386-:- (tnx CHR) +1.01: openbsd-2.1-puma#0-:openbsd.m68k-:-:mac68k-:- (tnx AKB) +1.01: openbsd-2.2-ele#2-:openbsd.i386-:-:i386-:- (tnx RC) +1.01: openbsd-2.2-generic#424-:openbsd.i386-:-:i386-:- (tnx ETT) +1.01: osf1-v2.0-240-:-:-:alpha-:- (tnx JF) +1.00: osf1-v3.2-148-:-:-:alpha-:- (tnx DL) +1.01: osf1-v3.2-148-:-:-:alpha-:- (tnx RSK) +1.01: osf1-v3.2-41-:-:-:alpha-:- (tnx MSD) +1.01: osf1-v3.2-mp-4.2-:-:-:alpha-:- (tnx MSD) +1.01: osf1-v4.0-386-:-:-:alpha-:- (tnx TEE) +1.01: osf1-v4.0-464-:-:-:alpha-:- (tnx AWB) +1.01: osf1-v4.0-564-:-:-:alpha-:- (tnx A2P) +1.01: osf1-v4.0-564.32-:-:-:alpha-:- (tnx TLF) +1.01: osf1-v4.0-878-:-:-:alpha-:- (tnx BJM) +1.01: sco_sv-3.2-2-:-:-:i386-:- (tnx PW) +1.01: sinix-l-5.41-d0005-:-:-:mx300i-:- (tnx IH) +1.01: sunos-4.1.1-1-:mc68020-:sun3-:sun3-:sun3- (tnx JWB) +1.01: sunos-4.1.1-1-:mc68020-:sun3-:sun3x-:sun3x- (tnx TT) +1.01: sunos-4.1.3-jl-2-:sparc-:sun4-:sun4c-:sun4c- (tnx T2K) +1.01: sunos-4.1.3_u1-1-:sparc-:sun4-:sun4c-:sun4c- (tnx MBS) +1.01: sunos-4.1.3_u1-1-:sparc-:sun4-:sun4m-:sun4m- (tnx RSK) +1.01: sunos-4.1.3_u1-10-:sparc-:sun4-:sun4m-:sun4m- (tnx aoki=???) +1.00: sunos-4.1.3_u1-4-:unknown-:sun4-:sun4m-:sun4m- (tnx J2B) +1.01: sunos-4.1.3_u1-6-:sparc-:sun4-:sun4m-:sun4m- (tnx RD) +1.01: sunos-4.1.4-1-:unknown-:sun4-:sun4m-:sun4m- (tnx M3S) +1.01: sunos-4.1.4-2-:sparc-:sun4-:sun4m-:sun4m- +1.01: sunos-5.3-generic-:sparc-:sun4-:sun4m-:sun4m- (tnx JDJ) +1.01: sunos-5.4-generic-:sparc-:sun4-:sun4m-:sun4m- (tnx jimo=???) +0.96: sunos-5.4-generic_101945-10-:sparc-:sun4-:sun4m-:sun4m- (tnx W2K) +1.00: sunos-5.4-generic_101945-34-:sparc-:sun4-:sun4m-:sun4m- (tnx ACB) +0.96: sunos-5.4-generic_101946-35-:i386-:i86pc-:i86pc-:i86pc- (tnx CK) +1.01: sunos-5.5-generic-:i386-:i86pc-:i86pc-:i86pc- (tnx seong=???) +1.01: sunos-5.5-generic-:sparc-:sun4-:sun4c-:sun4c- (tnx SPM) +1.01: sunos-5.5-generic-:sparc-:sun4-:sun4m-:sun4m- (tnx RDM) +1.01: sunos-5.5-generic-:sparc-:sun4-:sun4u-:sun4u- (tnx YC) +1.01: sunos-5.5-generic_103093-02-:sparc-:sun4-:sun4m-:sun4m- (tnx RF) +0.96: sunos-5.5-generic_103093-03-:sparc-:sun4-:sun4m-:sun4m- (tnx RDM) +1.01: sunos-5.5-generic_103093-06-:sparc-:sun4-:sun4m-:sun4m- (tnx ERH) +1.01: sunos-5.5-generic_103093-10-:sparc-:sun4-:sun4d-:sun4d- (tnx KT) +1.01: sunos-5.5-generic_103094-05-:i386-:i86pc-:i86pc-:i86pc- (tnx M2G) +1.01: sunos-5.5.1-generic-:i386-:i86pc-:i86pc-:i86pc- (tnx cro=???) +1.01: sunos-5.5.1-generic-:sparc-:sun4-:sun4c-:sun4c- (tnx CG) +1.01: sunos-5.5.1-generic-:sparc-:sun4-:sun4m-:sun4m- (tnx MBS) +1.01: sunos-5.5.1-generic-:sparc-:sun4-:sun4u-:sun4u- +0.96: sunos-5.5.1-generic_103640-02-:sparc-:sun4-:sun4m-:sun4m- (tnx SGC) +1.00: sunos-5.5.1-generic_103640-03-:sparc-:sun4-:sun4u-:sun4u- (tnx EG) +1.00: sunos-5.5.1-generic_103640-05-:sparc-:sun4-:sun4m-:sun4m- (tnx L2L) +1.01: sunos-5.5.1-generic_103640-05-:sparc-:sun4-:sun4u-:sun4u- (tnx KY) +1.01: sunos-5.5.1-generic_103640-06-:sparc-:sun4-:sun4u-:sun4u- (tnx RA) +1.01: sunos-5.5.1-generic_103640-08-:sparc-:sun4-:sun4c-:sun4c- (tnx RA) +1.01: sunos-5.5.1-generic_103640-08-:sparc-:sun4-:sun4d-:sun4d- (tnx MS) +1.01: sunos-5.5.1-generic_103640-08-:sparc-:sun4-:sun4m-:sun4m- (tnx S2P) +1.01: sunos-5.5.1-generic_103640-08-:sparc-:sun4-:sun4u-:sun4u- (tnx CM) +1.01: sunos-5.5.1-generic_103640-12-:sparc-:sun4-:sun4m-:sun4m- (tnx IK) +1.01: sunos-5.5.1-generic_103640-18-:sparc-:sun4-:sun4u-:sun4u- (tnx PMH) +1.01: sunos-5.5.1-generic_103641-08-:i386-:i86pc-:i86pc-:i86pc- (tnx TL) +1.01: sunos-5.5.1-generic_103641-12-:i386-:i86pc-:i86pc-:i86pc- (tnx JS) +1.01: sunos-5.5.1-generic_105428-01-:sparc-:sun4-:sun4u-:sun4u- (tnx BCM) +0.96: sunos-5.5.1-generic_patch-:i386-:i86pc-:i86pc-:i86pc- (tnx D2K) +1.01: sunos-5.6-generic-:sparc-:sun4-:sun4c-:sun4c- (tnx DS) +1.01: sunos-5.6-generic-:sparc-:sun4-:sun4m-:sun4m- (tnx BDM) +1.01: sunos-5.6-generic-:sparc-:sun4-:sun4u-:sun4u- (tnx RPS) +1.01: sunos-5.6-generic_105182-01-:i386-:i86pc-:i86pc-:i86pc- (tnx JFK) +1.01: sunos-5.6-generic_105182-04-:i386-:i86pc-:i86pc-:i86pc- (tnx YC) +0.96: ultrix-4.3-1-:pmax-:-:risc-:- (tnx YF) +1.01: ultrix-4.4-0-:-:-:risc-:- (tnx RSK) +1.01: unix_sv-4.2mp-2.1.2-:i386-:-:i386-:- (tnx J2W) + diff --git a/REMOVE.binmail b/REMOVE.binmail new file mode 100644 index 0000000..9532ac9 --- /dev/null +++ b/REMOVE.binmail @@ -0,0 +1,16 @@ +Here's how to remove binmail from your system. Don't do this if you have +configured qmail to use binmail for local delivery. + + +1. Find the binmail binary on your system: /usr/libexec/mail.local if + that exists, otherwise /bin/mail. + +2. Remove permissions from the binmail binary: + # chmod 0 /usr/libexec/mail.local + +3. If the binmail binary was /bin/mail, make sure that ``mail'' still + invokes a usable mailer. Under SVR4 you may want to link mail to + mailx. + +4. Comment out the comsat line in /etc/inetd.conf, and kill -HUP your + inetd. diff --git a/REMOVE.sendmail b/REMOVE.sendmail new file mode 100644 index 0000000..5be6e78 --- /dev/null +++ b/REMOVE.sendmail @@ -0,0 +1,28 @@ +Here's how to remove sendmail from your system. + +1. Find sendmail in your boot scripts. It's usually in either /etc/rc or + /etc/init.d/sendmail. It looks like + sendmail -bd -q15m + -q15m means that it should run the queue every 15 minutes; you may + see a different number. Comment out this line. + +2. Kill the sendmail daemon. You should first kill -STOP the daemon; if + any children are running, you should kill -CONT, wait, kill -STOP + again, and repeat ad nauseam. If there aren't any children, kill + -TERM and then kill -CONT. + +3. Check whether you have any messages in the sendmail queue, + /var/spool/mqueue. If you do, you will have to try flushing them with + sendmail.bak -q. If necessary, wait a while and run sendmail.bak -q + again. Repeat until the queue is empty. This may take several days. + +4. Remove the setuid bit on the sendmail binary, to prevent local users + from gaining extra privileges through sendmail's security holes. The + binary may be at several different locations: + # chmod 0 /usr/lib/sendmail + # chmod 0 /usr/sbin/sendmail + # chmod 0 /usr/lib/sendmail.mx + +5. Move the sendmail binary out of the way: + # mv /usr/lib/sendmail /usr/lib/sendmail.bak + # mv /usr/sbin/sendmail /usr/sbin/sendmail.bak diff --git a/SECURITY b/SECURITY new file mode 100644 index 0000000..098f124 --- /dev/null +++ b/SECURITY @@ -0,0 +1,131 @@ +Background: Every few months CERT announces Yet Another Security Hole In +Sendmail---something that lets local or even remote users take complete +control of the machine. I'm sure there are many more holes waiting to be +discovered; sendmail's design means that any minor bug in 46000 lines of +code is a major security risk. Other popular mailers, such as Smail, and +even mailing-list managers, such as Majordomo, seem nearly as bad. + +Note added in 1998: I wrote the above paragraph in December 1995, when +the latest version of sendmail was 8.6.12 (with 41000 lines of code). +Fourteen security holes were discovered from sendmail 8.6.12 through +8.8.5. See http://pobox.com/~djb/docs/maildisasters/sendmail.html. + +I started working on qmail because I was sick of this cycle of doom. +Here are some of the things I did to make sure that qmail will never let +an intruder into your machine. + + +1. Programs and files are not addresses. Don't treat them as addresses. + +sendmail treats programs and files as addresses. Obviously random people +can't be allowed to execute arbitrary programs or write to arbitrary +files, so sendmail goes through horrendous contortions trying to keep +track of whether a local user was ``responsible'' for an address. This +has proven to be an unmitigated disaster. + +In qmail, programs and files are not addresses. The local delivery +agent, qmail-local, can run programs or write to files as directed by +~user/.qmail, but it's always running as that user. (The notion of +``user'' is configurable, but root is never a user. To prevent silly +mistakes, qmail-local makes sure that neither ~user nor ~user/.qmail is +group-writable or world-writable.) + +Security impact: .qmail, like .cshrc and .exrc and various other files, +means that anyone who can write arbitrary files as a user can execute +arbitrary programs as that user. That's it. + + +2. Do as little as possible in setuid programs. + +A setuid program must operate in a very dangerous environment: a user is +under complete control of its fds, args, environ, cwd, tty, rlimits, +timers, signals, and more. Even worse, the list of controlled items +varies from one vendor's UNIX to the next, so it is very difficult to +write portable code that cleans up everything. + +Of the twenty most recent sendmail security holes, eleven worked only +because the entire sendmail system is setuid. + +Only one qmail program is setuid: qmail-queue. Its only purpose is to +add a new mail message to the outgoing queue. + + +3. Do as little as possible as root. + +The entire sendmail system runs as root, so there's no way that its +mistakes can be caught by the operating system's built-in protections. +In contrast, only two qmail programs, qmail-start and qmail-lspawn, +run as root. + + +4. Move separate functions into mutually untrusting programs. + +Five of the qmail programs---qmail-smtpd, qmail-send, qmail-rspawn, +qmail-remote, and tcp-env---are not security-critical. Even if all of +these programs are completely compromised, so that an intruder has +control over the qmaild, qmails, and qmailr accounts and the mail queue, +he still can't take over your system. None of the other programs trust +the results from these five. + +In fact, these programs don't even trust each other. They are in three +groups: tcp-env and qmail-smtpd, which run as qmaild; qmail-rspawn and +qmail-remote, which run as qmailr; and qmail-send, the queue manager, +which runs as qmails. Each group is immune from attacks by the others. + +(From root's point of view, as long as root doesn't send any mail, only +qmail-start and qmail-lspawn are security-critical. They don't write any +files or start any other programs as root.) + + +5. Don't parse. + +I have discovered that there are two types of command interfaces in the +world of computing: good interfaces and user interfaces. + +The essence of user interfaces is _parsing_---converting an unstructured +sequence of commands, in a format usually determined more by psychology +than by solid engineering, into structured data. + +When another programmer wants to talk to a user interface, he has to +_quote_: convert his structured data into an unstructured sequence of +commands that the parser will, he hopes, convert back into the original +structured data. + +This situation is a recipe for disaster. The parser often has bugs: it +fails to handle some inputs according to the documented interface. The +quoter often has bugs: it produces outputs that do not have the right +meaning. Only on rare joyous occasions does it happen that the parser +and the quoter both misinterpret the interface in the same way. + +When the original data is controlled by a malicious user, many of these +bugs translate into security holes. Some examples: the Linux login +-froot security hole; the classic find | xargs rm security hole; the +Majordomo injection security hole. Even a simple parser like getopt is +complicated enough for people to screw up the quoting. + +In qmail, all the internal file structures are incredibly simple: text0 +lines beginning with single-character commands. (text0 format means that +lines are separated by a 0 byte instead of line feed.) The program-level +interfaces don't take options. + +All the complexity of parsing RFC 822 address lists and rewriting +headers is in the qmail-inject program, which runs without privileges +and is essentially part of the UA. + + +6. Keep it simple, stupid. + +See BLURB for some of the reasons that qmail is so much smaller than +sendmail. There's nothing inherently complicated about writing a mailer. +(Except RFC 822 support; but that's only in qmail-inject.) Security +holes can't show up in features that don't exist. + + +7. Write bug-free code. + +I've mostly given up on the standard C library. Many of its facilities, +particularly stdio, seem designed to encourage bugs. A big chunk of +qmail is stolen from a basic C library that I've been developing for +several years for a variety of applications. The stralloc concept and +getln() make it very easy to avoid buffer overruns, memory leaks, and +artificial line length limits. diff --git a/SENDMAIL b/SENDMAIL new file mode 100644 index 0000000..9280c24 --- /dev/null +++ b/SENDMAIL @@ -0,0 +1,76 @@ +This document explains what you, as a user, will notice when the system +switches from sendmail to qmail. + +This is a global document, part of the qmail package, not reflecting the +decisions made by your system administrator. For details on + + * which local delivery agent qmail is configured to use, + * whether qmail is configured to use dot-forward, + * whether ezmlm is installed, + * whether fastforward is installed, and + * all other local configuration features, + +see your local sendmail-qmail upgrade announcement (which your system +administrator may have placed into /var/qmail/doc/ANNOUNCE). + + +--- Mailbox location + +If your system administrator has configured qmail to use binmail for +local deliveries, your mailbox will be in /var/spool/mail/you, just as +it was under sendmail. + +If your system administrator has configured qmail to use qmail-local for +local deliveries, your mailbox will be moved to ~you/Mailbox. There is a +symbolic link from /var/spool/mail/you to ~you/Mailbox, so your mail +reader will find the mailbox at its new location. + + +--- Loop control + +qmail-local automatically adds a Delivered-To field at the top of every +delivered message. It uses Delivered-To to prevent mail forwarding +loops, including cross-host mailing-list loops. + + +--- Outgoing messages + +qmail lets you use environment variables to control the appearance of +your outgoing mail, supplementing the features offered by your MUA. For +example, qmail-inject will set up Mail-Followup-To for you automatically +if you tell it which mailing lists you are subscribed to. See +qmail-inject(8) for a complete list of features. + +If you're at (say) sun.ee.movie.edu, qmail lets you type joe@mac for +joe@mac.ee.movie.edu, and joe@mac+ for joe@mac.movie.edu without the ee. +sendmail has a different interpretation of hostnames without dots. + + +--- Forwarding and mailing lists + +qmail gives you the power to set up your own mailing lists without +pestering your system administrator. + +Under qmail, you are in charge of all addresses of the form +you-anything. The delivery of you-anything is controlled by +~you/.qmail-anything, a file in your home directory. + +For example, if you want to set up a bug-of-the-month-club mailing list, +you can put a list of addresses into ~you/.qmail-botmc. Any mail to +you-botmc will be forwarded to all of those addresses. Mail directly to +you is controlled by ~you/.qmail. You can even set up a catch-all, +~you/.qmail-default, to handle unknown you- addresses. + +See dot-qmail(5) for the complete story. Beware that the syntax of +.qmail is different from the syntax of sendmail's .forward file. + +If your system administrator has configured qmail to use the dot-forward +compatibility tool, you can put forwarding addresses (and programs) into +.forward the same way you did with sendmail. + +If your system administrator has installed ezmlm, you can use ezmlm-make +to instantly set up a professional-quality mailing list, handling +subscriptions and archives automatically. + +If your system administrator has installed fastforward, you can easily +manage a large database of forwarding addresses. diff --git a/SYSDEPS b/SYSDEPS new file mode 100644 index 0000000..0bb01ec --- /dev/null +++ b/SYSDEPS @@ -0,0 +1,17 @@ +VERSION +systype +hasshsgr.h +hasnpbg1.h +select.h +hasflock.h +hassalen.h +fork.h +hassgact.h +direntry.h +hassgprm.h +haswaitp.h +hasmkffo.h +uint32.h +dns.lib +socket.lib +syslog.lib diff --git a/TARGETS b/TARGETS new file mode 100644 index 0000000..facdad7 --- /dev/null +++ b/TARGETS @@ -0,0 +1,387 @@ +auto-ccld.sh +make-load +find-systype +systype +load +make-compile +compile +fork.h +qmail-local.o +qmail.o +quote.o +now.o +gfrom.o +myctime.o +slurpclose.o +make-makelib +makelib +case_diffb.o +case_diffs.o +case_lowerb.o +case_lowers.o +case_starts.o +case.a +getln.o +getln2.o +getln.a +subgetopt.o +sgetopt.o +getopt.a +sig_alarm.o +hassgprm.h +sig_block.o +hassgact.h +sig_catch.o +sig_pause.o +sig_pipe.o +sig_child.o +sig_hup.o +sig_term.o +sig_bug.o +sig_misc.o +sig.a +open_append.o +open_excl.o +open_read.o +open_trunc.o +open_write.o +open.a +seek_cur.o +seek_end.o +seek_set.o +seek_trunc.o +seek.a +hasflock.h +lock_ex.o +lock_exnb.o +lock_un.o +lock.a +fd_copy.o +fd_move.o +fd.a +haswaitp.h +wait_pid.o +wait_nohang.o +wait.a +env.o +envread.o +env.a +stralloc_eady.o +stralloc_pend.o +stralloc_copy.o +stralloc_opys.o +stralloc_opyb.o +stralloc_cat.o +stralloc_cats.o +stralloc_catb.o +stralloc_arts.o +stralloc.a +alloc.o +alloc_re.o +alloc.a +strerr_sys.o +strerr_die.o +strerr.a +substdio.o +substdi.o +substdo.o +subfderr.o +subfdout.o +subfdouts.o +subfdin.o +subfdins.o +substdio_copy.o +substdio.a +error.o +error_str.o +error_temp.o +error.a +str_len.o +str_diff.o +str_diffn.o +str_cpy.o +str_chr.o +str_rchr.o +str_start.o +byte_chr.o +byte_rchr.o +byte_diff.o +byte_copy.o +byte_cr.o +byte_zero.o +str.a +fmt_str.o +fmt_strn.o +fmt_uint.o +fmt_uint0.o +fmt_ulong.o +scan_ulong.o +scan_8long.o +fs.a +datetime.o +datetime_un.o +datetime.a +auto-str.o +auto-str +auto_qmail.c +auto_qmail.o +auto-int8.o +auto-int8 +auto_patrn.c +auto_patrn.o +socket.lib +qmail-local +uint32.h +qmail-lspawn.o +select.h +chkspawn.o +auto-int.o +auto-int +auto_spawn.c +auto_spawn.o +chkspawn +spawn.o +chkshsgr.o +chkshsgr +hasshsgr.h +prot.o +coe.o +cdb_hash.o +cdb_unpack.o +cdb_seek.o +cdb.a +auto-uid.o +auto-uid +auto-gid.o +auto-gid +auto_uids.c +auto_uids.o +qmail-lspawn +qmail-getpw.o +auto_break.c +auto_break.o +auto_usera.c +auto_usera.o +qmail-getpw +qmail-remote.o +control.o +constmap.o +timeoutread.o +timeoutwrite.o +timeoutconn.o +tcpto.o +dns.o +ip.o +ipalloc.o +hassalen.h +ipme.o +ndelay.o +ndelay_off.o +ndelay.a +dns.lib +qmail-remote +qmail-rspawn.o +tcpto_clean.o +qmail-rspawn +direntry.h +qmail-clean.o +fmtqfn.o +auto_split.c +auto_split.o +qmail-clean +qmail-send.o +qsutil.o +newfield.o +prioq.o +hasmkffo.h +fifo.o +hasnpbg1.h +trigger.o +readsubdir.o +date822fmt.o +qmail-send +qmail-start.o +qmail-start +splogger.o +syslog.lib +splogger +qmail-queue.o +triggerpull.o +qmail-queue +qmail-inject.o +headerbody.o +hfield.o +token822.o +qmail-inject +predate.o +predate +datemail +mailsubj +qmail-upq +qmail-showctl.o +qmail-showctl +qmail-newu.o +cdbmss.o +cdbmake_pack.o +cdbmake_hash.o +cdbmake_add.o +cdbmake.a +qmail-newu +qmail-pw2u.o +qmail-pw2u +qmail-qread.o +qmail-qread +qmail-qstat +qmail-tcpto.o +qmail-tcpto +qmail-tcpok.o +qmail-tcpok +qmail-pop3d.o +commands.o +maildir.o +qmail-pop3d +qmail-popup.o +qmail-popup +qmail-qmqpc.o +qmail-qmqpc +qmail-qmqpd.o +received.o +qmail-qmqpd +qmail-qmtpd.o +rcpthosts.o +qmail-qmtpd +qmail-smtpd.o +qmail-smtpd +sendmail.o +sendmail +tcp-env.o +remoteinfo.o +tcp-env +qmail-newmrh.o +qmail-newmrh +config +config-fast +dnscname.o +dnsdoe.o +dnscname +dnsptr.o +dnsptr +dnsip.o +dnsip +dnsmxip.o +dnsmxip +dnsfq.o +dnsfq +hostname.o +hostname +ipmeprint.o +ipmeprint +qreceipt.o +qreceipt +qsmhook.o +qsmhook +qbiff.o +qbiff +forward.o +forward +preline.o +preline +condredirect.o +condredirect +bouncesaying.o +bouncesaying +except.o +except +maildirmake.o +maildirmake +maildir2mbox.o +maildir2mbox +maildirwatch.o +maildirwatch +qail +elq +pinq +idedit.o +idedit +install-big.o +install.o +install-big +hier.o +install +instcheck.o +instcheck +home +home+df +proc +proc+df +binm1 +binm1+df +binm2 +binm2+df +binm3 +binm3+df +it +qmail-local.0 +qmail-lspawn.0 +qmail-getpw.8 +qmail-getpw.0 +qmail-remote.0 +qmail-rspawn.0 +qmail-clean.0 +qmail-send.8 +qmail-send.0 +qmail-start.8 +qmail-start.0 +splogger.0 +qmail-queue.0 +qmail-inject.0 +mailsubj.0 +qmail-showctl.0 +qmail-newu.8 +qmail-newu.0 +qmail-pw2u.8 +qmail-pw2u.0 +qmail-qread.0 +qmail-qstat.0 +qmail-tcpto.0 +qmail-tcpok.0 +qmail-pop3d.0 +qmail-popup.0 +qmail-qmqpc.0 +qmail-qmqpd.0 +qmail-qmtpd.0 +qmail-smtpd.0 +tcp-env.0 +qmail-newmrh.8 +qmail-newmrh.0 +qreceipt.0 +qbiff.0 +forward.0 +preline.0 +condredirect.0 +bouncesaying.0 +except.0 +maildirmake.0 +maildir2mbox.0 +maildirwatch.0 +qmail.0 +qmail-limits.7 +qmail-limits.0 +qmail-log.0 +qmail-control.5 +qmail-control.0 +qmail-header.0 +qmail-users.5 +qmail-users.0 +dot-qmail.5 +dot-qmail.0 +qmail-command.0 +tcp-environ.0 +maildir.0 +mbox.0 +addresses.0 +envelopes.0 +forgeries.0 +man +setup +check diff --git a/TEST.deliver b/TEST.deliver new file mode 100644 index 0000000..4fc4c32 --- /dev/null +++ b/TEST.deliver @@ -0,0 +1,82 @@ +You can do several tests of qmail delivery without setting up qmail to +accept messages through SMTP or through /usr/lib/sendmail: + +1. After you start qmail, look for a + qmail: status: local 0/10 remote 0/20 + line in syslog. qmail-send always prints either ``cannot start'' or + ``status''. (The big number is a splogger timestamp.) + +2. Do a ps and look for the qmail daemons. There should be four of + them, all idle: qmail-send, running as qmails; qmail-lspawn, running + as root; qmail-rspawn, running as qmailr; and qmail-clean, running + as qmailq. You will also see splogger, running as qmaill. + +3. Local-local test: Send yourself an empty message. (Replace ``me'' + with your username. Make sure to include the ``to:'' colon.) + % echo to: me | /var/qmail/bin/qmail-inject + The message will show up immediately in your mailbox, and syslog + will show something like this: + qmail: new msg 53 + qmail: info msg 53: bytes 246 from qp 20345 uid 666 + qmail: starting delivery 1: msg 53 to local me@domain + qmail: status: local 1/10 remote 0/20 + qmail: delivery 1: success: did_1+0+0/ + qmail: status: local 0/10 remote 0/20 + qmail: end msg 53 + (53 is an inode number; 20345 is a process ID; your numbers will + probably be different.) + +4. Local-error test: Send a message to a nonexistent local address. + % echo to: nonexistent | /var/qmail/bin/qmail-inject + qmail: new msg 53 + qmail: info msg 53: bytes 246 from qp 20351 uid 666 + qmail: starting delivery 2: msg 53 to local nonexistent@domain + qmail: status: local 1/10 remote 0/20 + qmail: delivery 2: failure: No_such_address.__#5.1.1_/ + qmail: status: local 0/10 remote 0/20 + qmail: bounce msg 53 qp 20357 + qmail: end msg 53 + qmail: new msg 54 + qmail: info msg 54: bytes 743 from <> qp 20357 uid 666 + qmail: starting delivery 3: msg 54 to local me@domain + qmail: status: local 1/10 remote 0/20 + qmail: delivery 3: success: did_1+0+0/ + qmail: status: local 0/10 remote 0/20 + qmail: end msg 54 + You will now have a bounce message in your mailbox. + +5. Local-remote test: Send an empty message to your account on another + machine. + % echo to: me@wherever | /var/qmail/bin/qmail-inject + qmail: new msg 53 + qmail: info msg 53: bytes 246 from qp 20372 uid 666 + qmail: starting delivery 4: msg 53 to remote me@wherever + qmail: status: local 0/10 remote 1/20 + qmail: delivery 4: success: 1.2.3.4_accepted_message./... + qmail: status: local 0/10 remote 0/20 + qmail: end msg 53 + There will be a pause between ``starting delivery'' and ``success''; + SMTP is slow. Check that the message is in your mailbox on the other + machine. + +6. Local-postmaster test: Send mail to postmaster, any capitalization. + % echo to: POSTmaster | /var/qmail/bin/qmail-inject + Look for the message in the alias mailbox, normally ~alias/Mailbox. + +7. Double-bounce test: Send a message with a completely bad envelope. + % /var/qmail/bin/qmail-inject -f nonexistent + To: unknownuser + Subject: testing + + This is a test. This is only a test. + % + (Use end-of-file, not dot, to end the message.) Look for the double + bounce in the alias mailbox. + +8. Group membership test: + % cat > ~me/.qmail-groups + |groups >> MYGROUPS; exit 0 + % /var/qmail/bin/qmail-inject me-groups < /dev/null + % cat ~me/MYGROUPS + MYGROUPS will show your normal gid and nothing else. (Under Solaris, + make sure to use /usr/ucb/groups; /usr/bin/groups is broken.) diff --git a/TEST.receive b/TEST.receive new file mode 100644 index 0000000..7644845 --- /dev/null +++ b/TEST.receive @@ -0,0 +1,41 @@ +You can do several tests of messages entering the qmail system: + +1. SMTP server test: Forge some mail locally via SMTP. Replace ``me'' + with your username and ``domain'' with your host's name. + % telnet 127.0.0.1 25 + Trying 127.0.0.1... + Connected to 127.0.0.1. + Escape character is '^]'. + 220 domain ESMTP + helo dude + 250 domain + mail + 250 ok + rcpt + 250 ok + data + 354 go ahead + Subject: testing + + This is a test. + . + 250 ok 812345679 qp 12345 + quit + 221 domain + Connection closed by foreign host. + % + Look for the message in your mailbox. (Note for programmers: Most + SMTP servers need more text after MAIL and RCPT. See RFC 821.) + +2. Remote-local test: Send yourself some mail from another machine. + Look for the message in your mailbox. + +3. Remote-error test: Send some mail from another machine to + nonexistent@domain. Look for a bounce message in the remote mailbox. + +4. UA test: Try sending mail, first to a local account, then to a + remote account, with your normal user agent. + +5. Remote-postmaster test: Send mail from another machine to + PoStMaStEr@domain. Look for the message in the alias mailbox, + normally ~alias/Mailbox. diff --git a/THANKS b/THANKS new file mode 100644 index 0000000..76996f8 --- /dev/null +++ b/THANKS @@ -0,0 +1,338 @@ +Thanks to lots of people for success and failure reports, code, ideas, +and documentation. See CHANGES for details of specific contributions. +Sorry if I left anyone out. + +A2B = Are Bryne +A2L = Ali Lomonaco +A2P = Andrea Paolini +AAF = Adam A. Frey +AB = Alan Briggs +ABC = Alan B. Clegg +AC = Arne Coucheron +ACB = Andy C. Brandt +ADM = Adam D. Morley +AF = Andreas Faerber +AG = Armin Gruner +AGB = Andre Grosse Bley +AH = Amos Hayes +AI = Akihiro Iijima +AJ = Alan Jaffray +AJK = Antti-Juhani Kaijanaho +AKB = Allen K. Briggs +AL = Andreas Lamprecht +ALB = Allan L. Bazinet +ANR = Adriano Nagelschmidt Rodrigues +AP = Andrew Pam +AS = Akos Szalkai +AV = Alex Vostrikov +AWB = Andy W. Barclay +AY = Araki Yasuhiro +B1F = Bo Fussing +B2F = Brad Forschinger +B2H = Buck Huppmann +B2L = Brent Laminack +B2W = Bil Wendling +B3W = Boris Wedl +BB = Bruce Bodger +BC = Bob Collie +BCK = Benjamin C. Kite +BCM = Bill C. Miller +BDB = Boris D. Beletsky +BDM = Byron D. Miller +BEO = Bruce E. O'Neel +BET = Bennett E. Todd +BG = Bert Gijsbers +BH = Brad Howes +BJ = Brian Jackson +BJM = Barry J. Miller +BL = Brian Litzinger +BMF = Brian M. Fisk +BN = Bill Nugent +BP = Bruce Perens +BR = Brian J. Reichert +BS = Bjoern Stabell +BT = Brad Templeton +BTW = Brian T. Wightman +BW = Bill Weinman +BZ = Blaz Zupan +C2F = Chuck Foster +C2H = Christoph Heidermanns +C2S = Craig Shrimpton +CEJ = Colin Eric Johnson +CF = C. Ferree +CG = Chris Garrigues +CH = Chael Hall +CHR = Craig H. Rowland +CK = Christoph Kaesling +CL = Carsten Leonhardt +CLS = Christopher L. Seawood +CM = Charles Mattair +CMP = Chase M. Phillips +CR = Christian Riede +CS = Cloyce Spradling +CSH = Clayton S. Haapala +D1H = Dieter Heidner +D2H = Dan Hollis +D2K = Dax Kelson +D2S = Dan Senie +D3S = Don Samek +DA = Dave Arcuri +DAR = Daniel A. Reish +DB = David Buscher +DBK = Douglas B. Kerry +DC = Dan Cross +DCC = Daniel C. Cotey +DE = Daniel Egnor +DEH = Daniel E. Harris +DF = Dale Farnsworth +DG = David Guntner +DK = Dave Kopper +DL = Daniel Lawrence +DM = David Mazieres +DML = David M. Lew +DP = Dave Platt +DS = Dave Sill +DST = Daniel S. Thibadeau +DWS = David Wayne Summers +EC = Evan Champion +ECG = Eric C. Garrison +EG = Eivind Gjelseth +EK = Eric Krohn +EP = Emanuele Pucciarelli +ERH = Eric R. Hankins +ES = Eric Smith +ESM = Edward S. Marshall +ET = Eivind Tagseth +ETT = Emmanuel T. Tardieu +F2T = Frank Thieme +FE = Frank Ederveen +FN = Faried Nawaz +FPL = Frederik P. Lindberg +FT = Frank Tegtmeyer +FW = Frank Wagner +G1A = Graham Adams +G2A = Greg Andrews +GAW = Greg A. Woods +GB = Glenn Barry +GH = Gene Hightower +GL = Giles Lean +GLM = Grant L. Miller +H2S = Harley Silver +HCJ = Helio Coelho Jr. +HDG = Hans de Graaff +HG = Howard Goldstein +HHO = Harald Hanche-Olsen +HJB = Herbert J. Bernstein +HM = Hirokazu Morikawa +HS = Harlan Stenn +HT = Henry Timmerman +HW = Hal Wine +HWM = Henry W. Miller +IH = Ingmar Hupp +IK = Ivan Kohler +IKW = Ian Keith Wynne +IS = Icarus Sparry +IW = Ian Westcott +J1B = John Banghart +J1K = Jost Krieger +J2B = Jos Backus +J2K = Johannes Kroeger +J2M = Joel Maslak +J2P = John Parker +J2W = Jim Whitby +JAB = Jeremy A. Bussard +JAK = Johan A. Kullstam +JB = Joshua Buysse +JBB = Jason B. Brown +JBF = John B. Fleming +JC = Jim Clausing +JCD = Jeffrey C. Dege +JD = Joe Doupnik +JDHB = Johannes D. H. Beekhuizen +JDJ = Joshua D. Juran +JF = Janos Farkas +JFK = James F. Kane III +JGM = John G. Myers +JJB = J. J. Bailey +JJMK = Jonathan J. M. Katz +JJR = Jaron J. Rubenstein +JK = Jari Kirma +JL = Jim Littlefield +JLB = Julie L. Baumler +JLH = Jason L. Haar +JLW = Jason L. Wright +JM = Jim Meehan +JMS = Jason M. Stokes +JMT = John M. Twilley +JP = John Palkovic +JPB = Joe Block +JPH = Justin P. Hannah +JPR = Jean-Pierre Radley +JRL = John R. Levine +JRM = Jason R. Mastaler +JRY = Jamie R. Yukes +JS = Jesper Skriver +JTB = Jonathan T. Bowie +JW = John Whittaker +JWB = James W. Birdsall +K1J = Kyle Jones +K2J = Kevin Johnson +KA = Klaus Aigte +KB = Keith Burdis +KE = Kenny Elliott +KJJ = Kevin J. Johnson +KJS = Kevin J. Sawyer +KMD = Kevin M. Dulzo +KO = Keith Owens +KR = Kenji Rikitake +KT = Karsten Thygesen +KUT = Kai Uwe Tempel +KY = Kentaro Yoshitomi +L2L = Louis Larry +L3L = Luis Lopes +LB = Laurentiu Badea +LL = lilo +LW = Lionel Widdifield +M2C = Mark Crimmins +M2G = Michael R. Gile +M2H = Martin Hager +M2L = M. Lyons +M2R = Mark Riekenberg +M2S = Mikael Suokas +M3H = Michael Holzt +M3L = Michael Lazarou +M3S = Morten Skjelland +M4S = Michael Shields +MB = Martin Budsj? +MBS = Michael B. Scher +MC = Michael Cooley +MD = Mark Delany +MDI = Miguel de Icaza +ME = Marc Ewing +MEE = Mads E. Eilertsen +MF = Massimo Fusaro +MG = Michael Graff +MGM = Mitchell G. Morris +MH = Markus Hofmann +MJD = Mark-Jason Dominus +MJG = Manuel J. Galan +ML = Martin Lucina +MLH = May Liss Haarstad +MM = Martin Mersberger +MMM = Momchil M. Momchev +MMM2 = Marc M. Martinez +MP = Matt Paduano +MR = Mosfeq Rashid +MRG = Matthew R. Green +MS = Mark Spears +MSD = Mandell S. Degerness +MSS = Matthew S. Soffen +MT = Mark Thompson +MW = Mate Wierdl +MWE = Mark W. Eichin +NA = Norm Aleks +NAA = Nicholas A. Amato +NH = Nick Holloway +NND = N. Dudorov +NR = Norbert Roeding +NW = Nicholas Waples +OK = Oezguer Kesim +OR = Ollivier Robert +OS = Oliver Seiler +PB = Peter Bowyer +PCO = Peter C. Olsen +PGF = Paul Fox +PGR = Phil G. Rorex +PH = Paul Harrington +PJG = Paul Graham +PJH = Peter J. Hunter +PK = Petri Kaukasoina +PMH = Peter M. Haworth +PO = Paul Overell +PS = Paul Svensson +PT = Paul Taylor +PTW = P. T. Withington +PW = Peter Wilkinson +R2N = Rivo Nurges +RA = Russ Allbery +RAB = Randolph Allen Bentson +RAM = Robin A. McCollum +RB = Robert Bridgham +RC = Ryan Crum +RD = Rahul Dhesi +RDM = Raul D. Miller +REB = Ronald E. Bickers +RF = Rainer Fraedrich +RFH = Robert F. Harrison +RGS = Richard G. Sharman +RJC = Robert J. Carter +RJH = Randy Harmon +RJO = Richard J. Ohnemus +RK = Riho Kurg +RL = Robert Luce +RM = Rich McClellan +RN = Russell Nelson +RO = Roberto Oppedisano +RPS = Russell P. Sutherland +RS = Robert Sanders +RSK = Robert S. Krzaczek +S1R = Satish Ramachandran +S2P = Stefan Puscasu +S2R = Sean Reifschneider +S2S = Scott Schwartz +S2T = Steve Taylor +S3T = Steffen Thorsen +SA = Satoshi Adachi +SAE = Stefaan A. Eeckels +SAS = Steven A. Schrader +SB = Stephane Bortzmeyer +SC = Stefan Cars +SCW = Steven C. Work +SG = Steven Grimm +SGC = Stephen G. Comings +SJ = Sudish Joseph +SJB = SJ Burns +SJW = Stephen J. White +SLB = Steven L. Baur +SM = Shawn McHorse +SP = Stephen Parker +SPM = Salvatore P. Miccicke +SS = Simon Shapiro +SSB = Stik Bakken +ST = Steve Tylock +SV = Sven Velt +SVD = Stef Van Dessel +T2K = Tomoya Konishi +T2M = Toni Mueller +T2U = Todd Underwood +TA = Tetsuo Aoki +TB = Tobias Brox +TD = Tom Demmer +TEE = Thomas E. Erskine +TG = Tim Goodwin +TH = Ton Hospel +TJH = Timothy J. Hunt +TK = Terry Kennedy +TL = Timothy Lorenc +TLF = Timo L. Felbinger +TLM = Timothy L. Mayo +TM = Toshinori Maeno +TN = Thomas Neumann +TRR = Tracy R. Reed +TT = Takaki Taniguchi +TU = Tetsu Ushijima +TV = Tommi Virtanen +TVP = Tom van Peer +UO = Uwe Ohse +VBM = Vladimir B. Machulsky +VR = Vincenzo Romano +VU = Viriya Upatising +VV = Vince Vielhaber +W2K = Wolfram Kahl +WEB = William E. Baxter +WK = Werner Koch +WS = Wilbur Sims +WW = Wei Wu +YC = Yuji Chikahiro +YF = Yaroslav Faybishenko +ZU = Zin Uda diff --git a/THOUGHTS b/THOUGHTS new file mode 100644 index 0000000..d6910da --- /dev/null +++ b/THOUGHTS @@ -0,0 +1,418 @@ +Please note that this file is not called ``Internet Mail For Dummies.'' +It _records_ my thoughts on various issues. It does not _explain_ them. +Paragraphs are not organized except by section. The required background +varies wildly from one paragraph to the next. + +In this file, ``sendmail'' means Allman's creation; ``sendmail-clone'' +means the program in this package. + + +1. Security + +There are lots of interesting remote denial-of-service attacks on any +mail system. A long-term solution is to insist on prepayment for +unauthorized resource use. The tricky technical problem is to make the +prepayment enforcement mechanism cheaper than the expected cost of the +attacks. (For local denial-of-service attacks it's enough to be able to +figure out which user is responsible.) + +qmail-send's log was originally designed for profiling. It subsequently +sprouted some tracing features. However, there's no way to verify +securely that a particular message came from a particular local user; +how do you know the recipient is telling you the truth about the +contents of the message? With QUEUE_EXTRA it'd be possible to record a +one-way hash of each outgoing message, but a user who wants to send +``bad'' mail can avoid qmail entirely. + +I originally decided on security grounds not to put qmail advertisements +into SMTP responses: advertisements often act as version identifiers. +But this problem went away when I found a stable qmail URL. + +As qmail grows in popularity, the mere knowledge that rcpthosts is so +easily available will deter people from setting up unauthorized MXs. +(I've never seen an unauthorized MX, but I can imagine that it would be +rather annoying.) Note that, unlike the bat book checkcompat() kludge, +rcpthosts doesn't interfere with mailing lists. + +qmail-start doesn't bother with tty dissociation. On some old machines +this means that random people can send tty signals to the qmail daemons. +That's a security flaw in the job control subsystem, not in qmail. + +The resolver library isn't too bloated (before 4.9.4, at least), but it +uses stdio, which _is_ bloated. Reading /etc/resolv.conf costs lots of +memory in each qmail-remote process. So it's tempting to incorporate a +smaller resolver library into qmail. (Bonus: I'd avoid system-specific +problems with old resolvers.) The problem is that I'd then be writing a +fundamentally insecure library. I'd no longer be able to blame the BIND +authors and vendors for the fact that attackers can easily use DNS to +steal mail. Solution: insist that the resolver run on the same host; the +kernel can guarantee the security of low-numbered 127.0.0.1 UDP ports. + +NFS is the primary enemy of security partitioning under UNIX. Here's the +story. Sun knew from the start that NFS was completely insecure. It +tried to hide that fact by disallowing root access over NFS. Intruders +nevertheless broke into system after system, first obtaining bin access +and then obtaining root access. Various people thus decided to compound +Sun's error and build a wall between root and all other users: if all +system files are owned by root, and if there are no security holes other +than NFS, someone who breaks in via NFS won't be able to wipe out the +operating system---he'll merely be able to wipe out all user files. This +clueless policy means that, for example, all the qmail users have to be +replaced by root. See what I mean by ``enemy''? ... Basic NFS comments: +Aside from the cryptographic problem of having hosts communicate +securely, it's obvious that there's an administrative problem of mapping +client uids to server uids. If a host is secure and under your control, +you shouldn't have to map anything. If a host is under someone else's +control, you'll want to map his uids to one local account; it's his +client's job to decide which of his users get to talk NFS in the first +place. Sun's original map---root to nobody, everyone else left alone--- +is, as far as I can tell, always wrong. + + +2. Injecting mail locally (qmail-inject, sendmail-clone) + +RFC 822 section 3.4.9 prohibits certain visual effects in headers, and +the 822bis draft prohibits even more. qmail-inject could enforce these +absurd restrictions, but why waste the time? If you will suffer from +someone sending you ``flash mail,'' go find a better mail reader. + +qmail-inject's ``Cc: recipient list not shown: ;'' successfully stops +sendmail from adding Apparently-To. Unfortunately, old versions of +sendmail will append a host name. This wasn't fixed until sendmail 8.7. +How many years has it been since RFC 822 came out? + +sendmail discards duplicate addresses. This has probably resulted in +more lost and stolen mail over the years than the entire Chicago branch +of the United States Postal Service. The qmail system delivers messages +exactly as it's told to do. Along the same lines: qmail-inject is both +unable and unwilling to support anything like sendmail's (default) +nometoo option. Of course, a list manager could support nometoo. + +There should be a mechanism in qmail-inject that does for envelope +recipients what Return-Path does for the envelope sender. Then +qmail-inject -n could print the recipients. + +Should qmail-inject bounce messages with no recipients? Should there be +an option for this? If it stays as is (accept the message), qmail-inject +could at least avoid invoking qmail-queue. + +It is possible to extract non-unique Message-IDs out of qmail-inject. +Here's how: stop qmail-inject before it gets to the third line of +main(), then wait until the pids wrap around, then restart qmail-inject +and blast the message through, then start another qmail-inject with the +same pid in the same second. I'm not sure how to fix this without +system-supplied sequence numbers. (Of course, the user could just type +in his own non-unique Message-IDs.) + +The bat book says: ``Rules that hide hosts in a domain should be applied +only to sender addresses.'' Recipient masquerading works fine with +qmail. None of sendmail's pitfalls apply, basically because qmail has a +straight paper path. + +I predicted that I would receive some pressure to make up for the +failings of MUA writers who don't understand the concept of reliability. +(``Like, duh, you mean I'm supposed to check the sendmail exit code?'') +I was right. + + +3. Receiving mail from the network (tcp-env, qmail-smtpd) + +qmail-smtpd doesn't allow privacy-invading commands like VRFY and EXPN. +If you really want to publish such information, use a mechanism that +legitimate users actually know about, such as fingerd or httpd. + +RFC 1123 says that VRFY and EXPN are important to track down cross-host +mailing list loops. With Delivered-To, mailing list loops do no damage, +_and_ one of the list administrators gets a bounce message that shows +exactly how the loop occurred. Solve the problem, not the symptom. + +Should dns.c make special allowances for 127.0.0.1/localhost? + +badmailfrom (like 8BITMIME) is a waste of code space. + +In theory a MAIL or RCPT argument can contain unquoted LFs. In practice +there are a huge number of clients that terminate commands with just LF, +even if they use CR properly inside DATA. + + +4. Adding messages to the queue (qmail-queue) + +Should qmail-queue try to make sure enough disk space is free in +advance? When qmail-queue is invoked by qmail-local or (with ESMTP) +qmail-smtpd or qmail-qmtpd or qmail-qmqpd, it could be told a size in +advance. I wish UNIX had an atomic allocate-disk-space routine... + +The qmail.h interface (reflecting the qmail-queue interface, which in +turn reflects the current queue file structure) is constitutionally +incapable of handling an address that contains a 0 byte. I can't imagine +that this will be a problem. + +Should qmail-queue not bother queueing a message with no recipients? + + +5. Handling queued mail (qmail-send, qmail-clean) + +The queue directory must be local. Mounting it over NFS is extremely +dangerous---not that this stops people from running sendmail that way! +Diskless hosts should use mini-qmail instead. + +Queue reliability demands that single-byte writes be atomic. This is +true for a fixed-block filesystem such as UFS, and for a logging +filesystem such as LFS. + +qmail-send uses 8 bytes of memory per queued message. Double that for +reallocation. (Fix: use a small forest of heaps; i.e., keep several +prioqs.) Double again for buddy malloc()s. (Fix: be clever about the +heap sizes.) 32 bytes is worrisome, but not devastating. Even on my +disk-heavy memory-light machine, I'd run out of inodes long before +running out of memory. + +Some mail systems organize the queue by host. This is pointless as a +means of splitting up the queue directory. The real issue is what to do +when you suddenly find out that a host is up. For local SLIP/PPP links +you know in advance which hosts need this treatment, so you can handle +them with virtualdomains and serialmail. + +For the old queue structure I implemented recipient list compression: +if mail goes out to a giant mailing list, and most of the recipients are +delivered, make a new, compressed, todo list. But this really isn't +worth the effort: it saves only a tiny bit of CPU time. + +qmail-send doesn't have any notions of precedence, priority, fairness, +importance, etc. It handles the queue in first-seen-first-served order. +One could put a lot of work into doing something different, but that +work would be a waste: given the triggering mechanism and qmail's +deferral strategy, it is exceedingly rare for the queue to contain more +than one deliverable message at any given moment. + +Exception: Even with all the concurrency tricks, qmail-send can end up +spending a few minutes on a mailing list with thousands of remote +entries. A user might send a new message to a remote address in the +meantime. The simplest way to handle this would be to put big messages +on a separate channel. + +qmail-send will never start a pass for a job that it already has. This +means that, if one delivery takes longer than the retry interval, the +next pass will be delayed. I implemented the opposite strategy for the +old queue structure. Some hassles: mark() had to understand how job +input was buffered; every new delivery had to check whether the same +mpos in the same message was already being done. + +Some things that qmail-send does synchronously: queueing a bounce +message; doing a cleanup via qmail-clean; classifying and rewriting all +the addresses in a new message. As usual, making these asynchronous +would require some housekeeping, but could speed things up a bit. +(I'm willing to assume POSIX waitpid() for asynchronous bounces; putting +an unbounded buffer into wait_pid() for the sake of NeXTSTEP 3 is not +worthwhile.) + +Disk I/O is a bottleneck; UFS is reliable but it isn't fast. A good +logging filesystem offers much better performance, but logging +filesystems aren't widely available. Solution: Keep a journal, separate +from the queue, adequate to rebuild the queue (with at worst some +duplicate deliveries). Compress the journal. This would dramatically +reduce total disk I/O. + +Bounce aggregation is a dubious feature. Bounce records aren't +crashproof; there can be a huge delay between a failure and a bounce; +the resulting bounce format is unnecessarily complicated. I'm tempted to +scrap the bounce directory and send one bounce for each failing +recipient, with appropriate modifications in the accompanying text. + +qmail-stop implementation: setuid to UID_SEND; kill -TERM -1. Or run +qmail-start under an external service controller, such as supervise; +that's why it runs in the foreground. + +The readdir() interface hides I/O errors. Lower-level interfaces would +lead me into a thicket of portability problems. I'm really not sure what +to do about this. Of course, a hard I/O error means that mail is toast, +but a soft I/O error shouldn't cause any trouble. + +job_open() or pass_dochan() could be paranoid about the same id,channel +already being open; but, since messdone() is so paranoid, the worst +possible effect of a bug along these lines would be double delivery. + +Mathematical amusement: The optimal retry schedule is essentially, +though not exactly, independent of the actual distribution of message +delay times. What really matters is how much cost you assign to retries +and to particular increases in latency. qmail's current quadratic retry +schedule says that an hour-long delay in a day-old message is worth the +same as a ten-minute delay in an hour-old message; this doesn't seem so +unreasonable. + +Insider information: AOL retries their messages every five minutes for +three days straight. Hmmm. + + +6. Sending mail through the network (qmail-rspawn, qmail-remote) + +Are there any hosts, anywhere, whose mailers are bogged down by huge +messages to multiple recipients at a single host? For typical hosts, +multiple RCPTs per SMTP aren't an ``efficiency feature''; they're a +_slowness_ feature. Separate SMTP transactions have much lower latency. + +I've heard three complaints about bandwidth use from masochists sending +messages through a modem through a smarthost to thousands of users--- +without sublists! They can get much better performance with QMQP. + +In the opposite direction: It's tempting to remove the @host part of the +qmail-remote recip argument. Or at least avoid double-dns_cname. + +There are lots of reasons that qmail-rspawn should take a more active +role in qmail-remote's activities. It should call separate programs to +do (1) MX lookups, (2) SMTP connections, (3) QMTP connections. (But this +wouldn't be so important if the DNS library didn't burn so much memory.) + +I bounce ambiguous MXs. (An ``ambiguous MX'' is a best-preference MX +record sending me mail for a host that I don't recognize as local.) +Automatically treating ambiguous MXs as local is incompatible with my +design decision to keep local delivery working when the network goes +down. It puts more faith in DNS than DNS deserves. Much better: Have +your MX records generated automatically from control/locals. + +If I successfully connect to an MX host but it temporarily refuses to +accept the message, I give up and put the message back into the queue. +But several documents seem to suggest that I should try further MX +records. What are they thinking? My approach deals properly with downed +hosts, hosts that are unreachable through a firewall, and load +balancing; what else do people use multiple MX records for? + +Currently qmail-remote sends data in 1024-byte buffers. Perhaps it +should try to take account of the MTU. + +Perhaps qmail-remote should allocate a fixed amount of DNS/connect() +time across any number of MXs; this idea is due to Mark Delany. + +RFC 821 doesn't say what it means by ``text.'' qmail-remote assumes that +the server's reply text doesn't contain bare LFs. + +RFC 821 and RFC 1123 prohibit host names in MAIL FROM and RCPT TO from +being aliases. qmail-remote, like sendmail, rewrites aliases in RCPT; +people who don't list aliases in control/locals or sendmail's Cw are +implicitly relying on this conversion. It is course quite silly for an +internal DNS detail to have such an effect on mail delivery, but that's +how the Internet works. On the other hand, the compatibility arguments +do not apply to MAIL FROM. qmail-remote no longer bothers with CNAME +lookups for the envelope sender host. + + +7. Delivering mail locally (qmail-lspawn, qmail-local) + +qmail-local doesn't support comsat. comsat is a pointless abomination. +Use qbiff if you want that kind of notification. + +The getpwnam() interface hides I/O errors. Solution: qmail-pw2u. + + +8. sendmail V8's new features + +sendmail-8.8.0/doc/op/op.me includes a list of big improvements of +sendmail 8.8.0 over sendmail 5.67. Here's how qmail stacks up against +each of those improvements. (Of course, qmail has its own improvements, +but that's not the point of this list.) + +Connection caching, MX piggybacking: Nope. (Profile. Don't speculate.) + +Response to RCPT command is fast: Yup. + +IP addresses show up in Received lines: Yup. + +Self domain literal is properly handled: Yup. + +Different timeouts for QUIT, RCPT, etc.: No, just a single timeout. + +Proper <> handling, route-address pruning: Yes, but not configurable. + +ESMTP support: Yup. (Server-side, including PIPELINING.) + +8-bit clean: Yup. (Including server-side 8BITMIME support; same as +sendmail with the 8 option.) + +Configurable user database: Yup. + +BIND support: Yup. + +Keyed files: Yes, in fastforward. + +931/1413/Ident/TAP: Yup. + +Correct 822 address list parsing: Yup. (Note that sendmail still has +some major problems with quoting.) + +List-owner handling: Yup. + +Dynamic header allocation: Yup. + +Minimum number of disk blocks: Yes, via tunefs -m. (Or quotas; the right +setup has qmailq with a small quota, qmails with a larger quota, so that +qmail-send always has room to work.) + +Checkpointing: Yes, but not configurable---qmail always checkpoints. + +Error message configuration: Nope. + +GECOS matching: Not directly, but easy to hook in. + +Hop limit configuration: No. (qmail's limit is 100 hops. qmail offers +automatic loop protection much more advanced than hop counting.) + +MIME error messages: No. (qmail uses QSBMF error messages, which are +much easier to parse.) + +Forward file path: Yes, via /etc/passwd. + +Incoming SMTP configuration: Yes, via inetd or tcpserver. + +Privacy options: Yes, but they're not options. + +Best-MX mangling: Nope. See section 6 for further discussion. + +7-bit mangling: Nope. qmail always uses 8 bits. + +Support for up to 20 MX records: Yes, and more. qmail has no limits +other than memory. + +Correct quoting of name-and-address headers: Yup. + +VRFY and EXPN now different: Nope. qmail always hides this information. + +Multi-word classes, deferred macro expansion, separate envelope/header +$g processing, separate per-mailer envelope and header processing, new +command line flags, new configuration lines, new mailer flags, new +macros: These are sendmail-specific; they wouldn't even make sense for +qmail. For example, _of course_ qmail handles envelopes and headers +separately; they're almost entirely different objects! + + +9. Miscellany + +sendmail-clone and qsmhook are too bletcherous to be documented. (The +official replacement for qsmhook is preline, together with the +qmail-command environment variables.) + +I've considered making install atomic, but this is very difficult to do +right, and pointless if it isn't done right. + +RN suggests automatically putting together a reasonable set of lines for +/etc/passwd. I perceive this as getting into the adduser business, which +is worrisome: I'll be lynched the first time I screw up somebody's +passwd file. This should be left to OS-specific installation scripts. + +The BSD 4.2 inetd didn't allow a username. I think I can safely forget +about this. (DS notes that the username works under Ultrix even though +it's undocumented.) + +I should clean up the bput/put choices. + +Some of the stralloc_0()s indicate that certain lower-level routines +should grok stralloc. + +qmail assumes that all times are positive; that pid_t, time_t and ino_t +fit into unsigned long; that gid_t fits into int; that the character set +is ASCII; and that all pointers are interchangeable. Do I care? + +The bat book justifies sendmail's insane line-splitting mechanism by +pointing out that it might be useful for ``a 40-character braille +print-driving program.'' C'mon, guys, is that your best excuse? + +qmail's mascot is a dolphin. diff --git a/TODO b/TODO new file mode 100644 index 0000000..5841c0a --- /dev/null +++ b/TODO @@ -0,0 +1,23 @@ +consider stripping vdoms for VERPs; tnx PJH +consider ~ in qmail-local for doing defaultdelivery (not recursively) +consider POP bulletins +turn qmail-upq into a more serious queue-moving utility +consider fast-greeting option in qmail-smtpd +build a returnmail package + +expand strerr coverage +redo control interface +allow concurrency over 255 +allow more channels at compile time +test for linux fifo close bug at compile time + +eliminate qsmhook +finish OTBS conversion +use mess822 in qmail-inject +use mess822 in qreceipt +use mess822 in qbiff +use mess822 in maildirwatch +eliminate token822, headerbody, hfield +replace INTERNALS and THOUGHTS with a real paper describing qmail +handle IPv6 +rewrite everything from scratch diff --git a/UPGRADE b/UPGRADE new file mode 100644 index 0000000..959120d --- /dev/null +++ b/UPGRADE @@ -0,0 +1,66 @@ +SAVE COPIES OF YOUR OUTGOING MAIL! Like any other piece of software (and +information generally), the qmail system comes with NO WARRANTY. It's +much more secure and reliable than sendmail, but that's not saying much. + + +Here's how to upgrade to netqmail 1.05. This procedure will overwrite the +old qmail binaries. Furthermore, it may begin delivering messages from +the queue before you have had a chance to test it. + + +WARNING for upgrades from 1.00 or 1.01: qlist has been split into a +separate package. You can obtain it from http://pobox.com/~djb/qlist.html +if you have any users who need it. + +WARNING for upgrades from 1.01: recipientmap is gone. The virtualdomains +mechanism has been expanded to support virtual users. + + +Before starting, compare conf* to your old conf*, and make any necessary +changes. You can copy conf* from 1.02 or 1.03. + + +How to install: + + 1. Compile the programs and create the formatted man pages: + # make it man + + 2. Inform your users that mail will not be accepted for a few minutes. + + 3. Disable deliveries by killing your old qmail-send. Wait for it to + print ``exiting'' in the log. + + 4. Disable SMTP service by commenting out the smtp line in inetd.conf; + kill -HUP your inetd. (If you are using tcpserver, simply kill -STOP + your tcpserver. If you are running a QMTP server, disable that too.) + Wait for current qmail-smtpd processes to die. + + 5. Install the new binaries and man pages: + # make setup check + + 6. If your boot scripts are using qmail-start instead of /var/qmail/rc: + Copy /var/qmail/boot/home to /var/qmail/rc. (Use home+df instead if + you have installed dot-forward; use proc or proc+df if you are using + procmail by default for local deliveries.) Compare /var/qmail/rc to + your qmail-start boot line, and edit /var/qmail/rc if necessary. + Replace your qmail-start boot line with + csh -cf '/var/qmail/rc &' + + 7. Reenable deliveries: + # csh -cf '/var/qmail/rc &' + + 8. Read TEST.deliver. + + 9. Reenable SMTP service by restoring the smtp line in inetd.conf; kill + -HUP your inetd. (If you are using tcpserver, simply kill -CONT your + tcpserver. If you are running a QMTP server, reenable that too.) + +10. Read TEST.receive. + + +That's it! To report success: + % ( echo 'First M. Last'; cat `cat SYSDEPS` ) | mail djb-qst@cr.yp.to +Replace First M. Last with your name. + +If you have questions about qmail, join the qmail mailing list; see +http://pobox.com/~djb/qmail.html. diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..5ac3967 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +netqmail 1.06 diff --git a/addresses.5 b/addresses.5 new file mode 100644 index 0000000..1fd4c5b --- /dev/null +++ b/addresses.5 @@ -0,0 +1,260 @@ +.TH addresses 5 +.SH "NAME" +addresses \- formats for Internet mail addresses +.SH "INTRODUCTION" +A +.B mail address +is a string of characters containing @. + +Every mail address has a +.B local part +and a +.B domain part\fR. +The domain part is everything after the final @. +The local part is everything before. + +For example, the mail addresses + +.EX + God@heaven.af.mil + @heaven.af.mil + @at@@heaven.af.mil +.EE + +all have domain part +.BR heaven.af.mil . +The local parts are +.BR God , +empty, +and +.BR @at@ . + +Some domains have owners. +It is up to the owner of +.B heaven.af.mil +to say how mail messages will be delivered to addresses with domain part +.BR heaven.af.mil . + +The domain part of an address is interpreted without regard to case, so + +.EX + God@heaven.af.mil +.br + God@HEAVEN.AF.MIL +.br + God@Heaven.AF.Mil +.EE + +all refer to the same domain. + +There is one exceptional address that does not contain an @: +namely, the empty string. +The empty string cannot be used as a recipient address. +It can be used as a sender address so that +the real sender doesn't receive bounces. +.SH "QMAIL EXTENSIONS" +The +.B qmail +system allows several further types of addresses in mail envelopes. + +First, an envelope recipient address without an @ is interpreted as being at +.IR envnoathost . +For example, if +.I envnoathost +is +.BR heaven.af.mil , +the address +.B God +will be rewritten as +.BR God@heaven.af.mil . + +Second, the address +.B #@[] +is used as an envelope sender address for double bounces. + +Third, envelope sender addresses of the form +.I pre\fB@\fIhost\fB-@[] +are used to support variable envelope return paths (VERPs). +.B qmail-send +will rewrite +.I pre\fB@\fIhost\fB-@[] +as +.I prerecip\fB=\fIdomain\fB@\fIhost +for deliveries to +.IR recip\fB@\fIdomain . +Bounces directly from +.B qmail-send +will come back to +.IR pre\fB@\fIhost . +.SH "CHOOSING MAIL ADDRESSES" +Here are some suggestions on choosing mail addresses for the Internet. + +Do not use non-ASCII characters. +Under RFC 822 and RFC 821, +these characters cannot be used in mail headers or in SMTP commands. +In practice, they are regularly corrupted. + +Do not use ASCII control characters. +NUL is regularly corrupted. +CR and LF cannot be used in some combinations +and are corrupted in all. +None of these characters are usable on business cards. + +Avoid spaces and the characters + +.EX + \\"<>()[],;: +.EE + +These all require quoting in mail headers and in SMTP. +Many existing mail programs do not handle quoting properly. + +Do not use @ in a local part. +@ requires quoting in mail headers and in SMTP. +Many programs incorrectly look for the first @, +rather than the last @, +to find the domain part of an address. + +In a local part, +do not use two consecutive dots, a dot at the beginning, or a dot at the end. +Any of these would require quoting in mail headers. + +Do not use an empty local part; it cannot appear in SMTP commands. + +Avoid local parts longer than 64 characters. + +Be wary of uppercase letters in local parts. +Some mail programs (and users!) will incorrectly convert +.B God@heaven.af.mil +to +.BR god@heaven.af.mil . + +Be wary of the following characters: + +.EX + $&!#~`'^*|{} +.EE + +Some users will not know +how to feed these characters safely to their mail programs. + +In domain names, stick to letters, digits, dash, and dot. +One popular DNS resolver has, +under the banner of security, +recently begun destroying domain names +that contain certain other characters, +including underscore. +Exception: A dotted-decimal IP address in brackets, +such as +.BR [127.0.0.1] , +identifies a domain owned by whoever owns the host at that IP address, +and can be used safely. + +In a domain name, +do not use two consecutive dots, +a dot at the beginning, +or a dot at the end. +This means that, +when a domain name is broken down into components separated by dots, +there are no empty components. + +Always use at least one dot in a domain name. +If you own the +.B mil +domain, +don't bother using the address +.BR root@mil ; +most users will be unable to send messages to that address. +Same for the root domain. + +Avoid domain names longer than 64 characters. +.SH "ENCODED ADDRESSES IN SMTP COMMANDS" +RFC 821 defines an encoding of mail addresses in SMTP. +For example, the addresses + +.EX + God@heaven.af.mil +.br + a"quote@heaven.af.mil +.br + The Almighty.One@heaven.af.mil +.EE + +could be encoded in RCPT commands as + +.EX + RCPT TO: +.br + RCPT TO: +.br + RCPT TO: +.EE + +There are several restrictions in RFC 821 +on the mail addresses that can be used over SMTP. +Non-ASCII characters are prohibited. +The local part must not be empty. +The domain part must be a sequence of elements separated by dots, +where each element is either a component, +a sequence of digits preceded by #, +or a dotted-decimal IP address surrounded by brackets. +The only allowable characters in components are +letters, digits, and dashes. +Every component must (believe it or not) +have at least three characters; +the first character must be a letter; +the last character must not be a hyphen. +.SH "ENCODED ADDRESSES IN MAIL HEADERS" +RFC 822 defines an encoding of mail addresses +in certain header fields in a mail message. +For example, the addresses + +.EX + God@heaven.af.mil +.br + a"quote@heaven.af.mil +.br + The Almighty.One@heaven.af.mil +.EE + +could be encoded in a +.B To +field as + +.EX + To: God@heaven.af.mil, +.br + <@brl.mil:"a\\"quote"@heaven.af.mil>, +.br + "The Almighty".One@heaven.af.mil +.EE + +or perhaps + +.EX + To: < "God"@heaven .af.mil>, +.br + "a\\"quote" (Who?) @ heaven . af. mil +.br + , God<"The Almighty.One"@heaven.af.mil> +.EE + +There are several restrictions on the mail addresses that can +be used in these header fields. +Non-ASCII characters are prohibited. +The domain part must be a sequence of elements separated by dots, +where each element either (1) begins with [ and ends with ] +or (2) is a nonempty string of printable ASCII characters +not including any of + +.EX + \\".<>()[],;: +.EE + +and not including space. +.SH "SEE ALSO" +envelopes(5), +qmail-header(5), +qmail-inject(8), +qmail-remote(8), +qmail-smtpd(8) diff --git a/alloc.3 b/alloc.3 new file mode 100644 index 0000000..58b4432 --- /dev/null +++ b/alloc.3 @@ -0,0 +1,62 @@ +.TH alloc 3 +.SH NAME +alloc \- allocate memory +.SH SYNTAX +.B #include + +char *\fBalloc\fP(\fInew\fR); + +void \fBalloc_free\fP(\fIx\fR); + +void \fBalloc_re\fP(&\fIx\fR,\fIold\fR,\fInew\fR); + +char *\fIx\fR; +.br +unsigned int \fIold\fR; +.br +unsigned int \fInew\fR; +.SH DESCRIPTION +.B alloc +allocates enough space from the heap for +.I new +bytes of data, +adequately aligned for any data type. +.I new +may be 0. +.B alloc +returns a pointer to the space. +If space is not available, +.B alloc +returns 0, +setting +.B errno +appropriately. + +.B alloc_free +returns space to the heap. + +.B alloc_re +expands the space allocated to +.I x +from +.I old +bytes to +.I new +bytes. +It allocates new space, +copies +.I old +bytes from the old space to the new space, +returns the old space to the heap, +and changes +.I x +to point to the new space. +It then returns 1. +If space is not available, +.B alloc_re +returns 0, +leaving the old space alone. +.SH "SEE ALSO" +sbrk(2), +malloc(3), +error(3) diff --git a/alloc.c b/alloc.c new file mode 100644 index 0000000..c661453 --- /dev/null +++ b/alloc.c @@ -0,0 +1,32 @@ +#include "alloc.h" +#include "error.h" +extern char *malloc(); +extern void free(); + +#define ALIGNMENT 16 /* XXX: assuming that this alignment is enough */ +#define SPACE 4096 /* must be multiple of ALIGNMENT */ + +typedef union { char irrelevant[ALIGNMENT]; double d; } aligned; +static aligned realspace[SPACE / ALIGNMENT]; +#define space ((char *) realspace) +static unsigned int avail = SPACE; /* multiple of ALIGNMENT; 0<=avail<=SPACE */ + +/*@null@*//*@out@*/char *alloc(n) +unsigned int n; +{ + char *x; + n = ALIGNMENT + n - (n & (ALIGNMENT - 1)); /* XXX: could overflow */ + if (n <= avail) { avail -= n; return space + avail; } + x = malloc(n); + if (!x) errno = error_nomem; + return x; +} + +void alloc_free(x) +char *x; +{ + if (x >= space) + if (x < space + SPACE) + return; /* XXX: assuming that pointers are flat */ + free(x); +} diff --git a/alloc.h b/alloc.h new file mode 100644 index 0000000..1b1d893 --- /dev/null +++ b/alloc.h @@ -0,0 +1,8 @@ +#ifndef ALLOC_H +#define ALLOC_H + +extern /*@null@*//*@out@*/char *alloc(); +extern void alloc_free(); +extern int alloc_re(); + +#endif diff --git a/alloc_re.c b/alloc_re.c new file mode 100644 index 0000000..feb8b49 --- /dev/null +++ b/alloc_re.c @@ -0,0 +1,17 @@ +#include "alloc.h" +#include "byte.h" + +int alloc_re(x,m,n) +char **x; +unsigned int m; +unsigned int n; +{ + char *y; + + y = alloc(n); + if (!y) return 0; + byte_copy(y,m,*x); + alloc_free(*x); + *x = y; + return 1; +} diff --git a/auto-gid.c b/auto-gid.c new file mode 100644 index 0000000..774a8c1 --- /dev/null +++ b/auto-gid.c @@ -0,0 +1,51 @@ +#include +#include +#include "subfd.h" +#include "substdio.h" +#include "readwrite.h" +#include "exit.h" +#include "scan.h" +#include "fmt.h" + +char buf1[256]; +substdio ss1 = SUBSTDIO_FDBUF(write,1,buf1,sizeof(buf1)); + +void outs(s) +char *s; +{ + if (substdio_puts(&ss1,s) == -1) _exit(111); +} + +void main(argc,argv) +int argc; +char **argv; +{ + char *name; + char *value; + struct group *gr; + char strnum[FMT_ULONG]; + + name = argv[1]; + if (!name) _exit(100); + value = argv[2]; + if (!value) _exit(100); + + gr = getgrnam(value); + if (!gr) { + substdio_puts(subfderr,"fatal: unable to find group "); + substdio_puts(subfderr,value); + substdio_puts(subfderr,"\n"); + substdio_flush(subfderr); + _exit(111); + } + + strnum[fmt_ulong(strnum,(unsigned long) gr->gr_gid)] = 0; + + outs("int "); + outs(name); + outs(" = "); + outs(strnum); + outs(";\n"); + if (substdio_flush(&ss1) == -1) _exit(111); + _exit(0); +} diff --git a/auto-int.c b/auto-int.c new file mode 100644 index 0000000..c138869 --- /dev/null +++ b/auto-int.c @@ -0,0 +1,40 @@ +#include "substdio.h" +#include "readwrite.h" +#include "exit.h" +#include "scan.h" +#include "fmt.h" + +char buf1[256]; +substdio ss1 = SUBSTDIO_FDBUF(write,1,buf1,sizeof(buf1)); + +void puts(s) +char *s; +{ + if (substdio_puts(&ss1,s) == -1) _exit(111); +} + +void main(argc,argv) +int argc; +char **argv; +{ + char *name; + char *value; + unsigned long num; + char strnum[FMT_ULONG]; + + name = argv[1]; + if (!name) _exit(100); + value = argv[2]; + if (!value) _exit(100); + + scan_ulong(value,&num); + strnum[fmt_ulong(strnum,num)] = 0; + + puts("int "); + puts(name); + puts(" = "); + puts(strnum); + puts(";\n"); + if (substdio_flush(&ss1) == -1) _exit(111); + _exit(0); +} diff --git a/auto-int8.c b/auto-int8.c new file mode 100644 index 0000000..091978f --- /dev/null +++ b/auto-int8.c @@ -0,0 +1,40 @@ +#include "substdio.h" +#include "readwrite.h" +#include "exit.h" +#include "scan.h" +#include "fmt.h" + +char buf1[256]; +substdio ss1 = SUBSTDIO_FDBUF(write,1,buf1,sizeof(buf1)); + +void puts(s) +char *s; +{ + if (substdio_puts(&ss1,s) == -1) _exit(111); +} + +void main(argc,argv) +int argc; +char **argv; +{ + char *name; + char *value; + unsigned long num; + char strnum[FMT_ULONG]; + + name = argv[1]; + if (!name) _exit(100); + value = argv[2]; + if (!value) _exit(100); + + scan_8long(value,&num); + strnum[fmt_ulong(strnum,num)] = 0; + + puts("int "); + puts(name); + puts(" = "); + puts(strnum); + puts(";\n"); + if (substdio_flush(&ss1) == -1) _exit(111); + _exit(0); +} diff --git a/auto-str.c b/auto-str.c new file mode 100644 index 0000000..acc3d60 --- /dev/null +++ b/auto-str.c @@ -0,0 +1,44 @@ +#include "substdio.h" +#include "readwrite.h" +#include "exit.h" + +char buf1[256]; +substdio ss1 = SUBSTDIO_FDBUF(write,1,buf1,sizeof(buf1)); + +void puts(s) +char *s; +{ + if (substdio_puts(&ss1,s) == -1) _exit(111); +} + +void main(argc,argv) +int argc; +char **argv; +{ + char *name; + char *value; + unsigned char ch; + char octal[4]; + + name = argv[1]; + if (!name) _exit(100); + value = argv[2]; + if (!value) _exit(100); + + puts("char "); + puts(name); + puts("[] = \"\\\n"); + + while (ch = *value++) { + puts("\\"); + octal[3] = 0; + octal[2] = '0' + (ch & 7); ch >>= 3; + octal[1] = '0' + (ch & 7); ch >>= 3; + octal[0] = '0' + (ch & 7); + puts(octal); + } + + puts("\\\n\";\n"); + if (substdio_flush(&ss1) == -1) _exit(111); + _exit(0); +} diff --git a/auto-uid.c b/auto-uid.c new file mode 100644 index 0000000..326051c --- /dev/null +++ b/auto-uid.c @@ -0,0 +1,51 @@ +#include +#include +#include "subfd.h" +#include "substdio.h" +#include "readwrite.h" +#include "exit.h" +#include "scan.h" +#include "fmt.h" + +char buf1[256]; +substdio ss1 = SUBSTDIO_FDBUF(write,1,buf1,sizeof(buf1)); + +void outs(s) /* was named puts, but Solaris pwd.h includes stdio.h. dorks. */ +char *s; +{ + if (substdio_puts(&ss1,s) == -1) _exit(111); +} + +void main(argc,argv) +int argc; +char **argv; +{ + char *name; + char *value; + struct passwd *pw; + char strnum[FMT_ULONG]; + + name = argv[1]; + if (!name) _exit(100); + value = argv[2]; + if (!value) _exit(100); + + pw = getpwnam(value); + if (!pw) { + substdio_puts(subfderr,"fatal: unable to find user "); + substdio_puts(subfderr,value); + substdio_puts(subfderr,"\n"); + substdio_flush(subfderr); + _exit(111); + } + + strnum[fmt_ulong(strnum,(unsigned long) pw->pw_uid)] = 0; + + outs("int "); + outs(name); + outs(" = "); + outs(strnum); + outs(";\n"); + if (substdio_flush(&ss1) == -1) _exit(111); + _exit(0); +} diff --git a/auto_break.h b/auto_break.h new file mode 100644 index 0000000..b7f3a63 --- /dev/null +++ b/auto_break.h @@ -0,0 +1,6 @@ +#ifndef AUTO_BREAK_H +#define AUTO_BREAK_H + +extern char auto_break[]; + +#endif diff --git a/auto_patrn.h b/auto_patrn.h new file mode 100644 index 0000000..77cdf1f --- /dev/null +++ b/auto_patrn.h @@ -0,0 +1,6 @@ +#ifndef AUTO_PATRN_H +#define AUTO_PATRN_H + +extern int auto_patrn; + +#endif diff --git a/auto_qmail.h b/auto_qmail.h new file mode 100644 index 0000000..0c56001 --- /dev/null +++ b/auto_qmail.h @@ -0,0 +1,6 @@ +#ifndef AUTO_QMAIL_H +#define AUTO_QMAIL_H + +extern char auto_qmail[]; + +#endif diff --git a/auto_spawn.h b/auto_spawn.h new file mode 100644 index 0000000..165d988 --- /dev/null +++ b/auto_spawn.h @@ -0,0 +1,6 @@ +#ifndef AUTO_SPAWN_H +#define AUTO_SPAWN_H + +extern int auto_spawn; + +#endif diff --git a/auto_split.h b/auto_split.h new file mode 100644 index 0000000..3754129 --- /dev/null +++ b/auto_split.h @@ -0,0 +1,6 @@ +#ifndef AUTO_SPLIT_H +#define AUTO_SPLIT_H + +extern int auto_split; + +#endif diff --git a/auto_uids.h b/auto_uids.h new file mode 100644 index 0000000..1252ecb --- /dev/null +++ b/auto_uids.h @@ -0,0 +1,16 @@ +#ifndef AUTO_UIDS_H +#define AUTO_UIDS_H + +extern int auto_uida; +extern int auto_uidd; +extern int auto_uidl; +extern int auto_uido; +extern int auto_uidp; +extern int auto_uidq; +extern int auto_uidr; +extern int auto_uids; + +extern int auto_gidn; +extern int auto_gidq; + +#endif diff --git a/auto_usera.h b/auto_usera.h new file mode 100644 index 0000000..49d7755 --- /dev/null +++ b/auto_usera.h @@ -0,0 +1,6 @@ +#ifndef AUTO_USERA_H +#define AUTO_USERA_H + +extern char auto_usera[]; + +#endif diff --git a/binm1+df.sh b/binm1+df.sh new file mode 100644 index 0000000..0ff1a68 --- /dev/null +++ b/binm1+df.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +# Using splogger to send the log through syslog. +# Using dot-forward to support sendmail-style ~/.forward files. +# Using binmail to deliver messages to /var/spool/mail/$USER by default. +# Using BSD 4.4 binmail interface: /usr/libexec/mail.local -r + +exec env - PATH="QMAIL/bin:$PATH" \ +qmail-start '|dot-forward .forward +|preline -f /usr/libexec/mail.local -r "${SENDER:-MAILER-DAEMON}" -d "$USER"' \ +splogger qmail diff --git a/binm1.sh b/binm1.sh new file mode 100644 index 0000000..db79cbd --- /dev/null +++ b/binm1.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +# Using splogger to send the log through syslog. +# Using binmail to deliver messages to /var/spool/mail/$USER by default. +# Using BSD 4.4 binmail interface: /usr/libexec/mail.local -r + +exec env - PATH="QMAIL/bin:$PATH" \ +qmail-start \ +'|preline -f /usr/libexec/mail.local -r "${SENDER:-MAILER-DAEMON}" -d "$USER"' \ +splogger qmail diff --git a/binm2+df.sh b/binm2+df.sh new file mode 100644 index 0000000..4f78101 --- /dev/null +++ b/binm2+df.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +# Using splogger to send the log through syslog. +# Using dot-forward to support sendmail-style ~/.forward files. +# Using binmail to deliver messages to /var/spool/mail/$USER by default. +# Using SVR4 binmail interface: /bin/mail -r + +exec env - PATH="QMAIL/bin:$PATH" \ +qmail-start '|dot-forward .forward +|preline -f /bin/mail -r "${SENDER:-MAILER-DAEMON}" -d "$USER"' \ +splogger qmail diff --git a/binm2.sh b/binm2.sh new file mode 100644 index 0000000..7905308 --- /dev/null +++ b/binm2.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +# Using splogger to send the log through syslog. +# Using binmail to deliver messages to /var/spool/mail/$USER by default. +# Using SVR4 binmail interface: /bin/mail -r + +exec env - PATH="QMAIL/bin:$PATH" \ +qmail-start \ +'|preline -f /bin/mail -r "${SENDER:-MAILER-DAEMON}" -d "$USER"' \ +splogger qmail diff --git a/binm3+df.sh b/binm3+df.sh new file mode 100644 index 0000000..3d69401 --- /dev/null +++ b/binm3+df.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +# Using splogger to send the log through syslog. +# Using dot-forward to support sendmail-style ~/.forward files. +# Using binmail to deliver messages to /var/spool/mail/$USER by default. +# Using V7 binmail interface: /bin/mail -f + +exec env - PATH="QMAIL/bin:$PATH" \ +qmail-start '|dot-forward .forward +|preline -f /bin/mail -f "${SENDER:-MAILER-DAEMON}" -d "$USER"' \ +splogger qmail diff --git a/binm3.sh b/binm3.sh new file mode 100644 index 0000000..eb139e6 --- /dev/null +++ b/binm3.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +# Using splogger to send the log through syslog. +# Using binmail to deliver messages to /var/spool/mail/$USER by default. +# Using V7 binmail interface: /bin/mail -f + +exec env - PATH="QMAIL/bin:$PATH" \ +qmail-start \ +'|preline -f /bin/mail -f "${SENDER:-MAILER-DAEMON}" -d "$USER"' \ +splogger qmail diff --git a/bouncesaying.1 b/bouncesaying.1 new file mode 100644 index 0000000..d99a460 --- /dev/null +++ b/bouncesaying.1 @@ -0,0 +1,71 @@ +.TH bouncesaying 1 +.SH NAME +bouncesaying \- perhaps bounce each incoming message +.SH SYNOPSIS +in +.BR .qmail : +.B |bouncesaying +.I error +[ +.I program +[ +.I arg ... +] +] +.SH DESCRIPTION +.B bouncesaying +feeds each new mail message to +.I program +with the given arguments. +If +.I program +exits 0, +.B bouncesaying +prints +.I error +and bounces the message. + +If +.I program +exits 111, +.B bouncesaying +exits 111, +so delivery will be retried later. + +If +.I program +exits anything else +(or does not exist), +.B bouncesaying +exits 0, +so the rest of +.B .qmail +will be processed as usual. + +Note that +it is not safe for +.I program +to fork a child that +reads the message in the background. + +If +.I program +is not supplied, +.B bouncesaying +always bounces the message: + +.EX + |bouncesaying 'This address no longer accepts mail.' +.EE + +.B WARNING: +If you create a +.B .qmail +file to enable +.BR bouncesaying , +make sure to also add a line specifying delivery to your normal mailbox. +.SH "SEE ALSO" +condredirect(1), +except(1), +dot-qmail(5), +qmail-command(8) diff --git a/bouncesaying.c b/bouncesaying.c new file mode 100644 index 0000000..c7d0026 --- /dev/null +++ b/bouncesaying.c @@ -0,0 +1,41 @@ +#include "fork.h" +#include "strerr.h" +#include "error.h" +#include "wait.h" +#include "sig.h" +#include "exit.h" + +#define FATAL "bouncesaying: fatal: " + +void main(argc,argv) +int argc; +char **argv; +{ + int pid; + int wstat; + + if (!argv[1]) + strerr_die1x(100,"bouncesaying: usage: bouncesaying error [ program [ arg ... ] ]"); + + if (argv[2]) { + pid = fork(); + if (pid == -1) + strerr_die2sys(111,FATAL,"unable to fork: "); + if (pid == 0) { + execvp(argv[2],argv + 2); + if (error_temp(errno)) _exit(111); + _exit(100); + } + if (wait_pid(&wstat,pid) == -1) + strerr_die2x(111,FATAL,"wait failed"); + if (wait_crashed(wstat)) + strerr_die2x(111,FATAL,"child crashed"); + switch(wait_exitcode(wstat)) { + case 0: break; + case 111: strerr_die2x(111,FATAL,"temporary child error"); + default: _exit(0); + } + } + + strerr_die1x(100,argv[1]); +} diff --git a/byte.h b/byte.h new file mode 100644 index 0000000..de06c69 --- /dev/null +++ b/byte.h @@ -0,0 +1,13 @@ +#ifndef BYTE_H +#define BYTE_H + +extern unsigned int byte_chr(); +extern unsigned int byte_rchr(); +extern void byte_copy(); +extern void byte_copyr(); +extern int byte_diff(); +extern void byte_zero(); + +#define byte_equal(s,n,t) (!byte_diff((s),(n),(t))) + +#endif diff --git a/byte_chr.c b/byte_chr.c new file mode 100644 index 0000000..f81dde8 --- /dev/null +++ b/byte_chr.c @@ -0,0 +1,20 @@ +#include "byte.h" + +unsigned int byte_chr(s,n,c) +char *s; +register unsigned int n; +int c; +{ + register char ch; + register char *t; + + ch = c; + t = s; + for (;;) { + if (!n) break; if (*t == ch) break; ++t; --n; + if (!n) break; if (*t == ch) break; ++t; --n; + if (!n) break; if (*t == ch) break; ++t; --n; + if (!n) break; if (*t == ch) break; ++t; --n; + } + return t - s; +} diff --git a/byte_copy.c b/byte_copy.c new file mode 100644 index 0000000..eaad11b --- /dev/null +++ b/byte_copy.c @@ -0,0 +1,14 @@ +#include "byte.h" + +void byte_copy(to,n,from) +register char *to; +register unsigned int n; +register char *from; +{ + for (;;) { + if (!n) return; *to++ = *from++; --n; + if (!n) return; *to++ = *from++; --n; + if (!n) return; *to++ = *from++; --n; + if (!n) return; *to++ = *from++; --n; + } +} diff --git a/byte_cr.c b/byte_cr.c new file mode 100644 index 0000000..3e7a1d5 --- /dev/null +++ b/byte_cr.c @@ -0,0 +1,16 @@ +#include "byte.h" + +void byte_copyr(to,n,from) +register char *to; +register unsigned int n; +register char *from; +{ + to += n; + from += n; + for (;;) { + if (!n) return; *--to = *--from; --n; + if (!n) return; *--to = *--from; --n; + if (!n) return; *--to = *--from; --n; + if (!n) return; *--to = *--from; --n; + } +} diff --git a/byte_diff.c b/byte_diff.c new file mode 100644 index 0000000..cdbd760 --- /dev/null +++ b/byte_diff.c @@ -0,0 +1,16 @@ +#include "byte.h" + +int byte_diff(s,n,t) +register char *s; +register unsigned int n; +register char *t; +{ + for (;;) { + if (!n) return 0; if (*s != *t) break; ++s; ++t; --n; + if (!n) return 0; if (*s != *t) break; ++s; ++t; --n; + if (!n) return 0; if (*s != *t) break; ++s; ++t; --n; + if (!n) return 0; if (*s != *t) break; ++s; ++t; --n; + } + return ((int)(unsigned int)(unsigned char) *s) + - ((int)(unsigned int)(unsigned char) *t); +} diff --git a/byte_rchr.c b/byte_rchr.c new file mode 100644 index 0000000..476bc22 --- /dev/null +++ b/byte_rchr.c @@ -0,0 +1,23 @@ +#include "byte.h" + +unsigned int byte_rchr(s,n,c) +char *s; +register unsigned int n; +int c; +{ + register char ch; + register char *t; + register char *u; + + ch = c; + t = s; + u = 0; + for (;;) { + if (!n) break; if (*t == ch) u = t; ++t; --n; + if (!n) break; if (*t == ch) u = t; ++t; --n; + if (!n) break; if (*t == ch) u = t; ++t; --n; + if (!n) break; if (*t == ch) u = t; ++t; --n; + } + if (!u) u = t; + return u - s; +} diff --git a/byte_zero.c b/byte_zero.c new file mode 100644 index 0000000..92009ba --- /dev/null +++ b/byte_zero.c @@ -0,0 +1,13 @@ +#include "byte.h" + +void byte_zero(s,n) +char *s; +register unsigned int n; +{ + for (;;) { + if (!n) break; *s++ = 0; --n; + if (!n) break; *s++ = 0; --n; + if (!n) break; *s++ = 0; --n; + if (!n) break; *s++ = 0; --n; + } +} diff --git a/case.3 b/case.3 new file mode 100644 index 0000000..58bd724 --- /dev/null +++ b/case.3 @@ -0,0 +1,100 @@ +.TH case 3 +.SH NAME +case \- convert ASCII uppercase bytes to lowercase +.SH SYNTAX +.B #include + +void \fBcase_lowers\fP(\fIs\fR); +.br +void \fBcase_lowerb\fP(\fIs\fR,\fIlen\fR); + +int \fBcase_diffs\fP(\fIs\fR,\fIt\fR); +.br +int \fBcase_equals\fP(\fIs\fR,\fIt\fR); +.br +int \fBcase_starts\fP(\fIs\fR,\fIt\fR); + +int \fBcase_diffb\fP(\fIs\fR,\fIlen\fR,\fIt\fR); +.br +int \fBcase_startb\fP(\fIs\fR,\fIlen\fR,\fIt\fR); + +char *\fIs\fR; +.br +char *\fIt\fR; +.br +unsigned int \fIlen\fR; +.SH DESCRIPTION +.B case_lowers +converts each uppercase byte in the string +.I s +to lowercase. +.I s +must be 0-terminated. + +.B case_lowerb +converts each uppercase byte in the buffer +.IR s , +of length +.IR len , +to lowercase. + +.B case_diffs +lexicographically compares lowercase versions of the strings +.I s +and +.IR t . +It returns something positive, negative, or zero +when the first is larger than, smaller than, or equal to the second. +.I s +and +.I t +must be 0-terminated. + +.B case_equals +means +.BR !case_diffs . + +.B case_starts +returns 1 if a lowercase version of +.I s +starts with a lowercase version of +.IR t . +.I s +and +.I t +must be 0-terminated. + +.B case_diffb +lexicographically compares lowercase versions of the buffers +.I s +and +.IR t , +each of length +.IR len . +It returns something positive, negative, or zero +when the first is larger than, smaller than, or equal to the second. + +.B case_startb +returns 1 if a lowercase version of the buffer +.IR s , +of length +.IR len , +starts with a lowercase version of the string +.IR t . +.I t +must be 0-terminated. + +The +.B case +routines +are ASCII-specific. +They are suitable for programs that handle +case-independent networking protocols. + +All comparisons are performed on unsigned bytes. +.SH "SEE ALSO" +byte_diff(3), +byte_equal(3), +str_diff(3), +str_equal(3), +str_start(3) diff --git a/case.h b/case.h new file mode 100644 index 0000000..35a2434 --- /dev/null +++ b/case.h @@ -0,0 +1,13 @@ +#ifndef CASE_H +#define CASE_H + +extern void case_lowers(); +extern void case_lowerb(); +extern int case_diffs(); +extern int case_diffb(); +extern int case_starts(); +extern int case_startb(); + +#define case_equals(s,t) (!case_diffs((s),(t))) + +#endif diff --git a/case_diffb.c b/case_diffb.c new file mode 100644 index 0000000..9064b8a --- /dev/null +++ b/case_diffb.c @@ -0,0 +1,21 @@ +#include "case.h" + +int case_diffb(s,len,t) +register char *s; +unsigned int len; +register char *t; +{ + register unsigned char x; + register unsigned char y; + + while (len > 0) { + --len; + x = *s++ - 'A'; + if (x <= 'Z' - 'A') x += 'a'; else x += 'A'; + y = *t++ - 'A'; + if (y <= 'Z' - 'A') y += 'a'; else y += 'A'; + if (x != y) + return ((int)(unsigned int) x) - ((int)(unsigned int) y); + } + return 0; +} diff --git a/case_diffs.c b/case_diffs.c new file mode 100644 index 0000000..212c645 --- /dev/null +++ b/case_diffs.c @@ -0,0 +1,19 @@ +#include "case.h" + +int case_diffs(s,t) +register char *s; +register char *t; +{ + register unsigned char x; + register unsigned char y; + + for (;;) { + x = *s++ - 'A'; + if (x <= 'Z' - 'A') x += 'a'; else x += 'A'; + y = *t++ - 'A'; + if (y <= 'Z' - 'A') y += 'a'; else y += 'A'; + if (x != y) break; + if (!x) break; + } + return ((int)(unsigned int) x) - ((int)(unsigned int) y); +} diff --git a/case_lowerb.c b/case_lowerb.c new file mode 100644 index 0000000..4034c14 --- /dev/null +++ b/case_lowerb.c @@ -0,0 +1,14 @@ +#include "case.h" + +void case_lowerb(s,len) +char *s; +unsigned int len; +{ + unsigned char x; + while (len > 0) { + --len; + x = *s - 'A'; + if (x <= 'Z' - 'A') *s = x + 'a'; + ++s; + } +} diff --git a/case_lowers.c b/case_lowers.c new file mode 100644 index 0000000..208b3f5 --- /dev/null +++ b/case_lowers.c @@ -0,0 +1,12 @@ +#include "case.h" + +void case_lowers(s) +char *s; +{ + unsigned char x; + while (x = *s) { + x -= 'A'; + if (x <= 'Z' - 'A') *s = x + 'a'; + ++s; + } +} diff --git a/case_starts.c b/case_starts.c new file mode 100644 index 0000000..2278a0a --- /dev/null +++ b/case_starts.c @@ -0,0 +1,18 @@ +#include "case.h" + +int case_starts(s,t) +register char *s; +register char *t; +{ + register unsigned char x; + register unsigned char y; + + for (;;) { + x = *s++ - 'A'; + if (x <= 'Z' - 'A') x += 'a'; else x += 'A'; + y = *t++ - 'A'; + if (y <= 'Z' - 'A') y += 'a'; else y += 'A'; + if (!y) return 1; + if (x != y) return 0; + } +} diff --git a/cdb.3 b/cdb.3 new file mode 100644 index 0000000..a85b34c --- /dev/null +++ b/cdb.3 @@ -0,0 +1,62 @@ +.TH cdb 3 +.SH NAME +cdb \- read from a constant database +.SH SYNTAX +.B #include + +int \fBcdb_seek(\fP\fIfd,key,len,dlen\fR\fB)\fP; + +int \fIfd\fR; +.br +char *\fIkey\fR; +.br +unsigned int \fIlen\fR; +.br +uint32 *\fIdlen\fR; +.SH DESCRIPTION +.B cdb_seek +looks up +.I key +in a constant database. +It returns 1 if +.I key +is present, +0 if +.I key +is not present, +or \-1 if there was a read error. +.I key +is an array of +.I len +characters. + +.B cdb_seek +needs an open file descriptor, +.IR fd , +pointing to the database. +If +.B cdb_seek +returns 1, +it points +.I fd +at the beginning of the data portion of the first record +indexed by +.IR key , +and it stores the data length in +.IR dlen. +.B cdb_seek +does not provide a way to read subsequent records with the same key. + +It's fine to do several +.B cdb_seek +lookups with the same open file descriptor. +Beware, however, that two simultaneous +.B cdb_seek +lookups can fail horribly; +separate processes should not share the same database descriptor. +Furthermore, any updates after the database was opened +will be invisible. +It's rarely a good idea for a long-running program +to hold a database open. +.SH "SEE ALSO" +cdbget(1) diff --git a/cdb.h b/cdb.h new file mode 100644 index 0000000..571e5d6 --- /dev/null +++ b/cdb.h @@ -0,0 +1,12 @@ +#ifndef CDB_H +#define CDB_H + +#include "uint32.h" + +extern uint32 cdb_hash(); +extern uint32 cdb_unpack(); + +extern int cdb_bread(); +extern int cdb_seek(); + +#endif diff --git a/cdb_hash.c b/cdb_hash.c new file mode 100644 index 0000000..8238020 --- /dev/null +++ b/cdb_hash.c @@ -0,0 +1,16 @@ +#include "cdb.h" + +uint32 cdb_hash(buf,len) +unsigned char *buf; +unsigned int len; +{ + uint32 h; + + h = 5381; + while (len) { + --len; + h += (h << 5); + h ^= (uint32) *buf++; + } + return h; +} diff --git a/cdb_seek.c b/cdb_seek.c new file mode 100644 index 0000000..f31b87d --- /dev/null +++ b/cdb_seek.c @@ -0,0 +1,94 @@ +#include +#include +#include "cdb.h" + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +int cdb_bread(fd,buf,len) +int fd; +char *buf; +int len; +{ + int r; + while (len > 0) { + do + r = read(fd,buf,len); + while ((r == -1) && (errno == EINTR)); + if (r == -1) return -1; + if (r == 0) { errno = EIO; return -1; } + buf += r; + len -= r; + } + return 0; +} + +static int match(fd,key,len) +int fd; +char *key; +unsigned int len; +{ + char buf[32]; + int n; + int i; + + while (len > 0) { + n = sizeof(buf); + if (n > len) n = len; + if (cdb_bread(fd,buf,n) == -1) return -1; + for (i = 0;i < n;++i) if (buf[i] != key[i]) return 0; + key += n; + len -= n; + } + return 1; +} + +int cdb_seek(fd,key,len,dlen) +int fd; +char *key; +unsigned int len; +uint32 *dlen; +{ + char packbuf[8]; + uint32 pos; + uint32 h; + uint32 lenhash; + uint32 h2; + uint32 loop; + uint32 poskd; + + h = cdb_hash(key,len); + + pos = 8 * (h & 255); + if (lseek(fd,(off_t) pos,SEEK_SET) == -1) return -1; + + if (cdb_bread(fd,packbuf,8) == -1) return -1; + + pos = cdb_unpack(packbuf); + lenhash = cdb_unpack(packbuf + 4); + + if (!lenhash) return 0; + h2 = (h >> 8) % lenhash; + + for (loop = 0;loop < lenhash;++loop) { + if (lseek(fd,(off_t) (pos + 8 * h2),SEEK_SET) == -1) return -1; + if (cdb_bread(fd,packbuf,8) == -1) return -1; + poskd = cdb_unpack(packbuf + 4); + if (!poskd) return 0; + if (cdb_unpack(packbuf) == h) { + if (lseek(fd,(off_t) poskd,SEEK_SET) == -1) return -1; + if (cdb_bread(fd,packbuf,8) == -1) return -1; + if (cdb_unpack(packbuf) == len) + switch(match(fd,key,len)) { + case -1: + return -1; + case 1: + *dlen = cdb_unpack(packbuf + 4); + return 1; + } + } + if (++h2 == lenhash) h2 = 0; + } + return 0; +} diff --git a/cdb_unpack.c b/cdb_unpack.c new file mode 100644 index 0000000..c882202 --- /dev/null +++ b/cdb_unpack.c @@ -0,0 +1,12 @@ +#include "cdb.h" + +uint32 cdb_unpack(buf) +unsigned char *buf; +{ + uint32 num; + num = buf[3]; num <<= 8; + num += buf[2]; num <<= 8; + num += buf[1]; num <<= 8; + num += buf[0]; + return num; +} diff --git a/cdbmake.h b/cdbmake.h new file mode 100644 index 0000000..883a231 --- /dev/null +++ b/cdbmake.h @@ -0,0 +1,35 @@ +#ifndef CDBMAKE_H +#define CDBMAKE_H + +#include "uint32.h" + +#define CDBMAKE_HPLIST 1000 + +struct cdbmake_hp { uint32 h; uint32 p; } ; + +struct cdbmake_hplist { + struct cdbmake_hp hp[CDBMAKE_HPLIST]; + struct cdbmake_hplist *next; + int num; +} ; + +struct cdbmake { + char final[2048]; + uint32 count[256]; + uint32 start[256]; + struct cdbmake_hplist *head; + struct cdbmake_hp *split; /* includes space for hash */ + struct cdbmake_hp *hash; + uint32 numentries; +} ; + +extern void cdbmake_pack(); +#define CDBMAKE_HASHSTART ((uint32) 5381) +extern uint32 cdbmake_hashadd(); + +extern void cdbmake_init(); +extern int cdbmake_add(); +extern int cdbmake_split(); +extern uint32 cdbmake_throw(); + +#endif diff --git a/cdbmake_add.c b/cdbmake_add.c new file mode 100644 index 0000000..83a03ff --- /dev/null +++ b/cdbmake_add.c @@ -0,0 +1,118 @@ +#include "alloc.h" +#include "cdbmake.h" + +void cdbmake_init(cdbm) +struct cdbmake *cdbm; +{ + cdbm->head = 0; + cdbm->split = 0; + cdbm->hash = 0; + cdbm->numentries = 0; +} + +int cdbmake_add(cdbm,h,p,alloc) +struct cdbmake *cdbm; +uint32 h; +uint32 p; +char *(*alloc)(); +{ + struct cdbmake_hplist *head; + + head = cdbm->head; + if (!head || (head->num >= CDBMAKE_HPLIST)) { + head = (struct cdbmake_hplist *) alloc(sizeof(struct cdbmake_hplist)); + if (!head) return 0; + head->num = 0; + head->next = cdbm->head; + cdbm->head = head; + } + head->hp[head->num].h = h; + head->hp[head->num].p = p; + ++head->num; + ++cdbm->numentries; + return 1; +} + +int cdbmake_split(cdbm,alloc) +struct cdbmake *cdbm; +char *(*alloc)(); +{ + int i; + uint32 u; + uint32 memsize; + struct cdbmake_hplist *x; + + for (i = 0;i < 256;++i) + cdbm->count[i] = 0; + + for (x = cdbm->head;x;x = x->next) { + i = x->num; + while (i--) + ++cdbm->count[255 & x->hp[i].h]; + } + + memsize = 1; + for (i = 0;i < 256;++i) { + u = cdbm->count[i] * 2; + if (u > memsize) + memsize = u; + } + + memsize += cdbm->numentries; /* no overflow possible up to now */ + u = (uint32) 0 - (uint32) 1; + u /= sizeof(struct cdbmake_hp); + if (memsize > u) return 0; + + cdbm->split = (struct cdbmake_hp *) alloc(memsize * sizeof(struct cdbmake_hp)); + if (!cdbm->split) return 0; + + cdbm->hash = cdbm->split + cdbm->numentries; + + u = 0; + for (i = 0;i < 256;++i) { + u += cdbm->count[i]; /* bounded by numentries, so no overflow */ + cdbm->start[i] = u; + } + + for (x = cdbm->head;x;x = x->next) { + i = x->num; + while (i--) + cdbm->split[--cdbm->start[255 & x->hp[i].h]] = x->hp[i]; + } + + return 1; +} + +uint32 cdbmake_throw(cdbm,pos,b) +struct cdbmake *cdbm; +uint32 pos; +int b; +{ + uint32 len; + uint32 j; + uint32 count; + struct cdbmake_hp *hp; + uint32 where; + + count = cdbm->count[b]; + + len = count + count; /* no overflow possible */ + cdbmake_pack(cdbm->final + 8 * b,pos); + cdbmake_pack(cdbm->final + 8 * b + 4,len); + + if (len) { + for (j = 0;j < len;++j) + cdbm->hash[j].h = cdbm->hash[j].p = 0; + + hp = cdbm->split + cdbm->start[b]; + for (j = 0;j < count;++j) { + where = (hp->h >> 8) % len; + while (cdbm->hash[where].p) + if (++where == len) + where = 0; + cdbm->hash[where] = *hp++; + } + } + + return len; +} diff --git a/cdbmake_hash.c b/cdbmake_hash.c new file mode 100644 index 0000000..f9dc3e5 --- /dev/null +++ b/cdbmake_hash.c @@ -0,0 +1,10 @@ +#include "cdbmake.h" + +uint32 cdbmake_hashadd(h,c) +uint32 h; +unsigned int c; +{ + h += (h << 5); + h ^= (uint32) (unsigned char) c; + return h; +} diff --git a/cdbmake_pack.c b/cdbmake_pack.c new file mode 100644 index 0000000..04b5f5b --- /dev/null +++ b/cdbmake_pack.c @@ -0,0 +1,11 @@ +#include "cdbmake.h" + +void cdbmake_pack(buf,num) +unsigned char *buf; +uint32 num; +{ + *buf++ = num; num >>= 8; + *buf++ = num; num >>= 8; + *buf++ = num; num >>= 8; + *buf = num; +} diff --git a/cdbmss.c b/cdbmss.c new file mode 100644 index 0000000..2d8f367 --- /dev/null +++ b/cdbmss.c @@ -0,0 +1,65 @@ +#include "readwrite.h" +#include "seek.h" +#include "alloc.h" +#include "cdbmss.h" + +int cdbmss_start(c,fd) +struct cdbmss *c; +int fd; +{ + cdbmake_init(&c->cdbm); + c->fd = fd; + c->pos = sizeof(c->cdbm.final); + substdio_fdbuf(&c->ss,write,fd,c->ssbuf,sizeof(c->ssbuf)); + return seek_set(fd,(seek_pos) c->pos); +} + +int cdbmss_add(c,key,keylen,data,datalen) +struct cdbmss *c; +unsigned char *key; +unsigned int keylen; +unsigned char *data; +unsigned int datalen; +{ + uint32 h; + int i; + + cdbmake_pack(c->packbuf,(uint32) keylen); + cdbmake_pack(c->packbuf + 4,(uint32) datalen); + if (substdio_put(&c->ss,c->packbuf,8) == -1) return -1; + if (substdio_put(&c->ss,key,keylen) == -1) return -1; + if (substdio_put(&c->ss,data,datalen) == -1) return -1; + + h = CDBMAKE_HASHSTART; + for (i = 0;i < keylen;++i) + h = cdbmake_hashadd(h,(unsigned int) key[i]); + + if (!cdbmake_add(&c->cdbm,h,c->pos,alloc)) return -1; + + c->pos += 8 + keylen + datalen; /* XXX: overflow? */ + return 0; +} + +int cdbmss_finish(c) +struct cdbmss *c; +{ + int i; + uint32 len; + uint32 u; + + if (!cdbmake_split(&c->cdbm,alloc)) return -1; + + for (i = 0;i < 256;++i) { + len = cdbmake_throw(&c->cdbm,c->pos,i); + for (u = 0;u < len;++u) { + cdbmake_pack(c->packbuf,c->cdbm.hash[u].h); + cdbmake_pack(c->packbuf + 4,c->cdbm.hash[u].p); + if (substdio_put(&c->ss,c->packbuf,8) == -1) return -1; + c->pos += 8; /* XXX: overflow? */ + } + } + + if (substdio_flush(&c->ss) == -1) return -1; + if (seek_begin(c->fd) == -1) return -1; + return substdio_putflush(&c->ss,c->cdbm.final,sizeof(c->cdbm.final)); +} diff --git a/cdbmss.h b/cdbmss.h new file mode 100644 index 0000000..5e6bdf4 --- /dev/null +++ b/cdbmss.h @@ -0,0 +1,16 @@ +#ifndef CDBMSS_H +#define CDBMSS_H + +#include "cdbmake.h" +#include "substdio.h" + +struct cdbmss { + char ssbuf[1024]; + struct cdbmake cdbm; + substdio ss; + char packbuf[8]; + uint32 pos; + int fd; +} ; + +#endif diff --git a/chkshsgr.c b/chkshsgr.c new file mode 100644 index 0000000..2bcdade --- /dev/null +++ b/chkshsgr.c @@ -0,0 +1,9 @@ +#include "exit.h" +void main() +{ + short x[4]; + + x[0] = x[1] = 0; + if (getgroups(1,x) == 0) if (setgroups(1,x) == -1) _exit(1); + _exit(0); +} diff --git a/chkspawn.c b/chkspawn.c new file mode 100644 index 0000000..d19259e --- /dev/null +++ b/chkspawn.c @@ -0,0 +1,48 @@ +#include "substdio.h" +#include "subfd.h" +#include "fmt.h" +#include "select.h" +#include "exit.h" +#include "auto_spawn.h" + +char num[FMT_ULONG]; +fd_set fds; + +void main() +{ + unsigned long hiddenlimit; + unsigned long maxnumd; + + hiddenlimit = sizeof(fds) * 8; + maxnumd = (hiddenlimit - 5) / 2; + + if (auto_spawn < 1) { + substdio_puts(subfderr,"Oops. You have set conf-spawn lower than 1.\n"); + substdio_flush(subfderr); + _exit(1); + } + + if (auto_spawn > 255) { + substdio_puts(subfderr,"Oops. You have set conf-spawn higher than 255.\n"); + substdio_flush(subfderr); + _exit(1); + } + + if (auto_spawn > maxnumd) { + substdio_puts(subfderr,"Oops. Your system's FD_SET() has a hidden limit of "); + substdio_put(subfderr,num,fmt_ulong(num,hiddenlimit)); + substdio_puts(subfderr," descriptors.\n\ +This means that the qmail daemons could crash if you set the run-time\n\ +concurrency higher than "); + substdio_put(subfderr,num,fmt_ulong(num,maxnumd)); + substdio_puts(subfderr,". So I'm going to insist that the concurrency\n\ +limit in conf-spawn be at most "); + substdio_put(subfderr,num,fmt_ulong(num,maxnumd)); + substdio_puts(subfderr,". Right now it's "); + substdio_put(subfderr,num,fmt_ulong(num,(unsigned long) auto_spawn)); + substdio_puts(subfderr,".\n"); + substdio_flush(subfderr); + _exit(1); + } + _exit(0); +} diff --git a/coe.3 b/coe.3 new file mode 100644 index 0000000..06591b6 --- /dev/null +++ b/coe.3 @@ -0,0 +1,25 @@ +.TH coe 3 +.SH NAME +coe \- set close-on-exec flag for a descriptor +.SH SYNTAX +.B #include + +int \fBcoe\fP(\fIfd\fR); + +int \fIfd\fR; +.SH DESCRIPTION +.B coe +sets the close-on-exec flag for +file descriptor +.IR fd , +returning 0 if it was successful +or -1 on error. +If +.B coe +is successful, +.I fd +will be closed when the process calls +.BR execve . +.SH "SEE ALSO" +execve(2), +fcntl(2) diff --git a/coe.c b/coe.c new file mode 100644 index 0000000..d855158 --- /dev/null +++ b/coe.c @@ -0,0 +1,8 @@ +#include +#include "coe.h" + +int coe(fd) +int fd; +{ + return fcntl(fd,F_SETFD,1); +} diff --git a/coe.h b/coe.h new file mode 100644 index 0000000..1559bc1 --- /dev/null +++ b/coe.h @@ -0,0 +1,6 @@ +#ifndef COE_H +#define COE_H + +extern int coe(); + +#endif diff --git a/commands.c b/commands.c new file mode 100644 index 0000000..b0d3f61 --- /dev/null +++ b/commands.c @@ -0,0 +1,40 @@ +#include "commands.h" +#include "substdio.h" +#include "stralloc.h" +#include "str.h" +#include "case.h" + +static stralloc cmd = {0}; + +int commands(ss,c) +substdio *ss; +struct commands *c; +{ + int i; + char *arg; + + for (;;) { + if (!stralloc_copys(&cmd,"")) return -1; + + for (;;) { + if (!stralloc_readyplus(&cmd,1)) return -1; + i = substdio_get(ss,cmd.s + cmd.len,1); + if (i != 1) return i; + if (cmd.s[cmd.len] == '\n') break; + ++cmd.len; + } + + if (cmd.len > 0) if (cmd.s[cmd.len - 1] == '\r') --cmd.len; + + cmd.s[cmd.len] = 0; + + i = str_chr(cmd.s,' '); + arg = cmd.s + i; + while (*arg == ' ') ++arg; + cmd.s[i] = 0; + + for (i = 0;c[i].text;++i) if (case_equals(c[i].text,cmd.s)) break; + c[i].fun(arg); + if (c[i].flush) c[i].flush(); + } +} diff --git a/commands.h b/commands.h new file mode 100644 index 0000000..da05a8d --- /dev/null +++ b/commands.h @@ -0,0 +1,12 @@ +#ifndef COMMANDS_H +#define COMMANDS_H + +struct commands { + char *text; + void (*fun)(); + void (*flush)(); +} ; + +extern int commands(); + +#endif diff --git a/condredirect.1 b/condredirect.1 new file mode 100644 index 0000000..9f9d3c4 --- /dev/null +++ b/condredirect.1 @@ -0,0 +1,63 @@ +.TH condredirect 1 +.SH NAME +condredirect \- perhaps redirect mail to another address +.SH SYNOPSIS +in +.BR .qmail : +.B |condredirect +.I newaddress +.I program +[ +.I arg ... +] +.SH DESCRIPTION +.B condredirect +feeds each new mail message to +.I program +with the given arguments. +If +.I program +exits 0, +.B condredirect +forwards the mail message to +.IR newaddress , +and then exits 99, +so further commands in +.B .qmail +are ignored. + +If +.I program +exits 111, +.B condredirect +exits 111, +so delivery will be retried later. + +If +.I program +exits anything else +(or does not exist), +.B condredirect +exits 0, +so the rest of +.B .qmail +will be processed as usual. + +Note that +it is not safe for +.I program +to fork a child that +reads the message in the background. + +.B WARNING: +If you create a +.B .qmail +file to enable +.BR condredirect , +make sure to also add a line specifying delivery to your normal mailbox. +.SH "SEE ALSO" +bouncesaying(1), +except(1), +dot-qmail(5), +qmail-command(8), +qmail-queue(8) diff --git a/condredirect.c b/condredirect.c new file mode 100644 index 0000000..593d2f5 --- /dev/null +++ b/condredirect.c @@ -0,0 +1,85 @@ +#include "sig.h" +#include "readwrite.h" +#include "exit.h" +#include "env.h" +#include "error.h" +#include "fork.h" +#include "wait.h" +#include "seek.h" +#include "qmail.h" +#include "strerr.h" +#include "substdio.h" +#include "fmt.h" + +#define FATAL "condredirect: fatal: " + +struct qmail qqt; + +int mywrite(fd,buf,len) int fd; char *buf; int len; +{ + qmail_put(&qqt,buf,len); + return len; +} + +char inbuf[SUBSTDIO_INSIZE]; +char outbuf[1]; +substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof inbuf); +substdio ssout = SUBSTDIO_FDBUF(mywrite,-1,outbuf,sizeof outbuf); + +char num[FMT_ULONG]; + +void main(argc,argv) +int argc; +char **argv; +{ + char *sender; + char *dtline; + int pid; + int wstat; + char *qqx; + + if (!argv[1] || !argv[2]) + strerr_die1x(100,"condredirect: usage: condredirect newaddress program [ arg ... ]"); + + pid = fork(); + if (pid == -1) + strerr_die2sys(111,FATAL,"unable to fork: "); + if (pid == 0) { + execvp(argv[2],argv + 2); + if (error_temp(errno)) _exit(111); + _exit(100); + } + if (wait_pid(&wstat,pid) == -1) + strerr_die2x(111,FATAL,"wait failed"); + if (wait_crashed(wstat)) + strerr_die2x(111,FATAL,"child crashed"); + switch(wait_exitcode(wstat)) { + case 0: break; + case 111: strerr_die2x(111,FATAL,"temporary child error"); + default: _exit(0); + } + + if (seek_begin(0) == -1) + strerr_die2sys(111,FATAL,"unable to rewind: "); + sig_pipeignore(); + + sender = env_get("SENDER"); + if (!sender) strerr_die2x(100,FATAL,"SENDER not set"); + dtline = env_get("DTLINE"); + if (!dtline) strerr_die2x(100,FATAL,"DTLINE not set"); + + if (qmail_open(&qqt) == -1) + strerr_die2sys(111,FATAL,"unable to fork: "); + qmail_puts(&qqt,dtline); + if (substdio_copy(&ssout,&ssin) != 0) + strerr_die2sys(111,FATAL,"unable to read message: "); + substdio_flush(&ssout); + + num[fmt_ulong(num,qmail_qp(&qqt))] = 0; + + qmail_from(&qqt,sender); + qmail_to(&qqt,argv[1]); + qqx = qmail_close(&qqt); + if (*qqx) strerr_die2x(*qqx == 'D' ? 100 : 111,FATAL,qqx + 1); + strerr_die2x(99,"condredirect: qp ",num); +} diff --git a/conf-break b/conf-break new file mode 100644 index 0000000..28fd977 --- /dev/null +++ b/conf-break @@ -0,0 +1,9 @@ +- + +This character is the user-ext delimiter. The default delimiter is -, +meaning that user joe controls joe-anything. Some system administrators +prefer + or =. + +You can override this choice at run time with the qmail-users mechanism. + +Multicharacter delimiters are not permitted. diff --git a/conf-cc b/conf-cc new file mode 100644 index 0000000..e58fb9b --- /dev/null +++ b/conf-cc @@ -0,0 +1,3 @@ +cc -O2 + +This will be used to compile .c files. diff --git a/conf-groups b/conf-groups new file mode 100644 index 0000000..cec0845 --- /dev/null +++ b/conf-groups @@ -0,0 +1,6 @@ +qmail +nofiles + +These are the qmail groups. The second group should not have access to +any files, but it must be usable for processes; this requirement +excludes the ``nogroup'' and ``nobody'' groups on many systems. diff --git a/conf-ld b/conf-ld new file mode 100644 index 0000000..a9e796a --- /dev/null +++ b/conf-ld @@ -0,0 +1,3 @@ +cc -s + +This will be used to link .o files into an executable. diff --git a/conf-patrn b/conf-patrn new file mode 100644 index 0000000..70c72af --- /dev/null +++ b/conf-patrn @@ -0,0 +1,6 @@ +002 + +These stat bits are not allowed in ~ and ~/.qmail. On most systems, the +default umask is 022 or 077, so 022 will work here. + +Note that ~ftp, ~www, ~uucp, etc. should be owned by root. diff --git a/conf-qmail b/conf-qmail new file mode 100644 index 0000000..0267f30 --- /dev/null +++ b/conf-qmail @@ -0,0 +1,11 @@ +/var/qmail + +This is the qmail home directory. It must be a local directory, not +shared among machines. This is where qmail queues all mail messages. + +The queue (except for bounce message contents) is crashproof, if the +filesystem guarantees that single-byte writes are atomic and that +directory operations are synchronous. These guarantees are provided by +fixed-block filesystems such as UFS and by journaling filesystems. Under +Linux, make sure that all mail-handling filesystems are mounted with +synchronous metadata. diff --git a/conf-spawn b/conf-spawn new file mode 100644 index 0000000..d6a4256 --- /dev/null +++ b/conf-spawn @@ -0,0 +1,5 @@ +120 + +This is a silent concurrency limit. You can't set it above 255. On some +systems you can't set it above 125. qmail will refuse to compile if the +limit is too high. diff --git a/conf-split b/conf-split new file mode 100644 index 0000000..dc6aaf1 --- /dev/null +++ b/conf-split @@ -0,0 +1,3 @@ +23 + +This is the queue subdirectory split. diff --git a/conf-users b/conf-users new file mode 100644 index 0000000..9bb35df --- /dev/null +++ b/conf-users @@ -0,0 +1,15 @@ +alias +qmaild +qmaill +root +qmailp +qmailq +qmailr +qmails + +The qmail system is heavily partitioned for security; it does almost +nothing as root. + +The first eight lines of this file are the alias user, the daemon user, +the log user, the owner of miscellaneous files such as binaries, the +passwd user, the queue user, the remote user, and the send user. diff --git a/config-fast.sh b/config-fast.sh new file mode 100644 index 0000000..d05cda9 --- /dev/null +++ b/config-fast.sh @@ -0,0 +1,30 @@ +fqdn="$1" +echo Your fully qualified host name is "$fqdn". + +echo Putting "$fqdn" into control/me... +echo "$fqdn" > QMAIL/control/me +chmod 644 QMAIL/control/me + +( echo "$fqdn" | sed 's/^\([^\.]*\)\.\([^\.]*\)\./\2\./' | ( + read ddom + echo Putting "$ddom" into control/defaultdomain... + echo "$ddom" > QMAIL/control/defaultdomain + chmod 644 QMAIL/control/defaultdomain +) ) + +( echo "$fqdn" | sed 's/^.*\.\([^\.]*\)\.\([^\.]*\)$/\1.\2/' | ( + read pdom + echo Putting "$pdom" into control/plusdomain... + echo "$pdom" > QMAIL/control/plusdomain + chmod 644 QMAIL/control/plusdomain +) ) + +echo Putting "$fqdn" into control/locals... +echo "$fqdn" >> QMAIL/control/locals +chmod 644 QMAIL/control/locals + +echo Putting "$fqdn" into control/rcpthosts... +echo "$fqdn" >> QMAIL/control/rcpthosts +chmod 644 QMAIL/control/rcpthosts +echo "Now qmail will refuse to accept SMTP messages except to $fqdn." +echo 'Make sure to change rcpthosts if you add hosts to locals or virtualdomains!' diff --git a/config.sh b/config.sh new file mode 100644 index 0000000..8450070 --- /dev/null +++ b/config.sh @@ -0,0 +1,64 @@ +./hostname | tr '[A-Z]' '[a-z]' | ( + if read host + then + echo Your hostname is "$host". + ./dnsfq "$host" | tr '[A-Z]' '[a-z]' | ( + if read fqdn + then + echo Your host\'s fully qualified name in DNS is "$fqdn". + echo Putting "$fqdn" into control/me... + echo "$fqdn" > QMAIL/control/me + chmod 644 QMAIL/control/me + ( echo "$fqdn" | sed 's/^\([^\.]*\)\.\([^\.]*\)\./\2\./' | ( + read ddom + echo Putting "$ddom" into control/defaultdomain... + echo "$ddom" > QMAIL/control/defaultdomain + chmod 644 QMAIL/control/defaultdomain + ) ) + ( echo "$fqdn" | sed 's/^.*\.\([^\.]*\)\.\([^\.]*\)$/\1.\2/' | ( + read pdom + echo Putting "$pdom" into control/plusdomain... + echo "$pdom" > QMAIL/control/plusdomain + chmod 644 QMAIL/control/plusdomain + ) ) + echo ' ' + echo Checking local IP addresses: + : > QMAIL/control/locals + chmod 644 QMAIL/control/locals + ( ./dnsip "$fqdn" + ./ipmeprint ) | sort -u | \ + ( + while read localip + do + echo "$localip: " | tr -d '\012' + ./dnsptr "$localip" 2>/dev/null | ( + if read local + then + echo Adding "$local" to control/locals... + echo "$local" >> QMAIL/control/locals + else + echo PTR lookup failed. I assume this address has no DNS name. + fi + ) + done + ) + echo ' ' + echo If there are any other domain names that point to you, + echo you will have to add them to QMAIL/control/locals. + echo You don\'t have to worry about aliases, i.e., domains with CNAME records. + echo ' ' + echo Copying QMAIL/control/locals to QMAIL/control/rcpthosts... + cp QMAIL/control/locals QMAIL/control/rcpthosts + chmod 644 QMAIL/control/rcpthosts + echo 'Now qmail will refuse to accept SMTP messages except to those hosts.' + echo 'Make sure to change rcpthosts if you add hosts to locals or virtualdomains!' + else + echo Sorry, I couldn\'t find your host\'s canonical name in DNS. + echo You will have to set up control/me yourself. + fi + ) + else + echo Sorry, I couldn\'t find your hostname. + echo You will have to set up control/me yourself. + fi +) diff --git a/constmap.c b/constmap.c new file mode 100644 index 0000000..722e3b8 --- /dev/null +++ b/constmap.c @@ -0,0 +1,114 @@ +#include "constmap.h" +#include "alloc.h" +#include "case.h" + +static constmap_hash hash(s,len) +char *s; +int len; +{ + unsigned char ch; + constmap_hash h; + h = 5381; + while (len > 0) { + ch = *s++ - 'A'; + if (ch <= 'Z' - 'A') ch += 'a' - 'A'; + h = ((h << 5) + h) ^ ch; + --len; + } + return h; +} + +char *constmap(cm,s,len) +struct constmap *cm; +char *s; +int len; +{ + constmap_hash h; + int pos; + h = hash(s,len); + pos = cm->first[h & cm->mask]; + while (pos != -1) { + if (h == cm->hash[pos]) + if (len == cm->inputlen[pos]) + if (!case_diffb(cm->input[pos],len,s)) + return cm->input[pos] + cm->inputlen[pos] + 1; + pos = cm->next[pos]; + } + return 0; +} + +int constmap_init(cm,s,len,flagcolon) +struct constmap *cm; +char *s; +int len; +int flagcolon; +{ + int i; + int j; + int k; + int pos; + constmap_hash h; + + cm->num = 0; + for (j = 0;j < len;++j) if (!s[j]) ++cm->num; + + h = 64; + while (h && (h < cm->num)) h += h; + cm->mask = h - 1; + + cm->first = (int *) alloc(sizeof(int) * h); + if (cm->first) { + cm->input = (char **) alloc(sizeof(char *) * cm->num); + if (cm->input) { + cm->inputlen = (int *) alloc(sizeof(int) * cm->num); + if (cm->inputlen) { + cm->hash = (constmap_hash *) alloc(sizeof(constmap_hash) * cm->num); + if (cm->hash) { + cm->next = (int *) alloc(sizeof(int) * cm->num); + if (cm->next) { + for (h = 0;h <= cm->mask;++h) + cm->first[h] = -1; + pos = 0; + i = 0; + for (j = 0;j < len;++j) + if (!s[j]) { + k = j - i; + if (flagcolon) { + for (k = i;k < j;++k) + if (s[k] == ':') + break; + if (k >= j) { i = j + 1; continue; } + k -= i; + } + cm->input[pos] = s + i; + cm->inputlen[pos] = k; + h = hash(s + i,k); + cm->hash[pos] = h; + h &= cm->mask; + cm->next[pos] = cm->first[h]; + cm->first[h] = pos; + ++pos; + i = j + 1; + } + return 1; + } + alloc_free(cm->hash); + } + alloc_free(cm->inputlen); + } + alloc_free(cm->input); + } + alloc_free(cm->first); + } + return 0; +} + +void constmap_free(cm) +struct constmap *cm; +{ + alloc_free(cm->next); + alloc_free(cm->hash); + alloc_free(cm->inputlen); + alloc_free(cm->input); + alloc_free(cm->first); +} diff --git a/constmap.h b/constmap.h new file mode 100644 index 0000000..3f29179 --- /dev/null +++ b/constmap.h @@ -0,0 +1,20 @@ +#ifndef CONSTMAP_H +#define CONSTMAP_H + +typedef unsigned long constmap_hash; + +struct constmap { + int num; + constmap_hash mask; + constmap_hash *hash; + int *first; + int *next; + char **input; + int *inputlen; +} ; + +extern int constmap_init(); +extern void constmap_free(); +extern char *constmap(); + +#endif diff --git a/control.c b/control.c new file mode 100644 index 0000000..b655352 --- /dev/null +++ b/control.c @@ -0,0 +1,130 @@ +#include "readwrite.h" +#include "open.h" +#include "getln.h" +#include "stralloc.h" +#include "substdio.h" +#include "error.h" +#include "control.h" +#include "alloc.h" +#include "scan.h" + +static char inbuf[64]; +static stralloc line = {0}; +static stralloc me = {0}; +static int meok = 0; + +static void striptrailingwhitespace(sa) +stralloc *sa; +{ + while (sa->len > 0) + switch(sa->s[sa->len - 1]) + { + case '\n': case ' ': case '\t': + --sa->len; + break; + default: + return; + } +} + +int control_init() +{ + int r; + r = control_readline(&me,"control/me"); + if (r == 1) meok = 1; + return r; +} + +int control_rldef(sa,fn,flagme,def) +stralloc *sa; +char *fn; +int flagme; +char *def; +{ + int r; + r = control_readline(sa,fn); + if (r) return r; + if (flagme) if (meok) return stralloc_copy(sa,&me) ? 1 : -1; + if (def) return stralloc_copys(sa,def) ? 1 : -1; + return r; +} + +int control_readline(sa,fn) +stralloc *sa; +char *fn; +{ + substdio ss; + int fd; + int match; + + fd = open_read(fn); + if (fd == -1) { if (errno == error_noent) return 0; return -1; } + + substdio_fdbuf(&ss,read,fd,inbuf,sizeof(inbuf)); + + if (getln(&ss,sa,&match,'\n') == -1) { close(fd); return -1; } + + striptrailingwhitespace(sa); + close(fd); + return 1; +} + +int control_readint(i,fn) +int *i; +char *fn; +{ + unsigned long u; + switch(control_readline(&line,fn)) + { + case 0: return 0; + case -1: return -1; + } + if (!stralloc_0(&line)) return -1; + if (!scan_ulong(line.s,&u)) return 0; + *i = u; + return 1; +} + +int control_readfile(sa,fn,flagme) +stralloc *sa; +char *fn; +int flagme; +{ + substdio ss; + int fd; + int match; + + if (!stralloc_copys(sa,"")) return -1; + + fd = open_read(fn); + if (fd == -1) + { + if (errno == error_noent) + { + if (flagme && meok) + { + if (!stralloc_copy(sa,&me)) return -1; + if (!stralloc_0(sa)) return -1; + return 1; + } + return 0; + } + return -1; + } + + substdio_fdbuf(&ss,read,fd,inbuf,sizeof(inbuf)); + + for (;;) + { + if (getln(&ss,&line,&match,'\n') == -1) break; + if (!match && !line.len) { close(fd); return 1; } + striptrailingwhitespace(&line); + if (!stralloc_0(&line)) break; + if (line.s[0]) + if (line.s[0] != '#') + if (!stralloc_cat(sa,&line)) break; + if (!match) { close(fd); return 1; } + } + close(fd); + return -1; +} diff --git a/control.h b/control.h new file mode 100644 index 0000000..7cba89b --- /dev/null +++ b/control.h @@ -0,0 +1,10 @@ +#ifndef CONTROL_H +#define CONTROL_H + +extern int control_init(); +extern int control_readline(); +extern int control_rldef(); +extern int control_readint(); +extern int control_readfile(); + +#endif diff --git a/date822fmt.c b/date822fmt.c new file mode 100644 index 0000000..7674bd1 --- /dev/null +++ b/date822fmt.c @@ -0,0 +1,29 @@ +#include "datetime.h" +#include "fmt.h" +#include "date822fmt.h" + +static char *montab[12] = { +"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" +}; + +unsigned int date822fmt(s,dt) +char *s; +struct datetime *dt; +{ + unsigned int i; + unsigned int len; + len = 0; + i = fmt_uint(s,dt->mday); len += i; if (s) s += i; + i = fmt_str(s," "); len += i; if (s) s += i; + i = fmt_str(s,montab[dt->mon]); len += i; if (s) s += i; + i = fmt_str(s," "); len += i; if (s) s += i; + i = fmt_uint(s,dt->year + 1900); len += i; if (s) s += i; + i = fmt_str(s," "); len += i; if (s) s += i; + i = fmt_uint0(s,dt->hour,2); len += i; if (s) s += i; + i = fmt_str(s,":"); len += i; if (s) s += i; + i = fmt_uint0(s,dt->min,2); len += i; if (s) s += i; + i = fmt_str(s,":"); len += i; if (s) s += i; + i = fmt_uint0(s,dt->sec,2); len += i; if (s) s += i; + i = fmt_str(s," -0000\n"); len += i; if (s) s += i; + return len; +} diff --git a/date822fmt.h b/date822fmt.h new file mode 100644 index 0000000..1848e5a --- /dev/null +++ b/date822fmt.h @@ -0,0 +1,7 @@ +#ifndef DATE822FMT_H +#define DATE822FMT_H + +extern unsigned int date822fmt(); +#define DATE822FMT 60 + +#endif diff --git a/datemail.sh b/datemail.sh new file mode 100644 index 0000000..fd28f46 --- /dev/null +++ b/datemail.sh @@ -0,0 +1 @@ +exec QMAIL/bin/predate QMAIL/bin/sendmail ${1+"$@"} diff --git a/datetime.3 b/datetime.3 new file mode 100644 index 0000000..33a623f --- /dev/null +++ b/datetime.3 @@ -0,0 +1,73 @@ +.TH datetime 3 +.SH NAME +datetime \- convert between TAI labels and seconds +.SH SYNTAX +.B #include + +void \fBdatetime_tai\fP(&\fIdt\fR,\fIt\fR); + +datetime_sec \fBdatetime_untai\fP(&\fIdt\fR); + +struct datetime \fIdt\fR; +.br +datetime_sec \fIt\fR; +.SH DESCRIPTION +International Atomic Time, TAI, +is the fundamental unit for time measurements. +TAI has one label for every second of real time, +without complications such as leap seconds. + +A +struct datetime +variable, +such as +.IR dt , +stores a TAI label. +.I dt\fB.year +is the year number minus 1900; +.I dt\fB.mon +is the month number, from 0 (January) through 11 (December); +.I dt\fB.mday +is the day of the month, from 1 through 31; +.I dt\fB.hour +is the hour, from 0 through 23; +.I dt\fB.min +is the minute, from 0 through 59; +.I dt\fB.sec +is the second, from 0 through 59; +.I dt\fB.wday +is the day of the week, from 0 (Sunday) through 6 (Saturday); +.I dt\fB.yday +is the day of the year, from 0 through 365. + +The +.B datetime +library supports more convenient TAI manipulation with +the datetime_sec type. +A datetime_sec value, such as +.IR t , +is an integer referring to the +.IR t th +second after the beginning of 1970 TAI. +The first second of 1970 TAI was 0; +the next second was 1; +the last second of 1969 TAI was -1. +The difference between two datetime_sec values is a number +of real-time seconds. + +.B datetime_tai +converts a datetime_sec to a TAI label. + +.B datetime_untai +reads a TAI label +(specifically +.IR dt\fB.year , +.IR dt\fB.mon , +.IR dt\fB.mday , +.IR dt\fB.hour , +.IR dt\fB.min , +and +.IR dt\fB.sec ) +and returns a datetime_sec. +.SH "SEE ALSO" +now(3) diff --git a/datetime.c b/datetime.c new file mode 100644 index 0000000..7b8a803 --- /dev/null +++ b/datetime.c @@ -0,0 +1,55 @@ +/* 19950925 */ +#include "datetime.h" + +void datetime_tai(dt,t) +struct datetime *dt; +datetime_sec t; +{ + int day; + int tod; + int year; + int yday; + int wday; + int mon; + + tod = t % 86400; + day = t / 86400; + if (tod < 0) { tod += 86400; --day; } + + dt->hour = tod / 3600; + tod %= 3600; + dt->min = tod / 60; + dt->sec = tod % 60; + + wday = (day + 4) % 7; if (wday < 0) wday += 7; + dt->wday = wday; + + day -= 11017; + /* day 0 is march 1, 2000 */ + year = 5 + day / 146097; + day = day % 146097; if (day < 0) { day += 146097; --year; } + /* from now on, day is nonnegative */ + year *= 4; + if (day == 146096) { year += 3; day = 36524; } + else { year += day / 36524; day %= 36524; } + year *= 25; + year += day / 1461; + day %= 1461; + year *= 4; + yday = (day < 306); + if (day == 1460) { year += 3; day = 365; } + else { year += day / 365; day %= 365; } + yday += day; + + day *= 10; + mon = (day + 5) / 306; + day = day + 5 - 306 * mon; + day /= 10; + if (mon >= 10) { yday -= 306; ++year; mon -= 10; } + else { yday += 59; mon += 2; } + + dt->yday = yday; + dt->year = year - 1900; + dt->mon = mon; + dt->mday = day + 1; +} diff --git a/datetime.h b/datetime.h new file mode 100644 index 0000000..cde2a9b --- /dev/null +++ b/datetime.h @@ -0,0 +1,20 @@ +#ifndef DATETIME_H +#define DATETIME_H + +struct datetime { + int hour; + int min; + int sec; + int wday; + int mday; + int yday; + int mon; + int year; +} ; + +typedef long datetime_sec; + +extern void datetime_tai(); +extern datetime_sec datetime_untai(); + +#endif diff --git a/datetime_un.c b/datetime_un.c new file mode 100644 index 0000000..b5048f8 --- /dev/null +++ b/datetime_un.c @@ -0,0 +1,35 @@ +#include "datetime.h" + +/* roughly 100x faster than mktime() */ +datetime_sec datetime_untai(dt) +struct datetime *dt; +{ + int year; + int day; + int mon; + + year = dt->year + 1900; + + mon = dt->mon; + if (mon >= 2) { mon -= 2; } + else { mon += 10; --year; } + + day = (dt->mday - 1) * 10 + 5 + 306 * mon; + day /= 10; + + if (day == 365) { year -= 3; day = 1460; } + else { day += 365 * (year % 4); } + year /= 4; + + day += 1461 * (year % 25); + year /= 25; + + if (day == 36524) { year -= 3; day = 146096; } + else { day += 36524 * (year % 4); } + year /= 4; + + day += 146097 * (year - 5); + day += 11017; + + return ((day * 24 + dt->hour) * 60 + dt->min) * 60 + dt->sec; +} diff --git a/direntry.3 b/direntry.3 new file mode 100644 index 0000000..8928fbb --- /dev/null +++ b/direntry.3 @@ -0,0 +1,36 @@ +.TH direntry 3 +.SH NAME +direntry \- read directory entries +.SH SYNTAX +.B #include + +DIR *\fBopendir\fP(\fIfn\fR); + +struct direntry *\fBreaddir\fP(\fIdir\fP); + +void \fBclosedir\fP(\fIdir\fP); + +DIR *\fIdir\fR; +.br +char *\fIfn\fR; +.SH DESCRIPTION +The point of +.B direntry.h +is to provide a uniform interface to BSD's +.B sys/dir.h +and POSIX's +.BR dirent.h . + +The +.B readdir +interface is highly unsatisfactory. +It does not distinguish between I/O errors and end-of-directory. +It uses +.BR malloc . +The return type for +.B closedir +varies: some implementations return the +.B close +return value. +.SH "SEE ALSO" +readdir(3) diff --git a/direntry.h1 b/direntry.h1 new file mode 100644 index 0000000..f737676 --- /dev/null +++ b/direntry.h1 @@ -0,0 +1,8 @@ +#ifndef DIRENTRY_H +#define DIRENTRY_H + +#include +#include +#define direntry struct direct + +#endif diff --git a/direntry.h2 b/direntry.h2 new file mode 100644 index 0000000..0302ebe --- /dev/null +++ b/direntry.h2 @@ -0,0 +1,8 @@ +#ifndef DIRENTRY_H +#define DIRENTRY_H + +#include +#include +#define direntry struct dirent + +#endif diff --git a/dns.c b/dns.c new file mode 100644 index 0000000..e9faad7 --- /dev/null +++ b/dns.c @@ -0,0 +1,398 @@ +#include +#include +#include +#include +#include +#include +#include +extern int res_query(); +extern int res_search(); +#include "ip.h" +#include "ipalloc.h" +#include "fmt.h" +#include "alloc.h" +#include "str.h" +#include "stralloc.h" +#include "dns.h" +#include "case.h" + +static unsigned short getshort(c) unsigned char *c; +{ unsigned short u; u = c[0]; return (u << 8) + c[1]; } + +static union { HEADER hdr; unsigned char buf[PACKETSZ]; } response; +static int responselen; +static unsigned char *responseend; +static unsigned char *responsepos; + +static int numanswers; +static char name[MAXDNAME]; +static struct ip_address ip; +unsigned short pref; + +static stralloc glue = {0}; + +static int (*lookup)() = res_query; + +static int resolve(domain,type) +stralloc *domain; +int type; +{ + int n; + int i; + + errno = 0; + if (!stralloc_copy(&glue,domain)) return DNS_MEM; + if (!stralloc_0(&glue)) return DNS_MEM; + responselen = lookup(glue.s,C_IN,type,response.buf,sizeof(response)); + if (responselen <= 0) + { + if (errno == ECONNREFUSED) return DNS_SOFT; + if (h_errno == TRY_AGAIN) return DNS_SOFT; + return DNS_HARD; + } + if (responselen >= sizeof(response)) + responselen = sizeof(response); + responseend = response.buf + responselen; + responsepos = response.buf + sizeof(HEADER); + n = ntohs(response.hdr.qdcount); + while (n-- > 0) + { + i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME); + if (i < 0) return DNS_SOFT; + responsepos += i; + i = responseend - responsepos; + if (i < QFIXEDSZ) return DNS_SOFT; + responsepos += QFIXEDSZ; + } + numanswers = ntohs(response.hdr.ancount); + return 0; +} + +static int findname(wanttype) +int wanttype; +{ + unsigned short rrtype; + unsigned short rrdlen; + int i; + + if (numanswers <= 0) return 2; + --numanswers; + if (responsepos == responseend) return DNS_SOFT; + + i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME); + if (i < 0) return DNS_SOFT; + responsepos += i; + + i = responseend - responsepos; + if (i < 4 + 3 * 2) return DNS_SOFT; + + rrtype = getshort(responsepos); + rrdlen = getshort(responsepos + 8); + responsepos += 10; + + if (rrtype == wanttype) + { + if (dn_expand(response.buf,responseend,responsepos,name,MAXDNAME) < 0) + return DNS_SOFT; + responsepos += rrdlen; + return 1; + } + + responsepos += rrdlen; + return 0; +} + +static int findip(wanttype) +int wanttype; +{ + unsigned short rrtype; + unsigned short rrdlen; + int i; + + if (numanswers <= 0) return 2; + --numanswers; + if (responsepos == responseend) return DNS_SOFT; + + i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME); + if (i < 0) return DNS_SOFT; + responsepos += i; + + i = responseend - responsepos; + if (i < 4 + 3 * 2) return DNS_SOFT; + + rrtype = getshort(responsepos); + rrdlen = getshort(responsepos + 8); + responsepos += 10; + + if (rrtype == wanttype) + { + if (rrdlen < 4) + return DNS_SOFT; + ip.d[0] = responsepos[0]; + ip.d[1] = responsepos[1]; + ip.d[2] = responsepos[2]; + ip.d[3] = responsepos[3]; + responsepos += rrdlen; + return 1; + } + + responsepos += rrdlen; + return 0; +} + +static int findmx(wanttype) +int wanttype; +{ + unsigned short rrtype; + unsigned short rrdlen; + int i; + + if (numanswers <= 0) return 2; + --numanswers; + if (responsepos == responseend) return DNS_SOFT; + + i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME); + if (i < 0) return DNS_SOFT; + responsepos += i; + + i = responseend - responsepos; + if (i < 4 + 3 * 2) return DNS_SOFT; + + rrtype = getshort(responsepos); + rrdlen = getshort(responsepos + 8); + responsepos += 10; + + if (rrtype == wanttype) + { + if (rrdlen < 3) + return DNS_SOFT; + pref = (responsepos[0] << 8) + responsepos[1]; + if (dn_expand(response.buf,responseend,responsepos + 2,name,MAXDNAME) < 0) + return DNS_SOFT; + responsepos += rrdlen; + return 1; + } + + responsepos += rrdlen; + return 0; +} + +void dns_init(flagsearch) +int flagsearch; +{ + res_init(); + if (flagsearch) lookup = res_search; +} + +int dns_cname(sa) +stralloc *sa; +{ + int r; + int loop; + for (loop = 0;loop < 10;++loop) + { + if (!sa->len) return loop; + if (sa->s[sa->len - 1] == ']') return loop; + if (sa->s[sa->len - 1] == '.') { --sa->len; continue; } + switch(resolve(sa,T_ANY)) + { + case DNS_MEM: return DNS_MEM; + case DNS_SOFT: return DNS_SOFT; + case DNS_HARD: return loop; + default: + while ((r = findname(T_CNAME)) != 2) + { + if (r == DNS_SOFT) return DNS_SOFT; + if (r == 1) + { + if (!stralloc_copys(sa,name)) return DNS_MEM; + break; + } + } + if (r == 2) return loop; + } + } + return DNS_HARD; /* alias loop */ +} + +#define FMT_IAA 40 + +static int iaafmt(s,ip) +char *s; +struct ip_address *ip; +{ + unsigned int i; + unsigned int len; + len = 0; + i = fmt_ulong(s,(unsigned long) ip->d[3]); len += i; if (s) s += i; + i = fmt_str(s,"."); len += i; if (s) s += i; + i = fmt_ulong(s,(unsigned long) ip->d[2]); len += i; if (s) s += i; + i = fmt_str(s,"."); len += i; if (s) s += i; + i = fmt_ulong(s,(unsigned long) ip->d[1]); len += i; if (s) s += i; + i = fmt_str(s,"."); len += i; if (s) s += i; + i = fmt_ulong(s,(unsigned long) ip->d[0]); len += i; if (s) s += i; + i = fmt_str(s,".in-addr.arpa."); len += i; if (s) s += i; + return len; +} + +int dns_ptr(sa,ip) +stralloc *sa; +struct ip_address *ip; +{ + int r; + + if (!stralloc_ready(sa,iaafmt((char *) 0,ip))) return DNS_MEM; + sa->len = iaafmt(sa->s,ip); + switch(resolve(sa,T_PTR)) + { + case DNS_MEM: return DNS_MEM; + case DNS_SOFT: return DNS_SOFT; + case DNS_HARD: return DNS_HARD; + } + while ((r = findname(T_PTR)) != 2) + { + if (r == DNS_SOFT) return DNS_SOFT; + if (r == 1) + { + if (!stralloc_copys(sa,name)) return DNS_MEM; + return 0; + } + } + return DNS_HARD; +} + +static int dns_ipplus(ia,sa,pref) +ipalloc *ia; +stralloc *sa; +int pref; +{ + int r; + struct ip_mx ix; + + if (!stralloc_copy(&glue,sa)) return DNS_MEM; + if (!stralloc_0(&glue)) return DNS_MEM; + if (glue.s[0]) { + ix.pref = 0; + if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)]) + { + if (!ipalloc_append(ia,&ix)) return DNS_MEM; + return 0; + } + } + + switch(resolve(sa,T_A)) + { + case DNS_MEM: return DNS_MEM; + case DNS_SOFT: return DNS_SOFT; + case DNS_HARD: return DNS_HARD; + } + while ((r = findip(T_A)) != 2) + { + ix.ip = ip; + ix.pref = pref; + if (r == DNS_SOFT) return DNS_SOFT; + if (r == 1) + if (!ipalloc_append(ia,&ix)) return DNS_MEM; + } + return 0; +} + +int dns_ip(ia,sa) +ipalloc *ia; +stralloc *sa; +{ + if (!ipalloc_readyplus(ia,0)) return DNS_MEM; + ia->len = 0; + return dns_ipplus(ia,sa,0); +} + +int dns_mxip(ia,sa,random) +ipalloc *ia; +stralloc *sa; +unsigned long random; +{ + int r; + struct mx { stralloc sa; unsigned short p; } *mx; + struct ip_mx ix; + int nummx; + int i; + int j; + int flagsoft; + + if (!ipalloc_readyplus(ia,0)) return DNS_MEM; + ia->len = 0; + + if (!stralloc_copy(&glue,sa)) return DNS_MEM; + if (!stralloc_0(&glue)) return DNS_MEM; + if (glue.s[0]) { + ix.pref = 0; + if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)]) + { + if (!ipalloc_append(ia,&ix)) return DNS_MEM; + return 0; + } + } + + switch(resolve(sa,T_MX)) + { + case DNS_MEM: return DNS_MEM; + case DNS_SOFT: return DNS_SOFT; + case DNS_HARD: return dns_ip(ia,sa); + } + + mx = (struct mx *) alloc(numanswers * sizeof(struct mx)); + if (!mx) return DNS_MEM; + nummx = 0; + + while ((r = findmx(T_MX)) != 2) + { + if (r == DNS_SOFT) { alloc_free(mx); return DNS_SOFT; } + if (r == 1) + { + mx[nummx].p = pref; + mx[nummx].sa.s = 0; + if (!stralloc_copys(&mx[nummx].sa,name)) + { + while (nummx > 0) alloc_free(mx[--nummx].sa.s); + alloc_free(mx); return DNS_MEM; + } + ++nummx; + } + } + + if (!nummx) return dns_ip(ia,sa); /* e.g., CNAME -> A */ + + flagsoft = 0; + while (nummx > 0) + { + unsigned long numsame; + + i = 0; + numsame = 1; + for (j = 1;j < nummx;++j) + if (mx[j].p < mx[i].p) + { + i = j; + numsame = 1; + } + else if (mx[j].p == mx[i].p) + { + ++numsame; + random = random * 69069 + 1; + if ((random / 2) < (2147483647 / numsame)) + i = j; + } + + switch(dns_ipplus(ia,&mx[i].sa,mx[i].p)) + { + case DNS_MEM: case DNS_SOFT: + flagsoft = 1; break; + } + + alloc_free(mx[i].sa.s); + mx[i] = mx[--nummx]; + } + + alloc_free(mx); + return flagsoft; +} diff --git a/dns.h b/dns.h new file mode 100644 index 0000000..bca9490 --- /dev/null +++ b/dns.h @@ -0,0 +1,14 @@ +#ifndef DNS_H +#define DNS_H + +#define DNS_SOFT -1 +#define DNS_HARD -2 +#define DNS_MEM -3 + +void dns_init(); +int dns_cname(); +int dns_mxip(); +int dns_ip(); +int dns_ptr(); + +#endif diff --git a/dnscname.c b/dnscname.c new file mode 100644 index 0000000..b3cd6f1 --- /dev/null +++ b/dnscname.c @@ -0,0 +1,25 @@ +#include "substdio.h" +#include "subfd.h" +#include "stralloc.h" +#include "dns.h" +#include "dnsdoe.h" +#include "readwrite.h" +#include "exit.h" + +stralloc sa = {0}; + +void main(argc,argv) +int argc; +char **argv; +{ + if (!argv[1]) _exit(100); + + if (!stralloc_copys(&sa,argv[1])) + { substdio_putsflush(subfderr,"out of memory\n"); _exit(111); } + + dns_init(0); + dnsdoe(dns_cname(&sa)); + substdio_putflush(subfdout,sa.s,sa.len); + substdio_putsflush(subfdout,"\n"); + _exit(0); +} diff --git a/dnsdoe.c b/dnsdoe.c new file mode 100644 index 0000000..806ca53 --- /dev/null +++ b/dnsdoe.c @@ -0,0 +1,16 @@ +#include "substdio.h" +#include "subfd.h" +#include "exit.h" +#include "dns.h" +#include "dnsdoe.h" + +void dnsdoe(r) +int r; +{ + switch (r) + { + case DNS_HARD: substdio_putsflush(subfderr,"hard error\n"); _exit(100); + case DNS_SOFT: substdio_putsflush(subfderr,"soft error\n"); _exit(111); + case DNS_MEM: substdio_putsflush(subfderr,"out of memory\n"); _exit(111); + } +} diff --git a/dnsdoe.h b/dnsdoe.h new file mode 100644 index 0000000..0e47bf6 --- /dev/null +++ b/dnsdoe.h @@ -0,0 +1,6 @@ +#ifndef DNSDOE_H +#define DNSDOE_H + +extern void dnsdoe(); + +#endif diff --git a/dnsfq.c b/dnsfq.c new file mode 100644 index 0000000..b7619b9 --- /dev/null +++ b/dnsfq.c @@ -0,0 +1,32 @@ +#include "substdio.h" +#include "subfd.h" +#include "stralloc.h" +#include "dns.h" +#include "dnsdoe.h" +#include "ip.h" +#include "ipalloc.h" +#include "exit.h" + +stralloc sa = {0}; +ipalloc ia = {0}; + +void main(argc,argv) +int argc; +char **argv; +{ + if (!argv[1]) _exit(100); + + if (!stralloc_copys(&sa,argv[1])) + { substdio_putsflush(subfderr,"out of memory\n"); _exit(111); } + + dns_init(1); + dnsdoe(dns_ip(&ia,&sa)); + if (ia.len <= 0) + { + substdio_putsflush(subfderr,"no IP addresses\n"); _exit(100); + } + dnsdoe(dns_ptr(&sa,&ia.ix[0].ip)); + substdio_putflush(subfdout,sa.s,sa.len); + substdio_putsflush(subfdout,"\n"); + _exit(0); +} diff --git a/dnsip.c b/dnsip.c new file mode 100644 index 0000000..e7b671c --- /dev/null +++ b/dnsip.c @@ -0,0 +1,34 @@ +#include "substdio.h" +#include "subfd.h" +#include "stralloc.h" +#include "dns.h" +#include "dnsdoe.h" +#include "ip.h" +#include "ipalloc.h" +#include "exit.h" + +char temp[IPFMT]; + +stralloc sa = {0}; +ipalloc ia = {0}; + +void main(argc,argv) +int argc; +char **argv; +{ + int j; + + if (!argv[1]) _exit(100); + + if (!stralloc_copys(&sa,argv[1])) + { substdio_putsflush(subfderr,"out of memory\n"); _exit(111); } + + dns_init(0); + dnsdoe(dns_ip(&ia,&sa)); + for (j = 0;j < ia.len;++j) + { + substdio_put(subfdout,temp,ip_fmt(temp,&ia.ix[j].ip)); + substdio_putsflush(subfdout,"\n"); + } + _exit(0); +} diff --git a/dnsmxip.c b/dnsmxip.c new file mode 100644 index 0000000..6d8e137 --- /dev/null +++ b/dnsmxip.c @@ -0,0 +1,40 @@ +#include "substdio.h" +#include "subfd.h" +#include "stralloc.h" +#include "fmt.h" +#include "dns.h" +#include "dnsdoe.h" +#include "ip.h" +#include "ipalloc.h" +#include "now.h" +#include "exit.h" + +char temp[IPFMT + FMT_ULONG]; + +stralloc sa = {0}; +ipalloc ia = {0}; + +void main(argc,argv) +int argc; +char **argv; +{ + int j; + unsigned long r; + + if (!argv[1]) _exit(100); + + if (!stralloc_copys(&sa,argv[1])) + { substdio_putsflush(subfderr,"out of memory\n"); _exit(111); } + + r = now() + getpid(); + dns_init(0); + dnsdoe(dns_mxip(&ia,&sa,r)); + for (j = 0;j < ia.len;++j) + { + substdio_put(subfdout,temp,ip_fmt(temp,&ia.ix[j].ip)); + substdio_puts(subfdout," "); + substdio_put(subfdout,temp,fmt_ulong(temp,(unsigned long) ia.ix[j].pref)); + substdio_putsflush(subfdout,"\n"); + } + _exit(0); +} diff --git a/dnsptr.c b/dnsptr.c new file mode 100644 index 0000000..6a92fe0 --- /dev/null +++ b/dnsptr.c @@ -0,0 +1,27 @@ +#include "substdio.h" +#include "subfd.h" +#include "stralloc.h" +#include "str.h" +#include "scan.h" +#include "dns.h" +#include "dnsdoe.h" +#include "ip.h" +#include "exit.h" + +stralloc sa = {0}; +struct ip_address ip; + +void main(argc,argv) +int argc; +char **argv; +{ + if (!argv[1]) _exit(100); + + ip_scan(argv[1],&ip); + + dns_init(0); + dnsdoe(dns_ptr(&sa,&ip)); + substdio_putflush(subfdout,sa.s,sa.len); + substdio_putsflush(subfdout,"\n"); + _exit(0); +} diff --git a/dot-qmail.9 b/dot-qmail.9 new file mode 100644 index 0000000..52d4626 --- /dev/null +++ b/dot-qmail.9 @@ -0,0 +1,394 @@ +.TH dot-qmail 5 +.SH NAME +dot-qmail \- control the delivery of mail messages +.SH DESCRIPTION +Normally the +.B qmail-local +program delivers each incoming message to your system mailbox, +.IR homedir\fB/Mailbox , +where +.I homedir +is your home directory. + +It can instead +write the mail to a different file or directory, +forward it to another address, +distribute it to a mailing list, +or even execute programs, +all under your control. +.SH "THE QMAIL FILE" +To change +.BR qmail-local 's +behavior, set up a +.B .qmail +file in your home directory. + +.B .qmail +contains one or more lines. +Each line is a delivery instruction. +.B qmail-local +follows each instruction in turn. +There are five types of delivery instructions: +(1) comment; (2) program; (3) forward; (4) mbox; (5) maildir. +.TP 5 +(1) +A comment line begins with a number sign: + +.EX + # this is a comment +.EE + +.B qmail-local +ignores the line. +.TP 5 +(2) +A program line begins with a vertical bar: + +.EX + |preline /usr/ucb/vacation djb +.EE + +.B qmail-local +takes the rest of the line as a command to supply to +.BR sh . +See +.B qmail-command(8) +for further information. +.TP 5 +(3) +A forward line begins with an ampersand: + +.EX + &me@new.job.com +.EE + +.B qmail-local +takes the rest of the line as a mail address; +it uses +.B qmail-queue +to forward the message to that address. +The address must contain a fully qualified domain name; +it must not contain extra spaces, angle brackets, or comments: + +.EX + # the following examples are WRONG +.br + &me@new +.br + & +.br + & me@new.job.com +.br + &me@new.job.com (New Address) +.EE + +If the address begins with a letter or number, +you may leave out the ampersand: + +.EX + me@new.job.com +.EE + +Note that +.B qmail-local +omits its new +.B Return-Path +line when forwarding messages. +.TP 5 +(4) +An +.I mbox +line begins with a slash or dot, +and does not end with a slash: + +.EX + /home/djb/Mailbox.sos +.EE + +.B qmail-local +takes the entire line as a filename. +It appends the mail message to that file, +using +.BR flock -style +file locking if possible. +.B qmail-local +stores the mail message in +.I mbox +format, as described in +.BR mbox(5) . + +.B WARNING: +On many systems, +anyone who can read a file can +.B flock +it, and thus hold up +.BR qmail-local 's +delivery forever. +Do not deliver mail to a publicly accessible file! + +If +.B qmail-local +is able to lock the file, but has trouble writing to it +(because, for example, the disk is full), +it will truncate the file back to its original length. +However, it cannot prevent mailbox corruption if the system +crashes during delivery. +.TP 5 +(5) +A +.I maildir +line begins with a slash or dot, +and ends with a slash: + +.EX + /home/djb/Maildir/ +.EE + +.B qmail-local +takes the entire line as the name of a directory in +.I maildir +format. +It reliably stores the incoming message in that directory. +See +.B maildir(5) +for more details. +.PP +If +.B .qmail +has the execute bit set, +it must not contain any +program lines, +.I mbox +lines, +or +.I maildir +lines. +If +.B qmail-local +sees any such lines, +it will stop and indicate a temporary failure. + +If +.B .qmail +is completely empty (0 bytes long), or does not exist, +.B qmail-local +follows the +.I defaultdelivery +instructions set by your system administrator; +normally +.I defaultdelivery +is +.BR ./Mailbox , +so +.B qmail-local +appends the mail message to +.B Mailbox +in +.I mbox +format. + +.B .qmail +may contain extra spaces and tabs at the end of a line. +Blank lines are allowed, but not for the first line of +.BR .qmail . + +If +.B .qmail +is world-writable, +.B qmail-local +stops and indicates a temporary failure. +.SH "SAFE QMAIL EDITING" +Incoming messages can arrive at any moment. +If you want to safely edit your +.B .qmail +file, first set the sticky bit on your home directory: + +.EX + chmod +t $HOME +.EE + +.B qmail-local +will temporarily defer delivery of any message to you +if your home directory is sticky +(or group-writable or other-writable, +which should never happen). +Make sure to + +.EX + chmod -t $HOME +.EE + +when you are done! +It's a good idea to test your new +.B .qmail +file as follows: + +.EX + qmail-local -n $USER ~ $USER '' '' '' '' ./Mailbox +.EE +.SH "EXTENSION ADDRESSES" +In the +.B qmail +system, +you control all local addresses of the form +.IR user\fBBREAK\fIanything , +as well as the address +.I user +itself, +where +.I user +is your account name. +Delivery to +.I user\fBBREAK\fIanything +is controlled by the file +.IR homedir/\fB.qmail\-\fIanything . +(These rules may be changed by the system administrator; +see +.BR qmail-users (5).) + +The +.B alias +user controls all other addresses. +Delivery to +.I local +is controlled by the file +.IR homedir/\fB.qmail\-\fIlocal , +where +.I homedir +is +.BR alias 's +home directory. + +In the following description, +.B qmail-local +is handling a message addressed to +.IR local@domain , +where +.I local +is controlled by +.BR .qmail\-\fIext . +Here is what it does. + +If +.B .qmail\-\fIext +is completely empty, +.B qmail-local +follows the +.I defaultdelivery +instructions set by your system administrator. + +If +.B .qmail\-\fIext +doesn't exist, +.B qmail-local +will try some default +.B .qmail +files. +For example, +if +.I ext +is +.BR foo-bar , +.B qmail-local +will try first +.BR .qmail-foo-bar , +then +.BR .qmail-foo-default , +and finally +.BR .qmail-default . +If none of these exist, +.B qmail-local +will bounce the message. +(Exception: for the basic +.I user +address, +.B qmail-local +treats a nonexistent +.B .qmail +the same as an empty +.BR .qmail .) + +.B WARNING: +For security, +.B qmail-local +replaces any dots in +.I ext +with colons before checking +.BR .qmail\-\fIext . +For convenience, +.B qmail-local +converts any uppercase letters in +.I ext +to lowercase. + +When +.B qmail-local +forwards a message as instructed in +.B .qmail\-\fIext +(or +.BR .qmail-default ), +it checks whether +.B .qmail\-\fIext\fB-owner\fP +exists. +If so, +it uses +.I local\fB-owner@\fIdomain +as the envelope sender for the forwarded message. +Otherwise it retains the envelope sender of the original message. +Exception: +.B qmail-local +always retains the original envelope sender +if it is the empty address or +.BR #@[] , +i.e., if this is a bounce message. + +.B qmail-local +also supports +.B variable envelope return paths +(VERPs): +if +.B .qmail\-\fIext\fB-owner\fP +and +.B .qmail\-\fIext\fB-owner-default\fP +both exist, it uses +.I local\fB\-owner\-@\fIdomain\fB-@[] +as the envelope sender. +This will cause a recipient +.I recip\fB@\fIreciphost +to see an envelope sender of +.IR local\fB\-owner\-\fIrecip\fB=\fIreciphost\fB@\fIdomain . +.SH "ERROR HANDLING" +If a delivery instruction fails, +.B qmail-local +stops immediately and reports failure. +.B qmail-local +handles forwarding after all other instructions, +so any error in another type of delivery will prevent all forwarding. + +If a program returns exit code 99, +.B qmail-local +ignores all succeeding lines in +.BR .qmail , +but it still pays attention to previous forward lines. + +To set up independent instructions, +where a temporary or permanent failure in one instruction +does not affect the others, +move each instruction into a separate +.B .qmail\-\fIext +file, and set up a central +.B .qmail +file that forwards to all of the +.BR .qmail\-\fIext s. +Note that +.B qmail-local +can handle any number of forward lines simultaneously. +.SH "SEE ALSO" +envelopes(5), +maildir(5), +mbox(5), +qmail-users(5), +qmail-local(8), +qmail-command(8), +qmail-queue(8), +qmail-lspawn(8) diff --git a/elq.sh b/elq.sh new file mode 100644 index 0000000..7e20262 --- /dev/null +++ b/elq.sh @@ -0,0 +1 @@ +QMAIL/bin/maildir2mbox && exec elm ${1+"$@"} diff --git a/env.3 b/env.3 new file mode 100644 index 0000000..53a5f89 --- /dev/null +++ b/env.3 @@ -0,0 +1,31 @@ +.TH env 3 +.SH NAME +env \- manage the environment +.SH SYNTAX +.B #include + +char **\fBenviron\fP; + +char *\fBenv_get\fP(\fIname\fR); +.br +char *\fBenv_pick\fP(); + +char *\fIname\fR; +.SH DESCRIPTION +The environment, +.BR environ , +is a 0-terminated array of 0-terminated strings, +called environment variables. +Each environment variable is of the form +.IR name\fB=\fIvalue . + +.B env_get +returns the value of the first variable whose name is +.IR name , +or 0 if there is no such variable. + +.B env_pick +returns any variable in the environment, +or 0 if the environment is empty. +.SH "SEE ALSO" +environ(7) diff --git a/env.c b/env.c new file mode 100644 index 0000000..05d527b --- /dev/null +++ b/env.c @@ -0,0 +1,113 @@ +/* env.c, envread.c, env.h: environ library +Daniel J. Bernstein, djb@silverton.berkeley.edu. +Depends on str.h, alloc.h. +Requires environ. +19960113: rewrite. warning: interface is different. +No known patent problems. +*/ + +#include "str.h" +#include "alloc.h" +#include "env.h" + +int env_isinit = 0; /* if env_isinit: */ +static int ea; /* environ is a pointer to ea+1 char*'s. */ +static int en; /* the first en of those are ALLOCATED. environ[en] is 0. */ + +static void env_goodbye(i) int i; +{ + alloc_free(environ[i]); + environ[i] = environ[--en]; + environ[en] = 0; +} + +static char *null = 0; + +void env_clear() +{ + if (env_isinit) while (en) env_goodbye(0); + else environ = &null; +} + +static void env_unsetlen(s,len) char *s; int len; +{ + int i; + for (i = en - 1;i >= 0;--i) + if (!str_diffn(s,environ[i],len)) + if (environ[i][len] == '=') + env_goodbye(i); +} + +int env_unset(s) char *s; +{ + if (!env_isinit) if (!env_init()) return 0; + env_unsetlen(s,str_len(s)); + return 1; +} + +static int env_add(s) char *s; +{ + char *t; + t = env_findeq(s); + if (t) env_unsetlen(s,t - s); + if (en == ea) + { + ea += 30; + if (!alloc_re(&environ,(en + 1) * sizeof(char *),(ea + 1) * sizeof(char *))) + { ea = en; return 0; } + } + environ[en++] = s; + environ[en] = 0; + return 1; +} + +int env_put(s) char *s; +{ + char *u; + if (!env_isinit) if (!env_init()) return 0; + u = alloc(str_len(s) + 1); + if (!u) return 0; + str_copy(u,s); + if (!env_add(u)) { alloc_free(u); return 0; } + return 1; +} + +int env_put2(s,t) char *s; char *t; +{ + char *u; + int slen; + if (!env_isinit) if (!env_init()) return 0; + slen = str_len(s); + u = alloc(slen + str_len(t) + 2); + if (!u) return 0; + str_copy(u,s); + u[slen] = '='; + str_copy(u + slen + 1,t); + if (!env_add(u)) { alloc_free(u); return 0; } + return 1; +} + +int env_init() +{ + char **newenviron; + int i; + for (en = 0;environ[en];++en) ; + ea = en + 10; + newenviron = (char **) alloc((ea + 1) * sizeof(char *)); + if (!newenviron) return 0; + for (en = 0;environ[en];++en) + { + newenviron[en] = alloc(str_len(environ[en]) + 1); + if (!newenviron[en]) + { + for (i = 0;i < en;++i) alloc_free(newenviron[i]); + alloc_free(newenviron); + return 0; + } + str_copy(newenviron[en],environ[en]); + } + newenviron[en] = 0; + environ = newenviron; + env_isinit = 1; + return 1; +} diff --git a/env.h b/env.h new file mode 100644 index 0000000..9befc79 --- /dev/null +++ b/env.h @@ -0,0 +1,17 @@ +#ifndef ENV_H +#define ENV_H + +extern int env_isinit; + +extern int env_init(); +extern int env_put(); +extern int env_put2(); +extern int env_unset(); +extern /*@null@*/char *env_get(); +extern char *env_pick(); +extern void env_clear(); +extern char *env_findeq(); + +extern char **environ; + +#endif diff --git a/envelopes.5 b/envelopes.5 new file mode 100644 index 0000000..5f7084a --- /dev/null +++ b/envelopes.5 @@ -0,0 +1,231 @@ +.TH envelopes 5 +.SH "NAME" +envelopes \- sender/recipient lists attached to messages +.SH "INTRODUCTION" +Electronic mail messages are delivered in +.IR envelopes . + +An envelope lists a +.I sender +and one or more +.IR recipients . +Usually these +envelope addresses are the same +as the addresses listed in the message header: + +.EX + (envelope) from djb to root +.br + From: djb +.br + To: root +.EE + +In more complicated situations, though, +the envelope addresses may differ from the header addresses. +.SH "ENVELOPE EXAMPLES" +When a message is delivered to +several people at different locations, +it is first photocopied +and placed into several envelopes: + +.EX + (envelope) from djb to root +.br + From: djb Copy #1 of message +.br + To: root, god@brl.mil +.EE + +.EX + (envelope) from djb to god@brl.mil +.br + From: djb Copy #2 of message +.br + To: root, god@brl.mil +.EE + +When a message is delivered +to several people at the same location, +the sender doesn't have to photocopy it. +He can instead stuff it into +one envelope with several addresses; +the recipients will make the photocopy: + +.EX + (envelope) from djb to god@brl.mil, angel@brl.mil +.br + From: djb +.br + To: god@brl.mil, angel@brl.mil, joe, frde +.EE + +Bounced mail is sent back to the envelope sender address. +The bounced mail doesn't list an envelope sender, +so bounce loops are impossible: + +.EX + (envelope) from <> to djb +.br + From: MAILER-DAEMON +.br + To: djb +.br + Subject: unknown user frde +.EE + +The recipient of a message may make another copy +and forward it in a new envelope: + +.EX + (envelope) from djb to joe +.br + From: djb Original message +.br + To: joe +.EE + +.EX + (envelope) from joe to fred +.br + From: djb Forwarded message +.br + To: joe +.EE + +A mailing list works almost the same way: + +.EX + (envelope) from djb to sos-list +.br + From: djb Original message +.br + To: sos-list +.EE + +.EX + (envelope) from sos-owner to god@brl.mil +.br + From: djb Forwarded message +.br + To: sos-list to recipient #1 +.EE + +.EX + (envelope) from sos-owner to frde +.br + From: djb Forwarded message +.br + To: sos-list to recipient #2 +.EE + +Notice that the mailing list is set up +to replace the envelope sender with something new, +.BR sos-owner . +So bounces will come back to +.BR sos-owner : + +.EX + (envelope) from <> to sos-owner +.br + From: MAILER-DAEMON +.br + To: sos-owner +.br + Subject: unknown user frde +.EE + +It's a good idea to set up an extra address, +.BR sos-owner , +like this: +the original envelope sender (\fBdjb\fP) +has no way to fix bad +.B sos-list +addresses, +and of course bounces must not be sent to +.B sos-list +itself. +.SH "HOW ENVELOPE ADDRESSES ARE STORED" +Envelope sender and envelope recipient addresses +are transmitted and recorded in several ways. + +When a user injects mail through +.BR qmail-inject , +he can supply a +.B Return-Path +line or a +.B \-f +option for the envelope sender; +by default the envelope sender is his login name. +The envelope recipient addresses can be taken +from the command line or from various header fields, +depending on the options to +.BR qmail-inject . +Similar comments apply to +.BR sendmail . + +When a message is transferred from one machine to another through SMTP, +the envelope sender is given in a +.B MAIL FROM +command, +the envelope recipients are given in +.B RCPT TO +commands, +and the message is supplied separately by a +.B DATA +command. + +When a message is delivered by +.B qmail +to a single local recipient, +.B qmail-local +records the recipient in +.B Delivered-To +and the envelope sender in +.BR Return-Path . +It uses +.B Delivered-To +to detect mail forwarding loops. + +.B sendmail +normally records the envelope sender in +.BR Return-Path . +It does not record envelope recipient addresses, +on the theory that they are redundant: +you received the mail, +so you must have been one of the envelope recipients. + +Note that, +if the header doesn't have any recipient addresses, +.B sendmail +will move envelope recipient addresses back into the header. +This situation occurs if all addresses were originally listed as +.BR Bcc , +since +.B Bcc +is automatically removed. +When +.B sendmail +sees this, it creates a new +.B Apparently-To +header field with the envelope recipient addresses. +This has the strange effect that each blind-carbon-copy recipient will see +a list of all recipients on the same machine. + +When a message is stored in +.B mbox +format, +the envelope sender is recorded at the top of the message +as a UUCP-style +.B From +(no colon) line. +Note that this line is less reliable than the +.B Return-Path +line added by +.B qmail-local +or +.B sendmail\fP. +.SH "SEE ALSO" +qmail-header(5), +qmail-local(8), +qmail-inject(8) diff --git a/envread.c b/envread.c new file mode 100644 index 0000000..80185de --- /dev/null +++ b/envread.c @@ -0,0 +1,30 @@ +#include "env.h" +#include "str.h" + +extern /*@null@*/char *env_get(s) +char *s; +{ + int i; + unsigned int slen; + char *envi; + + slen = str_len(s); + for (i = 0;envi = environ[i];++i) + if ((!str_diffn(s,envi,slen)) && (envi[slen] == '=')) + return envi + slen + 1; + return 0; +} + +extern char *env_pick() +{ + return environ[0]; +} + +extern char *env_findeq(s) +char *s; +{ + for (;*s;++s) + if (*s == '=') + return s; + return 0; +} diff --git a/error.3 b/error.3 new file mode 100644 index 0000000..308e43c --- /dev/null +++ b/error.3 @@ -0,0 +1,45 @@ +.TH error 3 +.SH NAME +error \- syscall error codes +.SH SYNTAX +.B #include +.br +.B #include + +extern int \fBerror_intr\fP; +.br +extern int \fBerror_nomem\fP; +.br +extern int \fBerror_noent\fP; +.br +extern int \fBerror_txtbsy\fP; +.br +extern int \fBerror_io\fP; +.br +extern int \fBerror_exist\fP; +.br +extern int \fBerror_timeout\fP; +.br +extern int \fBerror_inprogress\fP; +.br +extern int \fBerror_wouldblock\fP; +.br +extern int \fBerror_again\fP; +.br +extern int \fBerror_pipe\fP; +.br +extern int \fBerror_perm\fP; +.br +extern int \fBerror_acces\fP; +.SH DESCRIPTION +UNIX syscalls provide detailed error codes in the +.B errno +variable. +The +.B error +library provides portable names for a variety of possible +.B errno +values. +.SH "SEE ALSO" +error_str(3), +error_temp(3) diff --git a/error.c b/error.c new file mode 100644 index 0000000..d51304f --- /dev/null +++ b/error.c @@ -0,0 +1,95 @@ +#include +#include "error.h" + +/* warning: as coverage improves here, should update error_{str,temp} */ + +int error_intr = +#ifdef EINTR +EINTR; +#else +-1; +#endif + +int error_nomem = +#ifdef ENOMEM +ENOMEM; +#else +-2; +#endif + +int error_noent = +#ifdef ENOENT +ENOENT; +#else +-3; +#endif + +int error_txtbsy = +#ifdef ETXTBSY +ETXTBSY; +#else +-4; +#endif + +int error_io = +#ifdef EIO +EIO; +#else +-5; +#endif + +int error_exist = +#ifdef EEXIST +EEXIST; +#else +-6; +#endif + +int error_timeout = +#ifdef ETIMEDOUT +ETIMEDOUT; +#else +-7; +#endif + +int error_inprogress = +#ifdef EINPROGRESS +EINPROGRESS; +#else +-8; +#endif + +int error_wouldblock = +#ifdef EWOULDBLOCK +EWOULDBLOCK; +#else +-9; +#endif + +int error_again = +#ifdef EAGAIN +EAGAIN; +#else +-10; +#endif + +int error_pipe = +#ifdef EPIPE +EPIPE; +#else +-11; +#endif + +int error_perm = +#ifdef EPERM +EPERM; +#else +-12; +#endif + +int error_acces = +#ifdef EACCES +EACCES; +#else +-13; +#endif diff --git a/error.h b/error.h new file mode 100644 index 0000000..5d98c6b --- /dev/null +++ b/error.h @@ -0,0 +1,23 @@ +#ifndef ERROR_H +#define ERROR_H + +#include + +extern int error_intr; +extern int error_nomem; +extern int error_noent; +extern int error_txtbsy; +extern int error_io; +extern int error_exist; +extern int error_timeout; +extern int error_inprogress; +extern int error_wouldblock; +extern int error_again; +extern int error_pipe; +extern int error_perm; +extern int error_acces; + +extern char *error_str(); +extern int error_temp(); + +#endif diff --git a/error_str.3 b/error_str.3 new file mode 100644 index 0000000..62043c4 --- /dev/null +++ b/error_str.3 @@ -0,0 +1,19 @@ +.TH error_str 3 +.SH NAME +error_str \- names for syscall error codes +.SH SYNTAX +.B #include + +char *\fBerror_str\fP(\fIe\fR); + +int \fIe\fR; +.SH DESCRIPTION +.B error_str +returns a printable string describing syscall error code +.IR e . +Normally +.I e +is +.BR errno . +.SH "SEE ALSO" +error(3) diff --git a/error_str.c b/error_str.c new file mode 100644 index 0000000..804d1fa --- /dev/null +++ b/error_str.c @@ -0,0 +1,276 @@ +#include +#include "error.h" + +#define X(e,s) if (i == e) return s; + +char *error_str(i) +int i; +{ + X(0,"no error") + X(error_intr,"interrupted system call") + X(error_nomem,"out of memory") + X(error_noent,"file does not exist") + X(error_txtbsy,"text busy") + X(error_io,"input/output error") + X(error_exist,"file already exists") + X(error_timeout,"timed out") + X(error_inprogress,"operation in progress") + X(error_again,"temporary failure") + X(error_wouldblock,"input/output would block") + X(error_pipe,"broken pipe") + X(error_perm,"permission denied") + X(error_acces,"access denied") +#ifdef ESRCH + X(ESRCH,"no such process") +#endif +#ifdef ENXIO + X(ENXIO,"device not configured") +#endif +#ifdef E2BIG + X(E2BIG,"argument list too long") +#endif +#ifdef ENOEXEC + X(ENOEXEC,"exec format error") +#endif +#ifdef EBADF + X(EBADF,"file descriptor not open") +#endif +#ifdef ECHILD + X(ECHILD,"no child processes") +#endif +#ifdef EDEADLK + X(EDEADLK,"operation would cause deadlock") +#endif +#ifdef EFAULT + X(EFAULT,"bad address") +#endif +#ifdef ENOTBLK + X(ENOTBLK,"not a block device") +#endif +#ifdef EBUSY + X(EBUSY,"device busy") +#endif +#ifdef EXDEV + X(EXDEV,"cross-device link") +#endif +#ifdef ENODEV + X(ENODEV,"device does not support operation") +#endif +#ifdef ENOTDIR + X(ENOTDIR,"not a directory") +#endif +#ifdef EISDIR + X(EISDIR,"is a directory") +#endif +#ifdef EINVAL + X(EINVAL,"invalid argument") +#endif +#ifdef ENFILE + X(ENFILE,"system cannot open more files") +#endif +#ifdef EMFILE + X(EMFILE,"process cannot open more files") +#endif +#ifdef ENOTTY + X(ENOTTY,"not a tty") +#endif +#ifdef EFBIG + X(EFBIG,"file too big") +#endif +#ifdef ENOSPC + X(ENOSPC,"out of disk space") +#endif +#ifdef ESPIPE + X(ESPIPE,"unseekable descriptor") +#endif +#ifdef EROFS + X(EROFS,"read-only file system") +#endif +#ifdef EMLINK + X(EMLINK,"too many links") +#endif +#ifdef EDOM + X(EDOM,"input out of range") +#endif +#ifdef ERANGE + X(ERANGE,"output out of range") +#endif +#ifdef EALREADY + X(EALREADY,"operation already in progress") +#endif +#ifdef ENOTSOCK + X(ENOTSOCK,"not a socket") +#endif +#ifdef EDESTADDRREQ + X(EDESTADDRREQ,"destination address required") +#endif +#ifdef EMSGSIZE + X(EMSGSIZE,"message too long") +#endif +#ifdef EPROTOTYPE + X(EPROTOTYPE,"incorrect protocol type") +#endif +#ifdef ENOPROTOOPT + X(ENOPROTOOPT,"protocol not available") +#endif +#ifdef EPROTONOSUPPORT + X(EPROTONOSUPPORT,"protocol not supported") +#endif +#ifdef ESOCKTNOSUPPORT + X(ESOCKTNOSUPPORT,"socket type not supported") +#endif +#ifdef EOPNOTSUPP + X(EOPNOTSUPP,"operation not supported") +#endif +#ifdef EPFNOSUPPORT + X(EPFNOSUPPORT,"protocol family not supported") +#endif +#ifdef EAFNOSUPPORT + X(EAFNOSUPPORT,"address family not supported") +#endif +#ifdef EADDRINUSE + X(EADDRINUSE,"address already used") +#endif +#ifdef EADDRNOTAVAIL + X(EADDRNOTAVAIL,"address not available") +#endif +#ifdef ENETDOWN + X(ENETDOWN,"network down") +#endif +#ifdef ENETUNREACH + X(ENETUNREACH,"network unreachable") +#endif +#ifdef ENETRESET + X(ENETRESET,"network reset") +#endif +#ifdef ECONNABORTED + X(ECONNABORTED,"connection aborted") +#endif +#ifdef ECONNRESET + X(ECONNRESET,"connection reset") +#endif +#ifdef ENOBUFS + X(ENOBUFS,"out of buffer space") +#endif +#ifdef EISCONN + X(EISCONN,"already connected") +#endif +#ifdef ENOTCONN + X(ENOTCONN,"not connected") +#endif +#ifdef ESHUTDOWN + X(ESHUTDOWN,"socket shut down") +#endif +#ifdef ETOOMANYREFS + X(ETOOMANYREFS,"too many references") +#endif +#ifdef ECONNREFUSED + X(ECONNREFUSED,"connection refused") +#endif +#ifdef ELOOP + X(ELOOP,"symbolic link loop") +#endif +#ifdef ENAMETOOLONG + X(ENAMETOOLONG,"file name too long") +#endif +#ifdef EHOSTDOWN + X(EHOSTDOWN,"host down") +#endif +#ifdef EHOSTUNREACH + X(EHOSTUNREACH,"host unreachable") +#endif +#ifdef ENOTEMPTY + X(ENOTEMPTY,"directory not empty") +#endif +#ifdef EPROCLIM + X(EPROCLIM,"too many processes") +#endif +#ifdef EUSERS + X(EUSERS,"too many users") +#endif +#ifdef EDQUOT + X(EDQUOT,"disk quota exceeded") +#endif +#ifdef ESTALE + X(ESTALE,"stale NFS file handle") +#endif +#ifdef EREMOTE + X(EREMOTE,"too many levels of remote in path") +#endif +#ifdef EBADRPC + X(EBADRPC,"RPC structure is bad") +#endif +#ifdef ERPCMISMATCH + X(ERPCMISMATCH,"RPC version mismatch") +#endif +#ifdef EPROGUNAVAIL + X(EPROGUNAVAIL,"RPC program unavailable") +#endif +#ifdef EPROGMISMATCH + X(EPROGMISMATCH,"program version mismatch") +#endif +#ifdef EPROCUNAVAIL + X(EPROCUNAVAIL,"bad procedure for program") +#endif +#ifdef ENOLCK + X(ENOLCK,"no locks available") +#endif +#ifdef ENOSYS + X(ENOSYS,"system call not available") +#endif +#ifdef EFTYPE + X(EFTYPE,"bad file type") +#endif +#ifdef EAUTH + X(EAUTH,"authentication error") +#endif +#ifdef ENEEDAUTH + X(ENEEDAUTH,"not authenticated") +#endif +#ifdef ENOSTR + X(ENOSTR,"not a stream device") +#endif +#ifdef ETIME + X(ETIME,"timer expired") +#endif +#ifdef ENOSR + X(ENOSR,"out of stream resources") +#endif +#ifdef ENOMSG + X(ENOMSG,"no message of desired type") +#endif +#ifdef EBADMSG + X(EBADMSG,"bad message type") +#endif +#ifdef EIDRM + X(EIDRM,"identifier removed") +#endif +#ifdef ENONET + X(ENONET,"machine not on network") +#endif +#ifdef ERREMOTE + X(ERREMOTE,"object not local") +#endif +#ifdef ENOLINK + X(ENOLINK,"link severed") +#endif +#ifdef EADV + X(EADV,"advertise error") +#endif +#ifdef ESRMNT + X(ESRMNT,"srmount error") +#endif +#ifdef ECOMM + X(ECOMM,"communication error") +#endif +#ifdef EPROTO + X(EPROTO,"protocol error") +#endif +#ifdef EMULTIHOP + X(EMULTIHOP,"multihop attempted") +#endif +#ifdef EREMCHG + X(EREMCHG,"remote address changed") +#endif + return "unknown error"; +} diff --git a/error_temp.3 b/error_temp.3 new file mode 100644 index 0000000..2f8229d --- /dev/null +++ b/error_temp.3 @@ -0,0 +1,27 @@ +.TH error_temp 3 +.SH NAME +error_temp \- identify soft syscall error codes +.SH SYNTAX +.B #include + +int \fBerror_temp\fP(\fIe\fR); + +int \fIe\fR; +.SH DESCRIPTION +.B error_temp +returns 1 if syscall error code +.I e +is a soft error, 0 if it is a hard error. +Normally +.I e +is +.BR errno . + +A hard error is persistent: +file not found, read-only file system, symbolic link loop, etc. + +A soft error is usually transient: +out of memory, out of disk space, I/O error, disk quota exceeded, +connection refused, host unreachable, etc. +.SH "SEE ALSO" +error(3) diff --git a/error_temp.c b/error_temp.c new file mode 100644 index 0000000..6782cef --- /dev/null +++ b/error_temp.c @@ -0,0 +1,80 @@ +#include +#include "error.h" + +#define X(n) if (e == n) return 1; + +int error_temp(e) +int e; +{ + X(error_intr) + X(error_nomem) + X(error_txtbsy) + X(error_io) + X(error_timeout) + X(error_wouldblock) + X(error_again) +#ifdef EDEADLK + X(EDEADLK) +#endif +#ifdef EBUSY + X(EBUSY) +#endif +#ifdef ENFILE + X(ENFILE) +#endif +#ifdef EMFILE + X(EMFILE) +#endif +#ifdef EFBIG + X(EFBIG) +#endif +#ifdef ENOSPC + X(ENOSPC) +#endif +#ifdef ENETDOWN + X(ENETDOWN) +#endif +#ifdef ENETUNREACH + X(ENETUNREACH) +#endif +#ifdef ENETRESET + X(ENETRESET) +#endif +#ifdef ECONNABORTED + X(ECONNABORTED) +#endif +#ifdef ECONNRESET + X(ECONNRESET) +#endif +#ifdef ENOBUFS + X(ENOBUFS) +#endif +#ifdef ETOOMANYREFS + X(ETOOMANYREFS) +#endif +#ifdef ECONNREFUSED + X(ECONNREFUSED) +#endif +#ifdef EHOSTDOWN + X(EHOSTDOWN) +#endif +#ifdef EHOSTUNREACH + X(EHOSTUNREACH) +#endif +#ifdef EPROCLIM + X(EPROCLIM) +#endif +#ifdef EUSERS + X(EUSERS) +#endif +#ifdef EDQUOT + X(EDQUOT) +#endif +#ifdef ESTALE + X(ESTALE) +#endif +#ifdef ENOLCK + X(ENOLCK) +#endif + return 0; +} diff --git a/except.1 b/except.1 new file mode 100644 index 0000000..4198cc2 --- /dev/null +++ b/except.1 @@ -0,0 +1,33 @@ +.TH except 1 +.SH NAME +except \- reverse the exit code of a program +.SH SYNOPSIS +.B except +.I program +[ +.I arg ... +] +.SH DESCRIPTION +.B except +runs +.I program +with the given arguments. + +If +.I program +exits 0, +.B except +exits 100. +If +.I program +exits 111, +.B except +exits 111. +If +.I program +exits anything else, +.B except +exits 0. +.SH "SEE ALSO" +bouncesaying(1), +condredirect(1) diff --git a/except.c b/except.c new file mode 100644 index 0000000..c553b3b --- /dev/null +++ b/except.c @@ -0,0 +1,37 @@ +#include "fork.h" +#include "strerr.h" +#include "wait.h" +#include "error.h" +#include "exit.h" + +#define FATAL "except: fatal: " + +void main(argc,argv) +int argc; +char **argv; +{ + int pid; + int wstat; + + if (!argv[1]) + strerr_die1x(100,"except: usage: except program [ arg ... ]"); + + pid = fork(); + if (pid == -1) + strerr_die2sys(111,FATAL,"unable to fork: "); + if (pid == 0) { + execvp(argv[1],argv + 1); + if (error_temp(errno)) _exit(111); + _exit(100); + } + + if (wait_pid(&wstat,pid) == -1) + strerr_die2x(111,FATAL,"wait failed"); + if (wait_crashed(wstat)) + strerr_die2x(111,FATAL,"child crashed"); + switch(wait_exitcode(wstat)) { + case 0: _exit(100); + case 111: strerr_die2x(111,FATAL,"temporary child error"); + default: _exit(0); + } +} diff --git a/exit.h b/exit.h new file mode 100644 index 0000000..39011c8 --- /dev/null +++ b/exit.h @@ -0,0 +1,6 @@ +#ifndef EXIT_H +#define EXIT_H + +extern void _exit(); + +#endif diff --git a/extra.h b/extra.h new file mode 100644 index 0000000..c598175 --- /dev/null +++ b/extra.h @@ -0,0 +1,7 @@ +#ifndef EXTRA_H +#define EXTRA_H + +#define QUEUE_EXTRA "" +#define QUEUE_EXTRALEN 0 + +#endif diff --git a/fd.h b/fd.h new file mode 100644 index 0000000..c3d6e3e --- /dev/null +++ b/fd.h @@ -0,0 +1,7 @@ +#ifndef FD_H +#define FD_H + +extern int fd_copy(); +extern int fd_move(); + +#endif diff --git a/fd_copy.3 b/fd_copy.3 new file mode 100644 index 0000000..758a7e7 --- /dev/null +++ b/fd_copy.3 @@ -0,0 +1,44 @@ +.TH fd_copy 3 +.SH NAME +fd_copy \- duplicate a descriptor +.SH SYNTAX +.B #include + +int \fBfd_copy\fP(\fIto\fR,\fIfrom\fR); + +int \fIto\fR; +.br +int \fIfrom\fR; +.SH DESCRIPTION +.B fd_copy +copies +descriptor +.I from +to descriptor +.IR to . +If +.I to +was already open, +.B fd_copy +closes it. +.B fd_copy +always leaves +.I from +intact; +if +.I to +and +.I from +are the same number, +.B fd_copy +does nothing. + +.B fd_copy +returns 0 on success, -1 on error. +.B fd_copy +does not guarantee that +.I to +will remain open, if it was open, in case of error. +.SH "SEE ALSO" +dup(2), +fd_move(3) diff --git a/fd_copy.c b/fd_copy.c new file mode 100644 index 0000000..b9f7167 --- /dev/null +++ b/fd_copy.c @@ -0,0 +1,13 @@ +#include +#include "fd.h" + +int fd_copy(to,from) +int to; +int from; +{ + if (to == from) return 0; + if (fcntl(from,F_GETFL,0) == -1) return -1; + close(to); + if (fcntl(from,F_DUPFD,to) == -1) return -1; + return 0; +} diff --git a/fd_move.3 b/fd_move.3 new file mode 100644 index 0000000..94aa1b7 --- /dev/null +++ b/fd_move.3 @@ -0,0 +1,41 @@ +.TH fd_move 3 +.SH NAME +fd_move \- renumber a descriptor +.SH SYNTAX +.B #include + +int \fBfd_move\fP(\fIto\fR,\fIfrom\fR); + +int \fIto\fR; +.br +int \fIfrom\fR; +.SH DESCRIPTION +.B fd_move +moves +descriptor +.I from +to descriptor +.IR to . +If +.I to +was already open, +.B fd_move +closes it. +If the move is successful, +.B fd_move +closes +.IR from . +Exception: +if +.I to +and +.I from +are the same number, +.B fd_move +does nothing. + +.B fd_move +returns 0 on success, -1 on error. +.SH "SEE ALSO" +dup(2), +fd_copy(3) diff --git a/fd_move.c b/fd_move.c new file mode 100644 index 0000000..1aa557f --- /dev/null +++ b/fd_move.c @@ -0,0 +1,11 @@ +#include "fd.h" + +int fd_move(to,from) +int to; +int from; +{ + if (to == from) return 0; + if (fd_copy(to,from) == -1) return -1; + close(from); + return 0; +} diff --git a/fifo.c b/fifo.c new file mode 100644 index 0000000..1f3d150 --- /dev/null +++ b/fifo.c @@ -0,0 +1,10 @@ +#include +#include +#include "hasmkffo.h" +#include "fifo.h" + +#ifdef HASMKFIFO +int fifo_make(fn,mode) char *fn; int mode; { return mkfifo(fn,mode); } +#else +int fifo_make(fn,mode) char *fn; int mode; { return mknod(fn,S_IFIFO | mode,0); } +#endif diff --git a/fifo.h b/fifo.h new file mode 100644 index 0000000..2c5469b --- /dev/null +++ b/fifo.h @@ -0,0 +1,6 @@ +#ifndef FIFO_H +#define FIFO_H + +extern int fifo_make(); + +#endif diff --git a/fifo_make.3 b/fifo_make.3 new file mode 100644 index 0000000..489233d --- /dev/null +++ b/fifo_make.3 @@ -0,0 +1,24 @@ +.TH fifo_make 3 +.SH NAME +fifo_make \- create a named pipe +.SH SYNTAX +.B #include + +int \fBfifo_make\fP(\fIfn\fR,\fImode\fR); + +char *\fIfn\fR; +.br +int \fImode\fR; +.SH DESCRIPTION +.B fifo_make +creates a new named pipe +with name +.I fn +and mode +.I mode +(modified by the process umask). + +.B fifo_make +returns 0 on success, -1 on error. +.SH "SEE ALSO" +mkfifo(2) diff --git a/find-systype.sh b/find-systype.sh new file mode 100644 index 0000000..16266d3 --- /dev/null +++ b/find-systype.sh @@ -0,0 +1,144 @@ +# oper-:arch-:syst-:chip-:kern- +# oper = operating system type; e.g., sunos-4.1.4 +# arch = machine language; e.g., sparc +# syst = which binaries can run; e.g., sun4 +# chip = chip model; e.g., micro-2-80 +# kern = kernel version; e.g., sun4m +# dependence: arch --- chip +# \ \ +# oper --- syst --- kern +# so, for example, syst is interpreted in light of oper, but chip is not. +# anyway, no slashes, no extra colons, no uppercase letters. +# the point of the extra -'s is to ease parsing: can add hierarchies later. +# e.g., *:i386-*:*:pentium-*:* would handle pentium-100 as well as pentium, +# and i386-486 (486s do have more instructions, you know) as well as i386. +# the idea here is to include ALL useful available information. + +exec 2>/dev/null +sys="`uname -s | tr '/:[A-Z]' '..[a-z]'`" +if [ x"$sys" != x ] +then + unamer="`uname -r | tr /: ..`" + unamem="`uname -m | tr /: ..`" + unamev="`uname -v | tr /: ..`" + + case "$sys" in + bsd.os) + # in bsd 4.4, uname -v does not have useful info. + # in bsd 4.4, uname -m is arch, not chip. + oper="$sys-$unamer" + arch="$unamem" + syst="" + chip="`sysctl -n hw.model`" + kern="" + ;; + freebsd) + # see above about bsd 4.4 + oper="$sys-$unamer" + arch="$unamem" + syst="" + chip="`sysctl -n hw.model`" # hopefully + kern="" + ;; + netbsd) + # see above about bsd 4.4 + oper="$sys-$unamer" + arch="$unamem" + syst="" + chip="`sysctl -n hw.model`" # hopefully + kern="" + ;; + linux) + # as in bsd 4.4, uname -v does not have useful info. + oper="$sys-$unamer" + syst="" + chip="$unamem" + kern="" + case "$chip" in + i386|i486|i586|i686) + arch="i386" + ;; + alpha) + arch="alpha" + ;; + esac + ;; + aix) + # naturally IBM has to get uname -r and uname -v backwards. dorks. + oper="$sys-$unamev-$unamer" + arch="`arch | tr /: ..`" + syst="" + chip="$unamem" + kern="" + ;; + sunos) + oper="$sys-$unamer-$unamev" + arch="`(uname -p || mach) | tr /: ..`" + syst="`arch | tr /: ..`" + chip="$unamem" # this is wrong; is there any way to get the real info? + kern="`arch -k | tr /: ..`" + ;; + unix_sv) + oper="$sys-$unamer-$unamev" + arch="`uname -m`" + syst="" + chip="$unamem" + kern="" + ;; + *) + oper="$sys-$unamer-$unamev" + arch="`arch | tr /: ..`" + syst="" + chip="$unamem" + kern="" + ;; + esac +else + $CC -c trycpp.c + $LD -o trycpp trycpp.o + case `./trycpp` in + nextstep) + oper="nextstep-`hostinfo | sed -n 's/^[ ]*NeXT Mach \([^:]*\):.*$/\1/p'`" + arch="`hostinfo | sed -n 's/^Processor type: \(.*\) (.*)$/\1/p' | tr /: ..`" + syst="" + chip="`hostinfo | sed -n 's/^Processor type: .* (\(.*\))$/\1/p' | tr ' /:' '...'`" + kern="" + ;; + *) + oper="unknown" + arch="" + syst="" + chip="" + kern="" + ;; + esac + rm -f trycpp.o trycpp +fi + +case "$chip" in +80486) + # let's try to be consistent here. (BSD/OS) + chip=i486 + ;; +i486DX) + # respect the hyphen hierarchy. (FreeBSD) + chip=i486-dx + ;; +i486.DX2) + # respect the hyphen hierarchy. (FreeBSD) + chip=i486-dx2 + ;; +Intel.586) + # no, you nitwits, there is no such chip. (NeXTStep) + chip=pentium + ;; +i586) + # no, you nitwits, there is no such chip. (Linux) + chip=pentium + ;; +i686) + # STOP SAYING THAT! (Linux) + chip=ppro +esac + +echo "$oper-:$arch-:$syst-:$chip-:$kern-" | tr ' [A-Z]' '.[a-z]' diff --git a/fmt.h b/fmt.h new file mode 100644 index 0000000..ba2fe16 --- /dev/null +++ b/fmt.h @@ -0,0 +1,25 @@ +#ifndef FMT_H +#define FMT_H + +#define FMT_ULONG 40 /* enough space to hold 2^128 - 1 in decimal, plus \0 */ +#define FMT_LEN ((char *) 0) /* convenient abbreviation */ + +extern unsigned int fmt_uint(); +extern unsigned int fmt_uint0(); +extern unsigned int fmt_xint(); +extern unsigned int fmt_nbbint(); +extern unsigned int fmt_ushort(); +extern unsigned int fmt_xshort(); +extern unsigned int fmt_nbbshort(); +extern unsigned int fmt_ulong(); +extern unsigned int fmt_xlong(); +extern unsigned int fmt_nbblong(); + +extern unsigned int fmt_plusminus(); +extern unsigned int fmt_minus(); +extern unsigned int fmt_0x(); + +extern unsigned int fmt_str(); +extern unsigned int fmt_strn(); + +#endif diff --git a/fmt_str.c b/fmt_str.c new file mode 100644 index 0000000..48930cf --- /dev/null +++ b/fmt_str.c @@ -0,0 +1,12 @@ +#include "fmt.h" + +unsigned int fmt_str(s,t) +register char *s; register char *t; +{ + register unsigned int len; + char ch; + len = 0; + if (s) { while (ch = t[len]) s[len++] = ch; } + else while (t[len]) len++; + return len; +} diff --git a/fmt_strn.c b/fmt_strn.c new file mode 100644 index 0000000..3ef58a6 --- /dev/null +++ b/fmt_strn.c @@ -0,0 +1,12 @@ +#include "fmt.h" + +unsigned int fmt_strn(s,t,n) +register char *s; register char *t; register unsigned int n; +{ + register unsigned int len; + char ch; + len = 0; + if (s) { while (n-- && (ch = t[len])) s[len++] = ch; } + else while (n-- && t[len]) len++; + return len; +} diff --git a/fmt_uint.c b/fmt_uint.c new file mode 100644 index 0000000..8595be8 --- /dev/null +++ b/fmt_uint.c @@ -0,0 +1,6 @@ +#include "fmt.h" + +unsigned int fmt_uint(s,u) register char *s; register unsigned int u; +{ + register unsigned long l; l = u; return fmt_ulong(s,l); +} diff --git a/fmt_uint0.c b/fmt_uint0.c new file mode 100644 index 0000000..81705f6 --- /dev/null +++ b/fmt_uint0.c @@ -0,0 +1,10 @@ +#include "fmt.h" + +unsigned int fmt_uint0(s,u,n) char *s; unsigned int u; unsigned int n; +{ + unsigned int len; + len = fmt_uint(FMT_LEN,u); + while (len < n) { if (s) *s++ = '0'; ++len; } + if (s) fmt_uint(s,u); + return len; +} diff --git a/fmt_ulong.c b/fmt_ulong.c new file mode 100644 index 0000000..ab12e8c --- /dev/null +++ b/fmt_ulong.c @@ -0,0 +1,13 @@ +#include "fmt.h" + +unsigned int fmt_ulong(s,u) register char *s; register unsigned long u; +{ + register unsigned int len; register unsigned long q; + len = 1; q = u; + while (q > 9) { ++len; q /= 10; } + if (s) { + s += len; + do { *--s = '0' + (u % 10); u /= 10; } while(u); /* handles u == 0 */ + } + return len; +} diff --git a/fmtqfn.c b/fmtqfn.c new file mode 100644 index 0000000..6229b4a --- /dev/null +++ b/fmtqfn.c @@ -0,0 +1,24 @@ +#include "fmtqfn.h" +#include "fmt.h" +#include "auto_split.h" + +unsigned int fmtqfn(s,dirslash,id,flagsplit) +char *s; +char *dirslash; +unsigned long id; +int flagsplit; +{ + unsigned int len; + unsigned int i; + + len = 0; + i = fmt_str(s,dirslash); len += i; if (s) s += i; + if (flagsplit) + { + i = fmt_ulong(s,id % auto_split); len += i; if (s) s += i; + i = fmt_str(s,"/"); len += i; if (s) s += i; + } + i = fmt_ulong(s,id); len += i; if (s) s += i; + if (s) *s++ = 0; ++len; + return len; +} diff --git a/fmtqfn.h b/fmtqfn.h new file mode 100644 index 0000000..a449aa1 --- /dev/null +++ b/fmtqfn.h @@ -0,0 +1,8 @@ +#ifndef FMTQFN_H +#define FMTQFN_H + +extern unsigned int fmtqfn(); + +#define FMTQFN 40 /* maximum space needed, if len(dirslash) <= 10 */ + +#endif diff --git a/forgeries.7 b/forgeries.7 new file mode 100644 index 0000000..cb99fa7 --- /dev/null +++ b/forgeries.7 @@ -0,0 +1,104 @@ +.TH forgeries 7 +.SH "NAME" +forgeries \- how easy it is to forge mail +.SH "SUMMARY" +An electronic mail message can easily be forged. +Almost everything in it, +including the return address, +is completely under the control of the sender. + +An electronic mail message can be manually traced to its origin +if (1) all system administrators of intermediate machines +are both cooperative and competent, +(2) the sender did not break low-level TCP/IP security, +and +(3) all intermediate machines are secure. + +Users of +.I cryptography +can automatically ensure the integrity and secrecy +of their mail messages, as long as +the sending and receiving machines are secure. +.SH "FORGERIES" +Like postal mail, +electronic mail can be created entirely at the whim of the sender. +.BR From , +.BR Sender , +.BR Return-Path , +and +.BR Message-ID +can all contain whatever information the sender wants. + +For example, if you inject a message through +.B sendmail +or +.B qmail-inject +or +.BR SMTP , +you can simply type in a +.B From +field. +In fact, +.B qmail-inject +lets you set up +.BR MAILUSER , +.BR MAILHOST , +and +.B MAILNAME +environment variables +to produce your desired +.B From +field on every message. +.SH "TRACING FORGERIES" +Like postal mail, +electronic mail is postmarked when it is sent. +Each machine that receives an electronic mail message +adds a +.B Received +line to the top. + +A modern +.B Received +line contains quite a bit of information. +In conjunction with the machine's logs, +it lets a competent system administrator +determine where the machine received the message from, +as long as the sender did not break low-level TCP/IP security +or security on that machine. + +Large multi-user machines often come with inadequate logging software. +Fortunately, a system administrator can easily obtain a copy of a +931/1413/Ident/TAP server, such as +.BR pidentd . +Unfortunately, +some system administrators fail to do this, +and are thus unable to figure out which local user +was responsible for generating a message. + +If all intermediate system administrators are competent, +and the sender did not break machine security or low-level TCP/IP security, +it is possible to trace a message backwards. +Unfortunately, some traces are stymied by intermediate system +administrators who are uncooperative or untrustworthy. +.SH "CRYPTOGRAPHY" +The sender of a mail message may place his message into a +.I cryptographic +envelope stamped with his seal. +Strong cryptography guarantees that any two messages with the same seal +were sent by the same cryptographic entity: +perhaps a single person, perhaps a group of cooperating people, +but in any case somebody who knows a secret originally held +only by the creator of the seal. +The seal is called a +.I public key\fR. + +Unfortunately, the creator of the seal is often an insecure machine, +or an untrustworthy central agency, +but most of the time seals are kept secure. + +One popular cryptographic program is +.BR pgp . +.SH "SEE ALSO" +pgp(1), +identd(8), +qmail-header(8) diff --git a/fork.h1 b/fork.h1 new file mode 100644 index 0000000..b786255 --- /dev/null +++ b/fork.h1 @@ -0,0 +1,7 @@ +#ifndef FORK_H +#define FORK_H + +extern int fork(); +#define vfork fork + +#endif diff --git a/fork.h2 b/fork.h2 new file mode 100644 index 0000000..41773b6 --- /dev/null +++ b/fork.h2 @@ -0,0 +1,7 @@ +#ifndef FORK_H +#define FORK_H + +extern int fork(); +extern int vfork(); + +#endif diff --git a/forward.1 b/forward.1 new file mode 100644 index 0000000..d0a7f95 --- /dev/null +++ b/forward.1 @@ -0,0 +1,24 @@ +.TH forward 1 +.SH NAME +forward \- forward new mail to one or more addresses +.SH SYNOPSIS +in +.BR .qmail : +.B |forward +.I address ... +.SH DESCRIPTION +.B forward +forwards each new mail message to the specified list of addresses. +It is a simple wrapper around +.BR qmail-queue . +It achieves the same results as listing each +.I address +separately in +.BR .qmail , +but it is more programmable since +.I address +can be constructed on the fly. +.SH "SEE ALSO" +dot-qmail(5), +qmail-command(8), +qmail-queue(8) diff --git a/forward.c b/forward.c new file mode 100644 index 0000000..e7390aa --- /dev/null +++ b/forward.c @@ -0,0 +1,60 @@ +#include "sig.h" +#include "readwrite.h" +#include "exit.h" +#include "env.h" +#include "qmail.h" +#include "strerr.h" +#include "substdio.h" +#include "fmt.h" + +#define FATAL "forward: fatal: " + +void die_nomem() { strerr_die2x(111,FATAL,"out of memory"); } + +struct qmail qqt; + +int mywrite(fd,buf,len) int fd; char *buf; int len; +{ + qmail_put(&qqt,buf,len); + return len; +} + +char inbuf[SUBSTDIO_INSIZE]; +char outbuf[1]; +substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof inbuf); +substdio ssout = SUBSTDIO_FDBUF(mywrite,-1,outbuf,sizeof outbuf); + +char num[FMT_ULONG]; + +void main(argc,argv) +int argc; +char **argv; +{ + char *sender; + char *dtline; + char *qqx; + + sig_pipeignore(); + + sender = env_get("NEWSENDER"); + if (!sender) + strerr_die2x(100,FATAL,"NEWSENDER not set"); + dtline = env_get("DTLINE"); + if (!dtline) + strerr_die2x(100,FATAL,"DTLINE not set"); + + if (qmail_open(&qqt) == -1) + strerr_die2sys(111,FATAL,"unable to fork: "); + qmail_puts(&qqt,dtline); + if (substdio_copy(&ssout,&ssin) != 0) + strerr_die2sys(111,FATAL,"unable to read message: "); + substdio_flush(&ssout); + + num[fmt_ulong(num,qmail_qp(&qqt))] = 0; + + qmail_from(&qqt,sender); + while (*++argv) qmail_to(&qqt,*argv); + qqx = qmail_close(&qqt); + if (*qqx) strerr_die2x(*qqx == 'D' ? 100 : 111,FATAL,qqx + 1); + strerr_die2x(0,"forward: qp ",num); +} diff --git a/gen_alloc.h b/gen_alloc.h new file mode 100644 index 0000000..b94a956 --- /dev/null +++ b/gen_alloc.h @@ -0,0 +1,7 @@ +#ifndef GEN_ALLOC_H +#define GEN_ALLOC_H + +#define GEN_ALLOC_typedef(ta,type,field,len,a) \ + typedef struct ta { type *field; unsigned int len; unsigned int a; } ta; + +#endif diff --git a/gen_allocdefs.h b/gen_allocdefs.h new file mode 100644 index 0000000..783a9b1 --- /dev/null +++ b/gen_allocdefs.h @@ -0,0 +1,34 @@ +#ifndef GEN_ALLOC_DEFS_H +#define GEN_ALLOC_DEFS_H + +#define GEN_ALLOC_ready(ta,type,field,len,a,i,n,x,base,ta_ready) \ +int ta_ready(x,n) register ta *x; register unsigned int n; \ +{ register unsigned int i; \ + if (x->field) { \ + i = x->a; \ + if (n > i) { \ + x->a = base + n + (n >> 3); \ + if (alloc_re(&x->field,i * sizeof(type),x->a * sizeof(type))) return 1; \ + x->a = i; return 0; } \ + return 1; } \ + x->len = 0; \ + return !!(x->field = (type *) alloc((x->a = n) * sizeof(type))); } + +#define GEN_ALLOC_readyplus(ta,type,field,len,a,i,n,x,base,ta_rplus) \ +int ta_rplus(x,n) register ta *x; register unsigned int n; \ +{ register unsigned int i; \ + if (x->field) { \ + i = x->a; n += x->len; \ + if (n > i) { \ + x->a = base + n + (n >> 3); \ + if (alloc_re(&x->field,i * sizeof(type),x->a * sizeof(type))) return 1; \ + x->a = i; return 0; } \ + return 1; } \ + x->len = 0; \ + return !!(x->field = (type *) alloc((x->a = n) * sizeof(type))); } + +#define GEN_ALLOC_append(ta,type,field,len,a,i,n,x,base,ta_rplus,ta_append) \ +int ta_append(x,i) register ta *x; register type *i; \ +{ if (!ta_rplus(x,1)) return 0; x->field[x->len++] = *i; return 1; } + +#endif diff --git a/getln.3 b/getln.3 new file mode 100644 index 0000000..ffe1953 --- /dev/null +++ b/getln.3 @@ -0,0 +1,51 @@ +.TH getln 3 +.SH NAME +getln \- read one line of data +.SH SYNTAX +.B #include + +int \fBgetln\fP(&\fIss\fR,&\fIsa\fR,&\fImatch\fR,\fIsep\fR); + +substdio \fIss\fR; +.br +stralloc \fIsa\fR; +.br +int \fImatch\fR; +.br +int \fIsep\fR; +.SH DESCRIPTION +.B getln +reads a line of characters, terminated by a +.I sep +character, +from +.IR ss . +It returns the line in +.I sa +and sets +.I match +to 1. + +If +.B getln +sees end-of-input before it sees +.IR sep , +it returns the partial line in +.I sa +and sets +.I match +to 0. + +.B getln +normally returns 0. +If it runs out of memory, +or encounters an error from +.IR ss , +it returns -1, +setting +.B errno +appropriately. +.SH "SEE ALSO" +stralloc(3), +substdio(3), +getln2(3) diff --git a/getln.c b/getln.c new file mode 100644 index 0000000..c5cb097 --- /dev/null +++ b/getln.c @@ -0,0 +1,20 @@ +#include "substdio.h" +#include "byte.h" +#include "stralloc.h" +#include "getln.h" + +int getln(ss,sa,match,sep) +register substdio *ss; +register stralloc *sa; +int *match; +int sep; +{ + char *cont; + unsigned int clen; + + if (getln2(ss,sa,&cont,&clen,sep) == -1) return -1; + if (!clen) { *match = 0; return 0; } + if (!stralloc_catb(sa,cont,clen)) return -1; + *match = 1; + return 0; +} diff --git a/getln.h b/getln.h new file mode 100644 index 0000000..cf4f934 --- /dev/null +++ b/getln.h @@ -0,0 +1,7 @@ +#ifndef GETLN_H +#define GETLN_H + +extern int getln(); +extern int getln2(); + +#endif diff --git a/getln2.3 b/getln2.3 new file mode 100644 index 0000000..f95105a --- /dev/null +++ b/getln2.3 @@ -0,0 +1,64 @@ +.TH getln2 3 +.SH NAME +getln2 \- read one line of data +.SH SYNTAX +.B #include + +int \fBgetln2\fP(&\fIss\fR,&\fIsa\fR,&\fIcont\fR,&\fIclen\fR,\fIsep\fR); + +substdio \fIss\fR; +.br +stralloc \fIsa\fR; +.br +char *\fIcont\fR; +.br +unsigned int \fIclen\fR; +.br +int \fIsep\fR; +.SH DESCRIPTION +.B getln2 +reads a line of characters, terminated by a +.I sep +character, +from +.IR ss . + +The line is returned in two pieces. +The first piece is stored in +.IR sa . +The second piece is +.IR cont , +a pointer to +.I clen +characters inside the +.I ss +buffer. +The second piece must be copied somewhere else +before +.I ss +is used again. + +If +.B getln2 +sees end-of-input before it sees +.IR sep , +it sets +.I clen +to 0 and does not set +.IR cont . +It puts the partial line into +.IR sa . + +.B getln2 +normally returns 0. +If it runs out of memory, +or encounters an error from +.IR ss , +it returns -1, +setting +.B errno +appropriately. +.SH "SEE ALSO" +stralloc(3), +substdio(3), +getln(3) diff --git a/getln2.c b/getln2.c new file mode 100644 index 0000000..9e576e9 --- /dev/null +++ b/getln2.c @@ -0,0 +1,31 @@ +#include "substdio.h" +#include "stralloc.h" +#include "byte.h" +#include "getln.h" + +int getln2(ss,sa,cont,clen,sep) +register substdio *ss; +register stralloc *sa; +/*@out@*/char **cont; +/*@out@*/unsigned int *clen; +int sep; +{ + register char *x; + register unsigned int i; + int n; + + if (!stralloc_ready(sa,0)) return -1; + sa->len = 0; + + for (;;) { + n = substdio_feed(ss); + if (n < 0) return -1; + if (n == 0) { *clen = 0; return 0; } + x = substdio_PEEK(ss); + i = byte_chr(x,n,sep); + if (i < n) { substdio_SEEK(ss,*clen = i + 1); *cont = x; return 0; } + if (!stralloc_readyplus(sa,n)) return -1; + i = sa->len; + sa->len = i + substdio_get(ss,sa->s + i,n); + } +} diff --git a/gfrom.c b/gfrom.c new file mode 100644 index 0000000..87f9ad4 --- /dev/null +++ b/gfrom.c @@ -0,0 +1,10 @@ +#include "str.h" +#include "gfrom.h" + +int gfrom(s,len) +char *s; +int len; +{ + while ((len > 0) && (*s == '>')) { ++s; --len; } + return (len >= 5) && !str_diffn(s,"From ",5); +} diff --git a/gfrom.h b/gfrom.h new file mode 100644 index 0000000..488193c --- /dev/null +++ b/gfrom.h @@ -0,0 +1,6 @@ +#ifndef GFROM_H +#define GFROM_H + +extern int gfrom(); + +#endif diff --git a/headerbody.c b/headerbody.c new file mode 100644 index 0000000..91965c0 --- /dev/null +++ b/headerbody.c @@ -0,0 +1,87 @@ +#include "stralloc.h" +#include "substdio.h" +#include "getln.h" +#include "hfield.h" +#include "headerbody.h" + +static int getsa(ss,sa,match) +substdio *ss; +stralloc *sa; +int *match; +{ + if (!*match) return 0; + if (getln(ss,sa,match,'\n') == -1) return -1; + if (*match) return 1; + if (!sa->len) return 0; + if (!stralloc_append(sa,"\n")) return -1; + return 1; +} + +static stralloc line = {0}; +static stralloc nextline = {0}; + +int headerbody(ss,dohf,hdone,dobl) +substdio *ss; +void (*dohf)(); +void (*hdone)(); +void (*dobl)(); +{ + int match; + int flaglineok; + match = 1; + flaglineok = 0; + for (;;) + { + switch(getsa(ss,&nextline,&match)) + { + case -1: + return -1; + case 0: + if (flaglineok) dohf(&line); + hdone(); + /* no message body; could insert blank line here */ + return 0; + } + if (flaglineok) + { + if ((nextline.s[0] == ' ') || (nextline.s[0] == '\t')) + { + if (!stralloc_cat(&line,&nextline)) return -1; + continue; + } + dohf(&line); + } + if (nextline.len == 1) + { + hdone(); + dobl(&nextline); + break; + } + if (stralloc_starts(&nextline,"From ")) + { + if (!stralloc_copys(&line,"MBOX-Line: ")) return -1; + if (!stralloc_cat(&line,&nextline)) return -1; + } + else + if (hfield_valid(nextline.s,nextline.len)) + { + if (!stralloc_copy(&line,&nextline)) return -1; + } + else + { + hdone(); + if (!stralloc_copys(&line,"\n")) return -1; + dobl(&line); + dobl(&nextline); + break; + } + flaglineok = 1; + } + for (;;) + switch(getsa(ss,&nextline,&match)) + { + case -1: return -1; + case 0: return 0; + case 1: dobl(&nextline); + } +} diff --git a/headerbody.h b/headerbody.h new file mode 100644 index 0000000..3bb2e2f --- /dev/null +++ b/headerbody.h @@ -0,0 +1,6 @@ +#ifndef HEADERBODY_H +#define HEADERBODY_H + +extern int headerbody(); + +#endif diff --git a/hfield.c b/hfield.c new file mode 100644 index 0000000..06de0be --- /dev/null +++ b/hfield.c @@ -0,0 +1,125 @@ +#include "hfield.h" + +static char *(hname[]) = { + "unknown-header" +, "sender" +, "from" +, "reply-to" +, "to" +, "cc" +, "bcc" +, "date" +, "message-id" +, "subject" +, "resent-sender" +, "resent-from" +, "resent-reply-to" +, "resent-to" +, "resent-cc" +, "resent-bcc" +, "resent-date" +, "resent-message-id" +, "return-receipt-to" +, "errors-to" +, "apparently-to" +, "received" +, "return-path" +, "delivered-to" +, "content-length" +, "content-type" +, "content-transfer-encoding" +, "notice-requested-upon-delivery-to" +, "mail-followup-to" +, 0 +}; + +static int hmatch(s,len,t) +char *s; +int len; +char *t; +{ + int i; + char ch; + + for (i = 0;ch = t[i];++i) + { + if (i >= len) return 0; + if (ch != s[i]) + { + if (ch == '-') return 0; + if (ch - 32 != s[i]) return 0; + } + } + for (;;) + { + if (i >= len) return 0; + ch = s[i]; + if (ch == ':') return 1; + if ((ch != ' ') && (ch != '\t')) return 0; + ++i; + } +} + +int hfield_known(s,len) +char *s; +int len; +{ + int i; + char *t; + + for (i = 1;t = hname[i];++i) + if (hmatch(s,len,t)) + return i; + return 0; +} + +int hfield_valid(s,len) +char *s; +int len; +{ + int i; + int j; + char ch; + + for (j = 0;j < len;++j) + if (s[j] == ':') + break; + if (j >= len) return 0; + while (j) + { + ch = s[j - 1]; + if ((ch != ' ') && (ch != '\t')) + break; + --j; + } + if (!j) return 0; + + for (i = 0;i < j;++i) + { + ch = s[i]; + if (ch <= 32) return 0; + if (ch >= 127) return 0; + } + return 1; +} + +unsigned int hfield_skipname(s,len) +char *s; +int len; +{ + int i; + char ch; + + for (i = 0;i < len;++i) + if (s[i] == ':') + break; + if (i < len) ++i; + while (i < len) + { + ch = s[i]; + if ((ch != '\t') && (ch != '\n') && (ch != '\r') && (ch != ' ')) + break; + ++i; + } + return i; +} diff --git a/hfield.h b/hfield.h new file mode 100644 index 0000000..20b30a5 --- /dev/null +++ b/hfield.h @@ -0,0 +1,38 @@ +#ifndef HFIELD_H +#define HFIELD_H + +extern unsigned int hfield_skipname(); +extern int hfield_known(); +extern int hfield_valid(); + +#define H_SENDER 1 +#define H_FROM 2 +#define H_REPLYTO 3 +#define H_TO 4 +#define H_CC 5 +#define H_BCC 6 +#define H_DATE 7 +#define H_MESSAGEID 8 +#define H_SUBJECT 9 +#define H_R_SENDER 10 +#define H_R_FROM 11 +#define H_R_REPLYTO 12 +#define H_R_TO 13 +#define H_R_CC 14 +#define H_R_BCC 15 +#define H_R_DATE 16 +#define H_R_MESSAGEID 17 +#define H_RETURNRECEIPTTO 18 +#define H_ERRORSTO 19 +#define H_APPARENTLYTO 20 +#define H_RECEIVED 21 +#define H_RETURNPATH 22 +#define H_DELIVEREDTO 23 +#define H_CONTENTLENGTH 24 +#define H_CONTENTTYPE 25 +#define H_CONTENTTRANSFERENCODING 26 +#define H_NOTICEREQUESTEDUPONDELIVERYTO 27 +#define H_MAILFOLLOWUPTO 28 +#define H_NUM 29 + +#endif diff --git a/hier.c b/hier.c new file mode 100644 index 0000000..28e568d --- /dev/null +++ b/hier.c @@ -0,0 +1,252 @@ +#include "auto_qmail.h" +#include "auto_split.h" +#include "auto_uids.h" +#include "fmt.h" +#include "fifo.h" + +char buf[100 + FMT_ULONG]; + +void dsplit(base,uid,mode) +char *base; /* must be under 100 bytes */ +int uid; +int mode; +{ + char *x; + unsigned long i; + + d(auto_qmail,base,uid,auto_gidq,mode); + + for (i = 0;i < auto_split;++i) { + x = buf; + x += fmt_str(x,base); + x += fmt_str(x,"/"); + x += fmt_ulong(x,i); + *x = 0; + + d(auto_qmail,buf,uid,auto_gidq,mode); + } +} + +void hier() +{ + h(auto_qmail,auto_uido,auto_gidq,0755); + + d(auto_qmail,"control",auto_uido,auto_gidq,0755); + d(auto_qmail,"users",auto_uido,auto_gidq,0755); + d(auto_qmail,"bin",auto_uido,auto_gidq,0755); + d(auto_qmail,"boot",auto_uido,auto_gidq,0755); + d(auto_qmail,"doc",auto_uido,auto_gidq,0755); + d(auto_qmail,"man",auto_uido,auto_gidq,0755); + d(auto_qmail,"man/cat1",auto_uido,auto_gidq,0755); + d(auto_qmail,"man/cat5",auto_uido,auto_gidq,0755); + d(auto_qmail,"man/cat7",auto_uido,auto_gidq,0755); + d(auto_qmail,"man/cat8",auto_uido,auto_gidq,0755); + d(auto_qmail,"man/man1",auto_uido,auto_gidq,0755); + d(auto_qmail,"man/man5",auto_uido,auto_gidq,0755); + d(auto_qmail,"man/man7",auto_uido,auto_gidq,0755); + d(auto_qmail,"man/man8",auto_uido,auto_gidq,0755); + + d(auto_qmail,"alias",auto_uida,auto_gidq,02755); + + d(auto_qmail,"queue",auto_uidq,auto_gidq,0750); + d(auto_qmail,"queue/pid",auto_uidq,auto_gidq,0700); + d(auto_qmail,"queue/intd",auto_uidq,auto_gidq,0700); + d(auto_qmail,"queue/todo",auto_uidq,auto_gidq,0750); + d(auto_qmail,"queue/bounce",auto_uids,auto_gidq,0700); + + dsplit("queue/mess",auto_uidq,0750); + dsplit("queue/info",auto_uids,0700); + dsplit("queue/local",auto_uids,0700); + dsplit("queue/remote",auto_uids,0700); + + d(auto_qmail,"queue/lock",auto_uidq,auto_gidq,0750); + z(auto_qmail,"queue/lock/tcpto",1024,auto_uidr,auto_gidq,0644); + z(auto_qmail,"queue/lock/sendmutex",0,auto_uids,auto_gidq,0600); + p(auto_qmail,"queue/lock/trigger",auto_uids,auto_gidq,0622); + + c(auto_qmail,"boot","home",auto_uido,auto_gidq,0755); + c(auto_qmail,"boot","home+df",auto_uido,auto_gidq,0755); + c(auto_qmail,"boot","proc",auto_uido,auto_gidq,0755); + c(auto_qmail,"boot","proc+df",auto_uido,auto_gidq,0755); + c(auto_qmail,"boot","binm1",auto_uido,auto_gidq,0755); + c(auto_qmail,"boot","binm1+df",auto_uido,auto_gidq,0755); + c(auto_qmail,"boot","binm2",auto_uido,auto_gidq,0755); + c(auto_qmail,"boot","binm2+df",auto_uido,auto_gidq,0755); + c(auto_qmail,"boot","binm3",auto_uido,auto_gidq,0755); + c(auto_qmail,"boot","binm3+df",auto_uido,auto_gidq,0755); + + c(auto_qmail,"doc","FAQ",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","UPGRADE",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","SENDMAIL",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","INSTALL",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","INSTALL.alias",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","INSTALL.ctl",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","INSTALL.ids",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","INSTALL.maildir",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","INSTALL.mbox",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","INSTALL.vsm",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","TEST.deliver",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","TEST.receive",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","REMOVE.sendmail",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","REMOVE.binmail",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","PIC.local2alias",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","PIC.local2ext",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","PIC.local2local",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","PIC.local2rem",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","PIC.local2virt",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","PIC.nullclient",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","PIC.relaybad",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","PIC.relaygood",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","PIC.rem2local",auto_uido,auto_gidq,0644); + + c(auto_qmail,"bin","qmail-queue",auto_uidq,auto_gidq,04711); + c(auto_qmail,"bin","qmail-lspawn",auto_uido,auto_gidq,0700); + c(auto_qmail,"bin","qmail-start",auto_uido,auto_gidq,0700); + c(auto_qmail,"bin","qmail-getpw",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-local",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-remote",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-rspawn",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-clean",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-send",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","splogger",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-newu",auto_uido,auto_gidq,0700); + c(auto_qmail,"bin","qmail-newmrh",auto_uido,auto_gidq,0700); + c(auto_qmail,"bin","qmail-pw2u",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-inject",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","predate",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","datemail",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","mailsubj",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-showctl",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-qread",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-qstat",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-tcpto",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-tcpok",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-pop3d",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-popup",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-qmqpc",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-qmqpd",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-qmtpd",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-smtpd",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","sendmail",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","tcp-env",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qreceipt",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qsmhook",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qbiff",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","forward",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","preline",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","condredirect",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","bouncesaying",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","except",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","maildirmake",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","maildir2mbox",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","maildirwatch",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qail",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","elq",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","pinq",auto_uido,auto_gidq,0755); + + c(auto_qmail,"man/man5","addresses.5",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat5","addresses.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man5","envelopes.5",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat5","envelopes.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man5","maildir.5",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat5","maildir.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man5","mbox.5",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat5","mbox.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man5","dot-qmail.5",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat5","dot-qmail.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man5","qmail-control.5",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat5","qmail-control.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man5","qmail-header.5",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat5","qmail-header.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man5","qmail-log.5",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat5","qmail-log.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man5","qmail-users.5",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat5","qmail-users.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man5","tcp-environ.5",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat5","tcp-environ.0",auto_uido,auto_gidq,0644); + + c(auto_qmail,"man/man7","forgeries.7",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat7","forgeries.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man7","qmail-limits.7",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat7","qmail-limits.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man7","qmail.7",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat7","qmail.0",auto_uido,auto_gidq,0644); + + c(auto_qmail,"man/man1","forward.1",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat1","forward.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man1","condredirect.1",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat1","condredirect.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man1","bouncesaying.1",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat1","bouncesaying.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man1","except.1",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat1","except.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man1","maildirmake.1",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat1","maildirmake.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man1","maildir2mbox.1",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat1","maildir2mbox.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man1","maildirwatch.1",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat1","maildirwatch.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man1","mailsubj.1",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat1","mailsubj.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man1","qreceipt.1",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat1","qreceipt.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man1","qbiff.1",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat1","qbiff.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man1","preline.1",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat1","preline.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man1","tcp-env.1",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat1","tcp-env.0",auto_uido,auto_gidq,0644); + + c(auto_qmail,"man/man8","qmail-local.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-local.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-lspawn.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-lspawn.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-getpw.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-getpw.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-remote.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-remote.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-rspawn.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-rspawn.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-clean.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-clean.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-send.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-send.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-start.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-start.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","splogger.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","splogger.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-queue.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-queue.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-inject.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-inject.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-showctl.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-showctl.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-newmrh.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-newmrh.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-newu.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-newu.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-pw2u.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-pw2u.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-qread.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-qread.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-qstat.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-qstat.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-tcpok.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-tcpok.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-tcpto.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-tcpto.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-pop3d.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-pop3d.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-popup.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-popup.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-qmqpc.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-qmqpc.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-qmqpd.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-qmqpd.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-qmtpd.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-qmtpd.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-smtpd.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-smtpd.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-command.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-command.0",auto_uido,auto_gidq,0644); +} diff --git a/home+df.sh b/home+df.sh new file mode 100644 index 0000000..7885cdf --- /dev/null +++ b/home+df.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +# Using splogger to send the log through syslog. +# Using dot-forward to support sendmail-style ~/.forward files. +# Using qmail-local to deliver messages to ~/Mailbox by default. + +exec env - PATH="QMAIL/bin:$PATH" \ +qmail-start '|dot-forward .forward +./Mailbox' splogger qmail diff --git a/home.sh b/home.sh new file mode 100644 index 0000000..c96c02b --- /dev/null +++ b/home.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +# Using splogger to send the log through syslog. +# Using qmail-local to deliver messages to ~/Mailbox by default. + +exec env - PATH="QMAIL/bin:$PATH" \ +qmail-start ./Mailbox splogger qmail diff --git a/hostname.c b/hostname.c new file mode 100644 index 0000000..39c7f87 --- /dev/null +++ b/hostname.c @@ -0,0 +1,17 @@ +#include "substdio.h" +#include "subfd.h" +#include "readwrite.h" +#include "exit.h" + +char host[256]; + +void main() +{ + host[0] = 0; /* sigh */ + gethostname(host,sizeof(host)); + host[sizeof(host) - 1] = 0; + substdio_puts(subfdoutsmall,host); + substdio_puts(subfdoutsmall,"\n"); + substdio_flush(subfdoutsmall); + _exit(0); +} diff --git a/idedit.c b/idedit.c new file mode 100644 index 0000000..e6747b5 --- /dev/null +++ b/idedit.c @@ -0,0 +1,147 @@ +#include +#include +#include +#include "readwrite.h" +#include "exit.h" +#include "scan.h" +#include "fmt.h" +#include "strerr.h" +#include "open.h" +#include "seek.h" +#include "fork.h" + +#define FATAL "idedit: fatal: " +#define WARNING "idedit: warning: " + +int fd; + +void byte(pos,value) +char *pos; +unsigned int value; +{ + unsigned long u; + unsigned char ch; + + if (pos[scan_ulong(pos,&u)]) return; + + if (seek_set(fd,(seek_pos) u) == -1) + strerr_die2sys(111,FATAL,"unable to seek: "); + + ch = value; + if (write(fd,&ch,1) != 1) + strerr_die2sys(111,FATAL,"unable to write: "); +} + +char *args[10]; + +void run() +{ + int pid; + int wstat; + + pid = fork(); + if (pid == -1) + strerr_die2sys(111,FATAL,"unable to fork: "); + + if (pid == 0) { + execv(*args,args); + strerr_die4sys(111,WARNING,"unable to run ",*args,": "); + } + + if (wait_pid(&wstat,pid) != pid) + strerr_die2sys(111,FATAL,"waitpid surprise"); +} + +void u(account,group,home,pos0,pos1,pos2,pos3) +char *account; +char *group; +char *home; +char *pos0; +char *pos1; +char *pos2; +char *pos3; +{ + struct passwd *pw; + unsigned int value; + + pw = getpwnam(account); + + if (!pw && group) { + args[0] = "add-account"; + args[1] = account; + args[2] = group; + args[3] = home; + args[4] = 0; + run(); + pw = getpwnam(account); + } + + if (!pw) + strerr_die3x(111,FATAL,"unable to find uid for ",account); + + value = pw->pw_uid; + byte(pos0,value); value >>= 8; + byte(pos1,value); value >>= 8; + byte(pos2,value); value >>= 8; + byte(pos3,value); value >>= 8; + if (value) + strerr_die3x(111,FATAL,"excessively large uid for ",account); +} + +void g(group,pos0,pos1,pos2,pos3) +char *group; +char *pos0; +char *pos1; +char *pos2; +char *pos3; +{ + struct group *gr; + unsigned int value; + + gr = getgrnam(group); + + if (!gr) { + args[0] = "add-group"; + args[1] = group; + args[2] = 0; + run(); + gr = getgrnam(group); + } + + if (!gr) + strerr_die3x(111,FATAL,"unable to find gid for ",group); + + value = gr->gr_gid; + byte(pos0,value); value >>= 8; + byte(pos1,value); value >>= 8; + byte(pos2,value); value >>= 8; + byte(pos3,value); value >>= 8; + if (value) + strerr_die3x(111,FATAL,"excessively large gid for ",group); +} + +void main(argc,argv) +int argc; +char **argv; +{ + if (argc < 42) _exit(100); + + fd = open_write(argv[1]); + if (fd == -1) strerr_die4sys(111,FATAL,"unable to open ",argv[1],": "); + + g("qmail",argv[34],argv[35],argv[36],argv[37]); + g("nofiles",argv[38],argv[39],argv[40],argv[41]); + + u("root",(char *) 0,"/",argv[14],argv[15],argv[16],argv[17]); + + u("qmaild","nofiles","/var/qmail",argv[6],argv[7],argv[8],argv[9]); + u("qmaill","nofiles","/var/qmail",argv[10],argv[11],argv[12],argv[13]); + u("qmailp","nofiles","/var/qmail",argv[18],argv[19],argv[20],argv[21]); + u("alias","nofiles","/var/qmail/alias",argv[2],argv[3],argv[4],argv[5]); + + u("qmailq","qmail","/var/qmail",argv[22],argv[23],argv[24],argv[25]); + u("qmailr","qmail","/var/qmail",argv[26],argv[27],argv[28],argv[29]); + u("qmails","qmail","/var/qmail",argv[30],argv[31],argv[32],argv[33]); + + _exit(0); +} diff --git a/install-big.c b/install-big.c new file mode 100644 index 0000000..df813df --- /dev/null +++ b/install-big.c @@ -0,0 +1,285 @@ +#include "auto_qmail.h" +#include "auto_split.h" +#include "auto_uids.h" +#include "fmt.h" +#include "fifo.h" + +char buf[100 + FMT_ULONG]; + +void dsplit(base,uid,mode) +char *base; /* must be under 100 bytes */ +int uid; +int mode; +{ + char *x; + unsigned long i; + + d(auto_qmail,base,uid,auto_gidq,mode); + + for (i = 0;i < auto_split;++i) { + x = buf; + x += fmt_str(x,base); + x += fmt_str(x,"/"); + x += fmt_ulong(x,i); + *x = 0; + + d(auto_qmail,buf,uid,auto_gidq,mode); + } +} + +void hier() +{ + h(auto_qmail,auto_uido,auto_gidq,0755); + + d(auto_qmail,"control",auto_uido,auto_gidq,0755); + d(auto_qmail,"users",auto_uido,auto_gidq,0755); + d(auto_qmail,"bin",auto_uido,auto_gidq,0755); + d(auto_qmail,"boot",auto_uido,auto_gidq,0755); + d(auto_qmail,"doc",auto_uido,auto_gidq,0755); + d(auto_qmail,"man",auto_uido,auto_gidq,0755); + d(auto_qmail,"man/cat1",auto_uido,auto_gidq,0755); + d(auto_qmail,"man/cat5",auto_uido,auto_gidq,0755); + d(auto_qmail,"man/cat7",auto_uido,auto_gidq,0755); + d(auto_qmail,"man/cat8",auto_uido,auto_gidq,0755); + d(auto_qmail,"man/man1",auto_uido,auto_gidq,0755); + d(auto_qmail,"man/man5",auto_uido,auto_gidq,0755); + d(auto_qmail,"man/man7",auto_uido,auto_gidq,0755); + d(auto_qmail,"man/man8",auto_uido,auto_gidq,0755); + + d(auto_qmail,"alias",auto_uida,auto_gidq,02755); + + d(auto_qmail,"queue",auto_uidq,auto_gidq,0750); + d(auto_qmail,"queue/pid",auto_uidq,auto_gidq,0700); + d(auto_qmail,"queue/intd",auto_uidq,auto_gidq,0700); + d(auto_qmail,"queue/todo",auto_uidq,auto_gidq,0750); + d(auto_qmail,"queue/bounce",auto_uids,auto_gidq,0700); + + dsplit("queue/mess",auto_uidq,0750); + dsplit("queue/info",auto_uids,0700); + dsplit("queue/local",auto_uids,0700); + dsplit("queue/remote",auto_uids,0700); + + d(auto_qmail,"queue/lock",auto_uidq,auto_gidq,0750); + z(auto_qmail,"queue/lock/tcpto",1024,auto_uidr,auto_gidq,0644); + z(auto_qmail,"queue/lock/sendmutex",0,auto_uids,auto_gidq,0600); + p(auto_qmail,"queue/lock/trigger",auto_uids,auto_gidq,0622); + + c(auto_qmail,"boot","home",auto_uido,auto_gidq,0755); + c(auto_qmail,"boot","home+df",auto_uido,auto_gidq,0755); + c(auto_qmail,"boot","proc",auto_uido,auto_gidq,0755); + c(auto_qmail,"boot","proc+df",auto_uido,auto_gidq,0755); + c(auto_qmail,"boot","binm1",auto_uido,auto_gidq,0755); + c(auto_qmail,"boot","binm1+df",auto_uido,auto_gidq,0755); + c(auto_qmail,"boot","binm2",auto_uido,auto_gidq,0755); + c(auto_qmail,"boot","binm2+df",auto_uido,auto_gidq,0755); + c(auto_qmail,"boot","binm3",auto_uido,auto_gidq,0755); + c(auto_qmail,"boot","binm3+df",auto_uido,auto_gidq,0755); + + c(auto_qmail,"doc","FAQ",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","UPGRADE",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","SENDMAIL",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","INSTALL",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","INSTALL.alias",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","INSTALL.ctl",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","INSTALL.ids",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","INSTALL.maildir",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","INSTALL.mbox",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","INSTALL.vsm",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","TEST.deliver",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","TEST.receive",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","REMOVE.sendmail",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","REMOVE.binmail",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","PIC.local2alias",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","PIC.local2ext",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","PIC.local2local",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","PIC.local2rem",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","PIC.local2virt",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","PIC.nullclient",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","PIC.relaybad",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","PIC.relaygood",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","PIC.rem2local",auto_uido,auto_gidq,0644); + + c(auto_qmail,"bin","qmail-queue",auto_uidq,auto_gidq,04711); + c(auto_qmail,"bin","qmail-lspawn",auto_uido,auto_gidq,0700); + c(auto_qmail,"bin","qmail-start",auto_uido,auto_gidq,0700); + c(auto_qmail,"bin","qmail-getpw",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-local",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-remote",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-rspawn",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-clean",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-send",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","splogger",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-newu",auto_uido,auto_gidq,0700); + c(auto_qmail,"bin","qmail-newmrh",auto_uido,auto_gidq,0700); + c(auto_qmail,"bin","qmail-pw2u",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-inject",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","predate",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","datemail",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","mailsubj",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-showctl",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-qread",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-qstat",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-tcpto",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-tcpok",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-pop3d",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-popup",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-qmqpc",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-qmqpd",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-qmtpd",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-smtpd",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","sendmail",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","tcp-env",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qreceipt",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qsmhook",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qbiff",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","forward",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","preline",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","condredirect",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","bouncesaying",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","except",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","maildirmake",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","maildir2mbox",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","maildirwatch",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qail",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","elq",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","pinq",auto_uido,auto_gidq,0755); + + c(auto_qmail,"man/man5","addresses.5",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat5","addresses.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man5","envelopes.5",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat5","envelopes.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man5","maildir.5",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat5","maildir.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man5","mbox.5",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat5","mbox.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man5","dot-qmail.5",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat5","dot-qmail.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man5","qmail-control.5",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat5","qmail-control.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man5","qmail-header.5",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat5","qmail-header.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man5","qmail-log.5",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat5","qmail-log.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man5","qmail-users.5",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat5","qmail-users.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man5","tcp-environ.5",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat5","tcp-environ.0",auto_uido,auto_gidq,0644); + + c(auto_qmail,"man/man7","forgeries.7",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat7","forgeries.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man7","qmail-limits.7",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat7","qmail-limits.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man7","qmail.7",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat7","qmail.0",auto_uido,auto_gidq,0644); + + c(auto_qmail,"man/man1","forward.1",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat1","forward.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man1","condredirect.1",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat1","condredirect.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man1","bouncesaying.1",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat1","bouncesaying.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man1","except.1",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat1","except.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man1","maildirmake.1",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat1","maildirmake.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man1","maildir2mbox.1",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat1","maildir2mbox.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man1","maildirwatch.1",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat1","maildirwatch.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man1","mailsubj.1",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat1","mailsubj.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man1","qreceipt.1",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat1","qreceipt.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man1","qbiff.1",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat1","qbiff.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man1","preline.1",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat1","preline.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man1","tcp-env.1",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat1","tcp-env.0",auto_uido,auto_gidq,0644); + + c(auto_qmail,"man/man8","qmail-local.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-local.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-lspawn.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-lspawn.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-getpw.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-getpw.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-remote.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-remote.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-rspawn.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-rspawn.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-clean.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-clean.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-send.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-send.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-start.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-start.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","splogger.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","splogger.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-queue.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-queue.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-inject.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-inject.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-showctl.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-showctl.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-newmrh.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-newmrh.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-newu.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-newu.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-pw2u.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-pw2u.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-qread.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-qread.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-qstat.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-qstat.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-tcpok.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-tcpok.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-tcpto.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-tcpto.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-pop3d.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-pop3d.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-popup.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-popup.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-qmqpc.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-qmqpc.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-qmqpd.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-qmqpd.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-qmtpd.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-qmtpd.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-smtpd.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-smtpd.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-command.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-command.0",auto_uido,auto_gidq,0644); + + c(auto_qmail,"bin","dot-forward",auto_uido,auto_gidq,0755); + + c(auto_qmail,"man/man1","dot-forward.1",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat1","dot-forward.0",auto_uido,auto_gidq,0644); + + d(auto_qmail,"doc/fastforward",auto_uido,auto_gidq,0755); + + c(auto_qmail,"bin","fastforward",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","printforward",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","setforward",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","newaliases",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","printmaillist",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","setmaillist",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","newinclude",auto_uido,auto_gidq,0755); + + c(auto_qmail,"doc/fastforward","ALIASES",auto_uido,auto_gidq,0644); + + c(auto_qmail,"man/man1","fastforward.1",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man1","printforward.1",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man1","setforward.1",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man1","newaliases.1",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man1","printmaillist.1",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man1","setmaillist.1",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man1","newinclude.1",auto_uido,auto_gidq,0644); + + c(auto_qmail,"man/cat1","fastforward.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat1","printforward.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat1","setforward.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat1","newaliases.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat1","printmaillist.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat1","setmaillist.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat1","newinclude.0",auto_uido,auto_gidq,0644); +} diff --git a/install.c b/install.c new file mode 100644 index 0000000..95034f2 --- /dev/null +++ b/install.c @@ -0,0 +1,164 @@ +#include "substdio.h" +#include "strerr.h" +#include "error.h" +#include "open.h" +#include "readwrite.h" +#include "exit.h" + +extern void hier(); + +#define FATAL "install: fatal: " + +int fdsourcedir = -1; + +void h(home,uid,gid,mode) +char *home; +int uid; +int gid; +int mode; +{ + if (mkdir(home,0700) == -1) + if (errno != error_exist) + strerr_die4sys(111,FATAL,"unable to mkdir ",home,": "); + if (chown(home,uid,gid) == -1) + strerr_die4sys(111,FATAL,"unable to chown ",home,": "); + if (chmod(home,mode) == -1) + strerr_die4sys(111,FATAL,"unable to chmod ",home,": "); +} + +void d(home,subdir,uid,gid,mode) +char *home; +char *subdir; +int uid; +int gid; +int mode; +{ + if (chdir(home) == -1) + strerr_die4sys(111,FATAL,"unable to switch to ",home,": "); + if (mkdir(subdir,0700) == -1) + if (errno != error_exist) + strerr_die6sys(111,FATAL,"unable to mkdir ",home,"/",subdir,": "); + if (chown(subdir,uid,gid) == -1) + strerr_die6sys(111,FATAL,"unable to chown ",home,"/",subdir,": "); + if (chmod(subdir,mode) == -1) + strerr_die6sys(111,FATAL,"unable to chmod ",home,"/",subdir,": "); +} + +void p(home,fifo,uid,gid,mode) +char *home; +char *fifo; +int uid; +int gid; +int mode; +{ + if (chdir(home) == -1) + strerr_die4sys(111,FATAL,"unable to switch to ",home,": "); + if (fifo_make(fifo,0700) == -1) + if (errno != error_exist) + strerr_die6sys(111,FATAL,"unable to mkfifo ",home,"/",fifo,": "); + if (chown(fifo,uid,gid) == -1) + strerr_die6sys(111,FATAL,"unable to chown ",home,"/",fifo,": "); + if (chmod(fifo,mode) == -1) + strerr_die6sys(111,FATAL,"unable to chmod ",home,"/",fifo,": "); +} + +char inbuf[SUBSTDIO_INSIZE]; +char outbuf[SUBSTDIO_OUTSIZE]; +substdio ssin; +substdio ssout; + +void c(home,subdir,file,uid,gid,mode) +char *home; +char *subdir; +char *file; +int uid; +int gid; +int mode; +{ + int fdin; + int fdout; + + if (fchdir(fdsourcedir) == -1) + strerr_die2sys(111,FATAL,"unable to switch back to source directory: "); + + fdin = open_read(file); + if (fdin == -1) + strerr_die4sys(111,FATAL,"unable to read ",file,": "); + substdio_fdbuf(&ssin,read,fdin,inbuf,sizeof inbuf); + + if (chdir(home) == -1) + strerr_die4sys(111,FATAL,"unable to switch to ",home,": "); + if (chdir(subdir) == -1) + strerr_die6sys(111,FATAL,"unable to switch to ",home,"/",subdir,": "); + + fdout = open_trunc(file); + if (fdout == -1) + strerr_die6sys(111,FATAL,"unable to write .../",subdir,"/",file,": "); + substdio_fdbuf(&ssout,write,fdout,outbuf,sizeof outbuf); + + switch(substdio_copy(&ssout,&ssin)) { + case -2: + strerr_die4sys(111,FATAL,"unable to read ",file,": "); + case -3: + strerr_die6sys(111,FATAL,"unable to write .../",subdir,"/",file,": "); + } + + close(fdin); + if (substdio_flush(&ssout) == -1) + strerr_die6sys(111,FATAL,"unable to write .../",subdir,"/",file,": "); + if (fsync(fdout) == -1) + strerr_die6sys(111,FATAL,"unable to write .../",subdir,"/",file,": "); + if (close(fdout) == -1) /* NFS silliness */ + strerr_die6sys(111,FATAL,"unable to write .../",subdir,"/",file,": "); + + if (chown(file,uid,gid) == -1) + strerr_die6sys(111,FATAL,"unable to chown .../",subdir,"/",file,": "); + if (chmod(file,mode) == -1) + strerr_die6sys(111,FATAL,"unable to chmod .../",subdir,"/",file,": "); +} + +void z(home,file,len,uid,gid,mode) +char *home; +char *file; +int len; +int uid; +int gid; +int mode; +{ + int fdout; + + if (chdir(home) == -1) + strerr_die4sys(111,FATAL,"unable to switch to ",home,": "); + + fdout = open_trunc(file); + if (fdout == -1) + strerr_die6sys(111,FATAL,"unable to write ",home,"/",file,": "); + substdio_fdbuf(&ssout,write,fdout,outbuf,sizeof outbuf); + + while (len-- > 0) + if (substdio_put(&ssout,"",1) == -1) + strerr_die6sys(111,FATAL,"unable to write ",home,"/",file,": "); + + if (substdio_flush(&ssout) == -1) + strerr_die6sys(111,FATAL,"unable to write ",home,"/",file,": "); + if (fsync(fdout) == -1) + strerr_die6sys(111,FATAL,"unable to write ",home,"/",file,": "); + if (close(fdout) == -1) /* NFS silliness */ + strerr_die6sys(111,FATAL,"unable to write ",home,"/",file,": "); + + if (chown(file,uid,gid) == -1) + strerr_die6sys(111,FATAL,"unable to chown ",home,"/",file,": "); + if (chmod(file,mode) == -1) + strerr_die6sys(111,FATAL,"unable to chmod ",home,"/",file,": "); +} + +void main() +{ + fdsourcedir = open_read("."); + if (fdsourcedir == -1) + strerr_die2sys(111,FATAL,"unable to open current directory: "); + + umask(077); + hier(); + _exit(0); +} diff --git a/instcheck.c b/instcheck.c new file mode 100644 index 0000000..d41efda --- /dev/null +++ b/instcheck.c @@ -0,0 +1,108 @@ +#include +#include +#include "strerr.h" +#include "error.h" +#include "readwrite.h" +#include "exit.h" + +extern void hier(); + +#define FATAL "instcheck: fatal: " +#define WARNING "instcheck: warning: " + +void perm(prefix1,prefix2,prefix3,file,type,uid,gid,mode) +char *prefix1; +char *prefix2; +char *prefix3; +char *file; +int type; +int uid; +int gid; +int mode; +{ + struct stat st; + + if (stat(file,&st) == -1) { + if (errno == error_noent) + strerr_warn6(WARNING,prefix1,prefix2,prefix3,file," does not exist",0); + else + strerr_warn4(WARNING,"unable to stat .../",file,": ",&strerr_sys); + return; + } + + if ((uid != -1) && (st.st_uid != uid)) + strerr_warn6(WARNING,prefix1,prefix2,prefix3,file," has wrong owner",0); + if ((gid != -1) && (st.st_gid != gid)) + strerr_warn6(WARNING,prefix1,prefix2,prefix3,file," has wrong group",0); + if ((st.st_mode & 07777) != mode) + strerr_warn6(WARNING,prefix1,prefix2,prefix3,file," has wrong permissions",0); + if ((st.st_mode & S_IFMT) != type) + strerr_warn6(WARNING,prefix1,prefix2,prefix3,file," has wrong type",0); +} + +void h(home,uid,gid,mode) +char *home; +int uid; +int gid; +int mode; +{ + perm("","","",home,S_IFDIR,uid,gid,mode); +} + +void d(home,subdir,uid,gid,mode) +char *home; +char *subdir; +int uid; +int gid; +int mode; +{ + if (chdir(home) == -1) + strerr_die4sys(111,FATAL,"unable to switch to ",home,": "); + perm("",home,"/",subdir,S_IFDIR,uid,gid,mode); +} + +void p(home,fifo,uid,gid,mode) +char *home; +char *fifo; +int uid; +int gid; +int mode; +{ + if (chdir(home) == -1) + strerr_die4sys(111,FATAL,"unable to switch to ",home,": "); + perm("",home,"/",fifo,S_IFIFO,uid,gid,mode); +} + +void c(home,subdir,file,uid,gid,mode) +char *home; +char *subdir; +char *file; +int uid; +int gid; +int mode; +{ + if (chdir(home) == -1) + strerr_die4sys(111,FATAL,"unable to switch to ",home,": "); + if (chdir(subdir) == -1) + strerr_die6sys(111,FATAL,"unable to switch to ",home,"/",subdir,": "); + perm(".../",subdir,"/",file,S_IFREG,uid,gid,mode); +} + +void z(home,file,len,uid,gid,mode) +char *home; +char *file; +int len; +int uid; +int gid; +int mode; +{ + if (chdir(home) == -1) + strerr_die4sys(111,FATAL,"unable to switch to ",home,": "); + perm("",home,"/",file,S_IFREG,uid,gid,mode); +} + +void main() +{ + hier(); + _exit(0); +} diff --git a/ip.c b/ip.c new file mode 100644 index 0000000..5528ad5 --- /dev/null +++ b/ip.c @@ -0,0 +1,53 @@ +#include "fmt.h" +#include "scan.h" +#include "ip.h" + +unsigned int ip_fmt(s,ip) +char *s; +struct ip_address *ip; +{ + unsigned int len; + unsigned int i; + + len = 0; + i = fmt_ulong(s,(unsigned long) ip->d[0]); len += i; if (s) s += i; + i = fmt_str(s,"."); len += i; if (s) s += i; + i = fmt_ulong(s,(unsigned long) ip->d[1]); len += i; if (s) s += i; + i = fmt_str(s,"."); len += i; if (s) s += i; + i = fmt_ulong(s,(unsigned long) ip->d[2]); len += i; if (s) s += i; + i = fmt_str(s,"."); len += i; if (s) s += i; + i = fmt_ulong(s,(unsigned long) ip->d[3]); len += i; if (s) s += i; + return len; +} + +unsigned int ip_scan(s,ip) +char *s; +struct ip_address *ip; +{ + unsigned int i; + unsigned int len; + unsigned long u; + + len = 0; + i = scan_ulong(s,&u); if (!i) return 0; ip->d[0] = u; s += i; len += i; + if (*s != '.') return 0; ++s; ++len; + i = scan_ulong(s,&u); if (!i) return 0; ip->d[1] = u; s += i; len += i; + if (*s != '.') return 0; ++s; ++len; + i = scan_ulong(s,&u); if (!i) return 0; ip->d[2] = u; s += i; len += i; + if (*s != '.') return 0; ++s; ++len; + i = scan_ulong(s,&u); if (!i) return 0; ip->d[3] = u; s += i; len += i; + return len; +} + +unsigned int ip_scanbracket(s,ip) +char *s; +struct ip_address *ip; +{ + unsigned int len; + + if (*s != '[') return 0; + len = ip_scan(s + 1,ip); + if (!len) return 0; + if (s[len + 1] != ']') return 0; + return len + 2; +} diff --git a/ip.h b/ip.h new file mode 100644 index 0000000..b99002f --- /dev/null +++ b/ip.h @@ -0,0 +1,11 @@ +#ifndef IP_H +#define IP_H + +struct ip_address { unsigned char d[4]; } ; + +extern unsigned int ip_fmt(); +#define IPFMT 19 +extern unsigned int ip_scan(); +extern unsigned int ip_scanbracket(); + +#endif diff --git a/ipalloc.c b/ipalloc.c new file mode 100644 index 0000000..81792d5 --- /dev/null +++ b/ipalloc.c @@ -0,0 +1,7 @@ +#include "alloc.h" +#include "gen_allocdefs.h" +#include "ip.h" +#include "ipalloc.h" + +GEN_ALLOC_readyplus(ipalloc,struct ip_mx,ix,len,a,i,n,x,10,ipalloc_readyplus) +GEN_ALLOC_append(ipalloc,struct ip_mx,ix,len,a,i,n,x,10,ipalloc_readyplus,ipalloc_append) diff --git a/ipalloc.h b/ipalloc.h new file mode 100644 index 0000000..ad61475 --- /dev/null +++ b/ipalloc.h @@ -0,0 +1,14 @@ +#ifndef IPALLOC_H +#define IPALLOC_H + +#include "ip.h" + +struct ip_mx { struct ip_address ip; int pref; } ; + +#include "gen_alloc.h" + +GEN_ALLOC_typedef(ipalloc,struct ip_mx,ix,len,a) +extern int ipalloc_readyplus(); +extern int ipalloc_append(); + +#endif diff --git a/ipme.c b/ipme.c new file mode 100644 index 0000000..3c86127 --- /dev/null +++ b/ipme.c @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include +#include +#include +#ifndef SIOCGIFCONF /* whatever works */ +#include +#endif +#include "hassalen.h" +#include "byte.h" +#include "ip.h" +#include "ipalloc.h" +#include "stralloc.h" +#include "ipme.h" + +static int ipmeok = 0; +ipalloc ipme = {0}; + +int ipme_is(ip) +struct ip_address *ip; +{ + int i; + if (ipme_init() != 1) return -1; + for (i = 0;i < ipme.len;++i) + if (byte_equal(&ipme.ix[i].ip,4,ip)) + return 1; + return 0; +} + +static stralloc buf = {0}; + +int ipme_init() +{ + struct ifconf ifc; + char *x; + struct ifreq *ifr; + struct sockaddr_in *sin; + int len; + int s; + struct ip_mx ix; + + if (ipmeok) return 1; + if (!ipalloc_readyplus(&ipme,0)) return 0; + ipme.len = 0; + ix.pref = 0; + + /* 0.0.0.0 is a special address which always refers to + * "this host, this network", according to RFC 1122, Sec. 3.2.1.3a. + */ + byte_copy(&ix.ip,4,"\0\0\0\0"); + if (!ipalloc_append(&ipme,&ix)) { return 0; } + if ((s = socket(AF_INET,SOCK_STREAM,0)) == -1) return -1; + + len = 256; + for (;;) { + if (!stralloc_ready(&buf,len)) { close(s); return 0; } + buf.len = 0; + ifc.ifc_buf = buf.s; + ifc.ifc_len = len; + if (ioctl(s,SIOCGIFCONF,&ifc) >= 0) /* > is for System V */ + if (ifc.ifc_len + sizeof(*ifr) + 64 < len) { /* what a stupid interface */ + buf.len = ifc.ifc_len; + break; + } + if (len > 200000) { close(s); return -1; } + len += 100 + (len >> 2); + } + x = buf.s; + while (x < buf.s + buf.len) { + ifr = (struct ifreq *) x; +#ifdef HASSALEN + len = sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len; + if (len < sizeof(*ifr)) + len = sizeof(*ifr); + if (ifr->ifr_addr.sa_family == AF_INET) { + sin = (struct sockaddr_in *) &ifr->ifr_addr; + byte_copy(&ix.ip,4,&sin->sin_addr); + if (ioctl(s,SIOCGIFFLAGS,x) == 0) + if (ifr->ifr_flags & IFF_UP) + if (!ipalloc_append(&ipme,&ix)) { close(s); return 0; } + } +#else + len = sizeof(*ifr); + if (ioctl(s,SIOCGIFFLAGS,x) == 0) + if (ifr->ifr_flags & IFF_UP) + if (ioctl(s,SIOCGIFADDR,x) == 0) + if (ifr->ifr_addr.sa_family == AF_INET) { + sin = (struct sockaddr_in *) &ifr->ifr_addr; + byte_copy(&ix.ip,4,&sin->sin_addr); + if (!ipalloc_append(&ipme,&ix)) { close(s); return 0; } + } +#endif + x += len; + } + close(s); + ipmeok = 1; + return 1; +} diff --git a/ipme.h b/ipme.h new file mode 100644 index 0000000..5bbf515 --- /dev/null +++ b/ipme.h @@ -0,0 +1,12 @@ +#ifndef IPME_H +#define IPME_H + +#include "ip.h" +#include "ipalloc.h" + +extern ipalloc ipme; + +extern int ipme_init(); +extern int ipme_is(); + +#endif diff --git a/ipmeprint.c b/ipmeprint.c new file mode 100644 index 0000000..1ef56e3 --- /dev/null +++ b/ipmeprint.c @@ -0,0 +1,24 @@ +#include "subfd.h" +#include "substdio.h" +#include "ip.h" +#include "ipme.h" +#include "exit.h" + +char temp[IPFMT]; + +void main() +{ + int j; + switch(ipme_init()) + { + case 0: substdio_putsflush(subfderr,"out of memory\n"); _exit(111); + case -1: substdio_putsflush(subfderr,"hard error\n"); _exit(100); + } + for (j = 0;j < ipme.len;++j) + { + substdio_put(subfdout,temp,ip_fmt(temp,&ipme.ix[j].ip)); + substdio_puts(subfdout,"\n"); + } + substdio_flush(subfdout); + _exit(0); +} diff --git a/lock.h b/lock.h new file mode 100644 index 0000000..a7dee41 --- /dev/null +++ b/lock.h @@ -0,0 +1,8 @@ +#ifndef LOCK_H +#define LOCK_H + +extern int lock_ex(); +extern int lock_un(); +extern int lock_exnb(); + +#endif diff --git a/lock_ex.c b/lock_ex.c new file mode 100644 index 0000000..a3351c9 --- /dev/null +++ b/lock_ex.c @@ -0,0 +1,11 @@ +#include +#include +#include +#include "hasflock.h" +#include "lock.h" + +#ifdef HASFLOCK +int lock_ex(fd) int fd; { return flock(fd,LOCK_EX); } +#else +int lock_ex(fd) int fd; { return lockf(fd,1,0); } +#endif diff --git a/lock_exnb.c b/lock_exnb.c new file mode 100644 index 0000000..5d2a14a --- /dev/null +++ b/lock_exnb.c @@ -0,0 +1,11 @@ +#include +#include +#include +#include "hasflock.h" +#include "lock.h" + +#ifdef HASFLOCK +int lock_exnb(fd) int fd; { return flock(fd,LOCK_EX | LOCK_NB); } +#else +int lock_exnb(fd) int fd; { return lockf(fd,2,0); } +#endif diff --git a/lock_un.c b/lock_un.c new file mode 100644 index 0000000..16e2f17 --- /dev/null +++ b/lock_un.c @@ -0,0 +1,11 @@ +#include +#include +#include +#include "hasflock.h" +#include "lock.h" + +#ifdef HASFLOCK +int lock_un(fd) int fd; { return flock(fd,LOCK_UN); } +#else +int lock_un(fd) int fd; { return lockf(fd,0,0); } +#endif diff --git a/maildir.5 b/maildir.5 new file mode 100644 index 0000000..5da9573 --- /dev/null +++ b/maildir.5 @@ -0,0 +1,239 @@ +.TH maildir 5 +.SH "NAME" +maildir \- directory for incoming mail messages +.SH "INTRODUCTION" +.I maildir +is a structure for +directories of incoming mail messages. +It solves the reliability problems that plague +.I mbox +files and +.I mh +folders. +.SH "RELIABILITY ISSUES" +A machine may crash while it is delivering a message. +For both +.I mbox +files and +.I mh +folders this means that the message will be silently truncated. +Even worse: for +.I mbox +format, if the message is truncated in the middle of a line, +it will be silently joined to the next message. +The mail transport agent will try again later to deliver the message, +but it is unacceptable that a corrupted message should show up at all. +In +.IR maildir , +every message is guaranteed complete upon delivery. + +A machine may have two programs simultaneously delivering mail +to the same user. +The +.I mbox +and +.I mh +formats require the programs to update a single central file. +If the programs do not use some locking mechanism, +the central file will be corrupted. +There are several +.I mbox +and +.I mh +locking mechanisms, +none of which work portably and reliably. +In contrast, in +.IR maildir , +no locks are ever necessary. +Different delivery processes never touch the same file. + +A user may try to delete messages from his mailbox at the same +moment that the machine delivers a new message. +For +.I mbox +and +.I mh +formats, the user's mail-reading program must know +what locking mechanism the mail-delivery programs use. +In contrast, in +.IR maildir , +any delivered message +can be safely updated or deleted by a mail-reading program. + +Many sites use Sun's +.B Network F\fPa\fBil\fPur\fBe System +(NFS), +presumably because the operating system vendor does not offer +anything else. +NFS exacerbates all of the above problems. +Some NFS implementations don't provide +.B any +reliable locking mechanism. +With +.I mbox +and +.I mh +formats, +if two machines deliver mail to the same user, +or if a user reads mail anywhere except the delivery machine, +the user's mail is at risk. +.I maildir +works without trouble over NFS. +.SH "THE MAILDIR STRUCTURE" +A directory in +.I maildir +format has three subdirectories, +all on the same filesystem: +.BR tmp , +.BR new , +and +.BR cur . + +Each file in +.B new +is a newly delivered mail message. +The modification time of the file is the delivery date of the message. +The message is delivered +.I without +an extra UUCP-style +.B From_ +line, +.I without +any +.B >From +quoting, +and +.I without +an extra blank line at the end. +The message is normally in RFC 822 format, +starting with a +.B Return-Path +line and a +.B Delivered-To +line, +but it could contain arbitrary binary data. +It might not even end with a newline. + +Files in +.B cur +are just like files in +.BR new . +The big difference is that files in +.B cur +are no longer new mail: +they have been seen by the user's mail-reading program. +.SH "HOW A MESSAGE IS DELIVERED" +The +.B tmp +directory is used to ensure reliable delivery, +as discussed here. + +A program delivers a mail message in six steps. +First, it +.B chdir()\fPs +to the +.I maildir +directory. +Second, it +.B stat()s +the name +.BR tmp/\fItime.pid.host , +where +.I time +is the number of seconds since the beginning of 1970 GMT, +.I pid +is the program's process ID, +and +.I host +is the host name. +Third, if +.B stat() +returned anything other than ENOENT, +the program sleeps for two seconds, updates +.IR time , +and tries the +.B stat() +again, a limited number of times. +Fourth, the program +creates +.BR tmp/\fItime.pid.host . +Fifth, the program +.I NFS-writes +the message to the file. +Sixth, the program +.BR link() s +the file to +.BR new/\fItime.pid.host . +At that instant the message has been successfully delivered. + +The delivery program is required to start a 24-hour timer before +creating +.BR tmp/\fItime.pid.host , +and to abort the delivery +if the timer expires. +Upon error, timeout, or normal completion, +the delivery program may attempt to +.B unlink() +.BR tmp/\fItime.pid.host . + +.I NFS-writing +means +(1) as usual, checking the number of bytes returned from each +.B write() +call; +(2) calling +.B fsync() +and checking its return value; +(3) calling +.B close() +and checking its return value. +(Standard NFS implementations handle +.B fsync() +incorrectly +but make up for it by abusing +.BR close() .) +.SH "HOW A MESSAGE IS READ" +A mail reader operates as follows. + +It looks through the +.B new +directory for new messages. +Say there is a new message, +.BR new/\fIunique . +The reader may freely display the contents of +.BR new/\fIunique , +delete +.BR new/\fIunique , +or rename +.B new/\fIunique +as +.BR cur/\fIunique:info . +See +.B http://pobox.com/~djb/proto/maildir.html +for the meaning of +.IR info . + +The reader is also expected to look through the +.B tmp +directory and to clean up any old files found there. +A file in +.B tmp +may be safely removed if it +has not been accessed in 36 hours. + +It is a good idea for readers to skip all filenames in +.B new +and +.B cur +starting with a dot. +Other than this, readers should not attempt to parse filenames. +.SH "ENVIRONMENT VARIABLES" +Mail readers supporting +.I maildir +use the +.B MAILDIR +environment variable +as the name of the user's primary mail directory. +.SH "SEE ALSO" +mbox(5), +qmail-local(8) diff --git a/maildir.c b/maildir.c new file mode 100644 index 0000000..efbd3d4 --- /dev/null +++ b/maildir.c @@ -0,0 +1,108 @@ +#include +#include +#include "prioq.h" +#include "env.h" +#include "stralloc.h" +#include "direntry.h" +#include "datetime.h" +#include "now.h" +#include "str.h" +#include "maildir.h" + +struct strerr maildir_chdir_err; +struct strerr maildir_scan_err; + +int maildir_chdir() +{ + char *maildir; + maildir = env_get("MAILDIR"); + if (!maildir) + STRERR(-1,maildir_chdir_err,"MAILDIR not set") + if (chdir(maildir) == -1) + STRERR_SYS3(-1,maildir_chdir_err,"unable to chdir to ",maildir,": ") + return 0; +} + +void maildir_clean(tmpname) +stralloc *tmpname; +{ + DIR *dir; + direntry *d; + datetime_sec time; + struct stat st; + + time = now(); + + dir = opendir("tmp"); + if (!dir) return; + + while (d = readdir(dir)) + { + if (d->d_name[0] == '.') continue; + if (!stralloc_copys(tmpname,"tmp/")) break; + if (!stralloc_cats(tmpname,d->d_name)) break; + if (!stralloc_0(tmpname)) break; + if (stat(tmpname->s,&st) == 0) + if (time > st.st_atime + 129600) + unlink(tmpname->s); + } + closedir(dir); +} + +static int append(pq,filenames,subdir,time) +prioq *pq; +stralloc *filenames; +char *subdir; +datetime_sec time; +{ + DIR *dir; + direntry *d; + struct prioq_elt pe; + unsigned int pos; + struct stat st; + + dir = opendir(subdir); + if (!dir) + STRERR_SYS3(-1,maildir_scan_err,"unable to scan $MAILDIR/",subdir,": ") + + while (d = readdir(dir)) + { + if (d->d_name[0] == '.') continue; + pos = filenames->len; + if (!stralloc_cats(filenames,subdir)) break; + if (!stralloc_cats(filenames,"/")) break; + if (!stralloc_cats(filenames,d->d_name)) break; + if (!stralloc_0(filenames)) break; + if (stat(filenames->s + pos,&st) == 0) + if (st.st_mtime < time) /* don't want to mix up the order */ + { + pe.dt = st.st_mtime; + pe.id = pos; + if (!prioq_insert(pq,&pe)) break; + } + } + + closedir(dir); + if (d) STRERR_SYS3(-1,maildir_scan_err,"unable to read $MAILDIR/",subdir,": ") + return 0; +} + +int maildir_scan(pq,filenames,flagnew,flagcur) +prioq *pq; +stralloc *filenames; +int flagnew; +int flagcur; +{ + struct prioq_elt pe; + datetime_sec time; + int r; + + if (!stralloc_copys(filenames,"")) return 0; + while (prioq_min(pq,&pe)) prioq_delmin(pq); + + time = now(); + + if (flagnew) if (append(pq,filenames,"new",time) == -1) return -1; + if (flagcur) if (append(pq,filenames,"cur",time) == -1) return -1; + return 0; +} diff --git a/maildir.h b/maildir.h new file mode 100644 index 0000000..7dd8826 --- /dev/null +++ b/maildir.h @@ -0,0 +1,12 @@ +#ifndef MAILDIR_H +#define MAILDIR_H + +#include "strerr.h" +extern struct strerr maildir_chdir_err; +extern struct strerr maildir_scan_err; + +extern int maildir_chdir(); +extern void maildir_clean(); +extern int maildir_scan(); + +#endif diff --git a/maildir2mbox.1 b/maildir2mbox.1 new file mode 100644 index 0000000..31423d9 --- /dev/null +++ b/maildir2mbox.1 @@ -0,0 +1,53 @@ +.TH maildir2mbox 1 +.SH NAME +maildir2mbox \- move mail from a maildir to an mbox +.SH SYNOPSIS +.B maildir2mbox +.SH DESCRIPTION +.B maildir2mbox +moves mail from a +.IR maildir -format +directory to an +.IR mbox -format +file. + +You must supply three environment variables to +.BR maildir2mbox : +.B MAILDIR +is the name of your +.I maildir +directory; +.B MAIL +is the name of your +.I mbox +file; +and +.B MAILTMP +is a temporary file that +.B maildir2mbox +can overwrite. +.B MAILTMP +and +.B MAIL +must be on the same filesystem. + +.B maildir2mbox +is reliable: +it will not remove messages +from +.B MAILDIR +until the messages have been successfully appended to +.BR MAIL . + +.B maildir2mbox +locks +.B MAIL +to protect against simultaneous access by a mail reader. +This locking system does not protect against simultaneous access +by another +.BR maildir2mbox ; +you should run only one +.B maildir2mbox +at a time. +.SH "SEE ALSO" +maildir(5) diff --git a/maildir2mbox.c b/maildir2mbox.c new file mode 100644 index 0000000..7364441 --- /dev/null +++ b/maildir2mbox.c @@ -0,0 +1,162 @@ +#include "readwrite.h" +#include "prioq.h" +#include "env.h" +#include "stralloc.h" +#include "subfd.h" +#include "substdio.h" +#include "getln.h" +#include "error.h" +#include "open.h" +#include "lock.h" +#include "gfrom.h" +#include "str.h" +#include "exit.h" +#include "myctime.h" +#include "maildir.h" + +char *mbox; +char *mboxtmp; + +stralloc filenames = {0}; +prioq pq = {0}; +prioq pq2 = {0}; + +stralloc line = {0}; + +stralloc ufline = {0}; + +char inbuf[SUBSTDIO_INSIZE]; +char outbuf[SUBSTDIO_OUTSIZE]; + +#define FATAL "maildir2mbox: fatal: " +#define WARNING "maildir2mbox: warning: " + +void die_nomem() { strerr_die2x(111,FATAL,"out of memory"); } + +void main() +{ + substdio ssin; + substdio ssout; + struct prioq_elt pe; + int fdoldmbox; + int fdnewmbox; + int fd; + int match; + int fdlock; + + umask(077); + + mbox = env_get("MAIL"); + if (!mbox) strerr_die2x(111,FATAL,"MAIL not set"); + mboxtmp = env_get("MAILTMP"); + if (!mboxtmp) strerr_die2x(111,FATAL,"MAILTMP not set"); + + if (maildir_chdir() == -1) + strerr_die1(111,FATAL,&maildir_chdir_err); + maildir_clean(&filenames); + if (maildir_scan(&pq,&filenames,1,1) == -1) + strerr_die1(111,FATAL,&maildir_scan_err); + + if (!prioq_min(&pq,&pe)) _exit(0); /* nothing new */ + + fdlock = open_append(mbox); + if (fdlock == -1) + strerr_die4sys(111,FATAL,"unable to lock ",mbox,": "); + if (lock_ex(fdlock) == -1) + strerr_die4sys(111,FATAL,"unable to lock ",mbox,": "); + + fdoldmbox = open_read(mbox); + if (fdoldmbox == -1) + strerr_die4sys(111,FATAL,"unable to read ",mbox,": "); + + fdnewmbox = open_trunc(mboxtmp); + if (fdnewmbox == -1) + strerr_die4sys(111,FATAL,"unable to create ",mboxtmp,": "); + + substdio_fdbuf(&ssin,read,fdoldmbox,inbuf,sizeof(inbuf)); + substdio_fdbuf(&ssout,write,fdnewmbox,outbuf,sizeof(outbuf)); + + switch(substdio_copy(&ssout,&ssin)) + { + case -2: strerr_die4sys(111,FATAL,"unable to read ",mbox,": "); + case -3: strerr_die4sys(111,FATAL,"unable to write to ",mboxtmp,": "); + } + + while (prioq_min(&pq,&pe)) + { + prioq_delmin(&pq); + if (!prioq_insert(&pq2,&pe)) die_nomem(); + + fd = open_read(filenames.s + pe.id); + if (fd == -1) + strerr_die4sys(111,FATAL,"unable to read $MAILDIR/",filenames.s + pe.id,": "); + substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf)); + + if (getln(&ssin,&line,&match,'\n') != 0) + strerr_die4sys(111,FATAL,"unable to read $MAILDIR/",filenames.s + pe.id,": "); + + if (!stralloc_copys(&ufline,"From XXX ")) die_nomem(); + if (match) + if (stralloc_starts(&line,"Return-Path: <")) + { + if (line.s[14] == '>') + { + if (!stralloc_copys(&ufline,"From MAILER-DAEMON ")) die_nomem(); + } + else + { + int i; + if (!stralloc_ready(&ufline,line.len)) die_nomem(); + if (!stralloc_copys(&ufline,"From ")) die_nomem(); + for (i = 14;i < line.len - 2;++i) + if ((line.s[i] == ' ') || (line.s[i] == '\t')) + ufline.s[ufline.len++] = '-'; + else + ufline.s[ufline.len++] = line.s[i]; + if (!stralloc_cats(&ufline," ")) die_nomem(); + } + } + if (!stralloc_cats(&ufline,myctime(pe.dt))) die_nomem(); + if (substdio_put(&ssout,ufline.s,ufline.len) == -1) + strerr_die4sys(111,FATAL,"unable to write to ",mboxtmp,": "); + + while (match && line.len) + { + if (gfrom(line.s,line.len)) + if (substdio_puts(&ssout,">") == -1) + strerr_die4sys(111,FATAL,"unable to write to ",mboxtmp,": "); + if (substdio_put(&ssout,line.s,line.len) == -1) + strerr_die4sys(111,FATAL,"unable to write to ",mboxtmp,": "); + if (!match) + { + if (substdio_puts(&ssout,"\n") == -1) + strerr_die4sys(111,FATAL,"unable to write to ",mboxtmp,": "); + break; + } + if (getln(&ssin,&line,&match,'\n') != 0) + strerr_die4sys(111,FATAL,"unable to read $MAILDIR/",filenames.s + pe.id,": "); + } + if (substdio_puts(&ssout,"\n")) + strerr_die4sys(111,FATAL,"unable to write to ",mboxtmp,": "); + + close(fd); + } + + if (substdio_flush(&ssout) == -1) + strerr_die4sys(111,FATAL,"unable to write to ",mboxtmp,": "); + if (fsync(fdnewmbox) == -1) + strerr_die4sys(111,FATAL,"unable to write to ",mboxtmp,": "); + if (close(fdnewmbox) == -1) /* NFS dorks */ + strerr_die4sys(111,FATAL,"unable to write to ",mboxtmp,": "); + if (rename(mboxtmp,mbox) == -1) + strerr_die6(111,FATAL,"unable to move ",mboxtmp," to ",mbox,": ",&strerr_sys); + + while (prioq_min(&pq2,&pe)) + { + prioq_delmin(&pq2); + if (unlink(filenames.s + pe.id) == -1) + strerr_warn4(WARNING,"$MAILDIR/",filenames.s + pe.id," will be delivered twice; unable to unlink: ",&strerr_sys); + } + + _exit(0); +} diff --git a/maildirmake.1 b/maildirmake.1 new file mode 100644 index 0000000..e5bd042 --- /dev/null +++ b/maildirmake.1 @@ -0,0 +1,15 @@ +.TH maildirmake 1 +.SH NAME +maildirmake \- create a maildir for incoming mail +.SH SYNOPSIS +.B maildirmake +.I dir +.SH DESCRIPTION +.B maildirmake +makes a new directory, +.IR dir , +in +.B maildir +format. +.SH "SEE ALSO" +maildir(5) diff --git a/maildirmake.c b/maildirmake.c new file mode 100644 index 0000000..9863fef --- /dev/null +++ b/maildirmake.c @@ -0,0 +1,24 @@ +#include "strerr.h" +#include "exit.h" + +#define FATAL "maildirmake: fatal: " + +void main(argc,argv) +int argc; +char **argv; +{ + umask(077); + if (!argv[1]) + strerr_die1x(100,"maildirmake: usage: maildirmake name"); + if (mkdir(argv[1],0700) == -1) + strerr_die4sys(111,FATAL,"unable to mkdir ",argv[1],": "); + if (chdir(argv[1]) == -1) + strerr_die4sys(111,FATAL,"unable to chdir to ",argv[1],": "); + if (mkdir("tmp",0700) == -1) + strerr_die4sys(111,FATAL,"unable to mkdir ",argv[1],"/tmp: "); + if (mkdir("new",0700) == -1) + strerr_die4sys(111,FATAL,"unable to mkdir ",argv[1],"/new: "); + if (mkdir("cur",0700) == -1) + strerr_die4sys(111,FATAL,"unable to mkdir ",argv[1],"/cur: "); + _exit(0); +} diff --git a/maildirwatch.1 b/maildirwatch.1 new file mode 100644 index 0000000..d1d70a0 --- /dev/null +++ b/maildirwatch.1 @@ -0,0 +1,23 @@ +.TH maildirwatch 1 +.SH NAME +maildirwatch \- look for new mail in a maildir +.SH SYNOPSIS +.B maildirwatch +.SH DESCRIPTION +.B maildirwatch +watches your +.I maildir +for new mail. +You must supply a +.B MAILDIR +environment variable +with the name of your +.I maildir +directory. + +.B maildirwatch +prints a new mail summary twice per minute. +It is designed to run inside a (VT100-compatible) window; +it clears the window before each summary. +.SH "SEE ALSO" +maildir(5) diff --git a/maildirwatch.c b/maildirwatch.c new file mode 100644 index 0000000..40d8322 --- /dev/null +++ b/maildirwatch.c @@ -0,0 +1,125 @@ +#include "getln.h" +#include "substdio.h" +#include "subfd.h" +#include "prioq.h" +#include "stralloc.h" +#include "str.h" +#include "exit.h" +#include "hfield.h" +#include "readwrite.h" +#include "open.h" +#include "headerbody.h" +#include "maildir.h" + +#define FATAL "maildirwatch: fatal: " + +void die_nomem() { strerr_die2x(111,FATAL,"out of memory"); } + +stralloc recipient = {0}; +stralloc sender = {0}; +stralloc fromline = {0}; +stralloc text = {0}; + +void addtext(s,n) char *s; int n; +{ + if (!stralloc_catb(&text,s,n)) die_nomem(); + if (text.len > 158) text.len = 158; +} +void dobody(h) stralloc *h; { addtext(h->s,h->len); } +void doheader(h) stralloc *h; +{ + int i; + switch(hfield_known(h->s,h->len)) + { + case H_SUBJECT: + i = hfield_skipname(h->s,h->len); + addtext(h->s + i,h->len - i); + break; + case H_DELIVEREDTO: + i = hfield_skipname(h->s,h->len); + if (i < h->len) + if (!stralloc_copyb(&recipient,h->s + i,h->len - i - 1)) die_nomem(); + break; + case H_RETURNPATH: + i = hfield_skipname(h->s,h->len); + if (i < h->len) + if (!stralloc_copyb(&sender,h->s + i,h->len - i - 1)) die_nomem(); + break; + case H_FROM: + if (!stralloc_copyb(&fromline,h->s,h->len - 1)) die_nomem(); + break; + } +} +void finishheader() { ; } + +stralloc filenames = {0}; +prioq pq = {0}; + +char inbuf[SUBSTDIO_INSIZE]; +substdio ssin; + +void main() +{ + struct prioq_elt pe; + int fd; + int i; + + if (maildir_chdir() == -1) + strerr_die1(111,FATAL,&maildir_chdir_err); + + for (;;) + { + maildir_clean(&filenames); + if (maildir_scan(&pq,&filenames,1,0) == -1) + strerr_die1(111,FATAL,&maildir_scan_err); + + substdio_putsflush(subfdout,"\033[;H\033[;J"); + + while (prioq_min(&pq,&pe)) + { + prioq_delmin(&pq); + + fd = open_read(filenames.s + pe.id); + if (fd == -1) continue; + substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf)); + + if (!stralloc_copys(&sender,"?")) die_nomem(); + if (!stralloc_copys(&recipient,"?")) die_nomem(); + if (!stralloc_copys(&fromline,"")) die_nomem(); + if (!stralloc_copys(&text,"")) die_nomem(); + if (headerbody(&ssin,doheader,finishheader,dobody) == -1) + strerr_die2x(111,FATAL,"trouble reading new message"); + + for (i = 0;i < fromline.len;++i) + if ((fromline.s[i] < 32) || (fromline.s[i] > 126)) + fromline.s[i] = '/'; + for (i = 0;i < sender.len;++i) + if ((sender.s[i] < 32) || (sender.s[i] > 126)) + sender.s[i] = '?'; + for (i = 0;i < recipient.len;++i) + if ((recipient.s[i] < 32) || (recipient.s[i] > 126)) + recipient.s[i] = '?'; + for (i = 0;i < text.len;++i) + if ((text.s[i] < 32) || (text.s[i] > 126)) + text.s[i] = '/'; + substdio_puts(subfdout,"FROM "); + substdio_put(subfdout,sender.s,sender.len); + substdio_puts(subfdout," TO <"); + substdio_put(subfdout,recipient.s,recipient.len); + substdio_puts(subfdout,">\n"); + if (fromline.len) + { + substdio_puts(subfdout,"\033[1m"); + substdio_put(subfdout,fromline.s,fromline.len); + substdio_puts(subfdout,"\033[0m\n"); + } + substdio_put(subfdout,text.s,text.len); + substdio_puts(subfdout,"\n\n"); + + close(fd); + } + + substdio_flush(subfdout); + sleep(30); + } +} diff --git a/mailsubj.1 b/mailsubj.1 new file mode 100644 index 0000000..687b8e8 --- /dev/null +++ b/mailsubj.1 @@ -0,0 +1,38 @@ +.TH mailsubj 1 +.SH NAME +mailsubj \- send a mail message with a subject line +.SH SYNOPSIS +.B mailsubj +.I subject +.I recip ... +.SH DESCRIPTION +.B mailsubj +inserts +.I subject +and the list of +.IR recip s +into a mail message: + +.EX + Subject: subject +.br + To: recip ... +.br + +.br + body +.EE + +.B mailsubj +reads the body of the message from its standard input. +Then it sends the message. + +Note that +.I subject +and +.I recip +must be quoted properly for the message header. +.SH "SEE ALSO" +addresses(5), +qmail-header(8), +qmail-inject(8) diff --git a/mailsubj.sh b/mailsubj.sh new file mode 100644 index 0000000..a93d3f4 --- /dev/null +++ b/mailsubj.sh @@ -0,0 +1,7 @@ +subject="$1" +shift +( echo Subject: "$subject" + echo To: ${1+"$@"} + echo '' + cat +) | QMAIL/bin/qmail-inject diff --git a/make-compile.sh b/make-compile.sh new file mode 100644 index 0000000..a1eb501 --- /dev/null +++ b/make-compile.sh @@ -0,0 +1 @@ +echo exec "$CC" -c '${1+"$@"}' diff --git a/make-load.sh b/make-load.sh new file mode 100644 index 0000000..de07d2e --- /dev/null +++ b/make-load.sh @@ -0,0 +1,2 @@ +echo 'main="$1"; shift' +echo exec "$LD" '-o "$main" "$main".o ${1+"$@"}' diff --git a/make-makelib.sh b/make-makelib.sh new file mode 100644 index 0000000..d6b7c8c --- /dev/null +++ b/make-makelib.sh @@ -0,0 +1,16 @@ +echo 'main="$1"; shift' +echo 'rm -f "$main"' +echo 'ar cr "$main" ${1+"$@"}' + +case "$1" in +sunos-5.*) ;; +unix_sv*) ;; +irix64-*) ;; +irix-*) ;; +dgux-*) ;; +hp-ux-*) ;; +sco*) ;; +*) + echo 'ranlib "$main"' + ;; +esac diff --git a/mbox.5 b/mbox.5 new file mode 100644 index 0000000..de1f837 --- /dev/null +++ b/mbox.5 @@ -0,0 +1,235 @@ +.TH mbox 5 +.SH "NAME" +mbox \- file containing mail messages +.SH "INTRODUCTION" +The most common format for storage of mail messages is +.I mbox +format. +An +.I mbox +is a single file containing zero or more mail messages. +.SH "MESSAGE FORMAT" +A message encoded in +.I mbox +format begins with a +.B From_ +line, continues with a series of +.B \fRnon-\fBFrom_ +lines, +and ends with a blank line. +A +.B From_ +line means any line that begins with the characters +F, r, o, m, space: + +.EX + From god@heaven.af.mil Sat Jan 3 01:05:34 1996 +.br + Return-Path: +.br + Delivered-To: djb@silverton.berkeley.edu +.br + Date: 3 Jan 1996 01:05:34 -0000 +.br + From: God +.br + To: djb@silverton.berkeley.edu (D. J. Bernstein) +.br + +.br + How's that mail system project coming along? +.br + +.EE + +The final line is a completely blank line (no spaces or tabs). +Notice that blank lines may also appear elsewhere in the message. + +The +.B From_ +line always looks like +.B From +.I envsender +.I date +.IR moreinfo . +.I envsender +is one word, without spaces or tabs; +it is usually the envelope sender of the message. +.I date +is the delivery date of the message. +It always contains exactly 24 characters in +.B asctime +format. +.I moreinfo +is optional; it may contain arbitrary information. + +Between the +.B From_ +line and the blank line is a message in RFC 822 format, +as described in +.BR qmail-header(5) , +subject to +.B >From quoting +as described below. +.SH "HOW A MESSAGE IS DELIVERED" +Here is how a program appends a message to an +.I mbox +file. + +It first creates a +.B From_ +line given the message's envelope sender and the current date. +If the envelope sender is empty (i.e., if this is a bounce message), +the program uses +.B MAILER-DAEMON +instead. +If the envelope sender contains spaces, tabs, or newlines, +the program replaces them with hyphens. + +The program then copies the message, applying +.B >From quoting +to each line. +.B >From quoting +ensures that the resulting lines are not +.B From_ +lines: +the program prepends a +.B > +to any +.B From_ +line, +.B >From_ +line, +.B >>From_ +line, +.B >>>From_ +line, +etc. + +Finally the program appends a blank line to the message. +If the last line of the message was a partial line, +it writes two newlines; +otherwise it writes one. +.SH "HOW A MESSAGE IS READ" +A reader scans through an +.I mbox +file looking for +.B From_ +lines. +Any +.B From_ +line marks the beginning of a message. +The reader should not attempt to take advantage of the fact that every +.B From_ +line (past the beginning of the file) +is preceded by a blank line. + +Once the reader finds a message, +it extracts a (possibly corrupted) envelope sender +and delivery date out of the +.B From_ +line. +It then reads until the next +.B From_ +line or end of file, whichever comes first. +It strips off the final blank line +and +deletes the +quoting of +.B >From_ +lines and +.B >>From_ +lines and so on. +The result is an RFC 822 message. +.SH "COMMON MBOX VARIANTS" +There are many variants of +.I mbox +format. +The variant described above is +.I mboxrd +format, popularized by Rahul Dhesi in June 1995. + +The original +.I mboxo +format quotes only +.B From_ +lines, not +.B >From_ +lines. +As a result it is impossible to tell whether + +.EX + From: djb@silverton.berkeley.edu (D. J. Bernstein) +.br + To: god@heaven.af.mil +.br + +.br + >From now through August I'll be doing beta testing. +.br + Thanks for your interest. +.EE + +was quoted in the original message. +An +.I mboxrd +reader will always strip off the quoting. + +.I mboxcl +format is like +.I mboxo +format, but includes a Content-Length field with the +number of bytes in the message. +.I mboxcl2 +format is like +.I mboxcl +but has no +.B >From +quoting. +These formats are used by SVR4 mailers. +.I mboxcl2 +cannot be read safely by +.I mboxrd +readers. +.SH "UNSPECIFIED DETAILS" +There are many locking mechanisms for +.I mbox +files. +.B qmail-local +always uses +.B flock +on systems that have it, otherwise +.BR lockf . + +The delivery date in a +.B From_ +line does not specify a time zone. +.B qmail-local +always creates the delivery date in GMT +so that +.I mbox +files can be safely transported from one time zone to another. + +If the mtime on a nonempty +.I mbox +file is greater than the atime, +the file has new mail. +If the mtime is smaller than the atime, +the new mail has been read. +If the atime equals the mtime, +there is no way to tell whether the file has new mail, +since +.B qmail-local +takes much less than a second to run. +One solution is for a mail reader to artificially set the +atime to the mtime plus 1. +Then the file has new mail if and only if the atime is +less than or equal to the mtime. + +Some mail readers place +.B Status +fields in each message to indicate which messages have been read. +.SH "SEE ALSO" +maildir(5), +qmail-header(5), +qmail-local(8) diff --git a/myctime.c b/myctime.c new file mode 100644 index 0000000..5003c39 --- /dev/null +++ b/myctime.c @@ -0,0 +1,37 @@ +#include "datetime.h" +#include "fmt.h" +#include "myctime.h" + +static char *daytab[7] = { +"Sun","Mon","Tue","Wed","Thu","Fri","Sat" +}; +static char *montab[12] = { +"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" +}; + +static char result[30]; + +char *myctime(t) +datetime_sec t; +{ + struct datetime dt; + unsigned int len; + datetime_tai(&dt,t); + len = 0; + len += fmt_str(result + len,daytab[dt.wday]); + result[len++] = ' '; + len += fmt_str(result + len,montab[dt.mon]); + result[len++] = ' '; + len += fmt_uint0(result + len,dt.mday,2); + result[len++] = ' '; + len += fmt_uint0(result + len,dt.hour,2); + result[len++] = ':'; + len += fmt_uint0(result + len,dt.min,2); + result[len++] = ':'; + len += fmt_uint0(result + len,dt.sec,2); + result[len++] = ' '; + len += fmt_uint(result + len,1900 + dt.year); + result[len++] = '\n'; + result[len++] = 0; + return result; +} diff --git a/myctime.h b/myctime.h new file mode 100644 index 0000000..ec1d03c --- /dev/null +++ b/myctime.h @@ -0,0 +1,6 @@ +#ifndef MYCTIME_H +#define MYCTIME_H + +extern char *myctime(); + +#endif diff --git a/ndelay.c b/ndelay.c new file mode 100644 index 0000000..438d1d8 --- /dev/null +++ b/ndelay.c @@ -0,0 +1,13 @@ +#include +#include +#include "ndelay.h" + +#ifndef O_NONBLOCK +#define O_NONBLOCK O_NDELAY +#endif + +int ndelay_on(fd) +int fd; +{ + return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) | O_NONBLOCK); +} diff --git a/ndelay.h b/ndelay.h new file mode 100644 index 0000000..68c6bce --- /dev/null +++ b/ndelay.h @@ -0,0 +1,7 @@ +#ifndef NDELAY_H +#define NDELAY_H + +extern int ndelay_on(); +extern int ndelay_off(); + +#endif diff --git a/ndelay_off.c b/ndelay_off.c new file mode 100644 index 0000000..86f8fbf --- /dev/null +++ b/ndelay_off.c @@ -0,0 +1,13 @@ +#include +#include +#include "ndelay.h" + +#ifndef O_NONBLOCK +#define O_NONBLOCK O_NDELAY +#endif + +int ndelay_off(fd) +int fd; +{ + return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) & ~O_NONBLOCK); +} diff --git a/newfield.c b/newfield.c new file mode 100644 index 0000000..78d6bb8 --- /dev/null +++ b/newfield.c @@ -0,0 +1,68 @@ +#include "fmt.h" +#include "datetime.h" +#include "stralloc.h" +#include "date822fmt.h" +#include "newfield.h" + +/* "Date: 26 Sep 1995 04:46:53 -0000\n" */ +stralloc newfield_date = {0}; +/* "Message-ID: <19950926044653.12345.qmail@silverton.berkeley.edu>\n" */ +stralloc newfield_msgid = {0}; + +static unsigned int datefmt(s,when) +char *s; +datetime_sec when; +{ + unsigned int i; + unsigned int len; + struct datetime dt; + datetime_tai(&dt,when); + len = 0; + i = fmt_str(s,"Date: "); len += i; if (s) s += i; + i = date822fmt(s,&dt); len += i; if (s) s += i; + return len; +} + +static unsigned int msgidfmt(s,idhost,idhostlen,when) +char *s; +char *idhost; +int idhostlen; +datetime_sec when; +{ + unsigned int i; + unsigned int len; + struct datetime dt; + datetime_tai(&dt,when); + len = 0; + i = fmt_str(s,"Message-ID: <"); len += i; if (s) s += i; + i = fmt_uint(s,dt.year + 1900); len += i; if (s) s += i; + i = fmt_uint0(s,dt.mon + 1,2); len += i; if (s) s += i; + i = fmt_uint0(s,dt.mday,2); len += i; if (s) s += i; + i = fmt_uint0(s,dt.hour,2); len += i; if (s) s += i; + i = fmt_uint0(s,dt.min,2); len += i; if (s) s += i; + i = fmt_uint0(s,dt.sec,2); len += i; if (s) s += i; + i = fmt_str(s,"."); len += i; if (s) s += i; + i = fmt_uint(s,getpid()); len += i; if (s) s += i; + i = fmt_str(s,".qmail@"); len += i; if (s) s += i; + i = fmt_strn(s,idhost,idhostlen); len += i; if (s) s += i; + i = fmt_str(s,">\n"); len += i; if (s) s += i; + return len; +} + +int newfield_datemake(when) +datetime_sec when; +{ + if (!stralloc_ready(&newfield_date,datefmt(FMT_LEN,when))) return 0; + newfield_date.len = datefmt(newfield_date.s,when); + return 1; +} + +int newfield_msgidmake(idhost,idhostlen,when) +char *idhost; +int idhostlen; +datetime_sec when; +{ + if (!stralloc_ready(&newfield_msgid,msgidfmt(FMT_LEN,idhost,idhostlen,when))) return 0; + newfield_msgid.len = msgidfmt(newfield_msgid.s,idhost,idhostlen,when); + return 1; +} diff --git a/newfield.h b/newfield.h new file mode 100644 index 0000000..a51352a --- /dev/null +++ b/newfield.h @@ -0,0 +1,12 @@ +#ifndef NEWFIELD_H +#define NEWFIELD_H + +#include "stralloc.h" + +extern stralloc newfield_date; +extern int newfield_datemake(); + +extern stralloc newfield_msgid; +extern int newfield_msgidmake(); + +#endif diff --git a/now.3 b/now.3 new file mode 100644 index 0000000..3d845b1 --- /dev/null +++ b/now.3 @@ -0,0 +1,14 @@ +.TH now 3 +.SH NAME +now \- get current time, in seconds +.SH SYNTAX +.B #include + +datetime_sec \fBnow\fP(); +.SH DESCRIPTION +.B now +returns the number of real-time seconds that have elapsed +since the end of 1969 TAI. +.SH "SEE ALSO" +datetime(3), +time(3) diff --git a/now.c b/now.c new file mode 100644 index 0000000..5ce4d90 --- /dev/null +++ b/now.c @@ -0,0 +1,8 @@ +#include +#include "datetime.h" +#include "now.h" + +datetime_sec now() +{ + return time((long *) 0); +} diff --git a/now.h b/now.h new file mode 100644 index 0000000..b8be182 --- /dev/null +++ b/now.h @@ -0,0 +1,8 @@ +#ifndef NOW_H +#define NOW_H + +#include "datetime.h" + +extern datetime_sec now(); + +#endif diff --git a/old-patches/README b/old-patches/README new file mode 100644 index 0000000..40fad8f --- /dev/null +++ b/old-patches/README @@ -0,0 +1,3 @@ +These files are not used. Their purpose is to show the changes between +netqmail-1.04 and netqmail-1.05. The latter is a combination of the +original netqmail-1.04 patch and James Craig Burley's qmail-isoc patch. diff --git a/old-patches/netqmail-1.04.patch b/old-patches/netqmail-1.04.patch new file mode 100644 index 0000000..9b1eb54 --- /dev/null +++ b/old-patches/netqmail-1.04.patch @@ -0,0 +1,414 @@ +diff -urN qmail-1.03/CHANGES netqmail-1.04/CHANGES +--- qmail-1.03/CHANGES Mon Jun 15 04:53:16 1998 ++++ netqmail-1.04/CHANGES Tue Oct 28 08:26:09 2003 +@@ -1,3 +1,17 @@ ++20031027 version: netqmail 1.04. ++20031027 doc: INSTALL points to http://lifewithqmail.org/lwq.html ++20031027 doc: qmail.7 identifies installation as netqmail and points to ++ http://qmail.org/ ++20031027 doc: qmail-queue.8 adds explanation of $QMAILQUEUE ++20031027 doc: qmail-log.5 adds reference to errors from $QMAILQUEUE script ++20031027 doc: FAQ also points to http://cr.yp.to/qmail/faq.html and ++ http://qmail.org/ ++20031027 code: qmail-smtpd identifies itself as netqmail ++20031027 code: if $QMAILQUEUE is set, it's invoked instead of qmail-queue ++20031024 code: changed errno from int to #include. ++20031024 code: fixed .qmail parsing bug. ++20031024 code: recognize 0.0.0.0 as a local address. ++20031024 code: sendmail's -f flag now overrides environment variables. + 19980615 version: qmail 1.03. + 19980614 doc: eliminated BIN.setup in favor of a web page. + 19980614 code: added other auto* to qmail-showctl output. +diff -urN qmail-1.03/FAQ netqmail-1.04/FAQ +--- qmail-1.03/FAQ Mon Jun 15 04:53:16 1998 ++++ netqmail-1.04/FAQ Mon Oct 27 17:33:12 2003 +@@ -1,3 +1,6 @@ ++See http://cr.yp.to/qmail/faq.html for newer FAQs not included in this ++document, and http://qmail.org/ for qmail community contributions. ++ + 1. Controlling the appearance of outgoing messages + 1.1. How do I set up host masquerading? + 1.2. How do I set up user masquerading? +diff -urN qmail-1.03/FILES netqmail-1.04/FILES +--- qmail-1.03/FILES Mon Jun 15 04:53:16 1998 ++++ netqmail-1.04/FILES Mon Oct 27 17:33:12 2003 +@@ -10,6 +10,7 @@ + INSTALL.ids + INSTALL.maildir + INSTALL.mbox ++INSTALL-1.03 + INSTALL.vsm + REMOVE.sendmail + REMOVE.binmail +diff -urN qmail-1.03/INSTALL netqmail-1.04/INSTALL +--- qmail-1.03/INSTALL Mon Jun 15 04:53:16 1998 ++++ netqmail-1.04/INSTALL Mon Oct 27 17:33:12 2003 +@@ -1,84 +1 @@ +-SAVE COPIES OF YOUR OUTGOING MAIL! Like any other piece of software (and +-information generally), the qmail system comes with NO WARRANTY. It's +-much more secure and reliable than sendmail, but that's not saying much. +- +- +-Things you have to decide before starting: +- +-* The qmail home directory, normally /var/qmail. To change this +-directory, edit conf-qmail now. +- +-* The names of the qmail users and the qmail groups. To change these +-names, edit conf-users and conf-groups now. +- +- +-To create /var/qmail and configure qmail (won't interfere with sendmail): +- +- 1. Create the qmail home directory: +- # mkdir /var/qmail +- +- 2. Read INSTALL.ids. You must set up the qmail group and the qmail +- users before compiling the programs. +- +- 3. Compile the programs and create the qmail directory tree: +- # make setup check +- +- 4. Read INSTALL.ctl and FAQ. Minimal survival command: +- # ./config +- +- 5. Read INSTALL.alias. Minimal survival command: +- # (cd ~alias; touch .qmail-postmaster .qmail-mailer-daemon .qmail-root) +- # chmod 644 ~alias/.qmail* +- +- 6. Read INSTALL.mbox and INSTALL.vsm. +- +- 7. Read INSTALL.maildir. +- +- 8. Copy /var/qmail/boot/home (or proc) to /var/qmail/rc. +- +- +-To test qmail deliveries (won't interfere with sendmail): +- +- 9. Enable deliveries of messages injected into qmail: +- # csh -cf '/var/qmail/rc &' +- +-10. Read TEST.deliver. +- +- +-To upgrade from sendmail to qmail: +- +-11. Read SENDMAIL. This is what your users will want to know about the +- switch from sendmail to qmail. +- +-12. Read REMOVE.sendmail. You must remove sendmail before installing +- qmail. +- +-13. Read REMOVE.binmail. +- +-14. Add +- csh -cf '/var/qmail/rc &' +- to your boot scripts, so that the qmail daemons are restarted +- whenever your system reboots. Make sure you include the &. +- +-15. Make qmail's ``sendmail'' wrapper available to MUAs: +- # ln -s /var/qmail/bin/sendmail /usr/lib/sendmail +- # ln -s /var/qmail/bin/sendmail /usr/sbin/sendmail +- /usr/sbin might not exist on your system. +- +-16. Set up qmail-smtpd in /etc/inetd.conf (all on one line): +- smtp stream tcp nowait qmaild /var/qmail/bin/tcp-env +- tcp-env /var/qmail/bin/qmail-smtpd +- +-17. Reboot. (Or kill -HUP your inetd and make sure the qmail daemons +- are running.) +- +-18. Read TEST.receive. +- +- +- +-That's it! To report success: +- % ( echo 'First M. Last'; cat `cat SYSDEPS` ) | mail djb-qst@cr.yp.to +-Replace First M. Last with your name. +- +-If you have questions about qmail, join the qmail mailing list; see +-http://pobox.com/~djb/qmail.html. ++See http://lifewithqmail.org/lwq.html +diff -urN qmail-1.03/Makefile netqmail-1.04/Makefile +--- qmail-1.03/Makefile Mon Jun 15 04:53:16 1998 ++++ netqmail-1.04/Makefile Mon Oct 27 17:33:13 2003 +@@ -1483,12 +1483,12 @@ + trigger.o fmtqfn.o quote.o now.o readsubdir.o qmail.o date822fmt.o \ + datetime.a case.a ndelay.a getln.a wait.a seek.a fd.a sig.a open.a \ + lock.a stralloc.a alloc.a substdio.a error.a str.a fs.a auto_qmail.o \ +-auto_split.o ++auto_split.o env.a + ./load qmail-send qsutil.o control.o constmap.o newfield.o \ + prioq.o trigger.o fmtqfn.o quote.o now.o readsubdir.o \ + qmail.o date822fmt.o datetime.a case.a ndelay.a getln.a \ + wait.a seek.a fd.a sig.a open.a lock.a stralloc.a alloc.a \ +- substdio.a error.a str.a fs.a auto_qmail.o auto_split.o ++ substdio.a error.a str.a fs.a auto_qmail.o auto_split.o env.a + + qmail-send.0: \ + qmail-send.8 +diff -urN qmail-1.03/README netqmail-1.04/README +--- qmail-1.03/README Mon Jun 15 04:53:16 1998 ++++ netqmail-1.04/README Mon Oct 27 17:33:13 2003 +@@ -3,6 +3,15 @@ + Copyright 1998 + D. J. Bernstein, qmail@pobox.com + ++netqmail 1.04 ++20031024 ++No copyright claimed for changes from qmail 1.03 to netqmail 1.04 ++Russell Nelson, nelson@qmail.org ++NOTE: netqmail 1.04 is a community-assembled distribution of qmail from ++the official qmail-1.03.tar.gz and patches approved by the community. ++D. J. Bernstein did not participate in, nor has he been asked to ++approve of this distribution. ++ + qmail is a secure, reliable, efficient, simple message transfer agent. + It is meant as a replacement for the entire sendmail-binmail system on + typical Internet-connected UNIX hosts. See BLURB, BLURB2, BLURB3, and +diff -urN qmail-1.03/UPGRADE netqmail-1.04/UPGRADE +--- qmail-1.03/UPGRADE Mon Jun 15 04:53:16 1998 ++++ netqmail-1.04/UPGRADE Mon Oct 27 17:33:13 2003 +@@ -3,7 +3,7 @@ + much more secure and reliable than sendmail, but that's not saying much. + + +-Here's how to upgrade to qmail 1.03. This procedure will overwrite the ++Here's how to upgrade to netqmail 1.04. This procedure will overwrite the + old qmail binaries. Furthermore, it may begin delivering messages from + the queue before you have had a chance to test it. + +@@ -17,7 +17,7 @@ + + + Before starting, compare conf* to your old conf*, and make any necessary +-changes. You can copy conf* from 1.02. ++changes. You can copy conf* from 1.02 or 1.03. + + + How to install: +diff -urN qmail-1.03/VERSION netqmail-1.04/VERSION +--- qmail-1.03/VERSION Mon Jun 15 04:53:16 1998 ++++ netqmail-1.04/VERSION Mon Oct 27 17:33:13 2003 +@@ -1 +1 @@ +-qmail 1.03 ++netqmail 1.04 +diff -urN qmail-1.03/cdb_seek.c netqmail-1.04/cdb_seek.c +--- qmail-1.03/cdb_seek.c Mon Jun 15 04:53:16 1998 ++++ netqmail-1.04/cdb_seek.c Mon Oct 27 17:33:13 2003 +@@ -1,6 +1,5 @@ + #include + #include +-extern int errno; + #include "cdb.h" + + #ifndef SEEK_SET +diff -urN qmail-1.03/dns.c netqmail-1.04/dns.c +--- qmail-1.03/dns.c Mon Jun 15 04:53:16 1998 ++++ netqmail-1.04/dns.c Mon Oct 27 17:33:13 2003 +@@ -7,8 +7,6 @@ + #include + extern int res_query(); + extern int res_search(); +-extern int errno; +-extern int h_errno; + #include "ip.h" + #include "ipalloc.h" + #include "fmt.h" +diff -urN qmail-1.03/error.3 netqmail-1.04/error.3 +--- qmail-1.03/error.3 Mon Jun 15 04:53:16 1998 ++++ netqmail-1.04/error.3 Mon Oct 27 17:33:13 2003 +@@ -3,8 +3,7 @@ + error \- syscall error codes + .SH SYNTAX + .B #include +- +-extern int \fBerrno\fP; ++.B #include + + extern int \fBerror_intr\fP; + .br +diff -urN qmail-1.03/error.h netqmail-1.04/error.h +--- qmail-1.03/error.h Mon Jun 15 04:53:16 1998 ++++ netqmail-1.04/error.h Mon Oct 27 17:33:13 2003 +@@ -1,7 +1,7 @@ + #ifndef ERROR_H + #define ERROR_H + +-extern int errno; ++#include + + extern int error_intr; + extern int error_nomem; +diff -urN qmail-1.03/ipme.c netqmail-1.04/ipme.c +--- qmail-1.03/ipme.c Mon Jun 15 04:53:16 1998 ++++ netqmail-1.04/ipme.c Mon Oct 27 17:33:13 2003 +@@ -46,6 +46,11 @@ + ipme.len = 0; + ix.pref = 0; + ++ /* 0.0.0.0 is a special address which always refers to ++ * "this host, this network", according to RFC 1122, Sec. 3.2.1.3a. ++ */ ++ byte_copy(&ix.ip,4,"\0\0\0\0"); ++ if (!ipalloc_append(&ipme,&ix)) { return 0; } + if ((s = socket(AF_INET,SOCK_STREAM,0)) == -1) return -1; + + len = 256; +diff -urN qmail-1.03/qmail-local.c netqmail-1.04/qmail-local.c +--- qmail-1.03/qmail-local.c Mon Jun 15 04:53:16 1998 ++++ netqmail-1.04/qmail-local.c Mon Oct 27 17:33:13 2003 +@@ -645,7 +645,7 @@ + { + cmds.s[j] = 0; + k = j; +- while ((k > i) && (cmds.s[k - 1] == ' ') || (cmds.s[k - 1] == '\t')) ++ while ((k > i) && ((cmds.s[k - 1] == ' ') || (cmds.s[k - 1] == '\t'))) + cmds.s[--k] = 0; + switch(cmds.s[i]) + { +diff -urN qmail-1.03/qmail-log.5 netqmail-1.04/qmail-log.5 +--- qmail-1.03/qmail-log.5 Mon Jun 15 04:53:16 1998 ++++ netqmail-1.04/qmail-log.5 Mon Oct 27 17:33:13 2003 +@@ -232,6 +232,11 @@ + is unable to queue a bounce message, + usually because the machine is almost out of memory. + It will try again later. ++This can also be caused by incorrect settings of ++.B $QMAILQUEUE ++or errors in a program or script which ++.B $QMAILQUEUE ++points to. + .TP + .B unable to stat ... + .B qmail-send +diff -urN qmail-1.03/qmail-queue.8 netqmail-1.04/qmail-queue.8 +--- qmail-1.03/qmail-queue.8 Mon Jun 15 04:53:16 1998 ++++ netqmail-1.04/qmail-queue.8 Mon Oct 27 17:33:13 2003 +@@ -40,6 +40,12 @@ + However, the recipients probably expect to see a proper header, + as described in + .BR qmail-header(5) . ++ ++Programs included with qmail which invoke ++.B qmail-queue ++will invoke the contents of ++.B $QMAILQUEUE ++instead, if that environment variable is set. + .SH "FILESYSTEM RESTRICTIONS" + .B qmail-queue + imposes two constraints on the queue structure: +diff -urN qmail-1.03/qmail-smtpd.c netqmail-1.04/qmail-smtpd.c +--- qmail-1.03/qmail-smtpd.c Mon Jun 15 04:53:16 1998 ++++ netqmail-1.04/qmail-smtpd.c Mon Oct 27 17:33:13 2003 +@@ -69,7 +69,7 @@ + } + void smtp_help() + { +- out("214 qmail home page: http://pobox.com/~djb/qmail.html\r\n"); ++ out("214 netqmail home page: http://qmail.org/netqmail\r\n"); + } + void smtp_quit() + { +diff -urN qmail-1.03/qmail.7 netqmail-1.04/qmail.7 +--- qmail-1.03/qmail.7 Mon Jun 15 04:53:16 1998 ++++ netqmail-1.04/qmail.7 Mon Oct 27 17:33:13 2003 +@@ -55,12 +55,14 @@ + and + .BR qmail-pop3d (8). + +-This documentation describes version +-1.03 ++This documentation describes netqmail version ++1.04 + of + .BR qmail . + See + .B http://pobox.com/~djb/qmail.html + for other + .BR qmail -related +-software. ++software, and ++.B http://qmail.org/ ++for other qmail community contributions. +diff -urN qmail-1.03/qmail.c netqmail-1.04/qmail.c +--- qmail-1.03/qmail.c Mon Jun 15 04:53:16 1998 ++++ netqmail-1.04/qmail.c Mon Oct 27 17:33:13 2003 +@@ -6,14 +6,25 @@ + #include "fd.h" + #include "qmail.h" + #include "auto_qmail.h" ++#include "env.h" + +-static char *binqqargs[2] = { "bin/qmail-queue", 0 } ; ++static char *binqqargs[2] = { 0, 0 } ; ++ ++static void setup_qqargs() ++{ ++ if(!binqqargs[0]) ++ binqqargs[0] = env_get("QMAILQUEUE"); ++ if(!binqqargs[0]) ++ binqqargs[0] = "bin/qmail-queue"; ++} + + int qmail_open(qq) + struct qmail *qq; + { + int pim[2]; + int pie[2]; ++ ++ setup_qqargs(); + + if (pipe(pim) == -1) return -1; + if (pipe(pie) == -1) { close(pim[0]); close(pim[1]); return -1; } +diff -urN qmail-1.03/sendmail.c netqmail-1.04/sendmail.c +--- qmail-1.03/sendmail.c Mon Jun 15 04:53:16 1998 ++++ netqmail-1.04/sendmail.c Mon Oct 27 17:33:13 2003 +@@ -45,6 +45,38 @@ + _exit(111); + } + ++void do_sender(s) ++const char *s; ++{ ++ char *x; ++ int n; ++ int a; ++ int i; ++ ++ env_unset("QMAILNAME"); ++ env_unset("MAILNAME"); ++ env_unset("NAME"); ++ env_unset("QMAILHOST"); ++ env_unset("MAILHOST"); ++ ++ n = str_len(s); ++ a = str_rchr(s, '@'); ++ if (a == n) ++ { ++ env_put2("QMAILUSER", s); ++ return; ++ } ++ env_put2("QMAILHOST", s + a + 1); ++ ++ x = (char *) alloc((a + 1) * sizeof(char)); ++ if (!x) nomem(); ++ for (i = 0; i < a; i++) ++ x[i] = s[i]; ++ x[i] = 0; ++ env_put2("QMAILUSER", x); ++ alloc_free(x); ++} ++ + int flagh; + char *sender; + +@@ -118,6 +150,7 @@ + if (sender) { + *arg++ = "-f"; + *arg++ = sender; ++ do_sender(sender); + } + *arg++ = "--"; + for (i = 0;i < argc;++i) *arg++ = argv[i]; + diff --git a/old-patches/qmail-isoc.patch b/old-patches/qmail-isoc.patch new file mode 100644 index 0000000..6594dde --- /dev/null +++ b/old-patches/qmail-isoc.patch @@ -0,0 +1,362 @@ +This patch is Copyright (C) 2004 by James Craig Burley. License +below. + +2004-01-19 0040 EST James Craig Burley + +This patch improves ISO C conformance of qmail code -- specifically, +of qmail-lspawn, qmail-newmrh, qmail-newu, qmail-pop3d, qmail-popup, +qmail-rspawn, and qmail-smtpd. This fixes two known bugs: + + - qmail-smtpd can be crashed by a client sending a sufficiently + long (e.g. 2GB) header line in an email. + + - qmail_lspawn, qmail-newmrh, qmail-newu, and qmail-rspawn might + crash or otherwise misbehave on hosts with a smaller "int" type + than "char *" type, e.g. 64-bit hosts with 32-bit "int"s. + +The other changes are unlikely to have any effect, except possibly on +unusual architectures and/or in the presence of rare optimizations. + +This is Version 1 of this patch. It adds the changes to +cdbmake_add.c, spawn.c, and, correspondingly, Makefile, which pertain +to the second bug listed above. + +See all my qmail patches at . + +License: + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +*** qmail-1.03/cdbmake_add.c Mon Jun 15 06:53:16 1998 +--- qmail-1.03.0/cdbmake_add.c Mon Jan 19 00:35:38 2004 +*************** +*** 1,2 **** +--- 1,3 ---- ++ #include "alloc.h" + #include "cdbmake.h" + +*** qmail-1.03/Makefile Mon Jun 15 06:53:16 1998 +--- qmail-1.03.0/Makefile Mon Jan 19 00:35:35 2004 +*************** makelib cdbmake_pack.o cdbmake_hash.o cd +*** 264,268 **** + + cdbmake_add.o: \ +! compile cdbmake_add.c cdbmake.h uint32.h + ./compile cdbmake_add.c + +--- 264,268 ---- + + cdbmake_add.o: \ +! compile cdbmake_add.c cdbmake.h alloc.h uint32.h + ./compile cdbmake_add.c + +*************** trylsock.c compile load +*** 1893,1897 **** + spawn.o: \ + compile chkspawn spawn.c sig.h wait.h substdio.h byte.h str.h \ +! stralloc.h gen_alloc.h select.h exit.h coe.h open.h error.h \ + auto_qmail.h auto_uids.h auto_spawn.h + ./chkspawn +--- 1893,1897 ---- + spawn.o: \ + compile chkspawn spawn.c sig.h wait.h substdio.h byte.h str.h \ +! stralloc.h gen_alloc.h select.h exit.h alloc.h coe.h open.h error.h \ + auto_qmail.h auto_uids.h auto_spawn.h + ./chkspawn +*** qmail-1.03/qmail-pop3d.c Mon Jun 15 06:53:16 1998 +--- qmail-1.03.0/qmail-pop3d.c Thu Jan 15 22:08:57 2004 +*************** void die_scan() { err("unable to scan $H +*** 67,71 **** + + void err_syntax() { err("syntax error"); } +! void err_unimpl() { err("unimplemented"); } + void err_deleted() { err("already deleted"); } + void err_nozero() { err("messages are counted from 1"); } +--- 67,71 ---- + + void err_syntax() { err("syntax error"); } +! void err_unimpl(arg) char *arg; { err("unimplemented"); } + void err_deleted() { err("already deleted"); } + void err_nozero() { err("messages are counted from 1"); } +*************** void err_nosuch() { err("unable to open +*** 74,78 **** + void err_nounlink() { err("unable to unlink all deleted messages"); } + +! void okay() { puts("+OK \r\n"); flush(); } + + void printfn(fn) char *fn; +--- 74,78 ---- + void err_nounlink() { err("unable to unlink all deleted messages"); } + +! void okay(arg) char *arg; { puts("+OK \r\n"); flush(); } + + void printfn(fn) char *fn; +*************** void getlist() +*** 147,151 **** + } + +! void pop3_stat() + { + int i; +--- 147,151 ---- + } + +! void pop3_stat(arg) char *arg; + { + int i; +*************** void pop3_stat() +*** 162,174 **** + } + +! void pop3_rset() + { + int i; + for (i = 0;i < numm;++i) m[i].flagdeleted = 0; + last = 0; +! okay(); + } + +! void pop3_last() + { + puts("+OK "); +--- 162,174 ---- + } + +! void pop3_rset(arg) char *arg; + { + int i; + for (i = 0;i < numm;++i) m[i].flagdeleted = 0; + last = 0; +! okay(0); + } + +! void pop3_last(arg) char *arg; + { + puts("+OK "); +*************** void pop3_last() +*** 178,182 **** + } + +! void pop3_quit() + { + int i; +--- 178,182 ---- + } + +! void pop3_quit(arg) char *arg; + { + int i; +*************** void pop3_quit() +*** 193,197 **** + rename(m[i].fn,line.s); /* if it fails, bummer */ + } +! okay(); + die(); + } +--- 193,197 ---- + rename(m[i].fn,line.s); /* if it fails, bummer */ + } +! okay(0); + die(); + } +*************** void pop3_dele(arg) char *arg; +*** 215,219 **** + m[i].flagdeleted = 1; + if (i + 1 > last) last = i + 1; +! okay(); + } + +--- 215,219 ---- + m[i].flagdeleted = 1; + if (i + 1 > last) last = i + 1; +! okay(0); + } + +*************** void dolisting(arg,flaguidl) char *arg; +*** 239,243 **** + } + else { +! okay(); + for (i = 0;i < numm;++i) + if (!m[i].flagdeleted) +--- 239,243 ---- + } + else { +! okay(0); + for (i = 0;i < numm;++i) + if (!m[i].flagdeleted) +*************** void pop3_top(arg) char *arg; +*** 268,272 **** + fd = open_read(m[i].fn); + if (fd == -1) { err_nosuch(); return; } +! okay(); + substdio_fdbuf(&ssmsg,read,fd,ssmsgbuf,sizeof(ssmsgbuf)); + blast(&ssmsg,limit); +--- 268,272 ---- + fd = open_read(m[i].fn); + if (fd == -1) { err_nosuch(); return; } +! okay(0); + substdio_fdbuf(&ssmsg,read,fd,ssmsgbuf,sizeof(ssmsgbuf)); + blast(&ssmsg,limit); +*************** char **argv; +*** 300,304 **** + getlist(); + +! okay(); + commands(&ssin,pop3commands); + die(); +--- 300,304 ---- + getlist(); + +! okay(0); + commands(&ssin,pop3commands); + die(); +*** qmail-1.03/qmail-popup.c Mon Jun 15 06:53:16 1998 +--- qmail-1.03.0/qmail-popup.c Thu Jan 15 22:09:21 2004 +*************** void die_badauth() { err("authorization +*** 65,72 **** + void err_syntax() { err("syntax error"); } + void err_wantuser() { err("USER first"); } +! void err_authoriz() { err("authorization first"); } + +! void okay() { puts("+OK \r\n"); flush(); } +! void pop3_quit() { okay(); die(); } + + +--- 65,72 ---- + void err_syntax() { err("syntax error"); } + void err_wantuser() { err("USER first"); } +! void err_authoriz(arg) char *arg; { err("authorization first"); } + +! void okay(arg) char *arg; { puts("+OK \r\n"); flush(); } +! void pop3_quit(arg) char *arg; { okay(0); die(); } + + +*************** void pop3_user(arg) char *arg; +*** 137,141 **** + { + if (!*arg) { err_syntax(); return; } +! okay(); + seenuser = 1; + if (!stralloc_copys(&username,arg)) die_nomem(); +--- 137,141 ---- + { + if (!*arg) { err_syntax(); return; } +! okay(0); + seenuser = 1; + if (!stralloc_copys(&username,arg)) die_nomem(); +*** qmail-1.03/qmail-smtpd.c Mon Jun 15 06:53:16 1998 +--- qmail-1.03.0/qmail-smtpd.c Thu Jan 15 22:12:02 2004 +*************** void straynewline() { out("451 See http: +*** 52,61 **** + void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); } + void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); } +! void err_unimpl() { out("502 unimplemented (#5.5.1)\r\n"); } + void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); } + void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); } + void err_wantrcpt() { out("503 RCPT first (#5.5.1)\r\n"); } +! void err_noop() { out("250 ok\r\n"); } +! void err_vrfy() { out("252 send some mail, i'll try my best\r\n"); } + void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); } + +--- 52,61 ---- + void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); } + void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); } +! void err_unimpl(arg) char *arg; { out("502 unimplemented (#5.5.1)\r\n"); } + void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); } + void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); } + void err_wantrcpt() { out("503 RCPT first (#5.5.1)\r\n"); } +! void err_noop(arg) char *arg; { out("250 ok\r\n"); } +! void err_vrfy(arg) char *arg; { out("252 send some mail, i'll try my best\r\n"); } + void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); } + +*************** void smtp_greet(code) char *code; +*** 68,76 **** + substdio_put(&ssout,greeting.s,greeting.len); + } +! void smtp_help() + { + out("214 qmail home page: http://pobox.com/~djb/qmail.html\r\n"); + } +! void smtp_quit() + { + smtp_greet("221 "); out("\r\n"); flush(); _exit(0); +--- 68,76 ---- + substdio_put(&ssout,greeting.s,greeting.len); + } +! void smtp_help(arg) char *arg; + { + out("214 qmail home page: http://pobox.com/~djb/qmail.html\r\n"); + } +! void smtp_quit(arg) char *arg; + { + smtp_greet("221 "); out("\r\n"); flush(); _exit(0); +*************** void smtp_ehlo(arg) char *arg; +*** 233,237 **** + seenmail = 0; dohelo(arg); + } +! void smtp_rset() + { + seenmail = 0; +--- 233,237 ---- + seenmail = 0; dohelo(arg); + } +! void smtp_rset(arg) char *arg; + { + seenmail = 0; +*************** int *hops; +*** 317,322 **** + if (pos < 2) if (ch != "\r\n"[pos]) flagmaybey = 0; + if (flagmaybey) if (pos == 1) flaginheader = 0; + } +- ++pos; + if (ch == '\n') { pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; } + } +--- 317,322 ---- + if (pos < 2) if (ch != "\r\n"[pos]) flagmaybey = 0; + if (flagmaybey) if (pos == 1) flaginheader = 0; ++ ++pos; + } + if (ch == '\n') { pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; } + } +*************** void acceptmessage(qp) unsigned long qp; +*** 366,370 **** + } + +! void smtp_data() { + int hops; + unsigned long qp; +--- 366,370 ---- + } + +! void smtp_data(arg) char *arg; { + int hops; + unsigned long qp; +*** qmail-1.03/spawn.c Mon Jun 15 06:53:16 1998 +--- qmail-1.03.0/spawn.c Mon Jan 19 00:35:38 2004 +*************** +*** 6,9 **** +--- 6,10 ---- + #include "byte.h" + #include "str.h" ++ #include "alloc.h" + #include "stralloc.h" + #include "select.h" diff --git a/open.h b/open.h new file mode 100644 index 0000000..c903113 --- /dev/null +++ b/open.h @@ -0,0 +1,10 @@ +#ifndef OPEN_H +#define OPEN_H + +extern int open_read(); +extern int open_excl(); +extern int open_append(); +extern int open_trunc(); +extern int open_write(); + +#endif diff --git a/open_append.c b/open_append.c new file mode 100644 index 0000000..93a0862 --- /dev/null +++ b/open_append.c @@ -0,0 +1,6 @@ +#include +#include +#include "open.h" + +int open_append(fn) char *fn; +{ return open(fn,O_WRONLY | O_NDELAY | O_APPEND | O_CREAT,0600); } diff --git a/open_excl.c b/open_excl.c new file mode 100644 index 0000000..887e7c8 --- /dev/null +++ b/open_excl.c @@ -0,0 +1,6 @@ +#include +#include +#include "open.h" + +int open_excl(fn) char *fn; +{ return open(fn,O_WRONLY | O_EXCL | O_CREAT,0644); } diff --git a/open_read.c b/open_read.c new file mode 100644 index 0000000..f503e48 --- /dev/null +++ b/open_read.c @@ -0,0 +1,6 @@ +#include +#include +#include "open.h" + +int open_read(fn) char *fn; +{ return open(fn,O_RDONLY | O_NDELAY); } diff --git a/open_trunc.c b/open_trunc.c new file mode 100644 index 0000000..e275085 --- /dev/null +++ b/open_trunc.c @@ -0,0 +1,6 @@ +#include +#include +#include "open.h" + +int open_trunc(fn) char *fn; +{ return open(fn,O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT,0644); } diff --git a/open_write.c b/open_write.c new file mode 100644 index 0000000..dcdcb95 --- /dev/null +++ b/open_write.c @@ -0,0 +1,6 @@ +#include +#include +#include "open.h" + +int open_write(fn) char *fn; +{ return open(fn,O_WRONLY | O_NDELAY); } diff --git a/other-patches/README b/other-patches/README new file mode 100644 index 0000000..9fc7e78 --- /dev/null +++ b/other-patches/README @@ -0,0 +1,51 @@ +If you have glibc-2.3.1 or later then none of the software written by +djb will compile because of an incompatible declaration of errno. + +Here you can find patches correcting this problem for software likely +you need if you run qmail. + +To use the patches +================== + +From the top distribution directory, apply the appropriate errno patch +with -p1. So, for example, for mess822, you would do + +tar zxvf mess822-0.58.tar.gz +cd mess822-0.58 +patch -p1 < /path/to/mess822-0.58.errno.patch + +In case of daemontools, you need the following adjustment: + +tar zxvf daemontools-0.76.tar.gz +cd admin/daemontools-0.76 +patch -p1 /path/to/daemontools-0.76.errno.patch + +Notes +----- + +ucspi-tcp: + + In addition to the errno patch, there are two other patches + included here; both update rblsmtpd's behavior to current + practices on the net. + + The a_record patch allows you to specify the error that will + be returned by rblsmtpd for a zone that has only A records in + it. + + The nodefaultrbl patch changes the behavior of rblsmtpd so + that if no `-r' flag is given, than instead of falling back to + rbl.maps.vix.com as the default RBL, rblsmtpd assumes that no + RBL is to be used. + +qmailanalog: + + In addition to the required errno change, the qmailanalog + patch also changes qmailanalog to accept both tai and tai64n + times. This is necessary because djb never updated qmailanalog + after he changed daemontools to tai64n. + +These notes were originally written by Mate Wierdl, and have been +modified for this distribution by Russell Nelson, and then were rewritten +by Mate again, and then added to by Russell Nelson. + diff --git a/other-patches/checkpassword-0.90.errno.patch b/other-patches/checkpassword-0.90.errno.patch new file mode 100644 index 0000000..1412ca0 --- /dev/null +++ b/other-patches/checkpassword-0.90.errno.patch @@ -0,0 +1,12 @@ +diff -u checkpassword-0.90.old/error.h checkpassword-0.90/error.h +--- checkpassword-0.90.old/error.h 2000-12-23 00:40:46.000000000 -0600 ++++ checkpassword-0.90/error.h 2003-01-14 00:49:57.000000000 -0600 +@@ -1,7 +1,7 @@ + #ifndef ERROR_H + #define ERROR_H + +-extern int errno; ++#include + + extern int error_intr; + extern int error_nomem; diff --git a/other-patches/daemontools-0.76.errno.patch b/other-patches/daemontools-0.76.errno.patch new file mode 100644 index 0000000..d1884f5 --- /dev/null +++ b/other-patches/daemontools-0.76.errno.patch @@ -0,0 +1,12 @@ +diff -ur daemontools-0.76.old/src/error.h daemontools-0.76/src/error.h +--- daemontools-0.76.old/src/error.h 2001-07-12 11:49:49.000000000 -0500 ++++ daemontools-0.76/src/error.h 2003-01-09 21:52:01.000000000 -0600 +@@ -3,7 +3,7 @@ + #ifndef ERROR_H + #define ERROR_H + +-extern int errno; ++#include + + extern int error_intr; + extern int error_nomem; diff --git a/other-patches/djbdns-1.05.errno.patch b/other-patches/djbdns-1.05.errno.patch new file mode 100644 index 0000000..5099c16 --- /dev/null +++ b/other-patches/djbdns-1.05.errno.patch @@ -0,0 +1,12 @@ +diff -u djbdns-1.05.old/error.h djbdns-1.05/error.h +--- djbdns-1.05.old/error.h 2001-02-11 15:11:23.000000000 -0600 ++++ djbdns-1.05/error.h 2003-01-08 16:08:42.000000000 -0600 +@@ -1,7 +1,7 @@ + #ifndef ERROR_H + #define ERROR_H + +-extern int errno; ++#include + + extern int error_intr; + extern int error_nomem; diff --git a/other-patches/dot-forward-0.71.errno.patch b/other-patches/dot-forward-0.71.errno.patch new file mode 100644 index 0000000..fb21349 --- /dev/null +++ b/other-patches/dot-forward-0.71.errno.patch @@ -0,0 +1,12 @@ +diff -u dot-forward-0.71.orig/error.h dot-forward-0.71/error.h +--- dot-forward-0.71.orig/error.h 1998-05-19 05:15:50.000000000 -0500 ++++ dot-forward-0.71/error.h 2004-01-29 11:16:01.000000000 -0600 +@@ -1,7 +1,7 @@ + #ifndef ERROR_H + #define ERROR_H + +-extern int errno; ++#include + + extern int error_intr; + extern int error_nomem; diff --git a/other-patches/fastforward-0.51.errno.patch b/other-patches/fastforward-0.51.errno.patch new file mode 100644 index 0000000..8dc0d4a --- /dev/null +++ b/other-patches/fastforward-0.51.errno.patch @@ -0,0 +1,22 @@ +diff -u fastforward-0.51.orig/cdb_seek.c fastforward-0.51/cdb_seek.c +--- fastforward-0.51.orig/cdb_seek.c 1998-05-19 11:25:42.000000000 -0500 ++++ fastforward-0.51/cdb_seek.c 2004-01-29 11:22:24.000000000 -0600 +@@ -1,6 +1,5 @@ + #include + #include +-extern int errno; + #include "cdb.h" + + #ifndef SEEK_SET +diff -u fastforward-0.51.orig/error.h fastforward-0.51/error.h +--- fastforward-0.51.orig/error.h 1998-05-19 11:25:42.000000000 -0500 ++++ fastforward-0.51/error.h 2004-01-29 11:14:47.000000000 -0600 +@@ -1,7 +1,7 @@ + #ifndef ERROR_H + #define ERROR_H + +-extern int errno; ++#include + + extern int error_intr; + extern int error_nomem; diff --git a/other-patches/mess822-0.58.errno.patch b/other-patches/mess822-0.58.errno.patch new file mode 100644 index 0000000..172396e --- /dev/null +++ b/other-patches/mess822-0.58.errno.patch @@ -0,0 +1,33 @@ +diff -u mess822-0.58.old/cdb_seek.c mess822-0.58/cdb_seek.c +--- mess822-0.58.old/cdb_seek.c 1998-09-04 21:33:37.000000000 -0500 ++++ mess822-0.58/cdb_seek.c 2003-01-13 23:17:30.000000000 -0600 +@@ -1,6 +1,5 @@ + #include + #include +-extern int errno; + #include "cdb.h" + + #ifndef SEEK_SET +diff -u mess822-0.58.old/error.h mess822-0.58/error.h +--- mess822-0.58.old/error.h 1998-09-04 21:33:37.000000000 -0500 ++++ mess822-0.58/error.h 2003-01-13 23:18:09.000000000 -0600 +@@ -1,7 +1,7 @@ + #ifndef ERROR_H + #define ERROR_H + +-extern int errno; ++#include + + extern int error_intr; + extern int error_nomem; +diff -u mess822-0.58.old/leapsecs_read.c mess822-0.58/leapsecs_read.c +--- mess822-0.58.old/leapsecs_read.c 1998-09-04 21:33:37.000000000 -0500 ++++ mess822-0.58/leapsecs_read.c 2003-01-13 23:19:17.000000000 -0600 +@@ -2,7 +2,6 @@ + #include + #include + #include +-extern int errno; + #include "tai.h" + #include "leapsecs.h" + diff --git a/other-patches/qmailanalog-0.70.errno.patch b/other-patches/qmailanalog-0.70.errno.patch new file mode 100644 index 0000000..94f4583 --- /dev/null +++ b/other-patches/qmailanalog-0.70.errno.patch @@ -0,0 +1,87 @@ +diff -u orig/error.3 ./error.3 +--- orig/error.3 1998-08-30 17:39:27.000000000 -0400 ++++ ./error.3 2004-09-06 12:17:26.000000000 -0400 +@@ -3,8 +3,8 @@ + error \- syscall error codes + .SH SYNTAX + .B #include +- +-extern int \fBerrno\fP; ++.br ++.B #include + + extern int \fBerror_intr\fP; + .br +diff -u orig/error.c ./error.c +--- orig/error.c 1998-08-30 17:39:27.000000000 -0400 ++++ ./error.c 2004-09-06 12:17:26.000000000 -0400 +@@ -1,4 +1,3 @@ +-#include + #include "error.h" + + /* warning: as coverage improves here, should update error_{str,temp} */ +diff -u orig/error.h ./error.h +--- orig/error.h 1998-08-30 17:39:27.000000000 -0400 ++++ ./error.h 2004-09-06 12:17:26.000000000 -0400 +@@ -1,7 +1,7 @@ + #ifndef ERROR_H + #define ERROR_H + +-extern int errno; ++#include + + extern int error_intr; + extern int error_nomem; +diff -u orig/Makefile ./Makefile +--- orig/Makefile 1998-08-30 17:39:26.000000000 -0400 ++++ ./Makefile 2004-09-06 12:17:26.000000000 -0400 +@@ -259,7 +259,7 @@ + matchup.o: \ + compile matchup.c stralloc.h gen_alloc.h gen_alloc.h gen_allocdefs.h \ + strerr.h getln.h substdio.h subfd.h substdio.h readwrite.h exit.h \ +-str.h fmt.h scan.h case.h ++str.h fmt.h scan.h case.h alloc.h + ./compile matchup.c + + open.a: \ +diff -u orig/matchup.c ./matchup.c +--- orig/matchup.c 1998-08-30 17:39:27.000000000 -0400 ++++ ./matchup.c 2004-09-06 12:23:20.000000000 -0400 +@@ -1,3 +1,4 @@ ++#include "alloc.h" + #include "stralloc.h" + #include "gen_alloc.h" + #include "gen_allocdefs.h" +@@ -439,6 +440,32 @@ + if (getln(subfdin,&line,&match,'\n') == -1) die_read(); + if (!match) break; + ++ if (line.s[0] == '@' && line.len >= 25) { ++ unsigned long secs; ++ unsigned long nanosecs; ++ unsigned long u; ++ ++ secs = 0; ++ nanosecs = 0; ++ for (i = 1; i < line.len;i++) { ++ u = line.s[i] - '0'; ++ if (u >= 10) { ++ u = line.s[i] - 'a'; ++ if (u >= 6) break; ++ u += 10; ++ } ++ secs <<= 4; ++ secs += nanosecs >> 28; ++ nanosecs &= 0xfffffff; ++ nanosecs <<= 4; ++ nanosecs += u; ++ } ++ secs -= 4611686018427387914ULL; ++ i = fmt_uint0(line.s,secs,9); ++ line.s[i++] = '.'; ++ i += fmt_uint0(line.s+i,nanosecs,9); ++ while (i < 25) line.s[i++] = ' '; ++ } + if (!stralloc_copy(&outline,&line)) nomem(); + + for (i = 0;i < line.len;++i) { diff --git a/other-patches/serialmail-0.75.errno.patch b/other-patches/serialmail-0.75.errno.patch new file mode 100644 index 0000000..bf01d1b --- /dev/null +++ b/other-patches/serialmail-0.75.errno.patch @@ -0,0 +1,23 @@ +diff -u serialmail-0.75.orig/error.h serialmail-0.75/error.h +--- serialmail-0.75.orig/error.h 1998-11-14 20:28:23.000000000 -0600 ++++ serialmail-0.75/error.h 2004-01-29 11:15:32.000000000 -0600 +@@ -1,7 +1,7 @@ + #ifndef ERROR_H + #define ERROR_H + +-extern int errno; ++#include + + extern int error_intr; + extern int error_nomem; +diff -u serialmail-0.75.orig/leapsecs_read.c serialmail-0.75/leapsecs_read.c +--- serialmail-0.75.orig/leapsecs_read.c 1998-11-14 20:28:23.000000000 -0600 ++++ serialmail-0.75/leapsecs_read.c 2004-01-29 11:20:09.000000000 -0600 +@@ -2,7 +2,6 @@ + #include + #include + #include +-extern int errno; + #include "tai.h" + #include "leapsecs.h" + diff --git a/other-patches/ucspi-tcp-0.88.a_record.patch b/other-patches/ucspi-tcp-0.88.a_record.patch new file mode 100644 index 0000000..903125e --- /dev/null +++ b/other-patches/ucspi-tcp-0.88.a_record.patch @@ -0,0 +1,64 @@ +diff -ruN --exclude conf-* ucspi-tcp-0.88/rblsmtpd.c ucspi-tcp-0.88.fix/rblsmtpd.c +--- ucspi-tcp-0.88/rblsmtpd.c Sat Mar 18 10:18:42 2000 ++++ ucspi-tcp-0.88.fix/rblsmtpd.c Wed Aug 9 16:42:33 2000 +@@ -60,16 +60,54 @@ + + void rbl(char *base) + { ++ int i; ++ char *altreply = 0; + if (decision) return; + if (!stralloc_copy(&tmp,&ip_reverse)) nomem(); ++ i = str_chr(base, ':'); ++ if (base[i]) { ++ base[i] = 0; ++ altreply = base+i+1; ++ } + if (!stralloc_cats(&tmp,base)) nomem(); +- if (dns_txt(&text,&tmp) == -1) { +- flagmustnotbounce = 1; +- if (flagfailclosed) { +- if (!stralloc_copys(&text,"temporary RBL lookup error")) nomem(); +- decision = 2; ++ if (altreply) { ++ if (dns_ip4(&text,&tmp) == -1) { ++ flagmustnotbounce = 1; ++ if (flagfailclosed) { ++ if (!stralloc_copys(&text,"temporary RBL lookup error")) nomem(); ++ decision = 2; ++ } ++ return; ++ } ++ if (text.len) { ++ if(!stralloc_copys(&text, "")) nomem(); ++ while(*altreply) { ++ char *x; ++ i = str_chr(altreply, '%'); ++ if(!stralloc_catb(&text, altreply, i)) nomem(); ++ if(altreply[i] && ++ altreply[i+1]=='I' && ++ altreply[i+2]=='P' && ++ altreply[i+3]=='%') { ++ if(!stralloc_catb(&text, ip_env, str_len(ip_env))) nomem(); ++ altreply+=i+4; ++ } else if(altreply[i]) { ++ if(!stralloc_cats(&text, "%")) nomem(); ++ altreply+=i+1; ++ } else { ++ altreply+=i; ++ } ++ } ++ } ++ } else { ++ if (dns_txt(&text,&tmp) == -1) { ++ flagmustnotbounce = 1; ++ if (flagfailclosed) { ++ if (!stralloc_copys(&text,"temporary RBL lookup error")) nomem(); ++ decision = 2; ++ } ++ return; + } +- return; + } + if (text.len) + if (flagrblbounce) diff --git a/other-patches/ucspi-tcp-0.88.errno.patch b/other-patches/ucspi-tcp-0.88.errno.patch new file mode 100644 index 0000000..fd93909 --- /dev/null +++ b/other-patches/ucspi-tcp-0.88.errno.patch @@ -0,0 +1,12 @@ +diff -u ucspi-tcp-0.88.old/error.h ucspi-tcp-0.88/error.h +--- ucspi-tcp-0.88.old/error.h 2000-03-18 09:18:20.000000000 -0600 ++++ ucspi-tcp-0.88/error.h 2003-01-08 13:39:12.000000000 -0600 +@@ -1,7 +1,7 @@ + #ifndef ERROR_H + #define ERROR_H + +-extern int errno; ++#include + + extern int error_intr; + extern int error_nomem; diff --git a/other-patches/ucspi-tcp-0.88.nodefaultrbl.patch b/other-patches/ucspi-tcp-0.88.nodefaultrbl.patch new file mode 100644 index 0000000..fd9e90e --- /dev/null +++ b/other-patches/ucspi-tcp-0.88.nodefaultrbl.patch @@ -0,0 +1,28 @@ +diff -ur ucspi-tcp-0.88.orig/rblsmtpd.c ucspi-tcp-0.88/rblsmtpd.c +--- ucspi-tcp-0.88.orig/rblsmtpd.c Thu Jan 16 14:33:56 2003 ++++ ucspi-tcp-0.88/rblsmtpd.c Thu Jan 16 14:38:17 2003 +@@ -155,7 +155,6 @@ + + main(int argc,char **argv,char **envp) + { +- int flagwantdefaultrbl = 1; + char *x; + int opt; + +@@ -182,7 +181,7 @@ + case 'c': flagfailclosed = 1; break; + case 'C': flagfailclosed = 0; break; + case 't': scan_ulong(optarg,&timeout); break; +- case 'r': rbl(optarg); flagwantdefaultrbl = 0; break; ++ case 'r': rbl(optarg); break; + case 'a': antirbl(optarg); break; + default: usage(); + } +@@ -190,7 +189,6 @@ + argv += optind; + if (!*argv) usage(); + +- if (flagwantdefaultrbl) rbl("rbl.maps.vix.com"); + if (decision >= 2) rblsmtpd(); + + pathexec_run(*argv,argv,envp); diff --git a/pinq.sh b/pinq.sh new file mode 100644 index 0000000..93e747a --- /dev/null +++ b/pinq.sh @@ -0,0 +1 @@ +QMAIL/bin/maildir2mbox && exec pine ${1+"$@"} diff --git a/predate.c b/predate.c new file mode 100644 index 0000000..9648f6e --- /dev/null +++ b/predate.c @@ -0,0 +1,116 @@ +#include +#include +#include "datetime.h" +#include "fork.h" +#include "wait.h" +#include "fd.h" +#include "fmt.h" +#include "strerr.h" +#include "substdio.h" +#include "subfd.h" +#include "readwrite.h" +#include "exit.h" + +#define FATAL "predate: fatal: " + +static char *montab[12] = { +"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" +}; + +char num[FMT_ULONG]; +char outbuf[1024]; + +void main(argc,argv) +int argc; +char **argv; +{ + time_t now; + struct tm *tm; + struct datetime dt; + datetime_sec utc; + datetime_sec local; + int minutes; + int pi[2]; + substdio ss; + int wstat; + int pid; + + sig_pipeignore(); + + if (!argv[1]) + strerr_die1x(100,"predate: usage: predate child"); + + if (pipe(pi) == -1) + strerr_die2sys(111,FATAL,"unable to create pipe: "); + + switch(pid = fork()) { + case -1: + strerr_die2sys(111,FATAL,"unable to fork: "); + case 0: + close(pi[1]); + if (fd_move(0,pi[0]) == -1) + strerr_die2sys(111,FATAL,"unable to set up fds: "); + sig_pipedefault(); + execvp(argv[1],argv + 1); + strerr_die4sys(111,FATAL,"unable to run ",argv[1],": "); + } + close(pi[0]); + substdio_fdbuf(&ss,write,pi[1],outbuf,sizeof(outbuf)); + + time(&now); + + tm = gmtime(&now); + dt.year = tm->tm_year; + dt.mon = tm->tm_mon; + dt.mday = tm->tm_mday; + dt.hour = tm->tm_hour; + dt.min = tm->tm_min; + dt.sec = tm->tm_sec; + utc = datetime_untai(&dt); /* utc == now, if gmtime ignores leap seconds */ + + tm = localtime(&now); + dt.year = tm->tm_year; + dt.mon = tm->tm_mon; + dt.mday = tm->tm_mday; + dt.hour = tm->tm_hour; + dt.min = tm->tm_min; + dt.sec = tm->tm_sec; + local = datetime_untai(&dt); + + substdio_puts(&ss,"Date: "); + substdio_put(&ss,num,fmt_uint(num,dt.mday)); + substdio_puts(&ss," "); + substdio_puts(&ss,montab[dt.mon]); + substdio_puts(&ss," "); + substdio_put(&ss,num,fmt_uint(num,dt.year + 1900)); + substdio_puts(&ss," "); + substdio_put(&ss,num,fmt_uint0(num,dt.hour,2)); + substdio_puts(&ss,":"); + substdio_put(&ss,num,fmt_uint0(num,dt.min,2)); + substdio_puts(&ss,":"); + substdio_put(&ss,num,fmt_uint0(num,dt.sec,2)); + + if (local < utc) { + minutes = (utc - local + 30) / 60; + substdio_puts(&ss," -"); + substdio_put(&ss,num,fmt_uint0(num,minutes / 60,2)); + substdio_put(&ss,num,fmt_uint0(num,minutes % 60,2)); + } + else { + minutes = (local - utc + 30) / 60; + substdio_puts(&ss," +"); + substdio_put(&ss,num,fmt_uint0(num,minutes / 60,2)); + substdio_put(&ss,num,fmt_uint0(num,minutes % 60,2)); + } + + substdio_puts(&ss,"\n"); + substdio_copy(&ss,subfdin); + substdio_flush(&ss); + close(pi[1]); + + if (wait_pid(&wstat,pid) == -1) + strerr_die2sys(111,FATAL,"wait failed: "); + if (wait_crashed(wstat)) + strerr_die2x(111,FATAL,"child crashed"); + _exit(wait_exitcode(wstat)); +} diff --git a/preline.1 b/preline.1 new file mode 100644 index 0000000..69a757c --- /dev/null +++ b/preline.1 @@ -0,0 +1,57 @@ +.TH preline 1 +.SH NAME +preline \- prepend lines to message +.SH SYNOPSIS +in +.BR .qmail\fIext : +.B | preline \fIcommand +.SH DESCRIPTION +.B preline +feeds each incoming mail message through +.IR command . +At the top of each message it inserts +a UUCP-style +.B From_ +line, a +.B Return-Path +line, and a +.B Delivered-To +line. + +.B preline +is useful for +.B procmail +and +ELM's +.BR filter , +which +do not understand the +.B qmail-command +environment variables. +.SH OPTIONS +.TP +.B \-d +Do not include the +.B Delivered-To +line. You should use this option when the +recipient of the incoming mail message is actually under remote control, +but was sent here through +.B control/virtualdomains +for manual routing. +.TP +.B \-f +Do not include the +.B From_ +line. You should use this option except for +.IR command s +that create +.I mbox +files. +.TP +.B \-r +Do not include the +.B Return-Path +line. +.SH "SEE ALSO" +mbox(5), +qmail-command(8) diff --git a/preline.c b/preline.c new file mode 100644 index 0000000..1a4cef8 --- /dev/null +++ b/preline.c @@ -0,0 +1,90 @@ +#include "fd.h" +#include "sgetopt.h" +#include "readwrite.h" +#include "strerr.h" +#include "substdio.h" +#include "exit.h" +#include "fork.h" +#include "wait.h" +#include "env.h" +#include "sig.h" +#include "error.h" + +#define FATAL "preline: fatal: " + +void die_usage() +{ + strerr_die1x(100,"preline: usage: preline cmd [ arg ... ]"); +} + +int flagufline = 1; char *ufline; +int flagrpline = 1; char *rpline; +int flagdtline = 1; char *dtline; + +char outbuf[SUBSTDIO_OUTSIZE]; +char inbuf[SUBSTDIO_INSIZE]; +substdio ssout = SUBSTDIO_FDBUF(write,1,outbuf,sizeof outbuf); +substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof inbuf); + +void main(argc,argv) +int argc; +char **argv; +{ + int opt; + int pi[2]; + int pid; + int wstat; + + sig_pipeignore(); + + if (!(ufline = env_get("UFLINE"))) die_usage(); + if (!(rpline = env_get("RPLINE"))) die_usage(); + if (!(dtline = env_get("DTLINE"))) die_usage(); + + while ((opt = getopt(argc,argv,"frdFRD")) != opteof) + switch(opt) { + case 'f': flagufline = 0; break; + case 'r': flagrpline = 0; break; + case 'd': flagdtline = 0; break; + case 'F': flagufline = 1; break; + case 'R': flagrpline = 1; break; + case 'D': flagdtline = 1; break; + default: die_usage(); + } + argc -= optind; + argv += optind; + if (!*argv) die_usage(); + + if (pipe(pi) == -1) + strerr_die2sys(111,FATAL,"unable to create pipe: "); + + pid = fork(); + if (pid == -1) + strerr_die2sys(111,FATAL,"unable to fork: "); + + if (pid == 0) { + close(pi[1]); + if (fd_move(0,pi[0]) == -1) + strerr_die2sys(111,FATAL,"unable to set up fds: "); + sig_pipedefault(); + execvp(*argv,argv); + strerr_die4sys(error_temp(errno) ? 111 : 100,FATAL,"unable to run ",*argv,": "); + } + close(pi[0]); + if (fd_move(1,pi[1]) == -1) + strerr_die2sys(111,FATAL,"unable to set up fds: "); + + if (flagufline) substdio_bputs(&ssout,ufline); + if (flagrpline) substdio_bputs(&ssout,rpline); + if (flagdtline) substdio_bputs(&ssout,dtline); + if (substdio_copy(&ssout,&ssin) != 0) + strerr_die2sys(111,FATAL,"unable to copy input: "); + substdio_flush(&ssout); + close(1); + + if (wait_pid(&wstat,pid) == -1) + strerr_die2sys(111,FATAL,"wait failed: "); + if (wait_crashed(wstat)) + strerr_die2x(111,FATAL,"child crashed"); + _exit(wait_exitcode(wstat)); +} diff --git a/prioq.c b/prioq.c new file mode 100644 index 0000000..051aa45 --- /dev/null +++ b/prioq.c @@ -0,0 +1,58 @@ +#include "alloc.h" +#include "gen_allocdefs.h" +#include "prioq.h" + +GEN_ALLOC_readyplus(prioq,struct prioq_elt,p,len,a,i,n,x,100,prioq_readyplus) + +int prioq_insert(pq,pe) +prioq *pq; +struct prioq_elt *pe; +{ + int i; + int j; + if (!prioq_readyplus(pq,1)) return 0; + j = pq->len++; + while (j) + { + i = (j - 1)/2; + if (pq->p[i].dt <= pe->dt) break; + pq->p[j] = pq->p[i]; + j = i; + } + pq->p[j] = *pe; + return 1; +} + +int prioq_min(pq,pe) +prioq *pq; +struct prioq_elt *pe; +{ + if (!pq->p) return 0; + if (!pq->len) return 0; + *pe = pq->p[0]; + return 1; +} + +void prioq_delmin(pq) +prioq *pq; +{ + int i; + int j; + int n; + if (!pq->p) return; + n = pq->len; + if (!n) return; + i = 0; + --n; + for (;;) + { + j = i + i + 2; + if (j > n) break; + if (pq->p[j - 1].dt <= pq->p[j].dt) --j; + if (pq->p[n].dt <= pq->p[j].dt) break; + pq->p[i] = pq->p[j]; + i = j; + } + pq->p[i] = pq->p[n]; + pq->len = n; +} diff --git a/prioq.h b/prioq.h new file mode 100644 index 0000000..32a0d7c --- /dev/null +++ b/prioq.h @@ -0,0 +1,15 @@ +#ifndef PRIOQ_H +#define PRIOQ_H + +#include "datetime.h" +#include "gen_alloc.h" + +struct prioq_elt { datetime_sec dt; unsigned long id; } ; + +GEN_ALLOC_typedef(prioq,struct prioq_elt,p,len,a) + +extern int prioq_insert(); +extern int prioq_min(); +extern void prioq_delmin(); + +#endif diff --git a/proc+df.sh b/proc+df.sh new file mode 100644 index 0000000..eb9e92c --- /dev/null +++ b/proc+df.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +# Using splogger to send the log through syslog. +# Using dot-forward to support sendmail-style ~/.forward files. +# Using procmail to deliver messages to /var/spool/mail/$USER by default. + +exec env - PATH="QMAIL/bin:$PATH" \ +qmail-start '|dot-forward .forward +|preline procmail' splogger qmail diff --git a/proc.sh b/proc.sh new file mode 100644 index 0000000..3c76220 --- /dev/null +++ b/proc.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +# Using splogger to send the log through syslog. +# Using procmail to deliver messages to /var/spool/mail/$USER by default. + +exec env - PATH="QMAIL/bin:$PATH" \ +qmail-start '|preline procmail' splogger qmail diff --git a/prot.c b/prot.c new file mode 100644 index 0000000..a38e0f9 --- /dev/null +++ b/prot.c @@ -0,0 +1,21 @@ +#include "hasshsgr.h" +#include "prot.h" + +/* XXX: there are more portability problems here waiting to leap out at me */ + +int prot_gid(gid) int gid; +{ +#ifdef HASSHORTSETGROUPS + short x[2]; + x[0] = gid; x[1] = 73; /* catch errors */ + if (setgroups(1,x) == -1) return -1; +#else + if (setgroups(1,&gid) == -1) return -1; +#endif + return setgid(gid); /* _should_ be redundant, but on some systems it isn't */ +} + +int prot_uid(uid) int uid; +{ + return setuid(uid); +} diff --git a/prot.h b/prot.h new file mode 100644 index 0000000..8c5d81a --- /dev/null +++ b/prot.h @@ -0,0 +1,7 @@ +#ifndef PROT_H +#define PROT_H + +extern int prot_gid(); +extern int prot_uid(); + +#endif diff --git a/qail.sh b/qail.sh new file mode 100644 index 0000000..7e31c33 --- /dev/null +++ b/qail.sh @@ -0,0 +1 @@ +QMAIL/bin/maildir2mbox && exec Mail ${1+"$@"} diff --git a/qbiff.1 b/qbiff.1 new file mode 100644 index 0000000..14db027 --- /dev/null +++ b/qbiff.1 @@ -0,0 +1,31 @@ +.TH qbiff 1 +.SH NAME +qbiff \- announce new mail the moment it arrives +.SH SYNOPSIS +in +.BR .qmail : +.B |qbiff +.SH DESCRIPTION +.B qbiff +writes a message to your screen +whenever a new mail message is delivered, +if you ran +.B biff y +after logging in. + +.B WARNING: +If you create a +.B .qmail +file to enable +.BR qbiff , +make sure to also add a line specifying delivery to your normal mailbox. +For example: + +.EX + /home/joe/Mailbox +.br + |qbiff +.EE +.SH "SEE ALSO" +biff(1), +dot-qmail(5) diff --git a/qbiff.c b/qbiff.c new file mode 100644 index 0000000..4e9f1d8 --- /dev/null +++ b/qbiff.c @@ -0,0 +1,113 @@ +#include +#include +#include +#ifndef UTMP_FILE +#ifdef _PATH_UTMP +#define UTMP_FILE _PATH_UTMP +#else +#define UTMP_FILE "/etc/utmp" +#endif +#endif +#include "readwrite.h" +#include "stralloc.h" +#include "substdio.h" +#include "subfd.h" +#include "open.h" +#include "byte.h" +#include "str.h" +#include "headerbody.h" +#include "hfield.h" +#include "env.h" +#include "exit.h" + +substdio ssutmp; +char bufutmp[sizeof(struct utmp) * 16]; +int fdutmp; +substdio sstty; +char buftty[1024]; +int fdtty; + +struct utmp ut; +char line[sizeof(ut.ut_line) + 1]; +stralloc woof = {0}; +stralloc tofrom = {0}; +stralloc text = {0}; + +void doit(s,n) char *s; int n; +{ + if (!stralloc_catb(&text,s,n)) _exit(0); + if (text.len > 78) text.len = 78; +} +void dobody(h) stralloc *h; { doit(h->s,h->len); } +void doheader(h) stralloc *h; +{ + int i; + if (hfield_known(h->s,h->len) == H_SUBJECT) + { + i = hfield_skipname(h->s,h->len); + doit(h->s + i,h->len - i); + } +} +void finishheader() { ; } + +void main() +{ + char *user; + char *sender; + char *userext; + struct stat st; + int i; + + if (chdir("/dev") == -1) _exit(0); + + if (!(user = env_get("USER"))) _exit(0); + if (!(sender = env_get("SENDER"))) _exit(0); + if (!(userext = env_get("LOCAL"))) _exit(0); + if (str_len(user) > sizeof(ut.ut_name)) _exit(0); + + if (!stralloc_copys(&tofrom,"*** TO <")) _exit(0); + if (!stralloc_cats(&tofrom,userext)) _exit(0); + if (!stralloc_cats(&tofrom,"> FROM <")) _exit(0); + if (!stralloc_cats(&tofrom,sender)) _exit(0); + if (!stralloc_cats(&tofrom,">")) _exit(0); + + for (i = 0;i < tofrom.len;++i) + if ((tofrom.s[i] < 32) || (tofrom.s[i] > 126)) + tofrom.s[i] = '_'; + + if (!stralloc_copys(&text," ")) _exit(0); + if (headerbody(subfdin,doheader,finishheader,dobody) == -1) _exit(0); + + for (i = 0;i < text.len;++i) + if ((text.s[i] < 32) || (text.s[i] > 126)) + text.s[i] = '/'; + + if (!stralloc_copys(&woof,"\015\n\007")) _exit(0); + if (!stralloc_cat(&woof,&tofrom)) _exit(0); + if (!stralloc_cats(&woof,"\015\n")) _exit(0); + if (!stralloc_cat(&woof,&text)) _exit(0); + if (!stralloc_cats(&woof,"\015\n")) _exit(0); + + fdutmp = open_read(UTMP_FILE); + if (fdutmp == -1) _exit(0); + substdio_fdbuf(&ssutmp,read,fdutmp,bufutmp,sizeof(bufutmp)); + + while (substdio_get(&ssutmp,&ut,sizeof(ut)) == sizeof(ut)) + if (!str_diffn(ut.ut_name,user,sizeof(ut.ut_name))) + { + byte_copy(line,sizeof(ut.ut_line),ut.ut_line); + line[sizeof(ut.ut_line)] = 0; + if (line[0] == '/') continue; + if (!line[0]) continue; + if (line[str_chr(line,'.')]) continue; + fdtty = open_append(line); + if (fdtty == -1) continue; + if (fstat(fdtty,&st) == -1) { close(fdtty); continue; } + if (!(st.st_mode & 0100)) { close(fdtty); continue; } + if (st.st_uid != getuid()) { close(fdtty); continue; } + substdio_fdbuf(&sstty,write,fdtty,buftty,sizeof(buftty)); + substdio_putflush(&sstty,woof.s,woof.len); + close(fdtty); + } + _exit(0); +} diff --git a/qlx.h b/qlx.h new file mode 100644 index 0000000..713946d --- /dev/null +++ b/qlx.h @@ -0,0 +1,18 @@ +#ifndef QLX_H +#define QLX_H + +/* 0, 111, 100 are qmail-local success, soft, hard */ + +#define QLX_USAGE 112 +#define QLX_BUG 101 +#define QLX_ROOT 113 +#define QLX_NFS 115 +#define QLX_NOALIAS 116 +#define QLX_CDB 117 +#define QLX_SYS 118 +#define QLX_NOMEM 119 +#define QLX_EXECSOFT 120 +#define QLX_EXECPW 121 +#define QLX_EXECHARD 126 + +#endif diff --git a/qmail-clean.8 b/qmail-clean.8 new file mode 100644 index 0000000..41795c6 --- /dev/null +++ b/qmail-clean.8 @@ -0,0 +1,13 @@ +.TH qmail-clean 8 +.SH NAME +qmail-clean \- clean up the queue directory +.SH SYNOPSIS +.B qmail-clean +.SH DESCRIPTION +.B qmail-clean +reads a cleanup command from descriptor 0, +performs the cleanup, +prints the results to descriptor 1, +and repeats. +.SH "SEE ALSO" +qmail-send(8) diff --git a/qmail-clean.c b/qmail-clean.c new file mode 100644 index 0000000..7539007 --- /dev/null +++ b/qmail-clean.c @@ -0,0 +1,98 @@ +#include +#include +#include "readwrite.h" +#include "sig.h" +#include "now.h" +#include "str.h" +#include "direntry.h" +#include "getln.h" +#include "stralloc.h" +#include "substdio.h" +#include "subfd.h" +#include "byte.h" +#include "scan.h" +#include "fmt.h" +#include "error.h" +#include "exit.h" +#include "fmtqfn.h" +#include "auto_qmail.h" + +#define OSSIFIED 129600 /* see qmail-send.c */ + +stralloc line = {0}; + +void cleanuppid() +{ + DIR *dir; + direntry *d; + struct stat st; + datetime_sec time; + + time = now(); + dir = opendir("pid"); + if (!dir) return; + while (d = readdir(dir)) + { + if (str_equal(d->d_name,".")) continue; + if (str_equal(d->d_name,"..")) continue; + if (!stralloc_copys(&line,"pid/")) continue; + if (!stralloc_cats(&line,d->d_name)) continue; + if (!stralloc_0(&line)) continue; + if (stat(line.s,&st) == -1) continue; + if (time < st.st_atime + OSSIFIED) continue; + unlink(line.s); + } + closedir(dir); +} + +char fnbuf[FMTQFN]; + +void respond(s) char *s; { if (substdio_putflush(subfdoutsmall,s,1) == -1) _exit(100); } + +void main() +{ + int i; + int match; + int cleanuploop; + unsigned long id; + + if (chdir(auto_qmail) == -1) _exit(111); + if (chdir("queue") == -1) _exit(111); + + sig_pipeignore(); + + if (!stralloc_ready(&line,200)) _exit(111); + + cleanuploop = 0; + + for (;;) + { + if (cleanuploop) --cleanuploop; else { cleanuppid(); cleanuploop = 30; } + if (getln(subfdinsmall,&line,&match,'\0') == -1) break; + if (!match) break; + if (line.len < 7) { respond("x"); continue; } + if (line.len > 100) { respond("x"); continue; } + if (line.s[line.len - 1]) { respond("x"); continue; } /* impossible */ + for (i = 5;i < line.len - 1;++i) + if ((unsigned char) (line.s[i] - '0') > 9) + { respond("x"); continue; } + if (!scan_ulong(line.s + 5,&id)) { respond("x"); continue; } + if (byte_equal(line.s,5,"foop/")) + { +#define U(prefix,flag) fmtqfn(fnbuf,prefix,id,flag); \ +if (unlink(fnbuf) == -1) if (errno != error_noent) { respond("!"); continue; } + U("intd/",0) + U("mess/",1) + respond("+"); + } + else if (byte_equal(line.s,4,"todo/")) + { + U("intd/",0) + U("todo/",0) + respond("+"); + } + else + respond("x"); + } + _exit(0); +} diff --git a/qmail-command.8 b/qmail-command.8 new file mode 100644 index 0000000..ad46377 --- /dev/null +++ b/qmail-command.8 @@ -0,0 +1,149 @@ +.TH qmail-command 8 +.SH NAME +qmail-command \- user-specified mail delivery program +.SH SYNOPSIS +in +.BR .qmail\fIext : +.B |\fIcommand +.SH DESCRIPTION +.B qmail-local +will, upon your request, +feed each incoming mail message through a program of your choice. + +When a mail message arrives, +.B qmail-local +runs +.B sh -c \fIcommand +in your home directory. +It makes the message available on +.IR command 's +standard input. + +.B WARNING: +The mail message does not begin with +.BR qmail-local 's +usual +.B Return-Path +and +.B Delivered-To +lines. + +Note that +.B qmail-local +uses the same file descriptor for every delivery +in your +.B .qmail +file, so it is not safe for +.I command +to fork a child that +reads the message in the background while the parent exits. +.SH "EXIT CODES" +.IR command 's +exit codes are interpreted as follows: +0 means that the delivery was successful; +99 means that the delivery was successful, +but that +.B qmail-local +should ignore all further delivery instructions; +100 means that the delivery failed permanently (hard error); +111 means that the delivery failed but should be tried again +in a little while (soft error). + +Currently 64, 65, 70, 76, 77, 78, and 112 are considered hard errors, +and all other codes are considered soft errors, +but +.I command +should avoid relying on this. +.SH "ENVIRONMENT VARIABLES" +.B qmail-local +supplies several useful environment variables to +.IR command . +.B WARNING: +These environment variables are not quoted. +They may contain special characters. +They are under the control of a possibly malicious remote user. + +.B SENDER +is the envelope sender address. +.B NEWSENDER +is the forwarding envelope sender address, +as described in +.BR dot-qmail(5) . +.B RECIPIENT +is the envelope recipient address, +.IR local@domain . +.B USER +is +.IR user . +.B HOME +is your home directory, +.IR homedir . +.B HOST +is the +.I domain +part of the recipient address. +.B LOCAL +is the +.I local +part. +.B EXT +is the +address extension, +.IR ext . + +.B HOST2 +is the portion of +.B HOST +preceding the last dot; +.B HOST3 +is the portion of +.B HOST +preceding the second-to-last dot; +.B HOST4 +is the portion of +.B HOST +preceding the third-to-last dot. + +.B EXT2 +is the portion of +.B EXT +following the first dash; +.B EXT3 +is the portion +following the second dash; +.B EXT4 +is the portion +following the third dash. +.B DEFAULT +is the portion +corresponding to the +.B default +part of the +.BR .qmail\- ... +file name; +.B DEFAULT +is not set if +the file name does not end with +.BR default . + +.B DTLINE +and +.B RPLINE +are the usual +.B Delivered-To +and +.B Return-Path +lines, +including newlines. +.B UFLINE +is the UUCP-style +.B From_ +line that +.B qmail-local +adds to +.IR mbox -format +files. +.SH "SEE ALSO" +dot-qmail(5), +envelopes(5), +qmail-local(8) diff --git a/qmail-control.9 b/qmail-control.9 new file mode 100644 index 0000000..503ce93 --- /dev/null +++ b/qmail-control.9 @@ -0,0 +1,78 @@ +.TH qmail-control 5 +.SH "NAME" +qmail-control \- qmail configuration files +.SH "INTRODUCTION" +You can change the behavior of the +.B qmail +system by modifying +.BR qmail 's +.I control files +in +.BR QMAILHOME/control . + +.B qmail +can survive with just one control file, +.IR me , +containing the +fully-qualified name of the current host. +This file is used as the default for +other hostname-related control files. + +Comments are allowed +in +.IR badmailfrom , +.IR locals , +.IR percenthack , +.IR qmqpservers , +.IR rcpthosts , +.IR smtproutes , +and +.IR virtualdomains . +Trailing spaces and tabs are allowed in any control file. + +The following table lists all control files +other than +.IR me . +See the corresponding man pages for further details. + +.RS +.nf +.ta 5c 10c +control default used by + +.I badmailfrom \fR(none) \fRqmail-smtpd +.I bouncefrom \fRMAILER-DAEMON \fRqmail-send +.I bouncehost \fIme \fRqmail-send +.I concurrencylocal \fR10 \fRqmail-send +.I concurrencyremote \fR20 \fRqmail-send +.I defaultdomain \fIme \fRqmail-inject +.I defaulthost \fIme \fRqmail-inject +.I databytes \fR0 \fRqmail-smtpd +.I doublebouncehost \fIme \fRqmail-send +.I doublebounceto \fRpostmaster \fRqmail-send +.I envnoathost \fIme \fRqmail-send +.I helohost \fIme \fRqmail-remote +.I idhost \fIme \fRqmail-inject +.I localiphost \fIme \fRqmail-smtpd +.I locals \fIme \fRqmail-send +.I morercpthosts \fR(none) \fRqmail-smtpd +.I percenthack \fR(none) \fRqmail-send +.I plusdomain \fIme \fRqmail-inject +.I qmqpservers \fR(none) \fRqmail-qmqpc +.I queuelifetime \fR604800 \fRqmail-send +.I rcpthosts \fR(none) \fRqmail-smtpd +.I smtpgreeting \fIme \fRqmail-smtpd +.I smtproutes \fR(none) \fRqmail-remote +.I timeoutconnect \fR60 \fRqmail-remote +.I timeoutremote \fR1200 \fRqmail-remote +.I timeoutsmtpd \fR1200 \fRqmail-smtpd +.I virtualdomains \fR(none) \fRqmail-send +.fi +.RE +.SH "SEE ALSO" +qmail-inject(8), +qmail-qmqpc(8), +qmail-remote(8), +qmail-send(8), +qmail-showctl(8), +qmail-smtpd(8) diff --git a/qmail-getpw.9 b/qmail-getpw.9 new file mode 100644 index 0000000..0f12e1c --- /dev/null +++ b/qmail-getpw.9 @@ -0,0 +1,114 @@ +.TH qmail-getpw 8 +.SH NAME +qmail-getpw \- give addresses to users +.SH SYNOPSIS +.B qmail-getpw +.I local +.SH DESCRIPTION +In +.BR qmail , +each user controls a vast array of local addresses. +.B qmail-getpw +finds the user that controls a particular address, +.IR local . +It prints six pieces of information, +each terminated by NUL: +.IR user ; +.IR uid ; +.IR gid ; +.IR homedir ; +.IR dash ; +and +.IR ext . +The user's account name is +.IR user ; +the user's uid and gid in decimal are +.I uid +and +.IR gid ; +the user's home directory is +.IR homedir ; +and messages to +.I local +will be handled by +.IR homedir\fB/.qmail\fIdashext . + +In case of trouble, +.B qmail-getpw +exits nonzero without printing anything. + +.B WARNING: +The operating system's +.B getpwnam +function, which is at the heart of +.BR qmail-getpw , +is inherently unreliable: +it fails to distinguish between temporary errors and nonexistent users. +Future versions of +.B getpwnam +should return ETXTBSY to indicate temporary errors +and ESRCH to indicate nonexistent users. +.SH "RULES" +.B qmail-getpw +considers an account in +.B /etc/passwd +to be a user if +(1) the account has a nonzero uid, +(2) the account's home directory exists (and is visible to +.BR qmail-getpw ), +and +(3) the account owns its home directory. +.B qmail-getpw +ignores account names containing uppercase letters. +.B qmail-getpw +also assumes that all account names are shorter than 32 characters. + +.B qmail-getpw +gives each user +control over the basic +.I user +address and +all addresses of the form +.IR user\fBBREAK\fIanything . +When +.I local +is +.IR user , +.I dash +and +.I ext +are both empty. +When +.I local +is +.IR user\fBBREAK\fIanything , +.I dash +is a hyphen and +.I ext +is +.IR anything . +.I user +may appear in any combination of uppercase and lowercase letters +at the front of +.IR local . + +A catch-all user, +.BR alias , +controls all other addresses. +In this case +.I ext +is +.I local +and +.I dash +is a hyphen. + +You can override all of +.BR qmail-getpw 's +decisions with the +.B qmail-users +mechanism, which is reliable, highly configurable, and much faster than +.BR qmail-getpw . +.SH "SEE ALSO" +qmail-users(5), +qmail-lspawn(8) diff --git a/qmail-getpw.c b/qmail-getpw.c new file mode 100644 index 0000000..128c682 --- /dev/null +++ b/qmail-getpw.c @@ -0,0 +1,88 @@ +#include +#include +#include +#include "readwrite.h" +#include "substdio.h" +#include "subfd.h" +#include "error.h" +#include "exit.h" +#include "byte.h" +#include "str.h" +#include "case.h" +#include "fmt.h" +#include "auto_usera.h" +#include "auto_break.h" +#include "qlx.h" + +#define GETPW_USERLEN 32 + +char *local; +struct passwd *pw; +char *dash; +char *extension; + +int userext() +{ + char username[GETPW_USERLEN]; + struct stat st; + + extension = local + str_len(local); + for (;;) { + if (extension - local < sizeof(username)) + if (!*extension || (*extension == *auto_break)) { + byte_copy(username,extension - local,local); + username[extension - local] = 0; + case_lowers(username); + errno = 0; + pw = getpwnam(username); + if (errno == error_txtbsy) _exit(QLX_SYS); + if (pw) + if (pw->pw_uid) + if (stat(pw->pw_dir,&st) == 0) { + if (st.st_uid == pw->pw_uid) { + dash = ""; + if (*extension) { ++extension; dash = "-"; } + return 1; + } + } + else + if (error_temp(errno)) _exit(QLX_NFS); + } + if (extension == local) return 0; + --extension; + } +} + +char num[FMT_ULONG]; + +void main(argc,argv) +int argc; +char **argv; +{ + local = argv[1]; + if (!local) _exit(100); + + if (!userext()) { + extension = local; + dash = "-"; + pw = getpwnam(auto_usera); + } + + if (!pw) _exit(QLX_NOALIAS); + + substdio_puts(subfdoutsmall,pw->pw_name); + substdio_put(subfdoutsmall,"",1); + substdio_put(subfdoutsmall,num,fmt_ulong(num,(long) pw->pw_uid)); + substdio_put(subfdoutsmall,"",1); + substdio_put(subfdoutsmall,num,fmt_ulong(num,(long) pw->pw_gid)); + substdio_put(subfdoutsmall,"",1); + substdio_puts(subfdoutsmall,pw->pw_dir); + substdio_put(subfdoutsmall,"",1); + substdio_puts(subfdoutsmall,dash); + substdio_put(subfdoutsmall,"",1); + substdio_puts(subfdoutsmall,extension); + substdio_put(subfdoutsmall,"",1); + substdio_flush(subfdoutsmall); + + _exit(0); +} diff --git a/qmail-header.5 b/qmail-header.5 new file mode 100644 index 0000000..d90323a --- /dev/null +++ b/qmail-header.5 @@ -0,0 +1,332 @@ +.TH qmail-header 5 +.SH NAME +qmail-header \- format of a mail message +.SH OVERVIEW +At the top of every mail message is a +highly structured +.BR header . +Many programs expect the header to carry certain information, +as described below. +The main function of +.B qmail-inject +is to make sure that each outgoing message has an appropriate header. + +For more detailed information, see +.BR http://pobox.com/~djb/proto/immhf.html . +.SH "MESSAGE STRUCTURE" +A message contains a series of +.I header fields\fR, +a blank line, +and a +.IR body : + +.EX + Received: (qmail-queue invoked by uid 666); +.br + 30 Jul 1996 11:54:54 -0000 +.br + From: djb@silverton.berkeley.edu (D. J. Bernstein) +.br + To: fred@silverton.berkeley.edu +.br + Date: 30 Jul 1996 11:54:54 -0000 +.br + Subject: Go, Bears! +.br + +.br + I've got money on this one. How about you? +.br + +.br + ---Dan (this is the third line of the body) +.EE + +Each header field has a +.IR name , +a colon, +some +.IR contents , +and a newline: + +.EX + Subject: Go, Bears! +.EE + +The field contents may be folded across several lines. +Each line past the first must begin with a space or tab: + +.EX + Received: (qmail-queue invoked by uid 666); +.br + 30 Jul 1996 11:54:54 -0000 +.EE + +The field name must not contain spaces, tabs, or colons. +Also, an empty field name is illegal. +.B qmail-inject +does not allow field names with unprintable characters. + +Case is irrelevant in field names: +.B subject +and +.B SUBJECT +and +.B SuBjEcT +have the same meaning. +.SH "ADDRESS LISTS" +Certain fields, such as +.BR To , +contain +.I address lists\fR. + +An address list contains some number of +.I addresses +or +.I address groups\fR, +separated by commas: + +.EX + a@b, c@d (Somebody), A Person , +.br + random group: g@h, i@j;, k@l +.EE + +An +.I address group +has some text, a colon, a list of addresses, +and a semicolon: + +.EX + random group: g@h, i@j; +.EE + +An address can appear in several forms. +The most common form is +.IR box@host . + +Every address must include a host name. +If +.B qmail-inject +sees a lone box name +it adds the +.I default host name\fR. + +All host names should be fully qualified. +.B qmail-inject +appends the +.I default domain name +to any name without dots: + +.EX + djb@silverton -> djb@silverton.berkeley.edu +.EE + +It appends the +.I plus domain name +to any name +that ends with a plus sign: + +.EX + eric@mammoth.cs+ -> eric@mammoth.cs.berkeley.edu +.EE + +A host name may be a dotted-decimal address: + +.EX + djb@[128.32.183.163] +.EE + +RFC 822 allows mailbox names inside angle brackets +to include +.I source routes\fR, +but +.B qmail-inject +strips all source routes out of addresses. +.SH "SENDER ADDRESSES" +.B qmail-inject +looks for sender address lists in the following fields: +.BR Sender , +.BR From , +.BR Reply-To , +.BR Return-Path , +.BR Return-Receipt-To , +.BR Errors-To , +.BR Resent-Sender , +.BR Resent-From , +.BR Resent-Reply-To . + +If there is no +.B From +field, +.B qmail-inject +adds a new +.B From +field with the name of the user invoking +.B qmail-inject. + +RFC 822 requires that certain sender fields contain +only a single address, but +.B qmail-inject +does not enforce this restriction. +.SH "RECIPIENT ADDRESSES" +.B qmail-inject +looks for recipient address lists in the following fields: +.BR To , +.BR Cc , +.BR Bcc , +.BR Apparently-To , +.BR Resent-To , +.BR Resent-Cc , +.BR Resent-Bcc . + +Every message must contain at least one +.B To +or +.B Cc +or +.BR Bcc . +.B qmail-inject +deletes any +.B Bcc +field. +If there is no +.B To +or +.B Cc +field, +.B qmail-inject +adds a line + +.EX + Cc: recipient list not shown: ; +.EE + +This complies with RFC 822; +it also works around some strange +.B sendmail +behavior, in case the message is passed through +.B sendmail +on another machine. +.SH STAMPS +Every message must contain a +.B Date +field, with the date in a strict format defined by RFC 822. +If necessary +.B qmail-inject +creates a new +.B Date +field with the current date (in GMT). + +Every message should contain a +.B Message-Id +field. +The field contents are a unique worldwide identifier for this message. +If necessary +.B qmail-inject +creates a new +.B Message-Id +field. + +Another important field is +.BR Received . +Every time the message is sent from one system to another, +a new +.B Received +field is added to the top of the message. +.B qmail-inject +does not create any +.B Received +fields. +.SH "RESENT MESSAGES" +A message is +.I resent +if it contains any of the following fields: +.BR Resent-Sender , +.BR Resent-From , +.BR Resent-Reply-To , +.BR Resent-To , +.BR Resent-Cc , +.BR Resent-Bcc , +.BR Resent-Date , +.BR Resent-Message-ID . + +If a message is resent, +.B qmail-inject +changes its behavior as follows. + +It deletes any +.B Resent-Bcc +field (as well as any +.B Bcc +field); +if there are no +.B Resent-To +or +.B Resent-Cc +fields, +.B qmail-inject +adds an appropriate +.B Resent-Cc +line. +It does +.I not +add a +.B Cc +line, +even if neither +.B To +nor +.B Cc +is present. + +If there is no +.B Resent-From +field, +.B qmail-inject +adds a new +.B Resent-From +field. +It does +.I not +add a new +.B From +field. + +.B qmail-inject +adds +.B Resent-Date +if one is not already present; +same for +.BR Resent-Message-Id . +It does +.I not +add new +.B Date +or +.B Message-Id +fields. +.SH "OTHER FEATURES" +Addresses are separated by commas, not spaces. +When +.B qmail-inject +sees an illegal space, +it inserts a comma: + +.EX + djb fred -> djb, fred +.EE + +.B qmail-inject +removes all +.B Return-Path +header fields. + +.B qmail-inject +also removes any +.B Content-Length +fields. +.SH "SEE ALSO" +addresses(5), +envelopes(5), +qmail-inject(8) diff --git a/qmail-inject.8 b/qmail-inject.8 new file mode 100644 index 0000000..59e11a7 --- /dev/null +++ b/qmail-inject.8 @@ -0,0 +1,309 @@ +.TH qmail-inject 8 +.SH NAME +qmail-inject \- preprocess and send a mail message +.SH SYNOPSIS +.B qmail-inject +[ +.B \-nNaAhH +] [ +.B \-f\fIsender +] [ +.I recip ... +] +.SH DESCRIPTION +.B qmail-inject +reads a mail message from its standard input, +adds appropriate information to the message header, +and invokes +.B qmail-queue +to send the message +to one or more recipients. + +See +.B qmail-header(5) +for information on how +.B qmail-inject +rewrites header fields. + +.B qmail-inject +normally exits 0. +It exits 100 if it was invoked improperly +or if there is a severe syntax error in the message. +It exits 111 for temporary errors. +.SH "ENVIRONMENT VARIABLES" +For the convenience of users who do not run +.B qmail-inject +directly, +.B qmail-inject +takes many options through environment variables. + +The user name in the +.B From +header field is set by +.BR QMAILUSER , +.BR MAILUSER , +.BR USER , +or +.BR LOGNAME , +whichever comes first. + +The host name is normally set by the +.I defaulthost +control +but can be overridden with +.B QMAILHOST +or +.BR MAILHOST . + +The personal name is +.BR QMAILNAME , +.BR MAILNAME , +or +.BR NAME . + +The default envelope sender address is the same as the +default +.B From +address, +but it can be overridden with +.B QMAILSUSER +and +.BR QMAILSHOST . +It may also be modified by the +.B r +and +.B m +letters described below. +Bounces will be sent to this address. + +If +.B QMAILMFTFILE +is set, +.B qmail-inject +reads a list of mailing list addresses, +one per line, +from that file. +If To+Cc includes one of those addresses (without regard to case), +.B qmail-inject +adds a Mail-Followup-To field +with all the To+Cc addresses. +.B qmail-inject +does not add Mail-Followup-To +to a message that already has one. + +The +.B QMAILINJECT +environment variable +can contain any of the following letters: +.TP +.B c +Use address-comment style for the +.B From +field. +Normally +.B qmail-inject +uses name-address style. +.TP +.B s +Do not look at any incoming +.B Return-Path +field. +Normally, if +.B Return-Path +is supplied, it sets the envelope sender address, +overriding all environment variables. +.B Return-Path +is deleted in any case. +.TP +.B f +Delete any incoming +.B From +field. +Normally, if +.B From +is supplied, it overrides the usual +.B From +field created by +.BR qmail-inject . +.TP +.B i +Delete any incoming +.B Message-ID +field. +Normally, if +.B Message-ID +is supplied, it overrides the usual +.B Message-ID +field created by +.BR qmail-inject . +.TP +.B r +Use a per-recipient VERP. +.B qmail-inject +will append each recipient address to the envelope sender +of the copy going to that recipient. +.TP +.B m +Use a per-message VERP. +.B qmail-inject +will append the current date and process ID to the envelope sender. +.SH OPTIONS +.TP +.B \-a +Send the message to all addresses given as +.I recip +arguments; +do not use header recipient addresses. +.TP +.B \-h +Send the message to all header recipient addresses. +For non-forwarded messages, this means +the addresses listed under +.BR To , +.BR Cc , +.BR Bcc , +.BR Apparently-To . +For forwarded messages, this means +the addresses listed under +.BR Resent-To , +.BR Resent-Cc , +.BR Resent-Bcc . +Do not use any +.I recip +arguments. +.TP +.B \-A +(Default.) +Send the message to all addresses given as +.I recip +arguments. +If no +.I recip +arguments are supplied, +send the message to all header recipient addresses. +.TP +.B \-H +Send the message to all header recipient addresses, +and to all addresses given as +.I recip +arguments. +.TP +.B \-f\fIsender +Pass +.I sender +to +.B qmail-queue +as the envelope sender address. +This overrides +.B Return-Path +and all environment variables. +.TP +.B \-N +(Default.) +Feed the resulting message to +.BR qmail-queue . +.TP +.B \-n +Print the message rather than feeding it to +.BR qmail-queue . +.SH "CONTROL FILES" +.TP 5 +.I defaultdomain +Default domain name. +Default: +.IR me , +if that is supplied; +otherwise the literal name +.BR defaultdomain , +which is probably not what you want. +.B qmail-inject +adds this name to any host name without dots, +including +.I defaulthost +if +.I defaulthost +does not have dots. +(Exception: see +.IR plusdomain .) + +The +.B QMAILDEFAULTDOMAIN +environment variable +overrides +.IR defaultdomain . +.TP 5 +.I defaulthost +Default host name. +Default: +.IR me , +if that is supplied; +otherwise the literal name +.BR defaulthost , +which is probably not what you want. +.B qmail-inject +adds this name to any address without a host name. +.I defaulthost +need not be the current host's name. +For example, +you may prefer that outgoing mail show +just your domain name. + +The +.B QMAILDEFAULTHOST +environment variable overrides +.IR defaulthost . +.TP 5 +.I idhost +Host name for Message-IDs. +Default: +.IR me , +if that is supplied; +otherwise the literal name +.BR idhost , +which is certainly not what you want. +.I idhost +need not be the current host's name. +For example, you may prefer to use fake +host names in Message-IDs. +However, +.I idhost +must be a fully-qualified name within your domain, +and each host in your domain should use a different +.IR idhost . + +The +.B QMAILIDHOST +environment variable overrides +.IR idhost . +.TP 5 +.I plusdomain +Plus domain name. +Default: +.IR me , +if that is supplied; +otherwise the literal name +.BR plusdomain , +which is probably not what you want. +.B qmail-inject +adds this name to any host name that ends with a plus sign, +including +.I defaulthost +if +.I defaulthost +ends with a plus sign. +If a host name does not have dots but ends with a plus sign, +.B qmail-inject +uses +.IR plusdomain , +not +.IR defaultdomain . + +The +.B QMAILPLUSDOMAIN +environment variable overrides +.IR plusdomain . +.SH "SEE ALSO" +addresses(5), +qmail-control(5), +qmail-header(5), +qmail-queue(8) diff --git a/qmail-inject.c b/qmail-inject.c new file mode 100644 index 0000000..753c18a --- /dev/null +++ b/qmail-inject.c @@ -0,0 +1,773 @@ +#include "sig.h" +#include "substdio.h" +#include "stralloc.h" +#include "subfd.h" +#include "sgetopt.h" +#include "getln.h" +#include "alloc.h" +#include "str.h" +#include "fmt.h" +#include "hfield.h" +#include "token822.h" +#include "control.h" +#include "env.h" +#include "gen_alloc.h" +#include "gen_allocdefs.h" +#include "error.h" +#include "qmail.h" +#include "now.h" +#include "exit.h" +#include "quote.h" +#include "headerbody.h" +#include "auto_qmail.h" +#include "newfield.h" +#include "constmap.h" + +#define LINELEN 80 + +datetime_sec starttime; + +char *qmopts; +int flagdeletesender = 0; +int flagdeletefrom = 0; +int flagdeletemessid = 0; +int flagnamecomment = 0; +int flaghackmess = 0; +int flaghackrecip = 0; +char *mailhost; +char *mailuser; +int mailusertokentype; +char *mailrhost; +char *mailruser; + +stralloc control_idhost = {0}; +stralloc control_defaultdomain = {0}; +stralloc control_defaulthost = {0}; +stralloc control_plusdomain = {0}; + +stralloc sender = {0}; +stralloc envsbuf = {0}; +token822_alloc envs = {0}; +int flagrh; + +int flagqueue; +struct qmail qqt; + +void put(s,len) char *s; int len; +{ if (flagqueue) qmail_put(&qqt,s,len); else substdio_put(subfdout,s,len); } +void puts(s) char *s; { put(s,str_len(s)); } + +void perm() { _exit(100); } +void temp() { _exit(111); } +void die_nomem() { + substdio_putsflush(subfderr,"qmail-inject: fatal: out of memory\n"); temp(); } +void die_invalid(sa) stralloc *sa; { + substdio_putsflush(subfderr,"qmail-inject: fatal: invalid header field: "); + substdio_putflush(subfderr,sa->s,sa->len); perm(); } +void die_qqt() { + substdio_putsflush(subfderr,"qmail-inject: fatal: unable to run qmail-queue\n"); temp(); } +void die_chdir() { + substdio_putsflush(subfderr,"qmail-inject: fatal: internal bug\n"); temp(); } +void die_read() { + if (errno == error_nomem) die_nomem(); + substdio_putsflush(subfderr,"qmail-inject: fatal: read error\n"); temp(); } +void doordie(sa,r) stralloc *sa; int r; { + if (r == 1) return; if (r == -1) die_nomem(); + substdio_putsflush(subfderr,"qmail-inject: fatal: unable to parse this line:\n"); + substdio_putflush(subfderr,sa->s,sa->len); perm(); } + +GEN_ALLOC_typedef(saa,stralloc,sa,len,a) +GEN_ALLOC_readyplus(saa,stralloc,sa,len,a,i,n,x,10,saa_readyplus) + +static stralloc sauninit = {0}; + +saa savedh = {0}; +saa hrlist = {0}; +saa tocclist = {0}; +saa hrrlist = {0}; +saa reciplist = {0}; +int flagresent; + +void exitnicely() +{ + char *qqx; + + if (!flagqueue) substdio_flush(subfdout); + + if (flagqueue) + { + int i; + + if (!stralloc_0(&sender)) die_nomem(); + qmail_from(&qqt,sender.s); + + for (i = 0;i < reciplist.len;++i) + { + if (!stralloc_0(&reciplist.sa[i])) die_nomem(); + qmail_to(&qqt,reciplist.sa[i].s); + } + if (flagrh) + if (flagresent) + for (i = 0;i < hrrlist.len;++i) + { + if (!stralloc_0(&hrrlist.sa[i])) die_nomem(); + qmail_to(&qqt,hrrlist.sa[i].s); + } + else + for (i = 0;i < hrlist.len;++i) + { + if (!stralloc_0(&hrlist.sa[i])) die_nomem(); + qmail_to(&qqt,hrlist.sa[i].s); + } + + qqx = qmail_close(&qqt); + if (*qqx) + if (*qqx == 'D') { + substdio_puts(subfderr,"qmail-inject: fatal: "); + substdio_puts(subfderr,qqx + 1); + substdio_puts(subfderr,"\n"); + substdio_flush(subfderr); + perm(); + } + else { + substdio_puts(subfderr,"qmail-inject: fatal: "); + substdio_puts(subfderr,qqx + 1); + substdio_puts(subfderr,"\n"); + substdio_flush(subfderr); + temp(); + } + } + + _exit(0); +} + +void savedh_append(h) +stralloc *h; +{ + if (!saa_readyplus(&savedh,1)) die_nomem(); + savedh.sa[savedh.len] = sauninit; + if (!stralloc_copy(savedh.sa + savedh.len,h)) die_nomem(); + ++savedh.len; +} + +void savedh_print() +{ + int i; + + for (i = 0;i < savedh.len;++i) + put(savedh.sa[i].s,savedh.sa[i].len); +} + +stralloc defaultdomainbuf = {0}; +token822_alloc defaultdomain = {0}; +stralloc defaulthostbuf = {0}; +token822_alloc defaulthost = {0}; +stralloc plusdomainbuf = {0}; +token822_alloc plusdomain = {0}; + +void rwroute(addr) +token822_alloc *addr; +{ + if (addr->t[addr->len - 1].type == TOKEN822_AT) + while (addr->len) + if (addr->t[--addr->len].type == TOKEN822_COLON) + return; +} + +void rwextraat(addr) +token822_alloc *addr; +{ + int i; + if (addr->t[0].type == TOKEN822_AT) + { + --addr->len; + for (i = 0;i < addr->len;++i) + addr->t[i] = addr->t[i + 1]; + } +} + +void rwextradot(addr) +token822_alloc *addr; +{ + int i; + if (addr->t[0].type == TOKEN822_DOT) + { + --addr->len; + for (i = 0;i < addr->len;++i) + addr->t[i] = addr->t[i + 1]; + } +} + +void rwnoat(addr) +token822_alloc *addr; +{ + int i; + int shift; + + for (i = 0;i < addr->len;++i) + if (addr->t[i].type == TOKEN822_AT) + return; + shift = defaulthost.len; + if (!token822_readyplus(addr,shift)) die_nomem(); + for (i = addr->len - 1;i >= 0;--i) + addr->t[i + shift] = addr->t[i]; + addr->len += shift; + for (i = 0;i < shift;++i) + addr->t[i] = defaulthost.t[shift - 1 - i]; +} + +void rwnodot(addr) +token822_alloc *addr; +{ + int i; + int shift; + for (i = 0;i < addr->len;++i) + { + if (addr->t[i].type == TOKEN822_DOT) + return; + if (addr->t[i].type == TOKEN822_AT) + break; + } + for (i = 0;i < addr->len;++i) + { + if (addr->t[i].type == TOKEN822_LITERAL) + return; + if (addr->t[i].type == TOKEN822_AT) + break; + } + shift = defaultdomain.len; + if (!token822_readyplus(addr,shift)) die_nomem(); + for (i = addr->len - 1;i >= 0;--i) + addr->t[i + shift] = addr->t[i]; + addr->len += shift; + for (i = 0;i < shift;++i) + addr->t[i] = defaultdomain.t[shift - 1 - i]; +} + +void rwplus(addr) +token822_alloc *addr; +{ + int i; + int shift; + + if (addr->t[0].type != TOKEN822_ATOM) return; + if (!addr->t[0].slen) return; + if (addr->t[0].s[addr->t[0].slen - 1] != '+') return; + + --addr->t[0].slen; /* remove + */ + + shift = plusdomain.len; + if (!token822_readyplus(addr,shift)) die_nomem(); + for (i = addr->len - 1;i >= 0;--i) + addr->t[i + shift] = addr->t[i]; + addr->len += shift; + for (i = 0;i < shift;++i) + addr->t[i] = plusdomain.t[shift - 1 - i]; +} + +void rwgeneric(addr) +token822_alloc *addr; +{ + if (!addr->len) return; /* don't rewrite <> */ + if (addr->len >= 2) + if (addr->t[1].type == TOKEN822_AT) + if (addr->t[0].type == TOKEN822_LITERAL) + if (!addr->t[0].slen) /* don't rewrite */ + return; + rwroute(addr); + if (!addr->len) return; /* <@foo:> -> <> */ + rwextradot(addr); + if (!addr->len) return; /* <.> -> <> */ + rwextraat(addr); + if (!addr->len) return; /* <@> -> <> */ + rwnoat(addr); + rwplus(addr); + rwnodot(addr); +} + +int setreturn(addr) +token822_alloc *addr; +{ + if (!sender.s) + { + token822_reverse(addr); + if (token822_unquote(&sender,addr) != 1) die_nomem(); + if (flaghackrecip) + if (!stralloc_cats(&sender,"-@[]")) die_nomem(); + token822_reverse(addr); + } + return 1; +} + +int rwreturn(addr) +token822_alloc *addr; +{ + rwgeneric(addr); + setreturn(addr); + return 1; +} + +int rwsender(addr) +token822_alloc *addr; +{ + rwgeneric(addr); + return 1; +} + +void rwappend(addr,xl) +token822_alloc *addr; +saa *xl; +{ + token822_reverse(addr); + if (!saa_readyplus(xl,1)) die_nomem(); + xl->sa[xl->len] = sauninit; + if (token822_unquote(&xl->sa[xl->len],addr) != 1) die_nomem(); + ++xl->len; + token822_reverse(addr); +} + +int rwhrr(addr) token822_alloc *addr; +{ rwgeneric(addr); rwappend(addr,&hrrlist); return 1; } +int rwhr(addr) token822_alloc *addr; +{ rwgeneric(addr); rwappend(addr,&hrlist); return 1; } +int rwtocc(addr) token822_alloc *addr; +{ rwgeneric(addr); rwappend(addr,&hrlist); rwappend(addr,&tocclist); return 1; } + +int htypeseen[H_NUM]; +stralloc hfbuf = {0}; +token822_alloc hfin = {0}; +token822_alloc hfrewrite = {0}; +token822_alloc hfaddr = {0}; + +void doheaderfield(h) +stralloc *h; +{ + int htype; + int (*rw)() = 0; + + htype = hfield_known(h->s,h->len); + if (flagdeletefrom) if (htype == H_FROM) return; + if (flagdeletemessid) if (htype == H_MESSAGEID) return; + if (flagdeletesender) if (htype == H_RETURNPATH) return; + + if (htype) + htypeseen[htype] = 1; + else + if (!hfield_valid(h->s,h->len)) + die_invalid(h); + + switch(htype) { + case H_TO: case H_CC: + rw = rwtocc; break; + case H_BCC: case H_APPARENTLYTO: + rw = rwhr; break; + case H_R_TO: case H_R_CC: case H_R_BCC: + rw = rwhrr; break; + case H_RETURNPATH: + rw = rwreturn; break; + case H_SENDER: case H_FROM: case H_REPLYTO: + case H_RETURNRECEIPTTO: case H_ERRORSTO: + case H_R_SENDER: case H_R_FROM: case H_R_REPLYTO: + rw = rwsender; break; + } + + if (rw) { + doordie(h,token822_parse(&hfin,h,&hfbuf)); + doordie(h,token822_addrlist(&hfrewrite,&hfaddr,&hfin,rw)); + if (token822_unparse(h,&hfrewrite,LINELEN) != 1) + die_nomem(); + } + + if (htype == H_BCC) return; + if (htype == H_R_BCC) return; + if (htype == H_RETURNPATH) return; + if (htype == H_CONTENTLENGTH) return; /* some things are just too stupid */ + savedh_append(h); +} + +void dobody(h) +stralloc *h; +{ + put(h->s,h->len); +} + +stralloc torecip = {0}; +token822_alloc tr = {0}; + +void dorecip(s) +char *s; +{ + if (!quote2(&torecip,s)) die_nomem(); + switch(token822_parse(&tr,&torecip,&hfbuf)) + { + case -1: die_nomem(); + case 0: + substdio_puts(subfderr,"qmail-inject: fatal: unable to parse address: "); + substdio_puts(subfderr,s); + substdio_putsflush(subfderr,"\n"); + perm(); + } + token822_reverse(&tr); + rwgeneric(&tr); + rwappend(&tr,&reciplist); +} + +stralloc defaultfrom = {0}; +token822_alloc df = {0}; + +void defaultfrommake() +{ + char *fullname; + fullname = env_get("QMAILNAME"); + if (!fullname) fullname = env_get("MAILNAME"); + if (!fullname) fullname = env_get("NAME"); + if (!token822_ready(&df,20)) die_nomem(); + df.len = 0; + df.t[df.len].type = TOKEN822_ATOM; + df.t[df.len].s = "From"; + df.t[df.len].slen = 4; + ++df.len; + df.t[df.len].type = TOKEN822_COLON; + ++df.len; + if (fullname && !flagnamecomment) + { + df.t[df.len].type = TOKEN822_QUOTE; + df.t[df.len].s = fullname; + df.t[df.len].slen = str_len(fullname); + ++df.len; + df.t[df.len].type = TOKEN822_LEFT; + ++df.len; + } + df.t[df.len].type = mailusertokentype; + df.t[df.len].s = mailuser; + df.t[df.len].slen = str_len(mailuser); + ++df.len; + if (mailhost) + { + df.t[df.len].type = TOKEN822_AT; + ++df.len; + df.t[df.len].type = TOKEN822_ATOM; + df.t[df.len].s = mailhost; + df.t[df.len].slen = str_len(mailhost); + ++df.len; + } + if (fullname && !flagnamecomment) + { + df.t[df.len].type = TOKEN822_RIGHT; + ++df.len; + } + if (fullname && flagnamecomment) + { + df.t[df.len].type = TOKEN822_COMMENT; + df.t[df.len].s = fullname; + df.t[df.len].slen = str_len(fullname); + ++df.len; + } + if (token822_unparse(&defaultfrom,&df,LINELEN) != 1) die_nomem(); + doordie(&defaultfrom,token822_parse(&df,&defaultfrom,&hfbuf)); + doordie(&defaultfrom,token822_addrlist(&hfrewrite,&hfaddr,&df,rwsender)); + if (token822_unparse(&defaultfrom,&hfrewrite,LINELEN) != 1) die_nomem(); +} + +stralloc defaultreturnpath = {0}; +token822_alloc drp = {0}; +stralloc hackedruser = {0}; +char strnum[FMT_ULONG]; + +void dodefaultreturnpath() +{ + if (!stralloc_copys(&hackedruser,mailruser)) die_nomem(); + if (flaghackmess) + { + if (!stralloc_cats(&hackedruser,"-")) die_nomem(); + if (!stralloc_catb(&hackedruser,strnum,fmt_ulong(strnum,(unsigned long) starttime))) die_nomem(); + if (!stralloc_cats(&hackedruser,".")) die_nomem(); + if (!stralloc_catb(&hackedruser,strnum,fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem(); + } + if (flaghackrecip) + if (!stralloc_cats(&hackedruser,"-")) die_nomem(); + if (!token822_ready(&drp,10)) die_nomem(); + drp.len = 0; + drp.t[drp.len].type = TOKEN822_ATOM; + drp.t[drp.len].s = "Return-Path"; + drp.t[drp.len].slen = 11; + ++drp.len; + drp.t[drp.len].type = TOKEN822_COLON; + ++drp.len; + drp.t[drp.len].type = TOKEN822_QUOTE; + drp.t[drp.len].s = hackedruser.s; + drp.t[drp.len].slen = hackedruser.len; + ++drp.len; + if (mailrhost) + { + drp.t[drp.len].type = TOKEN822_AT; + ++drp.len; + drp.t[drp.len].type = TOKEN822_ATOM; + drp.t[drp.len].s = mailrhost; + drp.t[drp.len].slen = str_len(mailrhost); + ++drp.len; + } + if (token822_unparse(&defaultreturnpath,&drp,LINELEN) != 1) die_nomem(); + doordie(&defaultreturnpath,token822_parse(&drp,&defaultreturnpath,&hfbuf)); + doordie(&defaultreturnpath + ,token822_addrlist(&hfrewrite,&hfaddr,&drp,rwreturn)); + if (token822_unparse(&defaultreturnpath,&hfrewrite,LINELEN) != 1) die_nomem(); +} + +int flagmft = 0; +stralloc mft = {0}; +struct constmap mapmft; + +void mft_init() +{ + char *x; + int r; + + x = env_get("QMAILMFTFILE"); + if (!x) return; + + r = control_readfile(&mft,x,0); + if (r == -1) die_read(); /*XXX*/ + if (!r) return; + + if (!constmap_init(&mapmft,mft.s,mft.len,0)) die_nomem(); + flagmft = 1; +} + +void finishmft() +{ + int i; + static stralloc sa = {0}; + static stralloc sa2 = {0}; + + if (!flagmft) return; + if (htypeseen[H_MAILFOLLOWUPTO]) return; + + for (i = 0;i < tocclist.len;++i) + if (constmap(&mapmft,tocclist.sa[i].s,tocclist.sa[i].len)) + break; + + if (i == tocclist.len) return; + + puts("Mail-Followup-To: "); + i = tocclist.len; + while (i--) { + if (!stralloc_copy(&sa,&tocclist.sa[i])) die_nomem(); + if (!stralloc_0(&sa)) die_nomem(); + if (!quote2(&sa2,sa.s)) die_nomem(); + put(sa2.s,sa2.len); + if (i) puts(",\n "); + } + puts("\n"); +} + +void finishheader() +{ + flagresent = + htypeseen[H_R_SENDER] || htypeseen[H_R_FROM] || htypeseen[H_R_REPLYTO] + || htypeseen[H_R_TO] || htypeseen[H_R_CC] || htypeseen[H_R_BCC] + || htypeseen[H_R_DATE] || htypeseen[H_R_MESSAGEID]; + + if (!sender.s) + dodefaultreturnpath(); + + if (!flagqueue) + { + static stralloc sa = {0}; + static stralloc sa2 = {0}; + + if (!stralloc_copy(&sa,&sender)) die_nomem(); + if (!stralloc_0(&sa)) die_nomem(); + if (!quote2(&sa2,sa.s)) die_nomem(); + + puts("Return-Path: <"); + put(sa2.s,sa2.len); + puts(">\n"); + } + + /* could check at this point whether there are any recipients */ + if (flagqueue) + if (qmail_open(&qqt) == -1) die_qqt(); + + if (flagresent) + { + if (!htypeseen[H_R_DATE]) + { + if (!newfield_datemake(starttime)) die_nomem(); + puts("Resent-"); + put(newfield_date.s,newfield_date.len); + } + if (!htypeseen[H_R_MESSAGEID]) + { + if (!newfield_msgidmake(control_idhost.s,control_idhost.len,starttime)) die_nomem(); + puts("Resent-"); + put(newfield_msgid.s,newfield_msgid.len); + } + if (!htypeseen[H_R_FROM]) + { + defaultfrommake(); + puts("Resent-"); + put(defaultfrom.s,defaultfrom.len); + } + if (!htypeseen[H_R_TO] && !htypeseen[H_R_CC]) + puts("Resent-Cc: recipient list not shown: ;\n"); + } + else + { + if (!htypeseen[H_DATE]) + { + if (!newfield_datemake(starttime)) die_nomem(); + put(newfield_date.s,newfield_date.len); + } + if (!htypeseen[H_MESSAGEID]) + { + if (!newfield_msgidmake(control_idhost.s,control_idhost.len,starttime)) die_nomem(); + put(newfield_msgid.s,newfield_msgid.len); + } + if (!htypeseen[H_FROM]) + { + defaultfrommake(); + put(defaultfrom.s,defaultfrom.len); + } + if (!htypeseen[H_TO] && !htypeseen[H_CC]) + puts("Cc: recipient list not shown: ;\n"); + finishmft(); + } + + savedh_print(); +} + +void getcontrols() +{ + static stralloc sa = {0}; + char *x; + + mft_init(); + + if (chdir(auto_qmail) == -1) die_chdir(); + if (control_init() == -1) die_read(); + + if (control_rldef(&control_defaultdomain,"control/defaultdomain",1,"defaultdomain") != 1) + die_read(); + x = env_get("QMAILDEFAULTDOMAIN"); + if (x) if (!stralloc_copys(&control_defaultdomain,x)) die_nomem(); + if (!stralloc_copys(&sa,".")) die_nomem(); + if (!stralloc_cat(&sa,&control_defaultdomain)) die_nomem(); + doordie(&sa,token822_parse(&defaultdomain,&sa,&defaultdomainbuf)); + + if (control_rldef(&control_defaulthost,"control/defaulthost",1,"defaulthost") != 1) + die_read(); + x = env_get("QMAILDEFAULTHOST"); + if (x) if (!stralloc_copys(&control_defaulthost,x)) die_nomem(); + if (!stralloc_copys(&sa,"@")) die_nomem(); + if (!stralloc_cat(&sa,&control_defaulthost)) die_nomem(); + doordie(&sa,token822_parse(&defaulthost,&sa,&defaulthostbuf)); + + if (control_rldef(&control_plusdomain,"control/plusdomain",1,"plusdomain") != 1) + die_read(); + x = env_get("QMAILPLUSDOMAIN"); + if (x) if (!stralloc_copys(&control_plusdomain,x)) die_nomem(); + if (!stralloc_copys(&sa,".")) die_nomem(); + if (!stralloc_cat(&sa,&control_plusdomain)) die_nomem(); + doordie(&sa,token822_parse(&plusdomain,&sa,&plusdomainbuf)); + + if (control_rldef(&control_idhost,"control/idhost",1,"idhost") != 1) + die_read(); + x = env_get("QMAILIDHOST"); + if (x) if (!stralloc_copys(&control_idhost,x)) die_nomem(); +} + +#define RECIP_DEFAULT 1 +#define RECIP_ARGS 2 +#define RECIP_HEADER 3 +#define RECIP_AH 4 + +void main(argc,argv) +int argc; +char **argv; +{ + int i; + int opt; + int recipstrategy; + + sig_pipeignore(); + + starttime = now(); + + qmopts = env_get("QMAILINJECT"); + if (qmopts) + while (*qmopts) + switch(*qmopts++) + { + case 'c': flagnamecomment = 1; break; + case 's': flagdeletesender = 1; break; + case 'f': flagdeletefrom = 1; break; + case 'i': flagdeletemessid = 1; break; + case 'r': flaghackrecip = 1; break; + case 'm': flaghackmess = 1; break; + } + + mailhost = env_get("QMAILHOST"); + if (!mailhost) mailhost = env_get("MAILHOST"); + mailrhost = env_get("QMAILSHOST"); + if (!mailrhost) mailrhost = mailhost; + + mailuser = env_get("QMAILUSER"); + if (!mailuser) mailuser = env_get("MAILUSER"); + if (!mailuser) mailuser = env_get("USER"); + if (!mailuser) mailuser = env_get("LOGNAME"); + if (!mailuser) mailuser = "anonymous"; + mailusertokentype = TOKEN822_ATOM; + if (quote_need(mailuser,str_len(mailuser))) mailusertokentype = TOKEN822_QUOTE; + mailruser = env_get("QMAILSUSER"); + if (!mailruser) mailruser = mailuser; + + for (i = 0;i < H_NUM;++i) htypeseen[i] = 0; + + recipstrategy = RECIP_DEFAULT; + flagqueue = 1; + + getcontrols(); + + if (!saa_readyplus(&hrlist,1)) die_nomem(); + if (!saa_readyplus(&tocclist,1)) die_nomem(); + if (!saa_readyplus(&hrrlist,1)) die_nomem(); + if (!saa_readyplus(&reciplist,1)) die_nomem(); + + while ((opt = getopt(argc,argv,"aAhHnNf:")) != opteof) + switch(opt) + { + case 'a': recipstrategy = RECIP_ARGS; break; + case 'A': recipstrategy = RECIP_DEFAULT; break; + case 'h': recipstrategy = RECIP_HEADER; break; + case 'H': recipstrategy = RECIP_AH; break; + case 'n': flagqueue = 0; break; + case 'N': flagqueue = 1; break; + case 'f': + if (!quote2(&sender,optarg)) die_nomem(); + doordie(&sender,token822_parse(&envs,&sender,&envsbuf)); + token822_reverse(&envs); + rwgeneric(&envs); + token822_reverse(&envs); + if (token822_unquote(&sender,&envs) != 1) die_nomem(); + break; + case '?': + default: + perm(); + } + argc -= optind; + argv += optind; + + if (recipstrategy == RECIP_DEFAULT) + recipstrategy = (*argv ? RECIP_ARGS : RECIP_HEADER); + + if (recipstrategy != RECIP_HEADER) + while (*argv) + dorecip(*argv++); + + flagrh = (recipstrategy != RECIP_ARGS); + + if (headerbody(subfdin,doheaderfield,finishheader,dobody) == -1) + die_read(); + exitnicely(); +} diff --git a/qmail-limits.9 b/qmail-limits.9 new file mode 100644 index 0000000..dca5426 --- /dev/null +++ b/qmail-limits.9 @@ -0,0 +1,30 @@ +.TH qmail-limits 7 +.SH "NAME" +qmail-limits \- artificial limits in the qmail system +.SH "DESCRIPTION" +The +.B qmail +system is able to handle messages of any size, +addresses of any size, mailing lists of any size, and so on, +except as limited by the available memory and disk space. + +However, it imposes certain artificial limits: +.TP 5 +1. +.B qmail-lspawn +silently limits the number of simultaneous local deliveries to SPAWN. +.B qmail-rspawn +silently limits the number of simultaneous remote deliveries to SPAWN. +.TP 5 +2. +.B qmail-queue +rejects any message with an envelope address longer than 1000 characters. +.TP 5 +3. +.B qmail-lspawn +truncates any overly long error report from a delivery program. +It appends a note saying that it did so. +.SH "SEE ALSO" +qmail-lspawn(8), +qmail-queue(8), +qmail-rspawn(8) diff --git a/qmail-local.8 b/qmail-local.8 new file mode 100644 index 0000000..c047697 --- /dev/null +++ b/qmail-local.8 @@ -0,0 +1,99 @@ +.TH qmail-local 8 +.SH NAME +qmail-local \- deliver or forward a mail message +.SH SYNOPSIS +.B qmail-local +[ +.B \-nN +] +.I user +.I homedir +.I local +.I dash +.I ext +.I domain +.I sender +.I defaultdelivery +.SH DESCRIPTION +.B qmail-local +reads a mail message +and delivers it to +.I user +by the procedure described in +.BR dot-qmail(5) . + +The message's envelope recipient is +.IR local@domain . +.B qmail-local +records +.I local@domain +in a new +.B Delivered-To +header field. +If exactly the same +.B Delivered-To: \fIlocal@domain +already appears in the header, +.B qmail-local +bounces the message, +to prevent mail forwarding loops. + +The message's envelope sender is +.IR sender . +.B qmail-local +records +.I sender +in a new +.B Return-Path +header field. + +.I homedir +is the user's home directory. +It must be an absolute directory name. + +.I dash +and +.I ext +identify the +.B .qmail\fIdashext +file used by +.BR qmail-local ; +see +.BR dot-qmail(5) . +Normally +.I dash +is either empty or a lone hyphen. +If it is empty, +.B qmail-local +treats a nonexistent +.B .qmail\fIext +the same way as an empty +.BR .qmail\fIext : +namely, following the delivery instructions in +.IR defaultdelivery . + +The standard input for +.B qmail-local +must be a seekable file, +so that +.B qmail-local +can read it more than once. +.SH "OPTIONS" +.TP +.B \-n +Instead of reading and delivering the message, +print a description of the delivery instructions. +.TP +.B \-N +(Default.) Read and deliver the message. +.SH "EXIT CODES" +0 if the delivery is completely successful; +nonzero if any delivery instruction failed. +Exit code 111 +indicates temporary failure. +.SH "SEE ALSO" +dot-qmail(5), +envelopes(5), +qmail-command(8), +qmail-queue(8), +qmail-send(8), +qmail-lspawn(8) diff --git a/qmail-local.c b/qmail-local.c new file mode 100644 index 0000000..6fec288 --- /dev/null +++ b/qmail-local.c @@ -0,0 +1,698 @@ +#include +#include +#include "readwrite.h" +#include "sig.h" +#include "env.h" +#include "byte.h" +#include "exit.h" +#include "fork.h" +#include "open.h" +#include "wait.h" +#include "lock.h" +#include "seek.h" +#include "substdio.h" +#include "getln.h" +#include "strerr.h" +#include "subfd.h" +#include "sgetopt.h" +#include "alloc.h" +#include "error.h" +#include "stralloc.h" +#include "fmt.h" +#include "str.h" +#include "now.h" +#include "case.h" +#include "quote.h" +#include "qmail.h" +#include "slurpclose.h" +#include "myctime.h" +#include "gfrom.h" +#include "auto_patrn.h" + +void usage() { strerr_die1x(100,"qmail-local: usage: qmail-local [ -nN ] user homedir local dash ext domain sender aliasempty"); } + +void temp_nomem() { strerr_die1x(111,"Out of memory. (#4.3.0)"); } +void temp_rewind() { strerr_die1x(111,"Unable to rewind message. (#4.3.0)"); } +void temp_childcrashed() { strerr_die1x(111,"Aack, child crashed. (#4.3.0)"); } +void temp_fork() { strerr_die3x(111,"Unable to fork: ",error_str(errno),". (#4.3.0)"); } +void temp_read() { strerr_die3x(111,"Unable to read message: ",error_str(errno),". (#4.3.0)"); } +void temp_slowlock() +{ strerr_die1x(111,"File has been locked for 30 seconds straight. (#4.3.0)"); } +void temp_qmail(fn) char *fn; +{ strerr_die5x(111,"Unable to open ",fn,": ",error_str(errno),". (#4.3.0)"); } + +int flagdoit; +int flag99; + +char *user; +char *homedir; +char *local; +char *dash; +char *ext; +char *host; +char *sender; +char *aliasempty; + +stralloc safeext = {0}; +stralloc ufline = {0}; +stralloc rpline = {0}; +stralloc envrecip = {0}; +stralloc dtline = {0}; +stralloc qme = {0}; +stralloc ueo = {0}; +stralloc cmds = {0}; +stralloc messline = {0}; +stralloc foo = {0}; + +char buf[1024]; +char outbuf[1024]; + +/* child process */ + +char fntmptph[80 + FMT_ULONG * 2]; +char fnnewtph[80 + FMT_ULONG * 2]; +void tryunlinktmp() { unlink(fntmptph); } +void sigalrm() { tryunlinktmp(); _exit(3); } + +void maildir_child(dir) +char *dir; +{ + unsigned long pid; + unsigned long time; + char host[64]; + char *s; + int loop; + struct stat st; + int fd; + substdio ss; + substdio ssout; + + sig_alarmcatch(sigalrm); + if (chdir(dir) == -1) { if (error_temp(errno)) _exit(1); _exit(2); } + pid = getpid(); + host[0] = 0; + gethostname(host,sizeof(host)); + for (loop = 0;;++loop) + { + time = now(); + s = fntmptph; + s += fmt_str(s,"tmp/"); + s += fmt_ulong(s,time); *s++ = '.'; + s += fmt_ulong(s,pid); *s++ = '.'; + s += fmt_strn(s,host,sizeof(host)); *s++ = 0; + if (stat(fntmptph,&st) == -1) if (errno == error_noent) break; + /* really should never get to this point */ + if (loop == 2) _exit(1); + sleep(2); + } + str_copy(fnnewtph,fntmptph); + byte_copy(fnnewtph,3,"new"); + + alarm(86400); + fd = open_excl(fntmptph); + if (fd == -1) _exit(1); + + substdio_fdbuf(&ss,read,0,buf,sizeof(buf)); + substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf)); + if (substdio_put(&ssout,rpline.s,rpline.len) == -1) goto fail; + if (substdio_put(&ssout,dtline.s,dtline.len) == -1) goto fail; + + switch(substdio_copy(&ssout,&ss)) + { + case -2: tryunlinktmp(); _exit(4); + case -3: goto fail; + } + + if (substdio_flush(&ssout) == -1) goto fail; + if (fsync(fd) == -1) goto fail; + if (close(fd) == -1) goto fail; /* NFS dorks */ + + if (link(fntmptph,fnnewtph) == -1) goto fail; + /* if it was error_exist, almost certainly successful; i hate NFS */ + tryunlinktmp(); _exit(0); + + fail: tryunlinktmp(); _exit(1); +} + +/* end child process */ + +void maildir(fn) +char *fn; +{ + int child; + int wstat; + + if (seek_begin(0) == -1) temp_rewind(); + + switch(child = fork()) + { + case -1: + temp_fork(); + case 0: + maildir_child(fn); + _exit(111); + } + + wait_pid(&wstat,child); + if (wait_crashed(wstat)) + temp_childcrashed(); + switch(wait_exitcode(wstat)) + { + case 0: break; + case 2: strerr_die1x(111,"Unable to chdir to maildir. (#4.2.1)"); + case 3: strerr_die1x(111,"Timeout on maildir delivery. (#4.3.0)"); + case 4: strerr_die1x(111,"Unable to read message. (#4.3.0)"); + default: strerr_die1x(111,"Temporary error on maildir delivery. (#4.3.0)"); + } +} + +void mailfile(fn) +char *fn; +{ + int fd; + substdio ss; + substdio ssout; + int match; + seek_pos pos; + int flaglocked; + + if (seek_begin(0) == -1) temp_rewind(); + + fd = open_append(fn); + if (fd == -1) + strerr_die5x(111,"Unable to open ",fn,": ",error_str(errno),". (#4.2.1)"); + + sig_alarmcatch(temp_slowlock); + alarm(30); + flaglocked = (lock_ex(fd) != -1); + alarm(0); + sig_alarmdefault(); + + seek_end(fd); + pos = seek_cur(fd); + + substdio_fdbuf(&ss,read,0,buf,sizeof(buf)); + substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf)); + if (substdio_put(&ssout,ufline.s,ufline.len)) goto writeerrs; + if (substdio_put(&ssout,rpline.s,rpline.len)) goto writeerrs; + if (substdio_put(&ssout,dtline.s,dtline.len)) goto writeerrs; + for (;;) + { + if (getln(&ss,&messline,&match,'\n') != 0) + { + strerr_warn3("Unable to read message: ",error_str(errno),". (#4.3.0)",0); + if (flaglocked) seek_trunc(fd,pos); close(fd); + _exit(111); + } + if (!match && !messline.len) break; + if (gfrom(messline.s,messline.len)) + if (substdio_bput(&ssout,">",1)) goto writeerrs; + if (substdio_bput(&ssout,messline.s,messline.len)) goto writeerrs; + if (!match) + { + if (substdio_bputs(&ssout,"\n")) goto writeerrs; + break; + } + } + if (substdio_bputs(&ssout,"\n")) goto writeerrs; + if (substdio_flush(&ssout)) goto writeerrs; + if (fsync(fd) == -1) goto writeerrs; + close(fd); + return; + + writeerrs: + strerr_warn5("Unable to write ",fn,": ",error_str(errno),". (#4.3.0)",0); + if (flaglocked) seek_trunc(fd,pos); + close(fd); + _exit(111); +} + +void mailprogram(prog) +char *prog; +{ + int child; + char *(args[4]); + int wstat; + + if (seek_begin(0) == -1) temp_rewind(); + + switch(child = fork()) + { + case -1: + temp_fork(); + case 0: + args[0] = "/bin/sh"; args[1] = "-c"; args[2] = prog; args[3] = 0; + sig_pipedefault(); + execv(*args,args); + strerr_die3x(111,"Unable to run /bin/sh: ",error_str(errno),". (#4.3.0)"); + } + + wait_pid(&wstat,child); + if (wait_crashed(wstat)) + temp_childcrashed(); + switch(wait_exitcode(wstat)) + { + case 100: + case 64: case 65: case 70: case 76: case 77: case 78: case 112: _exit(100); + case 0: break; + case 99: flag99 = 1; break; + default: _exit(111); + } +} + +unsigned long mailforward_qp = 0; + +void mailforward(recips) +char **recips; +{ + struct qmail qqt; + char *qqx; + substdio ss; + int match; + + if (seek_begin(0) == -1) temp_rewind(); + substdio_fdbuf(&ss,read,0,buf,sizeof(buf)); + + if (qmail_open(&qqt) == -1) temp_fork(); + mailforward_qp = qmail_qp(&qqt); + qmail_put(&qqt,dtline.s,dtline.len); + do + { + if (getln(&ss,&messline,&match,'\n') != 0) { qmail_fail(&qqt); break; } + qmail_put(&qqt,messline.s,messline.len); + } + while (match); + qmail_from(&qqt,ueo.s); + while (*recips) qmail_to(&qqt,*recips++); + qqx = qmail_close(&qqt); + if (!*qqx) return; + strerr_die3x(*qqx == 'D' ? 100 : 111,"Unable to forward message: ",qqx + 1,"."); +} + +void bouncexf() +{ + int match; + substdio ss; + + if (seek_begin(0) == -1) temp_rewind(); + substdio_fdbuf(&ss,read,0,buf,sizeof(buf)); + for (;;) + { + if (getln(&ss,&messline,&match,'\n') != 0) temp_read(); + if (!match) break; + if (messline.len <= 1) + break; + if (messline.len == dtline.len) + if (!str_diffn(messline.s,dtline.s,dtline.len)) + strerr_die1x(100,"This message is looping: it already has my Delivered-To line. (#5.4.6)"); + } +} + +void checkhome() +{ + struct stat st; + + if (stat(".",&st) == -1) + strerr_die3x(111,"Unable to stat home directory: ",error_str(errno),". (#4.3.0)"); + if (st.st_mode & auto_patrn) + strerr_die1x(111,"Uh-oh: home directory is writable. (#4.7.0)"); + if (st.st_mode & 01000) + if (flagdoit) + strerr_die1x(111,"Home directory is sticky: user is editing his .qmail file. (#4.2.1)"); + else + strerr_warn1("Warning: home directory is sticky.",0); +} + +int qmeox(dashowner) +char *dashowner; +{ + struct stat st; + + if (!stralloc_copys(&qme,".qmail")) temp_nomem(); + if (!stralloc_cats(&qme,dash)) temp_nomem(); + if (!stralloc_cat(&qme,&safeext)) temp_nomem(); + if (!stralloc_cats(&qme,dashowner)) temp_nomem(); + if (!stralloc_0(&qme)) temp_nomem(); + if (stat(qme.s,&st) == -1) + { + if (error_temp(errno)) temp_qmail(qme.s); + return -1; + } + return 0; +} + +int qmeexists(fd,cutable) +int *fd; +int *cutable; +{ + struct stat st; + + if (!stralloc_0(&qme)) temp_nomem(); + + *fd = open_read(qme.s); + if (*fd == -1) { + if (error_temp(errno)) temp_qmail(qme.s); + if (errno == error_perm) temp_qmail(qme.s); + if (errno == error_acces) temp_qmail(qme.s); + return 0; + } + + if (fstat(*fd,&st) == -1) temp_qmail(qme.s); + if ((st.st_mode & S_IFMT) == S_IFREG) { + if (st.st_mode & auto_patrn) + strerr_die1x(111,"Uh-oh: .qmail file is writable. (#4.7.0)"); + *cutable = !!(st.st_mode & 0100); + return 1; + } + close(*fd); + return 0; +} + +/* "" "": "" */ +/* "-/" "": "-/" "-/default" */ +/* "-/" "a": "-/a" "-/default" */ +/* "-/" "a-": "-/a-" "-/a-default" "-/default" */ +/* "-/" "a-b": "-/a-b" "-/a-default" "-/default" */ +/* "-/" "a-b-": "-/a-b-" "-/a-b-default" "-/a-default" "-/default" */ +/* "-/" "a-b-c": "-/a-b-c" "-/a-b-default" "-/a-default" "-/default" */ + +void qmesearch(fd,cutable) +int *fd; +int *cutable; +{ + int i; + + if (!stralloc_copys(&qme,".qmail")) temp_nomem(); + if (!stralloc_cats(&qme,dash)) temp_nomem(); + if (!stralloc_cat(&qme,&safeext)) temp_nomem(); + if (qmeexists(fd,cutable)) { + if (safeext.len >= 7) { + i = safeext.len - 7; + if (!byte_diff("default",7,safeext.s + i)) + if (i <= str_len(ext)) /* paranoia */ + if (!env_put2("DEFAULT",ext + i)) temp_nomem(); + } + return; + } + + for (i = safeext.len;i >= 0;--i) + if (!i || (safeext.s[i - 1] == '-')) { + if (!stralloc_copys(&qme,".qmail")) temp_nomem(); + if (!stralloc_cats(&qme,dash)) temp_nomem(); + if (!stralloc_catb(&qme,safeext.s,i)) temp_nomem(); + if (!stralloc_cats(&qme,"default")) temp_nomem(); + if (qmeexists(fd,cutable)) { + if (i <= str_len(ext)) /* paranoia */ + if (!env_put2("DEFAULT",ext + i)) temp_nomem(); + return; + } + } + + *fd = -1; +} + +unsigned long count_file = 0; +unsigned long count_forward = 0; +unsigned long count_program = 0; +char count_buf[FMT_ULONG]; + +void count_print() +{ + substdio_puts(subfdoutsmall,"did "); + substdio_put(subfdoutsmall,count_buf,fmt_ulong(count_buf,count_file)); + substdio_puts(subfdoutsmall,"+"); + substdio_put(subfdoutsmall,count_buf,fmt_ulong(count_buf,count_forward)); + substdio_puts(subfdoutsmall,"+"); + substdio_put(subfdoutsmall,count_buf,fmt_ulong(count_buf,count_program)); + substdio_puts(subfdoutsmall,"\n"); + if (mailforward_qp) + { + substdio_puts(subfdoutsmall,"qp "); + substdio_put(subfdoutsmall,count_buf,fmt_ulong(count_buf,mailforward_qp)); + substdio_puts(subfdoutsmall,"\n"); + } + substdio_flush(subfdoutsmall); +} + +void sayit(type,cmd,len) +char *type; +char *cmd; +int len; +{ + substdio_puts(subfdoutsmall,type); + substdio_put(subfdoutsmall,cmd,len); + substdio_putsflush(subfdoutsmall,"\n"); +} + +void main(argc,argv) +int argc; +char **argv; +{ + int opt; + int i; + int j; + int k; + int fd; + int numforward; + char **recips; + datetime_sec starttime; + int flagforwardonly; + char *x; + + umask(077); + sig_pipeignore(); + + if (!env_init()) temp_nomem(); + + flagdoit = 1; + while ((opt = getopt(argc,argv,"nN")) != opteof) + switch(opt) + { + case 'n': flagdoit = 0; break; + case 'N': flagdoit = 1; break; + default: + usage(); + } + argc -= optind; + argv += optind; + + if (!(user = *argv++)) usage(); + if (!(homedir = *argv++)) usage(); + if (!(local = *argv++)) usage(); + if (!(dash = *argv++)) usage(); + if (!(ext = *argv++)) usage(); + if (!(host = *argv++)) usage(); + if (!(sender = *argv++)) usage(); + if (!(aliasempty = *argv++)) usage(); + if (*argv) usage(); + + if (homedir[0] != '/') usage(); + if (chdir(homedir) == -1) + strerr_die5x(111,"Unable to switch to ",homedir,": ",error_str(errno),". (#4.3.0)"); + checkhome(); + + if (!env_put2("HOST",host)) temp_nomem(); + if (!env_put2("HOME",homedir)) temp_nomem(); + if (!env_put2("USER",user)) temp_nomem(); + if (!env_put2("LOCAL",local)) temp_nomem(); + + if (!stralloc_copys(&envrecip,local)) temp_nomem(); + if (!stralloc_cats(&envrecip,"@")) temp_nomem(); + if (!stralloc_cats(&envrecip,host)) temp_nomem(); + + if (!stralloc_copy(&foo,&envrecip)) temp_nomem(); + if (!stralloc_0(&foo)) temp_nomem(); + if (!env_put2("RECIPIENT",foo.s)) temp_nomem(); + + if (!stralloc_copys(&dtline,"Delivered-To: ")) temp_nomem(); + if (!stralloc_cat(&dtline,&envrecip)) temp_nomem(); + for (i = 0;i < dtline.len;++i) if (dtline.s[i] == '\n') dtline.s[i] = '_'; + if (!stralloc_cats(&dtline,"\n")) temp_nomem(); + + if (!stralloc_copy(&foo,&dtline)) temp_nomem(); + if (!stralloc_0(&foo)) temp_nomem(); + if (!env_put2("DTLINE",foo.s)) temp_nomem(); + + if (flagdoit) + bouncexf(); + + if (!env_put2("SENDER",sender)) temp_nomem(); + + if (!quote2(&foo,sender)) temp_nomem(); + if (!stralloc_copys(&rpline,"Return-Path: <")) temp_nomem(); + if (!stralloc_cat(&rpline,&foo)) temp_nomem(); + for (i = 0;i < rpline.len;++i) if (rpline.s[i] == '\n') rpline.s[i] = '_'; + if (!stralloc_cats(&rpline,">\n")) temp_nomem(); + + if (!stralloc_copy(&foo,&rpline)) temp_nomem(); + if (!stralloc_0(&foo)) temp_nomem(); + if (!env_put2("RPLINE",foo.s)) temp_nomem(); + + if (!stralloc_copys(&ufline,"From ")) temp_nomem(); + if (*sender) + { + int len; int i; char ch; + + len = str_len(sender); + if (!stralloc_readyplus(&ufline,len)) temp_nomem(); + for (i = 0;i < len;++i) + { + ch = sender[i]; + if ((ch == ' ') || (ch == '\t') || (ch == '\n')) ch = '-'; + ufline.s[ufline.len + i] = ch; + } + ufline.len += len; + } + else + if (!stralloc_cats(&ufline,"MAILER-DAEMON")) temp_nomem(); + if (!stralloc_cats(&ufline," ")) temp_nomem(); + starttime = now(); + if (!stralloc_cats(&ufline,myctime(starttime))) temp_nomem(); + + if (!stralloc_copy(&foo,&ufline)) temp_nomem(); + if (!stralloc_0(&foo)) temp_nomem(); + if (!env_put2("UFLINE",foo.s)) temp_nomem(); + + x = ext; + if (!env_put2("EXT",x)) temp_nomem(); + x += str_chr(x,'-'); if (*x) ++x; + if (!env_put2("EXT2",x)) temp_nomem(); + x += str_chr(x,'-'); if (*x) ++x; + if (!env_put2("EXT3",x)) temp_nomem(); + x += str_chr(x,'-'); if (*x) ++x; + if (!env_put2("EXT4",x)) temp_nomem(); + + if (!stralloc_copys(&safeext,ext)) temp_nomem(); + case_lowerb(safeext.s,safeext.len); + for (i = 0;i < safeext.len;++i) + if (safeext.s[i] == '.') + safeext.s[i] = ':'; + + i = str_len(host); + i = byte_rchr(host,i,'.'); + if (!stralloc_copyb(&foo,host,i)) temp_nomem(); + if (!stralloc_0(&foo)) temp_nomem(); + if (!env_put2("HOST2",foo.s)) temp_nomem(); + i = byte_rchr(host,i,'.'); + if (!stralloc_copyb(&foo,host,i)) temp_nomem(); + if (!stralloc_0(&foo)) temp_nomem(); + if (!env_put2("HOST3",foo.s)) temp_nomem(); + i = byte_rchr(host,i,'.'); + if (!stralloc_copyb(&foo,host,i)) temp_nomem(); + if (!stralloc_0(&foo)) temp_nomem(); + if (!env_put2("HOST4",foo.s)) temp_nomem(); + + flagforwardonly = 0; + qmesearch(&fd,&flagforwardonly); + if (fd == -1) + if (*dash) + strerr_die1x(100,"Sorry, no mailbox here by that name. (#5.1.1)"); + + if (!stralloc_copys(&ueo,sender)) temp_nomem(); + if (str_diff(sender,"")) + if (str_diff(sender,"#@[]")) + if (qmeox("-owner") == 0) + { + if (qmeox("-owner-default") == 0) + { + if (!stralloc_copys(&ueo,local)) temp_nomem(); + if (!stralloc_cats(&ueo,"-owner-@")) temp_nomem(); + if (!stralloc_cats(&ueo,host)) temp_nomem(); + if (!stralloc_cats(&ueo,"-@[]")) temp_nomem(); + } + else + { + if (!stralloc_copys(&ueo,local)) temp_nomem(); + if (!stralloc_cats(&ueo,"-owner@")) temp_nomem(); + if (!stralloc_cats(&ueo,host)) temp_nomem(); + } + } + if (!stralloc_0(&ueo)) temp_nomem(); + if (!env_put2("NEWSENDER",ueo.s)) temp_nomem(); + + if (!stralloc_ready(&cmds,0)) temp_nomem(); + cmds.len = 0; + if (fd != -1) + if (slurpclose(fd,&cmds,256) == -1) temp_nomem(); + + if (!cmds.len) + { + if (!stralloc_copys(&cmds,aliasempty)) temp_nomem(); + flagforwardonly = 0; + } + if (!cmds.len || (cmds.s[cmds.len - 1] != '\n')) + if (!stralloc_cats(&cmds,"\n")) temp_nomem(); + + numforward = 0; + i = 0; + for (j = 0;j < cmds.len;++j) + if (cmds.s[j] == '\n') + { + switch(cmds.s[i]) { case '#': case '.': case '/': case '|': break; + default: ++numforward; } + i = j + 1; + } + + recips = (char **) alloc((numforward + 1) * sizeof(char *)); + if (!recips) temp_nomem(); + numforward = 0; + + flag99 = 0; + + i = 0; + for (j = 0;j < cmds.len;++j) + if (cmds.s[j] == '\n') + { + cmds.s[j] = 0; + k = j; + while ((k > i) && ((cmds.s[k - 1] == ' ') || (cmds.s[k - 1] == '\t'))) + cmds.s[--k] = 0; + switch(cmds.s[i]) + { + case 0: /* k == i */ + if (i) break; + strerr_die1x(111,"Uh-oh: first line of .qmail file is blank. (#4.2.1)"); + case '#': + break; + case '.': + case '/': + ++count_file; + if (flagforwardonly) strerr_die1x(111,"Uh-oh: .qmail has file delivery but has x bit set. (#4.7.0)"); + if (cmds.s[k - 1] == '/') + if (flagdoit) maildir(cmds.s + i); + else sayit("maildir ",cmds.s + i,k - i); + else + if (flagdoit) mailfile(cmds.s + i); + else sayit("mbox ",cmds.s + i,k - i); + break; + case '|': + ++count_program; + if (flagforwardonly) strerr_die1x(111,"Uh-oh: .qmail has prog delivery but has x bit set. (#4.7.0)"); + if (flagdoit) mailprogram(cmds.s + i + 1); + else sayit("program ",cmds.s + i + 1,k - i - 1); + break; + case '+': + if (str_equal(cmds.s + i + 1,"list")) + flagforwardonly = 1; + break; + case '&': + ++i; + default: + ++count_forward; + if (flagdoit) recips[numforward++] = cmds.s + i; + else sayit("forward ",cmds.s + i,k - i); + break; + } + i = j + 1; + if (flag99) break; + } + + if (numforward) if (flagdoit) + { + recips[numforward] = 0; + mailforward(recips); + } + + count_print(); + _exit(0); +} diff --git a/qmail-log.5 b/qmail-log.5 new file mode 100644 index 0000000..52cd0e6 --- /dev/null +++ b/qmail-log.5 @@ -0,0 +1,266 @@ +.TH qmail-log 5 +.SH NAME +qmail-log \- the qmail activity record +.SH DESCRIPTION +.B qmail-send +prints a series of lines describing its activities. +Each possible line is described below. +.SH "STATUS" +.TP +.B status: local \fIl\fB/\fIL\fB remote \fIr\fB/\fIR\fB ... +.B qmail-send +is waiting for +.I l +local deliveries +and +.I r +remote deliveries. +The concurrency limits are +.I L +and +.IR R . +.TP +.B status: exiting +.B qmail-send +is done. +.SH "FATAL PROBLEMS" +.TP +.B alert: cannot start: ... +.B qmail-send +is unable to prepare itself for delivering messages; +it is giving up. +This normally indicates a serious configuration error, +but it can be caused by a temporary lack of resources. +.TP +.B alert: oh no! lost ... +One of the other daemons has died. +.B qmail-send +will exit as soon as possible. +.SH "SERIOUS PROBLEMS" +.TP +.B alert: unable to append to bounce message... +.B qmail-send +is unable to record a permanent failure, +usually because the disk is full. +This is a very serious problem; +.B qmail-send +cannot proceed without recording the results. +It will try again in ten seconds. +.TP +.B alert: out of memory... +.B qmail-send +tried to allocate more memory and failed. +It will try again in ten seconds. +.TP +.B alert: unable to opendir... +.B qmail-send +is having trouble reading a file list from disk, +usually because the system's file descriptor table is full, +but possibly because permissions are set incorrectly. +It will try again in ten seconds. +.TP +.B alert: unable to switch back... +.B qmail-send +was sent SIGHUP, +and it is unable to reenter the queue directory. +This is a very serious problem; +.B qmail-send +cannot proceed outside the queue directory. +It will try again in ten seconds. +.TP +.B alert: unable to reread... +.B qmail-send +was sent SIGHUP, +but it is unable to read the new controls. +It will continue operating with the original controls. +.SH "MESSAGES" +.TP +.B new msg \fIm\fB +.B qmail-send +is going to preprocess a queued message. +The message number, +.IR m , +is its disk inode number. +After a message is removed from the queue, +its number can be reused immediately. +.TP +.B info msg \fIm\fB: bytes \fIb\fB from <\fIs\fB> qp \fIq\fB uid \fIu\fB +Message +.I m +contains +.I b +bytes; +its envelope sender is +.IR s ; +it was queued by a user with user ID +.IR u . +.I q +is a long-term queue identifier, +the process ID of the +.B qmail-queue +that queued the message. +.TP +.B bounce msg \fIm\fB qp \fIq\fB +Message +.I m +had some delivery failures. +The long-term queue identifier of the bounce (or double-bounce) message +is +.IR q . +.TP +.B triple bounce: discarding ... +Message +.I m +had some delivery failures, +but it is already a double-bounce message, +so it must be thrown away. +Triple-bounce messages do not exist. +.TP +.B end msg \fIm\fB +.B qmail-send +is about to remove +message +.I m +from the queue. +.SH "DELIVERIES" +.TP +.B starting delivery \fId\fB: msg \fIm\fB to ... +.B qmail-send +is telling +.B qmail-lspawn +or +.B qmail-rspawn +to deliver message +.I m +to one recipient. +The delivery number, +.IR d , +starts at 1 and increases by 1 for each new delivery. +.TP +.B delivery \fId\fB: success: ... +Delivery +.I d +was successful. +.TP +.B delivery \fId\fB: failure: ... +Delivery +.I d +failed permanently. +The message will bounce. +.TP +.B delivery \fId\fB: deferral: ... +Delivery +.I d +failed temporarily. +This recipient will be retried later. +.TP +.B delivery \fId\fB: report mangled, will defer +There is a serious bug in +.B qmail-lspawn +or +.BR qmail-rspawn . +This recipient will be retried later. +.SH "WARNINGS" +.TP +.B internal error: delivery report out of range +.B qmail-lspawn +or +.B qmail-rspawn +has supplied a report on a nonexistent delivery. +This is a serious bug. +.TP +.B qmail-clean unable to clean up ... +For some reason +.B qmail-clean +is unable to remove the indicated file. +It will try again later. +.TP +.B trouble fsyncing ... +.B qmail-send +was unable to write to disk the results of preprocessing a queued message. +It will try again later. +.TP +.B trouble in select +There is an operating system bug. +.TP +.B trouble injecting bounce message... +.B qmail-send +was unable to queue a bounce message, +usually because the disk is full. +It will try again later. +.TP +.B trouble marking ... +.B qmail-send +was unable to record the result of a successful or permanently +unsuccessful delivery. +This means that the delivery will be tried again later. +.TP +.B trouble opening ... +.B qmail-send +was unable to open the list of local or remote recipients +for a message. +It will try again later. +.TP +.B trouble reading ... +Either +.B qmail-send +is unable to read a recipient list, +or it is unable to read the envelope of a queued +message, or it is out of memory. +Whatever it was doing, it will try again later. +.TP +.B trouble writing to ... +.B qmail-send +was unable to preprocess a queued message, +usually because the disk is full. +It will try again later. +.TP +.B unable to create ... +.B qmail-send +was unable to preprocess a queued message, +usually because the disk is out of inodes. +It will try again later. +.TP +.B unable to open ... +.B qmail-send +is unable to read the envelope of a queued message +for preprocessing. +It will try again later. +.TP +.B unable to start qmail-queue... +.B qmail-send +is unable to queue a bounce message, +usually because the machine is almost out of memory. +It will try again later. +This can also be caused by incorrect settings of +.B $QMAILQUEUE +or errors in a program or script which +.B $QMAILQUEUE +points to. +.TP +.B unable to stat ... +.B qmail-send +is unable to obtain information about a file that should exist. +It will try again later. +.TP +.B unable to unlink ... +.B qmail-send +is unable to remove a file. +It will try again later. +.TP +.B unable to utime ... +.B qmail-send +is about to exit, +and it is unable to record on disk +the next scheduled delivery time for a message. +The message will be retried as soon as +.B qmail-send +is restarted. +.TP +.B unknown record type in ... +There is a serious bug in either +.B qmail-queue +or +.BR qmail-send . +.SH "SEE ALSO" +qmail-send(8) diff --git a/qmail-lspawn.8 b/qmail-lspawn.8 new file mode 100644 index 0000000..da01741 --- /dev/null +++ b/qmail-lspawn.8 @@ -0,0 +1,46 @@ +.TH qmail-lspawn 8 +.SH NAME +qmail-lspawn \- schedule local deliveries +.SH SYNOPSIS +.B qmail-lspawn +.I defaultdelivery +.SH DESCRIPTION +.B qmail-lspawn +reads a series of local delivery commands from descriptor 0, +invokes +.B qmail-local +to perform the deliveries, +and prints the results to descriptor 1. +It passes +.I defaultdelivery +to +.B qmail-local +as the default delivery instruction. + +.B qmail-lspawn +invokes +.B qmail-local +asynchronously, +so the results may not be in the same order as the commands. + +For each recipient address, +.B qmail-lspawn +finds out which local user controls that address. +It first checks the +.B qmail-users +mechanism; if the address is not listed there, it invokes +.BR qmail-getpw . +.B qmail-lspawn +then runs +.B qmail-local +under the user's uid and gid. +It does not set up any supplementary groups. + +.B qmail-lspawn +treats an empty mailbox name as a trash address. +.SH "SEE ALSO" +envelopes(5), +qmail-users(5), +qmail-getpw(8), +qmail-send(8), +qmail-local(8) diff --git a/qmail-lspawn.c b/qmail-lspawn.c new file mode 100644 index 0000000..5109cc3 --- /dev/null +++ b/qmail-lspawn.c @@ -0,0 +1,234 @@ +#include "fd.h" +#include "wait.h" +#include "prot.h" +#include "substdio.h" +#include "stralloc.h" +#include "scan.h" +#include "exit.h" +#include "fork.h" +#include "error.h" +#include "cdb.h" +#include "case.h" +#include "slurpclose.h" +#include "auto_qmail.h" +#include "auto_uids.h" +#include "qlx.h" + +char *aliasempty; + +void initialize(argc,argv) +int argc; +char **argv; +{ + aliasempty = argv[1]; + if (!aliasempty) _exit(100); +} + +int truncreport = 3000; + +void report(ss,wstat,s,len) +substdio *ss; +int wstat; +char *s; +int len; +{ + int i; + if (wait_crashed(wstat)) + { substdio_puts(ss,"Zqmail-local crashed.\n"); return; } + switch(wait_exitcode(wstat)) + { + case QLX_CDB: + substdio_puts(ss,"ZTrouble reading users/cdb in qmail-lspawn.\n"); return; + case QLX_NOMEM: + substdio_puts(ss,"ZOut of memory in qmail-lspawn.\n"); return; + case QLX_SYS: + substdio_puts(ss,"ZTemporary failure in qmail-lspawn.\n"); return; + case QLX_NOALIAS: + substdio_puts(ss,"ZUnable to find alias user!\n"); return; + case QLX_ROOT: + substdio_puts(ss,"ZNot allowed to perform deliveries as root.\n"); return; + case QLX_USAGE: + substdio_puts(ss,"ZInternal qmail-lspawn bug.\n"); return; + case QLX_NFS: + substdio_puts(ss,"ZNFS failure in qmail-local.\n"); return; + case QLX_EXECHARD: + substdio_puts(ss,"DUnable to run qmail-local.\n"); return; + case QLX_EXECSOFT: + substdio_puts(ss,"ZUnable to run qmail-local.\n"); return; + case QLX_EXECPW: + substdio_puts(ss,"ZUnable to run qmail-getpw.\n"); return; + case 111: case 71: case 74: case 75: + substdio_put(ss,"Z",1); break; + case 0: + substdio_put(ss,"K",1); break; + case 100: + default: + substdio_put(ss,"D",1); break; + } + + for (i = 0;i < len;++i) if (!s[i]) break; + substdio_put(ss,s,i); +} + +stralloc lower = {0}; +stralloc nughde = {0}; +stralloc wildchars = {0}; + +void nughde_get(local) +char *local; +{ + char *(args[3]); + int pi[2]; + int gpwpid; + int gpwstat; + int r; + int fd; + int flagwild; + + if (!stralloc_copys(&lower,"!")) _exit(QLX_NOMEM); + if (!stralloc_cats(&lower,local)) _exit(QLX_NOMEM); + if (!stralloc_0(&lower)) _exit(QLX_NOMEM); + case_lowerb(lower.s,lower.len); + + if (!stralloc_copys(&nughde,"")) _exit(QLX_NOMEM); + + fd = open_read("users/cdb"); + if (fd == -1) + if (errno != error_noent) + _exit(QLX_CDB); + + if (fd != -1) + { + uint32 dlen; + unsigned int i; + + r = cdb_seek(fd,"",0,&dlen); + if (r != 1) _exit(QLX_CDB); + if (!stralloc_ready(&wildchars,(unsigned int) dlen)) _exit(QLX_NOMEM); + wildchars.len = dlen; + if (cdb_bread(fd,wildchars.s,wildchars.len) == -1) _exit(QLX_CDB); + + i = lower.len; + flagwild = 0; + + do + { + /* i > 0 */ + if (!flagwild || (i == 1) || (byte_chr(wildchars.s,wildchars.len,lower.s[i - 1]) < wildchars.len)) + { + r = cdb_seek(fd,lower.s,i,&dlen); + if (r == -1) _exit(QLX_CDB); + if (r == 1) + { + if (!stralloc_ready(&nughde,(unsigned int) dlen)) _exit(QLX_NOMEM); + nughde.len = dlen; + if (cdb_bread(fd,nughde.s,nughde.len) == -1) _exit(QLX_CDB); + if (flagwild) + if (!stralloc_cats(&nughde,local + i - 1)) _exit(QLX_NOMEM); + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + close(fd); + return; + } + } + --i; + flagwild = 1; + } + while (i); + + close(fd); + } + + if (pipe(pi) == -1) _exit(QLX_SYS); + args[0] = "bin/qmail-getpw"; + args[1] = local; + args[2] = 0; + switch(gpwpid = vfork()) + { + case -1: + _exit(QLX_SYS); + case 0: + if (prot_gid(auto_gidn) == -1) _exit(QLX_USAGE); + if (prot_uid(auto_uidp) == -1) _exit(QLX_USAGE); + close(pi[0]); + if (fd_move(1,pi[1]) == -1) _exit(QLX_SYS); + execv(*args,args); + _exit(QLX_EXECPW); + } + close(pi[1]); + + if (slurpclose(pi[0],&nughde,128) == -1) _exit(QLX_SYS); + + if (wait_pid(&gpwstat,gpwpid) != -1) + { + if (wait_crashed(gpwstat)) _exit(QLX_SYS); + if (wait_exitcode(gpwstat) != 0) _exit(wait_exitcode(gpwstat)); + } +} + +int spawn(fdmess,fdout,s,r,at) +int fdmess; int fdout; +char *s; char *r; int at; +{ + int f; + + if (!(f = fork())) + { + char *(args[11]); + unsigned long u; + int n; + int uid; + int gid; + char *x; + unsigned int xlen; + + r[at] = 0; + if (!r[0]) _exit(0); /* <> */ + + if (chdir(auto_qmail) == -1) _exit(QLX_USAGE); + + nughde_get(r); + + x = nughde.s; + xlen = nughde.len; + + args[0] = "bin/qmail-local"; + args[1] = "--"; + args[2] = x; + n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n; + + scan_ulong(x,&u); + uid = u; + n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n; + + scan_ulong(x,&u); + gid = u; + n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n; + + args[3] = x; + n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n; + + args[4] = r; + args[5] = x; + n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n; + + args[6] = x; + n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n; + + args[7] = r + at + 1; + args[8] = s; + args[9] = aliasempty; + args[10] = 0; + + if (fd_move(0,fdmess) == -1) _exit(QLX_SYS); + if (fd_move(1,fdout) == -1) _exit(QLX_SYS); + if (fd_copy(2,1) == -1) _exit(QLX_SYS); + if (prot_gid(gid) == -1) _exit(QLX_USAGE); + if (prot_uid(uid) == -1) _exit(QLX_USAGE); + if (!getuid()) _exit(QLX_ROOT); + + execv(*args,args); + if (error_temp(errno)) _exit(QLX_EXECSOFT); + _exit(QLX_EXECHARD); + } + return f; +} diff --git a/qmail-newmrh.9 b/qmail-newmrh.9 new file mode 100644 index 0000000..2f02f10 --- /dev/null +++ b/qmail-newmrh.9 @@ -0,0 +1,41 @@ +.TH qmail-newmrh 8 +.SH NAME +qmail-newmrh \- prepare morercpthosts for qmail-smtpd +.SH SYNOPSIS +.B qmail-newmrh +.SH DESCRIPTION +.B qmail-newmrh +reads the instructions in +.B QMAILHOME/control/morercpthosts +and writes them into +.B QMAILHOME/control/morercpthosts.cdb +in a binary format suited +for quick access by +.BR qmail-smtpd . + +If there is a problem with +.BR control/morercpthosts , +.B qmail-newmrh +complains and leaves +.B control/morercpthosts.cdb +alone. + +.B qmail-newmrh +ensures that +.B control/morercpthosts.cdb +is updated atomically, +so +.B qmail-smtpd +never has to wait for +.B qmail-newmrh +to finish. +However, +.B qmail-newmrh +makes no attempt to protect against two simultaneous updates of +.BR control/morercpthosts.cdb . + +The binary +.B control/morercpthosts.cdb +format is portable across machines. +.SH "SEE ALSO" +qmail-smtpd(8) diff --git a/qmail-newmrh.c b/qmail-newmrh.c new file mode 100644 index 0000000..25a4a10 --- /dev/null +++ b/qmail-newmrh.c @@ -0,0 +1,70 @@ +#include "strerr.h" +#include "stralloc.h" +#include "substdio.h" +#include "getln.h" +#include "exit.h" +#include "readwrite.h" +#include "open.h" +#include "auto_qmail.h" +#include "cdbmss.h" + +#define FATAL "qmail-newmrh: fatal: " + +void die_read() +{ + strerr_die2sys(111,FATAL,"unable to read control/morercpthosts: "); +} +void die_write() +{ + strerr_die2sys(111,FATAL,"unable to write to control/morercpthosts.tmp: "); +} + +char inbuf[1024]; +substdio ssin; + +int fd; +int fdtemp; + +struct cdbmss cdbmss; +stralloc line = {0}; +int match; + +void main() +{ + umask(033); + if (chdir(auto_qmail) == -1) + strerr_die4sys(111,FATAL,"unable to chdir to ",auto_qmail,": "); + + fd = open_read("control/morercpthosts"); + if (fd == -1) die_read(); + + substdio_fdbuf(&ssin,read,fd,inbuf,sizeof inbuf); + + fdtemp = open_trunc("control/morercpthosts.tmp"); + if (fdtemp == -1) die_write(); + + if (cdbmss_start(&cdbmss,fdtemp) == -1) die_write(); + + for (;;) { + if (getln(&ssin,&line,&match,'\n') != 0) die_read(); + case_lowerb(line.s,line.len); + while (line.len) { + if (line.s[line.len - 1] == ' ') { --line.len; continue; } + if (line.s[line.len - 1] == '\n') { --line.len; continue; } + if (line.s[line.len - 1] == '\t') { --line.len; continue; } + if (line.s[0] != '#') + if (cdbmss_add(&cdbmss,line.s,line.len,"",0) == -1) + die_write(); + break; + } + if (!match) break; + } + + if (cdbmss_finish(&cdbmss) == -1) die_write(); + if (fsync(fdtemp) == -1) die_write(); + if (close(fdtemp) == -1) die_write(); /* NFS stupidity */ + if (rename("control/morercpthosts.tmp","control/morercpthosts.cdb") == -1) + strerr_die2sys(111,FATAL,"unable to move control/morercpthosts.tmp to control/morercpthosts.cdb"); + + _exit(0); +} diff --git a/qmail-newu.9 b/qmail-newu.9 new file mode 100644 index 0000000..12f1b3f --- /dev/null +++ b/qmail-newu.9 @@ -0,0 +1,43 @@ +.TH qmail-newu 8 +.SH NAME +qmail-newu \- prepare address assignments for qmail-lspawn +.SH SYNOPSIS +.B qmail-newu +.SH DESCRIPTION +.B qmail-newu +reads the assignments in +.B QMAILHOME/users/assign +and writes them into +.B QMAILHOME/users/cdb +in a binary format suited +for quick access by +.BR qmail-lspawn . + +If there is a problem with +.BR users/assign , +.B qmail-newu +complains and leaves +.B users/cdb +alone. + +.B qmail-newu +ensures that +.B users/cdb +is updated atomically, +so +.B qmail-lspawn +never has to wait for +.B qmail-newu +to finish. +However, +.B qmail-newu +makes no attempt to protect against two simultaneous updates of +.BR users/cdb . + +The binary +.B users/cdb +format is portable across machines. +.SH "SEE ALSO" +qmail-users(5), +qmail-lspawn(8), +qmail-pw2u(8) diff --git a/qmail-newu.c b/qmail-newu.c new file mode 100644 index 0000000..1f9ecc3 --- /dev/null +++ b/qmail-newu.c @@ -0,0 +1,137 @@ +#include "stralloc.h" +#include "subfd.h" +#include "getln.h" +#include "substdio.h" +#include "cdbmss.h" +#include "exit.h" +#include "readwrite.h" +#include "open.h" +#include "error.h" +#include "case.h" +#include "auto_qmail.h" + +void die_temp() { _exit(111); } + +void die_chdir() +{ + substdio_putsflush(subfderr,"qmail-newu: fatal: unable to chdir\n"); + die_temp(); +} +void die_nomem() +{ + substdio_putsflush(subfderr,"qmail-newu: fatal: out of memory\n"); + die_temp(); +} +void die_opena() +{ + substdio_putsflush(subfderr,"qmail-newu: fatal: unable to open users/assign\n"); + die_temp(); +} +void die_reada() +{ + substdio_putsflush(subfderr,"qmail-newu: fatal: unable to read users/assign\n"); + die_temp(); +} +void die_format() +{ + substdio_putsflush(subfderr,"qmail-newu: fatal: bad format in users/assign\n"); + die_temp(); +} +void die_opent() +{ + substdio_putsflush(subfderr,"qmail-newu: fatal: unable to open users/cdb.tmp\n"); + die_temp(); +} +void die_writet() +{ + substdio_putsflush(subfderr,"qmail-newu: fatal: unable to write users/cdb.tmp\n"); + die_temp(); +} +void die_rename() +{ + substdio_putsflush(subfderr,"qmail-newu: fatal: unable to move users/cdb.tmp to users/cdb\n"); + die_temp(); +} + +struct cdbmss cdbmss; +stralloc key = {0}; +stralloc data = {0}; + +char inbuf[1024]; +substdio ssin; + +int fd; +int fdtemp; + +stralloc line = {0}; +int match; + +stralloc wildchars = {0}; + +void main() +{ + int i; + int numcolons; + + umask(033); + if (chdir(auto_qmail) == -1) die_chdir(); + + fd = open_read("users/assign"); + if (fd == -1) die_opena(); + + substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf)); + + fdtemp = open_trunc("users/cdb.tmp"); + if (fdtemp == -1) die_opent(); + + if (cdbmss_start(&cdbmss,fdtemp) == -1) die_writet(); + + if (!stralloc_copys(&wildchars,"")) die_nomem(); + + for (;;) { + if (getln(&ssin,&line,&match,'\n') != 0) die_reada(); + if (line.len && (line.s[0] == '.')) break; + if (!match) die_format(); + + if (byte_chr(line.s,line.len,'\0') < line.len) die_format(); + i = byte_chr(line.s,line.len,':'); + if (i == line.len) die_format(); + if (i == 0) die_format(); + if (!stralloc_copys(&key,"!")) die_nomem(); + if (line.s[0] == '+') { + if (!stralloc_catb(&key,line.s + 1,i - 1)) die_nomem(); + case_lowerb(key.s,key.len); + if (i >= 2) + if (byte_chr(wildchars.s,wildchars.len,line.s[i - 1]) == wildchars.len) + if (!stralloc_append(&wildchars,line.s + i - 1)) die_nomem(); + } + else { + if (!stralloc_catb(&key,line.s + 1,i - 1)) die_nomem(); + if (!stralloc_0(&key)) die_nomem(); + case_lowerb(key.s,key.len); + } + + if (!stralloc_copyb(&data,line.s + i + 1,line.len - i - 1)) die_nomem(); + + numcolons = 0; + for (i = 0;i < data.len;++i) + if (data.s[i] == ':') { + data.s[i] = 0; + if (++numcolons == 6) + break; + } + if (numcolons < 6) die_format(); + data.len = i; + + if (cdbmss_add(&cdbmss,key.s,key.len,data.s,data.len) == -1) die_writet(); + } + + if (cdbmss_add(&cdbmss,"",0,wildchars.s,wildchars.len) == -1) die_writet(); + + if (cdbmss_finish(&cdbmss) == -1) die_writet(); + if (fsync(fdtemp) == -1) die_writet(); + if (close(fdtemp) == -1) die_writet(); /* NFS stupidity */ + if (rename("users/cdb.tmp","users/cdb") == -1) die_rename(); + + _exit(0); +} diff --git a/qmail-pop3d.8 b/qmail-pop3d.8 new file mode 100644 index 0000000..de6b2ba --- /dev/null +++ b/qmail-pop3d.8 @@ -0,0 +1,43 @@ +.TH qmail-pop3d 8 +.SH NAME +qmail-pop3d \- distribute mail via POP +.SH SYNOPSIS +.B qmail-pop3d +.I maildirname +.SH DESCRIPTION +.B qmail-pop3d +lets a user read and delete his mail through the network. + +Mail is stored in a +.B maildir +called +.IR maildirname , +normally +.BR Maildir , +in the user's home directory. + +.B qmail-pop3d +is normally invoked +under +.BR qmail-popup , +which reads a username and password, +and +.BR /bin/checkpassword , +which checks the password and sets up environment variables. + +.B qmail-pop3d +has a 20-minute idle timeout. + +.B qmail-pop3d +supports UIDL, TOP, and LAST. + +.B qmail-pop3d +appends an extra blank line to every message +to work around serious bugs in certain clients. + +.B qmail-pop3d +is based on a program contributed by Russ Nelson. +.SH "SEE ALSO" +maildir(5), +qmail-local(8), +qmail-popup(8) diff --git a/qmail-pop3d.c b/qmail-pop3d.c new file mode 100644 index 0000000..0ca4f9c --- /dev/null +++ b/qmail-pop3d.c @@ -0,0 +1,305 @@ +#include +#include +#include "commands.h" +#include "sig.h" +#include "getln.h" +#include "stralloc.h" +#include "substdio.h" +#include "alloc.h" +#include "open.h" +#include "prioq.h" +#include "scan.h" +#include "fmt.h" +#include "str.h" +#include "exit.h" +#include "maildir.h" +#include "readwrite.h" +#include "timeoutread.h" +#include "timeoutwrite.h" + +void die() { _exit(0); } + +int saferead(fd,buf,len) int fd; char *buf; int len; +{ + int r; + r = timeoutread(1200,fd,buf,len); + if (r <= 0) die(); + return r; +} + +int safewrite(fd,buf,len) int fd; char *buf; int len; +{ + int r; + r = timeoutwrite(1200,fd,buf,len); + if (r <= 0) die(); + return r; +} + +char ssoutbuf[1024]; +substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf); + +char ssinbuf[128]; +substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf); + +void put(buf,len) char *buf; int len; +{ + substdio_put(&ssout,buf,len); +} +void puts(s) char *s; +{ + substdio_puts(&ssout,s); +} +void flush() +{ + substdio_flush(&ssout); +} +void err(s) char *s; +{ + puts("-ERR "); + puts(s); + puts("\r\n"); + flush(); +} + +void die_nomem() { err("out of memory"); die(); } +void die_nomaildir() { err("this user has no $HOME/Maildir"); die(); } +void die_scan() { err("unable to scan $HOME/Maildir"); die(); } + +void err_syntax() { err("syntax error"); } +void err_unimpl(arg) char *arg; { err("unimplemented"); } +void err_deleted() { err("already deleted"); } +void err_nozero() { err("messages are counted from 1"); } +void err_toobig() { err("not that many messages"); } +void err_nosuch() { err("unable to open that message"); } +void err_nounlink() { err("unable to unlink all deleted messages"); } + +void okay(arg) char *arg; { puts("+OK \r\n"); flush(); } + +void printfn(fn) char *fn; +{ + fn += 4; + put(fn,str_chr(fn,':')); +} + +char strnum[FMT_ULONG]; +stralloc line = {0}; + +void blast(ssfrom,limit) +substdio *ssfrom; +unsigned long limit; +{ + int match; + int inheaders = 1; + + for (;;) { + if (getln(ssfrom,&line,&match,'\n') != 0) die(); + if (!match && !line.len) break; + if (match) --line.len; /* no way to pass this info over POP */ + if (limit) if (!inheaders) if (!--limit) break; + if (!line.len) + inheaders = 0; + else + if (line.s[0] == '.') + put(".",1); + put(line.s,line.len); + put("\r\n",2); + if (!match) break; + } + put("\r\n.\r\n",5); + flush(); +} + +stralloc filenames = {0}; +prioq pq = {0}; + +struct message { + int flagdeleted; + unsigned long size; + char *fn; +} *m; +int numm; + +int last = 0; + +void getlist() +{ + struct prioq_elt pe; + struct stat st; + int i; + + maildir_clean(&line); + if (maildir_scan(&pq,&filenames,1,1) == -1) die_scan(); + + numm = pq.p ? pq.len : 0; + m = (struct message *) alloc(numm * sizeof(struct message)); + if (!m) die_nomem(); + + for (i = 0;i < numm;++i) { + if (!prioq_min(&pq,&pe)) { numm = i; break; } + prioq_delmin(&pq); + m[i].fn = filenames.s + pe.id; + m[i].flagdeleted = 0; + if (stat(m[i].fn,&st) == -1) + m[i].size = 0; + else + m[i].size = st.st_size; + } +} + +void pop3_stat(arg) char *arg; +{ + int i; + unsigned long total; + + total = 0; + for (i = 0;i < numm;++i) if (!m[i].flagdeleted) total += m[i].size; + puts("+OK "); + put(strnum,fmt_uint(strnum,numm)); + puts(" "); + put(strnum,fmt_ulong(strnum,total)); + puts("\r\n"); + flush(); +} + +void pop3_rset(arg) char *arg; +{ + int i; + for (i = 0;i < numm;++i) m[i].flagdeleted = 0; + last = 0; + okay(0); +} + +void pop3_last(arg) char *arg; +{ + puts("+OK "); + put(strnum,fmt_uint(strnum,last)); + puts("\r\n"); + flush(); +} + +void pop3_quit(arg) char *arg; +{ + int i; + for (i = 0;i < numm;++i) + if (m[i].flagdeleted) { + if (unlink(m[i].fn) == -1) err_nounlink(); + } + else + if (str_start(m[i].fn,"new/")) { + if (!stralloc_copys(&line,"cur/")) die_nomem(); + if (!stralloc_cats(&line,m[i].fn + 4)) die_nomem(); + if (!stralloc_cats(&line,":2,")) die_nomem(); + if (!stralloc_0(&line)) die_nomem(); + rename(m[i].fn,line.s); /* if it fails, bummer */ + } + okay(0); + die(); +} + +int msgno(arg) char *arg; +{ + unsigned long u; + if (!scan_ulong(arg,&u)) { err_syntax(); return -1; } + if (!u) { err_nozero(); return -1; } + --u; + if (u >= numm) { err_toobig(); return -1; } + if (m[u].flagdeleted) { err_deleted(); return -1; } + return u; +} + +void pop3_dele(arg) char *arg; +{ + int i; + i = msgno(arg); + if (i == -1) return; + m[i].flagdeleted = 1; + if (i + 1 > last) last = i + 1; + okay(0); +} + +void list(i,flaguidl) +int i; +int flaguidl; +{ + put(strnum,fmt_uint(strnum,i + 1)); + puts(" "); + if (flaguidl) printfn(m[i].fn); + else put(strnum,fmt_ulong(strnum,m[i].size)); + puts("\r\n"); +} + +void dolisting(arg,flaguidl) char *arg; int flaguidl; +{ + unsigned int i; + if (*arg) { + i = msgno(arg); + if (i == -1) return; + puts("+OK "); + list(i,flaguidl); + } + else { + okay(0); + for (i = 0;i < numm;++i) + if (!m[i].flagdeleted) + list(i,flaguidl); + puts(".\r\n"); + } + flush(); +} + +void pop3_uidl(arg) char *arg; { dolisting(arg,1); } +void pop3_list(arg) char *arg; { dolisting(arg,0); } + +substdio ssmsg; char ssmsgbuf[1024]; + +void pop3_top(arg) char *arg; +{ + int i; + unsigned long limit; + int fd; + + i = msgno(arg); + if (i == -1) return; + + arg += scan_ulong(arg,&limit); + while (*arg == ' ') ++arg; + if (scan_ulong(arg,&limit)) ++limit; else limit = 0; + + fd = open_read(m[i].fn); + if (fd == -1) { err_nosuch(); return; } + okay(0); + substdio_fdbuf(&ssmsg,read,fd,ssmsgbuf,sizeof(ssmsgbuf)); + blast(&ssmsg,limit); + close(fd); +} + +struct commands pop3commands[] = { + { "quit", pop3_quit, 0 } +, { "stat", pop3_stat, 0 } +, { "list", pop3_list, 0 } +, { "uidl", pop3_uidl, 0 } +, { "dele", pop3_dele, 0 } +, { "retr", pop3_top, 0 } +, { "rset", pop3_rset, 0 } +, { "last", pop3_last, 0 } +, { "top", pop3_top, 0 } +, { "noop", okay, 0 } +, { 0, err_unimpl, 0 } +} ; + +void main(argc,argv) +int argc; +char **argv; +{ + sig_alarmcatch(die); + sig_pipeignore(); + + if (!argv[1]) die_nomaildir(); + if (chdir(argv[1]) == -1) die_nomaildir(); + + getlist(); + + okay(0); + commands(&ssin,pop3commands); + die(); +} diff --git a/qmail-popup.8 b/qmail-popup.8 new file mode 100644 index 0000000..95f01bc --- /dev/null +++ b/qmail-popup.8 @@ -0,0 +1,65 @@ +.TH qmail-popup 8 +.SH NAME +qmail-popup \- read a POP username and password +.SH SYNOPSIS +.B qmail-popup +.I hostname +.I subprogram +.SH DESCRIPTION +.B qmail-popup +reads a POP username and password from the network. +It then runs +.IR subprogram . + +.B qmail-popup +is most commonly invoked from +.B inetd +as + +.EX + qmail-popup CHANGEME checkpassword qmail-pop3d Maildir +.EE + +with +CHANGEME +replaced by the fully qualified domain name of the local host. + +.B qmail-popup +expects descriptor 0 to read from the network +and descriptor 1 to write to the network. +It reads a username and password from descriptor 0 +in POP's USER-PASS style or APOP style. +It invokes +.IR subprogram , +with the same descriptors 0 and 1; +descriptor 2 writing to the network; +and descriptor 3 reading the username, a 0 byte, the password, +another 0 byte, +an APOP timestamp derived from +.IR hostname , +and a final 0 byte. +.B qmail-popup +then waits for +.I subprogram +to finish. +It prints an error message if +.I subprogram +crashes or exits nonzero. + +.B qmail-popup +should be used only within +a secure network. +Otherwise an eavesdropper can steal passwords. +Even if you use APOP, +an active attacker can still take over the connection +and wreak havoc. + +.B qmail-popup +has a 20-minute idle timeout. + +.B qmail-popup +is based on a program contributed by Russ Nelson. +.SH "SEE ALSO" +maildir(5), +qmail-local(8), +qmail-pop3d(8) diff --git a/qmail-popup.c b/qmail-popup.c new file mode 100644 index 0000000..906da67 --- /dev/null +++ b/qmail-popup.c @@ -0,0 +1,183 @@ +#include "commands.h" +#include "fd.h" +#include "sig.h" +#include "stralloc.h" +#include "substdio.h" +#include "alloc.h" +#include "wait.h" +#include "str.h" +#include "byte.h" +#include "now.h" +#include "fmt.h" +#include "exit.h" +#include "readwrite.h" +#include "timeoutread.h" +#include "timeoutwrite.h" + +void die() { _exit(1); } + +int saferead(fd,buf,len) int fd; char *buf; int len; +{ + int r; + r = timeoutread(1200,fd,buf,len); + if (r <= 0) die(); + return r; +} + +int safewrite(fd,buf,len) int fd; char *buf; int len; +{ + int r; + r = timeoutwrite(1200,fd,buf,len); + if (r <= 0) die(); + return r; +} + +char ssoutbuf[128]; +substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf); + +char ssinbuf[128]; +substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf); + +void puts(s) char *s; +{ + substdio_puts(&ssout,s); +} +void flush() +{ + substdio_flush(&ssout); +} +void err(s) char *s; +{ + puts("-ERR "); + puts(s); + puts("\r\n"); + flush(); +} + +void die_usage() { err("usage: popup hostname subprogram"); die(); } +void die_nomem() { err("out of memory"); die(); } +void die_pipe() { err("unable to open pipe"); die(); } +void die_write() { err("unable to write pipe"); die(); } +void die_fork() { err("unable to fork"); die(); } +void die_childcrashed() { err("aack, child crashed"); } +void die_badauth() { err("authorization failed"); } + +void err_syntax() { err("syntax error"); } +void err_wantuser() { err("USER first"); } +void err_authoriz(arg) char *arg; { err("authorization first"); } + +void okay(arg) char *arg; { puts("+OK \r\n"); flush(); } +void pop3_quit(arg) char *arg; { okay(0); die(); } + + +char unique[FMT_ULONG + FMT_ULONG + 3]; +char *hostname; +stralloc username = {0}; +int seenuser = 0; +char **childargs; +substdio ssup; +char upbuf[128]; + + +void doanddie(user,userlen,pass) +char *user; +unsigned int userlen; /* including 0 byte */ +char *pass; +{ + int child; + int wstat; + int pi[2]; + + if (fd_copy(2,1) == -1) die_pipe(); + close(3); + if (pipe(pi) == -1) die_pipe(); + if (pi[0] != 3) die_pipe(); + switch(child = fork()) { + case -1: + die_fork(); + case 0: + close(pi[1]); + sig_pipedefault(); + execvp(*childargs,childargs); + _exit(1); + } + close(pi[0]); + substdio_fdbuf(&ssup,write,pi[1],upbuf,sizeof upbuf); + if (substdio_put(&ssup,user,userlen) == -1) die_write(); + if (substdio_put(&ssup,pass,str_len(pass) + 1) == -1) die_write(); + if (substdio_puts(&ssup,"<") == -1) die_write(); + if (substdio_puts(&ssup,unique) == -1) die_write(); + if (substdio_puts(&ssup,hostname) == -1) die_write(); + if (substdio_put(&ssup,">",2) == -1) die_write(); + if (substdio_flush(&ssup) == -1) die_write(); + close(pi[1]); + byte_zero(pass,str_len(pass)); + byte_zero(upbuf,sizeof upbuf); + if (wait_pid(&wstat,child) == -1) die(); + if (wait_crashed(wstat)) die_childcrashed(); + if (wait_exitcode(wstat)) die_badauth(); + die(); +} +void pop3_greet() +{ + char *s; + s = unique; + s += fmt_uint(s,getpid()); + *s++ = '.'; + s += fmt_ulong(s,(unsigned long) now()); + *s++ = '@'; + *s++ = 0; + puts("+OK <"); + puts(unique); + puts(hostname); + puts(">\r\n"); + flush(); +} +void pop3_user(arg) char *arg; +{ + if (!*arg) { err_syntax(); return; } + okay(0); + seenuser = 1; + if (!stralloc_copys(&username,arg)) die_nomem(); + if (!stralloc_0(&username)) die_nomem(); +} +void pop3_pass(arg) char *arg; +{ + if (!seenuser) { err_wantuser(); return; } + if (!*arg) { err_syntax(); return; } + doanddie(username.s,username.len,arg); +} +void pop3_apop(arg) char *arg; +{ + char *space; + space = arg + str_chr(arg,' '); + if (!*space) { err_syntax(); return; } + *space++ = 0; + doanddie(arg,space - arg,space); +} + +struct commands pop3commands[] = { + { "user", pop3_user, 0 } +, { "pass", pop3_pass, 0 } +, { "apop", pop3_apop, 0 } +, { "quit", pop3_quit, 0 } +, { "noop", okay, 0 } +, { 0, err_authoriz, 0 } +} ; + +void main(argc,argv) +int argc; +char **argv; +{ + sig_alarmcatch(die); + sig_pipeignore(); + + hostname = argv[1]; + if (!hostname) die_usage(); + childargs = argv + 2; + if (!*childargs) die_usage(); + + pop3_greet(); + commands(&ssin,pop3commands); + die(); +} diff --git a/qmail-pw2u.9 b/qmail-pw2u.9 new file mode 100644 index 0000000..932cd4d --- /dev/null +++ b/qmail-pw2u.9 @@ -0,0 +1,241 @@ +.TH qmail-pw2u 8 +.SH NAME +qmail-pw2u \- build address assignments from a passwd file +.SH SYNOPSIS +.B qmail-pw2u +[ +.B \-/ohHuUC +] +[ +.B \-c\fIchar +] +.SH DESCRIPTION +.B qmail-pw2u +reads a V7-format passwd file from standard input +and prints a +.BR qmail-users -format +assignment file. + +A V7-format passwd file is a series of lines. +Each line has the format + +.EX + user:password:uid:gid:gecos:home:shell +.EE + +where +.I user +is an account name, +.I uid +and +.I gid +are the user id and group id of that account, +and +.I home +is the account's home directory. +.IR password , +.IR gecos , +and +.I shell +are ignored by +.BR qmail-pw2u . + +If you put the output of +.B qmail-pw2u +into +.BR QMAILHOME/users/assign , +and then run +.BR qmail-newu , +.B qmail-lspawn +will obey the assignments printed by +.BR qmail-pw2u . +.B WARNING: +After changing any users, uids, gids, or home directories +in your passwd file, +you must run +.B qmail-pw2u +and +.B qmail-newu +again if you want +.B qmail-lspawn +to see the changes. +.SH RULES +By default, +.B qmail-pw2u +follows the same rules as +.BR qmail-getpw . +It skips +.I user +if (1) +.I uid +is zero, +(2) +.I home +does not exist, +(3) +.I user +does not own +.IR home , +or +(4) +.I user +contains uppercase letters. +It then gives each remaining +.I user +control over the basic +.I user +address and +all addresses of the form +.IR user\fBBREAK\fIanything . +A catch-all user, +.BR alias , +controls all other addresses. + +You may change these rules by setting up files in +.BR QMAILHOME/users : +.TP +.B include +Allowed users, one per line. +If +.B include +exists, and +.I user +is not listed in +.BR include , +.I user +is ignored. +.TP +.B exclude +Ignored users, one per line. +If +.B exclude +exists, and +.I user +is listed in +.BR exclude , +.I user +is ignored. +.TP +.B mailnames +Replacement names for users. +Each line has the form + +.EX + user:mailname1:mailname2:... +.EE + +The addresses +.I mailname1 +and +.I mailname1\fBBREAK\fIext +and +.I mailname2 +and so on will be delivered +to +.IR user . + +.B WARNING: +The addresses +.I user +and +.I user\fBBREAK\fIext +will not be delivered to +.I user +unless +.I user +is listed as one of the +.IR mailname s. + +A line in +.B mailnames +is silently ignored if the user does not exist. +.TP +.B subusers +Extra addresses. +Each line has the form + +.EX + sub:user:pre: +.EE + +.I sub +will be handled by +.IR home\fB/.qmail\-\fIpre , +where +.I home +is +.IR user 's +home directory; +.I sub\fBBREAK\fIext +will be handled by +.IR home\fB/.qmail\-\fIpre\fB\-\fIext . +.TP +.B append +Extra assignments, +printed at the end of +.BR qmail-pw2u 's +output. +.SH OPTIONS +.TP +.B \-o +(Default.) +Skip +.I user +if +.I home +does not exist (or is not visible to +.BR qmail-pw2u ). +Skip +.I user +if +.I home +is not owned by +.IR user . +.TP +.B \-h +Stop if +.I home +does not exist. +This is appropriate if every user is supposed to have a home directory. +Skip +.I user +if +.I home +is not owned by +.IR user . +.TP +.B \-H +Do not check the existence or ownership of +.IR home . +.TP +.B \-U +(Default.) +Skip +.I user +if there are any uppercase letters in +.IR user . +.TP +.B \-u +Allow uppercase letters in +.IR user . +.TP +.B \-c\fIchar +Use +.I char +as the user-extension delimiter +in place of +.BR BREAK . +.TP +.B \-C +Disable the user-extension mechanism. +.TP +.B \-/ +Use +.IR home\fB/.qmail\-/ ... +instead of +.IR home\fB/.qmail\- ... +.SH "SEE ALSO" +qmail-users(5), +qmail-lspawn(8), +qmail-newu(8), +qmail-getpw(8) diff --git a/qmail-pw2u.c b/qmail-pw2u.c new file mode 100644 index 0000000..4146067 --- /dev/null +++ b/qmail-pw2u.c @@ -0,0 +1,312 @@ +#include +#include +#include "substdio.h" +#include "readwrite.h" +#include "subfd.h" +#include "sgetopt.h" +#include "control.h" +#include "constmap.h" +#include "stralloc.h" +#include "fmt.h" +#include "str.h" +#include "scan.h" +#include "open.h" +#include "error.h" +#include "getln.h" +#include "auto_break.h" +#include "auto_qmail.h" +#include "auto_usera.h" + +void die_chdir() +{ + substdio_putsflush(subfderr,"qmail-pw2u: fatal: unable to chdir\n"); + _exit(111); +} +void die_nomem() +{ + substdio_putsflush(subfderr,"qmail-pw2u: fatal: out of memory\n"); + _exit(111); +} +void die_read() +{ + substdio_putsflush(subfderr,"qmail-pw2u: fatal: unable to read input\n"); + _exit(111); +} +void die_write() +{ + substdio_putsflush(subfderr,"qmail-pw2u: fatal: unable to write output\n"); + _exit(111); +} +void die_control() +{ + substdio_putsflush(subfderr,"qmail-pw2u: fatal: unable to read controls\n"); + _exit(111); +} +void die_alias() +{ + substdio_puts(subfderr,"qmail-pw2u: fatal: unable to find "); + substdio_puts(subfderr,auto_usera); + substdio_puts(subfderr," user\n"); + substdio_flush(subfderr); + _exit(111); +} +void die_home(fn) char *fn; +{ + substdio_puts(subfderr,"qmail-pw2u: fatal: unable to stat "); + substdio_puts(subfderr,fn); + substdio_puts(subfderr,"\n"); + substdio_flush(subfderr); + _exit(111); +} +void die_user(s,len) char *s; unsigned int len; +{ + substdio_puts(subfderr,"qmail-pw2u: fatal: unable to find "); + substdio_put(subfderr,s,len); + substdio_puts(subfderr," user for subuser\n"); + substdio_flush(subfderr); + _exit(111); +} + +char *dashcolon = "-:"; +int flagalias = 0; +int flagnoupper = 1; +int homestrategy = 2; +/* 2: skip if home does not exist; skip if home is not owned by user */ +/* 1: stop if home does not exist; skip if home is not owned by user */ +/* 0: don't worry about home */ + +int okincl; stralloc incl = {0}; struct constmap mapincl; +int okexcl; stralloc excl = {0}; struct constmap mapexcl; +int okmana; stralloc mana = {0}; struct constmap mapmana; + +stralloc allusers = {0}; struct constmap mapuser; + +stralloc uugh = {0}; +stralloc user = {0}; +stralloc uidstr = {0}; +stralloc gidstr = {0}; +stralloc home = {0}; +unsigned long uid; + +stralloc line = {0}; + +void doaccount() +{ + struct stat st; + int i; + char *mailnames; + char *x; + unsigned int xlen; + + if (byte_chr(line.s,line.len,'\0') < line.len) return; + + x = line.s; xlen = line.len; i = byte_chr(x,xlen,':'); if (i == xlen) return; + if (!stralloc_copyb(&user,x,i)) die_nomem(); + if (!stralloc_0(&user)) die_nomem(); + ++i; x += i; xlen -= i; i = byte_chr(x,xlen,':'); if (i == xlen) return; + ++i; x += i; xlen -= i; i = byte_chr(x,xlen,':'); if (i == xlen) return; + if (!stralloc_copyb(&uidstr,x,i)) die_nomem(); + if (!stralloc_0(&uidstr)) die_nomem(); + scan_ulong(uidstr.s,&uid); + ++i; x += i; xlen -= i; i = byte_chr(x,xlen,':'); if (i == xlen) return; + if (!stralloc_copyb(&gidstr,x,i)) die_nomem(); + if (!stralloc_0(&gidstr)) die_nomem(); + ++i; x += i; xlen -= i; i = byte_chr(x,xlen,':'); if (i == xlen) return; + ++i; x += i; xlen -= i; i = byte_chr(x,xlen,':'); if (i == xlen) return; + if (!stralloc_copyb(&home,x,i)) die_nomem(); + if (!stralloc_0(&home)) die_nomem(); + + if (!uid) return; + if (flagnoupper) + for (i = 0;i < user.len;++i) + if ((user.s[i] >= 'A') && (user.s[i] <= 'Z')) + return; + if (okincl) + if (!constmap(&mapincl,user.s,user.len - 1)) + return; + if (okexcl) + if (constmap(&mapexcl,user.s,user.len - 1)) + return; + if (homestrategy) { + if (stat(home.s,&st) == -1) { + if (errno != error_noent) die_home(home.s); + if (homestrategy == 1) die_home(home.s); + return; + } + if (st.st_uid != uid) return; + } + + if (!stralloc_copys(&uugh,":")) die_nomem(); + if (!stralloc_cats(&uugh,user.s)) die_nomem(); + if (!stralloc_cats(&uugh,":")) die_nomem(); + if (!stralloc_cats(&uugh,uidstr.s)) die_nomem(); + if (!stralloc_cats(&uugh,":")) die_nomem(); + if (!stralloc_cats(&uugh,gidstr.s)) die_nomem(); + if (!stralloc_cats(&uugh,":")) die_nomem(); + if (!stralloc_cats(&uugh,home.s)) die_nomem(); + if (!stralloc_cats(&uugh,":")) die_nomem(); + + /* XXX: avoid recording in allusers unless sub actually needs it */ + if (!stralloc_cats(&allusers,user.s)) die_nomem(); + if (!stralloc_cats(&allusers,":")) die_nomem(); + if (!stralloc_catb(&allusers,uugh.s,uugh.len)) die_nomem(); + if (!stralloc_0(&allusers)) die_nomem(); + + if (str_equal(user.s,auto_usera)) { + if (substdio_puts(subfdout,"+") == -1) die_write(); + if (substdio_put(subfdout,uugh.s,uugh.len) == -1) die_write(); + if (substdio_puts(subfdout,dashcolon) == -1) die_write(); + if (substdio_puts(subfdout,":\n") == -1) die_write(); + flagalias = 1; + } + + mailnames = 0; + if (okmana) + mailnames = constmap(&mapmana,user.s,user.len - 1); + if (!mailnames) + mailnames = user.s; + + for (;;) { + while (*mailnames == ':') ++mailnames; + if (!*mailnames) break; + + i = str_chr(mailnames,':'); + + if (substdio_puts(subfdout,"=") == -1) die_write(); + if (substdio_put(subfdout,mailnames,i) == -1) die_write(); + if (substdio_put(subfdout,uugh.s,uugh.len) == -1) die_write(); + if (substdio_puts(subfdout,"::\n") == -1) die_write(); + + if (*auto_break) { + if (substdio_puts(subfdout,"+") == -1) die_write(); + if (substdio_put(subfdout,mailnames,i) == -1) die_write(); + if (substdio_put(subfdout,auto_break,1) == -1) die_write(); + if (substdio_put(subfdout,uugh.s,uugh.len) == -1) die_write(); + if (substdio_puts(subfdout,dashcolon) == -1) die_write(); + if (substdio_puts(subfdout,":\n") == -1) die_write(); + } + + mailnames += i; + } +} + +stralloc sub = {0}; + +void dosubuser() +{ + int i; + char *x; + unsigned int xlen; + char *uugh; + + x = line.s; xlen = line.len; i = byte_chr(x,xlen,':'); if (i == xlen) return; + if (!stralloc_copyb(&sub,x,i)) die_nomem(); + ++i; x += i; xlen -= i; i = byte_chr(x,xlen,':'); if (i == xlen) return; + uugh = constmap(&mapuser,x,i); + if (!uugh) die_user(x,i); + ++i; x += i; xlen -= i; i = byte_chr(x,xlen,':'); if (i == xlen) return; + + if (substdio_puts(subfdout,"=") == -1) die_write(); + if (substdio_put(subfdout,sub.s,sub.len) == -1) die_write(); + if (substdio_puts(subfdout,uugh) == -1) die_write(); + if (substdio_puts(subfdout,dashcolon) == -1) die_write(); + if (substdio_put(subfdout,x,i) == -1) die_write(); + if (substdio_puts(subfdout,":\n") == -1) die_write(); + + if (*auto_break) { + if (substdio_puts(subfdout,"+") == -1) die_write(); + if (substdio_put(subfdout,sub.s,sub.len) == -1) die_write(); + if (substdio_put(subfdout,auto_break,1) == -1) die_write(); + if (substdio_puts(subfdout,uugh) == -1) die_write(); + if (substdio_puts(subfdout,dashcolon) == -1) die_write(); + if (substdio_put(subfdout,x,i) == -1) die_write(); + if (substdio_puts(subfdout,"-:\n") == -1) die_write(); + } +} + +int fd; +substdio ss; +char ssbuf[SUBSTDIO_INSIZE]; + +void main(argc,argv) +int argc; +char **argv; +{ + int opt; + int match; + + while ((opt = getopt(argc,argv,"/ohHuUc:C")) != opteof) + switch(opt) { + case '/': dashcolon = "-/:"; break; + case 'o': homestrategy = 2; break; + case 'h': homestrategy = 1; break; + case 'H': homestrategy = 0; break; + case 'u': flagnoupper = 0; break; + case 'U': flagnoupper = 1; break; + case 'c': *auto_break = *optarg; break; + case 'C': *auto_break = 0; break; + case '?': + default: + _exit(100); + } + + if (chdir(auto_qmail) == -1) die_chdir(); + + /* no need for control_init() */ + + okincl = control_readfile(&incl,"users/include",0); + if (okincl == -1) die_control(); + if (okincl) if (!constmap_init(&mapincl,incl.s,incl.len,0)) die_nomem(); + + okexcl = control_readfile(&excl,"users/exclude",0); + if (okexcl == -1) die_control(); + if (okexcl) if (!constmap_init(&mapexcl,excl.s,excl.len,0)) die_nomem(); + + okmana = control_readfile(&mana,"users/mailnames",0); + if (okmana == -1) die_control(); + if (okmana) if (!constmap_init(&mapmana,mana.s,mana.len,1)) die_nomem(); + + if (!stralloc_copys(&allusers,"")) die_nomem(); + + for (;;) { + if (getln(subfdin,&line,&match,'\n') == -1) die_read(); + doaccount(); + if (!match) break; + } + if (!flagalias) die_alias(); + + fd = open_read("users/subusers"); + if (fd == -1) { + if (errno != error_noent) die_control(); + } + else { + substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf)); + + if (!constmap_init(&mapuser,allusers.s,allusers.len,1)) die_nomem(); + + for (;;) { + if (getln(&ss,&line,&match,'\n') == -1) die_read(); + dosubuser(); + if (!match) break; + } + + close(fd); + } + + fd = open_read("users/append"); + if (fd == -1) { + if (errno != error_noent) die_control(); + } + else { + substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf)); + for (;;) { + if (getln(&ss,&line,&match,'\n') == -1) die_read(); + if (substdio_put(subfdout,line.s,line.len) == -1) die_write(); + if (!match) break; + } + } + + if (substdio_puts(subfdout,".\n") == -1) die_write(); + if (substdio_flush(subfdout) == -1) die_write(); + _exit(0); +} diff --git a/qmail-qmqpc.8 b/qmail-qmqpc.8 new file mode 100644 index 0000000..e11a15e --- /dev/null +++ b/qmail-qmqpc.8 @@ -0,0 +1,29 @@ +.TH qmail-qmqpc 8 +.SH NAME +qmail-qmqpc \- queue a mail message via QMQP +.SH SYNOPSIS +.B qmail-qmqpc +.SH DESCRIPTION +.B qmail-qmqpc +offers the same interface as +.BR qmail-queue , +but it gives the message to a QMQP server +instead of storing it locally. + +In a +.B mini-qmail +installation, +.B qmail-queue +is replaced with a symbolic link to +.BR qmail-qmqpc . +.SH "CONTROL FILES" +.TP 5 +.I qmqpservers +IP addresses of QMQP servers, one address per line. +.B qmail-qmqpc +will try each address in turn until it establishes a QMQP connection +or runs out of addresses. +.SH "SEE ALSO" +qmail-control(5), +qmail-queue(8), +qmail-qmqpd(8) diff --git a/qmail-qmqpc.c b/qmail-qmqpc.c new file mode 100644 index 0000000..d5adf05 --- /dev/null +++ b/qmail-qmqpc.c @@ -0,0 +1,159 @@ +#include +#include +#include +#include +#include "substdio.h" +#include "getln.h" +#include "readwrite.h" +#include "exit.h" +#include "stralloc.h" +#include "slurpclose.h" +#include "error.h" +#include "sig.h" +#include "ip.h" +#include "timeoutconn.h" +#include "timeoutread.h" +#include "timeoutwrite.h" +#include "auto_qmail.h" +#include "control.h" +#include "fmt.h" + +#define PORT_QMQP 628 + +void die_success() { _exit(0); } +void die_perm() { _exit(31); } +void nomem() { _exit(51); } +void die_read() { if (errno == error_nomem) nomem(); _exit(54); } +void die_control() { _exit(55); } +void die_socket() { _exit(56); } +void die_home() { _exit(61); } +void die_temp() { _exit(71); } +void die_conn() { _exit(74); } +void die_format() { _exit(91); } + +int lasterror = 55; +int qmqpfd; + +int saferead(fd,buf,len) int fd; char *buf; int len; +{ + int r; + r = timeoutread(60,qmqpfd,buf,len); + if (r <= 0) die_conn(); + return r; +} +int safewrite(fd,buf,len) int fd; char *buf; int len; +{ + int r; + r = timeoutwrite(60,qmqpfd,buf,len); + if (r <= 0) die_conn(); + return r; +} + +char buf[1024]; +substdio to = SUBSTDIO_FDBUF(safewrite,-1,buf,sizeof buf); +substdio from = SUBSTDIO_FDBUF(saferead,-1,buf,sizeof buf); +substdio envelope = SUBSTDIO_FDBUF(read,1,buf,sizeof buf); +/* WARNING: can use only one of these at a time! */ + +stralloc beforemessage = {0}; +stralloc message = {0}; +stralloc aftermessage = {0}; + +char strnum[FMT_ULONG]; +stralloc line = {0}; + +void getmess() +{ + int match; + + if (slurpclose(0,&message,1024) == -1) die_read(); + + strnum[fmt_ulong(strnum,(unsigned long) message.len)] = 0; + if (!stralloc_copys(&beforemessage,strnum)) nomem(); + if (!stralloc_cats(&beforemessage,":")) nomem(); + if (!stralloc_copys(&aftermessage,",")) nomem(); + + if (getln(&envelope,&line,&match,'\0') == -1) die_read(); + if (!match) die_format(); + if (line.len < 2) die_format(); + if (line.s[0] != 'F') die_format(); + + strnum[fmt_ulong(strnum,(unsigned long) line.len - 2)] = 0; + if (!stralloc_cats(&aftermessage,strnum)) nomem(); + if (!stralloc_cats(&aftermessage,":")) nomem(); + if (!stralloc_catb(&aftermessage,line.s + 1,line.len - 2)) nomem(); + if (!stralloc_cats(&aftermessage,",")) nomem(); + + for (;;) { + if (getln(&envelope,&line,&match,'\0') == -1) die_read(); + if (!match) die_format(); + if (line.len < 2) break; + if (line.s[0] != 'T') die_format(); + + strnum[fmt_ulong(strnum,(unsigned long) line.len - 2)] = 0; + if (!stralloc_cats(&aftermessage,strnum)) nomem(); + if (!stralloc_cats(&aftermessage,":")) nomem(); + if (!stralloc_catb(&aftermessage,line.s + 1,line.len - 2)) nomem(); + if (!stralloc_cats(&aftermessage,",")) nomem(); + } +} + +void doit(server) +char *server; +{ + struct ip_address ip; + char ch; + + if (!ip_scan(server,&ip)) return; + + qmqpfd = socket(AF_INET,SOCK_STREAM,0); + if (qmqpfd == -1) die_socket(); + + if (timeoutconn(qmqpfd,&ip,PORT_QMQP,10) != 0) { + lasterror = 73; + if (errno == error_timeout) lasterror = 72; + close(qmqpfd); + return; + } + + strnum[fmt_ulong(strnum,(unsigned long) (beforemessage.len + message.len + aftermessage.len))] = 0; + substdio_puts(&to,strnum); + substdio_puts(&to,":"); + substdio_put(&to,beforemessage.s,beforemessage.len); + substdio_put(&to,message.s,message.len); + substdio_put(&to,aftermessage.s,aftermessage.len); + substdio_puts(&to,","); + substdio_flush(&to); + + for (;;) { + substdio_get(&from,&ch,1); + if (ch == 'K') die_success(); + if (ch == 'Z') die_temp(); + if (ch == 'D') die_perm(); + } +} + +stralloc servers = {0}; + +main() +{ + int i; + int j; + + sig_pipeignore(); + + if (chdir(auto_qmail) == -1) die_home(); + if (control_init() == -1) die_control(); + if (control_readfile(&servers,"control/qmqpservers",0) != 1) die_control(); + + getmess(); + + i = 0; + for (j = 0;j < servers.len;++j) + if (!servers.s[j]) { + doit(servers.s + i); + i = j + 1; + } + + _exit(lasterror); +} diff --git a/qmail-qmqpd.8 b/qmail-qmqpd.8 new file mode 100644 index 0000000..5142dfa --- /dev/null +++ b/qmail-qmqpd.8 @@ -0,0 +1,25 @@ +.TH qmail-qmqpd 8 +.SH NAME +qmail-qmqpd \- receive mail via QMQP +.SH SYNOPSIS +.B qmail-qmqpd +.SH DESCRIPTION +.B qmail-qmqpd +receives mail messages via the Quick Mail Queueing Protocol (QMQP) +and invokes +.B qmail-queue +to deposit them into the outgoing queue. +.B qmail-qmqpd +must be supplied several environment variables; +see +.BR tcp-environ(5) . + +.B qmail-qmqpd +will relay messages to any destination. +It should be invoked only for connections from preauthorized users. +.SH "SEE ALSO" +tcp-env(1), +tcpserver(1), +tcp-environ(5), +qmail-qmqpc(8), +qmail-queue(8) diff --git a/qmail-qmqpd.c b/qmail-qmqpd.c new file mode 100644 index 0000000..86cb284 --- /dev/null +++ b/qmail-qmqpd.c @@ -0,0 +1,174 @@ +#include "auto_qmail.h" +#include "qmail.h" +#include "received.h" +#include "sig.h" +#include "substdio.h" +#include "readwrite.h" +#include "exit.h" +#include "now.h" +#include "fmt.h" +#include "env.h" + +void resources() { _exit(111); } + +int safewrite(fd,buf,len) int fd; char *buf; int len; +{ + int r; + r = write(fd,buf,len); + if (r <= 0) _exit(0); + return r; +} +int saferead(fd,buf,len) int fd; char *buf; int len; +{ + int r; + r = read(fd,buf,len); + if (r <= 0) _exit(0); + return r; +} + +char ssinbuf[512]; +substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf); +char ssoutbuf[256]; +substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf); + +unsigned long bytesleft = 100; + +void getbyte(ch) +char *ch; +{ + if (!bytesleft--) _exit(100); + substdio_get(&ssin,ch,1); +} + +unsigned long getlen() +{ + unsigned long len = 0; + char ch; + + for (;;) { + getbyte(&ch); + if (ch == ':') return len; + if (len > 200000000) resources(); + len = 10 * len + (ch - '0'); + } +} + +void getcomma() +{ + char ch; + getbyte(&ch); + if (ch != ',') _exit(100); +} + +struct qmail qq; + +void identify() +{ + char *remotehost; + char *remoteinfo; + char *remoteip; + char *local; + + remotehost = env_get("TCPREMOTEHOST"); + if (!remotehost) remotehost = "unknown"; + remoteinfo = env_get("TCPREMOTEINFO"); + remoteip = env_get("TCPREMOTEIP"); + if (!remoteip) remoteip = "unknown"; + local = env_get("TCPLOCALHOST"); + if (!local) local = env_get("TCPLOCALIP"); + if (!local) local = "unknown"; + + received(&qq,"QMQP",local,remoteip,remotehost,remoteinfo,(char *) 0); +} + +char buf[1000]; +char strnum[FMT_ULONG]; + +int getbuf() +{ + unsigned long len; + int i; + + len = getlen(); + if (len >= 1000) { + for (i = 0;i < len;++i) getbyte(buf); + getcomma(); + buf[0] = 0; + return 0; + } + + for (i = 0;i < len;++i) getbyte(buf + i); + getcomma(); + buf[len] = 0; + return byte_chr(buf,len,'\0') == len; +} + +int flagok = 1; + +main() +{ + char *result; + unsigned long qp; + unsigned long len; + char ch; + + sig_pipeignore(); + sig_alarmcatch(resources); + alarm(3600); + + bytesleft = getlen(); + + len = getlen(); + + if (chdir(auto_qmail) == -1) resources(); + if (qmail_open(&qq) == -1) resources(); + qp = qmail_qp(&qq); + identify(); + + while (len > 0) { /* XXX: could speed this up */ + getbyte(&ch); + --len; + qmail_put(&qq,&ch,1); + } + getcomma(); + + if (getbuf()) + qmail_from(&qq,buf); + else { + qmail_from(&qq,""); + qmail_fail(&qq); + flagok = 0; + } + + while (bytesleft) + if (getbuf()) + qmail_to(&qq,buf); + else { + qmail_fail(&qq); + flagok = 0; + } + + bytesleft = 1; + getcomma(); + + result = qmail_close(&qq); + + if (!*result) { + len = fmt_str(buf,"Kok "); + len += fmt_ulong(buf + len,(unsigned long) now()); + len += fmt_str(buf + len," qp "); + len += fmt_ulong(buf + len,qp); + buf[len] = 0; + result = buf; + } + + if (!flagok) + result = "Dsorry, I can't accept addresses like that (#5.1.3)"; + + substdio_put(&ssout,strnum,fmt_ulong(strnum,(unsigned long) str_len(result))); + substdio_puts(&ssout,":"); + substdio_puts(&ssout,result); + substdio_puts(&ssout,","); + substdio_flush(&ssout); + _exit(0); +} diff --git a/qmail-qmtpd.8 b/qmail-qmtpd.8 new file mode 100644 index 0000000..244fdbf --- /dev/null +++ b/qmail-qmtpd.8 @@ -0,0 +1,32 @@ +.TH qmail-qmtpd 8 +.SH NAME +qmail-qmtpd \- receive mail via QMTP +.SH SYNOPSIS +.B qmail-qmtpd +.SH DESCRIPTION +.B qmail-qmtpd +receives mail messages via the Quick Mail Transfer Protocol (QMTP) +and invokes +.B qmail-queue +to deposit them into the outgoing queue. +.B qmail-qmtpd +must be supplied several environment variables; +see +.BR tcp-environ(5) . + +.B qmail-qmtpd +supports the +.IR rcpthosts , +.IR morercpthosts , +.BR RELAYCLIENT , +.IR databytes , +and +.B DATABYTES +mechanisms described in +.BR qmail-smtpd(8) . +.SH "SEE ALSO" +tcp-env(1), +tcp-environ(5), +qmail-control(5), +qmail-queue(8), +qmail-smtpd(8) diff --git a/qmail-qmtpd.c b/qmail-qmtpd.c new file mode 100644 index 0000000..df911a6 --- /dev/null +++ b/qmail-qmtpd.c @@ -0,0 +1,268 @@ +#include "stralloc.h" +#include "substdio.h" +#include "qmail.h" +#include "now.h" +#include "str.h" +#include "fmt.h" +#include "env.h" +#include "sig.h" +#include "rcpthosts.h" +#include "auto_qmail.h" +#include "readwrite.h" +#include "control.h" +#include "received.h" + +void badproto() { _exit(100); } +void resources() { _exit(111); } + +int safewrite(fd,buf,len) int fd; char *buf; int len; +{ + int r; + r = write(fd,buf,len); + if (r <= 0) _exit(0); + return r; +} + +char ssoutbuf[256]; +substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf); + +int saferead(fd,buf,len) int fd; char *buf; int len; +{ + int r; + substdio_flush(&ssout); + r = read(fd,buf,len); + if (r <= 0) _exit(0); + return r; +} + +char ssinbuf[512]; +substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf); + +unsigned long getlen() +{ + unsigned long len = 0; + char ch; + for (;;) { + substdio_get(&ssin,&ch,1); + if (ch == ':') return len; + if (len > 200000000) resources(); + len = 10 * len + (ch - '0'); + } +} + +void getcomma() +{ + char ch; + substdio_get(&ssin,&ch,1); + if (ch != ',') badproto(); +} + +unsigned int databytes = 0; +unsigned int bytestooverflow = 0; +struct qmail qq; + +char buf[1000]; +char buf2[100]; + +char *remotehost; +char *remoteinfo; +char *remoteip; +char *local; + +stralloc failure = {0}; + +char *relayclient; +int relayclientlen; + +main() +{ + char ch; + int i; + unsigned long biglen; + unsigned long len; + int flagdos; + int flagsenderok; + int flagbother; + unsigned long qp; + char *result; + char *x; + unsigned long u; + + sig_pipeignore(); + sig_alarmcatch(resources); + alarm(3600); + + if (chdir(auto_qmail) == -1) resources(); + + if (control_init() == -1) resources(); + if (rcpthosts_init() == -1) resources(); + relayclient = env_get("RELAYCLIENT"); + relayclientlen = relayclient ? str_len(relayclient) : 0; + + if (control_readint(&databytes,"control/databytes") == -1) resources(); + x = env_get("DATABYTES"); + if (x) { scan_ulong(x,&u); databytes = u; } + if (!(databytes + 1)) --databytes; + + remotehost = env_get("TCPREMOTEHOST"); + if (!remotehost) remotehost = "unknown"; + remoteinfo = env_get("TCPREMOTEINFO"); + remoteip = env_get("TCPREMOTEIP"); + if (!remoteip) remoteip = "unknown"; + local = env_get("TCPLOCALHOST"); + if (!local) local = env_get("TCPLOCALIP"); + if (!local) local = "unknown"; + + for (;;) { + if (!stralloc_copys(&failure,"")) resources(); + flagsenderok = 1; + + len = getlen(); + if (len == 0) badproto(); + + if (databytes) bytestooverflow = databytes + 1; + if (qmail_open(&qq) == -1) resources(); + qp = qmail_qp(&qq); + + substdio_get(&ssin,&ch,1); + --len; + if (ch == 10) flagdos = 0; + else if (ch == 13) flagdos = 1; + else badproto(); + + received(&qq,"QMTP",local,remoteip,remotehost,remoteinfo,(char *) 0); + + /* XXX: check for loops? only if len is big? */ + + if (flagdos) + while (len > 0) { + substdio_get(&ssin,&ch,1); + --len; + while ((ch == 13) && len) { + substdio_get(&ssin,&ch,1); + --len; + if (ch == 10) break; + if (bytestooverflow) if (!--bytestooverflow) qmail_fail(&qq); + qmail_put(&qq,"\015",1); + } + if (bytestooverflow) if (!--bytestooverflow) qmail_fail(&qq); + qmail_put(&qq,&ch,1); + } + else { + if (databytes) + if (len > databytes) { + bytestooverflow = 0; + qmail_fail(&qq); + } + while (len > 0) { /* XXX: could speed this up, obviously */ + substdio_get(&ssin,&ch,1); + --len; + qmail_put(&qq,&ch,1); + } + } + getcomma(); + + len = getlen(); + + if (len >= 1000) { + buf[0] = 0; + flagsenderok = 0; + for (i = 0;i < len;++i) + substdio_get(&ssin,&ch,1); + } + else { + for (i = 0;i < len;++i) { + substdio_get(&ssin,buf + i,1); + if (!buf[i]) flagsenderok = 0; + } + buf[len] = 0; + } + getcomma(); + + flagbother = 0; + qmail_from(&qq,buf); + if (!flagsenderok) qmail_fail(&qq); + + biglen = getlen(); + while (biglen > 0) { + if (!stralloc_append(&failure,"")) resources(); + + len = 0; + for (;;) { + if (!biglen) badproto(); + substdio_get(&ssin,&ch,1); + --biglen; + if (ch == ':') break; + if (len > 200000000) resources(); + len = 10 * len + (ch - '0'); + } + if (len >= biglen) badproto(); + if (len + relayclientlen >= 1000) { + failure.s[failure.len - 1] = 'L'; + for (i = 0;i < len;++i) + substdio_get(&ssin,&ch,1); + } + else { + for (i = 0;i < len;++i) { + substdio_get(&ssin,buf + i,1); + if (!buf[i]) failure.s[failure.len - 1] = 'N'; + } + buf[len] = 0; + + if (relayclient) + str_copy(buf + len,relayclient); + else + switch(rcpthosts(buf,len)) { + case -1: resources(); + case 0: failure.s[failure.len - 1] = 'D'; + } + + if (!failure.s[failure.len - 1]) { + qmail_to(&qq,buf); + flagbother = 1; + } + } + getcomma(); + biglen -= (len + 1); + } + getcomma(); + + if (!flagbother) qmail_fail(&qq); + result = qmail_close(&qq); + if (!flagsenderok) result = "Dunacceptable sender (#5.1.7)"; + if (databytes) if (!bytestooverflow) result = "Dsorry, that message size exceeds my databytes limit (#5.3.4)"; + + if (*result) + len = str_len(result); + else { + /* success! */ + len = 0; + len += fmt_str(buf2 + len,"Kok "); + len += fmt_ulong(buf2 + len,(unsigned long) now()); + len += fmt_str(buf2 + len," qp "); + len += fmt_ulong(buf2 + len,qp); + buf2[len] = 0; + result = buf2; + } + + len = fmt_ulong(buf,len); + buf[len++] = ':'; + len += fmt_str(buf + len,result); + buf[len++] = ','; + + for (i = 0;i < failure.len;++i) + switch(failure.s[i]) { + case 0: + substdio_put(&ssout,buf,len); + break; + case 'D': + substdio_puts(&ssout,"66:Dsorry, that domain isn't in my list of allowed rcpthosts (#5.7.1),"); + break; + default: + substdio_puts(&ssout,"46:Dsorry, I can't handle that recipient (#5.1.3),"); + break; + } + + /* ssout will be flushed when we read from the network again */ + } +} diff --git a/qmail-qread.8 b/qmail-qread.8 new file mode 100644 index 0000000..a4c31ef --- /dev/null +++ b/qmail-qread.8 @@ -0,0 +1,24 @@ +.TH qmail-qread 8 +.SH NAME +qmail-qread \- list outgoing messages and recipients +.SH SYNOPSIS +.B qmail-qread +.SH DESCRIPTION +.B qmail-qread +scans the outgoing queue of messages. +For each message it prints various human-readable information, +including the date the message entered the queue, +the number of bytes in the message, +the message sender, +and all the recipients still under consideration. + +.B qmail-qread +must be run either as +.B root +or with user id +.B qmails +and group id +.BR qmail . +.SH "SEE ALSO" +qmail-qstat(8), +qmail-send(8) diff --git a/qmail-qread.c b/qmail-qread.c new file mode 100644 index 0000000..8ec0fbd --- /dev/null +++ b/qmail-qread.c @@ -0,0 +1,175 @@ +#include +#include +#include "stralloc.h" +#include "substdio.h" +#include "subfd.h" +#include "fmt.h" +#include "str.h" +#include "getln.h" +#include "fmtqfn.h" +#include "readsubdir.h" +#include "auto_qmail.h" +#include "open.h" +#include "datetime.h" +#include "date822fmt.h" +#include "readwrite.h" +#include "error.h" +#include "exit.h" + +readsubdir rs; + +void die(n) int n; { substdio_flush(subfdout); _exit(n); } + +void warn(s1,s2) char *s1; char *s2; +{ + char *x; + x = error_str(errno); + substdio_puts(subfdout,s1); + substdio_puts(subfdout,s2); + substdio_puts(subfdout,": "); + substdio_puts(subfdout,x); + substdio_puts(subfdout,"\n"); +} + +void die_nomem() { substdio_puts(subfdout,"fatal: out of memory\n"); die(111); } +void die_chdir() { warn("fatal: unable to chdir",""); die(111); } +void die_opendir(fn) char *fn; { warn("fatal: unable to opendir ",fn); die(111); } + +void err(id) unsigned long id; +{ + char foo[FMT_ULONG]; + foo[fmt_ulong(foo,id)] = 0; + warn("warning: trouble with #",foo); +} + +char fnmess[FMTQFN]; +char fninfo[FMTQFN]; +char fnlocal[FMTQFN]; +char fnremote[FMTQFN]; +char fnbounce[FMTQFN]; + +char inbuf[1024]; +stralloc sender = {0}; + +unsigned long id; +datetime_sec qtime; +int flagbounce; +unsigned long size; + +unsigned int fmtstats(s) +char *s; +{ + struct datetime dt; + unsigned int len; + unsigned int i; + + len = 0; + datetime_tai(&dt,qtime); + i = date822fmt(s,&dt) - 7/*XXX*/; len += i; if (s) s += i; + i = fmt_str(s," GMT #"); len += i; if (s) s += i; + i = fmt_ulong(s,id); len += i; if (s) s += i; + i = fmt_str(s," "); len += i; if (s) s += i; + i = fmt_ulong(s,size); len += i; if (s) s += i; + i = fmt_str(s," <"); len += i; if (s) s += i; + i = fmt_str(s,sender.s + 1); len += i; if (s) s += i; + i = fmt_str(s,"> "); len += i; if (s) s += i; + if (flagbounce) + { + i = fmt_str(s," bouncing"); len += i; if (s) s += i; + } + + return len; +} + +stralloc stats = {0}; + +void out(s,n) char *s; unsigned int n; +{ + while (n > 0) + { + substdio_put(subfdout,((*s >= 32) && (*s <= 126)) ? s : "_",1); + --n; + ++s; + } +} +void outs(s) char *s; { out(s,str_len(s)); } +void outok(s) char *s; { substdio_puts(subfdout,s); } + +void putstats() +{ + if (!stralloc_ready(&stats,fmtstats(FMT_LEN))) die_nomem(); + stats.len = fmtstats(stats.s); + out(stats.s,stats.len); + outok("\n"); +} + +stralloc line = {0}; + +void main() +{ + int channel; + int match; + struct stat st; + int fd; + substdio ss; + int x; + + if (chdir(auto_qmail) == -1) die_chdir(); + if (chdir("queue") == -1) die_chdir(); + readsubdir_init(&rs,"info",die_opendir); + + while (x = readsubdir_next(&rs,&id)) + if (x > 0) + { + fmtqfn(fnmess,"mess/",id,1); + fmtqfn(fninfo,"info/",id,1); + fmtqfn(fnlocal,"local/",id,1); + fmtqfn(fnremote,"remote/",id,1); + fmtqfn(fnbounce,"bounce/",id,0); + + if (stat(fnmess,&st) == -1) { err(id); continue; } + size = st.st_size; + flagbounce = !stat(fnbounce,&st); + + fd = open_read(fninfo); + if (fd == -1) { err(id); continue; } + substdio_fdbuf(&ss,read,fd,inbuf,sizeof(inbuf)); + if (getln(&ss,&sender,&match,0) == -1) die_nomem(); + if (fstat(fd,&st) == -1) { close(fd); err(id); continue; } + close(fd); + qtime = st.st_mtime; + + putstats(); + + for (channel = 0;channel < 2;++channel) + { + fd = open_read(channel ? fnremote : fnlocal); + if (fd == -1) + { + if (errno != error_noent) + err(id); + } + else + { + for (;;) + { + if (getln(&ss,&line,&match,0) == -1) die_nomem(); + if (!match) break; + switch(line.s[0]) + { + case 'D': + outok(" done"); + case 'T': + outok(channel ? "\tremote\t" : "\tlocal\t"); + outs(line.s + 1); + outok("\n"); + break; + } + } + close(fd); + } + } + } + + die(0); +} diff --git a/qmail-qstat.8 b/qmail-qstat.8 new file mode 100644 index 0000000..3491e34 --- /dev/null +++ b/qmail-qstat.8 @@ -0,0 +1,18 @@ +.TH qmail-qstat 8 +.SH NAME +qmail-qstat \- summarize status of mail queue +.SH SYNOPSIS +.B qmail-qstat +.SH DESCRIPTION +.B qmail-qstat +gives a human-readable breakdown +of the number of messages at various spots in the mail queue. + +.B qmail-qstat +must be run either as +.B root +or with group id +.BR qmail . +.SH "SEE ALSO" +qmail-qread(8), +qmail-send(8) diff --git a/qmail-qstat.sh b/qmail-qstat.sh new file mode 100644 index 0000000..26a6d3f --- /dev/null +++ b/qmail-qstat.sh @@ -0,0 +1,7 @@ +cd QMAIL +messdirs=`echo queue/mess/* | wc -w` +messfiles=`find queue/mess/* -print | wc -w` +tododirs=`echo queue/todo | wc -w` +todofiles=`find queue/todo -print | wc -w` +echo messages in queue: `expr $messfiles - $messdirs` +echo messages in queue but not yet preprocessed: `expr $todofiles - $tododirs` diff --git a/qmail-queue.8 b/qmail-queue.8 new file mode 100644 index 0000000..ded3285 --- /dev/null +++ b/qmail-queue.8 @@ -0,0 +1,161 @@ +.TH qmail-queue 8 +.SH NAME +qmail-queue \- queue a mail message for delivery +.SH SYNOPSIS +.B qmail-queue +.SH DESCRIPTION +.B qmail-queue +reads a mail message from descriptor 0. +It then reads envelope information from descriptor 1. +It places the message into the outgoing queue +for future delivery by +.BR qmail-send . + +The envelope information is +an envelope sender address +followed by a list of envelope recipient addresses. +The sender address is preceded by the letter F +and terminated by a 0 byte. +Each recipient address is preceded by the letter T +and terminated by a 0 byte. +The list of recipient addresses is terminated by an extra 0 byte. +If +.B qmail-queue +sees end-of-file before the extra 0 byte, +it aborts without placing the message into the queue. + +Every envelope recipient address +should contain a username, +an @ sign, +and a fully qualified domain name. + +.B qmail-queue +always adds a +.B Received +line to the top of the message. +Other than this, +.B qmail-queue +does not inspect the message +and does not enforce any restrictions on its contents. +However, the recipients probably expect to see a proper header, +as described in +.BR qmail-header(5) . + +Programs included with qmail which invoke +.B qmail-queue +will invoke the contents of +.B $QMAILQUEUE +instead, if that environment variable is set. +.SH "FILESYSTEM RESTRICTIONS" +.B qmail-queue +imposes two constraints on the queue structure: +each +.B mess +subdirectory must be in the same filesystem as the +.B pid +directory; and each +.B todo +subdirectory must be in the same filesystem as the +.B intd +directory. +.SH "EXIT CODES" +.B qmail-queue +does not print diagnostics. +It exits +0 if +it has successfully queued the message. +It exits between 1 and 99 if +it has failed to queue the message. + +All +.B qmail-queue +error codes between 11 and 40 +indicate permanent errors: +.TP 5 +.B 11 +Address too long. +.TP +.B 31 +Mail server permanently refuses to send the message to any recipients. +(Not used by +.BR qmail-queue , +but can be used by programs offering the same interface.) +.PP +All other +.B qmail-queue +error codes indicate temporary errors: +.TP 5 +.B 51 +Out of memory. +.TP +.B 52 +Timeout. +.TP +.B 53 +Write error; e.g., disk full. +.TP +.B 54 +Unable to read the message or envelope. +.TP +.B 55 +Unable to read a configuration file. +(Not used by +.BR qmail-queue .) +.TP +.B 56 +Problem making a network connection from this host. +(Not used by +.BR qmail-queue .) +.TP +.B 61 +Problem with the qmail home directory. +.TP +.B 62 +Problem with the queue directory. +.TP +.B 63 +Problem with queue/pid. +.TP +.B 64 +Problem with queue/mess. +.TP +.B 65 +Problem with queue/intd. +.TP +.B 66 +Problem with queue/todo. +.TP +.B 71 +Mail server temporarily refuses to send the message to any recipients. +(Not used by +.BR qmail-queue .) +.TP +.B 72 +Connection to mail server timed out. +(Not used by +.BR qmail-queue .) +.TP +.B 73 +Connection to mail server rejected. +(Not used by +.BR qmail-queue .) +.TP +.B 74 +Connection to mail server succeeded, +but communication failed. +(Not used by +.BR qmail-queue .) +.TP +.B 81 +Internal bug; e.g., segmentation fault. +.TP +.B 91 +Envelope format error. +.SH "SEE ALSO" +addresses(5), +envelopes(5), +qmail-header(5), +qmail-inject(8), +qmail-qmqpc(8), +qmail-send(8), +qmail-smtpd(8) diff --git a/qmail-queue.c b/qmail-queue.c new file mode 100644 index 0000000..4b39a8f --- /dev/null +++ b/qmail-queue.c @@ -0,0 +1,254 @@ +#include +#include +#include "readwrite.h" +#include "sig.h" +#include "exit.h" +#include "open.h" +#include "seek.h" +#include "fmt.h" +#include "alloc.h" +#include "substdio.h" +#include "datetime.h" +#include "now.h" +#include "triggerpull.h" +#include "extra.h" +#include "auto_qmail.h" +#include "auto_uids.h" +#include "date822fmt.h" +#include "fmtqfn.h" + +#define DEATH 86400 /* 24 hours; _must_ be below q-s's OSSIFIED (36 hours) */ +#define ADDR 1003 + +char inbuf[2048]; +struct substdio ssin; +char outbuf[256]; +struct substdio ssout; + +datetime_sec starttime; +struct datetime dt; +unsigned long mypid; +unsigned long uid; +char *pidfn; +struct stat pidst; +unsigned long messnum; +char *messfn; +char *todofn; +char *intdfn; +int messfd; +int intdfd; +int flagmademess = 0; +int flagmadeintd = 0; + +void cleanup() +{ + if (flagmadeintd) + { + seek_trunc(intdfd,0); + if (unlink(intdfn) == -1) return; + } + if (flagmademess) + { + seek_trunc(messfd,0); + if (unlink(messfn) == -1) return; + } +} + +void die(e) int e; { _exit(e); } +void die_write() { cleanup(); die(53); } +void die_read() { cleanup(); die(54); } +void sigalrm() { /* thou shalt not clean up here */ die(52); } +void sigbug() { die(81); } + +unsigned int receivedlen; +char *received; +/* "Received: (qmail-queue invoked by alias); 26 Sep 1995 04:46:54 -0000\n" */ + +static unsigned int receivedfmt(s) +char *s; +{ + unsigned int i; + unsigned int len; + len = 0; + i = fmt_str(s,"Received: (qmail "); len += i; if (s) s += i; + i = fmt_ulong(s,mypid); len += i; if (s) s += i; + i = fmt_str(s," invoked "); len += i; if (s) s += i; + if (uid == auto_uida) + { i = fmt_str(s,"by alias"); len += i; if (s) s += i; } + else if (uid == auto_uidd) + { i = fmt_str(s,"from network"); len += i; if (s) s += i; } + else if (uid == auto_uids) + { i = fmt_str(s,"for bounce"); len += i; if (s) s += i; } + else + { + i = fmt_str(s,"by uid "); len += i; if (s) s += i; + i = fmt_ulong(s,uid); len += i; if (s) s += i; + } + i = fmt_str(s,"); "); len += i; if (s) s += i; + i = date822fmt(s,&dt); len += i; if (s) s += i; + return len; +} + +void received_setup() +{ + receivedlen = receivedfmt((char *) 0); + received = alloc(receivedlen + 1); + if (!received) die(51); + receivedfmt(received); +} + +unsigned int pidfmt(s,seq) +char *s; +unsigned long seq; +{ + unsigned int i; + unsigned int len; + + len = 0; + i = fmt_str(s,"pid/"); len += i; if (s) s += i; + i = fmt_ulong(s,mypid); len += i; if (s) s += i; + i = fmt_str(s,"."); len += i; if (s) s += i; + i = fmt_ulong(s,starttime); len += i; if (s) s += i; + i = fmt_str(s,"."); len += i; if (s) s += i; + i = fmt_ulong(s,seq); len += i; if (s) s += i; + ++len; if (s) *s++ = 0; + + return len; +} + +char *fnnum(dirslash,flagsplit) +char *dirslash; +int flagsplit; +{ + char *s; + + s = alloc(fmtqfn((char *) 0,dirslash,messnum,flagsplit)); + if (!s) die(51); + fmtqfn(s,dirslash,messnum,flagsplit); + return s; +} + +void pidopen() +{ + unsigned int len; + unsigned long seq; + + seq = 1; + len = pidfmt((char *) 0,seq); + pidfn = alloc(len); + if (!pidfn) die(51); + + for (seq = 1;seq < 10;++seq) + { + if (pidfmt((char *) 0,seq) > len) die(81); /* paranoia */ + pidfmt(pidfn,seq); + messfd = open_excl(pidfn); + if (messfd != -1) return; + } + + die(63); +} + +char tmp[FMT_ULONG]; + +void main() +{ + unsigned int len; + char ch; + + sig_blocknone(); + umask(033); + if (chdir(auto_qmail) == -1) die(61); + if (chdir("queue") == -1) die(62); + + mypid = getpid(); + uid = getuid(); + starttime = now(); + datetime_tai(&dt,starttime); + + received_setup(); + + sig_pipeignore(); + sig_miscignore(); + sig_alarmcatch(sigalrm); + sig_bugcatch(sigbug); + + alarm(DEATH); + + pidopen(); + if (fstat(messfd,&pidst) == -1) die(63); + + messnum = pidst.st_ino; + messfn = fnnum("mess/",1); + todofn = fnnum("todo/",0); + intdfn = fnnum("intd/",0); + + if (link(pidfn,messfn) == -1) die(64); + if (unlink(pidfn) == -1) die(63); + flagmademess = 1; + + substdio_fdbuf(&ssout,write,messfd,outbuf,sizeof(outbuf)); + substdio_fdbuf(&ssin,read,0,inbuf,sizeof(inbuf)); + + if (substdio_bput(&ssout,received,receivedlen) == -1) die_write(); + + switch(substdio_copy(&ssout,&ssin)) + { + case -2: die_read(); + case -3: die_write(); + } + + if (substdio_flush(&ssout) == -1) die_write(); + if (fsync(messfd) == -1) die_write(); + + intdfd = open_excl(intdfn); + if (intdfd == -1) die(65); + flagmadeintd = 1; + + substdio_fdbuf(&ssout,write,intdfd,outbuf,sizeof(outbuf)); + substdio_fdbuf(&ssin,read,1,inbuf,sizeof(inbuf)); + + if (substdio_bput(&ssout,"u",1) == -1) die_write(); + if (substdio_bput(&ssout,tmp,fmt_ulong(tmp,uid)) == -1) die_write(); + if (substdio_bput(&ssout,"",1) == -1) die_write(); + + if (substdio_bput(&ssout,"p",1) == -1) die_write(); + if (substdio_bput(&ssout,tmp,fmt_ulong(tmp,mypid)) == -1) die_write(); + if (substdio_bput(&ssout,"",1) == -1) die_write(); + + if (substdio_get(&ssin,&ch,1) < 1) die_read(); + if (ch != 'F') die(91); + if (substdio_bput(&ssout,&ch,1) == -1) die_write(); + for (len = 0;len < ADDR;++len) + { + if (substdio_get(&ssin,&ch,1) < 1) die_read(); + if (substdio_put(&ssout,&ch,1) == -1) die_write(); + if (!ch) break; + } + if (len >= ADDR) die(11); + + if (substdio_bput(&ssout,QUEUE_EXTRA,QUEUE_EXTRALEN) == -1) die_write(); + + for (;;) + { + if (substdio_get(&ssin,&ch,1) < 1) die_read(); + if (!ch) break; + if (ch != 'T') die(91); + if (substdio_bput(&ssout,&ch,1) == -1) die_write(); + for (len = 0;len < ADDR;++len) + { + if (substdio_get(&ssin,&ch,1) < 1) die_read(); + if (substdio_bput(&ssout,&ch,1) == -1) die_write(); + if (!ch) break; + } + if (len >= ADDR) die(11); + } + + if (substdio_flush(&ssout) == -1) die_write(); + if (fsync(intdfd) == -1) die_write(); + + if (link(intdfn,todofn) == -1) die(66); + + triggerpull(); + die(0); +} diff --git a/qmail-remote.8 b/qmail-remote.8 new file mode 100644 index 0000000..08bae85 --- /dev/null +++ b/qmail-remote.8 @@ -0,0 +1,205 @@ +.TH qmail-remote 8 +.SH NAME +qmail-remote \- send mail via SMTP +.SH SYNOPSIS +.B qmail-remote +.I host +.I sender +.I recip +[ +.I recip ... +] +.SH DESCRIPTION +.B qmail-remote +reads a mail message from its input +and sends the message +to one or more recipients +at a remote host. + +The remote host is +.BR qmail-remote 's +first argument, +.IR host . +.B qmail-remote +sends the message to +.IR host , +or to a mail exchanger for +.I host +listed in the Domain Name System, +via the Simple Mail Transfer Protocol (SMTP). +.I host +can be either a fully-qualified domain name: + +.EX + silverton.berkeley.edu +.EE + +or an IP address enclosed in brackets: + +.EX + [128.32.183.163] +.EE + +The envelope recipient addresses are listed as +.I recip +arguments to +.BR qmail-remote . +The envelope sender address is listed as +.I sender\fP. + +Note that +.B qmail-remote +does not take options +and does not follow the +.B getopt +standard. +.SH TRANSPARENCY +End-of-file in SMTP is encoded as dot CR LF. +A dot at the beginning of a line is encoded as dot dot. +It is impossible in SMTP to send a message that does not end with a newline. +.B qmail-remote +converts the UNIX newline convention into the SMTP newline convention +by inserting CR before each LF. + +It is a violation of the SMTP protocol +to send a message that contains long lines or non-ASCII characters. +However, +.B qmail-remote +will happily send such messages. +It is the user's responsibility to avoid generating illegal messages. +.SH "RESULTS" +.B qmail-remote +prints some number of +.I recipient reports\fP, +followed by a +.I message report\fR. +Each report is terminated by a 0 byte. +Each report begins with a single letter: +.TP 5 +r +Recipient report: acceptance. +.TP 5 +h +Recipient report: permanent rejection. +.TP 5 +s +Recipient report: temporary rejection. +.TP 5 +K +Message report: success. +.I host +has taken responsibility for delivering the message to each +acceptable recipient. +.TP 5 +Z +Message report: temporary failure. +.TP 5 +D +Message report: permanent failure. +.PP +After this letter comes a human-readable description of +what happened. + +The recipient reports will always be printed in the same order as +.BR qmail-remote 's +.I recip +arguments. +Note that in failure cases there may be fewer +recipient reports +than +.I recip +arguments. + +.B qmail-remote +always exits zero. +.SH "CONTROL FILES" +.TP 5 +.I helohost +Current host name, +for use solely in saying hello to the remote SMTP server. +Default: +.IR me , +if that is supplied; +otherwise +.B qmail-remote +refuses to run. +.TP 5 +.I smtproutes +Artificial SMTP routes. +Each route has the form +.IR domain\fB:\fIrelay , +without any extra spaces. +If +.I domain +matches +.IR host , +.B qmail-remote +will connect to +.IR relay , +as if +.I host +had +.I relay +as its only MX. +(It will also avoid doing any CNAME lookups on +.IR recip .) +.I host +may include a colon and a port number to use instead of the +normal SMTP port, 25: + +.EX + inside.af.mil:firewall.af.mil:26 +.EE + +.I relay +may be empty; +this tells +.B qmail-remote +to look up MX records as usual. +.I smtproutes +may include wildcards: + +.EX + .af.mil: + :heaven.af.mil +.EE + +Here +any address ending with +.B .af.mil +(but not +.B af.mil +itself) +is routed by its MX records; +any other address is artificially routed to +.BR heaven.af.mil . + +The +.B qmail +system does not protect you if you create an artificial +mail loop between machines. +However, +you are always safe using +.I smtproutes +if you do not accept mail from the network. +.TP 5 +.I timeoutconnect +Number of seconds +.B qmail-remote +will wait for the remote SMTP server to accept a connection. +Default: 60. +The kernel normally imposes a 75-second upper limit. +.TP 5 +.I timeoutremote +Number of seconds +.B qmail-remote +will wait for each response from the remote SMTP server. +Default: 1200. +.SH "SEE ALSO" +addresses(5), +envelopes(5), +qmail-control(5), +qmail-send(8), +qmail-smtpd(8), +qmail-tcpok(8), +qmail-tcpto(8) diff --git a/qmail-remote.c b/qmail-remote.c new file mode 100644 index 0000000..7d65473 --- /dev/null +++ b/qmail-remote.c @@ -0,0 +1,427 @@ +#include +#include +#include +#include +#include "sig.h" +#include "stralloc.h" +#include "substdio.h" +#include "subfd.h" +#include "scan.h" +#include "case.h" +#include "error.h" +#include "auto_qmail.h" +#include "control.h" +#include "dns.h" +#include "alloc.h" +#include "quote.h" +#include "ip.h" +#include "ipalloc.h" +#include "ipme.h" +#include "gen_alloc.h" +#include "gen_allocdefs.h" +#include "str.h" +#include "now.h" +#include "exit.h" +#include "constmap.h" +#include "tcpto.h" +#include "readwrite.h" +#include "timeoutconn.h" +#include "timeoutread.h" +#include "timeoutwrite.h" + +#define HUGESMTPTEXT 5000 + +#define PORT_SMTP 25 /* silly rabbit, /etc/services is for users */ +unsigned long port = PORT_SMTP; + +GEN_ALLOC_typedef(saa,stralloc,sa,len,a) +GEN_ALLOC_readyplus(saa,stralloc,sa,len,a,i,n,x,10,saa_readyplus) +static stralloc sauninit = {0}; + +stralloc helohost = {0}; +stralloc routes = {0}; +struct constmap maproutes; +stralloc host = {0}; +stralloc sender = {0}; + +saa reciplist = {0}; + +struct ip_address partner; + +void out(s) char *s; { if (substdio_puts(subfdoutsmall,s) == -1) _exit(0); } +void zero() { if (substdio_put(subfdoutsmall,"\0",1) == -1) _exit(0); } +void zerodie() { zero(); substdio_flush(subfdoutsmall); _exit(0); } +void outsafe(sa) stralloc *sa; { int i; char ch; +for (i = 0;i < sa->len;++i) { +ch = sa->s[i]; if (ch < 33) ch = '?'; if (ch > 126) ch = '?'; +if (substdio_put(subfdoutsmall,&ch,1) == -1) _exit(0); } } + +void temp_nomem() { out("ZOut of memory. (#4.3.0)\n"); zerodie(); } +void temp_oserr() { out("Z\ +System resources temporarily unavailable. (#4.3.0)\n"); zerodie(); } +void temp_noconn() { out("Z\ +Sorry, I wasn't able to establish an SMTP connection. (#4.4.1)\n"); zerodie(); } +void temp_read() { out("ZUnable to read message. (#4.3.0)\n"); zerodie(); } +void temp_dnscanon() { out("Z\ +CNAME lookup failed temporarily. (#4.4.3)\n"); zerodie(); } +void temp_dns() { out("Z\ +Sorry, I couldn't find any host by that name. (#4.1.2)\n"); zerodie(); } +void temp_chdir() { out("Z\ +Unable to switch to home directory. (#4.3.0)\n"); zerodie(); } +void temp_control() { out("Z\ +Unable to read control files. (#4.3.0)\n"); zerodie(); } +void perm_partialline() { out("D\ +SMTP cannot transfer messages with partial final lines. (#5.6.2)\n"); zerodie(); } +void perm_usage() { out("D\ +I (qmail-remote) was invoked improperly. (#5.3.5)\n"); zerodie(); } +void perm_dns() { out("D\ +Sorry, I couldn't find any host named "); +outsafe(&host); +out(". (#5.1.2)\n"); zerodie(); } +void perm_nomx() { out("D\ +Sorry, I couldn't find a mail exchanger or IP address. (#5.4.4)\n"); +zerodie(); } +void perm_ambigmx() { out("D\ +Sorry. Although I'm listed as a best-preference MX or A for that host,\n\ +it isn't in my control/locals file, so I don't treat it as local. (#5.4.6)\n"); +zerodie(); } + +void outhost() +{ + char x[IPFMT]; + if (substdio_put(subfdoutsmall,x,ip_fmt(x,&partner)) == -1) _exit(0); +} + +int flagcritical = 0; + +void dropped() { + out("ZConnected to "); + outhost(); + out(" but connection died. "); + if (flagcritical) out("Possible duplicate! "); + out("(#4.4.2)\n"); + zerodie(); +} + +int timeoutconnect = 60; +int smtpfd; +int timeout = 1200; + +int saferead(fd,buf,len) int fd; char *buf; int len; +{ + int r; + r = timeoutread(timeout,smtpfd,buf,len); + if (r <= 0) dropped(); + return r; +} +int safewrite(fd,buf,len) int fd; char *buf; int len; +{ + int r; + r = timeoutwrite(timeout,smtpfd,buf,len); + if (r <= 0) dropped(); + return r; +} + +char inbuf[1024]; +substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof inbuf); +char smtptobuf[1024]; +substdio smtpto = SUBSTDIO_FDBUF(safewrite,-1,smtptobuf,sizeof smtptobuf); +char smtpfrombuf[128]; +substdio smtpfrom = SUBSTDIO_FDBUF(saferead,-1,smtpfrombuf,sizeof smtpfrombuf); + +stralloc smtptext = {0}; + +void get(ch) +char *ch; +{ + substdio_get(&smtpfrom,ch,1); + if (*ch != '\r') + if (smtptext.len < HUGESMTPTEXT) + if (!stralloc_append(&smtptext,ch)) temp_nomem(); +} + +unsigned long smtpcode() +{ + unsigned char ch; + unsigned long code; + + if (!stralloc_copys(&smtptext,"")) temp_nomem(); + + get(&ch); code = ch - '0'; + get(&ch); code = code * 10 + (ch - '0'); + get(&ch); code = code * 10 + (ch - '0'); + for (;;) { + get(&ch); + if (ch != '-') break; + while (ch != '\n') get(&ch); + get(&ch); + get(&ch); + get(&ch); + } + while (ch != '\n') get(&ch); + + return code; +} + +void outsmtptext() +{ + int i; + if (smtptext.s) if (smtptext.len) { + out("Remote host said: "); + for (i = 0;i < smtptext.len;++i) + if (!smtptext.s[i]) smtptext.s[i] = '?'; + if (substdio_put(subfdoutsmall,smtptext.s,smtptext.len) == -1) _exit(0); + smtptext.len = 0; + } +} + +void quit(prepend,append) +char *prepend; +char *append; +{ + substdio_putsflush(&smtpto,"QUIT\r\n"); + /* waiting for remote side is just too ridiculous */ + out(prepend); + outhost(); + out(append); + out(".\n"); + outsmtptext(); + zerodie(); +} + +void blast() +{ + int r; + char ch; + + for (;;) { + r = substdio_get(&ssin,&ch,1); + if (r == 0) break; + if (r == -1) temp_read(); + if (ch == '.') + substdio_put(&smtpto,".",1); + while (ch != '\n') { + substdio_put(&smtpto,&ch,1); + r = substdio_get(&ssin,&ch,1); + if (r == 0) perm_partialline(); + if (r == -1) temp_read(); + } + substdio_put(&smtpto,"\r\n",2); + } + + flagcritical = 1; + substdio_put(&smtpto,".\r\n",3); + substdio_flush(&smtpto); +} + +stralloc recip = {0}; + +void smtp() +{ + unsigned long code; + int flagbother; + int i; + + if (smtpcode() != 220) quit("ZConnected to "," but greeting failed"); + + substdio_puts(&smtpto,"HELO "); + substdio_put(&smtpto,helohost.s,helohost.len); + substdio_puts(&smtpto,"\r\n"); + substdio_flush(&smtpto); + if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected"); + + substdio_puts(&smtpto,"MAIL FROM:<"); + substdio_put(&smtpto,sender.s,sender.len); + substdio_puts(&smtpto,">\r\n"); + substdio_flush(&smtpto); + code = smtpcode(); + if (code >= 500) quit("DConnected to "," but sender was rejected"); + if (code >= 400) quit("ZConnected to "," but sender was rejected"); + + flagbother = 0; + for (i = 0;i < reciplist.len;++i) { + substdio_puts(&smtpto,"RCPT TO:<"); + substdio_put(&smtpto,reciplist.sa[i].s,reciplist.sa[i].len); + substdio_puts(&smtpto,">\r\n"); + substdio_flush(&smtpto); + code = smtpcode(); + if (code >= 500) { + out("h"); outhost(); out(" does not like recipient.\n"); + outsmtptext(); zero(); + } + else if (code >= 400) { + out("s"); outhost(); out(" does not like recipient.\n"); + outsmtptext(); zero(); + } + else { + out("r"); zero(); + flagbother = 1; + } + } + if (!flagbother) quit("DGiving up on ",""); + + substdio_putsflush(&smtpto,"DATA\r\n"); + code = smtpcode(); + if (code >= 500) quit("D"," failed on DATA command"); + if (code >= 400) quit("Z"," failed on DATA command"); + + blast(); + code = smtpcode(); + flagcritical = 0; + if (code >= 500) quit("D"," failed after I sent the message"); + if (code >= 400) quit("Z"," failed after I sent the message"); + quit("K"," accepted message"); +} + +stralloc canonhost = {0}; +stralloc canonbox = {0}; + +void addrmangle(saout,s,flagalias,flagcname) +stralloc *saout; /* host has to be canonical, box has to be quoted */ +char *s; +int *flagalias; +int flagcname; +{ + int j; + + *flagalias = flagcname; + + j = str_rchr(s,'@'); + if (!s[j]) { + if (!stralloc_copys(saout,s)) temp_nomem(); + return; + } + if (!stralloc_copys(&canonbox,s)) temp_nomem(); + canonbox.len = j; + if (!quote(saout,&canonbox)) temp_nomem(); + if (!stralloc_cats(saout,"@")) temp_nomem(); + + if (!stralloc_copys(&canonhost,s + j + 1)) temp_nomem(); + if (flagcname) + switch(dns_cname(&canonhost)) { + case 0: *flagalias = 0; break; + case DNS_MEM: temp_nomem(); + case DNS_SOFT: temp_dnscanon(); + case DNS_HARD: ; /* alias loop, not our problem */ + } + + if (!stralloc_cat(saout,&canonhost)) temp_nomem(); +} + +void getcontrols() +{ + if (control_init() == -1) temp_control(); + if (control_readint(&timeout,"control/timeoutremote") == -1) temp_control(); + if (control_readint(&timeoutconnect,"control/timeoutconnect") == -1) + temp_control(); + if (control_rldef(&helohost,"control/helohost",1,(char *) 0) != 1) + temp_control(); + switch(control_readfile(&routes,"control/smtproutes",0)) { + case -1: + temp_control(); + case 0: + if (!constmap_init(&maproutes,"",0,1)) temp_nomem(); break; + case 1: + if (!constmap_init(&maproutes,routes.s,routes.len,1)) temp_nomem(); break; + } +} + +void main(argc,argv) +int argc; +char **argv; +{ + static ipalloc ip = {0}; + int i; + unsigned long random; + char **recips; + unsigned long prefme; + int flagallaliases; + int flagalias; + char *relayhost; + + sig_pipeignore(); + if (argc < 4) perm_usage(); + if (chdir(auto_qmail) == -1) temp_chdir(); + getcontrols(); + + + if (!stralloc_copys(&host,argv[1])) temp_nomem(); + + relayhost = 0; + for (i = 0;i <= host.len;++i) + if ((i == 0) || (i == host.len) || (host.s[i] == '.')) + if (relayhost = constmap(&maproutes,host.s + i,host.len - i)) + break; + if (relayhost && !*relayhost) relayhost = 0; + + if (relayhost) { + i = str_chr(relayhost,':'); + if (relayhost[i]) { + scan_ulong(relayhost + i + 1,&port); + relayhost[i] = 0; + } + if (!stralloc_copys(&host,relayhost)) temp_nomem(); + } + + + addrmangle(&sender,argv[2],&flagalias,0); + + if (!saa_readyplus(&reciplist,0)) temp_nomem(); + if (ipme_init() != 1) temp_oserr(); + + flagallaliases = 1; + recips = argv + 3; + while (*recips) { + if (!saa_readyplus(&reciplist,1)) temp_nomem(); + reciplist.sa[reciplist.len] = sauninit; + addrmangle(reciplist.sa + reciplist.len,*recips,&flagalias,!relayhost); + if (!flagalias) flagallaliases = 0; + ++reciplist.len; + ++recips; + } + + + random = now() + (getpid() << 16); + switch (relayhost ? dns_ip(&ip,&host) : dns_mxip(&ip,&host,random)) { + case DNS_MEM: temp_nomem(); + case DNS_SOFT: temp_dns(); + case DNS_HARD: perm_dns(); + case 1: + if (ip.len <= 0) temp_dns(); + } + + if (ip.len <= 0) perm_nomx(); + + prefme = 100000; + for (i = 0;i < ip.len;++i) + if (ipme_is(&ip.ix[i].ip)) + if (ip.ix[i].pref < prefme) + prefme = ip.ix[i].pref; + + if (relayhost) prefme = 300000; + if (flagallaliases) prefme = 500000; + + for (i = 0;i < ip.len;++i) + if (ip.ix[i].pref < prefme) + break; + + if (i >= ip.len) + perm_ambigmx(); + + for (i = 0;i < ip.len;++i) if (ip.ix[i].pref < prefme) { + if (tcpto(&ip.ix[i].ip)) continue; + + smtpfd = socket(AF_INET,SOCK_STREAM,0); + if (smtpfd == -1) temp_oserr(); + + if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0) { + tcpto_err(&ip.ix[i].ip,0); + partner = ip.ix[i].ip; + smtp(); /* does not return */ + } + tcpto_err(&ip.ix[i].ip,errno == error_timeout); + close(smtpfd); + } + + temp_noconn(); +} diff --git a/qmail-rspawn.8 b/qmail-rspawn.8 new file mode 100644 index 0000000..33e8d0d --- /dev/null +++ b/qmail-rspawn.8 @@ -0,0 +1,21 @@ +.TH qmail-rspawn 8 +.SH NAME +qmail-rspawn \- schedule remote deliveries +.SH SYNOPSIS +.B qmail-rspawn +.SH DESCRIPTION +.B qmail-rspawn +reads a series of remote delivery commands from descriptor 0, +invokes +.B qmail-remote +to perform the deliveries, +and prints the results to descriptor 1. + +.B qmail-rspawn +invokes +.B qmail-remote +asynchronously, +so the results may not be in the same order as the commands. +.SH "SEE ALSO" +qmail-send(8), +qmail-remote(8) diff --git a/qmail-rspawn.c b/qmail-rspawn.c new file mode 100644 index 0000000..9d838e6 --- /dev/null +++ b/qmail-rspawn.c @@ -0,0 +1,103 @@ +#include "fd.h" +#include "wait.h" +#include "substdio.h" +#include "exit.h" +#include "fork.h" +#include "error.h" +#include "tcpto.h" + +void initialize(argc,argv) +int argc; +char **argv; +{ + tcpto_clean(); +} + +int truncreport = 0; + +void report(ss,wstat,s,len) +substdio *ss; +int wstat; +char *s; +int len; +{ + int j; + int k; + int result; + int orr; + + if (wait_crashed(wstat)) + { substdio_puts(ss,"Zqmail-remote crashed.\n"); return; } + switch(wait_exitcode(wstat)) + { + case 0: break; + case 111: substdio_puts(ss,"ZUnable to run qmail-remote.\n"); return; + default: substdio_puts(ss,"DUnable to run qmail-remote.\n"); return; + } + if (!len) + { substdio_puts(ss,"Zqmail-remote produced no output.\n"); return; } + + result = -1; + j = 0; + for (k = 0;k < len;++k) + if (!s[k]) + { + if (s[j] == 'K') { result = 1; break; } + if (s[j] == 'Z') { result = 0; break; } + if (s[j] == 'D') break; + j = k + 1; + } + + orr = result; + switch(s[0]) + { + case 's': orr = 0; break; + case 'h': orr = -1; + } + + switch(orr) + { + case 1: substdio_put(ss,"K",1); break; + case 0: substdio_put(ss,"Z",1); break; + case -1: substdio_put(ss,"D",1); break; + } + + for (k = 1;k < len;) + if (!s[k++]) + { + substdio_puts(ss,s + 1); + if (result <= orr) + if (k < len) + switch(s[k]) + { + case 'Z': case 'D': case 'K': + substdio_puts(ss,s + k + 1); + } + break; + } +} + +int spawn(fdmess,fdout,s,r,at) +int fdmess; int fdout; +char *s; char *r; int at; +{ + int f; + char *(args[5]); + + args[0] = "qmail-remote"; + args[1] = r + at + 1; + args[2] = s; + args[3] = r; + args[4] = 0; + + if (!(f = vfork())) + { + if (fd_move(0,fdmess) == -1) _exit(111); + if (fd_move(1,fdout) == -1) _exit(111); + if (fd_copy(2,1) == -1) _exit(111); + execvp(*args,args); + if (error_temp(errno)) _exit(111); + _exit(100); + } + return f; +} diff --git a/qmail-send.9 b/qmail-send.9 new file mode 100644 index 0000000..acb04d0 --- /dev/null +++ b/qmail-send.9 @@ -0,0 +1,246 @@ +.TH qmail-send 8 +.SH NAME +qmail-send \- deliver mail messages from the queue +.SH SYNOPSIS +.B qmail-send +.SH DESCRIPTION +.B qmail-send +handles messages placed into the outgoing queue by +.BR qmail-queue . +It uses +.B qmail-lspawn +to deliver messages to local recipients and +.B qmail-rspawn +to deliver messages to remote recipients. +If a message is temporarily undeliverable to one or more addresses, +.B qmail-send +leaves it in the queue and tries the addresses again later. + +.B qmail-send +prints a readable record of its activities to descriptor 0. +It writes commands to +.BR qmail-lspawn , +.BR qmail-rspawn , +and +.B qmail-clean +on descriptors 1, 3, and 5, +and reads responses from descriptors 2, 4, and 6. +.B qmail-send +is responsible for avoiding deadlock. + +If +.B qmail-send +receives a TERM signal, +it will exit cleanly, after waiting +(possibly more than a minute) +for current delivery attempts to finish. + +If +.B qmail-send +receives an ALRM signal, +it will reschedule every message in the queue for immediate delivery. +.SH "CONTROL FILES" +.B WARNING: +.B qmail-send +reads its control files only when it starts. +If you change the control files, +you must stop and restart +.BR qmail-send . +Exception: +If +.B qmail-send +receives a HUP signal, +it will reread +.I locals +and +.IR virtualdomains . +.TP 5 +.I bouncefrom +Bounce username. +Default: +.BR MAILER-DAEMON . +.TP 5 +.I bouncehost +Bounce host. +Default: +.IR me , +if that is supplied; +otherwise the literal name +.BR bouncehost , +which is probably not what you want. +If a message is permanently undeliverable, +.B qmail-send +sends a +.B single-bounce +notice back to the message's envelope sender. +The notice is +.B From: \fIbouncefrom\fB@\fIbouncehost\fR, +although its envelope sender is empty. +.TP 5 +.I concurrencylocal +Maximum number of simultaneous local delivery attempts. +Default: 10. +If 0, local deliveries will be put on hold. +.I concurrencylocal +is limited at compile time to +SPAWN. +.TP 5 +.I concurrencyremote +Maximum number of simultaneous remote delivery attempts. +Default: 20. +If 0, remote deliveries will be put on hold. +.I concurrencyremote +is limited at compile time to +SPAWN. +.TP 5 +.I doublebouncehost +Double-bounce host. +Default: +.IR me , +if that is supplied; +otherwise the literal name +.BR doublebouncehost , +which is probably not what you want. +.TP 5 +.I doublebounceto +User to receive double-bounces. +Default: +.BR postmaster . +If a single-bounce notice is permanently undeliverable, +.B qmail-send +sends a +.B double-bounce +notice to +.IR doublebounceto\fB@\fIdoublebouncehost . +(If that bounces, +.B qmail-send +gives up.) +.TP 5 +.I envnoathost +Presumed domain name for addresses without @ signs. +Default: +.IR me , +if that is supplied; +otherwise the literal name +.BR envnoathost , +which is probably not what you want. +If +.B qmail-send +sees an envelope recipient address without an @ sign, +it appends +.B @\fIenvnoathost\fR. +.TP 5 +.I locals +List of domain names that the current host +receives mail for, +one per line. +Default: +.IR me , +if that is supplied; +otherwise +.B qmail-send +refuses to run. +An address +.I user@domain +is considered local if +.I domain +is listed in +.IR locals . +.TP 5 +.I percenthack +List of domain names where the percent hack is applied. +If +.I domain +is listed in +.IR percenthack , +any address of the form +.I user%fqdn@domain +is rewritten as +.IR user@fqdn . +.I user +may contain %, +so the percent hack may be applied repeatedly. +.B qmail-send +handles +.I percenthack +before +.IR locals . +.TP 5 +.I queuelifetime +Number of seconds +a message can stay in the queue. +Default: 604800 (one week). +After this time expires, +.B qmail-send +will try the message once more, +but it will treat any temporary delivery failures as +permanent failures. +.TP 5 +.I virtualdomains +List of virtual users or domains, one per line. +A virtual user has the form +.IR user\fB@\fIdomain\fB:\fIprepend , +without any extra spaces. +When +.B qmail-send +sees the recipient address +.IR user\fB@\fIdomain , +it converts it to +.I prepend\fB-\fIuser\fB@\fIdomain +and treats it as local. + +A virtual domain has the form +.IR domain\fB:\fIprepend . +It applies to any recipient address at +.IR domain . +For example, if + +.EX + nowhere.mil:joeBREAKfoo +.EE + +is in +.IR virtualdomains , +and a message arrives for +.BR info@nowhere.mil , +.B qmail-send +will rewrite the recipient address as +.B joeBREAKfoo-info@nowhere.mil +and deliver the message locally. + +.I virtualdomains +may contain wildcards: + +.EX + .fax:uucpBREAKfax + :aliasBREAKcatchall + .nowhere.mil:joeBREAKfoo-host +.EE + +.I virtualdomains +may also contain exceptions: +an empty +.I prepend +means that +.I domain +is not a virtual domain. + +.B qmail-send +handles +.I virtualdomains +after +.IR locals : +if a domain is listed in +.IR locals , +.I virtualdomains +does not apply. +.SH "SEE ALSO" +nice(1), +addresses(5), +envelopes(5), +qmail-control(5), +qmail-log(5), +qmail-queue(8), +qmail-clean(8), +qmail-lspawn(8), +qmail-rspawn(8) diff --git a/qmail-send.c b/qmail-send.c new file mode 100644 index 0000000..c31b522 --- /dev/null +++ b/qmail-send.c @@ -0,0 +1,1612 @@ +#include +#include +#include "readwrite.h" +#include "sig.h" +#include "direntry.h" +#include "control.h" +#include "select.h" +#include "open.h" +#include "seek.h" +#include "exit.h" +#include "lock.h" +#include "ndelay.h" +#include "now.h" +#include "getln.h" +#include "substdio.h" +#include "alloc.h" +#include "error.h" +#include "stralloc.h" +#include "str.h" +#include "byte.h" +#include "fmt.h" +#include "scan.h" +#include "case.h" +#include "auto_qmail.h" +#include "trigger.h" +#include "newfield.h" +#include "quote.h" +#include "qmail.h" +#include "qsutil.h" +#include "prioq.h" +#include "constmap.h" +#include "fmtqfn.h" +#include "readsubdir.h" + +/* critical timing feature #1: if not triggered, do not busy-loop */ +/* critical timing feature #2: if triggered, respond within fixed time */ +/* important timing feature: when triggered, respond instantly */ +#define SLEEP_TODO 1500 /* check todo/ every 25 minutes in any case */ +#define SLEEP_FUZZ 1 /* slop a bit on sleeps to avoid zeno effect */ +#define SLEEP_FOREVER 86400 /* absolute maximum time spent in select() */ +#define SLEEP_CLEANUP 76431 /* time between cleanups */ +#define SLEEP_SYSFAIL 123 +#define OSSIFIED 129600 /* 36 hours; _must_ exceed q-q's DEATH (24 hours) */ + +int lifetime = 604800; + +stralloc percenthack = {0}; +struct constmap mappercenthack; +stralloc locals = {0}; +struct constmap maplocals; +stralloc vdoms = {0}; +struct constmap mapvdoms; +stralloc envnoathost = {0}; +stralloc bouncefrom = {0}; +stralloc bouncehost = {0}; +stralloc doublebounceto = {0}; +stralloc doublebouncehost = {0}; + +char strnum2[FMT_ULONG]; +char strnum3[FMT_ULONG]; + +#define CHANNELS 2 +char *chanaddr[CHANNELS] = { "local/", "remote/" }; +char *chanstatusmsg[CHANNELS] = { " local ", " remote " }; +char *tochan[CHANNELS] = { " to local ", " to remote " }; +int chanfdout[CHANNELS] = { 1, 3 }; +int chanfdin[CHANNELS] = { 2, 4 }; +int chanskip[CHANNELS] = { 10, 20 }; + +int flagexitasap = 0; void sigterm() { flagexitasap = 1; } +int flagrunasap = 0; void sigalrm() { flagrunasap = 1; } +int flagreadasap = 0; void sighup() { flagreadasap = 1; } + +void cleandied() { log1("alert: oh no! lost qmail-clean connection! dying...\n"); + flagexitasap = 1; } + +int flagspawnalive[CHANNELS]; +void spawndied(c) int c; { log1("alert: oh no! lost spawn connection! dying...\n"); + flagspawnalive[c] = 0; flagexitasap = 1; } + +#define REPORTMAX 10000 + +datetime_sec recent; + + +/* this file is too long ----------------------------------------- FILENAMES */ + +stralloc fn = {0}; +stralloc fn2 = {0}; +char fnmake_strnum[FMT_ULONG]; + +void fnmake_init() +{ + while (!stralloc_ready(&fn,FMTQFN)) nomem(); + while (!stralloc_ready(&fn2,FMTQFN)) nomem(); +} + +void fnmake_info(id) unsigned long id; { fn.len = fmtqfn(fn.s,"info/",id,1); } +void fnmake_todo(id) unsigned long id; { fn.len = fmtqfn(fn.s,"todo/",id,0); } +void fnmake_mess(id) unsigned long id; { fn.len = fmtqfn(fn.s,"mess/",id,1); } +void fnmake_foop(id) unsigned long id; { fn.len = fmtqfn(fn.s,"foop/",id,0); } +void fnmake_split(id) unsigned long id; { fn.len = fmtqfn(fn.s,"",id,1); } +void fnmake2_bounce(id) unsigned long id; +{ fn2.len = fmtqfn(fn2.s,"bounce/",id,0); } +void fnmake_chanaddr(id,c) unsigned long id; int c; +{ fn.len = fmtqfn(fn.s,chanaddr[c],id,1); } + + +/* this file is too long ----------------------------------------- REWRITING */ + +stralloc rwline = {0}; + +/* 1 if by land, 2 if by sea, 0 if out of memory. not allowed to barf. */ +/* may trash recip. must set up rwline, between a T and a \0. */ +int rewrite(recip) +char *recip; +{ + int i; + int j; + char *x; + static stralloc addr = {0}; + int at; + + if (!stralloc_copys(&rwline,"T")) return 0; + if (!stralloc_copys(&addr,recip)) return 0; + + i = byte_rchr(addr.s,addr.len,'@'); + if (i == addr.len) { + if (!stralloc_cats(&addr,"@")) return 0; + if (!stralloc_cat(&addr,&envnoathost)) return 0; + } + + while (constmap(&mappercenthack,addr.s + i + 1,addr.len - i - 1)) { + j = byte_rchr(addr.s,i,'%'); + if (j == i) break; + addr.len = i; + i = j; + addr.s[i] = '@'; + } + + at = byte_rchr(addr.s,addr.len,'@'); + + if (constmap(&maplocals,addr.s + at + 1,addr.len - at - 1)) { + if (!stralloc_cat(&rwline,&addr)) return 0; + if (!stralloc_0(&rwline)) return 0; + return 1; + } + + for (i = 0;i <= addr.len;++i) + if (!i || (i == at + 1) || (i == addr.len) || ((i > at) && (addr.s[i] == '.'))) + if (x = constmap(&mapvdoms,addr.s + i,addr.len - i)) { + if (!*x) break; + if (!stralloc_cats(&rwline,x)) return 0; + if (!stralloc_cats(&rwline,"-")) return 0; + if (!stralloc_cat(&rwline,&addr)) return 0; + if (!stralloc_0(&rwline)) return 0; + return 1; + } + + if (!stralloc_cat(&rwline,&addr)) return 0; + if (!stralloc_0(&rwline)) return 0; + return 2; +} + +void senderadd(sa,sender,recip) +stralloc *sa; +char *sender; +char *recip; +{ + int i; + int j; + int k; + + i = str_len(sender); + if (i >= 4) + if (str_equal(sender + i - 4,"-@[]")) + { + j = byte_rchr(sender,i - 4,'@'); + k = str_rchr(recip,'@'); + if (recip[k] && (j + 5 <= i)) + { + /* owner-@host-@[] -> owner-recipbox=reciphost@host */ + while (!stralloc_catb(sa,sender,j)) nomem(); + while (!stralloc_catb(sa,recip,k)) nomem(); + while (!stralloc_cats(sa,"=")) nomem(); + while (!stralloc_cats(sa,recip + k + 1)) nomem(); + while (!stralloc_cats(sa,"@")) nomem(); + while (!stralloc_catb(sa,sender + j + 1,i - 5 - j)) nomem(); + return; + } + } + while (!stralloc_cats(sa,sender)) nomem(); +} + + +/* this file is too long ---------------------------------------------- INFO */ + +int getinfo(sa,dt,id) +stralloc *sa; +datetime_sec *dt; +unsigned long id; +{ + int fdinfo; + struct stat st; + static stralloc line = {0}; + int match; + substdio ss; + char buf[128]; + + fnmake_info(id); + fdinfo = open_read(fn.s); + if (fdinfo == -1) return 0; + if (fstat(fdinfo,&st) == -1) { close(fdinfo); return 0; } + substdio_fdbuf(&ss,read,fdinfo,buf,sizeof(buf)); + if (getln(&ss,&line,&match,'\0') == -1) { close(fdinfo); return 0; } + close(fdinfo); + if (!match) return 0; + if (line.s[0] != 'F') return 0; + + *dt = st.st_mtime; + while (!stralloc_copys(sa,line.s + 1)) nomem(); + while (!stralloc_0(sa)) nomem(); + return 1; +} + + +/* this file is too long ------------------------------------- COMMUNICATION */ + +substdio sstoqc; char sstoqcbuf[1024]; +substdio ssfromqc; char ssfromqcbuf[1024]; +stralloc comm_buf[CHANNELS] = { {0}, {0} }; +int comm_pos[CHANNELS]; + +void comm_init() +{ + int c; + substdio_fdbuf(&sstoqc,write,5,sstoqcbuf,sizeof(sstoqcbuf)); + substdio_fdbuf(&ssfromqc,read,6,ssfromqcbuf,sizeof(ssfromqcbuf)); + for (c = 0;c < CHANNELS;++c) + if (ndelay_on(chanfdout[c]) == -1) + /* this is so stupid: NDELAY semantics should be default on write */ + spawndied(c); /* drastic, but better than risking deadlock */ +} + +int comm_canwrite(c) +int c; +{ + /* XXX: could allow a bigger buffer; say 10 recipients */ + if (comm_buf[c].s && comm_buf[c].len) return 0; + return 1; +} + +void comm_write(c,delnum,id,sender,recip) +int c; +int delnum; +unsigned long id; +char *sender; +char *recip; +{ + char ch; + if (comm_buf[c].s && comm_buf[c].len) return; + while (!stralloc_copys(&comm_buf[c],"")) nomem(); + ch = delnum; + while (!stralloc_append(&comm_buf[c],&ch)) nomem(); + fnmake_split(id); + while (!stralloc_cats(&comm_buf[c],fn.s)) nomem(); + while (!stralloc_0(&comm_buf[c])) nomem(); + senderadd(&comm_buf[c],sender,recip); + while (!stralloc_0(&comm_buf[c])) nomem(); + while (!stralloc_cats(&comm_buf[c],recip)) nomem(); + while (!stralloc_0(&comm_buf[c])) nomem(); + comm_pos[c] = 0; +} + +void comm_selprep(nfds,wfds) +int *nfds; +fd_set *wfds; +{ + int c; + for (c = 0;c < CHANNELS;++c) + if (flagspawnalive[c]) + if (comm_buf[c].s && comm_buf[c].len) + { + FD_SET(chanfdout[c],wfds); + if (*nfds <= chanfdout[c]) + *nfds = chanfdout[c] + 1; + } +} + +void comm_do(wfds) +fd_set *wfds; +{ + int c; + for (c = 0;c < CHANNELS;++c) + if (flagspawnalive[c]) + if (comm_buf[c].s && comm_buf[c].len) + if (FD_ISSET(chanfdout[c],wfds)) + { + int w; + int len; + len = comm_buf[c].len; + w = write(chanfdout[c],comm_buf[c].s + comm_pos[c],len - comm_pos[c]); + if (w <= 0) + { + if ((w == -1) && (errno == error_pipe)) + spawndied(c); + else + continue; /* kernel select() bug; can't avoid busy-looping */ + } + else + { + comm_pos[c] += w; + if (comm_pos[c] == len) + comm_buf[c].len = 0; + } + } +} + + +/* this file is too long ------------------------------------------ CLEANUPS */ + +int flagcleanup; /* if 1, cleanupdir is initialized and ready */ +readsubdir cleanupdir; +datetime_sec cleanuptime; + +void cleanup_init() +{ + flagcleanup = 0; + cleanuptime = now(); +} + +void cleanup_selprep(wakeup) +datetime_sec *wakeup; +{ + if (flagcleanup) *wakeup = 0; + if (*wakeup > cleanuptime) *wakeup = cleanuptime; +} + +void cleanup_do() +{ + char ch; + struct stat st; + unsigned long id; + + if (!flagcleanup) + { + if (recent < cleanuptime) return; + readsubdir_init(&cleanupdir,"mess",pausedir); + flagcleanup = 1; + } + + switch(readsubdir_next(&cleanupdir,&id)) + { + case 1: + break; + case 0: + flagcleanup = 0; + cleanuptime = recent + SLEEP_CLEANUP; + default: + return; + } + + fnmake_mess(id); + if (stat(fn.s,&st) == -1) return; /* probably qmail-queue deleted it */ + if (recent <= st.st_atime + OSSIFIED) return; + + fnmake_info(id); + if (stat(fn.s,&st) == 0) return; + if (errno != error_noent) return; + fnmake_todo(id); + if (stat(fn.s,&st) == 0) return; + if (errno != error_noent) return; + + fnmake_foop(id); + if (substdio_putflush(&sstoqc,fn.s,fn.len) == -1) { cleandied(); return; } + if (substdio_get(&ssfromqc,&ch,1) != 1) { cleandied(); return; } + if (ch != '+') + log3("warning: qmail-clean unable to clean up ",fn.s,"\n"); +} + + +/* this file is too long ----------------------------------- PRIORITY QUEUES */ + +prioq pqdone = {0}; /* -todo +info; HOPEFULLY -local -remote */ +prioq pqchan[CHANNELS] = { {0}, {0} }; +/* pqchan 0: -todo +info +local ?remote */ +/* pqchan 1: -todo +info ?local +remote */ +prioq pqfail = {0}; /* stat() failure; has to be pqadded again */ + +void pqadd(id) +unsigned long id; +{ + struct prioq_elt pe; + struct prioq_elt pechan[CHANNELS]; + int flagchan[CHANNELS]; + struct stat st; + int c; + +#define CHECKSTAT if (errno != error_noent) goto fail; + + fnmake_info(id); + if (stat(fn.s,&st) == -1) + { + CHECKSTAT + return; /* someone yanking our chain */ + } + + fnmake_todo(id); + if (stat(fn.s,&st) != -1) return; /* look, ma, dad crashed writing info! */ + CHECKSTAT + + for (c = 0;c < CHANNELS;++c) + { + fnmake_chanaddr(id,c); + if (stat(fn.s,&st) == -1) { flagchan[c] = 0; CHECKSTAT } + else { flagchan[c] = 1; pechan[c].id = id; pechan[c].dt = st.st_mtime; } + } + + for (c = 0;c < CHANNELS;++c) + if (flagchan[c]) + while (!prioq_insert(&pqchan[c],&pechan[c])) nomem(); + + for (c = 0;c < CHANNELS;++c) if (flagchan[c]) break; + if (c == CHANNELS) + { + pe.id = id; pe.dt = now(); + while (!prioq_insert(&pqdone,&pe)) nomem(); + } + + return; + + fail: + log3("warning: unable to stat ",fn.s,"; will try again later\n"); + pe.id = id; pe.dt = now() + SLEEP_SYSFAIL; + while (!prioq_insert(&pqfail,&pe)) nomem(); +} + +void pqstart() +{ + readsubdir rs; + int x; + unsigned long id; + + readsubdir_init(&rs,"info",pausedir); + + while (x = readsubdir_next(&rs,&id)) + if (x > 0) + pqadd(id); +} + +void pqfinish() +{ + int c; + struct prioq_elt pe; + time_t ut[2]; /* XXX: more portable than utimbuf, but still worrisome */ + + for (c = 0;c < CHANNELS;++c) + while (prioq_min(&pqchan[c],&pe)) + { + prioq_delmin(&pqchan[c]); + fnmake_chanaddr(pe.id,c); + ut[0] = ut[1] = pe.dt; + if (utime(fn.s,ut) == -1) + log3("warning: unable to utime ",fn.s,"; message will be retried too soon\n"); + } +} + +void pqrun() +{ + int c; + int i; + for (c = 0;c < CHANNELS;++c) + if (pqchan[c].p) + if (pqchan[c].len) + for (i = 0;i < pqchan[c].len;++i) + pqchan[c].p[i].dt = recent; +} + + +/* this file is too long ---------------------------------------------- JOBS */ + +struct job + { + int refs; /* if 0, this struct is unused */ + unsigned long id; + int channel; + datetime_sec retry; + stralloc sender; + int numtodo; + int flaghiteof; + int flagdying; + } +; + +int numjobs; +struct job *jo; + +void job_init() +{ + int j; + while (!(jo = (struct job *) alloc(numjobs * sizeof(struct job)))) nomem(); + for (j = 0;j < numjobs;++j) + { + jo[j].refs = 0; + jo[j].sender.s = 0; + } +} + +int job_avail() +{ + int j; + for (j = 0;j < numjobs;++j) if (!jo[j].refs) return 1; + return 0; +} + +int job_open(id,channel) +unsigned long id; +int channel; +{ + int j; + for (j = 0;j < numjobs;++j) if (!jo[j].refs) break; + if (j == numjobs) return -1; + jo[j].refs = 1; + jo[j].id = id; + jo[j].channel = channel; + jo[j].numtodo = 0; + jo[j].flaghiteof = 0; + return j; +} + +void job_close(j) +int j; +{ + struct prioq_elt pe; + struct stat st; + + if (0 < --jo[j].refs) return; + + pe.id = jo[j].id; + pe.dt = jo[j].retry; + if (jo[j].flaghiteof && !jo[j].numtodo) + { + fnmake_chanaddr(jo[j].id,jo[j].channel); + if (unlink(fn.s) == -1) + { + log3("warning: unable to unlink ",fn.s,"; will try again later\n"); + pe.dt = now() + SLEEP_SYSFAIL; + } + else + { + int c; + for (c = 0;c < CHANNELS;++c) if (c != jo[j].channel) + { + fnmake_chanaddr(jo[j].id,c); + if (stat(fn.s,&st) == 0) return; /* more channels going */ + if (errno != error_noent) + { + log3("warning: unable to stat ",fn.s,"\n"); + break; /* this is the only reason for HOPEFULLY */ + } + } + pe.dt = now(); + while (!prioq_insert(&pqdone,&pe)) nomem(); + return; + } + } + + while (!prioq_insert(&pqchan[jo[j].channel],&pe)) nomem(); +} + + +/* this file is too long ------------------------------------------- BOUNCES */ + +char *stripvdomprepend(recip) +char *recip; +{ + int i; + char *domain; + int domainlen; + char *prepend; + + i = str_rchr(recip,'@'); + if (!recip[i]) return recip; + domain = recip + i + 1; + domainlen = str_len(domain); + + for (i = 0;i <= domainlen;++i) + if ((i == 0) || (i == domainlen) || (domain[i] == '.')) + if (prepend = constmap(&mapvdoms,domain + i,domainlen - i)) + { + if (!*prepend) break; + i = str_len(prepend); + if (str_diffn(recip,prepend,i)) break; + if (recip[i] != '-') break; + return recip + i + 1; + } + return recip; +} + +stralloc bouncetext = {0}; + +void addbounce(id,recip,report) +unsigned long id; +char *recip; +char *report; +{ + int fd; + int pos; + int w; + while (!stralloc_copys(&bouncetext,"<")) nomem(); + while (!stralloc_cats(&bouncetext,stripvdomprepend(recip))) nomem(); + for (pos = 0;pos < bouncetext.len;++pos) + if (bouncetext.s[pos] == '\n') + bouncetext.s[pos] = '_'; + while (!stralloc_cats(&bouncetext,">:\n")) nomem(); + while (!stralloc_cats(&bouncetext,report)) nomem(); + if (report[0]) + if (report[str_len(report) - 1] != '\n') + while (!stralloc_cats(&bouncetext,"\n")) nomem(); + for (pos = bouncetext.len - 2;pos > 0;--pos) + if (bouncetext.s[pos] == '\n') + if (bouncetext.s[pos - 1] == '\n') + bouncetext.s[pos] = '/'; + while (!stralloc_cats(&bouncetext,"\n")) nomem(); + fnmake2_bounce(id); + for (;;) + { + fd = open_append(fn2.s); + if (fd != -1) break; + log1("alert: unable to append to bounce message; HELP! sleeping...\n"); + sleep(10); + } + pos = 0; + while (pos < bouncetext.len) + { + w = write(fd,bouncetext.s + pos,bouncetext.len - pos); + if (w <= 0) + { + log1("alert: unable to append to bounce message; HELP! sleeping...\n"); + sleep(10); + } + else + pos += w; + } + close(fd); +} + +int injectbounce(id) +unsigned long id; +{ + struct qmail qqt; + struct stat st; + char *bouncesender; + char *bouncerecip; + int r; + int fd; + substdio ssread; + char buf[128]; + char inbuf[128]; + static stralloc sender = {0}; + static stralloc quoted = {0}; + datetime_sec birth; + unsigned long qp; + + if (!getinfo(&sender,&birth,id)) return 0; /* XXX: print warning */ + + /* owner-@host-@[] -> owner-@host */ + if (sender.len >= 5) + if (str_equal(sender.s + sender.len - 5,"-@[]")) + { + sender.len -= 4; + sender.s[sender.len - 1] = 0; + } + + fnmake2_bounce(id); + fnmake_mess(id); + if (stat(fn2.s,&st) == -1) + { + if (errno == error_noent) + return 1; + log3("warning: unable to stat ",fn2.s,"\n"); + return 0; + } + if (str_equal(sender.s,"#@[]")) + log3("triple bounce: discarding ",fn2.s,"\n"); + else + { + if (qmail_open(&qqt) == -1) + { log1("warning: unable to start qmail-queue, will try later\n"); return 0; } + qp = qmail_qp(&qqt); + + if (*sender.s) { bouncesender = ""; bouncerecip = sender.s; } + else { bouncesender = "#@[]"; bouncerecip = doublebounceto.s; } + + while (!newfield_datemake(now())) nomem(); + qmail_put(&qqt,newfield_date.s,newfield_date.len); + qmail_puts(&qqt,"From: "); + while (!quote("ed,&bouncefrom)) nomem(); + qmail_put(&qqt,quoted.s,quoted.len); + qmail_puts(&qqt,"@"); + qmail_put(&qqt,bouncehost.s,bouncehost.len); + qmail_puts(&qqt,"\nTo: "); + while (!quote2("ed,bouncerecip)) nomem(); + qmail_put(&qqt,quoted.s,quoted.len); + qmail_puts(&qqt,"\n\ +Subject: failure notice\n\ +\n\ +Hi. This is the qmail-send program at "); + qmail_put(&qqt,bouncehost.s,bouncehost.len); + qmail_puts(&qqt,*sender.s ? ".\n\ +I'm afraid I wasn't able to deliver your message to the following addresses.\n\ +This is a permanent error; I've given up. Sorry it didn't work out.\n\ +\n\ +" : ".\n\ +I tried to deliver a bounce message to this address, but the bounce bounced!\n\ +\n\ +"); + + fd = open_read(fn2.s); + if (fd == -1) + qmail_fail(&qqt); + else + { + substdio_fdbuf(&ssread,read,fd,inbuf,sizeof(inbuf)); + while ((r = substdio_get(&ssread,buf,sizeof(buf))) > 0) + qmail_put(&qqt,buf,r); + close(fd); + if (r == -1) + qmail_fail(&qqt); + } + + qmail_puts(&qqt,*sender.s ? "--- Below this line is a copy of the message.\n\n" : "--- Below this line is the original bounce.\n\n"); + qmail_puts(&qqt,"Return-Path: <"); + while (!quote2("ed,sender.s)) nomem(); + qmail_put(&qqt,quoted.s,quoted.len); + qmail_puts(&qqt,">\n"); + + fd = open_read(fn.s); + if (fd == -1) + qmail_fail(&qqt); + else + { + substdio_fdbuf(&ssread,read,fd,inbuf,sizeof(inbuf)); + while ((r = substdio_get(&ssread,buf,sizeof(buf))) > 0) + qmail_put(&qqt,buf,r); + close(fd); + if (r == -1) + qmail_fail(&qqt); + } + + qmail_from(&qqt,bouncesender); + qmail_to(&qqt,bouncerecip); + if (*qmail_close(&qqt)) + { log1("warning: trouble injecting bounce message, will try later\n"); return 0; } + + strnum2[fmt_ulong(strnum2,id)] = 0; + log2("bounce msg ",strnum2); + strnum2[fmt_ulong(strnum2,qp)] = 0; + log3(" qp ",strnum2,"\n"); + } + if (unlink(fn2.s) == -1) + { + log3("warning: unable to unlink ",fn2.s,"\n"); + return 0; + } + return 1; +} + + +/* this file is too long ---------------------------------------- DELIVERIES */ + +struct del + { + int used; + int j; + unsigned long delid; + seek_pos mpos; + stralloc recip; + } +; + +unsigned long masterdelid = 1; +unsigned int concurrency[CHANNELS] = { 10, 20 }; +unsigned int concurrencyused[CHANNELS] = { 0, 0 }; +struct del *d[CHANNELS]; +stralloc dline[CHANNELS]; +char delbuf[2048]; + +void del_status() +{ + int c; + + log1("status:"); + for (c = 0;c < CHANNELS;++c) { + strnum2[fmt_ulong(strnum2,(unsigned long) concurrencyused[c])] = 0; + strnum3[fmt_ulong(strnum3,(unsigned long) concurrency[c])] = 0; + log2(chanstatusmsg[c],strnum2); + log2("/",strnum3); + } + if (flagexitasap) log1(" exitasap"); + log1("\n"); +} + +void del_init() +{ + int c; + int i; + for (c = 0;c < CHANNELS;++c) + { + flagspawnalive[c] = 1; + while (!(d[c] = (struct del *) alloc(concurrency[c] * sizeof(struct del)))) + nomem(); + for (i = 0;i < concurrency[c];++i) + { d[c][i].used = 0; d[c][i].recip.s = 0; } + dline[c].s = 0; + while (!stralloc_copys(&dline[c],"")) nomem(); + } + del_status(); +} + +int del_canexit() +{ + int c; + for (c = 0;c < CHANNELS;++c) + if (flagspawnalive[c]) /* if dead, nothing we can do about its jobs */ + if (concurrencyused[c]) return 0; + return 1; +} + +int del_avail(c) +int c; +{ + return flagspawnalive[c] && comm_canwrite(c) && (concurrencyused[c] < concurrency[c]); +} + +void del_start(j,mpos,recip) +int j; +seek_pos mpos; +char *recip; +{ + int i; + int c; + + c = jo[j].channel; + if (!flagspawnalive[c]) return; + if (!comm_canwrite(c)) return; + + for (i = 0;i < concurrency[c];++i) if (!d[c][i].used) break; + if (i == concurrency[c]) return; + + if (!stralloc_copys(&d[c][i].recip,recip)) { nomem(); return; } + if (!stralloc_0(&d[c][i].recip)) { nomem(); return; } + d[c][i].j = j; ++jo[j].refs; + d[c][i].delid = masterdelid++; + d[c][i].mpos = mpos; + d[c][i].used = 1; ++concurrencyused[c]; + + comm_write(c,i,jo[j].id,jo[j].sender.s,recip); + + strnum2[fmt_ulong(strnum2,d[c][i].delid)] = 0; + strnum3[fmt_ulong(strnum3,jo[j].id)] = 0; + log2("starting delivery ",strnum2); + log3(": msg ",strnum3,tochan[c]); + logsafe(recip); + log1("\n"); + del_status(); +} + +void markdone(c,id,pos) +int c; +unsigned long id; +seek_pos pos; +{ + struct stat st; + int fd; + fnmake_chanaddr(id,c); + for (;;) + { + fd = open_write(fn.s); + if (fd == -1) break; + if (fstat(fd,&st) == -1) { close(fd); break; } + if (seek_set(fd,pos) == -1) { close(fd); break; } + if (write(fd,"D",1) != 1) { close(fd); break; } + /* further errors -> double delivery without us knowing about it, oh well */ + close(fd); + return; + } + log3("warning: trouble marking ",fn.s,"; message will be delivered twice!\n"); +} + +void del_dochan(c) +int c; +{ + int r; + char ch; + int i; + int delnum; + r = read(chanfdin[c],delbuf,sizeof(delbuf)); + if (r == -1) return; + if (r == 0) { spawndied(c); return; } + for (i = 0;i < r;++i) + { + ch = delbuf[i]; + while (!stralloc_append(&dline[c],&ch)) nomem(); + if (dline[c].len > REPORTMAX) + dline[c].len = REPORTMAX; + /* qmail-lspawn and qmail-rspawn are responsible for keeping it short */ + /* but from a security point of view, we don't trust rspawn */ + if (!ch && (dline[c].len > 1)) + { + delnum = (unsigned int) (unsigned char) dline[c].s[0]; + if ((delnum < 0) || (delnum >= concurrency[c]) || !d[c][delnum].used) + log1("warning: internal error: delivery report out of range\n"); + else + { + strnum3[fmt_ulong(strnum3,d[c][delnum].delid)] = 0; + if (dline[c].s[1] == 'Z') + if (jo[d[c][delnum].j].flagdying) + { + dline[c].s[1] = 'D'; + --dline[c].len; + while (!stralloc_cats(&dline[c],"I'm not going to try again; this message has been in the queue too long.\n")) nomem(); + while (!stralloc_0(&dline[c])) nomem(); + } + switch(dline[c].s[1]) + { + case 'K': + log3("delivery ",strnum3,": success: "); + logsafe(dline[c].s + 2); + log1("\n"); + markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos); + --jo[d[c][delnum].j].numtodo; + break; + case 'Z': + log3("delivery ",strnum3,": deferral: "); + logsafe(dline[c].s + 2); + log1("\n"); + break; + case 'D': + log3("delivery ",strnum3,": failure: "); + logsafe(dline[c].s + 2); + log1("\n"); + addbounce(jo[d[c][delnum].j].id,d[c][delnum].recip.s,dline[c].s + 2); + markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos); + --jo[d[c][delnum].j].numtodo; + break; + default: + log3("delivery ",strnum3,": report mangled, will defer\n"); + } + job_close(d[c][delnum].j); + d[c][delnum].used = 0; --concurrencyused[c]; + del_status(); + } + dline[c].len = 0; + } + } +} + +void del_selprep(nfds,rfds) +int *nfds; +fd_set *rfds; +{ + int c; + for (c = 0;c < CHANNELS;++c) + if (flagspawnalive[c]) + { + FD_SET(chanfdin[c],rfds); + if (*nfds <= chanfdin[c]) + *nfds = chanfdin[c] + 1; + } +} + +void del_do(rfds) +fd_set *rfds; +{ + int c; + for (c = 0;c < CHANNELS;++c) + if (flagspawnalive[c]) + if (FD_ISSET(chanfdin[c],rfds)) + del_dochan(c); +} + + +/* this file is too long -------------------------------------------- PASSES */ + +struct + { + unsigned long id; /* if 0, need a new pass */ + int j; /* defined if id; job number */ + int fd; /* defined if id; reading from {local,remote} */ + seek_pos mpos; /* defined if id; mark position */ + substdio ss; + char buf[128]; + } +pass[CHANNELS]; + +void pass_init() +{ + int c; + for (c = 0;c < CHANNELS;++c) pass[c].id = 0; +} + +void pass_selprep(wakeup) +datetime_sec *wakeup; +{ + int c; + struct prioq_elt pe; + if (flagexitasap) return; + for (c = 0;c < CHANNELS;++c) + if (pass[c].id) + if (del_avail(c)) + { *wakeup = 0; return; } + if (job_avail()) + for (c = 0;c < CHANNELS;++c) + if (!pass[c].id) + if (prioq_min(&pqchan[c],&pe)) + if (*wakeup > pe.dt) + *wakeup = pe.dt; + if (prioq_min(&pqfail,&pe)) + if (*wakeup > pe.dt) + *wakeup = pe.dt; + if (prioq_min(&pqdone,&pe)) + if (*wakeup > pe.dt) + *wakeup = pe.dt; +} + +static datetime_sec squareroot(x) /* result^2 <= x < (result + 1)^2 */ +datetime_sec x; /* assuming: >= 0 */ +{ + datetime_sec y; + datetime_sec yy; + datetime_sec y21; + int j; + + y = 0; yy = 0; + for (j = 15;j >= 0;--j) + { + y21 = (y << (j + 1)) + (1 << (j + j)); + if (y21 <= x - yy) { y += (1 << j); yy += y21; } + } + return y; +} + +datetime_sec nextretry(birth,c) +datetime_sec birth; +int c; +{ + int n; + + if (birth > recent) n = 0; + else n = squareroot(recent - birth); /* no need to add fuzz to recent */ + n += chanskip[c]; + return birth + n * n; +} + +void pass_dochan(c) +int c; +{ + datetime_sec birth; + struct prioq_elt pe; + static stralloc line = {0}; + int match; + + if (flagexitasap) return; + + if (!pass[c].id) + { + if (!job_avail()) return; + if (!prioq_min(&pqchan[c],&pe)) return; + if (pe.dt > recent) return; + fnmake_chanaddr(pe.id,c); + + prioq_delmin(&pqchan[c]); + pass[c].mpos = 0; + pass[c].fd = open_read(fn.s); + if (pass[c].fd == -1) goto trouble; + if (!getinfo(&line,&birth,pe.id)) { close(pass[c].fd); goto trouble; } + pass[c].id = pe.id; + substdio_fdbuf(&pass[c].ss,read,pass[c].fd,pass[c].buf,sizeof(pass[c].buf)); + pass[c].j = job_open(pe.id,c); + jo[pass[c].j].retry = nextretry(birth,c); + jo[pass[c].j].flagdying = (recent > birth + lifetime); + while (!stralloc_copy(&jo[pass[c].j].sender,&line)) nomem(); + } + + if (!del_avail(c)) return; + + if (getln(&pass[c].ss,&line,&match,'\0') == -1) + { + fnmake_chanaddr(pass[c].id,c); + log3("warning: trouble reading ",fn.s,"; will try again later\n"); + close(pass[c].fd); + job_close(pass[c].j); + pass[c].id = 0; + return; + } + if (!match) + { + close(pass[c].fd); + jo[pass[c].j].flaghiteof = 1; + job_close(pass[c].j); + pass[c].id = 0; + return; + } + switch(line.s[0]) + { + case 'T': + ++jo[pass[c].j].numtodo; + del_start(pass[c].j,pass[c].mpos,line.s + 1); + break; + case 'D': + break; + default: + fnmake_chanaddr(pass[c].id,c); + log3("warning: unknown record type in ",fn.s,"!\n"); + close(pass[c].fd); + job_close(pass[c].j); + pass[c].id = 0; + return; + } + + pass[c].mpos += line.len; + return; + + trouble: + log3("warning: trouble opening ",fn.s,"; will try again later\n"); + pe.dt = recent + SLEEP_SYSFAIL; + while (!prioq_insert(&pqchan[c],&pe)) nomem(); +} + +void messdone(id) +unsigned long id; +{ + char ch; + int c; + struct prioq_elt pe; + struct stat st; + + for (c = 0;c < CHANNELS;++c) + { + fnmake_chanaddr(id,c); + if (stat(fn.s,&st) == 0) return; /* false alarm; consequence of HOPEFULLY */ + if (errno != error_noent) + { + log3("warning: unable to stat ",fn.s,"; will try again later\n"); + goto fail; + } + } + + fnmake_todo(id); + if (stat(fn.s,&st) == 0) return; + if (errno != error_noent) + { + log3("warning: unable to stat ",fn.s,"; will try again later\n"); + goto fail; + } + + fnmake_info(id); + if (stat(fn.s,&st) == -1) + { + if (errno == error_noent) return; + log3("warning: unable to stat ",fn.s,"; will try again later\n"); + goto fail; + } + + /* -todo +info -local -remote ?bounce */ + if (!injectbounce(id)) + goto fail; /* injectbounce() produced error message */ + + strnum3[fmt_ulong(strnum3,id)] = 0; + log3("end msg ",strnum3,"\n"); + + /* -todo +info -local -remote -bounce */ + fnmake_info(id); + if (unlink(fn.s) == -1) + { + log3("warning: unable to unlink ",fn.s,"; will try again later\n"); + goto fail; + } + + /* -todo -info -local -remote -bounce; we can relax */ + fnmake_foop(id); + if (substdio_putflush(&sstoqc,fn.s,fn.len) == -1) { cleandied(); return; } + if (substdio_get(&ssfromqc,&ch,1) != 1) { cleandied(); return; } + if (ch != '+') + log3("warning: qmail-clean unable to clean up ",fn.s,"\n"); + + return; + + fail: + pe.id = id; pe.dt = now() + SLEEP_SYSFAIL; + while (!prioq_insert(&pqdone,&pe)) nomem(); +} + +void pass_do() +{ + int c; + struct prioq_elt pe; + + for (c = 0;c < CHANNELS;++c) pass_dochan(c); + if (prioq_min(&pqfail,&pe)) + if (pe.dt <= recent) + { + prioq_delmin(&pqfail); + pqadd(pe.id); + } + if (prioq_min(&pqdone,&pe)) + if (pe.dt <= recent) + { + prioq_delmin(&pqdone); + messdone(pe.id); + } +} + + +/* this file is too long ---------------------------------------------- TODO */ + +datetime_sec nexttodorun; +DIR *tododir; /* if 0, have to opendir again */ +stralloc todoline = {0}; +char todobuf[SUBSTDIO_INSIZE]; +char todobufinfo[512]; +char todobufchan[CHANNELS][1024]; + +void todo_init() +{ + tododir = 0; + nexttodorun = now(); + trigger_set(); +} + +void todo_selprep(nfds,rfds,wakeup) +int *nfds; +fd_set *rfds; +datetime_sec *wakeup; +{ + if (flagexitasap) return; + trigger_selprep(nfds,rfds); + if (tododir) *wakeup = 0; + if (*wakeup > nexttodorun) *wakeup = nexttodorun; +} + +void todo_do(rfds) +fd_set *rfds; +{ + struct stat st; + substdio ss; int fd; + substdio ssinfo; int fdinfo; + substdio sschan[CHANNELS]; + int fdchan[CHANNELS]; + int flagchan[CHANNELS]; + struct prioq_elt pe; + char ch; + int match; + unsigned long id; + unsigned int len; + direntry *d; + int c; + unsigned long uid; + unsigned long pid; + + fd = -1; + fdinfo = -1; + for (c = 0;c < CHANNELS;++c) fdchan[c] = -1; + + if (flagexitasap) return; + + if (!tododir) + { + if (!trigger_pulled(rfds)) + if (recent < nexttodorun) + return; + trigger_set(); + tododir = opendir("todo"); + if (!tododir) + { + pausedir("todo"); + return; + } + nexttodorun = recent + SLEEP_TODO; + } + + d = readdir(tododir); + if (!d) + { + closedir(tododir); + tododir = 0; + return; + } + if (str_equal(d->d_name,".")) return; + if (str_equal(d->d_name,"..")) return; + len = scan_ulong(d->d_name,&id); + if (!len || d->d_name[len]) return; + + fnmake_todo(id); + + fd = open_read(fn.s); + if (fd == -1) { log3("warning: unable to open ",fn.s,"\n"); return; } + + fnmake_mess(id); + /* just for the statistics */ + if (stat(fn.s,&st) == -1) + { log3("warning: unable to stat ",fn.s,"\n"); goto fail; } + + for (c = 0;c < CHANNELS;++c) + { + fnmake_chanaddr(id,c); + if (unlink(fn.s) == -1) if (errno != error_noent) + { log3("warning: unable to unlink ",fn.s,"\n"); goto fail; } + } + + fnmake_info(id); + if (unlink(fn.s) == -1) if (errno != error_noent) + { log3("warning: unable to unlink ",fn.s,"\n"); goto fail; } + + fdinfo = open_excl(fn.s); + if (fdinfo == -1) + { log3("warning: unable to create ",fn.s,"\n"); goto fail; } + + strnum3[fmt_ulong(strnum3,id)] = 0; + log3("new msg ",strnum3,"\n"); + + for (c = 0;c < CHANNELS;++c) flagchan[c] = 0; + + substdio_fdbuf(&ss,read,fd,todobuf,sizeof(todobuf)); + substdio_fdbuf(&ssinfo,write,fdinfo,todobufinfo,sizeof(todobufinfo)); + + uid = 0; + pid = 0; + + for (;;) + { + if (getln(&ss,&todoline,&match,'\0') == -1) + { + /* perhaps we're out of memory, perhaps an I/O error */ + fnmake_todo(id); + log3("warning: trouble reading ",fn.s,"\n"); goto fail; + } + if (!match) break; + + switch(todoline.s[0]) + { + case 'u': + scan_ulong(todoline.s + 1,&uid); + break; + case 'p': + scan_ulong(todoline.s + 1,&pid); + break; + case 'F': + if (substdio_putflush(&ssinfo,todoline.s,todoline.len) == -1) + { + fnmake_info(id); + log3("warning: trouble writing to ",fn.s,"\n"); goto fail; + } + log2("info msg ",strnum3); + strnum2[fmt_ulong(strnum2,(unsigned long) st.st_size)] = 0; + log2(": bytes ",strnum2); + log1(" from <"); logsafe(todoline.s + 1); + strnum2[fmt_ulong(strnum2,pid)] = 0; + log2("> qp ",strnum2); + strnum2[fmt_ulong(strnum2,uid)] = 0; + log2(" uid ",strnum2); + log1("\n"); + break; + case 'T': + switch(rewrite(todoline.s + 1)) + { + case 0: nomem(); goto fail; + case 2: c = 1; break; + default: c = 0; break; + } + if (fdchan[c] == -1) + { + fnmake_chanaddr(id,c); + fdchan[c] = open_excl(fn.s); + if (fdchan[c] == -1) + { log3("warning: unable to create ",fn.s,"\n"); goto fail; } + substdio_fdbuf(&sschan[c] + ,write,fdchan[c],todobufchan[c],sizeof(todobufchan[c])); + flagchan[c] = 1; + } + if (substdio_bput(&sschan[c],rwline.s,rwline.len) == -1) + { + fnmake_chanaddr(id,c); + log3("warning: trouble writing to ",fn.s,"\n"); goto fail; + } + break; + default: + fnmake_todo(id); + log3("warning: unknown record type in ",fn.s,"\n"); goto fail; + } + } + + close(fd); fd = -1; + + fnmake_info(id); + if (substdio_flush(&ssinfo) == -1) + { log3("warning: trouble writing to ",fn.s,"\n"); goto fail; } + if (fsync(fdinfo) == -1) + { log3("warning: trouble fsyncing ",fn.s,"\n"); goto fail; } + close(fdinfo); fdinfo = -1; + + for (c = 0;c < CHANNELS;++c) + if (fdchan[c] != -1) + { + fnmake_chanaddr(id,c); + if (substdio_flush(&sschan[c]) == -1) + { log3("warning: trouble writing to ",fn.s,"\n"); goto fail; } + if (fsync(fdchan[c]) == -1) + { log3("warning: trouble fsyncing ",fn.s,"\n"); goto fail; } + close(fdchan[c]); fdchan[c] = -1; + } + + fnmake_todo(id); + if (substdio_putflush(&sstoqc,fn.s,fn.len) == -1) { cleandied(); return; } + if (substdio_get(&ssfromqc,&ch,1) != 1) { cleandied(); return; } + if (ch != '+') + { + log3("warning: qmail-clean unable to clean up ",fn.s,"\n"); + return; + } + + pe.id = id; pe.dt = now(); + for (c = 0;c < CHANNELS;++c) + if (flagchan[c]) + while (!prioq_insert(&pqchan[c],&pe)) nomem(); + + for (c = 0;c < CHANNELS;++c) if (flagchan[c]) break; + if (c == CHANNELS) + while (!prioq_insert(&pqdone,&pe)) nomem(); + + return; + + fail: + if (fd != -1) close(fd); + if (fdinfo != -1) close(fdinfo); + for (c = 0;c < CHANNELS;++c) + if (fdchan[c] != -1) close(fdchan[c]); +} + + +/* this file is too long ---------------------------------------------- MAIN */ + +int getcontrols() { if (control_init() == -1) return 0; + if (control_readint(&lifetime,"control/queuelifetime") == -1) return 0; + if (control_readint(&concurrency[0],"control/concurrencylocal") == -1) return 0; + if (control_readint(&concurrency[1],"control/concurrencyremote") == -1) return 0; + if (control_rldef(&envnoathost,"control/envnoathost",1,"envnoathost") != 1) return 0; + if (control_rldef(&bouncefrom,"control/bouncefrom",0,"MAILER-DAEMON") != 1) return 0; + if (control_rldef(&bouncehost,"control/bouncehost",1,"bouncehost") != 1) return 0; + if (control_rldef(&doublebouncehost,"control/doublebouncehost",1,"doublebouncehost") != 1) return 0; + if (control_rldef(&doublebounceto,"control/doublebounceto",0,"postmaster") != 1) return 0; + if (!stralloc_cats(&doublebounceto,"@")) return 0; + if (!stralloc_cat(&doublebounceto,&doublebouncehost)) return 0; + if (!stralloc_0(&doublebounceto)) return 0; + if (control_readfile(&locals,"control/locals",1) != 1) return 0; + if (!constmap_init(&maplocals,locals.s,locals.len,0)) return 0; + switch(control_readfile(&percenthack,"control/percenthack",0)) + { + case -1: return 0; + case 0: if (!constmap_init(&mappercenthack,"",0,0)) return 0; break; + case 1: if (!constmap_init(&mappercenthack,percenthack.s,percenthack.len,0)) return 0; break; + } + switch(control_readfile(&vdoms,"control/virtualdomains",0)) + { + case -1: return 0; + case 0: if (!constmap_init(&mapvdoms,"",0,1)) return 0; break; + case 1: if (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) return 0; break; + } + return 1; } + +stralloc newlocals = {0}; +stralloc newvdoms = {0}; + +void regetcontrols() +{ + int r; + + if (control_readfile(&newlocals,"control/locals",1) != 1) + { log1("alert: unable to reread control/locals\n"); return; } + r = control_readfile(&newvdoms,"control/virtualdomains",0); + if (r == -1) + { log1("alert: unable to reread control/virtualdomains\n"); return; } + + constmap_free(&maplocals); + constmap_free(&mapvdoms); + + while (!stralloc_copy(&locals,&newlocals)) nomem(); + while (!constmap_init(&maplocals,locals.s,locals.len,0)) nomem(); + + if (r) + { + while (!stralloc_copy(&vdoms,&newvdoms)) nomem(); + while (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) nomem(); + } + else + while (!constmap_init(&mapvdoms,"",0,1)) nomem(); +} + +void reread() +{ + if (chdir(auto_qmail) == -1) + { + log1("alert: unable to reread controls: unable to switch to home directory\n"); + return; + } + regetcontrols(); + while (chdir("queue") == -1) + { + log1("alert: unable to switch back to queue directory; HELP! sleeping...\n"); + sleep(10); + } +} + +void main() +{ + int fd; + datetime_sec wakeup; + fd_set rfds; + fd_set wfds; + int nfds; + struct timeval tv; + int c; + + if (chdir(auto_qmail) == -1) + { log1("alert: cannot start: unable to switch to home directory\n"); _exit(111); } + if (!getcontrols()) + { log1("alert: cannot start: unable to read controls\n"); _exit(111); } + if (chdir("queue") == -1) + { log1("alert: cannot start: unable to switch to queue directory\n"); _exit(111); } + sig_pipeignore(); + sig_termcatch(sigterm); + sig_alarmcatch(sigalrm); + sig_hangupcatch(sighup); + sig_childdefault(); + umask(077); + + fd = open_write("lock/sendmutex"); + if (fd == -1) + { log1("alert: cannot start: unable to open mutex\n"); _exit(111); } + if (lock_exnb(fd) == -1) + { log1("alert: cannot start: qmail-send is already running\n"); _exit(111); } + + numjobs = 0; + for (c = 0;c < CHANNELS;++c) + { + char ch; + int u; + int r; + do + r = read(chanfdin[c],&ch,1); + while ((r == -1) && (errno == error_intr)); + if (r < 1) + { log1("alert: cannot start: hath the daemon spawn no fire?\n"); _exit(111); } + u = (unsigned int) (unsigned char) ch; + if (concurrency[c] > u) concurrency[c] = u; + numjobs += concurrency[c]; + } + + fnmake_init(); + + comm_init(); + + pqstart(); + job_init(); + del_init(); + pass_init(); + todo_init(); + cleanup_init(); + + while (!flagexitasap || !del_canexit()) + { + recent = now(); + + if (flagrunasap) { flagrunasap = 0; pqrun(); } + if (flagreadasap) { flagreadasap = 0; reread(); } + + wakeup = recent + SLEEP_FOREVER; + FD_ZERO(&rfds); + FD_ZERO(&wfds); + nfds = 1; + + comm_selprep(&nfds,&wfds); + del_selprep(&nfds,&rfds); + pass_selprep(&wakeup); + todo_selprep(&nfds,&rfds,&wakeup); + cleanup_selprep(&wakeup); + + if (wakeup <= recent) tv.tv_sec = 0; + else tv.tv_sec = wakeup - recent + SLEEP_FUZZ; + tv.tv_usec = 0; + + if (select(nfds,&rfds,&wfds,(fd_set *) 0,&tv) == -1) + if (errno == error_intr) + ; + else + log1("warning: trouble in select\n"); + else + { + recent = now(); + + comm_do(&wfds); + del_do(&rfds); + todo_do(&rfds); + pass_do(); + cleanup_do(); + } + } + pqfinish(); + log1("status: exiting\n"); + _exit(0); +} diff --git a/qmail-showctl.8 b/qmail-showctl.8 new file mode 100644 index 0000000..e6a211d --- /dev/null +++ b/qmail-showctl.8 @@ -0,0 +1,12 @@ +.TH qmail-showctl 8 +.SH NAME +qmail-showctl \- analyze the qmail configuration files +.SH SYNOPSIS +.B qmail-showctl +.SH DESCRIPTION +.B qmail-showctl +explains the current +.B qmail +configuration. +.SH "SEE ALSO" +qmail-control(8) diff --git a/qmail-showctl.c b/qmail-showctl.c new file mode 100644 index 0000000..a24aa63 --- /dev/null +++ b/qmail-showctl.c @@ -0,0 +1,306 @@ +#include +#include +#include "substdio.h" +#include "subfd.h" +#include "exit.h" +#include "fmt.h" +#include "str.h" +#include "control.h" +#include "constmap.h" +#include "stralloc.h" +#include "direntry.h" +#include "auto_uids.h" +#include "auto_qmail.h" +#include "auto_break.h" +#include "auto_patrn.h" +#include "auto_spawn.h" +#include "auto_split.h" + +stralloc me = {0}; +int meok; + +stralloc line = {0}; +char num[FMT_ULONG]; + +void safeput(buf,len) +char *buf; +unsigned int len; +{ + char ch; + + while (len > 0) { + ch = *buf; + if ((ch < 32) || (ch > 126)) ch = '?'; + substdio_put(subfdout,&ch,1); + ++buf; + --len; + } +} + +void do_int(fn,def,pre,post) +char *fn; +char *def; +char *pre; +char *post; +{ + int i; + substdio_puts(subfdout,"\n"); + substdio_puts(subfdout,fn); + substdio_puts(subfdout,": "); + switch(control_readint(&i,fn)) { + case 0: + substdio_puts(subfdout,"(Default.) "); + substdio_puts(subfdout,pre); + substdio_puts(subfdout,def); + substdio_puts(subfdout,post); + substdio_puts(subfdout,".\n"); + break; + case 1: + if (i < 0) i = 0; + substdio_puts(subfdout,pre); + substdio_put(subfdout,num,fmt_uint(num,i)); + substdio_puts(subfdout,post); + substdio_puts(subfdout,".\n"); + break; + default: + substdio_puts(subfdout,"Oops! Trouble reading this file.\n"); + break; + } +} + +void do_str(fn,flagme,def,pre) +char *fn; +int flagme; +char *def; +char *pre; +{ + substdio_puts(subfdout,"\n"); + substdio_puts(subfdout,fn); + substdio_puts(subfdout,": "); + switch(control_readline(&line,fn)) { + case 0: + substdio_puts(subfdout,"(Default.) "); + if (!stralloc_copys(&line,def)) { + substdio_puts(subfdout,"Oops! Out of memory.\n"); + break; + } + if (flagme && meok) + if (!stralloc_copy(&line,&me)) { + substdio_puts(subfdout,"Oops! Out of memory.\n"); + break; + } + case 1: + substdio_puts(subfdout,pre); + safeput(line.s,line.len); + substdio_puts(subfdout,".\n"); + break; + default: + substdio_puts(subfdout,"Oops! Trouble reading this file.\n"); + break; + } +} + +int do_lst(fn,def,pre,post) +char *fn; +char *def; +char *pre; +char *post; +{ + int i; + int j; + + substdio_puts(subfdout,"\n"); + substdio_puts(subfdout,fn); + substdio_puts(subfdout,": "); + switch(control_readfile(&line,fn)) { + case 0: + substdio_puts(subfdout,"(Default.) "); + substdio_puts(subfdout,def); + substdio_puts(subfdout,"\n"); + return 0; + case 1: + substdio_puts(subfdout,"\n"); + i = 0; + for (j = 0;j < line.len;++j) + if (!line.s[j]) { + substdio_puts(subfdout,pre); + safeput(line.s + i,j - i); + substdio_puts(subfdout,post); + substdio_puts(subfdout,"\n"); + i = j + 1; + } + return 1; + default: + substdio_puts(subfdout,"Oops! Trouble reading this file.\n"); + return -1; + } +} + +void main() +{ + DIR *dir; + direntry *d; + struct stat stmrh; + struct stat stmrhcdb; + + substdio_puts(subfdout,"qmail home directory: "); + substdio_puts(subfdout,auto_qmail); + substdio_puts(subfdout,".\n"); + + substdio_puts(subfdout,"user-ext delimiter: "); + substdio_puts(subfdout,auto_break); + substdio_puts(subfdout,".\n"); + + substdio_puts(subfdout,"paternalism (in decimal): "); + substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_patrn)); + substdio_puts(subfdout,".\n"); + + substdio_puts(subfdout,"silent concurrency limit: "); + substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_spawn)); + substdio_puts(subfdout,".\n"); + + substdio_puts(subfdout,"subdirectory split: "); + substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_split)); + substdio_puts(subfdout,".\n"); + + substdio_puts(subfdout,"user ids: "); + substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uida)); + substdio_puts(subfdout,", "); + substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uidd)); + substdio_puts(subfdout,", "); + substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uidl)); + substdio_puts(subfdout,", "); + substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uido)); + substdio_puts(subfdout,", "); + substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uidp)); + substdio_puts(subfdout,", "); + substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uidq)); + substdio_puts(subfdout,", "); + substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uidr)); + substdio_puts(subfdout,", "); + substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uids)); + substdio_puts(subfdout,".\n"); + + substdio_puts(subfdout,"group ids: "); + substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_gidn)); + substdio_puts(subfdout,", "); + substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_gidq)); + substdio_puts(subfdout,".\n"); + + if (chdir(auto_qmail) == -1) { + substdio_puts(subfdout,"Oops! Unable to chdir to "); + substdio_puts(subfdout,auto_qmail); + substdio_puts(subfdout,".\n"); + substdio_flush(subfdout); + _exit(111); + } + if (chdir("control") == -1) { + substdio_puts(subfdout,"Oops! Unable to chdir to control.\n"); + substdio_flush(subfdout); + _exit(111); + } + + dir = opendir("."); + if (!dir) { + substdio_puts(subfdout,"Oops! Unable to open current directory.\n"); + substdio_flush(subfdout); + _exit(111); + } + + meok = control_readline(&me,"me"); + if (meok == -1) { + substdio_puts(subfdout,"Oops! Trouble reading control/me."); + substdio_flush(subfdout); + _exit(111); + } + + do_lst("badmailfrom","Any MAIL FROM is allowed.",""," not accepted in MAIL FROM."); + do_str("bouncefrom",0,"MAILER-DAEMON","Bounce user name is "); + do_str("bouncehost",1,"bouncehost","Bounce host name is "); + do_int("concurrencylocal","10","Local concurrency is ",""); + do_int("concurrencyremote","20","Remote concurrency is ",""); + do_int("databytes","0","SMTP DATA limit is "," bytes"); + do_str("defaultdomain",1,"defaultdomain","Default domain name is "); + do_str("defaulthost",1,"defaulthost","Default host name is "); + do_str("doublebouncehost",1,"doublebouncehost","2B recipient host: "); + do_str("doublebounceto",0,"postmaster","2B recipient user: "); + do_str("envnoathost",1,"envnoathost","Presumed domain name is "); + do_str("helohost",1,"helohost","SMTP client HELO host name is "); + do_str("idhost",1,"idhost","Message-ID host name is "); + do_str("localiphost",1,"localiphost","Local IP address becomes "); + do_lst("locals","Messages for me are delivered locally.","Messages for "," are delivered locally."); + do_str("me",0,"undefined! Uh-oh","My name is "); + do_lst("percenthack","The percent hack is not allowed.","The percent hack is allowed for user%host@","."); + do_str("plusdomain",1,"plusdomain","Plus domain name is "); + do_lst("qmqpservers","No QMQP servers.","QMQP server: ","."); + do_int("queuelifetime","604800","Message lifetime in the queue is "," seconds"); + + if (do_lst("rcpthosts","SMTP clients may send messages to any recipient.","SMTP clients may send messages to recipients at ",".")) + do_lst("morercpthosts","No effect.","SMTP clients may send messages to recipients at ","."); + else + do_lst("morercpthosts","No rcpthosts; morercpthosts is irrelevant.","No rcpthosts; doesn't matter that morercpthosts has ","."); + /* XXX: check morercpthosts.cdb contents */ + substdio_puts(subfdout,"\nmorercpthosts.cdb: "); + if (stat("morercpthosts",&stmrh) == -1) + if (stat("morercpthosts.cdb",&stmrhcdb) == -1) + substdio_puts(subfdout,"(Default.) No effect.\n"); + else + substdio_puts(subfdout,"Oops! morercpthosts.cdb exists but morercpthosts doesn't.\n"); + else + if (stat("morercpthosts.cdb",&stmrhcdb) == -1) + substdio_puts(subfdout,"Oops! morercpthosts exists but morercpthosts.cdb doesn't.\n"); + else + if (stmrh.st_mtime > stmrhcdb.st_mtime) + substdio_puts(subfdout,"Oops! morercpthosts.cdb is older than morercpthosts.\n"); + else + substdio_puts(subfdout,"Modified recently enough; hopefully up to date.\n"); + + do_str("smtpgreeting",1,"smtpgreeting","SMTP greeting: 220 "); + do_lst("smtproutes","No artificial SMTP routes.","SMTP route: ",""); + do_int("timeoutconnect","60","SMTP client connection timeout is "," seconds"); + do_int("timeoutremote","1200","SMTP client data timeout is "," seconds"); + do_int("timeoutsmtpd","1200","SMTP server data timeout is "," seconds"); + do_lst("virtualdomains","No virtual domains.","Virtual domain: ",""); + + while (d = readdir(dir)) { + if (str_equal(d->d_name,".")) continue; + if (str_equal(d->d_name,"..")) continue; + if (str_equal(d->d_name,"bouncefrom")) continue; + if (str_equal(d->d_name,"bouncehost")) continue; + if (str_equal(d->d_name,"badmailfrom")) continue; + if (str_equal(d->d_name,"bouncefrom")) continue; + if (str_equal(d->d_name,"bouncehost")) continue; + if (str_equal(d->d_name,"concurrencylocal")) continue; + if (str_equal(d->d_name,"concurrencyremote")) continue; + if (str_equal(d->d_name,"databytes")) continue; + if (str_equal(d->d_name,"defaultdomain")) continue; + if (str_equal(d->d_name,"defaulthost")) continue; + if (str_equal(d->d_name,"doublebouncehost")) continue; + if (str_equal(d->d_name,"doublebounceto")) continue; + if (str_equal(d->d_name,"envnoathost")) continue; + if (str_equal(d->d_name,"helohost")) continue; + if (str_equal(d->d_name,"idhost")) continue; + if (str_equal(d->d_name,"localiphost")) continue; + if (str_equal(d->d_name,"locals")) continue; + if (str_equal(d->d_name,"me")) continue; + if (str_equal(d->d_name,"morercpthosts")) continue; + if (str_equal(d->d_name,"morercpthosts.cdb")) continue; + if (str_equal(d->d_name,"percenthack")) continue; + if (str_equal(d->d_name,"plusdomain")) continue; + if (str_equal(d->d_name,"qmqpservers")) continue; + if (str_equal(d->d_name,"queuelifetime")) continue; + if (str_equal(d->d_name,"rcpthosts")) continue; + if (str_equal(d->d_name,"smtpgreeting")) continue; + if (str_equal(d->d_name,"smtproutes")) continue; + if (str_equal(d->d_name,"timeoutconnect")) continue; + if (str_equal(d->d_name,"timeoutremote")) continue; + if (str_equal(d->d_name,"timeoutsmtpd")) continue; + if (str_equal(d->d_name,"virtualdomains")) continue; + substdio_puts(subfdout,"\n"); + substdio_puts(subfdout,d->d_name); + substdio_puts(subfdout,": I have no idea what this file does.\n"); + } + + substdio_flush(subfdout); + _exit(0); +} diff --git a/qmail-smtpd.8 b/qmail-smtpd.8 new file mode 100644 index 0000000..c4640b8 --- /dev/null +++ b/qmail-smtpd.8 @@ -0,0 +1,179 @@ +.TH qmail-smtpd 8 +.SH NAME +qmail-smtpd \- receive mail via SMTP +.SH SYNOPSIS +.B qmail-smtpd +.SH DESCRIPTION +.B qmail-smtpd +receives mail messages via the Simple Mail Transfer Protocol (SMTP) +and invokes +.B qmail-queue +to deposit them into the outgoing queue. +.B qmail-smtpd +must be supplied several environment variables; +see +.BR tcp-environ(5) . + +.B qmail-smtpd +is responsible for counting hops. +It rejects any message with 100 or more +.B Received +or +.B Delivered-To +header fields. + +.B qmail-smtpd +supports ESMTP, including the 8BITMIME and PIPELINING options. +.SH TRANSPARENCY +.B qmail-smtpd +converts the SMTP newline convention into the UNIX newline convention +by converting CR LF into LF. +It returns a temporary error and drops the connection on bare LFs; +see +.BR http://pobox.com/~djb/docs/smtplf.html . + +.B qmail-smtpd +accepts messages that contain long lines or non-ASCII characters, +even though such messages violate the SMTP protocol. +.SH "CONTROL FILES" +.TP 5 +.I badmailfrom +Unacceptable envelope sender addresses. +.B qmail-smtpd +will reject every recipient address for a message +if the envelope sender address is listed in +.IR badmailfrom . +A line in +.I badmailfrom +may be of the form +.BR @\fIhost , +meaning every address at +.IR host . +.TP 5 +.I databytes +Maximum number of bytes allowed in a message, +or 0 for no limit. +Default: 0. +If a message exceeds this limit, +.B qmail-smtpd +returns a permanent error code to the client; +in contrast, if +the disk is full or +.B qmail-smtpd +hits a resource limit, +.B qmail-smtpd +returns a temporary error code. + +.I databytes +counts bytes as stored on disk, not as transmitted through the network. +It does not count the +.B qmail-smtpd +Received line, the +.B qmail-queue +Received line, or the envelope. + +If the environment variable +.B DATABYTES +is set, it overrides +.IR databytes . +.TP 5 +.I localiphost +Replacement host name for local IP addresses. +Default: +.IR me , +if that is supplied. +.B qmail-smtpd +is responsible for recognizing dotted-decimal addresses for the +current host. +When it sees a recipient address of the form +.IR box@[d.d.d.d] , +where +.I d.d.d.d +is a local IP address, +it replaces +.IR [d.d.d.d] +with +.IR localiphost . +This is done before +.IR rcpthosts . +.TP 5 +.I morercpthosts +Extra allowed RCPT domains. +If +.I rcpthosts +and +.I morercpthosts +both exist, +.I morercpthosts +is effectively appended to +.IR rcpthosts . + +You must run +.B qmail-newmrh +whenever +.I morercpthosts +changes. + +Rule of thumb for large sites: +Put your 50 most commonly used domains into +.IR rcpthosts , +and the rest into +.IR morercpthosts . +.TP 5 +.I rcpthosts +Allowed RCPT domains. +If +.I rcpthosts +is supplied, +.B qmail-smtpd +will reject +any envelope recipient address with a domain not listed in +.IR rcpthosts . + +Exception: +If the environment variable +.B RELAYCLIENT +is set, +.B qmail-smtpd +will ignore +.IR rcpthosts , +and will append the value of +.B RELAYCLIENT +to each incoming recipient address. + +.I rcpthosts +may include wildcards: + +.EX + heaven.af.mil + .heaven.af.mil +.EE + +Envelope recipient addresses without @ signs are +always allowed through. +.TP 5 +.I smtpgreeting +SMTP greeting message. +Default: +.IR me , +if that is supplied; +otherwise +.B qmail-smtpd +will refuse to run. +The first word of +.I smtpgreeting +should be the current host's name. +.TP 5 +.I timeoutsmtpd +Number of seconds +.B qmail-smtpd +will wait for each new buffer of data from the remote SMTP client. +Default: 1200. +.SH "SEE ALSO" +tcp-env(1), +tcp-environ(5), +qmail-control(5), +qmail-inject(8), +qmail-newmrh(8), +qmail-queue(8), +qmail-remote(8) diff --git a/qmail-smtpd.c b/qmail-smtpd.c new file mode 100644 index 0000000..54df00c --- /dev/null +++ b/qmail-smtpd.c @@ -0,0 +1,421 @@ +#include "sig.h" +#include "readwrite.h" +#include "stralloc.h" +#include "substdio.h" +#include "alloc.h" +#include "auto_qmail.h" +#include "control.h" +#include "received.h" +#include "constmap.h" +#include "error.h" +#include "ipme.h" +#include "ip.h" +#include "qmail.h" +#include "str.h" +#include "fmt.h" +#include "scan.h" +#include "byte.h" +#include "case.h" +#include "env.h" +#include "now.h" +#include "exit.h" +#include "rcpthosts.h" +#include "timeoutread.h" +#include "timeoutwrite.h" +#include "commands.h" + +#define MAXHOPS 100 +unsigned int databytes = 0; +int timeout = 1200; + +int safewrite(fd,buf,len) int fd; char *buf; int len; +{ + int r; + r = timeoutwrite(timeout,fd,buf,len); + if (r <= 0) _exit(1); + return r; +} + +char ssoutbuf[512]; +substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf); + +void flush() { substdio_flush(&ssout); } +void out(s) char *s; { substdio_puts(&ssout,s); } + +void die_read() { _exit(1); } +void die_alarm() { out("451 timeout (#4.4.2)\r\n"); flush(); _exit(1); } +void die_nomem() { out("421 out of memory (#4.3.0)\r\n"); flush(); _exit(1); } +void die_control() { out("421 unable to read controls (#4.3.0)\r\n"); flush(); _exit(1); } +void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); } +void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); } + +void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); } +void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); } +void err_unimpl(arg) char *arg; { out("502 unimplemented (#5.5.1)\r\n"); } +void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); } +void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); } +void err_wantrcpt() { out("503 RCPT first (#5.5.1)\r\n"); } +void err_noop(arg) char *arg; { out("250 ok\r\n"); } +void err_vrfy(arg) char *arg; { out("252 send some mail, i'll try my best\r\n"); } +void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); } + + +stralloc greeting = {0}; + +void smtp_greet(code) char *code; +{ + substdio_puts(&ssout,code); + substdio_put(&ssout,greeting.s,greeting.len); +} +void smtp_help(arg) char *arg; +{ + out("214 netqmail home page: http://qmail.org/netqmail\r\n"); +} +void smtp_quit(arg) char *arg; +{ + smtp_greet("221 "); out("\r\n"); flush(); _exit(0); +} + +char *remoteip; +char *remotehost; +char *remoteinfo; +char *local; +char *relayclient; + +stralloc helohost = {0}; +char *fakehelo; /* pointer into helohost, or 0 */ + +void dohelo(arg) char *arg; { + if (!stralloc_copys(&helohost,arg)) die_nomem(); + if (!stralloc_0(&helohost)) die_nomem(); + fakehelo = case_diffs(remotehost,helohost.s) ? helohost.s : 0; +} + +int liphostok = 0; +stralloc liphost = {0}; +int bmfok = 0; +stralloc bmf = {0}; +struct constmap mapbmf; + +void setup() +{ + char *x; + unsigned long u; + + if (control_init() == -1) die_control(); + if (control_rldef(&greeting,"control/smtpgreeting",1,(char *) 0) != 1) + die_control(); + liphostok = control_rldef(&liphost,"control/localiphost",1,(char *) 0); + if (liphostok == -1) die_control(); + if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control(); + if (timeout <= 0) timeout = 1; + + if (rcpthosts_init() == -1) die_control(); + + bmfok = control_readfile(&bmf,"control/badmailfrom",0); + if (bmfok == -1) die_control(); + if (bmfok) + if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem(); + + if (control_readint(&databytes,"control/databytes") == -1) die_control(); + x = env_get("DATABYTES"); + if (x) { scan_ulong(x,&u); databytes = u; } + if (!(databytes + 1)) --databytes; + + remoteip = env_get("TCPREMOTEIP"); + if (!remoteip) remoteip = "unknown"; + local = env_get("TCPLOCALHOST"); + if (!local) local = env_get("TCPLOCALIP"); + if (!local) local = "unknown"; + remotehost = env_get("TCPREMOTEHOST"); + if (!remotehost) remotehost = "unknown"; + remoteinfo = env_get("TCPREMOTEINFO"); + relayclient = env_get("RELAYCLIENT"); + dohelo(remotehost); +} + + +stralloc addr = {0}; /* will be 0-terminated, if addrparse returns 1 */ + +int addrparse(arg) +char *arg; +{ + int i; + char ch; + char terminator; + struct ip_address ip; + int flagesc; + int flagquoted; + + terminator = '>'; + i = str_chr(arg,'<'); + if (arg[i]) + arg += i + 1; + else { /* partner should go read rfc 821 */ + terminator = ' '; + arg += str_chr(arg,':'); + if (*arg == ':') ++arg; + while (*arg == ' ') ++arg; + } + + /* strip source route */ + if (*arg == '@') while (*arg) if (*arg++ == ':') break; + + if (!stralloc_copys(&addr,"")) die_nomem(); + flagesc = 0; + flagquoted = 0; + for (i = 0;ch = arg[i];++i) { /* copy arg to addr, stripping quotes */ + if (flagesc) { + if (!stralloc_append(&addr,&ch)) die_nomem(); + flagesc = 0; + } + else { + if (!flagquoted && (ch == terminator)) break; + switch(ch) { + case '\\': flagesc = 1; break; + case '"': flagquoted = !flagquoted; break; + default: if (!stralloc_append(&addr,&ch)) die_nomem(); + } + } + } + /* could check for termination failure here, but why bother? */ + if (!stralloc_append(&addr,"")) die_nomem(); + + if (liphostok) { + i = byte_rchr(addr.s,addr.len,'@'); + if (i < addr.len) /* if not, partner should go read rfc 821 */ + if (addr.s[i + 1] == '[') + if (!addr.s[i + 1 + ip_scanbracket(addr.s + i + 1,&ip)]) + if (ipme_is(&ip)) { + addr.len = i + 1; + if (!stralloc_cat(&addr,&liphost)) die_nomem(); + if (!stralloc_0(&addr)) die_nomem(); + } + } + + if (addr.len > 900) return 0; + return 1; +} + +int bmfcheck() +{ + int j; + if (!bmfok) return 0; + if (constmap(&mapbmf,addr.s,addr.len - 1)) return 1; + j = byte_rchr(addr.s,addr.len,'@'); + if (j < addr.len) + if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) return 1; + return 0; +} + +int addrallowed() +{ + int r; + r = rcpthosts(addr.s,str_len(addr.s)); + if (r == -1) die_control(); + return r; +} + + +int seenmail = 0; +int flagbarf; /* defined if seenmail */ +stralloc mailfrom = {0}; +stralloc rcptto = {0}; + +void smtp_helo(arg) char *arg; +{ + smtp_greet("250 "); out("\r\n"); + seenmail = 0; dohelo(arg); +} +void smtp_ehlo(arg) char *arg; +{ + smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); + seenmail = 0; dohelo(arg); +} +void smtp_rset(arg) char *arg; +{ + seenmail = 0; + out("250 flushed\r\n"); +} +void smtp_mail(arg) char *arg; +{ + if (!addrparse(arg)) { err_syntax(); return; } + flagbarf = bmfcheck(); + seenmail = 1; + if (!stralloc_copys(&rcptto,"")) die_nomem(); + if (!stralloc_copys(&mailfrom,addr.s)) die_nomem(); + if (!stralloc_0(&mailfrom)) die_nomem(); + out("250 ok\r\n"); +} +void smtp_rcpt(arg) char *arg; { + if (!seenmail) { err_wantmail(); return; } + if (!addrparse(arg)) { err_syntax(); return; } + if (flagbarf) { err_bmf(); return; } + if (relayclient) { + --addr.len; + if (!stralloc_cats(&addr,relayclient)) die_nomem(); + if (!stralloc_0(&addr)) die_nomem(); + } + else + if (!addrallowed()) { err_nogateway(); return; } + if (!stralloc_cats(&rcptto,"T")) die_nomem(); + if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); + if (!stralloc_0(&rcptto)) die_nomem(); + out("250 ok\r\n"); +} + + +int saferead(fd,buf,len) int fd; char *buf; int len; +{ + int r; + flush(); + r = timeoutread(timeout,fd,buf,len); + if (r == -1) if (errno == error_timeout) die_alarm(); + if (r <= 0) die_read(); + return r; +} + +char ssinbuf[1024]; +substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf); + +struct qmail qqt; +unsigned int bytestooverflow = 0; + +void put(ch) +char *ch; +{ + if (bytestooverflow) + if (!--bytestooverflow) + qmail_fail(&qqt); + qmail_put(&qqt,ch,1); +} + +void blast(hops) +int *hops; +{ + char ch; + int state; + int flaginheader; + int pos; /* number of bytes since most recent \n, if fih */ + int flagmaybex; /* 1 if this line might match RECEIVED, if fih */ + int flagmaybey; /* 1 if this line might match \r\n, if fih */ + int flagmaybez; /* 1 if this line might match DELIVERED, if fih */ + + state = 1; + *hops = 0; + flaginheader = 1; + pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; + for (;;) { + substdio_get(&ssin,&ch,1); + if (flaginheader) { + if (pos < 9) { + if (ch != "delivered"[pos]) if (ch != "DELIVERED"[pos]) flagmaybez = 0; + if (flagmaybez) if (pos == 8) ++*hops; + if (pos < 8) + if (ch != "received"[pos]) if (ch != "RECEIVED"[pos]) flagmaybex = 0; + if (flagmaybex) if (pos == 7) ++*hops; + if (pos < 2) if (ch != "\r\n"[pos]) flagmaybey = 0; + if (flagmaybey) if (pos == 1) flaginheader = 0; + ++pos; + } + if (ch == '\n') { pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; } + } + switch(state) { + case 0: + if (ch == '\n') straynewline(); + if (ch == '\r') { state = 4; continue; } + break; + case 1: /* \r\n */ + if (ch == '\n') straynewline(); + if (ch == '.') { state = 2; continue; } + if (ch == '\r') { state = 4; continue; } + state = 0; + break; + case 2: /* \r\n + . */ + if (ch == '\n') straynewline(); + if (ch == '\r') { state = 3; continue; } + state = 0; + break; + case 3: /* \r\n + .\r */ + if (ch == '\n') return; + put("."); + put("\r"); + if (ch == '\r') { state = 4; continue; } + state = 0; + break; + case 4: /* + \r */ + if (ch == '\n') { state = 1; break; } + if (ch != '\r') { put("\r"); state = 0; } + } + put(&ch); + } +} + +char accept_buf[FMT_ULONG]; +void acceptmessage(qp) unsigned long qp; +{ + datetime_sec when; + when = now(); + out("250 ok "); + accept_buf[fmt_ulong(accept_buf,(unsigned long) when)] = 0; + out(accept_buf); + out(" qp "); + accept_buf[fmt_ulong(accept_buf,qp)] = 0; + out(accept_buf); + out("\r\n"); +} + +void smtp_data(arg) char *arg; { + int hops; + unsigned long qp; + char *qqx; + + if (!seenmail) { err_wantmail(); return; } + if (!rcptto.len) { err_wantrcpt(); return; } + seenmail = 0; + if (databytes) bytestooverflow = databytes + 1; + if (qmail_open(&qqt) == -1) { err_qqt(); return; } + qp = qmail_qp(&qqt); + out("354 go ahead\r\n"); + + received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo); + blast(&hops); + hops = (hops >= MAXHOPS); + if (hops) qmail_fail(&qqt); + qmail_from(&qqt,mailfrom.s); + qmail_put(&qqt,rcptto.s,rcptto.len); + + qqx = qmail_close(&qqt); + if (!*qqx) { acceptmessage(qp); return; } + if (hops) { out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; } + if (databytes) if (!bytestooverflow) { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); return; } + if (*qqx == 'D') out("554 "); else out("451 "); + out(qqx + 1); + out("\r\n"); +} + +struct commands smtpcommands[] = { + { "rcpt", smtp_rcpt, 0 } +, { "mail", smtp_mail, 0 } +, { "data", smtp_data, flush } +, { "quit", smtp_quit, flush } +, { "helo", smtp_helo, flush } +, { "ehlo", smtp_ehlo, flush } +, { "rset", smtp_rset, 0 } +, { "help", smtp_help, flush } +, { "noop", err_noop, flush } +, { "vrfy", err_vrfy, flush } +, { 0, err_unimpl, flush } +} ; + +void main() +{ + sig_pipeignore(); + if (chdir(auto_qmail) == -1) die_control(); + setup(); + if (ipme_init() != 1) die_ipme(); + smtp_greet("220 "); + out(" ESMTP\r\n"); + if (commands(&ssin,&smtpcommands) == 0) die_read(); + die_nomem(); +} diff --git a/qmail-start.9 b/qmail-start.9 new file mode 100644 index 0000000..29876ec --- /dev/null +++ b/qmail-start.9 @@ -0,0 +1,94 @@ +.TH qmail-start 8 +.SH NAME +qmail-start \- turn on mail delivery +.SH SYNOPSIS +.B qmail-start +[ +.I defaultdelivery +[ +.I logger arg ... +] +] +.SH DESCRIPTION +.B qmail-start +invokes +.BR qmail-send , +.BR qmail-lspawn , +.BR qmail-rspawn , +and +.BR qmail-clean , +under the proper uids and gids. +These four daemons cooperate to deliver messages from the queue. + +.B qmail-start +arranges for +.BR qmail-send 's +activity record to be sent to +.BR qmail-start 's +output. +See +.B qmail-log(5) +for the format of the activity record. +Other than this, +.B qmail-start +does not print anything, even on failure. + +If +.I defaultdelivery +is supplied, +.B qmail-start +passes it to +.BR qmail-lspawn . + +If +.I logger +is supplied, +.B qmail-start +invokes +.I logger +with the given arguments, +and feeds +.BR qmail-send 's +activity record through +.IR logger . + +Environment variables given to +.B qmail-start +will eventually be passed on to +.BR qmail-local , +so make sure to clean up the environment if you run +.B qmail-start +manually: + +.EX + # env - PATH="QMAILHOME/bin:$PATH" +.br + qmail-start ./Mailbox splogger qmail & +.br + (all on one line) +.EE + +Resource limits, controlling ttys, et al. are also passed from +.B qmail-start +to +.BR qmail-local . + +Note that +.B qmail-send +normally juggles several simultaneous deliveries. +To reduce +.BR qmail-send 's +impact on other programs, +you can run +.B qmail-start +with a low priority. +.SH "SEE ALSO" +logger(1), +splogger(1), +nice(1), +qmail-log(5), +qmail-local(8), +qmail-clean(8), +qmail-lspawn(8), +qmail-rspawn(8), +qmail-send(8) diff --git a/qmail-start.c b/qmail-start.c new file mode 100644 index 0000000..37423b0 --- /dev/null +++ b/qmail-start.c @@ -0,0 +1,120 @@ +#include "fd.h" +#include "prot.h" +#include "exit.h" +#include "fork.h" +#include "auto_uids.h" + +char *(qsargs[]) = { "qmail-send", 0 }; +char *(qcargs[]) = { "qmail-clean", 0 }; +char *(qlargs[]) = { "qmail-lspawn", "./Mailbox", 0 }; +char *(qrargs[]) = { "qmail-rspawn", 0 }; + +void die() { _exit(111); } + +int pi0[2]; +int pi1[2]; +int pi2[2]; +int pi3[2]; +int pi4[2]; +int pi5[2]; +int pi6[2]; + +void close23456() { close(2); close(3); close(4); close(5); close(6); } + +void closepipes() { + close(pi1[0]); close(pi1[1]); close(pi2[0]); close(pi2[1]); + close(pi3[0]); close(pi3[1]); close(pi4[0]); close(pi4[1]); + close(pi5[0]); close(pi5[1]); close(pi6[0]); close(pi6[1]); +} + +void main(argc,argv) +int argc; +char **argv; +{ + if (chdir("/") == -1) die(); + umask(077); + if (prot_gid(auto_gidq) == -1) die(); + + if (fd_copy(2,0) == -1) die(); + if (fd_copy(3,0) == -1) die(); + if (fd_copy(4,0) == -1) die(); + if (fd_copy(5,0) == -1) die(); + if (fd_copy(6,0) == -1) die(); + + if (argv[1]) { + qlargs[1] = argv[1]; + ++argv; + } + + if (argv[1]) { + if (pipe(pi0) == -1) die(); + switch(fork()) { + case -1: + die(); + case 0: + if (prot_gid(auto_gidn) == -1) die(); + if (prot_uid(auto_uidl) == -1) die(); + close(pi0[1]); + if (fd_move(0,pi0[0]) == -1) die(); + close23456(); + execvp(argv[1],argv + 1); + die(); + } + close(pi0[0]); + if (fd_move(1,pi0[1]) == -1) die(); + } + + if (pipe(pi1) == -1) die(); + if (pipe(pi2) == -1) die(); + if (pipe(pi3) == -1) die(); + if (pipe(pi4) == -1) die(); + if (pipe(pi5) == -1) die(); + if (pipe(pi6) == -1) die(); + + switch(fork()) { + case -1: die(); + case 0: + if (fd_copy(0,pi1[0]) == -1) die(); + if (fd_copy(1,pi2[1]) == -1) die(); + close23456(); + closepipes(); + execvp(*qlargs,qlargs); + die(); + } + + switch(fork()) { + case -1: die(); + case 0: + if (prot_uid(auto_uidr) == -1) die(); + if (fd_copy(0,pi3[0]) == -1) die(); + if (fd_copy(1,pi4[1]) == -1) die(); + close23456(); + closepipes(); + execvp(*qrargs,qrargs); + die(); + } + + switch(fork()) { + case -1: die(); + case 0: + if (prot_uid(auto_uidq) == -1) die(); + if (fd_copy(0,pi5[0]) == -1) die(); + if (fd_copy(1,pi6[1]) == -1) die(); + close23456(); + closepipes(); + execvp(*qcargs,qcargs); + die(); + } + + if (prot_uid(auto_uids) == -1) die(); + if (fd_copy(0,1) == -1) die(); + if (fd_copy(1,pi1[1]) == -1) die(); + if (fd_copy(2,pi2[0]) == -1) die(); + if (fd_copy(3,pi3[1]) == -1) die(); + if (fd_copy(4,pi4[0]) == -1) die(); + if (fd_copy(5,pi5[1]) == -1) die(); + if (fd_copy(6,pi6[0]) == -1) die(); + closepipes(); + execvp(*qsargs,qsargs); + die(); +} diff --git a/qmail-tcpok.8 b/qmail-tcpok.8 new file mode 100644 index 0000000..ed2efcf --- /dev/null +++ b/qmail-tcpok.8 @@ -0,0 +1,24 @@ +.TH qmail-tcpok 8 +.SH NAME +qmail-tcpok \- clear TCP timeout table +.SH SYNOPSIS +.B qmail-tcpok +.SH DESCRIPTION +.B qmail-tcpok +erases +.BR qmail-remote 's +current list of timeouts, +so that +.B qmail-remote +does not make any assumptions about failing addresses. + +.B qmail-tcpok +must be run either as +.B root +or with user id +.B qmailr +and group id +.BR qmail . +.SH "SEE ALSO" +qmail-remote(8), +qmail-tcpto(8) diff --git a/qmail-tcpok.c b/qmail-tcpok.c new file mode 100644 index 0000000..a9b9652 --- /dev/null +++ b/qmail-tcpok.c @@ -0,0 +1,35 @@ +#include "strerr.h" +#include "substdio.h" +#include "lock.h" +#include "open.h" +#include "readwrite.h" +#include "auto_qmail.h" +#include "exit.h" + +#define FATAL "qmail-tcpok: fatal: " + +char buf[1024]; /* XXX: must match size in tcpto_clean.c, tcpto.c */ +substdio ss; + +void main() +{ + int fd; + int i; + + if (chdir(auto_qmail) == -1) + strerr_die4sys(111,FATAL,"unable to chdir to ",auto_qmail,": "); + if (chdir("queue/lock") == -1) + strerr_die4sys(111,FATAL,"unable to chdir to ",auto_qmail,"/queue/lock: "); + + fd = open_write("tcpto"); + if (fd == -1) + strerr_die4sys(111,FATAL,"unable to write ",auto_qmail,"/queue/lock/tcpto: "); + if (lock_ex(fd) == -1) + strerr_die4sys(111,FATAL,"unable to lock ",auto_qmail,"/queue/lock/tcpto: "); + + substdio_fdbuf(&ss,write,fd,buf,sizeof buf); + for (i = 0;i < sizeof buf;++i) substdio_put(&ss,"",1); + if (substdio_flush(&ss) == -1) + strerr_die4sys(111,FATAL,"unable to clear ",auto_qmail,"/queue/lock/tcpto: "); + _exit(0); +} diff --git a/qmail-tcpto.8 b/qmail-tcpto.8 new file mode 100644 index 0000000..cb4ddd6 --- /dev/null +++ b/qmail-tcpto.8 @@ -0,0 +1,30 @@ +.TH qmail-tcpto 8 +.SH NAME +qmail-tcpto \- print TCP timeout table +.SH SYNOPSIS +.B qmail-tcpto +.SH DESCRIPTION +After an SMTP connection attempt times out, +.B qmail-remote +records the relevant IP address. +If the same address fails again (after at least two minutes with +no intervening successful connections), +.B qmail-remote +assumes that further attempts will fail for at least another hour. + +.B qmail-tcpto +prints +.BR qmail-remote 's +current list of timeouts. + +.B qmail-tcpto +must be run either as +.B root +or with user id +.B qmailr +and group id +.BR qmail . +.SH "SEE ALSO" +qmail-qread(8), +qmail-remote(8), +qmail-tcpok(8) diff --git a/qmail-tcpto.c b/qmail-tcpto.c new file mode 100644 index 0000000..d181ecf --- /dev/null +++ b/qmail-tcpto.c @@ -0,0 +1,85 @@ +/* XXX: this program knows quite a bit about tcpto's internals */ + +#include "substdio.h" +#include "subfd.h" +#include "auto_qmail.h" +#include "fmt.h" +#include "ip.h" +#include "lock.h" +#include "error.h" +#include "exit.h" +#include "datetime.h" +#include "now.h" + +void die(n) int n; { substdio_flush(subfdout); _exit(n); } + +void warn(s) char *s; +{ + char *x; + x = error_str(errno); + substdio_puts(subfdout,s); + substdio_puts(subfdout,": "); + substdio_puts(subfdout,x); + substdio_puts(subfdout,"\n"); +} + +void die_chdir() { warn("fatal: unable to chdir"); die(111); } +void die_open() { warn("fatal: unable to open tcpto"); die(111); } +void die_lock() { warn("fatal: unable to lock tcpto"); die(111); } +void die_read() { warn("fatal: unable to read tcpto"); die(111); } + +char tcpto_buf[1024]; + +char tmp[FMT_ULONG + IPFMT]; + +void main() +{ + int fdlock; + int fd; + int r; + int i; + char *record; + struct ip_address ip; + datetime_sec when; + datetime_sec start; + + if (chdir(auto_qmail) == -1) die_chdir(); + if (chdir("queue/lock") == -1) die_chdir(); + + fdlock = open_write("tcpto"); + if (fdlock == -1) die_open(); + fd = open_read("tcpto"); + if (fd == -1) die_open(); + if (lock_ex(fdlock) == -1) die_lock(); + r = read(fd,tcpto_buf,sizeof(tcpto_buf)); + close(fd); + close(fdlock); + + if (r == -1) die_read(); + r >>= 4; + + start = now(); + + record = tcpto_buf; + for (i = 0;i < r;++i) + { + if (record[4] >= 1) + { + byte_copy(&ip,4,record); + when = (unsigned long) (unsigned char) record[11]; + when = (when << 8) + (unsigned long) (unsigned char) record[10]; + when = (when << 8) + (unsigned long) (unsigned char) record[9]; + when = (when << 8) + (unsigned long) (unsigned char) record[8]; + + substdio_put(subfdout,tmp,ip_fmt(tmp,&ip)); + substdio_puts(subfdout," timed out "); + substdio_put(subfdout,tmp,fmt_ulong(tmp,(unsigned long) (start - when))); + substdio_puts(subfdout," seconds ago; # recent timeouts: "); + substdio_put(subfdout,tmp,fmt_ulong(tmp,(unsigned long) (unsigned char) record[4])); + substdio_puts(subfdout,"\n"); + } + record += 16; + } + + die(0); +} diff --git a/qmail-upq.sh b/qmail-upq.sh new file mode 100644 index 0000000..a068196 --- /dev/null +++ b/qmail-upq.sh @@ -0,0 +1,14 @@ +cd QMAIL +cd queue +for dir in mess info local remote +do + ( cd $dir; find . -type f -print ) | ( + cd $dir + while read path + do + id=`basename "$path"` + sub=`expr "$id" % SPLIT` + mv "$path" "$sub"/"$id" + done + ) +done diff --git a/qmail-users.9 b/qmail-users.9 new file mode 100644 index 0000000..c44a452 --- /dev/null +++ b/qmail-users.9 @@ -0,0 +1,113 @@ +.TH qmail-users 5 +.SH NAME +qmail-users \- assign mail addresses to users +.SH OVERVIEW +The file +.B QMAILHOME/users/assign +assigns addresses to users. For example, + +.EX + =joe.shmoe:joe:503:78:/home/joe::: +.EE + +says that mail for +.B joe.shmoe +should be delivered to user +.BR joe , +with uid 503 and gid 78, +as specified by +.BR /home/joe/.qmail . + +Assignments fed to +.B qmail-newu +will be used by +.B qmail-lspawn +to control +.BR qmail-local 's +deliveries. +See +.BR qmail-newu (8). +A change to +.B QMAILHOME/users/assign +will have no effect until +.B qmail-newu +is run. +.SH STRUCTURE +.B QMAILHOME/users/assign +is a series of assignments, one per line. +It ends with a line containing a single dot. +Lines must not contain NUL. +.SH "SIMPLE ASSIGNMENTS" +A simple assignment is a line of the form + +.EX + =local:user:uid:gid:homedir:dash:ext: +.EE + +Here +.I local +is an address; +.IR user , +.IR uid , +and +.I gid +are the account name, uid, and gid +of the user in charge of +.IR local ; +and messages to +.I local +will be controlled by +.IR homedir\fB/.qmail\fIdashext . + +If there are several assignments for the same +.I local +address, +.B qmail-lspawn +will use the first one. + +.I local +is interpreted without regard to case. +.SH "WILDCARD ASSIGNMENTS" +A wildcard assignment is a line of the form + +.EX + +loc:user:uid:gid:homedir:dash:pre: +.EE + +This assignment applies to any address beginning with +.IR loc , +including +.I loc +itself. +It means the same as + +.EX + =locext:user:uid:gid:homedir:dash:preext: +.EE + +for every string +.IR ext . + +A more specific wildcard assignment overrides a less specific +assignment, and a simple assignment overrides any wildcard assignment. +For example: + +.EX + +:alias:7790:2108:QMAILHOME/alias:-:: + +joe-:joe:507:100:/home/joe:-:: + =joe:joe:507:100:/home/joe::: +.EE + +The address +.B joe +is handled by the third line; +the address +.B joe-direct +is handled by the second line; +the address +.B bill +is handled by the first line. +.SH "SEE ALSO" +qmail-pw2u(8), +qmail-newu(8), +qmail-lspawn(8) diff --git a/qmail.7 b/qmail.7 new file mode 100644 index 0000000..df9c885 --- /dev/null +++ b/qmail.7 @@ -0,0 +1,68 @@ +.TH qmail 7 +.SH "NAME" +qmail \- overview of qmail documentation +.SH "INTRODUCTION" +.B qmail +is a secure, reliable, efficient, simple message transfer agent. + +Users who want to control incoming messages +should read +.BR dot-qmail (5). +Available commands for the +.B .qmail +file include +.BR qbiff (1), +.BR qreceipt (1), +.BR forward (1), +.BR bouncesaying (1), +and +.BR condredirect (1). +Other helpful commands include +.BR maildirmake (1), +.BR maildir2mbox (1), +and +.BR maildirwatch (1). + +System administrators who want to control the entire +.B qmail +system should start with +.BR qmail-control (5) +and +.BR qmail-start (8). +There are three queue-monitoring tools: +.BR qmail-qread (8), +.BR qmail-qstat (8), +and +.BR qmail-tcpto (8). +Incoming SMTP connections are handled by +.BR qmail-smtpd (8). + +.B qmail +offers two command-line message-sending interfaces: +.BR qmail-inject (8) +and +.BR mailsubj (1). +For background information on Internet mail messages, +see +.BR addresses (5), +.BR envelopes (5), +.BR qmail-header (5), +and +.BR forgeries (7). + +Miscellaneous documentation includes +.BR qmail-limits (7) +and +.BR qmail-pop3d (8). + +This documentation describes netqmail version +1.05 +of +.BR qmail . +See +.B http://pobox.com/~djb/qmail.html +for other +.BR qmail -related +software, and +.B http://qmail.org/ +for other qmail community contributions. diff --git a/qmail.c b/qmail.c new file mode 100644 index 0000000..186c092 --- /dev/null +++ b/qmail.c @@ -0,0 +1,136 @@ +#include "substdio.h" +#include "readwrite.h" +#include "wait.h" +#include "exit.h" +#include "fork.h" +#include "fd.h" +#include "qmail.h" +#include "auto_qmail.h" +#include "env.h" + +static char *binqqargs[2] = { 0, 0 } ; + +static void setup_qqargs() +{ + if(!binqqargs[0]) + binqqargs[0] = env_get("QMAILQUEUE"); + if(!binqqargs[0]) + binqqargs[0] = "bin/qmail-queue"; +} + +int qmail_open(qq) +struct qmail *qq; +{ + int pim[2]; + int pie[2]; + + setup_qqargs(); + + if (pipe(pim) == -1) return -1; + if (pipe(pie) == -1) { close(pim[0]); close(pim[1]); return -1; } + + switch(qq->pid = vfork()) { + case -1: + close(pim[0]); close(pim[1]); + close(pie[0]); close(pie[1]); + return -1; + case 0: + close(pim[1]); + close(pie[1]); + if (fd_move(0,pim[0]) == -1) _exit(120); + if (fd_move(1,pie[0]) == -1) _exit(120); + if (chdir(auto_qmail) == -1) _exit(61); + execv(*binqqargs,binqqargs); + _exit(120); + } + + qq->fdm = pim[1]; close(pim[0]); + qq->fde = pie[1]; close(pie[0]); + substdio_fdbuf(&qq->ss,write,qq->fdm,qq->buf,sizeof(qq->buf)); + qq->flagerr = 0; + return 0; +} + +unsigned long qmail_qp(qq) struct qmail *qq; +{ + return qq->pid; +} + +void qmail_fail(qq) struct qmail *qq; +{ + qq->flagerr = 1; +} + +void qmail_put(qq,s,len) struct qmail *qq; char *s; int len; +{ + if (!qq->flagerr) if (substdio_put(&qq->ss,s,len) == -1) qq->flagerr = 1; +} + +void qmail_puts(qq,s) struct qmail *qq; char *s; +{ + if (!qq->flagerr) if (substdio_puts(&qq->ss,s) == -1) qq->flagerr = 1; +} + +void qmail_from(qq,s) struct qmail *qq; char *s; +{ + if (substdio_flush(&qq->ss) == -1) qq->flagerr = 1; + close(qq->fdm); + substdio_fdbuf(&qq->ss,write,qq->fde,qq->buf,sizeof(qq->buf)); + qmail_put(qq,"F",1); + qmail_puts(qq,s); + qmail_put(qq,"",1); +} + +void qmail_to(qq,s) struct qmail *qq; char *s; +{ + qmail_put(qq,"T",1); + qmail_puts(qq,s); + qmail_put(qq,"",1); +} + +char *qmail_close(qq) +struct qmail *qq; +{ + int wstat; + int exitcode; + + qmail_put(qq,"",1); + if (!qq->flagerr) if (substdio_flush(&qq->ss) == -1) qq->flagerr = 1; + close(qq->fde); + + if (wait_pid(&wstat,qq->pid) != qq->pid) + return "Zqq waitpid surprise (#4.3.0)"; + if (wait_crashed(wstat)) + return "Zqq crashed (#4.3.0)"; + exitcode = wait_exitcode(wstat); + + switch(exitcode) { + case 115: /* compatibility */ + case 11: return "Denvelope address too long for qq (#5.1.3)"; + case 31: return "Dmail server permanently rejected message (#5.3.0)"; + case 51: return "Zqq out of memory (#4.3.0)"; + case 52: return "Zqq timeout (#4.3.0)"; + case 53: return "Zqq write error or disk full (#4.3.0)"; + case 0: if (!qq->flagerr) return ""; /* fall through */ + case 54: return "Zqq read error (#4.3.0)"; + case 55: return "Zqq unable to read configuration (#4.3.0)"; + case 56: return "Zqq trouble making network connection (#4.3.0)"; + case 61: return "Zqq trouble in home directory (#4.3.0)"; + case 63: + case 64: + case 65: + case 66: + case 62: return "Zqq trouble creating files in queue (#4.3.0)"; + case 71: return "Zmail server temporarily rejected message (#4.3.0)"; + case 72: return "Zconnection to mail server timed out (#4.4.1)"; + case 73: return "Zconnection to mail server rejected (#4.4.1)"; + case 74: return "Zcommunication with mail server failed (#4.4.2)"; + case 91: /* fall through */ + case 81: return "Zqq internal bug (#4.3.0)"; + case 120: return "Zunable to exec qq (#4.3.0)"; + default: + if ((exitcode >= 11) && (exitcode <= 40)) + return "Dqq permanent problem (#5.3.0)"; + return "Zqq temporary problem (#4.3.0)"; + } +} diff --git a/qmail.h b/qmail.h new file mode 100644 index 0000000..7fa13e2 --- /dev/null +++ b/qmail.h @@ -0,0 +1,24 @@ +#ifndef QMAIL_H +#define QMAIL_H + +#include "substdio.h" + +struct qmail { + int flagerr; + unsigned long pid; + int fdm; + int fde; + substdio ss; + char buf[1024]; +} ; + +extern int qmail_open(); +extern void qmail_put(); +extern void qmail_puts(); +extern void qmail_from(); +extern void qmail_to(); +extern void qmail_fail(); +extern char *qmail_close(); +extern unsigned long qmail_qp(); + +#endif diff --git a/qreceipt.1 b/qreceipt.1 new file mode 100644 index 0000000..4d3cd08 --- /dev/null +++ b/qreceipt.1 @@ -0,0 +1,33 @@ +.TH qreceipt 1 +.SH NAME +qreceipt \- respond to delivery notice requests +.SH SYNOPSIS +in +.BR .qmail : +.B |qreceipt +.I youraddress +.SH DESCRIPTION +When a mail message arrives with +.I youraddress +listed in a +.B Notice-Requested-Upon-Delivery-To +header field, +.B qreceipt +sends a success notice back to the envelope sender. + +.B WARNING: +If you create a +.B .qmail +file to enable +.BR qreceipt , +make sure to also add a line specifying delivery to your normal mailbox. +For example: + +.EX + /home/joe/Mailbox +.br + |qreceipt joe@nowhere.mil +.EE +.SH "SEE ALSO" +dot-qmail(5), +envelopes(5) diff --git a/qreceipt.c b/qreceipt.c new file mode 100644 index 0000000..49b9807 --- /dev/null +++ b/qreceipt.c @@ -0,0 +1,131 @@ +#include "sig.h" +#include "env.h" +#include "substdio.h" +#include "stralloc.h" +#include "subfd.h" +#include "getln.h" +#include "alloc.h" +#include "str.h" +#include "hfield.h" +#include "token822.h" +#include "error.h" +#include "gen_alloc.h" +#include "gen_allocdefs.h" +#include "headerbody.h" +#include "exit.h" +#include "open.h" +#include "quote.h" +#include "qmail.h" + +void die_noreceipt() { _exit(0); } +void die() { _exit(100); } +void die_temp() { _exit(111); } +void die_nomem() { + substdio_putsflush(subfderr,"qreceipt: fatal: out of memory\n"); die_temp(); } +void die_fork() { + substdio_putsflush(subfderr,"qreceipt: fatal: unable to fork\n"); die_temp(); } +void die_qqperm() { + substdio_putsflush(subfderr,"qreceipt: fatal: permanent qmail-queue error\n"); die(); } +void die_qqtemp() { + substdio_putsflush(subfderr,"qreceipt: fatal: temporary qmail-queue error\n"); die_temp(); } +void die_usage() { + substdio_putsflush(subfderr, + "qreceipt: usage: qreceipt deliveryaddress\n"); die(); } +void die_read() { + if (errno == error_nomem) die_nomem(); + substdio_putsflush(subfderr,"qreceipt: fatal: read error\n"); die_temp(); } +void doordie(sa,r) stralloc *sa; int r; { + if (r == 1) return; if (r == -1) die_nomem(); + substdio_putsflush(subfderr,"qreceipt: fatal: unable to parse this: "); + substdio_putflush(subfderr,sa->s,sa->len); die(); } + +char *target; + +int flagreceipt = 0; + +char *returnpath; +stralloc messageid = {0}; +stralloc sanotice = {0}; + +int rwnotice(addr) token822_alloc *addr; { token822_reverse(addr); + if (token822_unquote(&sanotice,addr) != 1) die_nomem(); + if (sanotice.len == str_len(target)) + if (!str_diffn(sanotice.s,target,sanotice.len)) + flagreceipt = 1; + token822_reverse(addr); return 1; } + +struct qmail qqt; + +stralloc quoted = {0}; + +void finishheader() +{ + char *qqx; + + if (!flagreceipt) die_noreceipt(); + if (str_equal(returnpath,"")) die_noreceipt(); + if (str_equal(returnpath,"#@[]")) die_noreceipt(); + + if (!quote2("ed,returnpath)) die_nomem(); + + if (qmail_open(&qqt) == -1) die_fork(); + + qmail_puts(&qqt,"From: DELIVERY NOTICE SYSTEM <"); + qmail_put(&qqt,quoted.s,quoted.len); + qmail_puts(&qqt,">\n"); + qmail_puts(&qqt,"To: <"); + qmail_put(&qqt,quoted.s,quoted.len); + qmail_puts(&qqt,">\n"); + qmail_puts(&qqt,"Subject: success notice\n\ +\n\ +Hi! This is the qreceipt program. Your message was delivered to the\n\ +following address: "); + qmail_puts(&qqt,target); + qmail_puts(&qqt,". Thanks for asking.\n"); + if (messageid.s) + { + qmail_puts(&qqt,"Your "); + qmail_put(&qqt,messageid.s,messageid.len); + } + + qmail_from(&qqt,""); + qmail_to(&qqt,returnpath); + qqx = qmail_close(&qqt); + + if (*qqx) + if (*qqx == 'D') die_qqperm(); + else die_qqtemp(); +} + +stralloc hfbuf = {0}; +token822_alloc hfin = {0}; +token822_alloc hfrewrite = {0}; +token822_alloc hfaddr = {0}; + +void doheaderfield(h) +stralloc *h; +{ + switch(hfield_known(h->s,h->len)) + { + case H_MESSAGEID: + if (!stralloc_copy(&messageid,h)) die_nomem(); + break; + case H_NOTICEREQUESTEDUPONDELIVERYTO: + doordie(h,token822_parse(&hfin,h,&hfbuf)); + doordie(h,token822_addrlist(&hfrewrite,&hfaddr,&hfin,rwnotice)); + break; + } +} + +void dobody(h) stralloc *h; { ; } + +void main(argc,argv) +int argc; +char **argv; +{ + sig_pipeignore(); + if (!(target = argv[1])) die_usage(); + if (!(returnpath = env_get("SENDER"))) die_usage(); + if (headerbody(subfdin,doheaderfield,finishheader,dobody) == -1) die_read(); + die_noreceipt(); +} diff --git a/qsmhook.c b/qsmhook.c new file mode 100644 index 0000000..d5b38aa --- /dev/null +++ b/qsmhook.c @@ -0,0 +1,137 @@ +#include "fd.h" +#include "stralloc.h" +#include "readwrite.h" +#include "sgetopt.h" +#include "wait.h" +#include "env.h" +#include "byte.h" +#include "str.h" +#include "alloc.h" +#include "exit.h" +#include "fork.h" +#include "case.h" +#include "subfd.h" +#include "error.h" +#include "substdio.h" +#include "sig.h" + +void die(e,s) int e; char *s; { substdio_putsflush(subfderr,s); _exit(e); } +void die_usage() { die(100,"qsmhook: fatal: incorrect usage\n"); } +void die_temp() { die(111,"qsmhook: fatal: temporary problem\n"); } +void die_read() { die(111,"qsmhook: fatal: unable to read message\n"); } +void die_badcmd() { die(100,"qsmhook: fatal: command not found\n"); } + +int flagrpline = 0; char *rpline; +int flagufline = 1; char *ufline; +int flagdtline = 0; char *dtline; +char *host; +char *sender; +char *recip; + +stralloc newarg = {0}; + +substdio ssout; +char outbuf[SUBSTDIO_OUTSIZE]; +substdio ssin; +char inbuf[SUBSTDIO_INSIZE]; + +void main(argc,argv) +int argc; +char **argv; +{ + int pid; + int wstat; + int pi[2]; + int opt; + char **arg; + char *x; + int i; + int flagesc; + + sig_pipeignore(); + + if (!(dtline = env_get("DTLINE"))) die_usage(); + if (!(rpline = env_get("RPLINE"))) die_usage(); + if (!(ufline = env_get("UFLINE"))) die_usage(); + if (!(recip = env_get("LOCAL"))) die_usage(); + if (!(host = env_get("HOST"))) die_usage(); + if (!(sender = env_get("SENDER"))) die_usage(); + + while ((opt = getopt(argc,argv,"DFlMmnPsx:")) != opteof) + switch(opt) + { + case 'D': case 'F': case 'M': break; /* be serious */ + case 'l': flagdtline = 1; break; /* also return-receipt-to? blech */ + case 'm': break; /* we only handle one recipient anyway */ + case 'n': flagufline = 0; break; + case 's': break; /* could call quote() otherwise, i suppose... */ + case 'P': flagrpline = 1; break; + case 'x': + if (case_starts(recip,optarg)) + recip += str_len(optarg); + break; + default: + _exit(100); + } + argc -= optind; + argv += optind; + + if (!*argv) die_usage(); + + for (arg = argv;x = *arg;++arg) + { + if (!stralloc_copys(&newarg,"")) die_temp(); + flagesc = 0; + for (i = 0;x[i];++i) + if (flagesc) + { + switch(x[i]) + { + case '%': if (!stralloc_cats(&newarg,"%")) die_temp(); break; + case 'g': if (!stralloc_cats(&newarg,sender)) die_temp(); break; + case 'h': if (!stralloc_cats(&newarg,host)) die_temp(); break; + case 'u': if (!stralloc_cats(&newarg,recip)) die_temp(); break; + } + flagesc = 0; + } + else + if (x[i] == '%') + flagesc = 1; + else + if (!stralloc_append(&newarg,&x[i])) die_temp(); + if (!stralloc_0(&newarg)) die_temp(); + i = str_len(newarg.s) + 1; + if (!(x = alloc(i))) die_temp(); + byte_copy(x,i,newarg.s); + *arg = x; + } + + if (pipe(pi) == -1) die_temp(); + + switch(pid = fork()) + { + case -1: + die_temp(); + case 0: + close(pi[1]); + if (fd_move(0,pi[0]) == -1) die_temp(); + sig_pipedefault(); + execvp(*argv,argv); + if (error_temp(errno)) die_temp(); + die_badcmd(); + } + close(pi[0]); + + substdio_fdbuf(&ssout,write,pi[1],outbuf,sizeof(outbuf)); + substdio_fdbuf(&ssin,read,0,inbuf,sizeof(inbuf)); + if (flagufline) substdio_bputs(&ssout,ufline); + if (flagrpline) substdio_bputs(&ssout,rpline); + if (flagdtline) substdio_bputs(&ssout,dtline); + if (substdio_copy(&ssout,&ssin) == -2) die_read(); + substdio_flush(&ssout); + close(pi[1]); + + if (wait_pid(&wstat,pid) == -1) die_temp(); + if (wait_crashed(wstat)) die_temp(); + _exit(wait_exitcode(wstat)); +} diff --git a/qsutil.c b/qsutil.c new file mode 100644 index 0000000..80c619d --- /dev/null +++ b/qsutil.c @@ -0,0 +1,46 @@ +#include "stralloc.h" +#include "readwrite.h" +#include "substdio.h" +#include "qsutil.h" + +static stralloc foo = {0}; + +static char errbuf[1]; +static struct substdio sserr = SUBSTDIO_FDBUF(write,0,errbuf,1); + +void logsa(sa) stralloc *sa; { + substdio_putflush(&sserr,sa->s,sa->len); } +void log1(s1) char *s1; { + substdio_putsflush(&sserr,s1); } +void log2(s1,s2) char *s1; char *s2; { + substdio_putsflush(&sserr,s1); + substdio_putsflush(&sserr,s2); } +void log3(s1,s2,s3) char *s1; char *s2; char *s3; { + substdio_putsflush(&sserr,s1); + substdio_putsflush(&sserr,s2); + substdio_putsflush(&sserr,s3); } +void nomem() { log1("alert: out of memory, sleeping...\n"); sleep(10); } + +void pausedir(dir) char *dir; +{ log3("alert: unable to opendir ",dir,", sleeping...\n"); sleep(10); } + +static int issafe(ch) char ch; +{ + if (ch == '%') return 0; /* general principle: allman's code is crap */ + if (ch < 33) return 0; + if (ch > 126) return 0; + return 1; +} + +void logsafe(s) char *s; +{ + int i; + while (!stralloc_copys(&foo,s)) nomem(); + for (i = 0;i < foo.len;++i) + if (foo.s[i] == '\n') + foo.s[i] = '/'; + else + if (!issafe(foo.s[i])) + foo.s[i] = '_'; + logsa(&foo); +} diff --git a/qsutil.h b/qsutil.h new file mode 100644 index 0000000..a746845 --- /dev/null +++ b/qsutil.h @@ -0,0 +1,12 @@ +#ifndef QSUTIL_H +#define QSUTIL_H + +extern void log1(); +extern void log2(); +extern void log3(); +extern void logsa(); +extern void nomem(); +extern void pausedir(); +extern void logsafe(); + +#endif diff --git a/quote.c b/quote.c new file mode 100644 index 0000000..659cfcd --- /dev/null +++ b/quote.c @@ -0,0 +1,83 @@ +#include "stralloc.h" +#include "str.h" +#include "quote.h" + +/* +quote() encodes a box as per rfc 821 and rfc 822, +while trying to do as little quoting as possible. +no, 821 and 822 don't have the same encoding. they're not even close. +no special encoding here for bytes above 127. +*/ + +static char ok[128] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +,0,7,0,7,7,7,7,7,0,0,7,7,0,7,7,7 ,7,7,7,7,7,7,7,7,7,7,0,0,0,7,0,7 +,0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 ,7,7,7,7,7,7,7,7,7,7,7,0,0,0,7,7 +,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 ,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,0 +} ; + +static int doit(saout,sain) +stralloc *saout; +stralloc *sain; +{ + char ch; + int i; + int j; + + if (!stralloc_ready(saout,sain->len * 2 + 2)) return 0; + j = 0; + saout->s[j++] = '"'; + for (i = 0;i < sain->len;++i) + { + ch = sain->s[i]; + if ((ch == '\r') || (ch == '\n') || (ch == '"') || (ch == '\\')) + saout->s[j++] = '\\'; + saout->s[j++] = ch; + } + saout->s[j++] = '"'; + saout->len = j; + return 1; +} + +int quote_need(s,n) +char *s; +unsigned int n; +{ + unsigned char uch; + int i; + if (!n) return 1; + for (i = 0;i < n;++i) + { + uch = s[i]; + if (uch >= 128) return 1; + if (!ok[uch]) return 1; + } + if (s[0] == '.') return 1; + if (s[n - 1] == '.') return 1; + for (i = 0;i < n - 1;++i) if (s[i] == '.') if (s[i + 1] == '.') return 1; + return 0; +} + +int quote(saout,sain) +stralloc *saout; +stralloc *sain; +{ + if (quote_need(sain->s,sain->len)) return doit(saout,sain); + return stralloc_copy(saout,sain); +} + +static stralloc foo = {0}; + +int quote2(sa,s) +stralloc *sa; +char *s; +{ + int j; + if (!*s) return stralloc_copys(sa,s); + j = str_rchr(s,'@'); + if (!stralloc_copys(&foo,s)) return 0; + if (!s[j]) return quote(sa,&foo); + foo.len = j; + if (!quote(sa,&foo)) return 0; + return stralloc_cats(sa,s + j); +} diff --git a/quote.h b/quote.h new file mode 100644 index 0000000..8ab7356 --- /dev/null +++ b/quote.h @@ -0,0 +1,8 @@ +#ifndef QUOTE_H +#define QUOTE_H + +extern int quote_need(); +extern int quote(); +extern int quote2(); + +#endif diff --git a/rcpthosts.c b/rcpthosts.c new file mode 100644 index 0000000..1bc3018 --- /dev/null +++ b/rcpthosts.c @@ -0,0 +1,60 @@ +#include "cdb.h" +#include "byte.h" +#include "open.h" +#include "error.h" +#include "control.h" +#include "constmap.h" +#include "stralloc.h" +#include "rcpthosts.h" + +static int flagrh = 0; +static stralloc rh = {0}; +static struct constmap maprh; +static int fdmrh; + +int rcpthosts_init() +{ + flagrh = control_readfile(&rh,"control/rcpthosts",0); + if (flagrh != 1) return flagrh; + if (!constmap_init(&maprh,rh.s,rh.len,0)) return flagrh = -1; + fdmrh = open_read("control/morercpthosts.cdb"); + if (fdmrh == -1) if (errno != error_noent) return flagrh = -1; + return 0; +} + +static stralloc host = {0}; + +int rcpthosts(buf,len) +char *buf; +int len; +{ + int j; + + if (flagrh != 1) return 1; + + j = byte_rchr(buf,len,'@'); + if (j >= len) return 1; /* presumably envnoathost is acceptable */ + + ++j; buf += j; len -= j; + + if (!stralloc_copyb(&host,buf,len)) return -1; + buf = host.s; + case_lowerb(buf,len); + + for (j = 0;j < len;++j) + if (!j || (buf[j] == '.')) + if (constmap(&maprh,buf + j,len - j)) return 1; + + if (fdmrh != -1) { + uint32 dlen; + int r; + + for (j = 0;j < len;++j) + if (!j || (buf[j] == '.')) { + r = cdb_seek(fdmrh,buf + j,len - j,&dlen); + if (r) return r; + } + } + + return 0; +} diff --git a/rcpthosts.h b/rcpthosts.h new file mode 100644 index 0000000..5e7e6cc --- /dev/null +++ b/rcpthosts.h @@ -0,0 +1,7 @@ +#ifndef RCPTHOSTS_H +#define RCPTHOSTS_H + +extern int rcpthosts_init(); +extern int rcpthosts(); + +#endif diff --git a/readsubdir.c b/readsubdir.c new file mode 100644 index 0000000..81aa241 --- /dev/null +++ b/readsubdir.c @@ -0,0 +1,49 @@ +#include "readsubdir.h" +#include "fmt.h" +#include "scan.h" +#include "str.h" +#include "auto_split.h" + +void readsubdir_init(rs,name,pause) +readsubdir *rs; +char *name; +void (*pause)(); +{ + rs->name = name; + rs->pause = pause; + rs->dir = 0; + rs->pos = 0; +} + +static char namepos[FMT_ULONG + 4 + READSUBDIR_NAMELEN]; + +int readsubdir_next(rs,id) +readsubdir *rs; +unsigned long *id; +{ + direntry *d; + unsigned int len; + + if (!rs->dir) + { + if (rs->pos >= auto_split) return 0; + if (str_len(rs->name) > READSUBDIR_NAMELEN) { rs->pos++; return -1; } + len = 0; + len += fmt_str(namepos + len,rs->name); + namepos[len++] = '/'; + len += fmt_ulong(namepos + len,(unsigned long) rs->pos); + namepos[len] = 0; + while (!(rs->dir = opendir(namepos))) rs->pause(namepos); + rs->pos++; + return -1; + } + + d = readdir(rs->dir); + if (!d) { closedir(rs->dir); rs->dir = 0; return -1; } + + if (str_equal(d->d_name,".")) return -1; + if (str_equal(d->d_name,"..")) return -1; + len = scan_ulong(d->d_name,id); + if (!len || d->d_name[len]) return -2; + return 1; +} diff --git a/readsubdir.h b/readsubdir.h new file mode 100644 index 0000000..353942f --- /dev/null +++ b/readsubdir.h @@ -0,0 +1,20 @@ +#ifndef READSUBDIR_H +#define READSUBDIR_H + +#include "direntry.h" + +typedef struct readsubdir + { + DIR *dir; + int pos; + char *name; + void (*pause)(); + } +readsubdir; + +extern void readsubdir_init(); +extern int readsubdir_next(); + +#define READSUBDIR_NAMELEN 10 + +#endif diff --git a/readwrite.h b/readwrite.h new file mode 100644 index 0000000..2a64968 --- /dev/null +++ b/readwrite.h @@ -0,0 +1,7 @@ +#ifndef READWRITE_H +#define READWRITE_H + +extern int read(); +extern int write(); + +#endif diff --git a/received.c b/received.c new file mode 100644 index 0000000..07706d5 --- /dev/null +++ b/received.c @@ -0,0 +1,71 @@ +#include "fmt.h" +#include "qmail.h" +#include "now.h" +#include "datetime.h" +#include "date822fmt.h" +#include "received.h" + +static int issafe(ch) char ch; +{ + if (ch == '.') return 1; + if (ch == '@') return 1; + if (ch == '%') return 1; + if (ch == '+') return 1; + if (ch == '/') return 1; + if (ch == '=') return 1; + if (ch == ':') return 1; + if (ch == '-') return 1; + if ((ch >= 'a') && (ch <= 'z')) return 1; + if ((ch >= 'A') && (ch <= 'Z')) return 1; + if ((ch >= '0') && (ch <= '9')) return 1; + return 0; +} + +void safeput(qqt,s) +struct qmail *qqt; +char *s; +{ + char ch; + while (ch = *s++) { + if (!issafe(ch)) ch = '?'; + qmail_put(qqt,&ch,1); + } +} + +static char buf[DATE822FMT]; + +/* "Received: from relay1.uu.net (HELO uunet.uu.net) (7@192.48.96.5)\n" */ +/* " by silverton.berkeley.edu with SMTP; 26 Sep 1995 04:46:54 -0000\n" */ + +void received(qqt,protocol,local,remoteip,remotehost,remoteinfo,helo) +struct qmail *qqt; +char *protocol; +char *local; +char *remoteip; +char *remotehost; +char *remoteinfo; +char *helo; +{ + struct datetime dt; + + qmail_puts(qqt,"Received: from "); + safeput(qqt,remotehost); + if (helo) { + qmail_puts(qqt," (HELO "); + safeput(qqt,helo); + qmail_puts(qqt,")"); + } + qmail_puts(qqt," ("); + if (remoteinfo) { + safeput(qqt,remoteinfo); + qmail_puts(qqt,"@"); + } + safeput(qqt,remoteip); + qmail_puts(qqt,")\n by "); + safeput(qqt,local); + qmail_puts(qqt," with "); + qmail_puts(qqt,protocol); + qmail_puts(qqt,"; "); + datetime_tai(&dt,now()); + qmail_put(qqt,buf,date822fmt(buf,&dt)); +} diff --git a/received.h b/received.h new file mode 100644 index 0000000..4e39dda --- /dev/null +++ b/received.h @@ -0,0 +1,6 @@ +#ifndef RECEIVED_H +#define RECEIVED_H + +extern void received(); + +#endif diff --git a/remoteinfo.c b/remoteinfo.c new file mode 100644 index 0000000..c7abd70 --- /dev/null +++ b/remoteinfo.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include "byte.h" +#include "substdio.h" +#include "ip.h" +#include "fmt.h" +#include "timeoutconn.h" +#include "timeoutread.h" +#include "timeoutwrite.h" +#include "remoteinfo.h" + +static char line[999]; +static int t; + +static int mywrite(fd,buf,len) int fd; char *buf; int len; +{ + return timeoutwrite(t,fd,buf,len); +} +static int myread(fd,buf,len) int fd; char *buf; int len; +{ + return timeoutread(t,fd,buf,len); +} + +char *remoteinfo_get(ipr,rp,ipl,lp,timeout) +struct ip_address *ipr; +unsigned long rp; +struct ip_address *ipl; +unsigned long lp; +int timeout; +{ + char *x; + int s; + struct sockaddr_in sin; + substdio ss; + char buf[32]; + unsigned int len; + int numcolons; + char ch; + + t = timeout; + + s = socket(AF_INET,SOCK_STREAM,0); + if (s == -1) return 0; + + byte_zero(&sin,sizeof(sin)); + sin.sin_family = AF_INET; + byte_copy(&sin.sin_addr,4,ipl); + sin.sin_port = 0; + if (bind(s,(struct sockaddr *) &sin,sizeof(sin)) == -1) { close(s); return 0; } + if (timeoutconn(s,ipr,113,timeout) == -1) { close(s); return 0; } + fcntl(s,F_SETFL,fcntl(s,F_GETFL,0) & ~O_NDELAY); + + len = 0; + len += fmt_ulong(line + len,rp); + len += fmt_str(line + len," , "); + len += fmt_ulong(line + len,lp); + len += fmt_str(line + len,"\r\n"); + + substdio_fdbuf(&ss,mywrite,s,buf,sizeof buf); + if (substdio_putflush(&ss,line,len) == -1) { close(s); return 0; } + + substdio_fdbuf(&ss,myread,s,buf,sizeof buf); + x = line; + numcolons = 0; + for (;;) { + if (substdio_get(&ss,&ch,1) != 1) { close(s); return 0; } + if ((ch == ' ') || (ch == '\t') || (ch == '\r')) continue; + if (ch == '\n') break; + if (numcolons < 3) { if (ch == ':') ++numcolons; } + else { *x++ = ch; if (x == line + sizeof(line) - 1) break; } + } + *x = 0; + close(s); + return line; +} diff --git a/remoteinfo.h b/remoteinfo.h new file mode 100644 index 0000000..d5d9097 --- /dev/null +++ b/remoteinfo.h @@ -0,0 +1,6 @@ +#ifndef REMOTEINFO_H +#define REMOTEINFO_H + +extern char *remoteinfo_get(); + +#endif diff --git a/scan.h b/scan.h new file mode 100644 index 0000000..53ce703 --- /dev/null +++ b/scan.h @@ -0,0 +1,27 @@ +#ifndef SCAN_H +#define SCAN_H + +extern unsigned int scan_uint(); +extern unsigned int scan_xint(); +extern unsigned int scan_nbbint(); +extern unsigned int scan_ushort(); +extern unsigned int scan_xshort(); +extern unsigned int scan_nbbshort(); +extern unsigned int scan_ulong(); +extern unsigned int scan_xlong(); +extern unsigned int scan_nbblong(); + +extern unsigned int scan_plusminus(); +extern unsigned int scan_0x(); + +extern unsigned int scan_whitenskip(); +extern unsigned int scan_nonwhitenskip(); +extern unsigned int scan_charsetnskip(); +extern unsigned int scan_noncharsetnskip(); + +extern unsigned int scan_strncmp(); +extern unsigned int scan_memcmp(); + +extern unsigned int scan_long(); + +#endif diff --git a/scan_8long.c b/scan_8long.c new file mode 100644 index 0000000..8b3a6df --- /dev/null +++ b/scan_8long.c @@ -0,0 +1,11 @@ +#include "scan.h" + +unsigned int scan_8long(s,u) register char *s; register unsigned long *u; +{ + register unsigned int pos; register unsigned long result; + register unsigned long c; + pos = 0; result = 0; + while ((c = (unsigned long) (unsigned char) (s[pos] - '0')) < 8) + { result = result * 8 + c; ++pos; } + *u = result; return pos; +} diff --git a/scan_ulong.c b/scan_ulong.c new file mode 100644 index 0000000..27c41ea --- /dev/null +++ b/scan_ulong.c @@ -0,0 +1,11 @@ +#include "scan.h" + +unsigned int scan_ulong(s,u) register char *s; register unsigned long *u; +{ + register unsigned int pos; register unsigned long result; + register unsigned long c; + pos = 0; result = 0; + while ((c = (unsigned long) (unsigned char) (s[pos] - '0')) < 10) + { result = result * 10 + c; ++pos; } + *u = result; return pos; +} diff --git a/seek.h b/seek.h new file mode 100644 index 0000000..964fba3 --- /dev/null +++ b/seek.h @@ -0,0 +1,15 @@ +#ifndef SEEK_H +#define SEEK_H + +typedef unsigned long seek_pos; + +extern seek_pos seek_cur(); + +extern int seek_set(); +extern int seek_end(); + +extern int seek_trunc(); + +#define seek_begin(fd) (seek_set((fd),(seek_pos) 0)) + +#endif diff --git a/seek_cur.c b/seek_cur.c new file mode 100644 index 0000000..c8a3ee8 --- /dev/null +++ b/seek_cur.c @@ -0,0 +1,7 @@ +#include +#include "seek.h" + +#define CUR 1 /* sigh */ + +seek_pos seek_cur(fd) int fd; +{ return lseek(fd,(off_t) 0,CUR); } diff --git a/seek_end.c b/seek_end.c new file mode 100644 index 0000000..8a7b3c5 --- /dev/null +++ b/seek_end.c @@ -0,0 +1,7 @@ +#include +#include "seek.h" + +#define END 2 /* sigh */ + +int seek_end(fd) int fd; +{ if (lseek(fd,(off_t) 0,END) == -1) return -1; return 0; } diff --git a/seek_set.c b/seek_set.c new file mode 100644 index 0000000..f540664 --- /dev/null +++ b/seek_set.c @@ -0,0 +1,7 @@ +#include +#include "seek.h" + +#define SET 0 /* sigh */ + +int seek_set(fd,pos) int fd; seek_pos pos; +{ if (lseek(fd,(off_t) pos,SET) == -1) return -1; return 0; } diff --git a/seek_trunc.c b/seek_trunc.c new file mode 100644 index 0000000..6a1a73e --- /dev/null +++ b/seek_trunc.c @@ -0,0 +1,5 @@ +#include +#include "seek.h" + +int seek_trunc(fd,pos) int fd; seek_pos pos; +{ return ftruncate(fd,(off_t) pos); } diff --git a/select.h1 b/select.h1 new file mode 100644 index 0000000..32d0968 --- /dev/null +++ b/select.h1 @@ -0,0 +1,8 @@ +#ifndef SELECT_H +#define SELECT_H + +#include +#include +extern int select(); + +#endif diff --git a/select.h2 b/select.h2 new file mode 100644 index 0000000..eb4b8fe --- /dev/null +++ b/select.h2 @@ -0,0 +1,9 @@ +#ifndef SELECT_H +#define SELECT_H + +#include +#include +#include +extern int select(); + +#endif diff --git a/sendmail.c b/sendmail.c new file mode 100644 index 0000000..b62fef8 --- /dev/null +++ b/sendmail.c @@ -0,0 +1,162 @@ +#include "sgetopt.h" +#include "substdio.h" +#include "subfd.h" +#include "alloc.h" +#include "auto_qmail.h" +#include "exit.h" +#include "env.h" +#include "str.h" + +void nomem() +{ + substdio_putsflush(subfderr,"sendmail: fatal: out of memory\n"); + _exit(111); +} + +void die_usage() +{ + substdio_putsflush(subfderr,"sendmail: usage: sendmail [ -t ] [ -fsender ] [ -Fname ] [ -bp ] [ -bs ] [ arg ... ]\n"); + _exit(100); +} + +char *smtpdarg[] = { "bin/qmail-smtpd", 0 }; +void smtpd() +{ + if (!env_get("PROTO")) { + if (!env_put("RELAYCLIENT=")) nomem(); + if (!env_put("DATABYTES=0")) nomem(); + if (!env_put("PROTO=TCP")) nomem(); + if (!env_put("TCPLOCALIP=127.0.0.1")) nomem(); + if (!env_put("TCPLOCALHOST=localhost")) nomem(); + if (!env_put("TCPREMOTEIP=127.0.0.1")) nomem(); + if (!env_put("TCPREMOTEHOST=localhost")) nomem(); + if (!env_put("TCPREMOTEINFO=sendmail-bs")) nomem(); + } + execv(*smtpdarg,smtpdarg); + substdio_putsflush(subfderr,"sendmail: fatal: unable to run qmail-smtpd\n"); + _exit(111); +} + +char *qreadarg[] = { "bin/qmail-qread", 0 }; +void mailq() +{ + execv(*qreadarg,qreadarg); + substdio_putsflush(subfderr,"sendmail: fatal: unable to run qmail-qread\n"); + _exit(111); +} + +void do_sender(s) +const char *s; +{ + char *x; + int n; + int a; + int i; + + env_unset("QMAILNAME"); + env_unset("MAILNAME"); + env_unset("NAME"); + env_unset("QMAILHOST"); + env_unset("MAILHOST"); + + n = str_len(s); + a = str_rchr(s, '@'); + if (a == n) + { + env_put2("QMAILUSER", s); + return; + } + env_put2("QMAILHOST", s + a + 1); + + x = (char *) alloc((a + 1) * sizeof(char)); + if (!x) nomem(); + for (i = 0; i < a; i++) + x[i] = s[i]; + x[i] = 0; + env_put2("QMAILUSER", x); + alloc_free(x); +} + +int flagh; +char *sender; + +void main(argc,argv) +int argc; +char **argv; +{ + int opt; + char **qiargv; + char **arg; + int i; + + if (chdir(auto_qmail) == -1) { + substdio_putsflush(subfderr,"sendmail: fatal: unable to switch to qmail home directory\n"); + _exit(111); + } + + flagh = 0; + sender = 0; + while ((opt = getopt(argc,argv,"vimte:f:p:o:B:F:EJxb:")) != opteof) + switch(opt) { + case 'B': break; + case 't': flagh = 1; break; + case 'f': sender = optarg; break; + case 'F': if (!env_put2("MAILNAME",optarg)) nomem(); break; + case 'p': break; /* could generate a Received line from optarg */ + case 'v': break; + case 'i': break; /* what an absurd concept */ + case 'x': break; /* SVR4 stupidity */ + case 'm': break; /* twisted-paper-path blindness, incompetent design */ + case 'e': break; /* qmail has only one error mode */ + case 'o': + switch(optarg[0]) { + case 'd': break; /* qmail has only one delivery mode */ + case 'e': break; /* see 'e' above */ + case 'i': break; /* see 'i' above */ + case 'm': break; /* see 'm' above */ + } + break; + case 'E': case 'J': /* Sony NEWS-OS */ + while (argv[optind][optpos]) ++optpos; /* skip optional argument */ + break; + case 'b': + switch(optarg[0]) { + case 'm': break; + case 'p': mailq(); + case 's': smtpd(); + default: die_usage(); + } + break; + default: + die_usage(); + } + argc -= optind; + argv += optind; + + if (str_equal(optprogname,"mailq")) + mailq(); + + if (str_equal(optprogname,"newaliases")) { + substdio_putsflush(subfderr,"sendmail: fatal: please use fastforward/newaliases instead\n"); + _exit(100); + } + + qiargv = (char **) alloc((argc + 10) * sizeof(char *)); + if (!qiargv) nomem(); + + arg = qiargv; + *arg++ = "bin/qmail-inject"; + *arg++ = (flagh ? "-H" : "-a"); + if (sender) { + *arg++ = "-f"; + *arg++ = sender; + do_sender(sender); + } + *arg++ = "--"; + for (i = 0;i < argc;++i) *arg++ = argv[i]; + *arg = 0; + + execv(*qiargv,qiargv); + substdio_putsflush(subfderr,"sendmail: fatal: unable to run qmail-inject\n"); + _exit(111); +} diff --git a/sgetopt.3 b/sgetopt.3 new file mode 100644 index 0000000..bde0c2b --- /dev/null +++ b/sgetopt.3 @@ -0,0 +1,28 @@ +.TH sgetopt 3 +.SH NAME +sgetopt \- get option character from command line +.SH SYNTAX +.B #include +.SH DESCRIPTION +The +.B sgetopt +library is just like the +.B getopt +library, +except that it prints errors using +.B substdio +rather than +.BR stdio . + +See +.B getopt(3) +for interface details. +.SH VERSION +sgetopt version 1.9, 931201. +.SH AUTHOR +Placed into the public domain by Daniel J. Bernstein. +.SH "SEE ALSO" +getopt(3), +subgetopt(3), +subfd(3), +substdio(3) diff --git a/sgetopt.c b/sgetopt.c new file mode 100644 index 0000000..a8bffc0 --- /dev/null +++ b/sgetopt.c @@ -0,0 +1,54 @@ +/* sgetopt.c, sgetopt.h: (yet another) improved getopt clone, outer layer +D. J. Bernstein, djb@pobox.com. +Depends on subgetopt.h, substdio.h, subfd.h. +No system requirements. +19970208: Cleanups. +931201: Baseline. +No known patent problems. + +Documentation in sgetopt.3. +*/ + +#include "substdio.h" +#include "subfd.h" +#define SGETOPTNOSHORT +#include "sgetopt.h" +#define SUBGETOPTNOSHORT +#include "subgetopt.h" + +#define getopt sgetoptmine +#define optind subgetoptind +#define opterr sgetopterr +#define optproblem subgetoptproblem +#define optprogname sgetoptprogname + +int opterr = 1; +char *optprogname = 0; + +int getopt(argc,argv,opts) +int argc; +char **argv; +char *opts; +{ + int c; + char *s; + + if (!optprogname) { + optprogname = *argv; + if (!optprogname) optprogname = ""; + for (s = optprogname;*s;++s) if (*s == '/') optprogname = s + 1; + } + c = subgetopt(argc,argv,opts); + if (opterr) + if (c == '?') { + char chp[2]; chp[0] = optproblem; chp[1] = '\n'; + substdio_puts(subfderr,optprogname); + if (argv[optind] && (optind < argc)) + substdio_puts(subfderr,": illegal option -- "); + else + substdio_puts(subfderr,": option requires an argument -- "); + substdio_put(subfderr,chp,2); + substdio_flush(subfderr); + } + return c; +} diff --git a/sgetopt.h b/sgetopt.h new file mode 100644 index 0000000..5f89127 --- /dev/null +++ b/sgetopt.h @@ -0,0 +1,21 @@ +#ifndef SGETOPT_H +#define SGETOPT_H + +#ifndef SGETOPTNOSHORT +#define getopt sgetoptmine +#define optarg subgetoptarg +#define optind subgetoptind +#define optpos subgetoptpos +#define opterr sgetopterr +#define optproblem subgetoptproblem +#define optprogname sgetoptprogname +#define opteof subgetoptdone +#endif + +#include "subgetopt.h" + +extern int sgetoptmine(); +extern int sgetopterr; +extern char *sgetoptprogname; + +#endif diff --git a/sig.h b/sig.h new file mode 100644 index 0000000..9c3a28c --- /dev/null +++ b/sig.h @@ -0,0 +1,43 @@ +#ifndef SIG_H +#define SIG_H + +extern void sig_catch(); +extern void sig_block(); +extern void sig_unblock(); +extern void sig_blocknone(); +extern void sig_pause(); + +extern void sig_dfl(); + +extern void sig_miscignore(); +extern void sig_bugcatch(); + +extern void sig_pipeignore(); +extern void sig_pipedefault(); + +extern void sig_contblock(); +extern void sig_contunblock(); +extern void sig_contcatch(); +extern void sig_contdefault(); + +extern void sig_termblock(); +extern void sig_termunblock(); +extern void sig_termcatch(); +extern void sig_termdefault(); + +extern void sig_alarmblock(); +extern void sig_alarmunblock(); +extern void sig_alarmcatch(); +extern void sig_alarmdefault(); + +extern void sig_childblock(); +extern void sig_childunblock(); +extern void sig_childcatch(); +extern void sig_childdefault(); + +extern void sig_hangupblock(); +extern void sig_hangupunblock(); +extern void sig_hangupcatch(); +extern void sig_hangupdefault(); + +#endif diff --git a/sig_alarm.c b/sig_alarm.c new file mode 100644 index 0000000..7092fdc --- /dev/null +++ b/sig_alarm.c @@ -0,0 +1,7 @@ +#include +#include "sig.h" + +void sig_alarmblock() { sig_block(SIGALRM); } +void sig_alarmunblock() { sig_unblock(SIGALRM); } +void sig_alarmcatch(f) void (*f)(); { sig_catch(SIGALRM,f); } +void sig_alarmdefault() { sig_catch(SIGALRM,SIG_DFL); } diff --git a/sig_block.c b/sig_block.c new file mode 100644 index 0000000..c6b096a --- /dev/null +++ b/sig_block.c @@ -0,0 +1,40 @@ +#include +#include "sig.h" +#include "hassgprm.h" + +void sig_block(sig) +int sig; +{ +#ifdef HASSIGPROCMASK + sigset_t ss; + sigemptyset(&ss); + sigaddset(&ss,sig); + sigprocmask(SIG_BLOCK,&ss,(sigset_t *) 0); +#else + sigblock(1 << (sig - 1)); +#endif +} + +void sig_unblock(sig) +int sig; +{ +#ifdef HASSIGPROCMASK + sigset_t ss; + sigemptyset(&ss); + sigaddset(&ss,sig); + sigprocmask(SIG_UNBLOCK,&ss,(sigset_t *) 0); +#else + sigsetmask(sigsetmask(~0) & ~(1 << (sig - 1))); +#endif +} + +void sig_blocknone() +{ +#ifdef HASSIGPROCMASK + sigset_t ss; + sigemptyset(&ss); + sigprocmask(SIG_SETMASK,&ss,(sigset_t *) 0); +#else + sigsetmask(0); +#endif +} diff --git a/sig_bug.c b/sig_bug.c new file mode 100644 index 0000000..ae09c07 --- /dev/null +++ b/sig_bug.c @@ -0,0 +1,17 @@ +#include +#include "sig.h" + +void sig_bugcatch(f) void (*f)(); +{ + sig_catch(SIGILL,f); + sig_catch(SIGABRT,f); + sig_catch(SIGFPE,f); + sig_catch(SIGBUS,f); + sig_catch(SIGSEGV,f); +#ifdef SIGSYS + sig_catch(SIGSYS,f); +#endif +#ifdef SIGEMT + sig_catch(SIGEMT,f); +#endif +} diff --git a/sig_catch.c b/sig_catch.c new file mode 100644 index 0000000..1888765 --- /dev/null +++ b/sig_catch.c @@ -0,0 +1,18 @@ +#include +#include "sig.h" +#include "hassgact.h" + +void sig_catch(sig,f) +int sig; +void (*f)(); +{ +#ifdef HASSIGACTION + struct sigaction sa; + sa.sa_handler = f; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sigaction(sig,&sa,(struct sigaction *) 0); +#else + signal(sig,f); /* won't work under System V, even nowadays---dorks */ +#endif +} diff --git a/sig_child.c b/sig_child.c new file mode 100644 index 0000000..fd5b39b --- /dev/null +++ b/sig_child.c @@ -0,0 +1,7 @@ +#include +#include "sig.h" + +void sig_childblock() { sig_block(SIGCHLD); } +void sig_childunblock() { sig_unblock(SIGCHLD); } +void sig_childcatch(f) void (*f)(); { sig_catch(SIGCHLD,f); } +void sig_childdefault() { sig_catch(SIGCHLD,SIG_DFL); } diff --git a/sig_hup.c b/sig_hup.c new file mode 100644 index 0000000..4beb87f --- /dev/null +++ b/sig_hup.c @@ -0,0 +1,7 @@ +#include +#include "sig.h" + +void sig_hangupblock() { sig_block(SIGHUP); } +void sig_hangupunblock() { sig_unblock(SIGHUP); } +void sig_hangupcatch(f) void (*f)(); { sig_catch(SIGHUP,f); } +void sig_hangupdefault() { sig_catch(SIGHUP,SIG_DFL); } diff --git a/sig_misc.c b/sig_misc.c new file mode 100644 index 0000000..287f6cb --- /dev/null +++ b/sig_misc.c @@ -0,0 +1,17 @@ +#include +#include "sig.h" + +void sig_miscignore() +{ + sig_catch(SIGVTALRM,SIG_IGN); + sig_catch(SIGPROF,SIG_IGN); + sig_catch(SIGQUIT,SIG_IGN); + sig_catch(SIGINT,SIG_IGN); + sig_catch(SIGHUP,SIG_IGN); +#ifdef SIGXCPU + sig_catch(SIGXCPU,SIG_IGN); +#endif +#ifdef SIGXFSZ + sig_catch(SIGXFSZ,SIG_IGN); +#endif +} diff --git a/sig_pause.c b/sig_pause.c new file mode 100644 index 0000000..3416734 --- /dev/null +++ b/sig_pause.c @@ -0,0 +1,14 @@ +#include +#include "sig.h" +#include "hassgprm.h" + +void sig_pause() +{ +#ifdef HASSIGPROCMASK + sigset_t ss; + sigemptyset(&ss); + sigsuspend(&ss); +#else + sigpause(0); +#endif +} diff --git a/sig_pipe.c b/sig_pipe.c new file mode 100644 index 0000000..594ae7d --- /dev/null +++ b/sig_pipe.c @@ -0,0 +1,5 @@ +#include +#include "sig.h" + +void sig_pipeignore() { sig_catch(SIGPIPE,SIG_IGN); } +void sig_pipedefault() { sig_catch(SIGPIPE,SIG_DFL); } diff --git a/sig_term.c b/sig_term.c new file mode 100644 index 0000000..ca72cc3 --- /dev/null +++ b/sig_term.c @@ -0,0 +1,7 @@ +#include +#include "sig.h" + +void sig_termblock() { sig_block(SIGTERM); } +void sig_termunblock() { sig_unblock(SIGTERM); } +void sig_termcatch(f) void (*f)(); { sig_catch(SIGTERM,f); } +void sig_termdefault() { sig_catch(SIGTERM,SIG_DFL); } diff --git a/slurpclose.c b/slurpclose.c new file mode 100644 index 0000000..2fcef15 --- /dev/null +++ b/slurpclose.c @@ -0,0 +1,19 @@ +#include "stralloc.h" +#include "readwrite.h" +#include "slurpclose.h" +#include "error.h" + +int slurpclose(fd,sa,bufsize) +int fd; +stralloc *sa; +int bufsize; +{ + int r; + for (;;) { + if (!stralloc_readyplus(sa,bufsize)) { close(fd); return -1; } + r = read(fd,sa->s + sa->len,bufsize); + if (r == -1) if (errno == error_intr) continue; + if (r <= 0) { close(fd); return r; } + sa->len += r; + } +} diff --git a/slurpclose.h b/slurpclose.h new file mode 100644 index 0000000..57e9eec --- /dev/null +++ b/slurpclose.h @@ -0,0 +1,6 @@ +#ifndef SLURPCLOSE_H +#define SLURPCLOSE_H + +extern int slurpclose(); + +#endif diff --git a/spawn.c b/spawn.c new file mode 100644 index 0000000..66475c0 --- /dev/null +++ b/spawn.c @@ -0,0 +1,260 @@ +#include +#include +#include "sig.h" +#include "wait.h" +#include "substdio.h" +#include "byte.h" +#include "str.h" +#include "alloc.h" +#include "stralloc.h" +#include "select.h" +#include "exit.h" +#include "coe.h" +#include "open.h" +#include "error.h" +#include "auto_qmail.h" +#include "auto_uids.h" +#include "auto_spawn.h" + +extern int truncreport; +extern int spawn(); +extern void report(); +extern void initialize(); + +struct delivery + { + int used; + int fdin; /* pipe input */ + int pid; /* zero if child is dead */ + int wstat; /* if !pid: status of child */ + int fdout; /* pipe output, -1 if !pid; delays eof until after death */ + stralloc output; + } +; + +struct delivery *d; + +void sigchld() +{ + int wstat; + int pid; + int i; + while ((pid = wait_nohang(&wstat)) > 0) + for (i = 0;i < auto_spawn;++i) if (d[i].used) + if (d[i].pid == pid) + { + close(d[i].fdout); d[i].fdout = -1; + d[i].wstat = wstat; d[i].pid = 0; + } +} + +int flagwriting = 1; + +int okwrite(fd,buf,n) int fd; char *buf; int n; +{ + int w; + if (!flagwriting) return n; + w = write(fd,buf,n); + if (w != -1) return w; + if (errno == error_intr) return -1; + flagwriting = 0; close(fd); + return n; +} + +int flagreading = 1; +char outbuf[1024]; substdio ssout; + +int stage = 0; /* reading 0:delnum 1:messid 2:sender 3:recip */ +int flagabort = 0; /* if 1, everything except delnum is garbage */ +int delnum; +stralloc messid = {0}; +stralloc sender = {0}; +stralloc recip = {0}; + +void err(s) char *s; +{ + char ch; ch = delnum; substdio_put(&ssout,&ch,1); + substdio_puts(&ssout,s); substdio_putflush(&ssout,"",1); +} + +void docmd() +{ + int f; + int i; + int j; + int fdmess; + int pi[2]; + struct stat st; + + if (flagabort) { err("Zqmail-spawn out of memory. (#4.3.0)\n"); return; } + if (delnum < 0) { err("ZInternal error: delnum negative. (#4.3.5)\n"); return; } + if (delnum >= auto_spawn) { err("ZInternal error: delnum too big. (#4.3.5)\n"); return; } + if (d[delnum].used) { err("ZInternal error: delnum in use. (#4.3.5)\n"); return; } + for (i = 0;i < messid.len;++i) + if (messid.s[i]) + if (!i || (messid.s[i] != '/')) + if ((unsigned char) (messid.s[i] - '0') > 9) + { err("DInternal error: messid has nonnumerics. (#5.3.5)\n"); return; } + if (messid.len > 100) { err("DInternal error: messid too long. (#5.3.5)\n"); return; } + if (!messid.s[0]) { err("DInternal error: messid too short. (#5.3.5)\n"); return; } + + if (!stralloc_copys(&d[delnum].output,"")) + { err("Zqmail-spawn out of memory. (#4.3.0)\n"); return; } + + j = byte_rchr(recip.s,recip.len,'@'); + if (j >= recip.len) { err("DSorry, address must include host name. (#5.1.3)\n"); return; } + + fdmess = open_read(messid.s); + if (fdmess == -1) { err("Zqmail-spawn unable to open message. (#4.3.0)\n"); return; } + + if (fstat(fdmess,&st) == -1) + { close(fdmess); err("Zqmail-spawn unable to fstat message. (#4.3.0)\n"); return; } + if ((st.st_mode & S_IFMT) != S_IFREG) + { close(fdmess); err("ZSorry, message has wrong type. (#4.3.5)\n"); return; } + if (st.st_uid != auto_uidq) /* aaack! qmailq has to be trusted! */ + /* your security is already toast at this point. damage control... */ + { close(fdmess); err("ZSorry, message has wrong owner. (#4.3.5)\n"); return; } + + if (pipe(pi) == -1) + { close(fdmess); err("Zqmail-spawn unable to create pipe. (#4.3.0)\n"); return; } + + coe(pi[0]); + + f = spawn(fdmess,pi[1],sender.s,recip.s,j); + close(fdmess); + if (f == -1) + { close(pi[0]); close(pi[1]); err("Zqmail-spawn unable to fork. (#4.3.0)\n"); return; } + + d[delnum].fdin = pi[0]; + d[delnum].fdout = pi[1]; coe(pi[1]); + d[delnum].pid = f; + d[delnum].used = 1; +} + +char cmdbuf[1024]; + +void getcmd() +{ + int i; + int r; + char ch; + + r = read(0,cmdbuf,sizeof(cmdbuf)); + if (r == 0) + { flagreading = 0; return; } + if (r == -1) + { + if (errno != error_intr) + flagreading = 0; + return; + } + + for (i = 0;i < r;++i) + { + ch = cmdbuf[i]; + switch(stage) + { + case 0: + delnum = (unsigned int) (unsigned char) ch; + messid.len = 0; stage = 1; break; + case 1: + if (!stralloc_append(&messid,&ch)) flagabort = 1; + if (ch) break; + sender.len = 0; stage = 2; break; + case 2: + if (!stralloc_append(&sender,&ch)) flagabort = 1; + if (ch) break; + recip.len = 0; stage = 3; break; + case 3: + if (!stralloc_append(&recip,&ch)) flagabort = 1; + if (ch) break; + docmd(); + flagabort = 0; stage = 0; break; + } + } +} + +char inbuf[128]; + +void main(argc,argv) +int argc; +char **argv; +{ + char ch; + int i; + int r; + fd_set rfds; + int nfds; + + if (chdir(auto_qmail) == -1) _exit(111); + if (chdir("queue/mess") == -1) _exit(111); + if (!stralloc_copys(&messid,"")) _exit(111); + if (!stralloc_copys(&sender,"")) _exit(111); + if (!stralloc_copys(&recip,"")) _exit(111); + + d = (struct delivery *) alloc((auto_spawn + 10) * sizeof(struct delivery)); + if (!d) _exit(111); + + substdio_fdbuf(&ssout,okwrite,1,outbuf,sizeof(outbuf)); + + sig_pipeignore(); + sig_childcatch(sigchld); + + initialize(argc,argv); + + ch = auto_spawn; substdio_putflush(&ssout,&ch,1); + + for (i = 0;i < auto_spawn;++i) { d[i].used = 0; d[i].output.s = 0; } + + for (;;) + { + if (!flagreading) + { + for (i = 0;i < auto_spawn;++i) if (d[i].used) break; + if (i >= auto_spawn) _exit(0); + } + sig_childunblock(); + + FD_ZERO(&rfds); + if (flagreading) FD_SET(0,&rfds); + nfds = 1; + for (i = 0;i < auto_spawn;++i) if (d[i].used) + { FD_SET(d[i].fdin,&rfds); if (d[i].fdin >= nfds) nfds = d[i].fdin + 1; } + + r = select(nfds,&rfds,(fd_set *) 0,(fd_set *) 0,(struct timeval *) 0); + sig_childblock(); + + if (r != -1) + { + if (flagreading) + if (FD_ISSET(0,&rfds)) + getcmd(); + for (i = 0;i < auto_spawn;++i) if (d[i].used) + if (FD_ISSET(d[i].fdin,&rfds)) + { + r = read(d[i].fdin,inbuf,128); + if (r == -1) + continue; /* read error on a readable pipe? be serious */ + if (r == 0) + { + ch = i; substdio_put(&ssout,&ch,1); + report(&ssout,d[i].wstat,d[i].output.s,d[i].output.len); + substdio_put(&ssout,"",1); + substdio_flush(&ssout); + close(d[i].fdin); d[i].used = 0; + continue; + } + while (!stralloc_readyplus(&d[i].output,r)) sleep(10); /*XXX*/ + byte_copy(d[i].output.s + d[i].output.len,r,inbuf); + d[i].output.len += r; + if (truncreport > 100) + if (d[i].output.len > truncreport) + { + char *truncmess = "\nError report too long, sorry.\n"; + d[i].output.len = truncreport - str_len(truncmess) - 3; + stralloc_cats(&d[i].output,truncmess); + } + } + } + } +} diff --git a/splogger.8 b/splogger.8 new file mode 100644 index 0000000..15096b4 --- /dev/null +++ b/splogger.8 @@ -0,0 +1,60 @@ +.TH splogger 8 +.SH NAME +splogger \- make entries in syslog +.SH SYNOPSIS +.B splogger +[ +.I tag +[ +.I fac +] +] +.SH DESCRIPTION +.B splogger +reads a series of messages and feeds them to +.BR syslog . +At the front of each message it puts +.I tag +(default: +.BR splogger ) +and a numerical timestamp. + +.B splogger +checks for +.B alert: +or +.B warning: +at the beginning of each message. +It selects a priority of +LOG_ALERT, LOG_WARNING, or LOG_INFO accordingly. + +.B splogger +logs messages with facility +.IR fac . +.I fac +(default: 2) +must be numeric. + +.B splogger +converts unprintable characters to question marks. + +.B splogger +does not log blank lines. + +.B splogger +folds messages after 800 characters, +since +.B syslog +can't handle long messages. +.B splogger +uses a + after the timestamp +to mark folded lines. + +Note that the +.B syslog +mechanism is inherently unreliable: +it does not guarantee that messages will be logged. +It is also very slow. +.SH "SEE ALSO" +syslog(3), +logger(8) diff --git a/splogger.c b/splogger.c new file mode 100644 index 0000000..fc49a33 --- /dev/null +++ b/splogger.c @@ -0,0 +1,72 @@ +#include +#include +#include +#include "error.h" +#include "substdio.h" +#include "subfd.h" +#include "exit.h" +#include "str.h" +#include "scan.h" +#include "fmt.h" + +char buf[800]; /* syslog truncates long lines (or crashes); GPACIC */ +int bufpos = 0; /* 0 <= bufpos < sizeof(buf) */ +int flagcont = 0; +int priority; /* defined if flagcont */ +char stamp[FMT_ULONG + FMT_ULONG + 3]; /* defined if flagcont */ + +void stamp_make() +{ + struct timeval tv; + char *s; + gettimeofday(&tv,(struct timezone *) 0); + s = stamp; + s += fmt_ulong(s,(unsigned long) tv.tv_sec); + *s++ = '.'; + s += fmt_uint0(s,(unsigned int) tv.tv_usec,6); + *s = 0; +} + +void flush() +{ + if (bufpos) { + buf[bufpos] = 0; + if (flagcont) + syslog(priority,"%s+%s",stamp,buf); /* logger folds invisibly; GPACIC */ + else { + stamp_make(); + priority = LOG_INFO; + if (str_start(buf,"warning:")) priority = LOG_WARNING; + if (str_start(buf,"alert:")) priority = LOG_ALERT; + syslog(priority,"%s %s",stamp,buf); + flagcont = 1; + } + } + bufpos = 0; +} + +void main(argc,argv) +int argc; +char **argv; +{ + char ch; + + if (argv[1]) + if (argv[2]) { + unsigned long facility; + scan_ulong(argv[2],&facility); + openlog(argv[1],0,facility << 3); + } + else + openlog(argv[1],0,LOG_MAIL); + else + openlog("splogger",0,LOG_MAIL); + + for (;;) { + if (substdio_get(subfdin,&ch,1) < 1) _exit(0); + if (ch == '\n') { flush(); flagcont = 0; continue; } + if (bufpos == sizeof(buf) - 1) flush(); + if ((ch < 32) || (ch > 126)) ch = '?'; /* logger truncates at 0; GPACIC */ + buf[bufpos++] = ch; + } +} diff --git a/str.h b/str.h new file mode 100644 index 0000000..e00773c --- /dev/null +++ b/str.h @@ -0,0 +1,14 @@ +#ifndef STR_H +#define STR_H + +extern unsigned int str_copy(); +extern int str_diff(); +extern int str_diffn(); +extern unsigned int str_len(); +extern unsigned int str_chr(); +extern unsigned int str_rchr(); +extern int str_start(); + +#define str_equal(s,t) (!str_diff((s),(t))) + +#endif diff --git a/str_chr.c b/str_chr.c new file mode 100644 index 0000000..3691826 --- /dev/null +++ b/str_chr.c @@ -0,0 +1,19 @@ +#include "str.h" + +unsigned int str_chr(s,c) +register char *s; +int c; +{ + register char ch; + register char *t; + + ch = c; + t = s; + for (;;) { + if (!*t) break; if (*t == ch) break; ++t; + if (!*t) break; if (*t == ch) break; ++t; + if (!*t) break; if (*t == ch) break; ++t; + if (!*t) break; if (*t == ch) break; ++t; + } + return t - s; +} diff --git a/str_cpy.c b/str_cpy.c new file mode 100644 index 0000000..453d790 --- /dev/null +++ b/str_cpy.c @@ -0,0 +1,16 @@ +#include "str.h" + +unsigned int str_copy(s,t) +register char *s; +register char *t; +{ + register int len; + + len = 0; + for (;;) { + if (!(*s = *t)) return len; ++s; ++t; ++len; + if (!(*s = *t)) return len; ++s; ++t; ++len; + if (!(*s = *t)) return len; ++s; ++t; ++len; + if (!(*s = *t)) return len; ++s; ++t; ++len; + } +} diff --git a/str_diff.c b/str_diff.c new file mode 100644 index 0000000..18f8927 --- /dev/null +++ b/str_diff.c @@ -0,0 +1,17 @@ +#include "str.h" + +int str_diff(s,t) +register char *s; +register char *t; +{ + register char x; + + for (;;) { + x = *s; if (x != *t) break; if (!x) break; ++s; ++t; + x = *s; if (x != *t) break; if (!x) break; ++s; ++t; + x = *s; if (x != *t) break; if (!x) break; ++s; ++t; + x = *s; if (x != *t) break; if (!x) break; ++s; ++t; + } + return ((int)(unsigned int)(unsigned char) x) + - ((int)(unsigned int)(unsigned char) *t); +} diff --git a/str_diffn.c b/str_diffn.c new file mode 100644 index 0000000..89142f1 --- /dev/null +++ b/str_diffn.c @@ -0,0 +1,18 @@ +#include "str.h" + +int str_diffn(s,t,len) +register char *s; +register char *t; +unsigned int len; +{ + register char x; + + for (;;) { + if (!len--) return 0; x = *s; if (x != *t) break; if (!x) break; ++s; ++t; + if (!len--) return 0; x = *s; if (x != *t) break; if (!x) break; ++s; ++t; + if (!len--) return 0; x = *s; if (x != *t) break; if (!x) break; ++s; ++t; + if (!len--) return 0; x = *s; if (x != *t) break; if (!x) break; ++s; ++t; + } + return ((int)(unsigned int)(unsigned char) x) + - ((int)(unsigned int)(unsigned char) *t); +} diff --git a/str_len.c b/str_len.c new file mode 100644 index 0000000..2d2f88b --- /dev/null +++ b/str_len.c @@ -0,0 +1,15 @@ +#include "str.h" + +unsigned int str_len(s) +register char *s; +{ + register char *t; + + t = s; + for (;;) { + if (!*t) return t - s; ++t; + if (!*t) return t - s; ++t; + if (!*t) return t - s; ++t; + if (!*t) return t - s; ++t; + } +} diff --git a/str_rchr.c b/str_rchr.c new file mode 100644 index 0000000..1bf19d3 --- /dev/null +++ b/str_rchr.c @@ -0,0 +1,22 @@ +#include "str.h" + +unsigned int str_rchr(s,c) +register char *s; +int c; +{ + register char ch; + register char *t; + register char *u; + + ch = c; + t = s; + u = 0; + for (;;) { + if (!*t) break; if (*t == ch) u = t; ++t; + if (!*t) break; if (*t == ch) u = t; ++t; + if (!*t) break; if (*t == ch) u = t; ++t; + if (!*t) break; if (*t == ch) u = t; ++t; + } + if (!u) u = t; + return u - s; +} diff --git a/str_start.c b/str_start.c new file mode 100644 index 0000000..2750af8 --- /dev/null +++ b/str_start.c @@ -0,0 +1,15 @@ +#include "str.h" + +int str_start(s,t) +register char *s; +register char *t; +{ + register char x; + + for (;;) { + x = *t++; if (!x) return 1; if (x != *s++) return 0; + x = *t++; if (!x) return 1; if (x != *s++) return 0; + x = *t++; if (!x) return 1; if (x != *s++) return 0; + x = *t++; if (!x) return 1; if (x != *s++) return 0; + } +} diff --git a/stralloc.3 b/stralloc.3 new file mode 100644 index 0000000..3123521 --- /dev/null +++ b/stralloc.3 @@ -0,0 +1,160 @@ +.TH stralloc 3 +.SH NAME +stralloc \- dynamically allocated strings +.SH SYNTAX +.B #include + +int \fBstralloc_ready\fP(&\fIsa\fR,\fIlen\fR); +.br +int \fBstralloc_readyplus\fP(&\fIsa\fR,\fIlen\fR); + +int \fBstralloc_copy\fP(&\fIsa\fR,&\fIsa2\fR); +.br +int \fBstralloc_copys\fP(&\fIsa\fR,\fIbuf\fR); +.br +int \fBstralloc_copyb\fP(&\fIsa\fR,\fIbuf\fR,\fIlen\fR); + +int \fBstralloc_cat\fP(&\fIsa\fR,&\fIsa2\fR); +.br +int \fBstralloc_cats\fP(&\fIsa\fR,\fIbuf\fR); +.br +int \fBstralloc_catb\fP(&\fIsa\fR,\fIbuf\fR,\fIlen\fR); + +int \fBstralloc_append\fP(&\fIsa\fR,\fIbuf\fR); +.br +int \fBstralloc_0\fP(&\fIsa\fR); + +int \fBstralloc_starts\fP(&\fIsa\fR,\fIbuf\fR); + +stralloc \fIsa\fR = {0}; +.br +stralloc \fIsa2\fR = {0}; +.br +unsigned int \fIlen\fR; +.br +char *\fIbuf\fR; +.SH DESCRIPTION +A +.B stralloc +variable holds a string in dynamically allocated space. +String length is limited only by memory. +String contents are unrestricted. + +The +.B stralloc +structure has three components: +.I sa\fB.s +is a pointer to the string, or 0 if it is not allocated; +.I sa\fB.len +is the number of bytes in the string, if it is allocated; +.I sa\fB.a +is the number of bytes allocated for the string, if it is allocated. +A +.B stralloc +variable should be initialized to {0}, +meaning unallocated. + +.B stralloc_ready +makes sure that +.I sa +has enough space allocated for +.I len +characters. +It allocates extra space if necessary. + +.B stralloc_readyplus +makes sure that +.I sa +has enough space allocated for +.I len +characters more than its current length. +If +.I sa +is unallocated, +.B stralloc_readyplus +is the same as +.BR stralloc_ready . + +.B stralloc_copy +copies +.I sa2 +to +.IR sa , +allocating space if necessary. +Here +.I sa2 +is an allocated +.B stralloc +variable. + +.B stralloc_copys +copies a 0-terminated string, +.IR buf , +to +.IR sa , +without the 0. + +.B stralloc_copyb +copies +.I len +characters from +.I buf +to +.IR sa . + +.B stralloc_cat +appends +.I sa2 +to +.IR sa , +allocating space if necessary. +If +.I sa +is unallocated, +.B stralloc_cat +is the same as +.BR stralloc_copy . + +.B stralloc_cats +and +.B stralloc_catb +are analogous to +.B stralloc_copys +and +.BR stralloc_copyb . + +.B stralloc_append +adds a single character, +.IR *buf , +to +.IR sa , +allocating space if necessary. + +.B stralloc_0 +adds a single 0 character +to +.IR sa . + +.B stralloc_starts +returns 1 if the 0-terminated string +.IR buf , +without the 0, +is a prefix of +.IR sa . +.SH "ERROR HANDLING" +If a +.B stralloc +routine runs out of memory, +it leaves +.I sa +alone and returns 0, +setting +.B errno +appropriately. +On success it returns 1; +this guarantees that +.I sa +is allocated. +.SH "SEE ALSO" +alloc(3), +error(3) diff --git a/stralloc.h b/stralloc.h new file mode 100644 index 0000000..fca496c --- /dev/null +++ b/stralloc.h @@ -0,0 +1,21 @@ +#ifndef STRALLOC_H +#define STRALLOC_H + +#include "gen_alloc.h" + +GEN_ALLOC_typedef(stralloc,char,s,len,a) + +extern int stralloc_ready(); +extern int stralloc_readyplus(); +extern int stralloc_copy(); +extern int stralloc_cat(); +extern int stralloc_copys(); +extern int stralloc_cats(); +extern int stralloc_copyb(); +extern int stralloc_catb(); +extern int stralloc_append(); /* beware: this takes a pointer to 1 char */ +extern int stralloc_starts(); + +#define stralloc_0(sa) stralloc_append(sa,"") + +#endif diff --git a/stralloc_arts.c b/stralloc_arts.c new file mode 100644 index 0000000..1ccb5a4 --- /dev/null +++ b/stralloc_arts.c @@ -0,0 +1,12 @@ +#include "byte.h" +#include "str.h" +#include "stralloc.h" + +int stralloc_starts(sa,s) +stralloc *sa; +char *s; +{ + int len; + len = str_len(s); + return (sa->len >= len) && byte_equal(s,len,sa->s); +} diff --git a/stralloc_cat.c b/stralloc_cat.c new file mode 100644 index 0000000..efbb112 --- /dev/null +++ b/stralloc_cat.c @@ -0,0 +1,9 @@ +#include "byte.h" +#include "stralloc.h" + +int stralloc_cat(sato,safrom) +stralloc *sato; +stralloc *safrom; +{ + return stralloc_catb(sato,safrom->s,safrom->len); +} diff --git a/stralloc_catb.c b/stralloc_catb.c new file mode 100644 index 0000000..67dbcc0 --- /dev/null +++ b/stralloc_catb.c @@ -0,0 +1,15 @@ +#include "stralloc.h" +#include "byte.h" + +int stralloc_catb(sa,s,n) +stralloc *sa; +char *s; +unsigned int n; +{ + if (!sa->s) return stralloc_copyb(sa,s,n); + if (!stralloc_readyplus(sa,n + 1)) return 0; + byte_copy(sa->s + sa->len,n,s); + sa->len += n; + sa->s[sa->len] = 'Z'; /* ``offensive programming'' */ + return 1; +} diff --git a/stralloc_cats.c b/stralloc_cats.c new file mode 100644 index 0000000..d300286 --- /dev/null +++ b/stralloc_cats.c @@ -0,0 +1,10 @@ +#include "byte.h" +#include "str.h" +#include "stralloc.h" + +int stralloc_cats(sa,s) +stralloc *sa; +char *s; +{ + return stralloc_catb(sa,s,str_len(s)); +} diff --git a/stralloc_copy.c b/stralloc_copy.c new file mode 100644 index 0000000..652aed6 --- /dev/null +++ b/stralloc_copy.c @@ -0,0 +1,9 @@ +#include "byte.h" +#include "stralloc.h" + +int stralloc_copy(sato,safrom) +stralloc *sato; +stralloc *safrom; +{ + return stralloc_copyb(sato,safrom->s,safrom->len); +} diff --git a/stralloc_eady.c b/stralloc_eady.c new file mode 100644 index 0000000..3a31f4b --- /dev/null +++ b/stralloc_eady.c @@ -0,0 +1,6 @@ +#include "alloc.h" +#include "stralloc.h" +#include "gen_allocdefs.h" + +GEN_ALLOC_ready(stralloc,char,s,len,a,i,n,x,30,stralloc_ready) +GEN_ALLOC_readyplus(stralloc,char,s,len,a,i,n,x,30,stralloc_readyplus) diff --git a/stralloc_opyb.c b/stralloc_opyb.c new file mode 100644 index 0000000..ac258b3 --- /dev/null +++ b/stralloc_opyb.c @@ -0,0 +1,14 @@ +#include "stralloc.h" +#include "byte.h" + +int stralloc_copyb(sa,s,n) +stralloc *sa; +char *s; +unsigned int n; +{ + if (!stralloc_ready(sa,n + 1)) return 0; + byte_copy(sa->s,n,s); + sa->len = n; + sa->s[n] = 'Z'; /* ``offensive programming'' */ + return 1; +} diff --git a/stralloc_opys.c b/stralloc_opys.c new file mode 100644 index 0000000..fdd7807 --- /dev/null +++ b/stralloc_opys.c @@ -0,0 +1,10 @@ +#include "byte.h" +#include "str.h" +#include "stralloc.h" + +int stralloc_copys(sa,s) +stralloc *sa; +char *s; +{ + return stralloc_copyb(sa,s,str_len(s)); +} diff --git a/stralloc_pend.c b/stralloc_pend.c new file mode 100644 index 0000000..a3443b8 --- /dev/null +++ b/stralloc_pend.c @@ -0,0 +1,5 @@ +#include "alloc.h" +#include "stralloc.h" +#include "gen_allocdefs.h" + +GEN_ALLOC_append(stralloc,char,s,len,a,i,n,x,30,stralloc_readyplus,stralloc_append) diff --git a/strerr.h b/strerr.h new file mode 100644 index 0000000..d18e833 --- /dev/null +++ b/strerr.h @@ -0,0 +1,80 @@ +#ifndef STRERR_H +#define STRERR_H + +struct strerr + { + struct strerr *who; + char *x; + char *y; + char *z; + } +; + +extern struct strerr strerr_sys; +extern void strerr_sysinit(); + +extern char *strerr(); +extern void strerr_warn(); +extern void strerr_die(); + +#define STRERR(r,se,a) \ +{ se.who = 0; se.x = a; se.y = 0; se.z = 0; return r; } + +#define STRERR_SYS(r,se,a) \ +{ se.who = &strerr_sys; se.x = a; se.y = 0; se.z = 0; return r; } +#define STRERR_SYS3(r,se,a,b,c) \ +{ se.who = &strerr_sys; se.x = a; se.y = b; se.z = c; return r; } + +#define strerr_warn6(x1,x2,x3,x4,x5,x6,se) \ +strerr_warn((x1),(x2),(x3),(x4),(x5),(x6),(struct strerr *) (se)) +#define strerr_warn5(x1,x2,x3,x4,x5,se) \ +strerr_warn((x1),(x2),(x3),(x4),(x5),(char *) 0,(struct strerr *) (se)) +#define strerr_warn4(x1,x2,x3,x4,se) \ +strerr_warn((x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(struct strerr *) (se)) +#define strerr_warn3(x1,x2,x3,se) \ +strerr_warn((x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se)) +#define strerr_warn2(x1,x2,se) \ +strerr_warn((x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se)) +#define strerr_warn1(x1,se) \ +strerr_warn((x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se)) + +#define strerr_die6(e,x1,x2,x3,x4,x5,x6,se) \ +strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(struct strerr *) (se)) +#define strerr_die5(e,x1,x2,x3,x4,x5,se) \ +strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,(struct strerr *) (se)) +#define strerr_die4(e,x1,x2,x3,x4,se) \ +strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(struct strerr *) (se)) +#define strerr_die3(e,x1,x2,x3,se) \ +strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se)) +#define strerr_die2(e,x1,x2,se) \ +strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se)) +#define strerr_die1(e,x1,se) \ +strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se)) + +#define strerr_die6sys(e,x1,x2,x3,x4,x5,x6) \ +strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),&strerr_sys) +#define strerr_die5sys(e,x1,x2,x3,x4,x5) \ +strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,&strerr_sys) +#define strerr_die4sys(e,x1,x2,x3,x4) \ +strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,&strerr_sys) +#define strerr_die3sys(e,x1,x2,x3) \ +strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,&strerr_sys) +#define strerr_die2sys(e,x1,x2) \ +strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,&strerr_sys) +#define strerr_die1sys(e,x1) \ +strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,&strerr_sys) + +#define strerr_die6x(e,x1,x2,x3,x4,x5,x6) \ +strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(struct strerr *) 0) +#define strerr_die5x(e,x1,x2,x3,x4,x5) \ +strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,(struct strerr *) 0) +#define strerr_die4x(e,x1,x2,x3,x4) \ +strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(struct strerr *) 0) +#define strerr_die3x(e,x1,x2,x3) \ +strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0) +#define strerr_die2x(e,x1,x2) \ +strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0) +#define strerr_die1x(e,x1) \ +strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0) + +#endif diff --git a/strerr_die.c b/strerr_die.c new file mode 100644 index 0000000..6092020 --- /dev/null +++ b/strerr_die.c @@ -0,0 +1,37 @@ +#include "substdio.h" +#include "subfd.h" +#include "exit.h" +#include "strerr.h" + +void strerr_warn(x1,x2,x3,x4,x5,x6,se) +char *x1; char *x2; char *x3; char *x4; char *x5; char *x6; +struct strerr *se; +{ + strerr_sysinit(); + + if (x1) substdio_puts(subfderr,x1); + if (x2) substdio_puts(subfderr,x2); + if (x3) substdio_puts(subfderr,x3); + if (x4) substdio_puts(subfderr,x4); + if (x5) substdio_puts(subfderr,x5); + if (x6) substdio_puts(subfderr,x6); + + while(se) { + if (se->x) substdio_puts(subfderr,se->x); + if (se->y) substdio_puts(subfderr,se->y); + if (se->z) substdio_puts(subfderr,se->z); + se = se->who; + } + + substdio_puts(subfderr,"\n"); + substdio_flush(subfderr); +} + +void strerr_die(e,x1,x2,x3,x4,x5,x6,se) +int e; +char *x1; char *x2; char *x3; char *x4; char *x5; char *x6; +struct strerr *se; +{ + strerr_warn(x1,x2,x3,x4,x5,x6,se); + _exit(e); +} diff --git a/strerr_sys.c b/strerr_sys.c new file mode 100644 index 0000000..198198b --- /dev/null +++ b/strerr_sys.c @@ -0,0 +1,12 @@ +#include "error.h" +#include "strerr.h" + +struct strerr strerr_sys; + +void strerr_sysinit() +{ + strerr_sys.who = 0; + strerr_sys.x = error_str(errno); + strerr_sys.y = ""; + strerr_sys.z = ""; +} diff --git a/subfd.h b/subfd.h new file mode 100644 index 0000000..bcb2e1e --- /dev/null +++ b/subfd.h @@ -0,0 +1,15 @@ +#ifndef SUBFD_H +#define SUBFD_H + +#include "substdio.h" + +extern substdio *subfdin; +extern substdio *subfdinsmall; +extern substdio *subfdout; +extern substdio *subfdoutsmall; +extern substdio *subfderr; + +extern int subfd_read(); +extern int subfd_readsmall(); + +#endif diff --git a/subfderr.c b/subfderr.c new file mode 100644 index 0000000..011ab0f --- /dev/null +++ b/subfderr.c @@ -0,0 +1,7 @@ +#include "readwrite.h" +#include "substdio.h" +#include "subfd.h" + +char subfd_errbuf[256]; +static substdio it = SUBSTDIO_FDBUF(write,2,subfd_errbuf,256); +substdio *subfderr = ⁢ diff --git a/subfdin.c b/subfdin.c new file mode 100644 index 0000000..a11d323 --- /dev/null +++ b/subfdin.c @@ -0,0 +1,13 @@ +#include "readwrite.h" +#include "substdio.h" +#include "subfd.h" + +int subfd_read(fd,buf,len) int fd; char *buf; int len; +{ + if (substdio_flush(subfdout) == -1) return -1; + return read(fd,buf,len); +} + +char subfd_inbuf[SUBSTDIO_INSIZE]; +static substdio it = SUBSTDIO_FDBUF(subfd_read,0,subfd_inbuf,SUBSTDIO_INSIZE); +substdio *subfdin = ⁢ diff --git a/subfdins.c b/subfdins.c new file mode 100644 index 0000000..36983ac --- /dev/null +++ b/subfdins.c @@ -0,0 +1,13 @@ +#include "readwrite.h" +#include "substdio.h" +#include "subfd.h" + +int subfd_readsmall(fd,buf,len) int fd; char *buf; int len; +{ + if (substdio_flush(subfdoutsmall) == -1) return -1; + return read(fd,buf,len); +} + +char subfd_inbufsmall[256]; +static substdio it = SUBSTDIO_FDBUF(subfd_readsmall,0,subfd_inbufsmall,256); +substdio *subfdinsmall = ⁢ diff --git a/subfdout.c b/subfdout.c new file mode 100644 index 0000000..0aee102 --- /dev/null +++ b/subfdout.c @@ -0,0 +1,7 @@ +#include "readwrite.h" +#include "substdio.h" +#include "subfd.h" + +char subfd_outbuf[SUBSTDIO_OUTSIZE]; +static substdio it = SUBSTDIO_FDBUF(write,1,subfd_outbuf,SUBSTDIO_OUTSIZE); +substdio *subfdout = ⁢ diff --git a/subfdouts.c b/subfdouts.c new file mode 100644 index 0000000..5be356d --- /dev/null +++ b/subfdouts.c @@ -0,0 +1,7 @@ +#include "readwrite.h" +#include "substdio.h" +#include "subfd.h" + +char subfd_outbufsmall[256]; +static substdio it = SUBSTDIO_FDBUF(write,1,subfd_outbufsmall,256); +substdio *subfdoutsmall = ⁢ diff --git a/subgetopt.3 b/subgetopt.3 new file mode 100644 index 0000000..aae03aa --- /dev/null +++ b/subgetopt.3 @@ -0,0 +1,357 @@ +.TH subgetopt 3 +.SH NAME +subgetopt \- get option character from command line +.SH SYNTAX +.B #include + +char *\fBsgoptarg\fP; +.br +int \fBsgoptind\fP; +.br +int \fBsgoptpos\fP; +.br +int \fBsgoptdone\fP; +.br +int \fBsgoptproblem\fP; + +int \fBsgopt(\fP\fIargc,argv,opts\fR\fB)\fP; + +int \fIargc\fR; +.br +char **\fIargv\fR; +.br +char *\fIopts\fR; +.SH DESCRIPTION +.B sgopt +returns the next valid command-line option character +from +.IR argv . + +Valid option characters are listed in the +.I opts +string. +.I opts +may be empty. +A character in +.I opts +may be followed by a colon, +in which case it +takes an +.I option argument\fR. +Avoid using the characters ?, :, and \- as option characters. + +Below +.I option argument +is abbreviated +as +.I optarg +and +.I command-line argument +is abbreviated as +.IR cmdarg . + +Options are listed in cmdargs which begin with +a minus sign. +Several options which do not take optargs may be combined +into one cmdarg. + +An option which takes an optarg may be handled in two ways. +If it appears at the very end of a cmdarg, +then the entire next cmdarg is the optarg. +But if there are any characters in the cmdarg +after the option character, +then those characters form the optarg. +The optarg is returned in +.BR sgoptarg . +Next time +.B sgopt +looks at the cmdarg which follows the optarg. + +If a cmdarg does not begin with a hyphen, +or if it is a lone hyphen not followed by any characters, +or if it begins with two hyphens, +then it terminates option processing, +and +.B sgopt +returns an appropriate code. +If there are two hyphens, +.B sgopt +will advance attention to the next cmdarg, +so it can be called again to read further options. +.SH "PROPER USAGE" +.B sgoptproblem +should be used only when +.B sgopt +returns ?. +.B sgoptind +and +.B sgoptpos +are defined all the time. +.B sgoptarg +is defined all the time; +it is null unless +.B sgopt +has just returned an option with optarg. + +.B sgopt +is typically used as follows. + +.EX +#include + +main(argc,argv) int argc; char **argv; { int opt; + +while ((opt = sgopt(argc,argv,"a:s")) != sgoptdone) +.br + switch(opt) { +.br + case 'a': +.br + printf("opt a with optarg %s\\n",sgoptarg); break; +.br + case 's': +.br + printf("opt s with no optarg\\n"); break; +.br + case '?': +.br + if (argv[sgoptind] && (sgoptind < argc)) +.br + printf("illegal opt %c\\n",sgoptproblem); +.br + else +.br + printf("missing arg, opt %c\\n",sgoptproblem); +.br + exit(1); +.br + } + +argv += sgoptind; +.br +while (*argv) printf("argument %s\\n",*argv++); +.br +exit(0); +.br +} +.EE + +The end of the command line is +marked by either +.IR argc , +or a null pointer in +.IR argv , +whichever comes first. +Normally +these two markers coincide, +so it is redundant +to test for +both +.I argv\fB[sgoptind] +and +.B sgoptind < \fIargc\fR. +The above code shows both tests as an illustration. + +.B Multiple option sets: +One useful technique is to call +.B sgopt +with a primary +.I opts +until it returns EOF, +then call +.B sgopt +with a secondary +.I opts +until it returns EOF. +The user can provide primary options, then a double hyphen, +and then secondary options. +No special handling is needed if some or all of the options are +omitted. +The same technique can be used for any number of option sets +in series. + +.B Multiple command lines: +Before parsing a new +.BR argv , +make sure to +set +.B sgoptind +and +.B sgoptpos +back to +1 and 0. +.SH "PARSING STAGES" +.B sgopt +keeps track of its position in +.I argv +with +.B sgoptind +and +.BR sgoptpos , +which are initialized to 1 and 0. +It looks at +.I argv\fB[sgoptind][sgoptpos] +and following characters. + +.B sgopt +indicates +that no more options are available by +returning +.BR sgoptdone , +which is initialized to +.BR SUBGETOPTDONE , +which is defined as \-1. + +.B sgopt +begins by setting +.B optarg +to null. + +.B Ending conditions: +If +.I argv +is null, or +.B sgoptind +is larger than +.IR argc , +or the current cmdarg +.I argv\fB[sgoptind] +is null, +then +.B sgopt +returns +.BR optdone . + +.B Stage one: +If the current character +is zero, +.B sgopt +moves to the beginning of the next cmdarg. +It then checks the ending conditions again. + +.B Stage two: +If +the current position is the begining of the cmdarg, +.B sgopt +checks whether +the current character +is a minus sign. +If not it returns +.BR optdone . +It then +moves +to the next character. +If that character is zero, +.B sgopt +moves +back to the beginning of the cmdarg, +and returns +.BR sgoptdone . +If the character is a minus sign, +.B sgopt +moves to the beginning of the next cmdarg, +and returns +.BR sgoptdone . + +.B Stage three: +.B sgopt +records the current character, +.IR c , +and moves to the next character. +There are three possibilities: +(1) +.I c +is an option character without optarg in +.IR opts , +or +(2) +.I c +is an option character with optarg in +.IR opts , +or +(3) +.I c +does not appear in +.IR opts . + +(1) +If +.I c +appears as an option character without optarg in +.IR opts , +.B sgopt +returns +.IR c . + +(2) +If +.I c +appears as an option character with optarg in +.IR opts , +.B sgopt +sets +.B sgoptarg +to the current position, +and moves to the next cmdarg. +If +.B sgoptarg +is nonempty, +.B sgopt +returns +.IR c . + +Then +.B sgopt +sets +.B sgoptarg +to +the current cmdarg. +If +the current cmdarg is null, +or past +.IR argc , +.B sgopt +sets +.B sgoptproblem +to +.I c +and returns ?. +Otherwise +.B sgopt +moves to the next +argument +and returns +.IR c . + +(2) +If +.I c +does not appear in +.IR opts , +.B sgopt +sets +.B sgoptproblem +to +.I c +and returns ?. +.SH "SYNTAX NOTE" +.B sgopt +is actually a macro abbreviation for +.BR subgetopt . +The external +.B sg +variables are also macros +for +.BR subget . +These macros are defined in +.BR , +unless +.B SUBGETOPTNOSHORT +is defined +when +.B +is included. +.SH VERSION +subgetopt version 0.9, 931129. +.SH AUTHOR +Placed into the public domain by Daniel J. Bernstein. diff --git a/subgetopt.c b/subgetopt.c new file mode 100644 index 0000000..dacf376 --- /dev/null +++ b/subgetopt.c @@ -0,0 +1,79 @@ +/* subgetopt.c, subgetopt.h: (yet another) improved getopt clone, inner layer +D. J. Bernstein, djb@pobox.com. +No dependencies. +No system requirements. +19970228: Cleanups. +931129: Adapted from getopt.c. +No known patent problems. + +Documentation in subgetopt.3. +*/ + +#define SUBGETOPTNOSHORT +#include "subgetopt.h" + +#define sgopt subgetopt +#define optind subgetoptind +#define optpos subgetoptpos +#define optarg subgetoptarg +#define optproblem subgetoptproblem +#define optdone subgetoptdone + +int optind = 1; +int optpos = 0; +char *optarg = 0; +int optproblem = 0; +int optdone = SUBGETOPTDONE; + +int sgopt(argc,argv,opts) +int argc; +char **argv; +char *opts; +{ + int c; + char *s; + + optarg = 0; + if (!argv || (optind >= argc) || !argv[optind]) return optdone; + if (optpos && !argv[optind][optpos]) { + ++optind; + optpos = 0; + if ((optind >= argc) || !argv[optind]) return optdone; + } + if (!optpos) { + if (argv[optind][0] != '-') return optdone; + ++optpos; + c = argv[optind][1]; + if ((c == '-') || (c == 0)) { + if (c) ++optind; + optpos = 0; + return optdone; + } + /* otherwise c is reassigned below */ + } + c = argv[optind][optpos]; + ++optpos; + s = opts; + while (*s) { + if (c == *s) { + if (s[1] == ':') { + optarg = argv[optind] + optpos; + ++optind; + optpos = 0; + if (!*optarg) { + optarg = argv[optind]; + if ((optind >= argc) || !optarg) { /* argument past end */ + optproblem = c; + return '?'; + } + ++optind; + } + } + return c; + } + ++s; + if (*s == ':') ++s; + } + optproblem = c; + return '?'; +} diff --git a/subgetopt.h b/subgetopt.h new file mode 100644 index 0000000..d26c62a --- /dev/null +++ b/subgetopt.h @@ -0,0 +1,24 @@ +#ifndef SUBGETOPT_H +#define SUBGETOPT_H + +#ifndef SUBGETOPTNOSHORT +#define sgopt subgetopt +#define sgoptarg subgetoptarg +#define sgoptind subgetoptind +#define sgoptpos subgetoptpos +#define sgoptproblem subgetoptproblem +#define sgoptprogname subgetoptprogname +#define sgoptdone subgetoptdone +#endif + +#define SUBGETOPTDONE -1 + +extern int subgetopt(); +extern char *subgetoptarg; +extern int subgetoptind; +extern int subgetoptpos; +extern int subgetoptproblem; +extern char *subgetoptprogname; +extern int subgetoptdone; + +#endif diff --git a/substdi.c b/substdi.c new file mode 100644 index 0000000..42407a1 --- /dev/null +++ b/substdi.c @@ -0,0 +1,91 @@ +#include "substdio.h" +#include "byte.h" +#include "error.h" + +static int oneread(op,fd,buf,len) +register int (*op)(); +register int fd; +register char *buf; +register int len; +{ + register int r; + + for (;;) { + r = op(fd,buf,len); + if (r == -1) if (errno == error_intr) continue; + return r; + } +} + +static int getthis(s,buf,len) +register substdio *s; +register char *buf; +register int len; +{ + register int r; + register int q; + + r = s->p; + q = r - len; + if (q > 0) { r = len; s->p = q; } else s->p = 0; + byte_copy(buf,r,s->x + s->n); + s->n += r; + return r; +} + +int substdio_feed(s) +register substdio *s; +{ + register int r; + register int q; + + if (s->p) return s->p; + q = s->n; + r = oneread(s->op,s->fd,s->x,q); + if (r <= 0) return r; + s->p = r; + q -= r; + s->n = q; + if (q > 0) /* damn, gotta shift */ byte_copyr(s->x + q,r,s->x); + return r; +} + +int substdio_bget(s,buf,len) +register substdio *s; +register char *buf; +register int len; +{ + register int r; + + if (s->p > 0) return getthis(s,buf,len); + r = s->n; if (r <= len) return oneread(s->op,s->fd,buf,r); + r = substdio_feed(s); if (r <= 0) return r; + return getthis(s,buf,len); +} + +int substdio_get(s,buf,len) +register substdio *s; +register char *buf; +register int len; +{ + register int r; + + if (s->p > 0) return getthis(s,buf,len); + if (s->n <= len) return oneread(s->op,s->fd,buf,len); + r = substdio_feed(s); if (r <= 0) return r; + return getthis(s,buf,len); +} + +char *substdio_peek(s) +register substdio *s; +{ + return s->x + s->n; +} + +void substdio_seek(s,len) +register substdio *s; +register int len; +{ + s->n += len; + s->p -= len; +} diff --git a/substdio.c b/substdio.c new file mode 100644 index 0000000..d03dff2 --- /dev/null +++ b/substdio.c @@ -0,0 +1,15 @@ +#include "substdio.h" + +void substdio_fdbuf(s,op,fd,buf,len) +register substdio *s; +register int (*op)(); +register int fd; +register char *buf; +register int len; +{ + s->x = buf; + s->fd = fd; + s->op = op; + s->p = 0; + s->n = len; +} diff --git a/substdio.h b/substdio.h new file mode 100644 index 0000000..c3f7f7d --- /dev/null +++ b/substdio.h @@ -0,0 +1,47 @@ +#ifndef SUBSTDIO_H +#define SUBSTDIO_H + +typedef struct substdio { + char *x; + int p; + int n; + int fd; + int (*op)(); +} substdio; + +#define SUBSTDIO_FDBUF(op,fd,buf,len) { (buf), 0, (len), (fd), (op) } + +extern void substdio_fdbuf(); + +extern int substdio_flush(); +extern int substdio_put(); +extern int substdio_bput(); +extern int substdio_putflush(); +extern int substdio_puts(); +extern int substdio_bputs(); +extern int substdio_putsflush(); + +extern int substdio_get(); +extern int substdio_bget(); +extern int substdio_feed(); + +extern char *substdio_peek(); +extern void substdio_seek(); + +#define substdio_fileno(s) ((s)->fd) + +#define SUBSTDIO_INSIZE 8192 +#define SUBSTDIO_OUTSIZE 8192 + +#define substdio_PEEK(s) ( (s)->x + (s)->n ) +#define substdio_SEEK(s,len) ( ( (s)->p -= (len) ) , ( (s)->n += (len) ) ) + +#define substdio_BPUTC(s,c) \ + ( ((s)->n != (s)->p) \ + ? ( (s)->x[(s)->p++] = (c), 0 ) \ + : substdio_bput((s),&(c),1) \ + ) + +extern int substdio_copy(); + +#endif diff --git a/substdio_copy.c b/substdio_copy.c new file mode 100644 index 0000000..71cf200 --- /dev/null +++ b/substdio_copy.c @@ -0,0 +1,18 @@ +#include "substdio.h" + +int substdio_copy(ssout,ssin) +register substdio *ssout; +register substdio *ssin; +{ + register int n; + register char *x; + + for (;;) { + n = substdio_feed(ssin); + if (n < 0) return -2; + if (!n) return 0; + x = substdio_PEEK(ssin); + if (substdio_put(ssout,x,n) == -1) return -3; + substdio_SEEK(ssin,n); + } +} diff --git a/substdo.c b/substdo.c new file mode 100644 index 0000000..fb616f7 --- /dev/null +++ b/substdo.c @@ -0,0 +1,108 @@ +#include "substdio.h" +#include "str.h" +#include "byte.h" +#include "error.h" + +static int allwrite(op,fd,buf,len) +register int (*op)(); +register int fd; +register char *buf; +register int len; +{ + register int w; + + while (len) { + w = op(fd,buf,len); + if (w == -1) { + if (errno == error_intr) continue; + return -1; /* note that some data may have been written */ + } + if (w == 0) ; /* luser's fault */ + buf += w; + len -= w; + } + return 0; +} + +int substdio_flush(s) +register substdio *s; +{ + register int p; + + p = s->p; + if (!p) return 0; + s->p = 0; + return allwrite(s->op,s->fd,s->x,p); +} + +int substdio_bput(s,buf,len) +register substdio *s; +register char *buf; +register int len; +{ + register int n; + + while (len > (n = s->n - s->p)) { + byte_copy(s->x + s->p,n,buf); s->p += n; buf += n; len -= n; + if (substdio_flush(s) == -1) return -1; + } + /* now len <= s->n - s->p */ + byte_copy(s->x + s->p,len,buf); + s->p += len; + return 0; +} + +int substdio_put(s,buf,len) +register substdio *s; +register char *buf; +register int len; +{ + register int n; + + n = s->n; + if (len > n - s->p) { + if (substdio_flush(s) == -1) return -1; + /* now s->p == 0 */ + if (n < SUBSTDIO_OUTSIZE) n = SUBSTDIO_OUTSIZE; + while (len > s->n) { + if (n > len) n = len; + if (allwrite(s->op,s->fd,buf,n) == -1) return -1; + buf += n; + len -= n; + } + } + /* now len <= s->n - s->p */ + byte_copy(s->x + s->p,len,buf); + s->p += len; + return 0; +} + +int substdio_putflush(s,buf,len) +register substdio *s; +register char *buf; +register int len; +{ + if (substdio_flush(s) == -1) return -1; + return allwrite(s->op,s->fd,buf,len); +} + +int substdio_bputs(s,buf) +register substdio *s; +register char *buf; +{ + return substdio_bput(s,buf,str_len(buf)); +} + +int substdio_puts(s,buf) +register substdio *s; +register char *buf; +{ + return substdio_put(s,buf,str_len(buf)); +} + +int substdio_putsflush(s,buf) +register substdio *s; +register char *buf; +{ + return substdio_putflush(s,buf,str_len(buf)); +} diff --git a/tcp-env.1 b/tcp-env.1 new file mode 100644 index 0000000..edd46f2 --- /dev/null +++ b/tcp-env.1 @@ -0,0 +1,67 @@ +.TH tcp-env 1 +.SH NAME +tcp-env \- set up TCP-related environment variables +.SH SYNOPSIS +.B tcp-env +[ +.B \-rR +] +[ +.B \-t\fItimeout +] +.I program +[ +.I arg ... +] +.SH DESCRIPTION +The input for +.B tcp-env +must be a TCP connection. +.B tcp-env +finds out information about that connection, +puts the information into several environment variables +as described in +.B tcp-environ(5), +and runs +.I program +with the given arguments. + +Usually +.B tcp-env +is run from +.BR inetd . +It might instead be run from another server +that already sets up the right environment variables; +if +.B PROTO +is set to +.B TCP +when +.B tcp-env +is invoked, +.B tcp-env +assumes that all the other variables are set up properly, +and it does not check whether the input is a TCP connection. +.SH OPTIONS +.TP +.B \-r +(Default.) +Attempt to obtain +.B TCPREMOTEINFO +from the remote host. +.TP +.B \-R +Do not attempt to obtain +.B TCPREMOTEINFO +from the remote host. +.TP +.B \-t\fItimeout +Give up on the +.B TCPREMOTEINFO +connection attempt after +.I timeout +seconds. +Default: 30. +.SH "SEE ALSO" +tcp-environ(5), +inetd(8) diff --git a/tcp-env.c b/tcp-env.c new file mode 100644 index 0000000..feb85cc --- /dev/null +++ b/tcp-env.c @@ -0,0 +1,129 @@ +#include +#include +#include +#include +#include "sig.h" +#include "stralloc.h" +#include "str.h" +#include "env.h" +#include "fmt.h" +#include "scan.h" +#include "subgetopt.h" +#include "ip.h" +#include "dns.h" +#include "byte.h" +#include "remoteinfo.h" +#include "exit.h" +#include "case.h" + +void die() { _exit(111); } + +struct sockaddr_in salocal; +unsigned long localport; +struct ip_address iplocal; +stralloc localname = {0}; + +struct sockaddr_in saremote; +unsigned long remoteport; +struct ip_address ipremote; +stralloc remotename = {0}; + +char temp[IPFMT + FMT_ULONG]; + +void main(argc,argv) +int argc; +char *argv[]; +{ + int dummy; + char *proto; + int opt; + int flagremoteinfo; + unsigned long timeout; + + sig_pipeignore(); + + flagremoteinfo = 1; + timeout = 30; + while ((opt = sgopt(argc,argv,"rRt:")) != sgoptdone) + switch(opt) + { + case 'r': flagremoteinfo = 1; break; + case 'R': flagremoteinfo = 0; break; + case 't': scan_ulong(sgoptarg,&timeout); break; + } + + argv += sgoptind; + argc -= sgoptind; + + if (argc < 1) die(); + if (!env_init()) die(); + + proto = env_get("PROTO"); + if (!proto || str_diff(proto,"TCP")) + { + if (!env_put("PROTO=TCP")) die(); + + dummy = sizeof(salocal); + if (getsockname(0,(struct sockaddr *) &salocal,&dummy) == -1) die(); + + localport = ntohs(salocal.sin_port); + temp[fmt_ulong(temp,localport)] = 0; + if (!env_put2("TCPLOCALPORT",temp)) die(); + + byte_copy(&iplocal,4,&salocal.sin_addr); + temp[ip_fmt(temp,&iplocal)] = 0; + if (!env_put2("TCPLOCALIP",temp)) die(); + + switch(dns_ptr(&localname,&iplocal)) + { + case DNS_MEM: die(); + case DNS_SOFT: + if (!stralloc_copys(&localname,"softdnserror")) die(); + case 0: + if (!stralloc_0(&localname)) die(); + case_lowers(localname.s); + if (!env_put2("TCPLOCALHOST",localname.s)) die(); + break; + default: + if (!env_unset("TCPLOCALHOST")) die(); + } + + dummy = sizeof(saremote); + if (getpeername(0,(struct sockaddr *) &saremote,&dummy) == -1) die(); + + remoteport = ntohs(saremote.sin_port); + temp[fmt_ulong(temp,remoteport)] = 0; + if (!env_put2("TCPREMOTEPORT",temp)) die(); + + byte_copy(&ipremote,4,&saremote.sin_addr); + temp[ip_fmt(temp,&ipremote)] = 0; + if (!env_put2("TCPREMOTEIP",temp)) die(); + + switch(dns_ptr(&remotename,&ipremote)) + { + case DNS_MEM: die(); + case DNS_SOFT: + if (!stralloc_copys(&remotename,"softdnserror")) die(); + case 0: + if (!stralloc_0(&remotename)) die(); + case_lowers(remotename.s); + if (!env_put2("TCPREMOTEHOST",remotename.s)) die(); + break; + default: + if (!env_unset("TCPREMOTEHOST")) die(); + } + + if (!env_unset("TCPREMOTEINFO")) die(); + if (flagremoteinfo) + { + char *rinfo; + rinfo = remoteinfo_get(&ipremote,remoteport,&iplocal,localport,(int) timeout); + if (rinfo) + if (!env_put2("TCPREMOTEINFO",rinfo)) die(); + } + } + + sig_pipedefault(); + execvp(*argv,argv); + die(); +} diff --git a/tcp-environ.5 b/tcp-environ.5 new file mode 100644 index 0000000..b5cb83b --- /dev/null +++ b/tcp-environ.5 @@ -0,0 +1,62 @@ +.TH tcp-environ 5 +.SH NAME +tcp-environ \- TCP-related environment variables +.SH DESCRIPTION +The following environment variables +describe a TCP connection. +They are set up by +.BR tcp-env , +.BR tcpclient , +and +.BR tcpserver . +Note that +.BR TCPLOCALHOST , +.BR TCPREMOTEHOST , +and +.B TCPREMOTEINFO +can contain arbitrary characters. +.TP 5 +PROTO +The string +.BR TCP . +.TP 5 +TCPLOCALHOST +The domain name of the local host, +with uppercase letters converted to lowercase. +If there is no currently available domain name +for the local IP address, +.B TCPLOCALHOST +is not set. +.TP 5 +TCPLOCALIP +The IP address of the local host, in dotted-decimal form. +.TP 5 +TCPLOCALPORT +The local TCP port number, in decimal. +.TP 5 +TCPREMOTEHOST +The domain name of the remote host, +with uppercase letters converted to lowercase. +If there is no currently available domain name +for the remote IP address, +.B TCPREMOTEHOST +is not set. +.TP 5 +TCPREMOTEINFO +A connection-specific string, perhaps a username, +supplied by the remote host +via 931/1413/IDENT/TAP. +If the remote host did not supply connection information, +.B TCPREMOTEINFO +is not set. +.TP 5 +TCPREMOTEIP +The IP address of the remote host. +.TP 5 +TCPREMOTEPORT +The remote TCP port number. +.SH "SEE ALSO" +tcpclient(1), +tcpserver(1), +tcp-env(1), +tcp(4) diff --git a/tcpto.c b/tcpto.c new file mode 100644 index 0000000..8d100d5 --- /dev/null +++ b/tcpto.c @@ -0,0 +1,165 @@ +#include "tcpto.h" +#include "open.h" +#include "lock.h" +#include "seek.h" +#include "now.h" +#include "ip.h" +#include "byte.h" +#include "datetime.h" +#include "readwrite.h" + +char tcpto_buf[1024]; + +static int flagwasthere; +static int fdlock; + +static int getbuf() +{ + int r; + int fd; + + fdlock = open_write("queue/lock/tcpto"); + if (fdlock == -1) return 0; + fd = open_read("queue/lock/tcpto"); + if (fd == -1) { close(fdlock); return 0; } + if (lock_ex(fdlock) == -1) { close(fdlock); close(fd); return 0; } + r = read(fd,tcpto_buf,sizeof(tcpto_buf)); + close(fd); + if (r < 0) { close(fdlock); return 0; } + r >>= 4; + if (!r) close(fdlock); + return r; +} + +int tcpto(ip) struct ip_address *ip; +{ + int n; + int i; + char *record; + datetime_sec when; + + flagwasthere = 0; + + n = getbuf(); + if (!n) return 0; + close(fdlock); + + record = tcpto_buf; + for (i = 0;i < n;++i) + { + if (byte_equal(ip->d,4,record)) + { + flagwasthere = 1; + if (record[4] >= 2) + { + when = (unsigned long) (unsigned char) record[11]; + when = (when << 8) + (unsigned long) (unsigned char) record[10]; + when = (when << 8) + (unsigned long) (unsigned char) record[9]; + when = (when << 8) + (unsigned long) (unsigned char) record[8]; + + if (now() - when < ((60 + (getpid() & 31)) << 6)) + return 1; + } + return 0; + } + record += 16; + } + return 0; +} + +void tcpto_err(ip,flagerr) struct ip_address *ip; int flagerr; +{ + int n; + int i; + char *record; + datetime_sec when; + datetime_sec firstwhen; + int firstpos; + datetime_sec lastwhen; + + if (!flagerr) + if (!flagwasthere) + return; /* could have been added, but not worth the effort to check */ + + n = getbuf(); + if (!n) return; + + record = tcpto_buf; + for (i = 0;i < n;++i) + { + if (byte_equal(ip->d,4,record)) + { + if (!flagerr) + record[4] = 0; + else + { + lastwhen = (unsigned long) (unsigned char) record[11]; + lastwhen = (lastwhen << 8) + (unsigned long) (unsigned char) record[10]; + lastwhen = (lastwhen << 8) + (unsigned long) (unsigned char) record[9]; + lastwhen = (lastwhen << 8) + (unsigned long) (unsigned char) record[8]; + when = now(); + + if (record[4] && (when < 120 + lastwhen)) { close(fdlock); return; } + + if (++record[4] > 10) record[4] = 10; + record[8] = when; when >>= 8; + record[9] = when; when >>= 8; + record[10] = when; when >>= 8; + record[11] = when; + } + if (seek_set(fdlock,i << 4) == 0) + if (write(fdlock,record,16) < 16) + ; /*XXX*/ + close(fdlock); + return; + } + record += 16; + } + + if (!flagerr) { close(fdlock); return; } + + record = tcpto_buf; + for (i = 0;i < n;++i) + { + if (!record[4]) break; + record += 16; + } + + if (i >= n) + { + firstpos = -1; + record = tcpto_buf; + for (i = 0;i < n;++i) + { + when = (unsigned long) (unsigned char) record[11]; + when = (when << 8) + (unsigned long) (unsigned char) record[10]; + when = (when << 8) + (unsigned long) (unsigned char) record[9]; + when = (when << 8) + (unsigned long) (unsigned char) record[8]; + when += (record[4] << 10); + if ((firstpos < 0) || (when < firstwhen)) + { + firstpos = i; + firstwhen = when; + } + record += 16; + } + i = firstpos; + } + + if (i >= 0) + { + record = tcpto_buf + (i << 4); + byte_copy(record,4,ip->d); + when = now(); + record[8] = when; when >>= 8; + record[9] = when; when >>= 8; + record[10] = when; when >>= 8; + record[11] = when; + record[4] = 1; + if (seek_set(fdlock,i << 4) == 0) + if (write(fdlock,record,16) < 16) + ; /*XXX*/ + } + + close(fdlock); +} diff --git a/tcpto.h b/tcpto.h new file mode 100644 index 0000000..cd44a42 --- /dev/null +++ b/tcpto.h @@ -0,0 +1,8 @@ +#ifndef TCPTO_H +#define TCPTO_H + +extern int tcpto(); +extern void tcpto_err(); +extern void tcpto_clean(); + +#endif diff --git a/tcpto_clean.c b/tcpto_clean.c new file mode 100644 index 0000000..ed48506 --- /dev/null +++ b/tcpto_clean.c @@ -0,0 +1,20 @@ +#include "tcpto.h" +#include "open.h" +#include "substdio.h" +#include "readwrite.h" + +char tcpto_cleanbuf[1024]; + +void tcpto_clean() /* running from queue/mess */ +{ + int fd; + int i; + substdio ss; + + fd = open_write("../lock/tcpto"); + if (fd == -1) return; + substdio_fdbuf(&ss,write,fd,tcpto_cleanbuf,sizeof(tcpto_cleanbuf)); + for (i = 0;i < sizeof(tcpto_cleanbuf);++i) substdio_put(&ss,"",1); + substdio_flush(&ss); /* if it fails, bummer */ + close(fd); +} diff --git a/timeoutconn.c b/timeoutconn.c new file mode 100644 index 0000000..33a16d9 --- /dev/null +++ b/timeoutconn.c @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include "ndelay.h" +#include "select.h" +#include "error.h" +#include "readwrite.h" +#include "ip.h" +#include "byte.h" +#include "timeoutconn.h" + +int timeoutconn(s,ip,port,timeout) +int s; +struct ip_address *ip; +unsigned int port; +int timeout; +{ + char ch; + struct sockaddr_in sin; + char *x; + fd_set wfds; + struct timeval tv; + + byte_zero(&sin,sizeof(sin)); + byte_copy(&sin.sin_addr,4,ip); + x = (char *) &sin.sin_port; + x[1] = port; port >>= 8; x[0] = port; + sin.sin_family = AF_INET; + + if (ndelay_on(s) == -1) return -1; + + /* XXX: could bind s */ + + if (connect(s,(struct sockaddr *) &sin,sizeof(sin)) == 0) { + ndelay_off(s); + return 0; + } + if ((errno != error_inprogress) && (errno != error_wouldblock)) return -1; + + FD_ZERO(&wfds); + FD_SET(s,&wfds); + tv.tv_sec = timeout; tv.tv_usec = 0; + + if (select(s + 1,(fd_set *) 0,&wfds,(fd_set *) 0,&tv) == -1) return -1; + if (FD_ISSET(s,&wfds)) { + int dummy; + dummy = sizeof(sin); + if (getpeername(s,(struct sockaddr *) &sin,&dummy) == -1) { + read(s,&ch,1); + return -1; + } + ndelay_off(s); + return 0; + } + + errno = error_timeout; /* note that connect attempt is continuing */ + return -1; +} diff --git a/timeoutconn.h b/timeoutconn.h new file mode 100644 index 0000000..88aab06 --- /dev/null +++ b/timeoutconn.h @@ -0,0 +1,6 @@ +#ifndef TIMEOUTCONN_H +#define TIMEOUTCONN_H + +extern int timeoutconn(); + +#endif diff --git a/timeoutread.c b/timeoutread.c new file mode 100644 index 0000000..c75e29c --- /dev/null +++ b/timeoutread.c @@ -0,0 +1,22 @@ +#include "timeoutread.h" +#include "select.h" +#include "error.h" +#include "readwrite.h" + +int timeoutread(t,fd,buf,len) int t; int fd; char *buf; int len; +{ + fd_set rfds; + struct timeval tv; + + tv.tv_sec = t; + tv.tv_usec = 0; + + FD_ZERO(&rfds); + FD_SET(fd,&rfds); + + if (select(fd + 1,&rfds,(fd_set *) 0,(fd_set *) 0,&tv) == -1) return -1; + if (FD_ISSET(fd,&rfds)) return read(fd,buf,len); + + errno = error_timeout; + return -1; +} diff --git a/timeoutread.h b/timeoutread.h new file mode 100644 index 0000000..20d3bfc --- /dev/null +++ b/timeoutread.h @@ -0,0 +1,6 @@ +#ifndef TIMEOUTREAD_H +#define TIMEOUTREAD_H + +extern int timeoutread(); + +#endif diff --git a/timeoutwrite.c b/timeoutwrite.c new file mode 100644 index 0000000..516d283 --- /dev/null +++ b/timeoutwrite.c @@ -0,0 +1,22 @@ +#include "timeoutwrite.h" +#include "select.h" +#include "error.h" +#include "readwrite.h" + +int timeoutwrite(t,fd,buf,len) int t; int fd; char *buf; int len; +{ + fd_set wfds; + struct timeval tv; + + tv.tv_sec = t; + tv.tv_usec = 0; + + FD_ZERO(&wfds); + FD_SET(fd,&wfds); + + if (select(fd + 1,(fd_set *) 0,&wfds,(fd_set *) 0,&tv) == -1) return -1; + if (FD_ISSET(fd,&wfds)) return write(fd,buf,len); + + errno = error_timeout; + return -1; +} diff --git a/timeoutwrite.h b/timeoutwrite.h new file mode 100644 index 0000000..4725861 --- /dev/null +++ b/timeoutwrite.h @@ -0,0 +1,6 @@ +#ifndef TIMEOUTWRITE_H +#define TIMEOUTWRITE_H + +extern int timeoutwrite(); + +#endif diff --git a/token822.c b/token822.c new file mode 100644 index 0000000..48a4388 --- /dev/null +++ b/token822.c @@ -0,0 +1,513 @@ +#include "stralloc.h" +#include "alloc.h" +#include "str.h" +#include "token822.h" +#include "gen_allocdefs.h" + +static struct token822 comma = { TOKEN822_COMMA }; + +void token822_reverse(ta) +token822_alloc *ta; +{ + int i; + int n; + struct token822 temp; + + n = ta->len - 1; + for (i = 0;i + i < n;++i) + { + temp = ta->t[i]; + ta->t[i] = ta->t[n - i]; + ta->t[n - i] = temp; + } +} + +GEN_ALLOC_ready(token822_alloc,struct token822,t,len,a,i,n,x,30,token822_ready) +GEN_ALLOC_readyplus(token822_alloc,struct token822,t,len,a,i,n,x,30,token822_readyplus) +GEN_ALLOC_append(token822_alloc,struct token822,t,len,a,i,n,x,30,token822_readyplus,token822_append) + +static int needspace(t1,t2) +int t1; +int t2; +{ + if (!t1) return 0; + if (t1 == TOKEN822_COLON) return 1; + if (t1 == TOKEN822_COMMA) return 1; + if (t2 == TOKEN822_LEFT) return 1; + switch(t1) + { + case TOKEN822_ATOM: case TOKEN822_LITERAL: + case TOKEN822_QUOTE: case TOKEN822_COMMENT: + switch(t2) + { + case TOKEN822_ATOM: case TOKEN822_LITERAL: + case TOKEN822_QUOTE: case TOKEN822_COMMENT: + return 1; + } + } + return 0; +} + +static int atomok(ch) +char ch; +{ + switch(ch) + { + case ' ': case '\t': case '\r': case '\n': + case '(': case '[': case '"': + case '<': case '>': case ';': case ':': + case '@': case ',': case '.': + return 0; + } + return 1; +} + +static void atomcheck(t) +struct token822 *t; +{ + int i; + char ch; + for (i = 0;i < t->slen;++i) + { + ch = t->s[i]; + if ((ch < 32) || (ch > 126) || (ch == ')') || (ch == ']') || (ch == '\\')) + { + t->type = TOKEN822_QUOTE; + return; + } + } +} + +int token822_unparse(sa,ta,linelen) +stralloc *sa; +token822_alloc *ta; +unsigned int linelen; +{ + struct token822 *t; + int len; + int ch; + int i; + int j; + int lasttype; + int newtype; + char *s; + char *lineb; + char *linee; + + len = 0; + lasttype = 0; + for (i = 0;i < ta->len;++i) + { + t = ta->t + i; + newtype = t->type; + if (needspace(lasttype,newtype)) + ++len; + lasttype = newtype; + switch(newtype) + { + case TOKEN822_COMMA: + len += 3; break; + case TOKEN822_AT: case TOKEN822_DOT: case TOKEN822_LEFT: case TOKEN822_RIGHT: + case TOKEN822_SEMI: case TOKEN822_COLON: + ++len; break; + case TOKEN822_ATOM: case TOKEN822_QUOTE: case TOKEN822_LITERAL: case TOKEN822_COMMENT: + if (t->type != TOKEN822_ATOM) len += 2; + for (j = 0;j < t->slen;++j) + switch(ch = t->s[j]) + { + case '"': case '[': case ']': case '(': case ')': + case '\\': case '\r': case '\n': ++len; + default: ++len; + } + break; + } + } + len += 2; + + if (!stralloc_ready(sa,len)) + return -1; + + s = sa->s; + lineb = s; + linee = 0; + + lasttype = 0; + for (i = 0;i < ta->len;++i) + { + t = ta->t + i; + newtype = t->type; + if (needspace(lasttype,newtype)) + *s++ = ' '; + lasttype = newtype; + switch(newtype) + { + case TOKEN822_COMMA: + *s++ = ','; +#define NSUW \ + s[0] = '\n'; s[1] = ' '; \ + if (linee && (!linelen || (s - lineb <= linelen))) \ + { while (linee < s) { linee[0] = linee[2]; ++linee; } linee -= 2; } \ + else { if (linee) lineb = linee + 1; linee = s; s += 2; } + NSUW + break; + case TOKEN822_AT: *s++ = '@'; break; + case TOKEN822_DOT: *s++ = '.'; break; + case TOKEN822_LEFT: *s++ = '<'; break; + case TOKEN822_RIGHT: *s++ = '>'; break; + case TOKEN822_SEMI: *s++ = ';'; break; + case TOKEN822_COLON: *s++ = ':'; break; + case TOKEN822_ATOM: case TOKEN822_QUOTE: case TOKEN822_LITERAL: case TOKEN822_COMMENT: + if (t->type == TOKEN822_QUOTE) *s++ = '"'; + if (t->type == TOKEN822_LITERAL) *s++ = '['; + if (t->type == TOKEN822_COMMENT) *s++ = '('; + for (j = 0;j < t->slen;++j) + switch(ch = t->s[j]) + { + case '"': case '[': case ']': case '(': case ')': + case '\\': case '\r': case '\n': *s++ = '\\'; + default: *s++ = ch; + } + if (t->type == TOKEN822_QUOTE) *s++ = '"'; + if (t->type == TOKEN822_LITERAL) *s++ = ']'; + if (t->type == TOKEN822_COMMENT) *s++ = ')'; + break; + } + } + NSUW + --s; + sa->len = s - sa->s; + return 1; +} + +int token822_unquote(sa,ta) +stralloc *sa; +token822_alloc *ta; +{ + struct token822 *t; + int len; + int i; + int j; + char *s; + + len = 0; + for (i = 0;i < ta->len;++i) + { + t = ta->t + i; + switch(t->type) + { + case TOKEN822_COMMA: case TOKEN822_AT: case TOKEN822_DOT: case TOKEN822_LEFT: + case TOKEN822_RIGHT: case TOKEN822_SEMI: case TOKEN822_COLON: + ++len; break; + case TOKEN822_LITERAL: + len += 2; + case TOKEN822_ATOM: case TOKEN822_QUOTE: + len += t->slen; + } + } + + if (!stralloc_ready(sa,len)) + return -1; + + s = sa->s; + + for (i = 0;i < ta->len;++i) + { + t = ta->t + i; + switch(t->type) + { + case TOKEN822_COMMA: *s++ = ','; break; + case TOKEN822_AT: *s++ = '@'; break; + case TOKEN822_DOT: *s++ = '.'; break; + case TOKEN822_LEFT: *s++ = '<'; break; + case TOKEN822_RIGHT: *s++ = '>'; break; + case TOKEN822_SEMI: *s++ = ';'; break; + case TOKEN822_COLON: *s++ = ':'; break; + case TOKEN822_ATOM: case TOKEN822_QUOTE: case TOKEN822_LITERAL: + if (t->type == TOKEN822_LITERAL) *s++ = '['; + for (j = 0;j < t->slen;++j) + *s++ = t->s[j]; + if (t->type == TOKEN822_LITERAL) *s++ = ']'; + break; + case TOKEN822_COMMENT: break; + } + } + sa->len = s - sa->s; + return 1; +} + +int token822_parse(ta,sa,buf) +token822_alloc *ta; +stralloc *sa; +stralloc *buf; +{ + int i; + int salen; + int level; + struct token822 *t; + int numtoks; + int numchars; + char *cbuf; + + salen = sa->len; + + numchars = 0; + numtoks = 0; + for (i = 0;i < salen;++i) + switch(sa->s[i]) + { + case '.': case ',': case '@': case '<': case '>': case ':': case ';': + ++numtoks; break; + case ' ': case '\t': case '\r': case '\n': break; + case ')': case ']': return 0; + /* other control chars and non-ASCII chars are also bad, in theory */ + case '(': + level = 1; + while (level) + { + if (++i >= salen) return 0; + switch(sa->s[i]) + { + case '(': ++level; break; + case ')': --level; break; + case '\\': if (++i >= salen) return 0; + default: ++numchars; + } + } + ++numtoks; + break; + case '"': + level = 1; + while (level) + { + if (++i >= salen) return 0; + switch(sa->s[i]) + { + case '"': --level; break; + case '\\': if (++i >= salen) return 0; + default: ++numchars; + } + } + ++numtoks; + break; + case '[': + level = 1; + while (level) + { + if (++i >= salen) return 0; + switch(sa->s[i]) + { + case ']': --level; break; + case '\\': if (++i >= salen) return 0; + default: ++numchars; + } + } + ++numtoks; + break; + default: + do + { + if (sa->s[i] == '\\') if (++i >= salen) break; + ++numchars; + if (++i >= salen) + break; + } + while (atomok(sa->s[i])); + --i; + ++numtoks; + } + + if (!token822_ready(ta,numtoks)) + return -1; + if (!stralloc_ready(buf,numchars)) + return -1; + cbuf = buf->s; + ta->len = numtoks; + + t = ta->t; + for (i = 0;i < salen;++i) + switch(sa->s[i]) + { + case '.': t->type = TOKEN822_DOT; ++t; break; + case ',': t->type = TOKEN822_COMMA; ++t; break; + case '@': t->type = TOKEN822_AT; ++t; break; + case '<': t->type = TOKEN822_LEFT; ++t; break; + case '>': t->type = TOKEN822_RIGHT; ++t; break; + case ':': t->type = TOKEN822_COLON; ++t; break; + case ';': t->type = TOKEN822_SEMI; ++t; break; + case ' ': case '\t': case '\r': case '\n': break; + case '(': + t->type = TOKEN822_COMMENT; t->s = cbuf; t->slen = 0; + level = 1; + while (level) + { + ++i; /* assert: < salen */ + switch(sa->s[i]) + { + case '(': ++level; break; + case ')': --level; break; + case '\\': ++i; /* assert: < salen */ + default: *cbuf++ = sa->s[i]; ++t->slen; + } + } + ++t; + break; + case '"': + t->type = TOKEN822_QUOTE; t->s = cbuf; t->slen = 0; + level = 1; + while (level) + { + ++i; /* assert: < salen */ + switch(sa->s[i]) + { + case '"': --level; break; + case '\\': ++i; /* assert: < salen */ + default: *cbuf++ = sa->s[i]; ++t->slen; + } + } + ++t; + break; + case '[': + t->type = TOKEN822_LITERAL; t->s = cbuf; t->slen = 0; + level = 1; + while (level) + { + ++i; /* assert: < salen */ + switch(sa->s[i]) + { + case ']': --level; break; + case '\\': ++i; /* assert: < salen */ + default: *cbuf++ = sa->s[i]; ++t->slen; + } + } + ++t; + break; + default: + t->type = TOKEN822_ATOM; t->s = cbuf; t->slen = 0; + do + { + if (sa->s[i] == '\\') if (++i >= salen) break; + *cbuf++ = sa->s[i]; ++t->slen; + if (++i >= salen) + break; + } + while (atomok(sa->s[i])); + atomcheck(t); + --i; + ++t; + } + return 1; +} + +static int gotaddr(taout,taaddr,callback) +token822_alloc *taout; +token822_alloc *taaddr; +int (*callback)(); +{ + int i; + + if (callback(taaddr) != 1) + return 0; + + if (!token822_readyplus(taout,taaddr->len)) + return 0; + + for (i = 0;i < taaddr->len;++i) + taout->t[taout->len++] = taaddr->t[i]; + + taaddr->len = 0; + return 1; +} + +int token822_addrlist(taout,taaddr,ta,callback) +token822_alloc *taout; +token822_alloc *taaddr; +token822_alloc *ta; +int (*callback)(); +{ + struct token822 *t; + struct token822 *beginning; + int ingroup; + int wordok; + + taout->len = 0; + taaddr->len = 0; + + if (!token822_readyplus(taout,1)) return -1; + if (!token822_readyplus(taaddr,1)) return -1; + + ingroup = 0; + wordok = 1; + + beginning = ta->t + 2; + t = ta->t + ta->len - 1; + + /* rfc 822 address lists are easy to parse from right to left */ + +#define FLUSH if (taaddr->len) if (!gotaddr(taout,taaddr,callback)) return -1; +#define FLUSHCOMMA if (taaddr->len) { \ +if (!gotaddr(taout,taaddr,callback)) return -1; \ +if (!token822_append(taout,&comma)) return -1; } +#define ADDRLEFT if (!token822_append(taaddr,t--)) return -1; +#define OUTLEFT if (!token822_append(taout,t--)) return -1; + + while (t >= beginning) + { + switch(t->type) + { + case TOKEN822_SEMI: + FLUSHCOMMA + if (ingroup) return 0; + ingroup = 1; + wordok = 1; + break; + case TOKEN822_COLON: + FLUSH + if (!ingroup) return 0; + ingroup = 0; + while ((t >= beginning) && (t->type != TOKEN822_COMMA)) + OUTLEFT + if (t >= beginning) + OUTLEFT + wordok = 1; + continue; + case TOKEN822_RIGHT: + FLUSHCOMMA + OUTLEFT + while ((t >= beginning) && (t->type != TOKEN822_LEFT)) + ADDRLEFT + /* important to use address here even if it's empty: <> */ + if (!gotaddr(taout,taaddr,callback)) return -1; + if (t < beginning) return 0; + OUTLEFT + while ((t >= beginning) && ((t->type == TOKEN822_COMMENT) || (t->type == TOKEN822_ATOM) || (t->type == TOKEN822_QUOTE) || (t->type == TOKEN822_AT) || (t->type == TOKEN822_DOT))) + OUTLEFT + wordok = 0; + continue; + case TOKEN822_ATOM: case TOKEN822_QUOTE: case TOKEN822_LITERAL: + if (!wordok) + FLUSHCOMMA + wordok = 0; + ADDRLEFT + continue; + case TOKEN822_COMMENT: + /* comment is lexically a space; shouldn't affect wordok */ + break; + case TOKEN822_COMMA: + FLUSH + wordok = 1; + break; + default: + wordok = 1; + ADDRLEFT + continue; + } + OUTLEFT + } + FLUSH + ++t; + while (t > ta->t) + if (!token822_append(taout,--t)) return -1; + + token822_reverse(taout); + return 1; +} diff --git a/token822.h b/token822.h new file mode 100644 index 0000000..9ca35cf --- /dev/null +++ b/token822.h @@ -0,0 +1,37 @@ +#ifndef TOKEN822_H +#define TOKEN822_H + +struct token822 + { + int type; + char *s; + int slen; + } +; + +#include "gen_alloc.h" +GEN_ALLOC_typedef(token822_alloc,struct token822,t,len,a) + +extern int token822_parse(); +extern int token822_addrlist(); +extern int token822_unquote(); +extern int token822_unparse(); +extern void token822_free(); +extern void token822_reverse(); +extern int token822_ready(); +extern int token822_readyplus(); +extern int token822_append(); + +#define TOKEN822_ATOM 1 +#define TOKEN822_QUOTE 2 +#define TOKEN822_LITERAL 3 +#define TOKEN822_COMMENT 4 +#define TOKEN822_LEFT 5 +#define TOKEN822_RIGHT 6 +#define TOKEN822_AT 7 +#define TOKEN822_COMMA 8 +#define TOKEN822_SEMI 9 +#define TOKEN822_COLON 10 +#define TOKEN822_DOT 11 + +#endif diff --git a/trigger.c b/trigger.c new file mode 100644 index 0000000..39f81b8 --- /dev/null +++ b/trigger.c @@ -0,0 +1,41 @@ +#include "select.h" +#include "open.h" +#include "trigger.h" +#include "hasnpbg1.h" + +static int fd = -1; +#ifdef HASNAMEDPIPEBUG1 +static int fdw = -1; +#endif + +void trigger_set() +{ + if (fd != -1) + close(fd); +#ifdef HASNAMEDPIPEBUG1 + if (fdw != -1) + close(fdw); +#endif + fd = open_read("lock/trigger"); +#ifdef HASNAMEDPIPEBUG1 + fdw = open_write("lock/trigger"); +#endif +} + +void trigger_selprep(nfds,rfds) +int *nfds; +fd_set *rfds; +{ + if (fd != -1) + { + FD_SET(fd,rfds); + if (*nfds < fd + 1) *nfds = fd + 1; + } +} + +int trigger_pulled(rfds) +fd_set *rfds; +{ + if (fd != -1) if (FD_ISSET(fd,rfds)) return 1; + return 0; +} diff --git a/trigger.h b/trigger.h new file mode 100644 index 0000000..dec24ef --- /dev/null +++ b/trigger.h @@ -0,0 +1,8 @@ +#ifndef TRIGGER_H +#define TRIGGER_H + +extern void trigger_set(); +extern void trigger_selprep(); +extern int trigger_pulled(); + +#endif diff --git a/triggerpull.c b/triggerpull.c new file mode 100644 index 0000000..30b9a97 --- /dev/null +++ b/triggerpull.c @@ -0,0 +1,16 @@ +#include "ndelay.h" +#include "open.h" +#include "triggerpull.h" + +void triggerpull() +{ + int fd; + + fd = open_write("lock/trigger"); + if (fd >= 0) + { + ndelay_on(fd); + write(fd,"",1); /* if it fails, bummer */ + close(fd); + } +} diff --git a/triggerpull.h b/triggerpull.h new file mode 100644 index 0000000..6d097bb --- /dev/null +++ b/triggerpull.h @@ -0,0 +1,6 @@ +#ifndef TRIGGERPULL_H +#define TRIGGERPULL_H + +extern void triggerpull(); + +#endif diff --git a/trycpp.c b/trycpp.c new file mode 100644 index 0000000..d7d83ad --- /dev/null +++ b/trycpp.c @@ -0,0 +1,7 @@ +void main() +{ +#ifdef NeXT + printf("nextstep\n"); exit(0); +#endif + printf("unknown\n"); exit(0); +} diff --git a/trydrent.c b/trydrent.c new file mode 100644 index 0000000..c778176 --- /dev/null +++ b/trydrent.c @@ -0,0 +1,8 @@ +#include +#include + +void foo() +{ + DIR *dir; + struct dirent *d; +} diff --git a/tryflock.c b/tryflock.c new file mode 100644 index 0000000..8c8aa76 --- /dev/null +++ b/tryflock.c @@ -0,0 +1,8 @@ +#include +#include +#include + +void main() +{ + flock(0,LOCK_EX | LOCK_UN | LOCK_NB); +} diff --git a/trylsock.c b/trylsock.c new file mode 100644 index 0000000..fbce408 --- /dev/null +++ b/trylsock.c @@ -0,0 +1,4 @@ +main() +{ + ; +} diff --git a/trymkffo.c b/trymkffo.c new file mode 100644 index 0000000..0b119c6 --- /dev/null +++ b/trymkffo.c @@ -0,0 +1,7 @@ +#include +#include + +void main() +{ + mkfifo("temp-trymkffo",0); +} diff --git a/trynpbg1.c b/trynpbg1.c new file mode 100644 index 0000000..9d5f80b --- /dev/null +++ b/trynpbg1.c @@ -0,0 +1,26 @@ +#include "select.h" +#include "open.h" +#include "fifo.h" + +#define FN "temp-trynpbg1.fifo" + +void main() +{ + int flagbug; + struct timeval instant; + fd_set rfds; + + flagbug = 0; + if (fifo_make(FN,0600) != -1) { + close(0); + if (open_read(FN) == 0) { + FD_ZERO(&rfds); + FD_SET(0,&rfds); + instant.tv_sec = instant.tv_usec = 0; + if (select(1,&rfds,(fd_set *) 0,(fd_set *) 0,&instant) > 0) + flagbug = 1; + } + unlink(FN); + } + _exit(!flagbug); +} diff --git a/tryrsolv.c b/tryrsolv.c new file mode 100644 index 0000000..fbce408 --- /dev/null +++ b/tryrsolv.c @@ -0,0 +1,4 @@ +main() +{ + ; +} diff --git a/trysalen.c b/trysalen.c new file mode 100644 index 0000000..731a109 --- /dev/null +++ b/trysalen.c @@ -0,0 +1,11 @@ +#include +#include +#include +#include +#include + +void foo() +{ + struct sockaddr sa; + sa.sa_len = 0; +} diff --git a/trysgact.c b/trysgact.c new file mode 100644 index 0000000..263cb21 --- /dev/null +++ b/trysgact.c @@ -0,0 +1,10 @@ +#include + +void main() +{ + struct sigaction sa; + sa.sa_handler = 0; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sigaction(0,&sa,(struct sigaction *) 0); +} diff --git a/trysgprm.c b/trysgprm.c new file mode 100644 index 0000000..ed28857 --- /dev/null +++ b/trysgprm.c @@ -0,0 +1,10 @@ +#include + +void main() +{ + sigset_t ss; + + sigemptyset(&ss); + sigaddset(&ss,SIGCHLD); + sigprocmask(SIG_SETMASK,&ss,(sigset_t *) 0); +} diff --git a/tryshsgr.c b/tryshsgr.c new file mode 100644 index 0000000..807e15d --- /dev/null +++ b/tryshsgr.c @@ -0,0 +1,14 @@ +void main() +{ + short x[4]; + + x[0] = x[1] = 1; + if (getgroups(1,x) == 0) if (setgroups(1,x) == -1) _exit(1); + + if (getgroups(1,x) == -1) _exit(1); + if (x[1] != 1) _exit(1); + x[1] = 2; + if (getgroups(1,x) == -1) _exit(1); + if (x[1] != 2) _exit(1); + _exit(0); +} diff --git a/trysysel.c b/trysysel.c new file mode 100644 index 0000000..f6ed055 --- /dev/null +++ b/trysysel.c @@ -0,0 +1,8 @@ +#include +#include +#include /* SVR4 silliness */ + +void foo() +{ + ; +} diff --git a/trysyslog.c b/trysyslog.c new file mode 100644 index 0000000..4b99afc --- /dev/null +++ b/trysyslog.c @@ -0,0 +1,9 @@ +#include +#include +#include + +main() +{ + openlog("foo",0,LOG_MAIL); + syslog(0,"foo"); +} diff --git a/tryulong32.c b/tryulong32.c new file mode 100644 index 0000000..a108076 --- /dev/null +++ b/tryulong32.c @@ -0,0 +1,11 @@ +void main() +{ + unsigned long u; + u = 1; + u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u; + u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u; + u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u; + u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u; + if (!u) _exit(0); + _exit(1); +} diff --git a/tryvfork.c b/tryvfork.c new file mode 100644 index 0000000..21387e4 --- /dev/null +++ b/tryvfork.c @@ -0,0 +1,4 @@ +void main() +{ + vfork(); +} diff --git a/trywaitp.c b/trywaitp.c new file mode 100644 index 0000000..7e73bfa --- /dev/null +++ b/trywaitp.c @@ -0,0 +1,7 @@ +#include +#include + +void main() +{ + waitpid(0,0,0); +} diff --git a/uint32.h1 b/uint32.h1 new file mode 100644 index 0000000..6599aa0 --- /dev/null +++ b/uint32.h1 @@ -0,0 +1,6 @@ +#ifndef UINT32_H +#define UINT32_H + +typedef unsigned int uint32; + +#endif diff --git a/uint32.h2 b/uint32.h2 new file mode 100644 index 0000000..716430d --- /dev/null +++ b/uint32.h2 @@ -0,0 +1,6 @@ +#ifndef UINT32_H +#define UINT32_H + +typedef unsigned long uint32; + +#endif diff --git a/wait.3 b/wait.3 new file mode 100644 index 0000000..8c41f4b --- /dev/null +++ b/wait.3 @@ -0,0 +1,93 @@ +.TH wait 3 +.SH NAME +wait \- check child process status +.SH SYNTAX +.B #include + +int \fBwait_nohang\fP(&\fIwstat\fR); +.br +int \fBwait_stop\fP(&\fIwstat\fR); +.br +int \fBwait_stopnohang\fP(&\fIwstat\fR); +.br +int \fBwait_pid\fP(&\fIwstat\fR,\fIpid\fR); + +int \fBwait_exitcode\fP(\fIwstat\fR); +.br +int \fBwait_crashed\fP(\fIwstat\fR); +.br +int \fBwait_stopped\fP(\fIwstat\fR); +.br +int \fBwait_stopsig\fP(\fIwstat\fR); + +int \fIpid\fR; +.br +int \fIwstat\fR; +.SH DESCRIPTION +.B wait_nohang +looks for zombies (child processes that have exited). +If it sees a zombie, +it eliminates the zombie, +puts the zombie's exit status into +.IR wstat , +and returns the zombie's process ID. +If there are several zombies, +.B wait_nohang +picks one. +If there are children but no zombies, +.B wait_nohang +returns 0. +If there are no children, +.B wait_nohang +returns -1, +setting +.B errno +appropriately. + +.B wait_stopnohang +is similar to +.BR wait_nohang , +but it also looks for children that have stopped. + +.B wait_stop +is similar to +.BR wait_stopnohang , +but if there are children it will pause waiting for one of them +to stop or exit. + +.B wait_pid +waits for child process +.I pid +to exit. +It eliminates any zombie that shows up in the meantime, +discarding the exit status. + +.B wait_stop +and +.B wait_pid +retry upon +.BR error_intr . +.SH "STATUS PARSING" +If the child stopped, +.B wait_stopped +is nonzero; +.B wait_stopsig +is the signal that caused the child to stop. + +If the child exited by crashing, +.B wait_stopped +is zero; +.B wait_crashed +is nonzero. + +If the child exited normally, +.B wait_stopped +is zero; +.B wait_crashed +is zero; +and +.B wait_exitcode +is the child's exit code. +.SH "SEE ALSO" +wait(2), +error(3) diff --git a/wait.h b/wait.h new file mode 100644 index 0000000..cdb77c3 --- /dev/null +++ b/wait.h @@ -0,0 +1,14 @@ +#ifndef WAIT_H +#define WAIT_H + +extern int wait_pid(); +extern int wait_nohang(); +extern int wait_stop(); +extern int wait_stopnohang(); + +#define wait_crashed(w) ((w) & 127) +#define wait_exitcode(w) ((w) >> 8) +#define wait_stopsig(w) ((w) >> 8) +#define wait_stopped(w) (((w) & 127) == 127) + +#endif diff --git a/wait_nohang.c b/wait_nohang.c new file mode 100644 index 0000000..bea2774 --- /dev/null +++ b/wait_nohang.c @@ -0,0 +1,12 @@ +#include +#include +#include "haswaitp.h" + +int wait_nohang(wstat) int *wstat; +{ +#ifdef HASWAITPID + return waitpid(-1,wstat,WNOHANG); +#else + return wait3(wstat,WNOHANG,(struct rusage *) 0); +#endif +} diff --git a/wait_pid.c b/wait_pid.c new file mode 100644 index 0000000..d7a7e84 --- /dev/null +++ b/wait_pid.c @@ -0,0 +1,39 @@ +#include +#include +#include "error.h" +#include "haswaitp.h" + +#ifdef HASWAITPID + +int wait_pid(wstat,pid) int *wstat; int pid; +{ + int r; + + do + r = waitpid(pid,wstat,0); + while ((r == -1) && (errno == error_intr)); + return r; +} + +#else + +/* XXX untested */ +/* XXX breaks down with more than two children */ +static int oldpid = 0; +static int oldwstat; /* defined if(oldpid) */ + +int wait_pid(wstat,pid) int *wstat; int pid; +{ + int r; + + if (pid == oldpid) { *wstat = oldwstat; oldpid = 0; return pid; } + + do { + r = wait(wstat); + if ((r != pid) && (r != -1)) { oldwstat = *wstat; oldpid = r; continue; } + } + while ((r == -1) && (errno == error_intr)); + return r; +} + +#endif diff --git a/warn-auto.sh b/warn-auto.sh new file mode 100644 index 0000000..36d2313 --- /dev/null +++ b/warn-auto.sh @@ -0,0 +1,2 @@ +#!/bin/sh +# WARNING: This file was auto-generated. Do not edit! diff --git a/warn-shsgr b/warn-shsgr new file mode 100644 index 0000000..37c351e --- /dev/null +++ b/warn-shsgr @@ -0,0 +1,3 @@ +Oops. Your getgroups() returned 0, and setgroups() failed; this means +that I can't reliably do my shsgr test. Please either ``make'' as root +or ``make'' while you're in one or more supplementary groups.