diff options
-rw-r--r-- | BIN.Makefile | 24 | ||||
-rw-r--r-- | BIN.README | 19 | ||||
-rw-r--r-- | BLURB | 43 | ||||
-rw-r--r-- | BLURB2 | 26 | ||||
-rw-r--r-- | BLURB3 | 93 | ||||
-rw-r--r-- | BLURB4 | 44 | ||||
-rw-r--r-- | CHANGES | 1484 | ||||
-rw-r--r-- | COPYRIGHT | 33 | ||||
-rw-r--r-- | FAQ | 709 | ||||
-rw-r--r-- | FILES | 434 | ||||
-rw-r--r-- | INSTALL | 1 | ||||
-rw-r--r-- | INSTALL.alias | 40 | ||||
-rw-r--r-- | INSTALL.ctl | 38 | ||||
-rw-r--r-- | INSTALL.ids | 72 | ||||
-rw-r--r-- | INSTALL.maildir | 59 | ||||
-rw-r--r-- | INSTALL.mbox | 53 | ||||
-rw-r--r-- | INSTALL.vsm | 50 | ||||
-rw-r--r-- | INTERNALS | 156 | ||||
-rw-r--r-- | Makefile | 2141 | ||||
-rw-r--r-- | PIC.local2alias | 37 | ||||
-rw-r--r-- | PIC.local2ext | 41 | ||||
-rw-r--r-- | PIC.local2local | 40 | ||||
-rw-r--r-- | PIC.local2rem | 38 | ||||
-rw-r--r-- | PIC.local2virt | 44 | ||||
-rw-r--r-- | PIC.nullclient | 38 | ||||
-rw-r--r-- | PIC.relaybad | 8 | ||||
-rw-r--r-- | PIC.relaygood | 33 | ||||
-rw-r--r-- | PIC.rem2local | 36 | ||||
-rw-r--r-- | README | 295 | ||||
-rw-r--r-- | REMOVE.binmail | 16 | ||||
-rw-r--r-- | REMOVE.sendmail | 28 | ||||
-rw-r--r-- | SECURITY | 131 | ||||
-rw-r--r-- | SENDMAIL | 76 | ||||
-rw-r--r-- | SYSDEPS | 17 | ||||
-rw-r--r-- | TARGETS | 387 | ||||
-rw-r--r-- | TEST.deliver | 82 | ||||
-rw-r--r-- | TEST.receive | 41 | ||||
-rw-r--r-- | THANKS | 338 | ||||
-rw-r--r-- | THOUGHTS | 418 | ||||
-rw-r--r-- | TODO | 23 | ||||
-rw-r--r-- | UPGRADE | 66 | ||||
-rw-r--r-- | VERSION | 1 | ||||
-rw-r--r-- | addresses.5 | 260 | ||||
-rw-r--r-- | alloc.3 | 62 | ||||
-rw-r--r-- | alloc.c | 32 | ||||
-rw-r--r-- | alloc.h | 8 | ||||
-rw-r--r-- | alloc_re.c | 17 | ||||
-rw-r--r-- | auto-gid.c | 51 | ||||
-rw-r--r-- | auto-int.c | 40 | ||||
-rw-r--r-- | auto-int8.c | 40 | ||||
-rw-r--r-- | auto-str.c | 44 | ||||
-rw-r--r-- | auto-uid.c | 51 | ||||
-rw-r--r-- | auto_break.h | 6 | ||||
-rw-r--r-- | auto_patrn.h | 6 | ||||
-rw-r--r-- | auto_qmail.h | 6 | ||||
-rw-r--r-- | auto_spawn.h | 6 | ||||
-rw-r--r-- | auto_split.h | 6 | ||||
-rw-r--r-- | auto_uids.h | 16 | ||||
-rw-r--r-- | auto_usera.h | 6 | ||||
-rw-r--r-- | binm1+df.sh | 11 | ||||
-rw-r--r-- | binm1.sh | 10 | ||||
-rw-r--r-- | binm2+df.sh | 11 | ||||
-rw-r--r-- | binm2.sh | 10 | ||||
-rw-r--r-- | binm3+df.sh | 11 | ||||
-rw-r--r-- | binm3.sh | 10 | ||||
-rw-r--r-- | bouncesaying.1 | 71 | ||||
-rw-r--r-- | bouncesaying.c | 41 | ||||
-rw-r--r-- | byte.h | 13 | ||||
-rw-r--r-- | byte_chr.c | 20 | ||||
-rw-r--r-- | byte_copy.c | 14 | ||||
-rw-r--r-- | byte_cr.c | 16 | ||||
-rw-r--r-- | byte_diff.c | 16 | ||||
-rw-r--r-- | byte_rchr.c | 23 | ||||
-rw-r--r-- | byte_zero.c | 13 | ||||
-rw-r--r-- | case.3 | 100 | ||||
-rw-r--r-- | case.h | 13 | ||||
-rw-r--r-- | case_diffb.c | 21 | ||||
-rw-r--r-- | case_diffs.c | 19 | ||||
-rw-r--r-- | case_lowerb.c | 14 | ||||
-rw-r--r-- | case_lowers.c | 12 | ||||
-rw-r--r-- | case_starts.c | 18 | ||||
-rw-r--r-- | cdb.3 | 62 | ||||
-rw-r--r-- | cdb.h | 12 | ||||
-rw-r--r-- | cdb_hash.c | 16 | ||||
-rw-r--r-- | cdb_seek.c | 94 | ||||
-rw-r--r-- | cdb_unpack.c | 12 | ||||
-rw-r--r-- | cdbmake.h | 35 | ||||
-rw-r--r-- | cdbmake_add.c | 118 | ||||
-rw-r--r-- | cdbmake_hash.c | 10 | ||||
-rw-r--r-- | cdbmake_pack.c | 11 | ||||
-rw-r--r-- | cdbmss.c | 65 | ||||
-rw-r--r-- | cdbmss.h | 16 | ||||
-rw-r--r-- | chkshsgr.c | 9 | ||||
-rw-r--r-- | chkspawn.c | 48 | ||||
-rw-r--r-- | coe.3 | 25 | ||||
-rw-r--r-- | coe.c | 8 | ||||
-rw-r--r-- | coe.h | 6 | ||||
-rw-r--r-- | commands.c | 40 | ||||
-rw-r--r-- | commands.h | 12 | ||||
-rw-r--r-- | condredirect.1 | 63 | ||||
-rw-r--r-- | condredirect.c | 85 | ||||
-rw-r--r-- | conf-break | 9 | ||||
-rw-r--r-- | conf-cc | 3 | ||||
-rw-r--r-- | conf-groups | 6 | ||||
-rw-r--r-- | conf-ld | 3 | ||||
-rw-r--r-- | conf-patrn | 6 | ||||
-rw-r--r-- | conf-qmail | 11 | ||||
-rw-r--r-- | conf-spawn | 5 | ||||
-rw-r--r-- | conf-split | 3 | ||||
-rw-r--r-- | conf-users | 15 | ||||
-rw-r--r-- | config-fast.sh | 30 | ||||
-rw-r--r-- | config.sh | 64 | ||||
-rw-r--r-- | constmap.c | 114 | ||||
-rw-r--r-- | constmap.h | 20 | ||||
-rw-r--r-- | control.c | 130 | ||||
-rw-r--r-- | control.h | 10 | ||||
-rw-r--r-- | date822fmt.c | 29 | ||||
-rw-r--r-- | date822fmt.h | 7 | ||||
-rw-r--r-- | datemail.sh | 1 | ||||
-rw-r--r-- | datetime.3 | 73 | ||||
-rw-r--r-- | datetime.c | 55 | ||||
-rw-r--r-- | datetime.h | 20 | ||||
-rw-r--r-- | datetime_un.c | 35 | ||||
-rw-r--r-- | direntry.3 | 36 | ||||
-rw-r--r-- | direntry.h1 | 8 | ||||
-rw-r--r-- | direntry.h2 | 8 | ||||
-rw-r--r-- | dns.c | 398 | ||||
-rw-r--r-- | dns.h | 14 | ||||
-rw-r--r-- | dnscname.c | 25 | ||||
-rw-r--r-- | dnsdoe.c | 16 | ||||
-rw-r--r-- | dnsdoe.h | 6 | ||||
-rw-r--r-- | dnsfq.c | 32 | ||||
-rw-r--r-- | dnsip.c | 34 | ||||
-rw-r--r-- | dnsmxip.c | 40 | ||||
-rw-r--r-- | dnsptr.c | 27 | ||||
-rw-r--r-- | dot-qmail.9 | 394 | ||||
-rw-r--r-- | elq.sh | 1 | ||||
-rw-r--r-- | env.3 | 31 | ||||
-rw-r--r-- | env.c | 113 | ||||
-rw-r--r-- | env.h | 17 | ||||
-rw-r--r-- | envelopes.5 | 231 | ||||
-rw-r--r-- | envread.c | 30 | ||||
-rw-r--r-- | error.3 | 45 | ||||
-rw-r--r-- | error.c | 95 | ||||
-rw-r--r-- | error.h | 23 | ||||
-rw-r--r-- | error_str.3 | 19 | ||||
-rw-r--r-- | error_str.c | 276 | ||||
-rw-r--r-- | error_temp.3 | 27 | ||||
-rw-r--r-- | error_temp.c | 80 | ||||
-rw-r--r-- | except.1 | 33 | ||||
-rw-r--r-- | except.c | 37 | ||||
-rw-r--r-- | exit.h | 6 | ||||
-rw-r--r-- | extra.h | 7 | ||||
-rw-r--r-- | fd.h | 7 | ||||
-rw-r--r-- | fd_copy.3 | 44 | ||||
-rw-r--r-- | fd_copy.c | 13 | ||||
-rw-r--r-- | fd_move.3 | 41 | ||||
-rw-r--r-- | fd_move.c | 11 | ||||
-rw-r--r-- | fifo.c | 10 | ||||
-rw-r--r-- | fifo.h | 6 | ||||
-rw-r--r-- | fifo_make.3 | 24 | ||||
-rw-r--r-- | find-systype.sh | 144 | ||||
-rw-r--r-- | fmt.h | 25 | ||||
-rw-r--r-- | fmt_str.c | 12 | ||||
-rw-r--r-- | fmt_strn.c | 12 | ||||
-rw-r--r-- | fmt_uint.c | 6 | ||||
-rw-r--r-- | fmt_uint0.c | 10 | ||||
-rw-r--r-- | fmt_ulong.c | 13 | ||||
-rw-r--r-- | fmtqfn.c | 24 | ||||
-rw-r--r-- | fmtqfn.h | 8 | ||||
-rw-r--r-- | forgeries.7 | 104 | ||||
-rw-r--r-- | fork.h1 | 7 | ||||
-rw-r--r-- | fork.h2 | 7 | ||||
-rw-r--r-- | forward.1 | 24 | ||||
-rw-r--r-- | forward.c | 60 | ||||
-rw-r--r-- | gen_alloc.h | 7 | ||||
-rw-r--r-- | gen_allocdefs.h | 34 | ||||
-rw-r--r-- | getln.3 | 51 | ||||
-rw-r--r-- | getln.c | 20 | ||||
-rw-r--r-- | getln.h | 7 | ||||
-rw-r--r-- | getln2.3 | 64 | ||||
-rw-r--r-- | getln2.c | 31 | ||||
-rw-r--r-- | gfrom.c | 10 | ||||
-rw-r--r-- | gfrom.h | 6 | ||||
-rw-r--r-- | headerbody.c | 87 | ||||
-rw-r--r-- | headerbody.h | 6 | ||||
-rw-r--r-- | hfield.c | 125 | ||||
-rw-r--r-- | hfield.h | 38 | ||||
-rw-r--r-- | hier.c | 252 | ||||
-rw-r--r-- | home+df.sh | 9 | ||||
-rw-r--r-- | home.sh | 7 | ||||
-rw-r--r-- | hostname.c | 17 | ||||
-rw-r--r-- | idedit.c | 147 | ||||
-rw-r--r-- | install-big.c | 285 | ||||
-rw-r--r-- | install.c | 164 | ||||
-rw-r--r-- | instcheck.c | 108 | ||||
-rw-r--r-- | ip.c | 53 | ||||
-rw-r--r-- | ip.h | 11 | ||||
-rw-r--r-- | ipalloc.c | 7 | ||||
-rw-r--r-- | ipalloc.h | 14 | ||||
-rw-r--r-- | ipme.c | 100 | ||||
-rw-r--r-- | ipme.h | 12 | ||||
-rw-r--r-- | ipmeprint.c | 24 | ||||
-rw-r--r-- | lock.h | 8 | ||||
-rw-r--r-- | lock_ex.c | 11 | ||||
-rw-r--r-- | lock_exnb.c | 11 | ||||
-rw-r--r-- | lock_un.c | 11 | ||||
-rw-r--r-- | maildir.5 | 239 | ||||
-rw-r--r-- | maildir.c | 108 | ||||
-rw-r--r-- | maildir.h | 12 | ||||
-rw-r--r-- | maildir2mbox.1 | 53 | ||||
-rw-r--r-- | maildir2mbox.c | 162 | ||||
-rw-r--r-- | maildirmake.1 | 15 | ||||
-rw-r--r-- | maildirmake.c | 24 | ||||
-rw-r--r-- | maildirwatch.1 | 23 | ||||
-rw-r--r-- | maildirwatch.c | 125 | ||||
-rw-r--r-- | mailsubj.1 | 38 | ||||
-rw-r--r-- | mailsubj.sh | 7 | ||||
-rw-r--r-- | make-compile.sh | 1 | ||||
-rw-r--r-- | make-load.sh | 2 | ||||
-rw-r--r-- | make-makelib.sh | 16 | ||||
-rw-r--r-- | mbox.5 | 235 | ||||
-rw-r--r-- | myctime.c | 37 | ||||
-rw-r--r-- | myctime.h | 6 | ||||
-rw-r--r-- | ndelay.c | 13 | ||||
-rw-r--r-- | ndelay.h | 7 | ||||
-rw-r--r-- | ndelay_off.c | 13 | ||||
-rw-r--r-- | newfield.c | 68 | ||||
-rw-r--r-- | newfield.h | 12 | ||||
-rw-r--r-- | now.3 | 14 | ||||
-rw-r--r-- | now.c | 8 | ||||
-rw-r--r-- | now.h | 8 | ||||
-rw-r--r-- | old-patches/README | 3 | ||||
-rw-r--r-- | old-patches/netqmail-1.04.patch | 414 | ||||
-rw-r--r-- | old-patches/qmail-isoc.patch | 362 | ||||
-rw-r--r-- | open.h | 10 | ||||
-rw-r--r-- | open_append.c | 6 | ||||
-rw-r--r-- | open_excl.c | 6 | ||||
-rw-r--r-- | open_read.c | 6 | ||||
-rw-r--r-- | open_trunc.c | 6 | ||||
-rw-r--r-- | open_write.c | 6 | ||||
-rw-r--r-- | other-patches/README | 51 | ||||
-rw-r--r-- | other-patches/checkpassword-0.90.errno.patch | 12 | ||||
-rw-r--r-- | other-patches/daemontools-0.76.errno.patch | 12 | ||||
-rw-r--r-- | other-patches/djbdns-1.05.errno.patch | 12 | ||||
-rw-r--r-- | other-patches/dot-forward-0.71.errno.patch | 12 | ||||
-rw-r--r-- | other-patches/fastforward-0.51.errno.patch | 22 | ||||
-rw-r--r-- | other-patches/mess822-0.58.errno.patch | 33 | ||||
-rw-r--r-- | other-patches/qmailanalog-0.70.errno.patch | 87 | ||||
-rw-r--r-- | other-patches/serialmail-0.75.errno.patch | 23 | ||||
-rw-r--r-- | other-patches/ucspi-tcp-0.88.a_record.patch | 64 | ||||
-rw-r--r-- | other-patches/ucspi-tcp-0.88.errno.patch | 12 | ||||
-rw-r--r-- | other-patches/ucspi-tcp-0.88.nodefaultrbl.patch | 28 | ||||
-rw-r--r-- | pinq.sh | 1 | ||||
-rw-r--r-- | predate.c | 116 | ||||
-rw-r--r-- | preline.1 | 57 | ||||
-rw-r--r-- | preline.c | 90 | ||||
-rw-r--r-- | prioq.c | 58 | ||||
-rw-r--r-- | prioq.h | 15 | ||||
-rw-r--r-- | proc+df.sh | 9 | ||||
-rw-r--r-- | proc.sh | 7 | ||||
-rw-r--r-- | prot.c | 21 | ||||
-rw-r--r-- | prot.h | 7 | ||||
-rw-r--r-- | qail.sh | 1 | ||||
-rw-r--r-- | qbiff.1 | 31 | ||||
-rw-r--r-- | qbiff.c | 113 | ||||
-rw-r--r-- | qlx.h | 18 | ||||
-rw-r--r-- | qmail-clean.8 | 13 | ||||
-rw-r--r-- | qmail-clean.c | 98 | ||||
-rw-r--r-- | qmail-command.8 | 149 | ||||
-rw-r--r-- | qmail-control.9 | 78 | ||||
-rw-r--r-- | qmail-getpw.9 | 114 | ||||
-rw-r--r-- | qmail-getpw.c | 88 | ||||
-rw-r--r-- | qmail-header.5 | 332 | ||||
-rw-r--r-- | qmail-inject.8 | 309 | ||||
-rw-r--r-- | qmail-inject.c | 773 | ||||
-rw-r--r-- | qmail-limits.9 | 30 | ||||
-rw-r--r-- | qmail-local.8 | 99 | ||||
-rw-r--r-- | qmail-local.c | 698 | ||||
-rw-r--r-- | qmail-log.5 | 266 | ||||
-rw-r--r-- | qmail-lspawn.8 | 46 | ||||
-rw-r--r-- | qmail-lspawn.c | 234 | ||||
-rw-r--r-- | qmail-newmrh.9 | 41 | ||||
-rw-r--r-- | qmail-newmrh.c | 70 | ||||
-rw-r--r-- | qmail-newu.9 | 43 | ||||
-rw-r--r-- | qmail-newu.c | 137 | ||||
-rw-r--r-- | qmail-pop3d.8 | 43 | ||||
-rw-r--r-- | qmail-pop3d.c | 305 | ||||
-rw-r--r-- | qmail-popup.8 | 65 | ||||
-rw-r--r-- | qmail-popup.c | 183 | ||||
-rw-r--r-- | qmail-pw2u.9 | 241 | ||||
-rw-r--r-- | qmail-pw2u.c | 312 | ||||
-rw-r--r-- | qmail-qmqpc.8 | 29 | ||||
-rw-r--r-- | qmail-qmqpc.c | 159 | ||||
-rw-r--r-- | qmail-qmqpd.8 | 25 | ||||
-rw-r--r-- | qmail-qmqpd.c | 174 | ||||
-rw-r--r-- | qmail-qmtpd.8 | 32 | ||||
-rw-r--r-- | qmail-qmtpd.c | 268 | ||||
-rw-r--r-- | qmail-qread.8 | 24 | ||||
-rw-r--r-- | qmail-qread.c | 175 | ||||
-rw-r--r-- | qmail-qstat.8 | 18 | ||||
-rw-r--r-- | qmail-qstat.sh | 7 | ||||
-rw-r--r-- | qmail-queue.8 | 161 | ||||
-rw-r--r-- | qmail-queue.c | 254 | ||||
-rw-r--r-- | qmail-remote.8 | 205 | ||||
-rw-r--r-- | qmail-remote.c | 427 | ||||
-rw-r--r-- | qmail-rspawn.8 | 21 | ||||
-rw-r--r-- | qmail-rspawn.c | 103 | ||||
-rw-r--r-- | qmail-send.9 | 246 | ||||
-rw-r--r-- | qmail-send.c | 1612 | ||||
-rw-r--r-- | qmail-showctl.8 | 12 | ||||
-rw-r--r-- | qmail-showctl.c | 306 | ||||
-rw-r--r-- | qmail-smtpd.8 | 179 | ||||
-rw-r--r-- | qmail-smtpd.c | 421 | ||||
-rw-r--r-- | qmail-start.9 | 94 | ||||
-rw-r--r-- | qmail-start.c | 120 | ||||
-rw-r--r-- | qmail-tcpok.8 | 24 | ||||
-rw-r--r-- | qmail-tcpok.c | 35 | ||||
-rw-r--r-- | qmail-tcpto.8 | 30 | ||||
-rw-r--r-- | qmail-tcpto.c | 85 | ||||
-rw-r--r-- | qmail-upq.sh | 14 | ||||
-rw-r--r-- | qmail-users.9 | 113 | ||||
-rw-r--r-- | qmail.7 | 68 | ||||
-rw-r--r-- | qmail.c | 136 | ||||
-rw-r--r-- | qmail.h | 24 | ||||
-rw-r--r-- | qreceipt.1 | 33 | ||||
-rw-r--r-- | qreceipt.c | 131 | ||||
-rw-r--r-- | qsmhook.c | 137 | ||||
-rw-r--r-- | qsutil.c | 46 | ||||
-rw-r--r-- | qsutil.h | 12 | ||||
-rw-r--r-- | quote.c | 83 | ||||
-rw-r--r-- | quote.h | 8 | ||||
-rw-r--r-- | rcpthosts.c | 60 | ||||
-rw-r--r-- | rcpthosts.h | 7 | ||||
-rw-r--r-- | readsubdir.c | 49 | ||||
-rw-r--r-- | readsubdir.h | 20 | ||||
-rw-r--r-- | readwrite.h | 7 | ||||
-rw-r--r-- | received.c | 71 | ||||
-rw-r--r-- | received.h | 6 | ||||
-rw-r--r-- | remoteinfo.c | 77 | ||||
-rw-r--r-- | remoteinfo.h | 6 | ||||
-rw-r--r-- | scan.h | 27 | ||||
-rw-r--r-- | scan_8long.c | 11 | ||||
-rw-r--r-- | scan_ulong.c | 11 | ||||
-rw-r--r-- | seek.h | 15 | ||||
-rw-r--r-- | seek_cur.c | 7 | ||||
-rw-r--r-- | seek_end.c | 7 | ||||
-rw-r--r-- | seek_set.c | 7 | ||||
-rw-r--r-- | seek_trunc.c | 5 | ||||
-rw-r--r-- | select.h1 | 8 | ||||
-rw-r--r-- | select.h2 | 9 | ||||
-rw-r--r-- | sendmail.c | 162 | ||||
-rw-r--r-- | sgetopt.3 | 28 | ||||
-rw-r--r-- | sgetopt.c | 54 | ||||
-rw-r--r-- | sgetopt.h | 21 | ||||
-rw-r--r-- | sig.h | 43 | ||||
-rw-r--r-- | sig_alarm.c | 7 | ||||
-rw-r--r-- | sig_block.c | 40 | ||||
-rw-r--r-- | sig_bug.c | 17 | ||||
-rw-r--r-- | sig_catch.c | 18 | ||||
-rw-r--r-- | sig_child.c | 7 | ||||
-rw-r--r-- | sig_hup.c | 7 | ||||
-rw-r--r-- | sig_misc.c | 17 | ||||
-rw-r--r-- | sig_pause.c | 14 | ||||
-rw-r--r-- | sig_pipe.c | 5 | ||||
-rw-r--r-- | sig_term.c | 7 | ||||
-rw-r--r-- | slurpclose.c | 19 | ||||
-rw-r--r-- | slurpclose.h | 6 | ||||
-rw-r--r-- | spawn.c | 260 | ||||
-rw-r--r-- | splogger.8 | 60 | ||||
-rw-r--r-- | splogger.c | 72 | ||||
-rw-r--r-- | str.h | 14 | ||||
-rw-r--r-- | str_chr.c | 19 | ||||
-rw-r--r-- | str_cpy.c | 16 | ||||
-rw-r--r-- | str_diff.c | 17 | ||||
-rw-r--r-- | str_diffn.c | 18 | ||||
-rw-r--r-- | str_len.c | 15 | ||||
-rw-r--r-- | str_rchr.c | 22 | ||||
-rw-r--r-- | str_start.c | 15 | ||||
-rw-r--r-- | stralloc.3 | 160 | ||||
-rw-r--r-- | stralloc.h | 21 | ||||
-rw-r--r-- | stralloc_arts.c | 12 | ||||
-rw-r--r-- | stralloc_cat.c | 9 | ||||
-rw-r--r-- | stralloc_catb.c | 15 | ||||
-rw-r--r-- | stralloc_cats.c | 10 | ||||
-rw-r--r-- | stralloc_copy.c | 9 | ||||
-rw-r--r-- | stralloc_eady.c | 6 | ||||
-rw-r--r-- | stralloc_opyb.c | 14 | ||||
-rw-r--r-- | stralloc_opys.c | 10 | ||||
-rw-r--r-- | stralloc_pend.c | 5 | ||||
-rw-r--r-- | strerr.h | 80 | ||||
-rw-r--r-- | strerr_die.c | 37 | ||||
-rw-r--r-- | strerr_sys.c | 12 | ||||
-rw-r--r-- | subfd.h | 15 | ||||
-rw-r--r-- | subfderr.c | 7 | ||||
-rw-r--r-- | subfdin.c | 13 | ||||
-rw-r--r-- | subfdins.c | 13 | ||||
-rw-r--r-- | subfdout.c | 7 | ||||
-rw-r--r-- | subfdouts.c | 7 | ||||
-rw-r--r-- | subgetopt.3 | 357 | ||||
-rw-r--r-- | subgetopt.c | 79 | ||||
-rw-r--r-- | subgetopt.h | 24 | ||||
-rw-r--r-- | substdi.c | 91 | ||||
-rw-r--r-- | substdio.c | 15 | ||||
-rw-r--r-- | substdio.h | 47 | ||||
-rw-r--r-- | substdio_copy.c | 18 | ||||
-rw-r--r-- | substdo.c | 108 | ||||
-rw-r--r-- | tcp-env.1 | 67 | ||||
-rw-r--r-- | tcp-env.c | 129 | ||||
-rw-r--r-- | tcp-environ.5 | 62 | ||||
-rw-r--r-- | tcpto.c | 165 | ||||
-rw-r--r-- | tcpto.h | 8 | ||||
-rw-r--r-- | tcpto_clean.c | 20 | ||||
-rw-r--r-- | timeoutconn.c | 59 | ||||
-rw-r--r-- | timeoutconn.h | 6 | ||||
-rw-r--r-- | timeoutread.c | 22 | ||||
-rw-r--r-- | timeoutread.h | 6 | ||||
-rw-r--r-- | timeoutwrite.c | 22 | ||||
-rw-r--r-- | timeoutwrite.h | 6 | ||||
-rw-r--r-- | token822.c | 513 | ||||
-rw-r--r-- | token822.h | 37 | ||||
-rw-r--r-- | trigger.c | 41 | ||||
-rw-r--r-- | trigger.h | 8 | ||||
-rw-r--r-- | triggerpull.c | 16 | ||||
-rw-r--r-- | triggerpull.h | 6 | ||||
-rw-r--r-- | trycpp.c | 7 | ||||
-rw-r--r-- | trydrent.c | 8 | ||||
-rw-r--r-- | tryflock.c | 8 | ||||
-rw-r--r-- | trylsock.c | 4 | ||||
-rw-r--r-- | trymkffo.c | 7 | ||||
-rw-r--r-- | trynpbg1.c | 26 | ||||
-rw-r--r-- | tryrsolv.c | 4 | ||||
-rw-r--r-- | trysalen.c | 11 | ||||
-rw-r--r-- | trysgact.c | 10 | ||||
-rw-r--r-- | trysgprm.c | 10 | ||||
-rw-r--r-- | tryshsgr.c | 14 | ||||
-rw-r--r-- | trysysel.c | 8 | ||||
-rw-r--r-- | trysyslog.c | 9 | ||||
-rw-r--r-- | tryulong32.c | 11 | ||||
-rw-r--r-- | tryvfork.c | 4 | ||||
-rw-r--r-- | trywaitp.c | 7 | ||||
-rw-r--r-- | uint32.h1 | 6 | ||||
-rw-r--r-- | uint32.h2 | 6 | ||||
-rw-r--r-- | wait.3 | 93 | ||||
-rw-r--r-- | wait.h | 14 | ||||
-rw-r--r-- | wait_nohang.c | 12 | ||||
-rw-r--r-- | wait_pid.c | 39 | ||||
-rw-r--r-- | warn-auto.sh | 2 | ||||
-rw-r--r-- | warn-shsgr | 3 |
449 files changed, 32673 insertions, 0 deletions
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. @@ -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. @@ -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. @@ -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) @@ -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. @@ -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 <sys/types.h>, 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) <route>''; + 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. @@ -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. @@ -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 @@ -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:<joe@heaven.af.mil> + RCPT TO:<bill@irs.gov> 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:<joe@heaven.af.mil> + RCPT TO:<bill@irs.gov> 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:<spammer@aol.com> + RCPT TO:<bill@irs.gov> + + 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:<joe@heaven.af.mil> + | RCPT TO:<bill@irs.gov> + | + | 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:<joe@heaven.af.mil> + RCPT TO:<bill@irs.gov> 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:<bill@irs.gov> + | RCPT TO:<joe@heaven.af.mil> + | + | 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. @@ -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 <nelson@qmail.org> +Charles Cazabon <charlesc-software-netqmail@pyropus.ca> +Dave Sill <de5@sws5.ornl.gov> +Peter Samuel <Peter.Samuel@gormand.com.au> +Henning Brauer <henning@bsws.de> +Andrew Richards <ar-nq@acrconsulting.co.uk> +Richard Lyons <rick-netqmail@frob.com.au> +John Levine <netqmail@johnlevine.com> +Scott Gifford <sgifford@suspectclass.com> +Kyle Wheeler <kyle-netqmail@memoryhole.net> +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. @@ -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 @@ -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 <me@domain> 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 <me@domain> 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 <me@domain> 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 <me@domain> + 250 ok + rcpt <me@domain> + 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. @@ -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. @@ -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 @@ -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. @@ -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:<God@heaven.af.mil> +.br + RCPT TO:<a\\"quote@heaven.af.mil> +.br + RCPT TO:<The\\ Almighty.One@heaven.af.mil> +.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) @@ -0,0 +1,62 @@ +.TH alloc 3 +.SH NAME +alloc \- allocate memory +.SH SYNTAX +.B #include <alloc.h> + +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) @@ -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); +} @@ -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 <sys/types.h> +#include <grp.h> +#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 <sys/types.h> +#include <pwd.h> +#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]); +} @@ -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; + } +} @@ -0,0 +1,100 @@ +.TH case 3 +.SH NAME +case \- convert ASCII uppercase bytes to lowercase +.SH SYNTAX +.B #include <case.h> + +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) @@ -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; + } +} @@ -0,0 +1,62 @@ +.TH cdb 3 +.SH NAME +cdb \- read from a constant database +.SH SYNTAX +.B #include <cdb.h> + +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) @@ -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 <sys/types.h> +#include <errno.h> +#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); +} @@ -0,0 +1,25 @@ +.TH coe 3 +.SH NAME +coe \- set close-on-exec flag for a descriptor +.SH SYNTAX +.B #include <coe.h> + +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) @@ -0,0 +1,8 @@ +#include <fcntl.h> +#include "coe.h" + +int coe(fd) +int fd; +{ + return fcntl(fd,F_SETFD,1); +} @@ -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. @@ -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. @@ -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 <datetime.h> + +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 <direntry.h> + +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 <sys/types.h> +#include <sys/dir.h> +#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 <sys/types.h> +#include <dirent.h> +#define direntry struct dirent + +#endif @@ -0,0 +1,398 @@ +#include <stdio.h> +#include <netdb.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <resolv.h> +#include <errno.h> +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; +} @@ -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 @@ -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); +} @@ -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 + &<me@new.job.com> +.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) @@ -0,0 +1 @@ +QMAIL/bin/maildir2mbox && exec elm ${1+"$@"} @@ -0,0 +1,31 @@ +.TH env 3 +.SH NAME +env \- manage the environment +.SH SYNTAX +.B #include <env.h> + +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) @@ -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; +} @@ -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; +} @@ -0,0 +1,45 @@ +.TH error 3 +.SH NAME +error \- syscall error codes +.SH SYNTAX +.B #include <error.h> +.br +.B #include <errno.h> + +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) @@ -0,0 +1,95 @@ +#include <errno.h> +#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 @@ -0,0 +1,23 @@ +#ifndef ERROR_H +#define ERROR_H + +#include <errno.h> + +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 <error.h> + +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 <errno.h> +#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 <error.h> + +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 <errno.h> +#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); + } +} @@ -0,0 +1,6 @@ +#ifndef EXIT_H +#define EXIT_H + +extern void _exit(); + +#endif @@ -0,0 +1,7 @@ +#ifndef EXTRA_H +#define EXTRA_H + +#define QUEUE_EXTRA "" +#define QUEUE_EXTRALEN 0 + +#endif @@ -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 <fd.h> + +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 <fcntl.h> +#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 <fd.h> + +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; +} @@ -0,0 +1,10 @@ +#include <sys/types.h> +#include <sys/stat.h> +#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 @@ -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 <fifo.h> + +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]' @@ -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) @@ -0,0 +1,7 @@ +#ifndef FORK_H +#define FORK_H + +extern int fork(); +#define vfork fork + +#endif @@ -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 @@ -0,0 +1,51 @@ +.TH getln 3 +.SH NAME +getln \- read one line of data +.SH SYNTAX +.B #include <getln.h> + +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) @@ -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; +} @@ -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 <getln.h> + +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); + } +} @@ -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); +} @@ -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 @@ -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 @@ -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 <sys/types.h> +#include <pwd.h> +#include <grp.h> +#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 <sys/types.h> +#include <sys/stat.h> +#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); +} @@ -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; +} @@ -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 @@ -0,0 +1,100 @@ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/in.h> +#ifndef SIOCGIFCONF /* whatever works */ +#include <sys/sockio.h> +#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; +} @@ -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); +} @@ -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 <sys/types.h> +#include <sys/file.h> +#include <fcntl.h> +#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 <sys/types.h> +#include <sys/file.h> +#include <fcntl.h> +#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 <sys/types.h> +#include <sys/file.h> +#include <fcntl.h> +#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 <sys/types.h> +#include <sys/stat.h> +#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 @@ -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: <god@heaven.af.mil> +.br + Delivered-To: djb@silverton.berkeley.edu +.br + Date: 3 Jan 1996 01:05:34 -0000 +.br + From: God <god@heaven.af.mil> +.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 <sys/types.h> +#include <fcntl.h> +#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 <sys/types.h> +#include <fcntl.h> +#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 @@ -0,0 +1,14 @@ +.TH now 3 +.SH NAME +now \- get current time, in seconds +.SH SYNTAX +.B #include <now.h> + +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) @@ -0,0 +1,8 @@ +#include <time.h> +#include "datetime.h" +#include "now.h" + +datetime_sec now() +{ + return time((long *) 0); +} @@ -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 <sys/types.h> + #include <errno.h> +-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 <errno.h> + 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 <error.h> +- +-extern int \fBerrno\fP; ++.B #include <errno.h> + + 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 <errno.h> + + 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 <craig-qmail@jcb-sc.com> + +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 <http://www.jcb-sc.com/qmail/patches/>. + +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" @@ -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 <sys/types.h> +#include <fcntl.h> +#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 <sys/types.h> +#include <fcntl.h> +#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 <sys/types.h> +#include <fcntl.h> +#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 <sys/types.h> +#include <fcntl.h> +#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 <sys/types.h> +#include <fcntl.h> +#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 <errno.h> + + 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 <errno.h> + + 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 <errno.h> + + 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 <errno.h> + + 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 <sys/types.h> + #include <errno.h> +-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 <errno.h> + + 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 <sys/types.h> + #include <errno.h> +-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 <errno.h> + + 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 <sys/stat.h> + #include <fcntl.h> + #include <errno.h> +-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 <error.h> +- +-extern int \fBerrno\fP; ++.br ++.B #include <errno.h> + + 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 <errno.h> + #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 <errno.h> + + 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 <errno.h> + + 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 <sys/stat.h> + #include <fcntl.h> + #include <errno.h> +-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 <errno.h> + + 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); @@ -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 <sys/types.h> +#include <time.h> +#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)); +} @@ -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; +} @@ -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 @@ -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 @@ -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); +} @@ -0,0 +1,7 @@ +#ifndef PROT_H +#define PROT_H + +extern int prot_gid(); +extern int prot_uid(); + +#endif @@ -0,0 +1 @@ +QMAIL/bin/maildir2mbox && exec Mail ${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) @@ -0,0 +1,113 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <utmp.h> +#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); +} @@ -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 <sys/types.h> +#include <sys/stat.h> +#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 <sys/types.h> +#include <sys/stat.h> +#include <pwd.h> +#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 <e@f>, +.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 <foo@[]> */ + 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 <sys/types.h> +#include <sys/stat.h> +#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 <sys/types.h> +#include <sys/stat.h> +#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 <sys/types.h> +#include <sys/stat.h> +#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 <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#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 <sys/types.h> +#include <sys/stat.h> +#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 <sys/types.h> +#include <sys/stat.h> +#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 <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#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 <sys/types.h> +#include <sys/stat.h> +#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 <sys/types.h> +#include <sys/stat.h> +#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) @@ -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. @@ -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)"; + } +} @@ -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 @@ -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); +} @@ -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 <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <fcntl.h> +#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 @@ -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; +} @@ -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 <sys/types.h> +#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 <sys/types.h> +#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 <sys/types.h> +#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 <sys/types.h> +#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 <sys/types.h> +#include <sys/time.h> +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 <sys/types.h> +#include <sys/time.h> +#include <sys/select.h> +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 <sgetopt.h> +.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 @@ -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 <signal.h> +#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 <signal.h> +#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 <signal.h> +#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 <signal.h> +#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 <signal.h> +#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 <signal.h> +#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 <signal.h> +#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 <signal.h> +#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 <signal.h> +#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 <signal.h> +#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 @@ -0,0 +1,260 @@ +#include <sys/types.h> +#include <sys/stat.h> +#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 <sys/types.h> +#include <sys/time.h> +#include <syslog.h> +#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; + } +} @@ -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 <stralloc.h> + +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 = ""; +} @@ -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 <subgetopt.h> + +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 <subgetopt.h> + +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 <subgetopt.h> , +unless +.B SUBGETOPTNOSHORT +is defined +when +.B <subgetopt.h> +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 <sys/types.h> +#include <sys/socket.h> +#include <sys/param.h> +#include <netinet/in.h> +#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) @@ -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); +} @@ -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 <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#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 <sys/types.h> +#include <dirent.h> + +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 <sys/types.h> +#include <sys/file.h> +#include <fcntl.h> + +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 <sys/types.h> +#include <sys/stat.h> + +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 <sys/types.h> +#include <sys/param.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <sys/socket.h> + +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 <signal.h> + +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 <signal.h> + +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 <sys/types.h> +#include <sys/time.h> +#include <sys/select.h> /* 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 <sys/types.h> +#include <sys/time.h> +#include <syslog.h> + +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 <sys/types.h> +#include <sys/wait.h> + +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 @@ -0,0 +1,93 @@ +.TH wait 3 +.SH NAME +wait \- check child process status +.SH SYNTAX +.B #include <wait.h> + +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) @@ -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 <sys/types.h> +#include <sys/wait.h> +#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 <sys/types.h> +#include <sys/wait.h> +#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. |