summaryrefslogtreecommitdiffstats
path: root/contrib/amd/scripts
diff options
context:
space:
mode:
authorobrien <obrien@FreeBSD.org>1998-08-23 22:07:21 +0000
committerobrien <obrien@FreeBSD.org>1998-08-23 22:07:21 +0000
commit663d5a0f32ed8dfc091ffb6153161591ac6ba563 (patch)
tree60b090a6cbdb64326bb128ea49a231d08eb2680e /contrib/amd/scripts
downloadFreeBSD-src-663d5a0f32ed8dfc091ffb6153161591ac6ba563.zip
FreeBSD-src-663d5a0f32ed8dfc091ffb6153161591ac6ba563.tar.gz
Virgin import of AMD (am-utils) v6.0a16
Diffstat (limited to 'contrib/amd/scripts')
-rw-r--r--contrib/amd/scripts/Makefile.am49
-rw-r--r--contrib/amd/scripts/Makefile.in379
-rw-r--r--contrib/amd/scripts/am-eject.in52
-rw-r--r--contrib/amd/scripts/amd.conf-sample94
-rw-r--r--contrib/amd/scripts/amd.conf.5539
-rwxr-xr-xcontrib/amd/scripts/amd2ldif.in58
-rwxr-xr-xcontrib/amd/scripts/amd2sun.in51
-rwxr-xr-xcontrib/amd/scripts/ctl-amd.in113
-rwxr-xr-xcontrib/amd/scripts/ctl-hlfsd.in101
-rw-r--r--contrib/amd/scripts/expn.11370
-rwxr-xr-xcontrib/amd/scripts/expn.in1370
-rwxr-xr-xcontrib/amd/scripts/fix-amd-map.in52
-rwxr-xr-xcontrib/amd/scripts/fixrmtab24
-rw-r--r--contrib/amd/scripts/lostaltmail.conf-sample84
-rwxr-xr-xcontrib/amd/scripts/lostaltmail.in648
-rwxr-xr-xcontrib/amd/scripts/wait4amd.in45
-rwxr-xr-xcontrib/amd/scripts/wait4amd2die.in49
17 files changed, 5078 insertions, 0 deletions
diff --git a/contrib/amd/scripts/Makefile.am b/contrib/amd/scripts/Makefile.am
new file mode 100644
index 0000000..9ebd691
--- /dev/null
+++ b/contrib/amd/scripts/Makefile.am
@@ -0,0 +1,49 @@
+## Process this file with automake to produce Makefile.in
+
+# Package: am-utils
+# Level: Makefile for scripts/ directory
+# Author: Erez Zadok
+
+sbin_SCRIPTS = \
+ am-eject \
+ amd2ldif \
+ amd2sun \
+ ctl-amd \
+ ctl-hlfsd \
+ fixrmtab \
+ fix-amd-map \
+ lostaltmail \
+ wait4amd \
+ wait4amd2die
+
+bin_SCRIPTS = \
+ expn
+
+sysconf_DATA = \
+ amd.conf-sample \
+ lostaltmail.conf-sample
+
+# man pages
+man_MANS = \
+ amd.conf.5 \
+ expn.1
+
+EXTRA_DIST = \
+ am-eject.in \
+ amd2ldif.in \
+ amd2sun.in \
+ ctl-amd.in \
+ ctl-hlfsd.in \
+ fixrmtab \
+ fix-amd-map.in \
+ lostaltmail.in \
+ wait4amd.in \
+ wait4amd2die.in \
+ \
+ expn.in \
+ \
+ amd.conf-sample \
+ lostaltmail.conf-sample \
+ $(man_MANS)
+
+CLEANFILES = $(sbin_SCRIPTS) $(bin_SCRIPTS)
diff --git a/contrib/amd/scripts/Makefile.in b/contrib/amd/scripts/Makefile.in
new file mode 100644
index 0000000..1c69cec
--- /dev/null
+++ b/contrib/amd/scripts/Makefile.in
@@ -0,0 +1,379 @@
+# Makefile.in generated automatically by automake 1.3.1 from Makefile.am
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+# Package: am-utils
+# Level: Makefile for scripts/ directory
+# Author: Erez Zadok
+
+
+SHELL = /bin/sh
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+mandir = @mandir@
+includedir = @includedir@
+oldincludedir = /usr/include
+
+DISTDIR =
+
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+
+top_builddir = ..
+
+ACLOCAL = @ACLOCAL@
+AUTOCONF = @AUTOCONF@
+AUTOMAKE = @AUTOMAKE@
+AUTOHEADER = @AUTOHEADER@
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+transform = @program_transform_name@
+
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+host_alias = @host_alias@
+host_triplet = @host@
+AR = @AR@
+CC = @CC@
+CPP = @CPP@
+LEX = @LEX@
+LIBTOOL = @LIBTOOL@
+LIBTOOL_LDFLAGS = @LIBTOOL_LDFLAGS@
+LTALLOCA = @LTALLOCA@
+LTLIBOBJS = @LTLIBOBJS@
+PACKAGE = @PACKAGE@
+PERL = @PERL@
+RANLIB = @RANLIB@
+VERSION = @VERSION@
+
+sbin_SCRIPTS = \
+ am-eject \
+ amd2ldif \
+ amd2sun \
+ ctl-amd \
+ ctl-hlfsd \
+ fixrmtab \
+ fix-amd-map \
+ lostaltmail \
+ wait4amd \
+ wait4amd2die
+
+bin_SCRIPTS = \
+ expn
+
+sysconf_DATA = \
+ amd.conf-sample \
+ lostaltmail.conf-sample
+
+# man pages
+man_MANS = \
+ amd.conf.5 \
+ expn.1
+
+EXTRA_DIST = \
+ am-eject.in \
+ amd2ldif.in \
+ amd2sun.in \
+ ctl-amd.in \
+ ctl-hlfsd.in \
+ fixrmtab \
+ fix-amd-map.in \
+ lostaltmail.in \
+ wait4amd.in \
+ wait4amd2die.in \
+ \
+ expn.in \
+ \
+ amd.conf-sample \
+ lostaltmail.conf-sample \
+ $(man_MANS)
+
+CLEANFILES = $(sbin_SCRIPTS) $(bin_SCRIPTS)
+mkinstalldirs = $(SHELL) $(top_srcdir)/aux/mkinstalldirs
+CONFIG_HEADER = ../config.h
+CONFIG_CLEAN_FILES = am-eject amd2ldif amd2sun ctl-amd ctl-hlfsd expn \
+fix-amd-map lostaltmail wait4amd wait4amd2die
+SCRIPTS = $(bin_SCRIPTS) $(sbin_SCRIPTS)
+
+man1dir = $(mandir)/man1
+man5dir = $(mandir)/man5
+MANS = $(man_MANS)
+
+NROFF = nroff
+DATA = $(sysconf_DATA)
+
+DIST_COMMON = Makefile.am Makefile.in am-eject.in amd2ldif.in \
+amd2sun.in ctl-amd.in ctl-hlfsd.in expn.in fix-amd-map.in \
+lostaltmail.in wait4amd.in wait4amd2die.in
+
+
+DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
+
+TAR = gtar
+GZIP = --best
+all: Makefile $(SCRIPTS) $(MANS) $(DATA)
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/./aux/configure.in $(ACLOCAL_M4)
+ cd $(top_srcdir) && $(AUTOMAKE) --localdir=./aux --gnu --include-deps scripts/Makefile
+
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ cd $(top_builddir) \
+ && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
+
+am-eject: $(top_builddir)/config.status am-eject.in
+ cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+amd2ldif: $(top_builddir)/config.status amd2ldif.in
+ cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+amd2sun: $(top_builddir)/config.status amd2sun.in
+ cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+ctl-amd: $(top_builddir)/config.status ctl-amd.in
+ cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+ctl-hlfsd: $(top_builddir)/config.status ctl-hlfsd.in
+ cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+expn: $(top_builddir)/config.status expn.in
+ cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+fix-amd-map: $(top_builddir)/config.status fix-amd-map.in
+ cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+lostaltmail: $(top_builddir)/config.status lostaltmail.in
+ cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+wait4amd: $(top_builddir)/config.status wait4amd.in
+ cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+wait4amd2die: $(top_builddir)/config.status wait4amd2die.in
+ cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+
+install-binSCRIPTS: $(bin_SCRIPTS)
+ @$(NORMAL_INSTALL)
+ $(mkinstalldirs) $(DESTDIR)$(bindir)
+ @list='$(bin_SCRIPTS)'; for p in $$list; do \
+ if test -f $$p; then \
+ echo " $(INSTALL_SCRIPT) $$p $(DESTDIR)$(bindir)/`echo $$p|sed '$(transform)'`"; \
+ $(INSTALL_SCRIPT) $$p $(DESTDIR)$(bindir)/`echo $$p|sed '$(transform)'`; \
+ else if test -f $(srcdir)/$$p; then \
+ echo " $(INSTALL_SCRIPT) $(srcdir)/$$p $(DESTDIR)$(bindir)/`echo $$p|sed '$(transform)'`"; \
+ $(INSTALL_SCRIPT) $(srcdir)/$$p $(DESTDIR)$(bindir)/`echo $$p|sed '$(transform)'`; \
+ else :; fi; fi; \
+ done
+
+uninstall-binSCRIPTS:
+ @$(NORMAL_UNINSTALL)
+ list='$(bin_SCRIPTS)'; for p in $$list; do \
+ rm -f $(DESTDIR)$(bindir)/`echo $$p|sed '$(transform)'`; \
+ done
+
+install-sbinSCRIPTS: $(sbin_SCRIPTS)
+ @$(NORMAL_INSTALL)
+ $(mkinstalldirs) $(DESTDIR)$(sbindir)
+ @list='$(sbin_SCRIPTS)'; for p in $$list; do \
+ if test -f $$p; then \
+ echo " $(INSTALL_SCRIPT) $$p $(DESTDIR)$(sbindir)/`echo $$p|sed '$(transform)'`"; \
+ $(INSTALL_SCRIPT) $$p $(DESTDIR)$(sbindir)/`echo $$p|sed '$(transform)'`; \
+ else if test -f $(srcdir)/$$p; then \
+ echo " $(INSTALL_SCRIPT) $(srcdir)/$$p $(DESTDIR)$(sbindir)/`echo $$p|sed '$(transform)'`"; \
+ $(INSTALL_SCRIPT) $(srcdir)/$$p $(DESTDIR)$(sbindir)/`echo $$p|sed '$(transform)'`; \
+ else :; fi; fi; \
+ done
+
+uninstall-sbinSCRIPTS:
+ @$(NORMAL_UNINSTALL)
+ list='$(sbin_SCRIPTS)'; for p in $$list; do \
+ rm -f $(DESTDIR)$(sbindir)/`echo $$p|sed '$(transform)'`; \
+ done
+
+install-man1:
+ $(mkinstalldirs) $(DESTDIR)$(man1dir)
+ @list='$(man1_MANS)'; \
+ l2='$(man_MANS)'; for i in $$l2; do \
+ case "$$i" in \
+ *.1*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+ else file=$$i; fi; \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " $(INSTALL_DATA) $$file $(DESTDIR)$(man1dir)/$$inst"; \
+ $(INSTALL_DATA) $$file $(DESTDIR)$(man1dir)/$$inst; \
+ done
+
+uninstall-man1:
+ @list='$(man1_MANS)'; \
+ l2='$(man_MANS)'; for i in $$l2; do \
+ case "$$i" in \
+ *.1*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " rm -f $(DESTDIR)$(man1dir)/$$inst"; \
+ rm -f $(DESTDIR)$(man1dir)/$$inst; \
+ done
+
+install-man5:
+ $(mkinstalldirs) $(DESTDIR)$(man5dir)
+ @list='$(man5_MANS)'; \
+ l2='$(man_MANS)'; for i in $$l2; do \
+ case "$$i" in \
+ *.5*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+ else file=$$i; fi; \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " $(INSTALL_DATA) $$file $(DESTDIR)$(man5dir)/$$inst"; \
+ $(INSTALL_DATA) $$file $(DESTDIR)$(man5dir)/$$inst; \
+ done
+
+uninstall-man5:
+ @list='$(man5_MANS)'; \
+ l2='$(man_MANS)'; for i in $$l2; do \
+ case "$$i" in \
+ *.5*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " rm -f $(DESTDIR)$(man5dir)/$$inst"; \
+ rm -f $(DESTDIR)$(man5dir)/$$inst; \
+ done
+install-man: $(MANS)
+ @$(NORMAL_INSTALL)
+ $(MAKE) install-man1 install-man5
+uninstall-man:
+ @$(NORMAL_UNINSTALL)
+ $(MAKE) uninstall-man1 uninstall-man5
+
+install-sysconfDATA: $(sysconf_DATA)
+ @$(NORMAL_INSTALL)
+ $(mkinstalldirs) $(DESTDIR)$(sysconfdir)
+ @list='$(sysconf_DATA)'; for p in $$list; do \
+ if test -f $(srcdir)/$$p; then \
+ echo " $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(sysconfdir)/$$p"; \
+ $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(sysconfdir)/$$p; \
+ else if test -f $$p; then \
+ echo " $(INSTALL_DATA) $$p $(DESTDIR)$(sysconfdir)/$$p"; \
+ $(INSTALL_DATA) $$p $(DESTDIR)$(sysconfdir)/$$p; \
+ fi; fi; \
+ done
+
+uninstall-sysconfDATA:
+ @$(NORMAL_UNINSTALL)
+ list='$(sysconf_DATA)'; for p in $$list; do \
+ rm -f $(DESTDIR)$(sysconfdir)/$$p; \
+ done
+tags: TAGS
+TAGS:
+
+
+distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
+
+subdir = scripts
+
+distdir: $(DISTFILES)
+ @for file in $(DISTFILES); do \
+ d=$(srcdir); \
+ test -f $(distdir)/$$file \
+ || ln $$d/$$file $(distdir)/$$file 2> /dev/null \
+ || cp -p $$d/$$file $(distdir)/$$file; \
+ done
+info:
+dvi:
+check: all
+ $(MAKE)
+installcheck:
+install-exec: install-binSCRIPTS install-sbinSCRIPTS install-sysconfDATA
+ @$(NORMAL_INSTALL)
+
+install-data: install-man
+ @$(NORMAL_INSTALL)
+
+install: install-exec install-data all
+ @:
+
+uninstall: uninstall-binSCRIPTS uninstall-sbinSCRIPTS uninstall-man uninstall-sysconfDATA
+
+install-strip:
+ $(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' INSTALL_SCRIPT='$(INSTALL_PROGRAM)' install
+installdirs:
+ $(mkinstalldirs) $(DATADIR)$(bindir) $(DATADIR)$(sbindir) \
+ $(DESTDIR)$(mandir)/man1 $(DESTDIR)$(mandir)/man5 \
+ $(DATADIR)$(sysconfdir)
+
+
+mostlyclean-generic:
+ -test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES)
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -rm -f Makefile $(DISTCLEANFILES)
+ -rm -f config.cache config.log stamp-h stamp-h[0-9]*
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+ -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+mostlyclean: mostlyclean-generic
+
+clean: clean-generic mostlyclean
+
+distclean: distclean-generic clean
+ -rm -f config.status
+ -rm -f libtool
+
+maintainer-clean: maintainer-clean-generic distclean
+ @echo "This command is intended for maintainers to use;"
+ @echo "it deletes files that may require special tools to rebuild."
+
+.PHONY: uninstall-binSCRIPTS install-binSCRIPTS uninstall-sbinSCRIPTS \
+install-sbinSCRIPTS install-man1 uninstall-man1 install-man5 \
+uninstall-man5 install-man uninstall-man uninstall-sysconfDATA \
+install-sysconfDATA tags distdir info dvi installcheck install-exec \
+install-data install uninstall all installdirs mostlyclean-generic \
+distclean-generic clean-generic maintainer-clean-generic clean \
+mostlyclean distclean maintainer-clean
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/contrib/amd/scripts/am-eject.in b/contrib/amd/scripts/am-eject.in
new file mode 100644
index 0000000..ed7e2d0
--- /dev/null
+++ b/contrib/amd/scripts/am-eject.in
@@ -0,0 +1,52 @@
+#!/bin/sh
+# auto-unmount floppy/cd directory before ejecting device
+# script taken from Debian Linux's amd
+#
+# Package: am-utils-6.0
+# (Additional) author: Erez Zadok <ezk@cs.columbia.edu>
+
+# set path
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+PATH=@sbindir@:@bindir@:/usr/ucb:/usr/bin:/bin:${PATH}
+export PATH
+
+if [ $# -ne 1 ]; then
+ echo "Usage: $0 cd|fd"
+ exit 2
+fi
+
+# determine toplevel mount point of amd
+fs=`amq | grep ' toplvl ' | cut -d' ' -f1`
+if [ "$fs" = "" ]; then
+ echo "Cannot determine amd toplevel directory"
+ exit 2
+fi
+
+# append name of medium
+case "$1" in
+ cd|fd) fs=$fs/$1;;
+ *) echo "Usage: $0 cd|fd"; exit 2;;
+esac
+
+# is the medium mounted?
+if amq | grep -q "^$fs" >/dev/null 2>&1; then
+ # if yes, try to unmount it
+ sync
+ amq -u $fs
+ sleep 2
+ if amq | grep -q "^$fs" >/dev/null 2>&1; then
+ # failed, bail out
+ echo -n "Cannot unmount $fs; in use by:"
+ fuser -uv -m $fs
+ echo ""
+ exit 1
+ fi
+else
+ echo "$fs not mounted"
+fi
+
+case $1 in
+ cd) eject;; # eject CD-ROM
+ fd) echo "Ok to remove disk";;
+esac
diff --git a/contrib/amd/scripts/amd.conf-sample b/contrib/amd/scripts/amd.conf-sample
new file mode 100644
index 0000000..59ce1f8
--- /dev/null
+++ b/contrib/amd/scripts/amd.conf-sample
@@ -0,0 +1,94 @@
+# -*- text -*-
+# A SAMPLE CONFIGURATION FILE FOR AMD
+
+##############################################################################
+# GLOBAL OPTIONS SECTION (must be first in amd.conf file)
+[ global ]
+# (amd -n)
+normalize_hostnames = yes | no
+# (amd -p)
+print_pid = yes | no
+pid_file = /dev/stdout | /var/run/amd.pid
+# (amd -r)
+restart_mounts = yes | no
+unmount_on_exit = no | yes
+# (amd -a)
+auto_dir = /a
+# duration in seconds that a looked up name remain cached (amd -c)
+cache_duration = 300
+# (amd -d)
+local_domain = cs.columbia.edu
+# (amd -k)
+karch = sun4m
+arch = sun4
+# if you don't like autoconf picking up "sunos5" as the os-type, override it
+os = sos5
+# (amd -o)
+osver = 2.5.1
+# if you print_version after setting up "os", it will show it. (amd -v)
+print_version = yes | no
+
+# (amd -l)
+log_file = /var/log/amd | syslog | syslog:facility
+# NFS (RPC/UDP) retry interval, in tenths of secs (amd -t interval.counter)
+nfs_retry_interval = 8
+nfs_retransmit_counter = 110
+# (amd -w)
+dismount_interval = 120
+# (amd -y)
+nis_domain = nisDom-CS.columbia.edu
+# (amd -x)
+log_options = fatal,error,user,warn,info,map,stats,all
+# (amd -D)
+debug_options = all,amq,daemon,fork,full,info,mem,mtab,str,test,trace
+# (amd -S)
+plock = no | yes
+# selectors on /defaults are off by default
+selectors_on_default = yes | no
+# should browsable maps show number of entries to df/statfs (default=no)
+show_statfs_entries = yes | no
+# (hpux) cluster name (amd -C)
+cluster = ???
+# LDAP (Lightweight Directory Access Protocol) options
+ldap_base = ldap.your.domain:389
+ldap_hostports = "ou=Marketing, o=AMD Ltd, c=US"
+ldap_cache_seconds = 0 (default)
+ldap_cache_maxmem = 131072 (default)
+# default base name for hesiod maps
+hesiod_base = automount
+# these 5 options can be overridden by each map individually
+browsable_dirs = yes | no | full
+map_options = cache:=all
+map_type = file|hesiod|ndbm|nis|nisplus|passwd|union|ldap
+mount_type = nfs | autofs
+search_path = /etc/local:/etc/amdmaps:/misc/yp
+# alternate RPC program number to register with the port mapper
+portmap_program = 300019-300029
+# Use fully qualified host names
+fully_qualified_hosts = yes | no
+
+##############################################################################
+# DEFINE AN AMD MOUNT POINT
+[ /home ]
+# map name must be defined, all else are optional
+map_name = /etc/amd.home | amd.home
+map_options = cache:=all
+# if map type is not defined, will search all map types (default)
+map_type = file|hesiod|ndbm|nis|nisplus|passwd|union|ldap
+search_path = /etc/local:/etc/amdmaps:/misc/yp
+# an amd or autofs mount point
+mount_type = nfs | autofs
+browsable_dirs = yes | no
+# an optional tag to be used with amd -T tag. untagged entries are always
+# used. Tagged ones get used only if specified with "amd -T"
+tag = test
+
+##############################################################################
+# DEFINE ANOTHER AMD MOUNT POINT
+[ /src ]
+map_name = /usr/local/lib/amdmaps/amu.src
+# regular amd (nfs) mount point (default)
+# don't try the "autofs" type. It is not implemented yet.
+mount_type = nfs
+
+##############################################################################
diff --git a/contrib/amd/scripts/amd.conf.5 b/contrib/amd/scripts/amd.conf.5
new file mode 100644
index 0000000..2022fad
--- /dev/null
+++ b/contrib/amd/scripts/amd.conf.5
@@ -0,0 +1,539 @@
+.\"
+.\" Copyright (c) 1997-1998 Erez Zadok
+.\" Copyright (c) 1990 Jan-Simon Pendry
+.\" Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+.\" Copyright (c) 1990 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Jan-Simon Pendry at Imperial College, London.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgment:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" %W% (Berkeley) %G%
+.\"
+.\" $Id: amd.conf.5,v 5.2.2.1 1997/02/09 15:11:15 ezk beta $
+.\"
+.TH AMQ.CONF 8 "7 August 1997"
+.SH NAME
+amd.conf \- amd configuration file
+.SH SYNOPSIS
+.B amd.conf
+.SH DESCRIPTION
+The
+.B amd.conf
+file is the configuration file for amd, as part of the am-utils suite.
+.P
+.B amd.conf
+contains runtime configuration information for the
+.B amd
+automounter program.
+.\" **************************************************************************
+.SH FILE FORMAT
+.P
+The file consists of sections and parameters. A section begins with the
+name of the section in square brackets and continues until the next section
+begins or the end the file is reached. Sections contain parameters of the
+form 'name = value'.
+.P
+The file is line-based - that is, each newline-terminated line represents
+either a comment, a section name or a parameter. No line-continuation
+syntax is available.
+.P
+Section, parameter names and their values are case sensitive.
+.P
+Only the first equals sign in a parameter is significant. Whitespace before
+or after the first equals sign is discarded. Leading, trailing and
+internal whitespace in section and parameter names is irrelevant. Leading
+and trailing whitespace in a parameter value is discarded. Internal
+whitespace within a parameter value is not allowed, unless the whole
+parameter value is quoted with double quotes as in 'name = "some value"'.
+.P
+Any line beginning with a pound sign (#) is ignored, as are lines containing
+only whitespace.
+.P
+The values following the equals sign in parameters are all either a string
+(no quotes needed if string does not include spaces) or a boolean, which may
+be given as yes/no. Case is significant in all values. Some items such as
+cache timeouts are numeric.
+.\" **************************************************************************
+.SH SECTIONS
+.SS The [global] section
+Parameters in this section either apply to amd as a whole, or to all other
+regular map sections which follow. There should be only one global section
+defined in one configuration file.
+.P
+It is highly recommended that this section be specified first in the
+configuration file. If it is not, then regular map sections which precede
+it will not use global values defined later.
+
+.SS Regular [/map] sections
+Parameters in regular (non-global) sections apply to a single map entry.
+For example, if the map section
+.B [/homes]
+is defined, then all parameters following it will be applied to the
+.I /homes
+amd-managed mount point.
+.\" **************************************************************************
+.SH PARAMETERS
+.SS Parameters common to all sections
+These parameters can be specified either in the global or a map specific
+section. Entries specified in a map-specific section override the default
+value or one defined in the global section. If such a common parameter is
+specified only in the global section, it is applicable to all regular map
+sections that follow.
+.\" **************************************************************************
+.TP
+.BR browsable_dirs " (string, default=no)"
+If "yes", then amd's top-level mount points will be browsable to
+.BR readdir (3)
+calls. This means you could run for example
+.BR ls (3)
+and see what keys are available to mount in that directory. Not all entries
+are made visible to readdir(3): the "/default" entry, wildcard
+entries, and those with a "/" in them are not included. If you specify
+"full" to this option, all but "/default" will be visible.
+Note that if you run a command which will attempt to
+.BR stat (2)
+the entries, such as often done by "ls -l" or "ls -F", amd will attempt to
+mount
+.I every
+entry in that map. This is often called a ``mount storm''.
+
+.TP
+.BR map_options " (string, default no options)"
+This option is the same as specifying map options on the command line to
+amd, such as "cache:=all".
+
+.TP
+.BR map_type " (string, default search all map types)"
+If specified, amd will initialize the map only for the type given. This is
+useful to avoid the default map search type used by amd which takes longer
+and can have undesired side-effects such as initializing NIS even if not
+used. Possible values are
+
+.nf
+\fBfile\fR plain files
+\fBhesiod\fR Hesiod name service from MIT
+\fBldap\fR Lightweight Directory Access Protocol
+\fBndbm\fR (New) dbm style hash files
+\fBnis\fR Network Information Services (version 2)
+\fBnisplus\fR Network Information Services Plus (version 3)
+\fBpasswd\fR local password files
+\fBunion\fR union maps
+.fi
+
+.TP
+.BR mount_type " (string, default=nfs)"
+All amd mount types default to NFS. That is, amd is an NFS server on the
+map mount points, for the local host it is running on. If "autofs" is
+specified, amd will be an autofs server for those mount points.
+
+.TP
+.BR search_path " (string, default no search path)"
+This provides a (colon-delimited) search path for file maps. Using a search
+path, sites can allow for local map customizations and overrides, and can
+distributed maps in several locations as needed.
+
+.\" **************************************************************************
+.SS Parameters applicable to the global section only
+
+.TP
+.BR arch " (string, default to compiled in value)"
+Allows you to override the value of the
+.I arch
+amd variable.
+
+.TP
+.BR auto_dir " (string, default=/a)"
+Same as the
+.B \-a
+option to amd. This sets the private directory where amd will create
+sub-directories for its real mount points.
+
+.TP
+.BR cache_duration " (numeric, default=300)"
+Same as the
+.B \-c
+option to amd. Sets the duration in seconds that looked up map entries
+remain in the cache.
+
+.TP
+.BR cluster " (string, default no cluster)"
+Same as the
+.B \-C
+option to amd. Specifies the alternate HP-UX cluster to use.
+
+.TP
+.BR debug_options " (string, default no debug options)"
+Same as the
+.B \-D
+option to amd. Specify any debugging options for amd. Works only if
+am-utils was configured for debugging using the --enable-debug option. The
+"mem" option alone can be turned on via --enable-debug=mem. Otherwise
+debugging options are ignored. Options are comma delimited, and can be
+preceded by the string "no" to negate their meaning. You can get the list
+of supported debugging options by running amd \-v. Possible values are:
+
+.nf
+\fBall\fR all options
+\fBamq\fR register for amq
+\fBdaemon\fR enter daemon mode
+\fBfork\fR fork server
+\fBfull\fR program trace
+\fBinfo\fR info service specific debugging (hesiod, nis, etc.)
+\fBmem\fR trace memory allocations
+\fBmtab\fR use local "./mtab" file
+\fBstr\fR debug string munging
+\fBtest\fR full debug but no daemon
+\fBtrace\fR protocol trace
+.fi
+
+.TP
+.BR dismount_interval " (numeric, default=120)"
+Same as the
+.B \-w
+option to amd. Specify in seconds, the time between attempts to dismount
+file systems that have exceeded their cached times.
+
+.TP
+.BR fully_qualified_hosts " (string, default=no)"
+If "yes",
+.I Amd
+will perform RPC authentication using fully-qualified host names. This is
+necessary for some systems, and especially when performing cross-domain
+mounting. For this function to work, the
+.I Amd
+variable ${hostd} is used, requiring that ${domain} not be null.
+
+.TP
+.BR hesiod_base " (string, default=automount)"
+Specify the base name for hesiod maps.
+
+.TP
+.BR karch " (string, default to karch of the system)"
+Same as the
+.B \-k
+option to amd. Allows you to override the kernel-architecture of your
+system. Useful for example on Sun (Sparc) machines, where you can build one
+amd binary, and run it on multiple machines, yet you want each one to get
+the correct
+.I karch
+variable set (for example, sun4c, sun4m, sun4u, etc.) Note that if not
+specified, amd will use uname(2) to figure out the kernel architecture of
+the machine.
+
+.TP
+.BR ldap_base " (string, default not set)"
+Specify the base name for LDAP.
+
+.TP
+.BR ldap_cache_maxmem " (numeric, default=131072)"
+Specify the maximum memory amd should use to cache LDAP entries.
+
+.TP
+.BR ldap_cache_seconds " (numeric, default=0)"
+Specify the number of seconds to keep entries in the cache.
+
+.TP
+.BR ldap_hostports " (string, default not set)"
+Specify LDAP-specific values such as country and organization.
+
+.TP
+.BR local_domain " (string, default no sub-domain)"
+Same as the
+.B \-d
+option to amd. Specify the local domain name. If this option is not given
+the domain name is determined from the hostname, by removing the first
+component of the fully-qualified host name.
+
+.TP
+.BR log_file " (string, default=/dev/stderr)"
+Same as the
+.B \-l
+option to amd. Specify a file name to log amd events to.
+If the string
+.B /dev/stderr
+is specified, amd will send its events to the standard error file descriptor.
+If the string
+.B syslog
+is given, amd will record its events with the system logger
+.BR syslogd (8).
+The default syslog facility used is LOG_DAEMON. If you
+wish to change it, append its name to the log file name, delimited by a
+single colon. For example, if
+.I logfile
+is the string
+.B syslog:local7
+then amd will log messages via
+.IR syslog (3)
+using the LOG_LOCAL7 facility (if it exists on the system).
+
+.TP
+.BR log_options " (string, default no logging options)"
+Same as the
+.B \-x
+option to amd. Specify any logging options for amd. Options are comma
+delimited, and can be preceded by the string "no" to negate their meaning.
+The "debug" logging option is only available if am-utils was configured with
+--enable-debug. You can get the list of supported debugging options by
+running amd \-v. Possible values are:
+
+.nf
+\fBall\fR all messages
+\fBdebug\fR debug messages
+\fBerror\fR non-fatal system errors
+\fBfatal\fR fatal errors
+\fBinfo\fR information
+\fBmap\fR map errors
+\fBstats\fR additional statistical information
+\fBuser\fR non-fatal user errors
+\fBwarn\fR warnings
+\fBwarning\fR warnings
+.fi
+
+.TP
+.BR nfs_retransmit_counter " (numeric, default=110)"
+Same as the
+.I counter
+part of the
+.BI \-t " interval.counter"
+option to amd. Specifies the retransmit counter's value in
+.I tenths
+of seconds.
+
+.TP
+.BR nfs_retry_interval " (numeric, default=8)"
+Same as the
+.I interval
+part of the
+.BI \-t " interval.counter"
+option to amd. Specifies the interval in
+.I tenths
+of seconds, between NFS/RPC/UDP retries.
+
+.TP
+.BR nis_domain " (string, default to local NIS domain name)"
+Same as the
+.B \-y
+option to amd. Specify an alternative NIS domain from which to fetch the
+NIS maps. The default is the system domain name. This option is ignored if
+NIS support is not available.
+
+.TP
+.BR normalize_hostnames " (boolean, default=no)"
+Same as the
+.B \-n
+option to amd. If "yes", then the name refereed to by ${rhost} is
+normalized relative to the host database before being used. The effect is
+to translate aliases into ``official'' names.
+
+.TP
+.BR os " (string, default to compiled in value)"
+Same as the
+.B \-O
+option to amd. Allows you to override the compiled-in name of the operating
+system. Useful when the built-in name is not desired for backward
+compatibility reasons. For example, if the build in name is ``sunos5'', you
+can override it to ``sos5'', and use older maps which were written with the
+latter in mind.
+
+.TP
+.BR osver " (string, default to compiled in value)"
+Same as the
+.B \-o
+option to amd. Override the compiled-in version number of the operating
+system. Useful when the built in version is not desired for backward
+compatibility reasons. For example, if the build in version is ``2.5.1'',
+you can override it to ``5.5.1'', and use older maps that were written with
+the latter in mind.
+
+.TP
+.BR pid_file " (string, default=/dev/stdout)"
+Specify a file to store the process ID of the running daemon into. If not
+specified, amd will print its process id onto the standard output. Useful
+for killing amd after it had run. Note that the PID of a running amd can
+also be retrieved via
+.B amq
+.BR \-p .
+This file is used only if the print_pid option is on.
+
+.TP
+.BR plock " (boolean, default=yes)"
+Same as the
+.B \-S
+option to amd.
+If "yes", lock the running executable pages of amd into memory. To improve
+amd's performance, systems that support the
+.BR plock (3)
+call can lock the amd process into memory. This way there is less chance it
+the operating system will schedule, page out, and swap the amd process as
+needed. This improves amd's performance, at the cost of reserving the
+memory used by the amd process (making it unavailable for other processes).
+
+.TP
+.BR portmap_program " (numeric, default=300019)"
+Specify an alternate Port-mapper RPC program number, other than the official
+number. This is useful when running multiple amd processes. For example,
+you can run another amd in "test" mode, without affecting the primary amd
+process in any way. For safety reasons, the alternate program numbers that
+can be specified must be in the range 300019-300029, inclusive.
+.B amq
+has an option
+.B -P
+which can be used to specify an alternate program number of an amd to
+contact. In this way, amq can fully control any number of amd processes
+running on the same host.
+
+.TP
+.BR print_pid " (boolean, default=no)"
+Same as the
+.B \-p
+option to amd. If "yes", amd will print its process ID upon starting.
+
+.TP
+.BR print_version " (boolean, default=no)"
+Same as the
+.B \-v
+option to amd, but the version prints and amd continues to run. If "yes",
+amd will print its version information string, which includes some
+configuration and compilation values.
+
+.TP
+.BR restart_mounts " (boolean, default=no)"
+Same as the
+.B \-r
+option to amd. If "yes"
+.B amd
+will scan the mount table to determine which file systems are currently
+mounted. Whenever one of these would have been auto-mounted,
+.B amd
+inherits it.
+
+.TP
+.BR selectors_on_default " (boolean, default=no)"
+If "yes", then the /default entry of maps will be look for and process any
+selectors before setting defaults for all other keys in that map. Useful
+when you want to set different options for a complete map based on some
+parameters. For example, you may want to better the NFS performance over
+slow slip-based networks as follows:
+
+.nf
+/defaults \\
+ wire==slip-net;opts:=intr,rsize=1024,wsize=1024 \\
+ wire!=slip-net;opts:=intr,rsize=8192,wsize=8192
+.fi
+
+.TP
+.BR show_statfs_entries " (boolean), default=no)"
+If "yes", then all maps which are browsable will also show the number of
+entries (keys) they have when "df" runs. (This is accomplished by returning
+non-zero values to the statfs(2) system call).
+
+.TP
+.BR unmount_on_exist " (boolean), default=no)"
+If "yes", then amd will attempt to unmount all file systems which it knows
+about. Normally amd leaves all (esp. NFS) mounted file systems intact.
+Note that amd does not know about file systems mounted before it starts up,
+unless the restart_mounts option or
+.B \-r
+flag are used.
+
+.\" **************************************************************************
+.SS Parameters applicable to regular map sections
+
+.TP
+.BR map_name " (string, must be specified)"
+Name of the map where the keys are located.
+
+.TP
+.BR tag " (string, default no tag)"
+Each map entry in the configuration file can be tagged. If no tag is
+specified, that map section will always be processed by amd. If it is
+specified, then amd will process the map if the
+.B -T
+option was given to amd, and the value given to that command-line option
+matches that in the map section.
+
+.\" **************************************************************************
+.SH EXAMPLES
+Here is a real amd configuration file I use daily.
+.P
+.nf
+# GLOBAL OPTIONS SECTION
+[ global ]
+normalize_hostnames = no
+print_pid = no
+restart_mounts = yes
+auto_dir = /n
+log_file = /var/log/amd
+log_options = all
+#debug_options = all
+plock = no
+selectors_on_default = yes
+# config.guess picks up "sunos5" and I don't want to edit my maps yet
+os = sos5
+# if you print_version after setting up "os", it will show it.
+print_version = no
+map_type = file
+search_path = /etc/amdmaps:/usr/lib/amd:/usr/local/AMD/lib
+browsable_dirs = yes
+
+# DEFINE AN AMD MOUNT POINT
+[ /u ]
+map_name = amd.u
+
+[ /proj ]
+map_name = amd.proj
+
+[ /src ]
+map_name = amd.src
+
+[ /misc ]
+map_name = amd.misc
+
+[ /import ]
+map_name = amd.import
+
+[ /tftpboot/.amd ]
+tag = tftpboot
+map_name = amd.tftpboot
+.fi
+.\" **************************************************************************
+.SH "SEE ALSO"
+.BR amd (8),
+.BR amq (8),
+.BR ctl-amd (8).
+.SH AUTHORS
+Erez Zadok <ezk@cs.columbia.edu>, Department of Computer Science, Columbia
+University, New York, USA.
+.P
+Other authors and contributors to am-utils are listed in the
+.B AUTHORS
+file distributed with am-utils.
diff --git a/contrib/amd/scripts/amd2ldif.in b/contrib/amd/scripts/amd2ldif.in
new file mode 100755
index 0000000..6d3c28a
--- /dev/null
+++ b/contrib/amd/scripts/amd2ldif.in
@@ -0,0 +1,58 @@
+#!@PERL@
+
+$usage=<<EOU;
+Usage $0 mapname base < mapfile >mapfile.ldif
+
+mapname: name of the amd map beeing converted to ldif
+base : The LDAP search base. Do not forget the quotes!
+
+This script should/could be used in a Makefile together
+with ldif2ldbm(8C) to automagically update the ldap
+databases and restart slapd whenever a master copy of
+the maps have changed. Remember "cd /var/yp; make" ?
+EOU
+
+my $fmt = "%-12s: %s\n";
+my $tfmt = "%-15s: %s\n";
+my $mapname = $ARGV[0] or die $usage;
+my $base = $ARGV[1] or die $usage;
+$time = time();
+
+print "dn: cn=amdmap timestamp, $base\n";
+printf "$tfmt", "cn", "amdmap timestamp";
+printf "$tfmt", "objectClass", "amdmapTimestamp";
+printf "$tfmt", "amdmapTimestamp", $time;
+print "\n";
+
+my $line = "";
+my $done = 0;
+
+while (<STDIN>) {
+ chomp;
+ if (/\s*(.+)\\\s*/) {
+ if ($line) {
+ $line .= " ".$1;
+ } else {
+ $line = $1;
+ }
+ $done = 0;
+ } else {
+ s/^\s+//g;
+ $line .= $_;
+ $done = 1;
+ }
+ if ($done) {
+ my @vals = split(/\s+/,$line);
+ my $key = shift @vals;
+ my $entry;
+
+ print "dn: cn=amdmap $mapname\[$key\], $base\n";
+ printf "$fmt","cn","amdmap $mapname\[$key\]";
+ printf "$fmt","objectClass", "amdmap";
+ printf "$fmt","amdmapName", $mapname;
+ printf "$fmt","amdmapKey", $key;
+ printf "$fmt","amdmapValue", join(' ',@vals);
+ print "\n";
+ $line = ""; $done = 0;
+ }
+}
diff --git a/contrib/amd/scripts/amd2sun.in b/contrib/amd/scripts/amd2sun.in
new file mode 100755
index 0000000..df69b09
--- /dev/null
+++ b/contrib/amd/scripts/amd2sun.in
@@ -0,0 +1,51 @@
+#!@PERL@
+# convert amd maps to Sun automount maps
+# usage: amd2sun file
+#
+# Package: am-utils-6.0
+# Author: "Mark D. Baushke" <mdb@cisco.com>
+
+print "# file created by amd2sun
+#
+# DO NOT EDIT THIS FILE AT ALL
+# It is automatically generated from the amd mount map - edit that instead
+#
+";
+while (<>) {
+ print, next if /^#/;
+ chop;
+ $line = $_;
+ while ($line =~ /\\$/) {
+ chop $line;
+ $line2 = <>;
+ $line2 =~ s/^\s*//;
+ $line .= $line2;
+ chop $line;
+ }
+
+ next unless $line =~ /^([^\s]+)\s+(.*)$/;
+
+ $fs = $1; $rest=$2;
+
+ if ($fs =~ /^\/defaults/) {
+ ($defopts = $rest) =~ s/^.*[\s;]opts:=([^;\s]+)[;\s]*.*$/\1/;
+ next;
+ }
+
+ $opts=$defopts;
+
+ if ($rest =~ /opts:=([^;\s]+)[;\s]/) {
+ $opts = $1;
+ }
+
+ $opts =~ s/,ping=[-\d]+//g;
+
+ ($rhost = $rest) =~ s/^.*[\s;]rhost:=([^;\s]+)[;\s]*.*$/\1/;
+ ($rfs = $rest) =~ s/^.*[\s;]rfs:=([^;\s]+)[;\s]*.*$/\1/;
+
+ if ($rest =~ /sublink:=([^;\s]+)[;\s]/ ) {
+ $rfs .= "/$1";
+ }
+
+ print "$fs -$opts $rhost:$rfs\n";
+}
diff --git a/contrib/amd/scripts/ctl-amd.in b/contrib/amd/scripts/ctl-amd.in
new file mode 100755
index 0000000..665a7dd
--- /dev/null
+++ b/contrib/amd/scripts/ctl-amd.in
@@ -0,0 +1,113 @@
+#!/bin/sh
+# control starting, stopping, or restarting amd.
+# usage: ctl-amd [start | stop | restart]
+#
+# Package: am-utils-6.0
+# Author: Erez Zadok <ezk@cs.columbia.edu>
+
+# set path
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+PATH=@sbindir@:@bindir@:/usr/ucb:/usr/bin:/bin:${PATH}
+export PATH
+
+# kill the named process(es)
+killproc()
+{
+# first try to get PID via an amq RPC
+pid=`amq -p 2>/dev/null`
+if test "$pid" != ""
+then
+ kill $pid
+ return 0
+fi
+
+# try bsd style ps
+pscmd="ps axc"
+pid=`${pscmd} 2>/dev/null | grep "$1" | sed -e 's/^ *//' -e 's/ .*//'`
+if test "$pid" != ""
+then
+ kill $pid
+ return 0
+fi
+
+# try bsd44 style ps
+pscmd="ps -x"
+pid=`${pscmd} 2>/dev/null | grep "$1" | sed -e 's/^ *//' -e 's/ .*//'`
+if test "$pid" != ""
+then
+ kill $pid
+ return 0
+fi
+
+# try svr4 style ps
+pscmd="ps -e"
+pid=`${pscmd} 2>/dev/null | grep "$1" | sed -e 's/^ *//' -e 's/ .*//'`
+if test "$pid" != ""
+then
+ kill $pid
+ return 0
+fi
+
+# failed
+return 1
+}
+
+# search for amd.conf file
+CF_FILE="${prefix}/etc/amd.conf"
+# any local copy of the conf file overrides the "global" one
+if [ -f /etc/amd.conf ]
+then
+ CF_FILE="/etc/amd.conf"
+fi
+if [ -f ${prefix}/etc/amd.conf ]
+then
+ CF_FILE="${prefix}/etc/amd.conf"
+fi
+if [ -f /etc/local/amd.conf ]
+then
+ CF_FILE="/etc/local/amd.conf"
+fi
+
+# if have the directory /tftpboot/.amd, then add a tag to include it
+CF_TAG=""
+if [ -d /tftpboot/.amd ]
+then
+ CF_TAG="-T tftpboot"
+fi
+
+case "$1" in
+'start')
+ #
+ # Start the amd automounter.
+ #
+ if [ -x @sbindir@/amd ]
+ then
+ # do not specify full path of amd so killproc() works
+ amd -F $CF_FILE $CF_TAG
+ fi
+ ;;
+
+'stop')
+ # prepend space to program name to ensure only amd process dies
+ killproc " amd"
+ ;;
+
+'restart')
+ # kill amd, wait for it to die, then restart
+ echo "killing amd..."
+ ctl-amd stop
+ wait4amd2die
+ if [ $? != 0 ]
+ then
+ echo "NOT restarting amd!"
+ else
+ echo "Restarting amd..."
+ ctl-amd start
+ fi
+ ;;
+
+*)
+ echo "Usage: @sbindir@/ctl-amd [ start | stop | restart ]"
+ ;;
+esac
diff --git a/contrib/amd/scripts/ctl-hlfsd.in b/contrib/amd/scripts/ctl-hlfsd.in
new file mode 100755
index 0000000..a6e97f6
--- /dev/null
+++ b/contrib/amd/scripts/ctl-hlfsd.in
@@ -0,0 +1,101 @@
+#!/bin/sh
+# control starting, stopping, or restarting hlfsd.
+# usage: ctl-hlfsd [start | stop | restart]
+#
+# Package: am-utils-6.0
+# Author: Erez Zadok <ezk@cs.columbia.edu>
+
+# set path
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+PATH=@sbindir@:@bindir@:/usr/ucb:/usr/bin:/bin:${PATH}
+export PATH
+
+# kill the named process(es)
+killproc()
+{
+# try bsd style ps
+pscmd="ps axc"
+pid=`${pscmd} 2>/dev/null | grep "$1" | sed -e 's/^ *//' -e 's/ .*//'`
+if test "$pid" != ""
+then
+ kill $pid
+ return 0
+fi
+
+# try bsd44 style ps
+pscmd="ps -x"
+pid=`${pscmd} 2>/dev/null | grep "$1" | sed -e 's/^ *//' -e 's/ .*//'`
+if test "$pid" != ""
+then
+ kill $pid
+ return 0
+fi
+
+# try svr4 style ps
+pscmd="ps -e"
+pid=`${pscmd} 2>/dev/null | grep "$1" | sed -e 's/^ *//' -e 's/ .*//'`
+if test "$pid" != ""
+then
+ kill $pid
+ return 0
+fi
+
+# failed
+return 1
+}
+
+# locate logs directory
+if [ -d /var/log ]; then
+ logdir="/var/log"
+else
+ logdir="/tmp"
+fi
+
+# locate the mail spool directory
+if [ -d /var/mail/. ]; then
+ maildir="/var/mail"
+ altmaildir="/var/alt_mail"
+else
+ maildir="/usr/spool/mail"
+ altmaildir="/usr/spool/alt_mail"
+fi
+
+# locate any optional password file
+if [ -f ${prefix}/etc/passwd ]; then
+ PASSWD_FILE="-P ${prefix}/etc/passwd"
+else
+ PASSWD_FILE=""
+fi
+
+case "$1" in
+'start')
+ #
+ # Start the hlfsd mail redirector service
+ #
+ if [ -x @sbindir@/hlfsd -a -h $maildir ]
+ then
+ echo @sbindir@/hlfsd ${PASSWD_FILE} -a $altmaildir -x all -D fork -l $logdir/hlfsd /mail/home .mailspool
+ @sbindir@/hlfsd ${PASSWD_FILE} -a $altmaildir -x all -D fork -l $logdir/hlfsd /mail/home .mailspool &
+ fi
+ ;;
+
+'stop')
+ # prepend space to program name to ensure only amd process dies
+ killproc " hlfsd"
+ ;;
+
+'restart')
+ # kill hlfsd, wait for it to die, then restart
+ echo "killing hlfsd..."
+ ctl-hlfsd stop
+ echo "Waiting for 10 seconds..."
+ sleep 10 # hope that's enough
+ echo "Restarting hlfsd..."
+ ctl-hlfsd start
+ ;;
+
+*)
+ echo "Usage: @sbindir@/ctl-hlfsd [ start | stop | restart ]"
+ ;;
+esac
diff --git a/contrib/amd/scripts/expn.1 b/contrib/amd/scripts/expn.1
new file mode 100644
index 0000000..b2bd1b6
--- /dev/null
+++ b/contrib/amd/scripts/expn.1
@@ -0,0 +1,1370 @@
+#!@PERL@
+'di ';
+'ds 00 \\"';
+'ig 00 ';
+#
+# THIS PROGRAM IS ITS OWN MANUAL PAGE. INSTALL IN man & bin.
+#
+
+# hardcoded constants, should work fine for BSD-based systems
+#require 'sys/socket.ph'; # perl 4
+use Socket; # perl 5
+$AF_INET = &AF_INET;
+$SOCK_STREAM = &SOCK_STREAM;
+$sockaddr = 'S n a4 x8';
+
+# system requirements:
+# must have 'nslookup' and 'hostname' programs.
+
+# $Header: /home/muir/bin/RCS/expn,v 3.9 1995/10/02 17:51:35 muir Exp muir $
+
+# TODO:
+# less magic should apply to command-line addresses
+# less magic should apply to local addresses
+# add magic to deal with cross-domain cnames
+
+# Checklist: (hard addresses)
+# 250 Kimmo Suominen <"|/usr/local/mh/lib/slocal -user kim"@grendel.tac.nyc.ny.us>
+# harry@hofmann.cs.Berkeley.EDU -> harry@tenet (.berkeley.edu) [dead]
+# bks@cs.berkeley.edu -> shiva.CS (.berkeley.edu) [dead]
+# dan@tc.cornell.edu -> brown@tiberius (.tc.cornell.edu)
+
+#############################################################################
+#
+# Copyright (c) 1993 David Muir Sharnoff
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the David Muir Sharnoff.
+# 4. The name of David Sharnoff may not be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE DAVID MUIR SHARNOFF ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL DAVID MUIR SHARNOFF BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# This copyright notice derrived from material copyrighted by the Regents
+# of the University of California.
+#
+# Contributions accepted.
+#
+#############################################################################
+
+# overall structure:
+# in an effort to not trace each address individually, but rather
+# ask each server in turn a whole bunch of questions, addresses to
+# be expanded are queued up.
+#
+# This means that all accounting w.r.t. an address must be stored in
+# various arrays. Generally these arrays are indexed by the
+# string "$addr *** $server" where $addr is the address to be
+# expanded "foo" or maybe "foo@bar" and $server is the hostname
+# of the SMTP server to contact.
+#
+
+# important global variables:
+#
+# @hosts : list of servers still to be contacted
+# $server : name of the current we are currently looking at
+# @users = $users{@hosts[0]} : addresses to expand at this server
+# $u = $users[0] : the current address being expanded
+# $names{"$users[0] *** $server"} : the 'name' associated with the address
+# $mxbacktrace{"$users[0] *** $server"} : record of mx expansion
+# $mx_secondary{$server} : other mx relays at the same priority
+# $domainify_fallback{"$users[0] *** $server"} : alternative names to try
+# instead of $server if $server doesn't work
+# $temporary_redirect{"$users[0] *** $server"} : when trying alternates,
+# temporarily channel all tries along current path
+# $giveup{$server} : do not bother expanding addresses at $server
+# $verbose : -v
+# $watch : -w
+# $vw : -v or -w
+# $debug : -d
+# $valid : -a
+# $levels : -1
+# S : the socket connection to $server
+
+$have_nslookup = 1; # we have the nslookup program
+$port = 'smtp';
+$av0 = $0;
+$ENV{'PATH'} .= ":/usr/etc" unless $ENV{'PATH'} =~ m,/usr/etc,;
+$ENV{'PATH'} .= ":/usr/ucb" unless $ENV{'PATH'} =~ m,/usr/ucb,;
+select(STDERR);
+
+$0 = "$av0 - running hostname";
+chop($name = `hostname || uname -n`);
+
+$0 = "$av0 - lookup host FQDN and IP addr";
+($hostname,$aliases,$type,$len,$thisaddr) = gethostbyname($name);
+
+$0 = "$av0 - parsing args";
+$usage = "Usage: $av0 [-1avwd] user[\@host] [user2[host2] ...]";
+for $a (@ARGV) {
+ die $usage if $a eq "-";
+ while ($a =~ s/^(-.*)([1avwd])/$1/) {
+ eval '$'."flag_$2 += 1";
+ }
+ next if $a eq "-";
+ die $usage if $a =~ /^-/;
+ &expn(&parse($a,$hostname,undef,1));
+}
+$verbose = $flag_v;
+$watch = $flag_w;
+$vw = $flag_v + $flag_w;
+$debug = $flag_d;
+$valid = $flag_a;
+$levels = $flag_1;
+
+die $usage unless @hosts;
+if ($valid) {
+ if ($valid == 1) {
+ $validRequirement = 0.8;
+ } elsif ($valid == 2) {
+ $validRequirement = 1.0;
+ } elsif ($valid == 3) {
+ $validRequirement = 0.9;
+ } else {
+ $validRequirement = (1 - (1/($valid-3)));
+ print "validRequirement = $validRequirement\n" if $debug;
+ }
+}
+
+$0 = "$av0 - building local socket";
+($name,$aliases,$proto) = getprotobyname('tcp');
+($name,$aliases,$port) = getservbyname($port,'tcp')
+ unless $port =~ /^\d+/;
+$this = pack($sockaddr, &AF_INET, 0, $thisaddr);
+
+HOST:
+while (@hosts) {
+ $server = shift(@hosts);
+ @users = split(' ',$users{$server});
+ delete $users{$server};
+
+ # is this server already known to be bad?
+ $0 = "$av0 - looking up $server";
+ if ($giveup{$server}) {
+ &giveup('mx domainify',$giveup{$server});
+ next;
+ }
+
+ # do we already have an mx record for this host?
+ next HOST if &mxredirect($server,*users);
+
+ # look it up, or try for an mx.
+ $0 = "$av0 - gethostbyname($server)";
+
+ ($name,$aliases,$type,$len,$thataddr) = gethostbyname($server);
+ # if we can't get an A record, try for an MX record.
+ unless($thataddr) {
+ &mxlookup(1,$server,"$server: could not resolve name",*users);
+ next HOST;
+ }
+
+ # get a connection, or look for an mx
+ $0 = "$av0 - socket to $server";
+ $that = pack($sockaddr, &AF_INET, $port, $thataddr);
+ socket(S, &AF_INET, &SOCK_STREAM, $proto)
+ || die "socket: $!";
+ $0 = "$av0 - bind to $server";
+ bind(S, $this)
+ || die "bind $hostname,0: $!";
+ $0 = "$av0 - connect to $server";
+ print "debug = $debug server = $server\n" if $debug > 8;
+ if (! connect(S, $that) || ($debug == 10 && $server =~ /relay\d.UU.NET$/i)) {
+ $0 = "$av0 - $server: could not connect: $!\n";
+ $emsg = $!;
+ unless (&mxlookup(0,$server,"$server: could not connect: $!",*users)) {
+ &giveup('mx',"$server: Could not connect: $emsg");
+ }
+ next HOST;
+ }
+ select((select(S),$| = 1)[0]); # don't buffer output to S
+
+ # read the greeting
+ $0 = "$av0 - talking to $server";
+ &alarm("greeting with $server",'');
+ while(<S>) {
+ alarm(0);
+ print if $watch;
+ if (/^(\d+)([- ])/) {
+ if ($1 != 220) {
+ $0 = "$av0 - bad numeric response from $server";
+ &alarm("giving up after bad response from $server",'');
+ &read_response($2,$watch);
+ alarm(0);
+ print STDERR "$server: NOT 220 greeting: $_"
+ if ($debug || $vw);
+ if (&mxlookup(0,$server,"$server: did not respond with a 220 greeting",*users)) {
+ close(S);
+ next HOST;
+ }
+ }
+ last if ($2 eq " ");
+ } else {
+ $0 = "$av0 - bad response from $server";
+ print STDERR "$server: NOT 220 greeting: $_"
+ if ($debug || $vw);
+ unless (&mxlookup(0,$server,"$server: did not respond with SMTP codes",*users)) {
+ &giveup('',"$server: did not talk SMTP");
+ }
+ close(S);
+ next HOST;
+ }
+ &alarm("greeting with $server",'');
+ }
+ alarm(0);
+
+ # if this causes problems, remove it
+ $0 = "$av0 - sending helo to $server";
+ &alarm("sending helo to $server","");
+ &ps("helo $hostname");
+ while(<S>) {
+ print if $watch;
+ last if /^\d+ /;
+ }
+ alarm(0);
+
+ # try the users, one by one
+ USER:
+ while(@users) {
+ $u = shift(@users);
+ $0 = "$av0 - expanding $u [\@$server]";
+
+ # do we already have a name for this user?
+ $oldname = $names{"$u *** $server"};
+
+ print &compact($u,$server)." ->\n" if ($verbose && ! $valid);
+ if ($valid) {
+ #
+ # when running with -a, we delay taking any action
+ # on the results of our query until we have looked
+ # at the complete output. @toFinal stores expansions
+ # that will be final if we take them. @toExpn stores
+ # expnansions that are not final. @isValid keeps
+ # track of our ability to send mail to each of the
+ # expansions.
+ #
+ @isValid = ();
+ @toFinal = ();
+ @toExpn = ();
+ }
+
+# ($ecode,@expansion) = &expn_vrfy($u,$server);
+ (@foo) = &expn_vrfy($u,$server);
+ ($ecode,@expansion) = @foo;
+ if ($ecode) {
+ &giveup('',$ecode,$u);
+ last USER;
+ }
+
+ for $s (@expansion) {
+ $s =~ s/[\n\r]//g;
+ $0 = "$av0 - parsing $server: $s";
+
+ $skipwatch = $watch;
+
+ if ($s =~ /^[25]51([- ]).*<(.+)>/) {
+ print "$s" if $watch;
+ print "(pretending 250$1<$2>)" if ($debug && $watch);
+ print "\n" if $watch;
+ $s = "250$1<$2>";
+ $skipwatch = 0;
+ }
+
+ if ($s =~ /^250([- ])(.+)/) {
+ print "$s\n" if $skipwatch;
+ ($done,$addr) = ($1,$2);
+ ($newhost, $newaddr, $newname) = &parse($addr,$server,$oldname, $#expansion == 0);
+ print "($newhost, $newaddr, $newname) = &parse($addr, $server, $oldname)\n" if $debug;
+ if (! $newhost) {
+ # no expansion is possible w/o a new server to call
+ if ($valid) {
+ push(@isValid, &validAddr($newaddr));
+ push(@toFinal,$newaddr,$server,$newname);
+ } else {
+ &verbose(&final($newaddr,$server,$newname));
+ }
+ } else {
+ $newmxhost = &mx($newhost,$newaddr);
+ print "$newmxhost = &mx($newhost)\n"
+ if ($debug && $newhost ne $newmxhost);
+ $0 = "$av0 - parsing $newaddr [@$newmxhost]";
+ print "levels = $levels, level{$u *** $server} = ".$level{"$u *** $server"}."\n" if ($debug > 1);
+ # If the new server is the current one,
+ # it would have expanded things for us
+ # if it could have. Mx records must be
+ # followed to compare server names.
+ # We are also done if the recursion
+ # count has been exceeded.
+ if (&trhost($newmxhost) eq &trhost($server) || ($levels && $level{"$u *** $server"} >= $levels)) {
+ if ($valid) {
+ push(@isValid, &validAddr($newaddr));
+ push(@toFinal,$newaddr,$newmxhost,$newname);
+ } else {
+ &verbose(&final($newaddr,$newmxhost,$newname));
+ }
+ } else {
+ # more work to do...
+ if ($valid) {
+ push(@isValid, &validAddr($newaddr));
+ push(@toExpn,$newmxhost,$newaddr,$newname,$level{"$u *** $server"});
+ } else {
+ &verbose(&expn($newmxhost,$newaddr,$newname,$level{"$u *** $server"}));
+ }
+ }
+ }
+ last if ($done eq " ");
+ next;
+ }
+ # 550 is a known code... Should the be
+ # included in -a output? Might be a bug
+ # here. Does it matter? Can assume that
+ # there won't be UNKNOWN USER responses
+ # mixed with valid users?
+ if ($s =~ /^(550)([- ])/) {
+ if ($valid) {
+ print STDERR "\@$server:$u ($oldname) USER UNKNOWN\n";
+ } else {
+ &verbose(&final($u,$server,$oldname,"USER UNKNOWN"));
+ }
+ last if ($2 eq " ");
+ next;
+ }
+ # 553 is a known code...
+ if ($s =~ /^(553)([- ])/) {
+ if ($valid) {
+ print STDERR "\@$server:$u ($oldname) USER AMBIGUOUS\n";
+ } else {
+ &verbose(&final($u,$server,$oldname,"USER AMBIGUOUS"));
+ }
+ last if ($2 eq " ");
+ next;
+ }
+ # 252 is a known code...
+ if ($s =~ /^(252)([- ])/) {
+ if ($valid) {
+ print STDERR "\@$server:$u ($oldname) REFUSED TO VRFY\n";
+ } else {
+ &verbose(&final($u,$server,$oldname,"REFUSED TO VRFY"));
+ }
+ last if ($2 eq " ");
+ next;
+ }
+ &giveup('',"$server: did not grok '$s'",$u);
+ last USER;
+ }
+
+ if ($valid) {
+ #
+ # now we decide if we are going to take these
+ # expansions or roll them back.
+ #
+ $avgValid = &average(@isValid);
+ print "avgValid = $avgValid\n" if $debug;
+ if ($avgValid >= $validRequirement) {
+ print &compact($u,$server)." ->\n" if $verbose;
+ while (@toExpn) {
+ &verbose(&expn(splice(@toExpn,0,4)));
+ }
+ while (@toFinal) {
+ &verbose(&final(splice(@toFinal,0,3)));
+ }
+ } else {
+ print "Tossing some valid to avoid invalid ".&compact($u,$server)."\n" if ($avgValid > 0.0 && ($vw || $debug));
+ print &compact($u,$server)." ->\n" if $verbose;
+ &verbose(&final($u,$server,$newname));
+ }
+ }
+ }
+
+ &alarm("sending 'quit' to $server",'');
+ $0 = "$av0 - sending 'quit' to $server";
+ &ps("quit");
+ while(<S>) {
+ print if $watch;
+ last if /^\d+ /;
+ }
+ close(S);
+ alarm(0);
+}
+
+$0 = "$av0 - printing final results";
+print "----------\n" if $vw;
+select(STDOUT);
+for $f (sort @final) {
+ print "$f\n";
+}
+unlink("/tmp/expn$$");
+exit(0);
+
+
+# abandon all attempts deliver to $server
+# register the current addresses as the final ones
+sub giveup
+{
+ local($redirect_okay,$reason,$user) = @_;
+ local($us,@so,$nh,@remaining_users);
+ local($pk,$file,$line);
+ ($pk, $file, $line) = caller;
+
+ $0 = "$av0 - giving up on $server: $reason";
+ #
+ # add back a user if we gave up in the middle
+ #
+ push(@users,$user) if $user;
+ #
+ # don't bother with this system anymore
+ #
+ unless ($giveup{$server}) {
+ $giveup{$server} = $reason;
+ print STDERR "$reason\n";
+ }
+ print "Giveup at $file:$line!!! redirect okay = $redirect_okay; $reason\n" if $debug;
+ #
+ # Wait!
+ # Before giving up, see if there is a chance that
+ # there is another host to redirect to!
+ # (Kids, don't do this at home! Hacking is a dangerous
+ # crime and you could end up behind bars.)
+ #
+ for $u (@users) {
+ if ($redirect_okay =~ /\bmx\b/) {
+ next if &try_fallback('mx',$u,*server,
+ *mx_secondary,
+ *already_mx_fellback);
+ }
+ if ($redirect_okay =~ /\bdomainify\b/) {
+ next if &try_fallback('domainify',$u,*server,
+ *domainify_fallback,
+ *already_domainify_fellback);
+ }
+ push(@remaining_users,$u);
+ }
+ @users = @remaining_users;
+ for $u (@users) {
+ print &compact($u,$server)." ->\n" if ($verbose && $valid && $u);
+ &verbose(&final($u,$server,$names{"$u *** $server"},$reason));
+ }
+}
+#
+# This routine is used only within &giveup. It checks to
+# see if we really have to giveup or if there is a second
+# chance because we did something before that can be
+# backtracked.
+#
+# %fallback{"$user *** $host"} tracks what is able to fallback
+# %fellback{"$user *** $host"} tracks what has fallen back
+#
+# If there is a valid backtrack, then queue up the new possibility
+#
+sub try_fallback
+{
+ local($method,$user,*host,*fall_table,*fellback) = @_;
+ local($us,$fallhost,$oldhost,$ft,$i);
+
+ if ($debug > 8) {
+ print "Fallback table $method:\n";
+ for $i (sort keys %fall_table) {
+ print "\t'$i'\t\t'$fall_table{$i}'\n";
+ }
+ print "Fellback table $method:\n";
+ for $i (sort keys %fellback) {
+ print "\t'$i'\t\t'$fellback{$i}'\n";
+ }
+ print "U: $user H: $host\n";
+ }
+
+ $us = "$user *** $host";
+ if (defined $fellback{$us}) {
+ #
+ # Undo a previous fallback so that we can try again
+ # Nested fallbacks are avoided because they could
+ # lead to infinite loops
+ #
+ $fallhost = $fellback{$us};
+ print "Already $method fell back from $us -> \n" if $debug;
+ $us = "$user *** $fallhost";
+ $oldhost = $fallhost;
+ } elsif (($method eq 'mx') && (defined $mxbacktrace{$us}) && (defined $mx_secondary{$mxbacktrace{$us}})) {
+ print "Fallback an MX expansion $us -> \n" if $debug;
+ $oldhost = $mxbacktrace{$us};
+ } else {
+ print "Oldhost($host, $us) = " if $debug;
+ $oldhost = $host;
+ }
+ print "$oldhost\n" if $debug;
+ if (((defined $fall_table{$us}) && ($ft = $us)) || ((defined $fall_table{$oldhost}) && ($ft = $oldhost))) {
+ print "$method Fallback = ".$fall_table{$ft}."\n" if $debug;
+ local(@so,$newhost);
+ @so = split(' ',$fall_table{$ft});
+ $newhost = shift(@so);
+ print "Falling back ($method) $us -> $newhost (from $oldhost)\n" if $debug;
+ if ($method eq 'mx') {
+ if (! defined ($mxbacktrace{"$user *** $newhost"})) {
+ if (defined $mxbacktrace{"$user *** $oldhost"}) {
+ print "resetting oldhost $oldhost to the original: " if $debug;
+ $oldhost = $mxbacktrace{"$user *** $oldhost"};
+ print "$oldhost\n" if $debug;
+ }
+ $mxbacktrace{"$user *** $newhost"} = $oldhost;
+ print "mxbacktrace $user *** $newhost -> $oldhost\n" if $debug;
+ }
+ $mx{&trhost($oldhost)} = $newhost;
+ } else {
+ $temporary_redirect{$us} = $newhost;
+ }
+ if (@so) {
+ print "Can still $method $us: @so\n" if $debug;
+ $fall_table{$ft} = join(' ',@so);
+ } else {
+ print "No more fallbacks for $us\n" if $debug;
+ delete $fall_table{$ft};
+ }
+ if (defined $create_host_backtrack{$us}) {
+ $create_host_backtrack{"$user *** $newhost"}
+ = $create_host_backtrack{$us};
+ }
+ $fellback{"$user *** $newhost"} = $oldhost;
+ &expn($newhost,$user,$names{$us},$level{$us});
+ return 1;
+ }
+ delete $temporary_redirect{$us};
+ $host = $oldhost;
+ return 0;
+}
+# return 1 if you could send mail to the address as is.
+sub validAddr
+{
+ local($addr) = @_;
+ $res = &do_validAddr($addr);
+ print "validAddr($addr) = $res\n" if $debug;
+ $res;
+}
+sub do_validAddr
+{
+ local($addr) = @_;
+ local($urx) = "[-A-Za-z_.0-9+]+";
+
+ # \u
+ return 0 if ($addr =~ /^\\/);
+ # ?@h
+ return 1 if ($addr =~ /.\@$urx$/);
+ # @h:?
+ return 1 if ($addr =~ /^\@$urx\:./);
+ # h!u
+ return 1 if ($addr =~ /^$urx!./);
+ # u
+ return 1 if ($addr =~ /^$urx$/);
+ # ?
+ print "validAddr($addr) = ???\n" if $debug;
+ return 0;
+}
+# Some systems use expn and vrfy interchangeably. Some only
+# implement one or the other. Some check expn against mailing
+# lists and vrfy against users. It doesn't appear to be
+# consistent.
+#
+# So, what do we do? We try everything!
+#
+#
+# Ranking of result codes: good: 250, 251/551, 252, 550, anything else
+#
+# Ranking of inputs: best: user@host.domain, okay: user
+#
+# Return value: $error_string, @responses_from_server
+sub expn_vrfy
+{
+ local($u,$server) = @_;
+ local(@c) = ('expn', 'vrfy');
+ local(@try_u) = $u;
+ local(@ret,$code);
+
+ if (($u =~ /(.+)@(.+)/) && (&trhost($2) eq &trhost($server))) {
+ push(@try_u,$1);
+ }
+
+ TRY:
+ for $c (@c) {
+ for $try_u (@try_u) {
+ &alarm("${c}'ing $try_u on $server",'',$u);
+ &ps("$c $try_u");
+ alarm(0);
+ $s = <S>;
+ if ($s eq '') {
+ return "$server: lost connection";
+ }
+ if ($s !~ /^(\d+)([- ])/) {
+ return "$server: garbled reply to '$c $try_u'";
+ }
+ if ($1 == 250) {
+ $code = 250;
+ @ret = ("",$s);
+ push(@ret,&read_response($2,$debug));
+ return (@ret);
+ }
+ if ($1 == 551 || $1 == 251) {
+ $code = $1;
+ @ret = ("",$s);
+ push(@ret,&read_response($2,$debug));
+ next;
+ }
+ if ($1 == 252 && ($code == 0 || $code == 550)) {
+ $code = 252;
+ @ret = ("",$s);
+ push(@ret,&read_response($2,$watch));
+ next;
+ }
+ if ($1 == 550 && $code == 0) {
+ $code = 550;
+ @ret = ("",$s);
+ push(@ret,&read_response($2,$watch));
+ next;
+ }
+ &read_response($2,$watch);
+ }
+ }
+ return "$server: expn/vrfy not implemented" unless @ret;
+ return @ret;
+}
+# sometimes the old parse routine (now parse2) didn't
+# reject funky addresses.
+sub parse
+{
+ local($oldaddr,$server,$oldname,$one_to_one) = @_;
+ local($newhost, $newaddr, $newname, $um) = &parse2($oldaddr,$server,$oldname,$one_to_one);
+ if ($newaddr =~ m,^["/],) {
+ return (undef, $oldaddr, $newname) if $valid;
+ return (undef, $um, $newname);
+ }
+ return ($newhost, $newaddr, $newname);
+}
+
+# returns ($new_smtp_server,$new_address,$new_name)
+# given a response from a SMTP server ($newaddr), the
+# current host ($server), the old "name" and a flag that
+# indicates if it is being called during the initial
+# command line parsing ($parsing_args)
+sub parse2
+{
+ local($newaddr,$context_host,$old_name,$parsing_args) = @_;
+ local(@names) = $old_name;
+ local($urx) = "[-A-Za-z_.0-9+]+";
+ local($unmangle);
+
+ #
+ # first, separate out the address part.
+ #
+
+ #
+ # [NAME] <ADDR [(NAME)]>
+ # [NAME] <[(NAME)] ADDR
+ # ADDR [(NAME)]
+ # (NAME) ADDR
+ # [(NAME)] <ADDR>
+ #
+ if ($newaddr =~ /^\<(.*)\>$/) {
+ print "<A:$1>\n" if $debug;
+ ($newaddr) = &trim($1);
+ print "na = $newaddr\n" if $debug;
+ }
+ if ($newaddr =~ /^([^\<\>]*)\<([^\<\>]*)\>([^\<\>]*)$/) {
+ # address has a < > pair in it.
+ print "N:$1 <A:$2> N:$3\n" if $debug;
+ ($newaddr) = &trim($2);
+ unshift(@names, &trim($3,$1));
+ print "na = $newaddr\n" if $debug;
+ }
+ if ($newaddr =~ /^([^\(\)]*)\(([^\(\)]*)\)([^\(\)]*)$/) {
+ # address has a ( ) pair in it.
+ print "A:$1 (N:$2) A:$3\n" if $debug;
+ unshift(@names,&trim($2));
+ local($f,$l) = (&trim($1),&trim($3));
+ if (($f && $l) || !($f || $l)) {
+ # address looks like:
+ # foo (bar) baz or (bar)
+ # not allowed!
+ print STDERR "Could not parse $newaddr\n" if $vw;
+ return(undef,$newaddr,&firstname(@names));
+ }
+ $newaddr = $f if $f;
+ $newaddr = $l if $l;
+ print "newaddr now = $newaddr\n" if $debug;
+ }
+ #
+ # @foo:bar
+ # j%k@l
+ # a@b
+ # b!a
+ # a
+ #
+ $unmangle = $newaddr;
+ if ($newaddr =~ /^\@($urx)\:(.+)$/) {
+ print "(\@:)" if $debug;
+ # this is a bit of a cheat, but it seems necessary
+ return (&domainify($1,$context_host,$2),$2,&firstname(@names),$unmangle);
+ }
+ if ($newaddr =~ /^(.+)\@($urx)$/) {
+ print "(\@)" if $debug;
+ return (&domainify($2,$context_host,$newaddr),$newaddr,&firstname(@names),$unmangle);
+ }
+ if ($parsing_args) {
+ if ($newaddr =~ /^($urx)\!(.+)$/) {
+ return (&domainify($1,$context_host,$newaddr),$newaddr,&firstname(@names),$unmangle);
+ }
+ if ($newaddr =~ /^($urx)$/) {
+ return ($context_host,$newaddr,&firstname(@names),$unmangle);
+ }
+ print STDERR "Could not parse $newaddr\n";
+ }
+ print "(?)" if $debug;
+ return(undef,$newaddr,&firstname(@names),$unmangle);
+}
+# return $u (@$server) unless $u includes reference to $server
+sub compact
+{
+ local($u, $server) = @_;
+ local($se) = $server;
+ local($sp);
+ $se =~ s/(\W)/\\$1/g;
+ $sp = " (\@$server)";
+ if ($u !~ /$se/i) {
+ return "$u$sp";
+ }
+ return $u;
+}
+# remove empty (spaces don't count) members from an array
+sub trim
+{
+ local(@v) = @_;
+ local($v,@r);
+ for $v (@v) {
+ $v =~ s/^\s+//;
+ $v =~ s/\s+$//;
+ push(@r,$v) if ($v =~ /\S/);
+ }
+ return(@r);
+}
+# using the host part of an address, and the server name, add the
+# servers' domain to the address if it doesn't already have a
+# domain. Since this sometimes fails, save a back reference so
+# it can be unrolled.
+sub domainify
+{
+ local($host,$domain_host,$u) = @_;
+ local($domain,$newhost);
+
+ # cut of trailing dots
+ $host =~ s/\.$//;
+ $domain_host =~ s/\.$//;
+
+ if ($domain_host !~ /\./) {
+ #
+ # domain host isn't, keep $host whatever it is
+ #
+ print "domainify($host,$domain_host) = $host\n" if $debug;
+ return $host;
+ }
+
+ #
+ # There are several weird situtations that need to be
+ # accounted for. They have to do with domain relay hosts.
+ #
+ # Examples:
+ # host server "right answer"
+ #
+ # shiva.cs cs.berkeley.edu shiva.cs.berkeley.edu
+ # shiva cs.berkeley.edu shiva.cs.berekley.edu
+ # cumulus reed.edu @reed.edu:cumulus.uucp
+ # tiberius tc.cornell.edu tiberius.tc.cornell.edu
+ #
+ # The first try must always be to cut the domain part out of
+ # the server and tack it onto the host.
+ #
+ # A reasonable second try is to tack the whole server part onto
+ # the host and for each possible repeated element, eliminate
+ # just that part.
+ #
+ # These extra "guesses" get put into the %domainify_fallback
+ # array. They will be used to give addresses a second chance
+ # in the &giveup routine
+ #
+
+ local(%fallback);
+
+ local($long);
+ $long = "$host $domain_host";
+ $long =~ tr/A-Z/a-z/;
+ print "long = $long\n" if $debug;
+ if ($long =~ s/^([^ ]+\.)([^ ]+) \2(\.[^ ]+\.[^ ]+)/$1$2$3/) {
+ # matches shiva.cs cs.berkeley.edu and returns shiva.cs.berkeley.edu
+ print "condensed fallback $host $domain_host -> $long\n" if $debug;
+ $fallback{$long} = 9;
+ }
+
+ local($fh);
+ $fh = $domain_host;
+ while ($fh =~ /\./) {
+ print "FALLBACK $host.$fh = 1\n" if $debug > 7;
+ $fallback{"$host.$fh"} = 1;
+ $fh =~ s/^[^\.]+\.//;
+ }
+
+ $fallback{"$host.$domain_host"} = 2;
+
+ ($domain = $domain_host) =~ s/^[^\.]+//;
+ $fallback{"$host$domain"} = 6
+ if ($domain =~ /\./);
+
+ if ($host =~ /\./) {
+ #
+ # Host is already okay, but let's look for multiple
+ # interpretations
+ #
+ print "domainify($host,$domain_host) = $host\n" if $debug;
+ delete $fallback{$host};
+ $domainify_fallback{"$u *** $host"} = join(' ',sort {$fallback{$b} <=> $fallback{$a};} keys %fallback) if %fallback;
+ return $host;
+ }
+
+ $domain = ".$domain_host"
+ if ($domain !~ /\..*\./);
+ $newhost = "$host$domain";
+
+ $create_host_backtrack{"$u *** $newhost"} = $domain_host;
+ print "domainify($host,$domain_host) = $newhost\n" if $debug;
+ delete $fallback{$newhost};
+ $domainify_fallback{"$u *** $newhost"} = join(' ',sort {$fallback{$b} <=> $fallback{$a};} keys %fallback) if %fallback;
+ if ($debug) {
+ print "fallback = ";
+ print $domainify_fallback{"$u *** $newhost"}
+ if defined($domainify_fallback{"$u *** $newhost"});
+ print "\n";
+ }
+ return $newhost;
+}
+# return the first non-empty element of an array
+sub firstname
+{
+ local(@names) = @_;
+ local($n);
+ while(@names) {
+ $n = shift(@names);
+ return $n if $n =~ /\S/;
+ }
+ return undef;
+}
+# queue up more addresses to expand
+sub expn
+{
+ local($host,$addr,$name,$level) = @_;
+ if ($host) {
+ $host = &trhost($host);
+
+ if (($debug > 3) || (defined $giveup{$host})) {
+ unshift(@hosts,$host) unless $users{$host};
+ } else {
+ push(@hosts,$host) unless $users{$host};
+ }
+ $users{$host} .= " $addr";
+ $names{"$addr *** $host"} = $name;
+ $level{"$addr *** $host"} = $level + 1;
+ print "expn($host,$addr,$name)\n" if $debug;
+ return "\t$addr\n";
+ } else {
+ return &final($addr,'NONE',$name);
+ }
+}
+# compute the numerical average value of an array
+sub average
+{
+ local(@e) = @_;
+ return 0 unless @e;
+ local($e,$sum);
+ for $e (@e) {
+ $sum += $e;
+ }
+ $sum / @e;
+}
+# print to the server (also to stdout, if -w)
+sub ps
+{
+ local($p) = @_;
+ print ">>> $p\n" if $watch;
+ print S "$p\n";
+}
+# return case-adjusted name for a host (for comparison purposes)
+sub trhost
+{
+ # treat foo.bar as an alias for Foo.BAR
+ local($host) = @_;
+ local($trhost) = $host;
+ $trhost =~ tr/A-Z/a-z/;
+ if ($trhost{$trhost}) {
+ $host = $trhost{$trhost};
+ } else {
+ $trhost{$trhost} = $host;
+ }
+ $trhost{$trhost};
+}
+# re-queue users if an mx record dictates a redirect
+# don't allow a user to be redirected more than once
+sub mxredirect
+{
+ local($server,*users) = @_;
+ local($u,$nserver,@still_there);
+
+ $nserver = &mx($server);
+
+ if (&trhost($nserver) ne &trhost($server)) {
+ $0 = "$av0 - mx redirect $server -> $nserver\n";
+ for $u (@users) {
+ if (defined $mxbacktrace{"$u *** $nserver"}) {
+ push(@still_there,$u);
+ } else {
+ $mxbacktrace{"$u *** $nserver"} = $server;
+ print "mxbacktrace{$u *** $nserver} = $server\n"
+ if ($debug > 1);
+ &expn($nserver,$u,$names{"$u *** $server"});
+ }
+ }
+ @users = @still_there;
+ if (! @users) {
+ return $nserver;
+ } else {
+ return undef;
+ }
+ }
+ return undef;
+}
+# follow mx records, return a hostname
+# also follow temporary redirections comming from &domainify and
+# &mxlookup
+sub mx
+{
+ local($h,$u) = @_;
+
+ for (;;) {
+ if (defined $mx{&trhost($h)} && $h ne $mx{&trhost($h)}) {
+ $0 = "$av0 - mx expand $h";
+ $h = $mx{&trhost($h)};
+ return $h;
+ }
+ if ($u) {
+ if (defined $temporary_redirect{"$u *** $h"}) {
+ $0 = "$av0 - internal redirect $h";
+ print "Temporary redirect taken $u *** $h -> " if $debug;
+ $h = $temporary_redirect{"$u *** $h"};
+ print "$h\n" if $debug;
+ next;
+ }
+ $htr = &trhost($h);
+ if (defined $temporary_redirect{"$u *** $htr"}) {
+ $0 = "$av0 - internal redirect $h";
+ print "temporary redirect taken $u *** $h -> " if $debug;
+ $h = $temporary_redirect{"$u *** $htr"};
+ print "$h\n" if $debug;
+ next;
+ }
+ }
+ return $h;
+ }
+}
+# look up mx records with the name server.
+# re-queue expansion requests if possible
+# optionally give up on this host.
+sub mxlookup
+{
+ local($lastchance,$server,$giveup,*users) = @_;
+ local(*T);
+ local(*NSLOOKUP);
+ local($nh, $pref,$cpref);
+ local($o0) = $0;
+ local($nserver);
+ local($name,$aliases,$type,$len,$thataddr);
+ local(%fallback);
+
+ return 1 if &mxredirect($server,*users);
+
+ if ((defined $mx{$server}) || (! $have_nslookup)) {
+ return 0 unless $lastchance;
+ &giveup('mx domainify',$giveup);
+ return 0;
+ }
+
+ $0 = "$av0 - nslookup of $server";
+ open(T,">/tmp/expn$$") || die "open > /tmp/expn$$: $!\n";
+ print T "set querytype=MX\n";
+ print T "$server\n";
+ close(T);
+ $cpref = 1.0E12;
+ undef $nserver;
+ open(NSLOOKUP,"nslookup < /tmp/expn$$ 2>&1 |") || die "open nslookup: $!";
+ while(<NSLOOKUP>) {
+ print if ($debug > 2);
+ if (/mail exchanger = ([-A-Za-z_.0-9+]+)/) {
+ $nh = $1;
+ if (/preference = (\d+)/) {
+ $pref = $1;
+ if ($pref < $cpref) {
+ $nserver = $nh;
+ $cpref = $pref;
+ } elsif ($pref) {
+ $fallback{$pref} .= " $nh";
+ }
+ }
+ }
+ if (/Non-existent domain/) {
+ #
+ # These addresss are hosed. Kaput! Dead!
+ # However, if we created the address in the
+ # first place then there is a chance of
+ # salvation.
+ #
+ 1 while(<NSLOOKUP>);
+ close(NSLOOKUP);
+ return 0 unless $lastchance;
+ &giveup('domainify',"$server: Non-existent domain",undef,1);
+ return 0;
+ }
+
+ }
+ close(NSLOOKUP);
+ unlink("/tmp/expn$$");
+ unless ($nserver) {
+ $0 = "$o0 - finished mxlookup";
+ return 0 unless $lastchance;
+ &giveup('mx domainify',"$server: Could not resolve address");
+ return 0;
+ }
+
+ # provide fallbacks in case $nserver doesn't work out
+ if (defined $fallback{$cpref}) {
+ $mx_secondary{$server} = $fallback{$cpref};
+ }
+
+ $0 = "$av0 - gethostbyname($nserver)";
+ ($name,$aliases,$type,$len,$thataddr) = gethostbyname($nserver);
+
+ unless ($thataddr) {
+ $0 = $o0;
+ return 0 unless $lastchance;
+ &giveup('mx domainify',"$nserver: could not resolve address");
+ return 0;
+ }
+ print "MX($server) = $nserver\n" if $debug;
+ print "$server -> $nserver\n" if $vw && !$debug;
+ $mx{&trhost($server)} = $nserver;
+ # redeploy the users
+ unless (&mxredirect($server,*users)) {
+ return 0 unless $lastchance;
+ &giveup('mx domainify',"$nserver: only one level of mx redirect allowed");
+ return 0;
+ }
+ $0 = "$o0 - finished mxlookup";
+ return 1;
+}
+# if mx expansion did not help to resolve an address
+# (ie: foo@bar became @baz:foo@bar, then undo the
+# expansion).
+# this is only used by &final
+sub mxunroll
+{
+ local(*host,*addr) = @_;
+ local($r) = 0;
+ print "looking for mxbacktrace{$addr *** $host}\n"
+ if ($debug > 1);
+ while (defined $mxbacktrace{"$addr *** $host"}) {
+ print "Unrolling MX expnasion: \@$host:$addr -> "
+ if ($debug || $verbose);
+ $host = $mxbacktrace{"$addr *** $host"};
+ print "\@$host:$addr\n"
+ if ($debug || $verbose);
+ $r = 1;
+ }
+ return 1 if $r;
+ $addr = "\@$host:$addr"
+ if ($host =~ /\./);
+ return 0;
+}
+# register a completed expnasion. Make the final address as
+# simple as possible.
+sub final
+{
+ local($addr,$host,$name,$error) = @_;
+ local($he);
+ local($hb,$hr);
+ local($au,$ah);
+
+ if ($error =~ /Non-existent domain/) {
+ #
+ # If we created the domain, then let's undo the
+ # damage...
+ #
+ if (defined $create_host_backtrack{"$addr *** $host"}) {
+ while (defined $create_host_backtrack{"$addr *** $host"}) {
+ print "Un&domainifying($host) = " if $debug;
+ $host = $create_host_backtrack{"$addr *** $host"};
+ print "$host\n" if $debug;
+ }
+ $error = "$host: could not locate";
+ } else {
+ #
+ # If we only want valid addresses, toss out
+ # bad host names.
+ #
+ if ($valid) {
+ print STDERR "\@$host:$addr ($name) Non-existent domain\n";
+ return "";
+ }
+ }
+ }
+
+ MXUNWIND: {
+ $0 = "$av0 - final parsing of \@$host:$addr";
+ ($he = $host) =~ s/(\W)/\\$1/g;
+ if ($addr !~ /@/) {
+ # addr does not contain any host
+ $addr = "$addr@$host";
+ } elsif ($addr !~ /$he/i) {
+ # if host part really something else, use the something
+ # else.
+ if ($addr =~ m/(.*)\@([^\@]+)$/) {
+ ($au,$ah) = ($1,$2);
+ print "au = $au ah = $ah\n" if $debug;
+ if (defined $temporary_redirect{"$addr *** $ah"}) {
+ $addr = "$au\@".$temporary_redirect{"$addr *** $ah"};
+ print "Rewrite! to $addr\n" if $debug;
+ next MXUNWIND;
+ }
+ }
+ # addr does not contain full host
+ if ($valid) {
+ if ($host =~ /^([^\.]+)(\..+)$/) {
+ # host part has a . in it - foo.bar
+ ($hb, $hr) = ($1, $2);
+ if ($addr =~ /\@([^\.\@]+)$/ && ($1 eq $hb)) {
+ # addr part has not .
+ # and matches beginning of
+ # host part -- tack on a
+ # domain name.
+ $addr .= $hr;
+ } else {
+ &mxunroll(*host,*addr)
+ && redo MXUNWIND;
+ }
+ } else {
+ &mxunroll(*host,*addr)
+ && redo MXUNWIND;
+ }
+ } else {
+ $addr = "${addr}[\@$host]"
+ if ($host =~ /\./);
+ }
+ }
+ }
+ $name = "$name " if $name;
+ $error = " $error" if $error;
+ if ($valid) {
+ push(@final,"$name<$addr>");
+ } else {
+ push(@final,"$name<$addr>$error");
+ }
+ "\t$name<$addr>$error\n";
+}
+
+sub alarm
+{
+ local($alarm_action,$alarm_redirect,$alarm_user) = @_;
+ alarm(3600);
+ $SIG{ALRM} = 'handle_alarm';
+}
+# this involves one great big ugly hack.
+# the "next HOST" unwinds the stack!
+sub handle_alarm
+{
+ &giveup($alarm_redirect,"Timed out during $alarm_action",$alarm_user);
+ next HOST;
+}
+
+# read the rest of the current smtp daemon's response (and toss it away)
+sub read_response
+{
+ local($done,$watch) = @_;
+ local(@resp);
+ print $s if $watch;
+ while(($done eq "-") && ($s = <S>) && ($s =~ /^\d+([- ])/)) {
+ print $s if $watch;
+ $done = $1;
+ push(@resp,$s);
+ }
+ return @resp;
+}
+# print args if verbose. Return them in any case
+sub verbose
+{
+ local(@tp) = @_;
+ print "@tp" if $verbose;
+}
+# to pass perl -w:
+@tp;
+$flag_a;
+$flag_d;
+$flag_1;
+%already_domainify_fellback;
+%already_mx_fellback;
+&handle_alarm;
+################### BEGIN PERL/TROFF TRANSITION
+.00 ;
+
+'di
+.nr nl 0-1
+.nr % 0
+.\\"'; __END__
+.\" ############## END PERL/TROFF TRANSITION
+.TH EXPN 1 "March 11, 1993"
+.AT 3
+.SH NAME
+expn \- recursively expand mail aliases
+.SH SYNOPSIS
+.B expn
+.RI [ -a ]
+.RI [ -v ]
+.RI [ -w ]
+.RI [ -d ]
+.RI [ -1 ]
+.IR user [@ hostname ]
+.RI [ user [@ hostname ]]...
+.SH DESCRIPTION
+.B expn
+will use the SMTP
+.B expn
+and
+.B vrfy
+commands to expand mail aliases.
+It will first look up the addresses you provide on the command line.
+If those expand into addresses on other systems, it will
+connect to the other systems and expand again. It will keep
+doing this until no further expansion is possible.
+.SH OPTIONS
+The default output of
+.B expn
+can contain many lines which are not valid
+email addresses. With the
+.I -aa
+flag, only expansions that result in legal addresses
+are used. Since many mailing lists have an illegal
+address or two, the single
+.IR -a ,
+address, flag specifies that a few illegal addresses can
+be mixed into the results. More
+.I -a
+flags vary the ratio. Read the source to track down
+the formula. With the
+.I -a
+option, you should be able to construct a new mailing
+list out of an existing one.
+.LP
+If you wish to limit the number of levels deep that
+.B expn
+will recurse as it traces addresses, use the
+.I -1
+option. For each
+.I -1
+another level will be traversed. So,
+.I -111
+will traverse no more than three levels deep.
+.LP
+The normal mode of operation for
+.B expn
+is to do all of its work silently.
+The following options make it more verbose.
+It is not necessary to make it verbose to see what it is
+doing because as it works, it changes its
+.BR argv [0]
+variable to reflect its current activity.
+To see how it is expanding things, the
+.IR -v ,
+verbose, flag will cause
+.B expn
+to show each address before
+and after translation as it works.
+The
+.IR -w ,
+watch, flag will cause
+.B expn
+to show you its conversations with the mail daemons.
+Finally, the
+.IR -d ,
+debug, flag will expose many of the inner workings so that
+it is possible to eliminate bugs.
+.SH ENVIRONMENT
+No enviroment variables are used.
+.SH FILES
+.PD 0
+.B /tmp/expn$$
+.B temporary file used as input to
+.BR nslookup .
+.SH SEE ALSO
+.BR aliases (5),
+.BR sendmail (8),
+.BR nslookup (8),
+RFC 823, and RFC 1123.
+.SH BUGS
+Not all mail daemons will implement
+.B expn
+or
+.BR vrfy .
+It is not possible to verify addresses that are served
+by such daemons.
+.LP
+When attempting to connect to a system to verify an address,
+.B expn
+only tries one IP address. Most mail daemons
+will try harder.
+.LP
+It is assumed that you are running domain names and that
+the
+.BR nslookup (8)
+program is available. If not,
+.B expn
+will not be able to verify many addresses. It will also pause
+for a long time unless you change the code where it says
+.I $have_nslookup = 1
+to read
+.I $have_nslookup =
+.IR 0 .
+.LP
+Lastly,
+.B expn
+does not handle every valid address. If you have an example,
+please submit a bug report.
+.SH CREDITS
+In 1986 or so, Jon Broome wrote a program of the same name
+that did about the same thing. It has since suffered bit rot
+and Jon Broome has dropped off the face of the earth!
+(Jon, if you are out there, drop me a line)
+.SH AVAILABILITY
+The latest version of
+.B expn
+is available through anonymous ftp at
+.IR ftp://ftp.idiom.com/pub/muir-programs/expn .
+.SH AUTHOR
+.I David Muir Sharnoff\ \ \ \ <muir@idiom.com>
diff --git a/contrib/amd/scripts/expn.in b/contrib/amd/scripts/expn.in
new file mode 100755
index 0000000..b2bd1b6
--- /dev/null
+++ b/contrib/amd/scripts/expn.in
@@ -0,0 +1,1370 @@
+#!@PERL@
+'di ';
+'ds 00 \\"';
+'ig 00 ';
+#
+# THIS PROGRAM IS ITS OWN MANUAL PAGE. INSTALL IN man & bin.
+#
+
+# hardcoded constants, should work fine for BSD-based systems
+#require 'sys/socket.ph'; # perl 4
+use Socket; # perl 5
+$AF_INET = &AF_INET;
+$SOCK_STREAM = &SOCK_STREAM;
+$sockaddr = 'S n a4 x8';
+
+# system requirements:
+# must have 'nslookup' and 'hostname' programs.
+
+# $Header: /home/muir/bin/RCS/expn,v 3.9 1995/10/02 17:51:35 muir Exp muir $
+
+# TODO:
+# less magic should apply to command-line addresses
+# less magic should apply to local addresses
+# add magic to deal with cross-domain cnames
+
+# Checklist: (hard addresses)
+# 250 Kimmo Suominen <"|/usr/local/mh/lib/slocal -user kim"@grendel.tac.nyc.ny.us>
+# harry@hofmann.cs.Berkeley.EDU -> harry@tenet (.berkeley.edu) [dead]
+# bks@cs.berkeley.edu -> shiva.CS (.berkeley.edu) [dead]
+# dan@tc.cornell.edu -> brown@tiberius (.tc.cornell.edu)
+
+#############################################################################
+#
+# Copyright (c) 1993 David Muir Sharnoff
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the David Muir Sharnoff.
+# 4. The name of David Sharnoff may not be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE DAVID MUIR SHARNOFF ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL DAVID MUIR SHARNOFF BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# This copyright notice derrived from material copyrighted by the Regents
+# of the University of California.
+#
+# Contributions accepted.
+#
+#############################################################################
+
+# overall structure:
+# in an effort to not trace each address individually, but rather
+# ask each server in turn a whole bunch of questions, addresses to
+# be expanded are queued up.
+#
+# This means that all accounting w.r.t. an address must be stored in
+# various arrays. Generally these arrays are indexed by the
+# string "$addr *** $server" where $addr is the address to be
+# expanded "foo" or maybe "foo@bar" and $server is the hostname
+# of the SMTP server to contact.
+#
+
+# important global variables:
+#
+# @hosts : list of servers still to be contacted
+# $server : name of the current we are currently looking at
+# @users = $users{@hosts[0]} : addresses to expand at this server
+# $u = $users[0] : the current address being expanded
+# $names{"$users[0] *** $server"} : the 'name' associated with the address
+# $mxbacktrace{"$users[0] *** $server"} : record of mx expansion
+# $mx_secondary{$server} : other mx relays at the same priority
+# $domainify_fallback{"$users[0] *** $server"} : alternative names to try
+# instead of $server if $server doesn't work
+# $temporary_redirect{"$users[0] *** $server"} : when trying alternates,
+# temporarily channel all tries along current path
+# $giveup{$server} : do not bother expanding addresses at $server
+# $verbose : -v
+# $watch : -w
+# $vw : -v or -w
+# $debug : -d
+# $valid : -a
+# $levels : -1
+# S : the socket connection to $server
+
+$have_nslookup = 1; # we have the nslookup program
+$port = 'smtp';
+$av0 = $0;
+$ENV{'PATH'} .= ":/usr/etc" unless $ENV{'PATH'} =~ m,/usr/etc,;
+$ENV{'PATH'} .= ":/usr/ucb" unless $ENV{'PATH'} =~ m,/usr/ucb,;
+select(STDERR);
+
+$0 = "$av0 - running hostname";
+chop($name = `hostname || uname -n`);
+
+$0 = "$av0 - lookup host FQDN and IP addr";
+($hostname,$aliases,$type,$len,$thisaddr) = gethostbyname($name);
+
+$0 = "$av0 - parsing args";
+$usage = "Usage: $av0 [-1avwd] user[\@host] [user2[host2] ...]";
+for $a (@ARGV) {
+ die $usage if $a eq "-";
+ while ($a =~ s/^(-.*)([1avwd])/$1/) {
+ eval '$'."flag_$2 += 1";
+ }
+ next if $a eq "-";
+ die $usage if $a =~ /^-/;
+ &expn(&parse($a,$hostname,undef,1));
+}
+$verbose = $flag_v;
+$watch = $flag_w;
+$vw = $flag_v + $flag_w;
+$debug = $flag_d;
+$valid = $flag_a;
+$levels = $flag_1;
+
+die $usage unless @hosts;
+if ($valid) {
+ if ($valid == 1) {
+ $validRequirement = 0.8;
+ } elsif ($valid == 2) {
+ $validRequirement = 1.0;
+ } elsif ($valid == 3) {
+ $validRequirement = 0.9;
+ } else {
+ $validRequirement = (1 - (1/($valid-3)));
+ print "validRequirement = $validRequirement\n" if $debug;
+ }
+}
+
+$0 = "$av0 - building local socket";
+($name,$aliases,$proto) = getprotobyname('tcp');
+($name,$aliases,$port) = getservbyname($port,'tcp')
+ unless $port =~ /^\d+/;
+$this = pack($sockaddr, &AF_INET, 0, $thisaddr);
+
+HOST:
+while (@hosts) {
+ $server = shift(@hosts);
+ @users = split(' ',$users{$server});
+ delete $users{$server};
+
+ # is this server already known to be bad?
+ $0 = "$av0 - looking up $server";
+ if ($giveup{$server}) {
+ &giveup('mx domainify',$giveup{$server});
+ next;
+ }
+
+ # do we already have an mx record for this host?
+ next HOST if &mxredirect($server,*users);
+
+ # look it up, or try for an mx.
+ $0 = "$av0 - gethostbyname($server)";
+
+ ($name,$aliases,$type,$len,$thataddr) = gethostbyname($server);
+ # if we can't get an A record, try for an MX record.
+ unless($thataddr) {
+ &mxlookup(1,$server,"$server: could not resolve name",*users);
+ next HOST;
+ }
+
+ # get a connection, or look for an mx
+ $0 = "$av0 - socket to $server";
+ $that = pack($sockaddr, &AF_INET, $port, $thataddr);
+ socket(S, &AF_INET, &SOCK_STREAM, $proto)
+ || die "socket: $!";
+ $0 = "$av0 - bind to $server";
+ bind(S, $this)
+ || die "bind $hostname,0: $!";
+ $0 = "$av0 - connect to $server";
+ print "debug = $debug server = $server\n" if $debug > 8;
+ if (! connect(S, $that) || ($debug == 10 && $server =~ /relay\d.UU.NET$/i)) {
+ $0 = "$av0 - $server: could not connect: $!\n";
+ $emsg = $!;
+ unless (&mxlookup(0,$server,"$server: could not connect: $!",*users)) {
+ &giveup('mx',"$server: Could not connect: $emsg");
+ }
+ next HOST;
+ }
+ select((select(S),$| = 1)[0]); # don't buffer output to S
+
+ # read the greeting
+ $0 = "$av0 - talking to $server";
+ &alarm("greeting with $server",'');
+ while(<S>) {
+ alarm(0);
+ print if $watch;
+ if (/^(\d+)([- ])/) {
+ if ($1 != 220) {
+ $0 = "$av0 - bad numeric response from $server";
+ &alarm("giving up after bad response from $server",'');
+ &read_response($2,$watch);
+ alarm(0);
+ print STDERR "$server: NOT 220 greeting: $_"
+ if ($debug || $vw);
+ if (&mxlookup(0,$server,"$server: did not respond with a 220 greeting",*users)) {
+ close(S);
+ next HOST;
+ }
+ }
+ last if ($2 eq " ");
+ } else {
+ $0 = "$av0 - bad response from $server";
+ print STDERR "$server: NOT 220 greeting: $_"
+ if ($debug || $vw);
+ unless (&mxlookup(0,$server,"$server: did not respond with SMTP codes",*users)) {
+ &giveup('',"$server: did not talk SMTP");
+ }
+ close(S);
+ next HOST;
+ }
+ &alarm("greeting with $server",'');
+ }
+ alarm(0);
+
+ # if this causes problems, remove it
+ $0 = "$av0 - sending helo to $server";
+ &alarm("sending helo to $server","");
+ &ps("helo $hostname");
+ while(<S>) {
+ print if $watch;
+ last if /^\d+ /;
+ }
+ alarm(0);
+
+ # try the users, one by one
+ USER:
+ while(@users) {
+ $u = shift(@users);
+ $0 = "$av0 - expanding $u [\@$server]";
+
+ # do we already have a name for this user?
+ $oldname = $names{"$u *** $server"};
+
+ print &compact($u,$server)." ->\n" if ($verbose && ! $valid);
+ if ($valid) {
+ #
+ # when running with -a, we delay taking any action
+ # on the results of our query until we have looked
+ # at the complete output. @toFinal stores expansions
+ # that will be final if we take them. @toExpn stores
+ # expnansions that are not final. @isValid keeps
+ # track of our ability to send mail to each of the
+ # expansions.
+ #
+ @isValid = ();
+ @toFinal = ();
+ @toExpn = ();
+ }
+
+# ($ecode,@expansion) = &expn_vrfy($u,$server);
+ (@foo) = &expn_vrfy($u,$server);
+ ($ecode,@expansion) = @foo;
+ if ($ecode) {
+ &giveup('',$ecode,$u);
+ last USER;
+ }
+
+ for $s (@expansion) {
+ $s =~ s/[\n\r]//g;
+ $0 = "$av0 - parsing $server: $s";
+
+ $skipwatch = $watch;
+
+ if ($s =~ /^[25]51([- ]).*<(.+)>/) {
+ print "$s" if $watch;
+ print "(pretending 250$1<$2>)" if ($debug && $watch);
+ print "\n" if $watch;
+ $s = "250$1<$2>";
+ $skipwatch = 0;
+ }
+
+ if ($s =~ /^250([- ])(.+)/) {
+ print "$s\n" if $skipwatch;
+ ($done,$addr) = ($1,$2);
+ ($newhost, $newaddr, $newname) = &parse($addr,$server,$oldname, $#expansion == 0);
+ print "($newhost, $newaddr, $newname) = &parse($addr, $server, $oldname)\n" if $debug;
+ if (! $newhost) {
+ # no expansion is possible w/o a new server to call
+ if ($valid) {
+ push(@isValid, &validAddr($newaddr));
+ push(@toFinal,$newaddr,$server,$newname);
+ } else {
+ &verbose(&final($newaddr,$server,$newname));
+ }
+ } else {
+ $newmxhost = &mx($newhost,$newaddr);
+ print "$newmxhost = &mx($newhost)\n"
+ if ($debug && $newhost ne $newmxhost);
+ $0 = "$av0 - parsing $newaddr [@$newmxhost]";
+ print "levels = $levels, level{$u *** $server} = ".$level{"$u *** $server"}."\n" if ($debug > 1);
+ # If the new server is the current one,
+ # it would have expanded things for us
+ # if it could have. Mx records must be
+ # followed to compare server names.
+ # We are also done if the recursion
+ # count has been exceeded.
+ if (&trhost($newmxhost) eq &trhost($server) || ($levels && $level{"$u *** $server"} >= $levels)) {
+ if ($valid) {
+ push(@isValid, &validAddr($newaddr));
+ push(@toFinal,$newaddr,$newmxhost,$newname);
+ } else {
+ &verbose(&final($newaddr,$newmxhost,$newname));
+ }
+ } else {
+ # more work to do...
+ if ($valid) {
+ push(@isValid, &validAddr($newaddr));
+ push(@toExpn,$newmxhost,$newaddr,$newname,$level{"$u *** $server"});
+ } else {
+ &verbose(&expn($newmxhost,$newaddr,$newname,$level{"$u *** $server"}));
+ }
+ }
+ }
+ last if ($done eq " ");
+ next;
+ }
+ # 550 is a known code... Should the be
+ # included in -a output? Might be a bug
+ # here. Does it matter? Can assume that
+ # there won't be UNKNOWN USER responses
+ # mixed with valid users?
+ if ($s =~ /^(550)([- ])/) {
+ if ($valid) {
+ print STDERR "\@$server:$u ($oldname) USER UNKNOWN\n";
+ } else {
+ &verbose(&final($u,$server,$oldname,"USER UNKNOWN"));
+ }
+ last if ($2 eq " ");
+ next;
+ }
+ # 553 is a known code...
+ if ($s =~ /^(553)([- ])/) {
+ if ($valid) {
+ print STDERR "\@$server:$u ($oldname) USER AMBIGUOUS\n";
+ } else {
+ &verbose(&final($u,$server,$oldname,"USER AMBIGUOUS"));
+ }
+ last if ($2 eq " ");
+ next;
+ }
+ # 252 is a known code...
+ if ($s =~ /^(252)([- ])/) {
+ if ($valid) {
+ print STDERR "\@$server:$u ($oldname) REFUSED TO VRFY\n";
+ } else {
+ &verbose(&final($u,$server,$oldname,"REFUSED TO VRFY"));
+ }
+ last if ($2 eq " ");
+ next;
+ }
+ &giveup('',"$server: did not grok '$s'",$u);
+ last USER;
+ }
+
+ if ($valid) {
+ #
+ # now we decide if we are going to take these
+ # expansions or roll them back.
+ #
+ $avgValid = &average(@isValid);
+ print "avgValid = $avgValid\n" if $debug;
+ if ($avgValid >= $validRequirement) {
+ print &compact($u,$server)." ->\n" if $verbose;
+ while (@toExpn) {
+ &verbose(&expn(splice(@toExpn,0,4)));
+ }
+ while (@toFinal) {
+ &verbose(&final(splice(@toFinal,0,3)));
+ }
+ } else {
+ print "Tossing some valid to avoid invalid ".&compact($u,$server)."\n" if ($avgValid > 0.0 && ($vw || $debug));
+ print &compact($u,$server)." ->\n" if $verbose;
+ &verbose(&final($u,$server,$newname));
+ }
+ }
+ }
+
+ &alarm("sending 'quit' to $server",'');
+ $0 = "$av0 - sending 'quit' to $server";
+ &ps("quit");
+ while(<S>) {
+ print if $watch;
+ last if /^\d+ /;
+ }
+ close(S);
+ alarm(0);
+}
+
+$0 = "$av0 - printing final results";
+print "----------\n" if $vw;
+select(STDOUT);
+for $f (sort @final) {
+ print "$f\n";
+}
+unlink("/tmp/expn$$");
+exit(0);
+
+
+# abandon all attempts deliver to $server
+# register the current addresses as the final ones
+sub giveup
+{
+ local($redirect_okay,$reason,$user) = @_;
+ local($us,@so,$nh,@remaining_users);
+ local($pk,$file,$line);
+ ($pk, $file, $line) = caller;
+
+ $0 = "$av0 - giving up on $server: $reason";
+ #
+ # add back a user if we gave up in the middle
+ #
+ push(@users,$user) if $user;
+ #
+ # don't bother with this system anymore
+ #
+ unless ($giveup{$server}) {
+ $giveup{$server} = $reason;
+ print STDERR "$reason\n";
+ }
+ print "Giveup at $file:$line!!! redirect okay = $redirect_okay; $reason\n" if $debug;
+ #
+ # Wait!
+ # Before giving up, see if there is a chance that
+ # there is another host to redirect to!
+ # (Kids, don't do this at home! Hacking is a dangerous
+ # crime and you could end up behind bars.)
+ #
+ for $u (@users) {
+ if ($redirect_okay =~ /\bmx\b/) {
+ next if &try_fallback('mx',$u,*server,
+ *mx_secondary,
+ *already_mx_fellback);
+ }
+ if ($redirect_okay =~ /\bdomainify\b/) {
+ next if &try_fallback('domainify',$u,*server,
+ *domainify_fallback,
+ *already_domainify_fellback);
+ }
+ push(@remaining_users,$u);
+ }
+ @users = @remaining_users;
+ for $u (@users) {
+ print &compact($u,$server)." ->\n" if ($verbose && $valid && $u);
+ &verbose(&final($u,$server,$names{"$u *** $server"},$reason));
+ }
+}
+#
+# This routine is used only within &giveup. It checks to
+# see if we really have to giveup or if there is a second
+# chance because we did something before that can be
+# backtracked.
+#
+# %fallback{"$user *** $host"} tracks what is able to fallback
+# %fellback{"$user *** $host"} tracks what has fallen back
+#
+# If there is a valid backtrack, then queue up the new possibility
+#
+sub try_fallback
+{
+ local($method,$user,*host,*fall_table,*fellback) = @_;
+ local($us,$fallhost,$oldhost,$ft,$i);
+
+ if ($debug > 8) {
+ print "Fallback table $method:\n";
+ for $i (sort keys %fall_table) {
+ print "\t'$i'\t\t'$fall_table{$i}'\n";
+ }
+ print "Fellback table $method:\n";
+ for $i (sort keys %fellback) {
+ print "\t'$i'\t\t'$fellback{$i}'\n";
+ }
+ print "U: $user H: $host\n";
+ }
+
+ $us = "$user *** $host";
+ if (defined $fellback{$us}) {
+ #
+ # Undo a previous fallback so that we can try again
+ # Nested fallbacks are avoided because they could
+ # lead to infinite loops
+ #
+ $fallhost = $fellback{$us};
+ print "Already $method fell back from $us -> \n" if $debug;
+ $us = "$user *** $fallhost";
+ $oldhost = $fallhost;
+ } elsif (($method eq 'mx') && (defined $mxbacktrace{$us}) && (defined $mx_secondary{$mxbacktrace{$us}})) {
+ print "Fallback an MX expansion $us -> \n" if $debug;
+ $oldhost = $mxbacktrace{$us};
+ } else {
+ print "Oldhost($host, $us) = " if $debug;
+ $oldhost = $host;
+ }
+ print "$oldhost\n" if $debug;
+ if (((defined $fall_table{$us}) && ($ft = $us)) || ((defined $fall_table{$oldhost}) && ($ft = $oldhost))) {
+ print "$method Fallback = ".$fall_table{$ft}."\n" if $debug;
+ local(@so,$newhost);
+ @so = split(' ',$fall_table{$ft});
+ $newhost = shift(@so);
+ print "Falling back ($method) $us -> $newhost (from $oldhost)\n" if $debug;
+ if ($method eq 'mx') {
+ if (! defined ($mxbacktrace{"$user *** $newhost"})) {
+ if (defined $mxbacktrace{"$user *** $oldhost"}) {
+ print "resetting oldhost $oldhost to the original: " if $debug;
+ $oldhost = $mxbacktrace{"$user *** $oldhost"};
+ print "$oldhost\n" if $debug;
+ }
+ $mxbacktrace{"$user *** $newhost"} = $oldhost;
+ print "mxbacktrace $user *** $newhost -> $oldhost\n" if $debug;
+ }
+ $mx{&trhost($oldhost)} = $newhost;
+ } else {
+ $temporary_redirect{$us} = $newhost;
+ }
+ if (@so) {
+ print "Can still $method $us: @so\n" if $debug;
+ $fall_table{$ft} = join(' ',@so);
+ } else {
+ print "No more fallbacks for $us\n" if $debug;
+ delete $fall_table{$ft};
+ }
+ if (defined $create_host_backtrack{$us}) {
+ $create_host_backtrack{"$user *** $newhost"}
+ = $create_host_backtrack{$us};
+ }
+ $fellback{"$user *** $newhost"} = $oldhost;
+ &expn($newhost,$user,$names{$us},$level{$us});
+ return 1;
+ }
+ delete $temporary_redirect{$us};
+ $host = $oldhost;
+ return 0;
+}
+# return 1 if you could send mail to the address as is.
+sub validAddr
+{
+ local($addr) = @_;
+ $res = &do_validAddr($addr);
+ print "validAddr($addr) = $res\n" if $debug;
+ $res;
+}
+sub do_validAddr
+{
+ local($addr) = @_;
+ local($urx) = "[-A-Za-z_.0-9+]+";
+
+ # \u
+ return 0 if ($addr =~ /^\\/);
+ # ?@h
+ return 1 if ($addr =~ /.\@$urx$/);
+ # @h:?
+ return 1 if ($addr =~ /^\@$urx\:./);
+ # h!u
+ return 1 if ($addr =~ /^$urx!./);
+ # u
+ return 1 if ($addr =~ /^$urx$/);
+ # ?
+ print "validAddr($addr) = ???\n" if $debug;
+ return 0;
+}
+# Some systems use expn and vrfy interchangeably. Some only
+# implement one or the other. Some check expn against mailing
+# lists and vrfy against users. It doesn't appear to be
+# consistent.
+#
+# So, what do we do? We try everything!
+#
+#
+# Ranking of result codes: good: 250, 251/551, 252, 550, anything else
+#
+# Ranking of inputs: best: user@host.domain, okay: user
+#
+# Return value: $error_string, @responses_from_server
+sub expn_vrfy
+{
+ local($u,$server) = @_;
+ local(@c) = ('expn', 'vrfy');
+ local(@try_u) = $u;
+ local(@ret,$code);
+
+ if (($u =~ /(.+)@(.+)/) && (&trhost($2) eq &trhost($server))) {
+ push(@try_u,$1);
+ }
+
+ TRY:
+ for $c (@c) {
+ for $try_u (@try_u) {
+ &alarm("${c}'ing $try_u on $server",'',$u);
+ &ps("$c $try_u");
+ alarm(0);
+ $s = <S>;
+ if ($s eq '') {
+ return "$server: lost connection";
+ }
+ if ($s !~ /^(\d+)([- ])/) {
+ return "$server: garbled reply to '$c $try_u'";
+ }
+ if ($1 == 250) {
+ $code = 250;
+ @ret = ("",$s);
+ push(@ret,&read_response($2,$debug));
+ return (@ret);
+ }
+ if ($1 == 551 || $1 == 251) {
+ $code = $1;
+ @ret = ("",$s);
+ push(@ret,&read_response($2,$debug));
+ next;
+ }
+ if ($1 == 252 && ($code == 0 || $code == 550)) {
+ $code = 252;
+ @ret = ("",$s);
+ push(@ret,&read_response($2,$watch));
+ next;
+ }
+ if ($1 == 550 && $code == 0) {
+ $code = 550;
+ @ret = ("",$s);
+ push(@ret,&read_response($2,$watch));
+ next;
+ }
+ &read_response($2,$watch);
+ }
+ }
+ return "$server: expn/vrfy not implemented" unless @ret;
+ return @ret;
+}
+# sometimes the old parse routine (now parse2) didn't
+# reject funky addresses.
+sub parse
+{
+ local($oldaddr,$server,$oldname,$one_to_one) = @_;
+ local($newhost, $newaddr, $newname, $um) = &parse2($oldaddr,$server,$oldname,$one_to_one);
+ if ($newaddr =~ m,^["/],) {
+ return (undef, $oldaddr, $newname) if $valid;
+ return (undef, $um, $newname);
+ }
+ return ($newhost, $newaddr, $newname);
+}
+
+# returns ($new_smtp_server,$new_address,$new_name)
+# given a response from a SMTP server ($newaddr), the
+# current host ($server), the old "name" and a flag that
+# indicates if it is being called during the initial
+# command line parsing ($parsing_args)
+sub parse2
+{
+ local($newaddr,$context_host,$old_name,$parsing_args) = @_;
+ local(@names) = $old_name;
+ local($urx) = "[-A-Za-z_.0-9+]+";
+ local($unmangle);
+
+ #
+ # first, separate out the address part.
+ #
+
+ #
+ # [NAME] <ADDR [(NAME)]>
+ # [NAME] <[(NAME)] ADDR
+ # ADDR [(NAME)]
+ # (NAME) ADDR
+ # [(NAME)] <ADDR>
+ #
+ if ($newaddr =~ /^\<(.*)\>$/) {
+ print "<A:$1>\n" if $debug;
+ ($newaddr) = &trim($1);
+ print "na = $newaddr\n" if $debug;
+ }
+ if ($newaddr =~ /^([^\<\>]*)\<([^\<\>]*)\>([^\<\>]*)$/) {
+ # address has a < > pair in it.
+ print "N:$1 <A:$2> N:$3\n" if $debug;
+ ($newaddr) = &trim($2);
+ unshift(@names, &trim($3,$1));
+ print "na = $newaddr\n" if $debug;
+ }
+ if ($newaddr =~ /^([^\(\)]*)\(([^\(\)]*)\)([^\(\)]*)$/) {
+ # address has a ( ) pair in it.
+ print "A:$1 (N:$2) A:$3\n" if $debug;
+ unshift(@names,&trim($2));
+ local($f,$l) = (&trim($1),&trim($3));
+ if (($f && $l) || !($f || $l)) {
+ # address looks like:
+ # foo (bar) baz or (bar)
+ # not allowed!
+ print STDERR "Could not parse $newaddr\n" if $vw;
+ return(undef,$newaddr,&firstname(@names));
+ }
+ $newaddr = $f if $f;
+ $newaddr = $l if $l;
+ print "newaddr now = $newaddr\n" if $debug;
+ }
+ #
+ # @foo:bar
+ # j%k@l
+ # a@b
+ # b!a
+ # a
+ #
+ $unmangle = $newaddr;
+ if ($newaddr =~ /^\@($urx)\:(.+)$/) {
+ print "(\@:)" if $debug;
+ # this is a bit of a cheat, but it seems necessary
+ return (&domainify($1,$context_host,$2),$2,&firstname(@names),$unmangle);
+ }
+ if ($newaddr =~ /^(.+)\@($urx)$/) {
+ print "(\@)" if $debug;
+ return (&domainify($2,$context_host,$newaddr),$newaddr,&firstname(@names),$unmangle);
+ }
+ if ($parsing_args) {
+ if ($newaddr =~ /^($urx)\!(.+)$/) {
+ return (&domainify($1,$context_host,$newaddr),$newaddr,&firstname(@names),$unmangle);
+ }
+ if ($newaddr =~ /^($urx)$/) {
+ return ($context_host,$newaddr,&firstname(@names),$unmangle);
+ }
+ print STDERR "Could not parse $newaddr\n";
+ }
+ print "(?)" if $debug;
+ return(undef,$newaddr,&firstname(@names),$unmangle);
+}
+# return $u (@$server) unless $u includes reference to $server
+sub compact
+{
+ local($u, $server) = @_;
+ local($se) = $server;
+ local($sp);
+ $se =~ s/(\W)/\\$1/g;
+ $sp = " (\@$server)";
+ if ($u !~ /$se/i) {
+ return "$u$sp";
+ }
+ return $u;
+}
+# remove empty (spaces don't count) members from an array
+sub trim
+{
+ local(@v) = @_;
+ local($v,@r);
+ for $v (@v) {
+ $v =~ s/^\s+//;
+ $v =~ s/\s+$//;
+ push(@r,$v) if ($v =~ /\S/);
+ }
+ return(@r);
+}
+# using the host part of an address, and the server name, add the
+# servers' domain to the address if it doesn't already have a
+# domain. Since this sometimes fails, save a back reference so
+# it can be unrolled.
+sub domainify
+{
+ local($host,$domain_host,$u) = @_;
+ local($domain,$newhost);
+
+ # cut of trailing dots
+ $host =~ s/\.$//;
+ $domain_host =~ s/\.$//;
+
+ if ($domain_host !~ /\./) {
+ #
+ # domain host isn't, keep $host whatever it is
+ #
+ print "domainify($host,$domain_host) = $host\n" if $debug;
+ return $host;
+ }
+
+ #
+ # There are several weird situtations that need to be
+ # accounted for. They have to do with domain relay hosts.
+ #
+ # Examples:
+ # host server "right answer"
+ #
+ # shiva.cs cs.berkeley.edu shiva.cs.berkeley.edu
+ # shiva cs.berkeley.edu shiva.cs.berekley.edu
+ # cumulus reed.edu @reed.edu:cumulus.uucp
+ # tiberius tc.cornell.edu tiberius.tc.cornell.edu
+ #
+ # The first try must always be to cut the domain part out of
+ # the server and tack it onto the host.
+ #
+ # A reasonable second try is to tack the whole server part onto
+ # the host and for each possible repeated element, eliminate
+ # just that part.
+ #
+ # These extra "guesses" get put into the %domainify_fallback
+ # array. They will be used to give addresses a second chance
+ # in the &giveup routine
+ #
+
+ local(%fallback);
+
+ local($long);
+ $long = "$host $domain_host";
+ $long =~ tr/A-Z/a-z/;
+ print "long = $long\n" if $debug;
+ if ($long =~ s/^([^ ]+\.)([^ ]+) \2(\.[^ ]+\.[^ ]+)/$1$2$3/) {
+ # matches shiva.cs cs.berkeley.edu and returns shiva.cs.berkeley.edu
+ print "condensed fallback $host $domain_host -> $long\n" if $debug;
+ $fallback{$long} = 9;
+ }
+
+ local($fh);
+ $fh = $domain_host;
+ while ($fh =~ /\./) {
+ print "FALLBACK $host.$fh = 1\n" if $debug > 7;
+ $fallback{"$host.$fh"} = 1;
+ $fh =~ s/^[^\.]+\.//;
+ }
+
+ $fallback{"$host.$domain_host"} = 2;
+
+ ($domain = $domain_host) =~ s/^[^\.]+//;
+ $fallback{"$host$domain"} = 6
+ if ($domain =~ /\./);
+
+ if ($host =~ /\./) {
+ #
+ # Host is already okay, but let's look for multiple
+ # interpretations
+ #
+ print "domainify($host,$domain_host) = $host\n" if $debug;
+ delete $fallback{$host};
+ $domainify_fallback{"$u *** $host"} = join(' ',sort {$fallback{$b} <=> $fallback{$a};} keys %fallback) if %fallback;
+ return $host;
+ }
+
+ $domain = ".$domain_host"
+ if ($domain !~ /\..*\./);
+ $newhost = "$host$domain";
+
+ $create_host_backtrack{"$u *** $newhost"} = $domain_host;
+ print "domainify($host,$domain_host) = $newhost\n" if $debug;
+ delete $fallback{$newhost};
+ $domainify_fallback{"$u *** $newhost"} = join(' ',sort {$fallback{$b} <=> $fallback{$a};} keys %fallback) if %fallback;
+ if ($debug) {
+ print "fallback = ";
+ print $domainify_fallback{"$u *** $newhost"}
+ if defined($domainify_fallback{"$u *** $newhost"});
+ print "\n";
+ }
+ return $newhost;
+}
+# return the first non-empty element of an array
+sub firstname
+{
+ local(@names) = @_;
+ local($n);
+ while(@names) {
+ $n = shift(@names);
+ return $n if $n =~ /\S/;
+ }
+ return undef;
+}
+# queue up more addresses to expand
+sub expn
+{
+ local($host,$addr,$name,$level) = @_;
+ if ($host) {
+ $host = &trhost($host);
+
+ if (($debug > 3) || (defined $giveup{$host})) {
+ unshift(@hosts,$host) unless $users{$host};
+ } else {
+ push(@hosts,$host) unless $users{$host};
+ }
+ $users{$host} .= " $addr";
+ $names{"$addr *** $host"} = $name;
+ $level{"$addr *** $host"} = $level + 1;
+ print "expn($host,$addr,$name)\n" if $debug;
+ return "\t$addr\n";
+ } else {
+ return &final($addr,'NONE',$name);
+ }
+}
+# compute the numerical average value of an array
+sub average
+{
+ local(@e) = @_;
+ return 0 unless @e;
+ local($e,$sum);
+ for $e (@e) {
+ $sum += $e;
+ }
+ $sum / @e;
+}
+# print to the server (also to stdout, if -w)
+sub ps
+{
+ local($p) = @_;
+ print ">>> $p\n" if $watch;
+ print S "$p\n";
+}
+# return case-adjusted name for a host (for comparison purposes)
+sub trhost
+{
+ # treat foo.bar as an alias for Foo.BAR
+ local($host) = @_;
+ local($trhost) = $host;
+ $trhost =~ tr/A-Z/a-z/;
+ if ($trhost{$trhost}) {
+ $host = $trhost{$trhost};
+ } else {
+ $trhost{$trhost} = $host;
+ }
+ $trhost{$trhost};
+}
+# re-queue users if an mx record dictates a redirect
+# don't allow a user to be redirected more than once
+sub mxredirect
+{
+ local($server,*users) = @_;
+ local($u,$nserver,@still_there);
+
+ $nserver = &mx($server);
+
+ if (&trhost($nserver) ne &trhost($server)) {
+ $0 = "$av0 - mx redirect $server -> $nserver\n";
+ for $u (@users) {
+ if (defined $mxbacktrace{"$u *** $nserver"}) {
+ push(@still_there,$u);
+ } else {
+ $mxbacktrace{"$u *** $nserver"} = $server;
+ print "mxbacktrace{$u *** $nserver} = $server\n"
+ if ($debug > 1);
+ &expn($nserver,$u,$names{"$u *** $server"});
+ }
+ }
+ @users = @still_there;
+ if (! @users) {
+ return $nserver;
+ } else {
+ return undef;
+ }
+ }
+ return undef;
+}
+# follow mx records, return a hostname
+# also follow temporary redirections comming from &domainify and
+# &mxlookup
+sub mx
+{
+ local($h,$u) = @_;
+
+ for (;;) {
+ if (defined $mx{&trhost($h)} && $h ne $mx{&trhost($h)}) {
+ $0 = "$av0 - mx expand $h";
+ $h = $mx{&trhost($h)};
+ return $h;
+ }
+ if ($u) {
+ if (defined $temporary_redirect{"$u *** $h"}) {
+ $0 = "$av0 - internal redirect $h";
+ print "Temporary redirect taken $u *** $h -> " if $debug;
+ $h = $temporary_redirect{"$u *** $h"};
+ print "$h\n" if $debug;
+ next;
+ }
+ $htr = &trhost($h);
+ if (defined $temporary_redirect{"$u *** $htr"}) {
+ $0 = "$av0 - internal redirect $h";
+ print "temporary redirect taken $u *** $h -> " if $debug;
+ $h = $temporary_redirect{"$u *** $htr"};
+ print "$h\n" if $debug;
+ next;
+ }
+ }
+ return $h;
+ }
+}
+# look up mx records with the name server.
+# re-queue expansion requests if possible
+# optionally give up on this host.
+sub mxlookup
+{
+ local($lastchance,$server,$giveup,*users) = @_;
+ local(*T);
+ local(*NSLOOKUP);
+ local($nh, $pref,$cpref);
+ local($o0) = $0;
+ local($nserver);
+ local($name,$aliases,$type,$len,$thataddr);
+ local(%fallback);
+
+ return 1 if &mxredirect($server,*users);
+
+ if ((defined $mx{$server}) || (! $have_nslookup)) {
+ return 0 unless $lastchance;
+ &giveup('mx domainify',$giveup);
+ return 0;
+ }
+
+ $0 = "$av0 - nslookup of $server";
+ open(T,">/tmp/expn$$") || die "open > /tmp/expn$$: $!\n";
+ print T "set querytype=MX\n";
+ print T "$server\n";
+ close(T);
+ $cpref = 1.0E12;
+ undef $nserver;
+ open(NSLOOKUP,"nslookup < /tmp/expn$$ 2>&1 |") || die "open nslookup: $!";
+ while(<NSLOOKUP>) {
+ print if ($debug > 2);
+ if (/mail exchanger = ([-A-Za-z_.0-9+]+)/) {
+ $nh = $1;
+ if (/preference = (\d+)/) {
+ $pref = $1;
+ if ($pref < $cpref) {
+ $nserver = $nh;
+ $cpref = $pref;
+ } elsif ($pref) {
+ $fallback{$pref} .= " $nh";
+ }
+ }
+ }
+ if (/Non-existent domain/) {
+ #
+ # These addresss are hosed. Kaput! Dead!
+ # However, if we created the address in the
+ # first place then there is a chance of
+ # salvation.
+ #
+ 1 while(<NSLOOKUP>);
+ close(NSLOOKUP);
+ return 0 unless $lastchance;
+ &giveup('domainify',"$server: Non-existent domain",undef,1);
+ return 0;
+ }
+
+ }
+ close(NSLOOKUP);
+ unlink("/tmp/expn$$");
+ unless ($nserver) {
+ $0 = "$o0 - finished mxlookup";
+ return 0 unless $lastchance;
+ &giveup('mx domainify',"$server: Could not resolve address");
+ return 0;
+ }
+
+ # provide fallbacks in case $nserver doesn't work out
+ if (defined $fallback{$cpref}) {
+ $mx_secondary{$server} = $fallback{$cpref};
+ }
+
+ $0 = "$av0 - gethostbyname($nserver)";
+ ($name,$aliases,$type,$len,$thataddr) = gethostbyname($nserver);
+
+ unless ($thataddr) {
+ $0 = $o0;
+ return 0 unless $lastchance;
+ &giveup('mx domainify',"$nserver: could not resolve address");
+ return 0;
+ }
+ print "MX($server) = $nserver\n" if $debug;
+ print "$server -> $nserver\n" if $vw && !$debug;
+ $mx{&trhost($server)} = $nserver;
+ # redeploy the users
+ unless (&mxredirect($server,*users)) {
+ return 0 unless $lastchance;
+ &giveup('mx domainify',"$nserver: only one level of mx redirect allowed");
+ return 0;
+ }
+ $0 = "$o0 - finished mxlookup";
+ return 1;
+}
+# if mx expansion did not help to resolve an address
+# (ie: foo@bar became @baz:foo@bar, then undo the
+# expansion).
+# this is only used by &final
+sub mxunroll
+{
+ local(*host,*addr) = @_;
+ local($r) = 0;
+ print "looking for mxbacktrace{$addr *** $host}\n"
+ if ($debug > 1);
+ while (defined $mxbacktrace{"$addr *** $host"}) {
+ print "Unrolling MX expnasion: \@$host:$addr -> "
+ if ($debug || $verbose);
+ $host = $mxbacktrace{"$addr *** $host"};
+ print "\@$host:$addr\n"
+ if ($debug || $verbose);
+ $r = 1;
+ }
+ return 1 if $r;
+ $addr = "\@$host:$addr"
+ if ($host =~ /\./);
+ return 0;
+}
+# register a completed expnasion. Make the final address as
+# simple as possible.
+sub final
+{
+ local($addr,$host,$name,$error) = @_;
+ local($he);
+ local($hb,$hr);
+ local($au,$ah);
+
+ if ($error =~ /Non-existent domain/) {
+ #
+ # If we created the domain, then let's undo the
+ # damage...
+ #
+ if (defined $create_host_backtrack{"$addr *** $host"}) {
+ while (defined $create_host_backtrack{"$addr *** $host"}) {
+ print "Un&domainifying($host) = " if $debug;
+ $host = $create_host_backtrack{"$addr *** $host"};
+ print "$host\n" if $debug;
+ }
+ $error = "$host: could not locate";
+ } else {
+ #
+ # If we only want valid addresses, toss out
+ # bad host names.
+ #
+ if ($valid) {
+ print STDERR "\@$host:$addr ($name) Non-existent domain\n";
+ return "";
+ }
+ }
+ }
+
+ MXUNWIND: {
+ $0 = "$av0 - final parsing of \@$host:$addr";
+ ($he = $host) =~ s/(\W)/\\$1/g;
+ if ($addr !~ /@/) {
+ # addr does not contain any host
+ $addr = "$addr@$host";
+ } elsif ($addr !~ /$he/i) {
+ # if host part really something else, use the something
+ # else.
+ if ($addr =~ m/(.*)\@([^\@]+)$/) {
+ ($au,$ah) = ($1,$2);
+ print "au = $au ah = $ah\n" if $debug;
+ if (defined $temporary_redirect{"$addr *** $ah"}) {
+ $addr = "$au\@".$temporary_redirect{"$addr *** $ah"};
+ print "Rewrite! to $addr\n" if $debug;
+ next MXUNWIND;
+ }
+ }
+ # addr does not contain full host
+ if ($valid) {
+ if ($host =~ /^([^\.]+)(\..+)$/) {
+ # host part has a . in it - foo.bar
+ ($hb, $hr) = ($1, $2);
+ if ($addr =~ /\@([^\.\@]+)$/ && ($1 eq $hb)) {
+ # addr part has not .
+ # and matches beginning of
+ # host part -- tack on a
+ # domain name.
+ $addr .= $hr;
+ } else {
+ &mxunroll(*host,*addr)
+ && redo MXUNWIND;
+ }
+ } else {
+ &mxunroll(*host,*addr)
+ && redo MXUNWIND;
+ }
+ } else {
+ $addr = "${addr}[\@$host]"
+ if ($host =~ /\./);
+ }
+ }
+ }
+ $name = "$name " if $name;
+ $error = " $error" if $error;
+ if ($valid) {
+ push(@final,"$name<$addr>");
+ } else {
+ push(@final,"$name<$addr>$error");
+ }
+ "\t$name<$addr>$error\n";
+}
+
+sub alarm
+{
+ local($alarm_action,$alarm_redirect,$alarm_user) = @_;
+ alarm(3600);
+ $SIG{ALRM} = 'handle_alarm';
+}
+# this involves one great big ugly hack.
+# the "next HOST" unwinds the stack!
+sub handle_alarm
+{
+ &giveup($alarm_redirect,"Timed out during $alarm_action",$alarm_user);
+ next HOST;
+}
+
+# read the rest of the current smtp daemon's response (and toss it away)
+sub read_response
+{
+ local($done,$watch) = @_;
+ local(@resp);
+ print $s if $watch;
+ while(($done eq "-") && ($s = <S>) && ($s =~ /^\d+([- ])/)) {
+ print $s if $watch;
+ $done = $1;
+ push(@resp,$s);
+ }
+ return @resp;
+}
+# print args if verbose. Return them in any case
+sub verbose
+{
+ local(@tp) = @_;
+ print "@tp" if $verbose;
+}
+# to pass perl -w:
+@tp;
+$flag_a;
+$flag_d;
+$flag_1;
+%already_domainify_fellback;
+%already_mx_fellback;
+&handle_alarm;
+################### BEGIN PERL/TROFF TRANSITION
+.00 ;
+
+'di
+.nr nl 0-1
+.nr % 0
+.\\"'; __END__
+.\" ############## END PERL/TROFF TRANSITION
+.TH EXPN 1 "March 11, 1993"
+.AT 3
+.SH NAME
+expn \- recursively expand mail aliases
+.SH SYNOPSIS
+.B expn
+.RI [ -a ]
+.RI [ -v ]
+.RI [ -w ]
+.RI [ -d ]
+.RI [ -1 ]
+.IR user [@ hostname ]
+.RI [ user [@ hostname ]]...
+.SH DESCRIPTION
+.B expn
+will use the SMTP
+.B expn
+and
+.B vrfy
+commands to expand mail aliases.
+It will first look up the addresses you provide on the command line.
+If those expand into addresses on other systems, it will
+connect to the other systems and expand again. It will keep
+doing this until no further expansion is possible.
+.SH OPTIONS
+The default output of
+.B expn
+can contain many lines which are not valid
+email addresses. With the
+.I -aa
+flag, only expansions that result in legal addresses
+are used. Since many mailing lists have an illegal
+address or two, the single
+.IR -a ,
+address, flag specifies that a few illegal addresses can
+be mixed into the results. More
+.I -a
+flags vary the ratio. Read the source to track down
+the formula. With the
+.I -a
+option, you should be able to construct a new mailing
+list out of an existing one.
+.LP
+If you wish to limit the number of levels deep that
+.B expn
+will recurse as it traces addresses, use the
+.I -1
+option. For each
+.I -1
+another level will be traversed. So,
+.I -111
+will traverse no more than three levels deep.
+.LP
+The normal mode of operation for
+.B expn
+is to do all of its work silently.
+The following options make it more verbose.
+It is not necessary to make it verbose to see what it is
+doing because as it works, it changes its
+.BR argv [0]
+variable to reflect its current activity.
+To see how it is expanding things, the
+.IR -v ,
+verbose, flag will cause
+.B expn
+to show each address before
+and after translation as it works.
+The
+.IR -w ,
+watch, flag will cause
+.B expn
+to show you its conversations with the mail daemons.
+Finally, the
+.IR -d ,
+debug, flag will expose many of the inner workings so that
+it is possible to eliminate bugs.
+.SH ENVIRONMENT
+No enviroment variables are used.
+.SH FILES
+.PD 0
+.B /tmp/expn$$
+.B temporary file used as input to
+.BR nslookup .
+.SH SEE ALSO
+.BR aliases (5),
+.BR sendmail (8),
+.BR nslookup (8),
+RFC 823, and RFC 1123.
+.SH BUGS
+Not all mail daemons will implement
+.B expn
+or
+.BR vrfy .
+It is not possible to verify addresses that are served
+by such daemons.
+.LP
+When attempting to connect to a system to verify an address,
+.B expn
+only tries one IP address. Most mail daemons
+will try harder.
+.LP
+It is assumed that you are running domain names and that
+the
+.BR nslookup (8)
+program is available. If not,
+.B expn
+will not be able to verify many addresses. It will also pause
+for a long time unless you change the code where it says
+.I $have_nslookup = 1
+to read
+.I $have_nslookup =
+.IR 0 .
+.LP
+Lastly,
+.B expn
+does not handle every valid address. If you have an example,
+please submit a bug report.
+.SH CREDITS
+In 1986 or so, Jon Broome wrote a program of the same name
+that did about the same thing. It has since suffered bit rot
+and Jon Broome has dropped off the face of the earth!
+(Jon, if you are out there, drop me a line)
+.SH AVAILABILITY
+The latest version of
+.B expn
+is available through anonymous ftp at
+.IR ftp://ftp.idiom.com/pub/muir-programs/expn .
+.SH AUTHOR
+.I David Muir Sharnoff\ \ \ \ <muir@idiom.com>
diff --git a/contrib/amd/scripts/fix-amd-map.in b/contrib/amd/scripts/fix-amd-map.in
new file mode 100755
index 0000000..6746462
--- /dev/null
+++ b/contrib/amd/scripts/fix-amd-map.in
@@ -0,0 +1,52 @@
+#!@PERL@
+#
+# fix an old-syntax amd map to new one
+#
+# takes any number of files on the command line, and produces
+# a fixed map on stdout.
+#
+# Package: am-utils-6.0
+# Author: Erez Zadok <ezk@cs.columbia.edu>
+#
+
+##############################################################################
+### MAINTAINER EDITABLE SECTION
+
+# Mappings of old names to new ones:
+# Update when needed, do not forget commas but not on the last entry!
+# For your convenience, this is the complete list of all OSs that were
+# supported by amd-upl102, in their old names:
+#
+# 386bsd acis43 aix3 aoi aux bsd43 bsd44 bsdi11
+# concentrix dgux fpx4 freebsd hcx hlh42 hpux irix3 irix4 irix5 isc3
+# linux mach2 mach3 netbsd news4 next osf1 pyrOSx riscix riscos
+# rtu6 sos3 sos4 sos5 stellix svr4 u2_2 u3_0 u4_0 u4_2 u4_3 u4_4
+# umax43 utek utx32 xinu43
+#
+%mappings = (
+ "sos4", "sunos4",
+ "sos5", "sunos5",
+ "freebsd", "freebsd2"
+);
+
+##############################################################################
+### DO NOT EDIT ANYTHING BELOW
+
+# This is a trivial parser and works as follows:
+# (1) read each line
+# (2) search of regexps that start with '=', continue with a word to replace
+# and end with a non-value name (whitespace, ';', or newline
+while (<>) {
+ # skip trivial lines
+ if ($_ =~ /^$/ || $_ =~ /^#/) {
+ print;
+ next;
+ }
+ # modify the line if needed
+ foreach $m (keys %mappings) {
+ $val = $mappings{$m};
+ $_ =~ s/=$m([^a-zA-Z0-9_])/=$val$1/g;
+ }
+ # print the (possibly) modified line
+ print;
+}
diff --git a/contrib/amd/scripts/fixrmtab b/contrib/amd/scripts/fixrmtab
new file mode 100755
index 0000000..33b7bcf
--- /dev/null
+++ b/contrib/amd/scripts/fixrmtab
@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+# Invalidate /etc/rmtab entries for hosts named.
+# Restart mountd for changes to take effect.
+#
+# usage: fixrmtab host1 host2 ...
+#
+# Package: am-utils-6.0
+# Author: Andreas Stolcke <stolcke@icsi.berkeley.edu>
+
+#set -x
+
+RMTAB=/etc/rmtab
+TMP=/tmp/rmtab.$$
+
+if [ ! -f /etc/rmtab ]; then
+ exit 0
+fi
+
+for host in $*
+do
+ sed -e '/^'$host':/s/^./#/' $RMTAB > $TMP && cp $TMP $RMTAB
+done
+rm -f $TMP
diff --git a/contrib/amd/scripts/lostaltmail.conf-sample b/contrib/amd/scripts/lostaltmail.conf-sample
new file mode 100644
index 0000000..a20158c
--- /dev/null
+++ b/contrib/amd/scripts/lostaltmail.conf-sample
@@ -0,0 +1,84 @@
+# -*- perl -*-
+##############################################################################
+# #
+# CONFIGURABLE VALUES #
+# #
+##############################################################################
+
+$MAILGRUNT="postmaster"; # To whom to send log mail if mail is prefered.
+
+$TMPDIR="/tmp/"; # Place lostmail can do its dirty work.
+
+$LOCAL_LOCK_EXT=".lock"; # Name of file local mailer uses to lock
+ # spool file. This the correct setting for
+ # /bin/mail
+
+$SYSTEM_FROM_ADDRESS="Mailer-Daemon";
+
+$MAILDIR="/var/alt_mail"; # What directory should I run out of.
+$MAILER='/usr/lib/sendmail -t'; # Which mailer should I use.
+
+$LOCALMAILJUNK='.*~|\#.*|core'; # Files name patterns that might appear in
+ # alt_mail and should be ignored. This REGEXP
+ # gets or'ed with $MAILJUNK below.
+
+$SMTPHOST='localhost'; # The name of a local host which speaks SMTP
+ # and knows *all* your aliases. You probably
+ # don't want to change this. If the machine
+ # running lost_alt mail doesn't run an SMTP,
+ # daemon then something is either wrong or you
+ # should be setting `noverify' to prevent
+ # SMTP verification.
+
+$HOSTNAME='localhost'; # Hostname to use for SMTP HELO
+
+# Subject of lost log mail message. Must define $MAILGRUNT.
+# I overwrite this variable in the subroutine Clean_up. Please make sure I
+# haven't noodle-headdly forgotten to remove that hack in the distribution!
+# No newline here please. The script will insert it for you.
+$LOG_SUBJECT="Log of lostmail resends";
+
+##############################################################################
+# #
+# DEFAULTED CONFIGURATIONS #
+# #
+##############################################################################
+
+$LOGFILE="$TMPDIR" . "lostlog";
+
+
+# MAILJUNK is a pattern of ignorable alt_mail files which are either common
+# to most platforms or actually produced by this script. You should customize
+# this REGEXP by hacking at $LOCALMAILJUNK above.
+$MAILJUNK='[a-z]\.[0-9]*|\.\.?|lost\+found';
+
+$LOCKEXT=".lostlock"; # our lock file extension. Should not need to
+ # modify
+
+$MESSAGE_DELIM="^From[^:]"; # /bin/mail message delimiter. Your milage
+ # may differ
+
+$HEADER_BODY_DELIM="\n"; # RFC 822 header-body delimiter.
+
+$RESENT_TO="Resent-To: "; #
+$RESENT_FROM="Resent-From: "; # Resent headers (RFC 822).
+$RESENT_DATE="Resent-Date: "; # You probably don't want to muck with these.
+$RESENT_INFO="X-Resent-Info: "; # (special one to alert folks about mail).
+
+
+##############################################################################
+# #
+# LOSTMAIL DEFINITIONS (DON'T TOUCH) #
+# #
+##############################################################################
+
+$FALSE=0;
+$TRUE=(! $FALSE);
+
+$OK=$TRUE;
+$ABORT_RESEND=2;
+$LOCK_RETRIES=10; # The number of seconds/retries lost mail
+ # should wait before requeing or aborting a
+ # resend.
+
+TRUE; # Ansures true return from include file.
diff --git a/contrib/amd/scripts/lostaltmail.in b/contrib/amd/scripts/lostaltmail.in
new file mode 100755
index 0000000..5ba454c
--- /dev/null
+++ b/contrib/amd/scripts/lostaltmail.in
@@ -0,0 +1,648 @@
+#!@PERL@ -sw
+#
+# Package: am-utils-6.0
+# Author: James Tanis <jtt@cs.columbia.edu>
+#
+
+############################################################################
+#
+# lostaltmail -- remail files files found alt_mail (or -a argument to hlfsd) to
+# whomever should receive it. This version is for SMTP varient which
+# support VRFY as a non-expanding verifier!!! (sendmail V8 is a an
+# example).
+#
+# Usage: lostaltmail [-debug] [-nomail] [-noverify]
+#
+# GLOBAL VARIABLES (as if you care :-) )
+# Probably a very incomplete list.
+#
+# Everything in the config file for this program *and* ...
+#
+# $debug: set it from the command line with -debug. Does the obvious
+# $nomail: set it from the command line with -nomail. *Not* implied by
+# $debug
+# $currentTO: The addresss we are currently checking on. Actually this is
+# left over from an earlier version of lostaltmail and will hopefully
+# go away.
+# $noverify: set it from the address line. Avoid verification of $currentTO.
+# This should be relatively safe as long as your are willing to
+# endure bounces from mail that cannot be redelivered as opposed to
+# just getting a warning. UNTESTED (but should work).
+#
+# $logopen: state variable indicating weather the log file (should there be
+# one) is in fact open.
+#
+# @allentries: Array of all the directory entries in $MAILDIR
+# @allnames: Array of all *likely* recipients. It is created from @allentries
+# sans junk files (see $MAILJUNK and $LOCALMAILJUNK)
+# @wanderers: Array of all the files associated with a *single* address
+# which might need remailing. Should lostaltmail die unexpectedly,
+# it might leave a temporary file containing messages it was
+# currently trying to deliver. These will get picked and resent
+# later.
+#
+# VRFY: Handle onto SMTP verification channel. Not to be confused with mail
+# delivery; only verification occurs accross this handle.
+#
+############################################################################
+
+##############################################################################
+# #
+# SMTP_SEND #
+# #
+##############################################################################
+#
+# Send a message to the smtp channel. Inserts the necessary NEWLINE if it
+# does not exist;
+# I stole this from myself. It shouldn nott be printing errors to STDERR, but
+# this is a quick hack.
+#
+sub smtp_send {
+ local ($msg) = @_;
+ local ($length);
+
+ $length=length($msg);
+
+ if ( $msg !~ /^.*\n$/ ) {
+ $msg = $msg . "\n";
+ $length++;
+ }
+
+
+ if ( ! syswrite (VRFY, $msg, $length)) {
+ print STDERR "Failing SMTP write: $msg";
+ return 0;
+ }
+
+ return 1;
+}
+
+##############################################################################
+# #
+# SMTP_RECV #
+# #
+##############################################################################
+#
+# Read in lines from SMTP connection and return the final
+# Really hideous -- please excuse.
+#
+sub smtp_recv {
+ local ($line,$rin, $win, $ein, $readbuf, $ret);
+ $readbuf = "";
+
+ $rin = $win = $ein = ''; # Null fd sets,
+ vec ($rin, fileno(VRFY), 1) = 1; # Stolen straight from the example;
+ $ein = $rin | $win; # This is probably useless
+
+
+LINE_OF_INPUT:
+ while (1) { # Read in all the input
+
+ if ((select ( $rin, $win, $ein, 600.0))[0] == 0 ) {
+ print "select returned -1" if ($debug);
+ return -1; # timeout
+ }
+ sysread (VRFY, $readbuf, 1024);
+ chop ($readbuf);
+
+ foreach $line ( split('\n', $readbuf)) {
+
+ # This loop is actually needed since V8 has a multi-line greet.
+
+ ( $line =~ /^(\d\d\d).*/ && ($SMTP_retval=$1)) ||
+ warn "Badly formed reply from SMTP peer: $line\n";
+
+ # Space after return code indicates EOT
+
+ if ($line =~ /^\d\d\d /) {
+ $ret = $line; # Oddly $line is in a different context here;
+ # and thus we need to export it out of the
+ # while loop via $ret.
+ last LINE_OF_INPUT;
+ }
+ } # End of read.
+ } # End of input.
+
+ return $ret;
+}
+
+
+
+
+##############################################################################
+# #
+# LOG_INFO #
+# #
+##############################################################################
+#
+#
+# Opens appropriate logging file -- STDOUT (cron) or temp file (mail).
+#
+sub Log_info {
+ local($message) = @_;
+
+ if ( !$logopened ) {
+ if ( $MAILGRUNT eq "" || $debug) {
+ open (LOGFILE, ">-") || die "Unable to open stdout";
+ }
+ else {
+ # Snarf the log into a tmp file for final mailing to MAILGRUNT
+ $logfile = $LOGFILE . ".$$";
+ open (LOGFILE, (">". "$logfile")) || die "Unable to create log file";
+ }
+ }
+
+ $logopened=1; # Note that the log is now open
+
+ # Heart of the function.
+ print LOGFILE "$message";
+
+ print LOGFILE "\n" if ( index($message,"\n") == -1 );
+}
+
+##############################################################################
+# #
+# LOCK_FILE #
+# #
+##############################################################################
+
+#
+# Tries to grab a lock on the supplied file name.
+# Spins for a bit if it can't on the assumption that the lock will be released
+# quickly. If it times out and it's allowed to requeue, it will defer
+# until later, other wise write a message to loginfo.
+
+# If a recurring error or really unexpected situation arrises, return
+# ABORT_RESEND
+#
+# PARAMETERS
+# mailfile: path to the file to resend.
+# should_requeue: BOOLEAN - TRUE if the mailfile should be put on the
+# queue for a later retry if we can not finish
+# now.
+
+sub Lock_file {
+
+ local($mailfile,$should_requeue,$i,$new_lost_file) = @_;
+
+# We need to rename the current mailbox so that mail can loop back into it if
+# the resent mail just gets looped right back to us.
+ $new_lost_file = $mailfile . ".$$";
+
+# make a tmpfile name based on mailfile;
+ $lostlockfile = "$mailfile" . "$LOCKEXT";
+
+ if ( ! open(LOCKFILE, (">" . $lostlockfile)) ) {
+ printf(STDERR "Could not create lostlockfile for %s: %s\n", $mailfile,$!);
+ return $ABORT_RESEND;
+ }
+ close(LOCKFILE);
+
+ $maillockfile = "$mailfile" . "$LOCAL_LOCK_EXT";
+
+ for ($i=0; $i < $LOCK_RETRIES && ! link ($lostlockfile, $maillockfile);
+ $i++) {
+ sleep(1);
+ }
+
+ unlink($lostlockfile); # No matter what eliminate our cruft
+
+ if ( $i == $LOCK_RETRIES ) {
+ &Log_info("Could not grab lock on: " . "$mailfile" . " :timed out");
+ if ( $should_requeue ) {
+ &Log_info("Requeing " . "$mailfile" . " for later retry");
+ $retry_list .= " $mailfile";
+ }
+ else {
+ &Log_info("Giving up on: " . "$mailfile");
+ }
+
+ return $ABORT_RESEND;
+ }
+
+ # We created the link and therefore have the lock
+
+ if (rename ($mailfile, $new_lost_file) == 0 ){
+ # Failed to rename file -- this is serious.
+ unlink($maillockfile);
+ return $ABORT_RESEND;
+ }
+
+ unlink($maillockfile);
+ return $new_lost_file;
+
+}
+
+##############################################################################
+# #
+# PARSE NEXT MAIL MESSAGE #
+# #
+##############################################################################
+#
+# Parameters:
+# mailfile: handle of mailfile to use.
+#
+# Parses the next message in the mail file and inserts it in $current_msg
+#
+sub Get_next_msg {
+ local($mailfile,$found_body_delimiter) = @_;
+
+ # If this is the first message in the spool file, read the first line
+ # otherwise use the MESSAGE_DELIM line from the previous message (which we
+ # were forced to overread).
+
+ $done=$FALSE;
+ $found_body_delimiter=$FALSE;
+
+ # This if eats the very first "From " line and should never fire again.
+ if ( ! defined $current_msg ) {<$mailfile>};
+ undef ($current_msg); # Erase the old message.
+
+
+ # Read the mailfile and pass through all the lines up until the next
+ # message delimiter. Kill any previous resend headers.
+ while ( <$mailfile> ) {
+ last if (/$MESSAGE_DELIM/);
+ next if ( !$found_body_delimiter && /[Rr][Ee][Ss][Ee][Nn][Tt]-.+:/);
+ if ( !$found_body_delimiter && /^$HEADER_BODY_DELIM/) {
+ &Splice_in_resent_headers();
+ $found_body_delimiter=$TRUE;
+ }
+ if (defined($current_msg)) {
+ $current_msg .= $_;
+ } else {
+ $current_msg = $_;
+ }
+ }
+
+ # Return TRUE when we have hit the end of the file.
+ if (!defined($_) || $_ eq "" ) {
+ return $TRUE;
+ } else {
+ return $FALSE;
+ }
+}
+
+##############################################################################
+# #
+# SPLICE IN RESENT_HEADERS #
+# #
+##############################################################################
+#
+# Insert the Resent- headers at the *current location* of the message stream
+# (In Engish, print out a few Resent-X: lines and return :-) )
+# In addition splice in the X-resent-info: header.
+
+#
+# Paremters: None.
+# Return: None
+#
+sub Splice_in_resent_headers {
+ local($date,$utctime,$weekday,$time,$month,$hostname);
+
+ $current_msg .= "$RESENT_TO" . "$currentTO" . "\n";
+ $current_msg .= "$RESENT_FROM" . "$SYSTEM_FROM_ADDRESS" . "\n";
+
+ # Calculate date and time. It is a bit of a shame to do this each time
+ # the time needs to be acurate.
+
+ @utctime=gmtime(time);
+
+ $weekday=(Sun,Mon,Tue,Wed,Thu,Fri,Sat)[$utctime[6]];
+
+
+ # If the minutes or second do not take two columns each, patch em up.
+ if ( $utctime[1] < 10 ) {
+ if ( $utctime[0] < 10 ) {
+ $time=sprintf("%d:0%d:0%d",$utctime[2],$utctime[1],$utctime[0]);
+ }
+ else {
+ $time=sprintf("%d:0%d:%d",$utctime[2],$utctime[1],$utctime[0]);
+ }
+ }
+ else {
+ if ( $utctime[0] < 10 ) {
+ $time=sprintf("%d:%d:0%d",$utctime[2],$utctime[1],$utctime[0]);
+ }
+ else {
+ $time=sprintf("%d:%2d:%2d",$utctime[2],$utctime[1],$utctime[0]);
+ }
+ }
+
+ $month=(Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec)[$utctime[4]];
+
+ $date=sprintf("%s, %d %s %d %s UTC", $weekday, $utctime[3], $month, (($utctime[5] < 93 ? 20 : 19).$utctime[5]), $time);
+
+ $current_msg .= "$RESENT_DATE" . $date . "\n";
+
+ if ( defined $RESENT_INFO && $RESENT_INFO ne "") {
+ $hostname=`uname -n`;
+ $current_msg .= "$RESENT_INFO" . "Lost mail resent from ". $hostname;
+ }
+
+ return;
+}
+
+##############################################################################
+# #
+# DO_REMAIL #
+# #
+##############################################################################
+#
+# Actually resends the mail. Talks to the process configured as $MAILER
+# We need better handling.
+#
+sub Do_remail {
+ open (MAILER, "| $MAILER $currentTO") || return $ABORT_RESEND;
+ print MAILER $current_msg;
+ close (MAILER);
+}
+
+##############################################################################
+# #
+# CLEAN_UP #
+# #
+##############################################################################
+#
+# Clean up my messes.
+#
+sub Clean_up {
+ local ($hostname);
+
+ # Ugly local hack that you should never have seen, but I forgot to
+ # remove. Hopefully it did not kill you (I tried as you see), but you
+ # should eiter remove or update it for yourself. I find the message
+ # subject needs to have the hostname to be useful.
+ #
+ chop ($hostname=`uname -n`);
+ $LOG_SUBJECT="$LOG_SUBJECT from $hostname" if ( $hostname =~ /.*\.cs\.columbia\.edu/ );
+ #
+ # End of ugly local hack
+
+ # Mail any log info to MAILGRUNT.
+ if (defined($logfile) && $logfile ne "" ) {
+ close (LOGFILE); # Flush logfile output.
+ if ( -s $logfile ) {
+ open (MAILER, "| $MAILER $MAILGRUNT");
+
+ print MAILER "To: $MAILGRUNT\n";
+ print MAILER "Subject: $LOG_SUBJECT\n";
+ print MAILER "$HEADER_BODY_DELIM";
+
+ open (LOGFILE, "< $logfile");
+
+ while (<LOGFILE>) {
+ print MAILER $_;
+ }
+ close (MAILER);
+ close (LOGFILE);
+ }
+
+ unlink($logfile);
+ }
+ exit(0);
+}
+
+
+##############################################################################
+# #
+# COLLECT_WANDERERS #
+# #
+##############################################################################
+
+#
+# Collects other files that appear to be mail file for the $currentTO
+# but were not remailed successfully.
+#
+# Parameters: none (but uses $currentTO)
+# Return: True if a old mail directory is found. False otherwise.
+# Side effects: $wanderers set.
+#
+sub Collect_wanderers {
+
+ undef (@wanderers);
+
+ # Slurp in the directory and close.
+
+ return ($found);
+}
+
+#############################################################################
+# #
+# REMAIL ALL #
+# #
+#############################################################################
+
+#
+# Takes an array of files that all seem to share a common repcipient and
+# remails them if possible.
+#
+# Parameters: None (uses @wanderers).
+#
+sub Remail_all {
+ local($file,$i);
+
+ $i=0;
+ foreach $file (@wanderers) {
+ if ( !open (LOSTFILE, "< $file")) {
+ &Log_info("Could not open " . "$file" . " for remailing");
+ next;
+ }
+
+ do { # Power loop!
+ $done = &Get_next_msg(LOSTFILE); # Retrieve the next message...
+ &Do_remail; # and remail it.
+ } until $done;
+ undef ($current_msg); # Erase the final remailed message.
+
+ close(LOSTFILE); # Tidy up.
+
+ unlink ($file); # Remove the remailed file
+ $i++;
+ }
+
+}
+
+#############################################################################
+# #
+# CHECK_USER #
+# #
+#############################################################################
+
+#
+# Checks the password tables for the uid of $currentTO. If the user is
+# uid 0 (ie *supposed* to get mail in altmail) or unknown the resend is
+# aborted.
+#
+#
+sub Check_user {
+ local (@passwdinfo);
+ undef (@passwdinfo);
+
+ if ( !&vrfy_user($currentTO) ) {
+ &Log_info("Possible non user mail file: $currentTO");
+ return $ABORT_RESEND;
+ }
+
+ @passwdinfo = getpwnam($currentTO);
+
+ print "Non user mailable mail: Name: $currentTO\n"
+ if ( $debug && ! defined @passwdinfo );
+
+ return !$ABORT_RESEND if ( ! defined @passwdinfo ); # A non user but evidently mailable
+
+ print "Check User(): Name: $currentTO -- UID: $passwdinfo[2]\n" if ($debug);
+
+ return $ABORT_RESEND if ( $passwdinfo[2] == 0 );
+
+
+ return !$ABORT_RESEND;
+}
+
+#############################################################################
+# #
+# VRFY USER #
+# #
+#############################################################################
+#
+# Use SMTP VRFY to insure that argument is in fact a legal mail id.
+# Boolean: TRUE if mailable account, FALSE if not.
+
+sub vrfy_user {
+
+ local ($mailname,$repl) = @_;
+
+ if ( !&smtp_send("vrfy $mailname") ) {
+ &Log_info("Failed sending to vrfy smtp command for: $mailname");
+ return 0;
+ }
+
+ $repl = &smtp_recv;
+
+ print "VRFY REPLY: $repl\n" if ($debug);
+
+ return ( $repl =~ /^2\d\d/ );
+
+
+}
+
+
+#############################################################################
+# #
+# MAIN PROC #
+# #
+#############################################################################
+
+# dummy code to shut up perl -w
+$debug = 0 if !defined($debug);
+print $nomail if $debug > 1;
+print $RESENT_FROM if $debug > 1;
+print $logopen if $debug > 1;
+print $LOCAL_LOCK_EXT if $debug > 1;
+print $RESENT_TO if $debug > 1;
+print $LOCKEXT if $debug > 1;
+print $RESENT_DATE if $debug > 1;
+print $MESSAGE_DELIM if $debug > 1;
+print $SMTP_retval if $debug > 1;
+print $found if $debug > 1;
+print $retry_list if $debug > 1;
+print $MAILJUNK if $debug > 1;
+print $noverify if $debug > 1;
+print $SYSTEM_FROM_ADDRESS if $debug > 1;
+
+# BEGIN: stuff
+$prefix="@prefix@";
+$CONFIGDIR="@sysconfdir@"; # Directory where global config lives
+require "$CONFIGDIR/lostaltmail.conf" if (-f "$CONFIGDIR/lostaltmail.conf");
+require "/etc/global/lostaltmail.conf" if (-f "/etc/global/lostaltmail.conf");
+require "/etc/os/lostaltmail.conf" if (-f "/etc/os/lostaltmail.conf");
+require "/etc/local/lostaltmail.conf" if (-f "/etc/local/lostaltmail.conf");
+
+
+require "ctime.pl";
+use Socket;
+#require "sys/socket.ph";
+
+# SET some initial state variales
+$logopen = 0;
+
+#
+# Change to alt_dir
+#
+# Important!! This directory should be local. Folks will be responsible
+# for finding this out for themselves.
+#
+chdir ( $MAILDIR ) || die "Cannot change to $MAILDIR (`x' bit not set?)";
+
+#
+# slurp in directory
+#
+opendir (MAIL, ".") || die "Cannot open $MAILDIR (`r' bit not set?)";
+@allentries= readdir (MAIL);
+closedir (MAIL);
+@allnames = grep (!/$LOCALMAILJUNK|$MAILJUNK/, @allentries);
+
+# Open chanel to SMTP for verification -- unless this option is
+# configured off.
+
+if ( ! $noverify ) {
+ local($addr, $port,$sockaddr);
+
+ socket (VRFY, &AF_INET, &SOCK_STREAM, 0) ||
+ die "Could not create TCP socket (SMTP channel)";
+
+ $addr = (gethostbyname($SMTPHOST))[4]; # Just use the first addr
+
+ die "Could not obtain STMP host ($SMTPHOST) address"
+ if ( $addr eq "" );
+
+ $port = (getservbyname('smtp','tcp'))[2]; # Get smtp port.
+ die "Could not obtain SMTP port number" if (!defined($port));
+
+ printf("SMTP: address: %s port: $port\n",
+ join ('.',unpack('C4',$addr))) if ($debug);
+
+ $sockaddr = pack('n2C4x8',2, $port ,unpack('C4',$addr));
+
+ printf("Sockaddr: %s\n", join (' ',unpack('C14',$sockaddr))) if ($debug);
+
+ connect (VRFY, $sockaddr) ||
+ die "Could not connect to SMTP daemon on $SMTPHOST";
+
+ print "Establshed SMTP channel\n" if ($debug);
+
+ &smtp_recv; # Greet wait
+ &smtp_send("helo $SMTPHOST"); # Helo message for picky SMTPs
+ &smtp_recv; # Helo reply
+
+ # Connection is up and ready to VRFY
+}
+
+# main stuff starts here
+foreach $currentTO (@allnames) {
+ next if ( &Check_user == $ABORT_RESEND);
+
+ undef (@wanderers); # Just reset this at each pass.
+ @wanderers=grep (/$currentTO\.\d+/, @allentries);
+
+ $remail_file = &Lock_file($currentTO,$FALSE); # Need to lock the spool.
+
+ next if ( $remail_file eq $ABORT_RESEND); # Could not get that lock
+
+ push (@wanderers, $remail_file); # Try to resend "old" files.
+ print "List to remail: @wanderers\n" if ($debug);
+ # check if there is something to remail
+ &Remail_all if ( defined @wanderers && !$nomail);
+}
+
+# this stuff should run at the end
+foreach $file (grep (/$LOCALMAILJUNK/,@allentries)) {
+
+ if ($debug) {
+ print "Would unlink $file\n" if ($debug);
+ } else {
+ unlink $file if (-f $file);
+ }
+
+}
+&Clean_up; # Do a clean exit.
diff --git a/contrib/amd/scripts/wait4amd.in b/contrib/amd/scripts/wait4amd.in
new file mode 100755
index 0000000..5fd5030
--- /dev/null
+++ b/contrib/amd/scripts/wait4amd.in
@@ -0,0 +1,45 @@
+#!/bin/sh
+# wait for amd to start up and then execute program
+# usage: wait4amd <hostname> [<command> [args ...]]
+# If only hostname is supplied, command defaults to rsh $hostname
+#
+# Package: am-utils-6.0
+# Author: Erez Zadok <ezk@cs.columbia.edu>
+
+#set -x
+
+if [ "X$1" = "X" ]; then
+ echo "Usage: wait4amd <hostname> [<command> [args ...]]"
+ exit 1
+else
+ hostname=$1
+ shift
+fi
+
+# set path
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+PATH=@sbindir@:@bindir@:${PATH}
+export PATH
+
+while true
+do
+ amq -h $hostname > /dev/null 2>&1
+ if [ $? != 0 ]
+ then
+ # failed
+ echo "Amd not up. Sleeping..."
+ sleep 5;
+ else
+ echo "Amd is active on host $hostname!"
+ cmd=$*
+ if [ -z "${cmd}" ]
+ then
+ cmd="rlogin $hostname"
+ fi
+ echo "Running: $cmd"
+ $cmd
+ echo "Sleep 1 second"
+ sleep 1
+ fi
+done
diff --git a/contrib/amd/scripts/wait4amd2die.in b/contrib/amd/scripts/wait4amd2die.in
new file mode 100755
index 0000000..d3541e7
--- /dev/null
+++ b/contrib/amd/scripts/wait4amd2die.in
@@ -0,0 +1,49 @@
+#!/bin/sh
+# wait for amd to die on local host before returning from program.
+# Usage: wait4amd2die [delay [count]]
+# If not specified, delay=5 seconds and count=6 (total 30 seconds)
+# If at end of total delay amd is till up, return 1; else return 0.
+#
+# Package: am-utils-6.0
+# Author: Erez Zadok <ezk@cs.columbia.edu>
+
+#set -x
+
+# set path
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+PATH=@sbindir@:@bindir@:/usr/bin:/bin:${PATH}
+export PATH
+
+# how long to wait?
+if test -n "$1"
+then
+ delay=$1
+else
+ delay=5
+fi
+# how many times to delay
+if test -n "$2"
+then
+ count=$2
+else
+ count=6
+fi
+
+i=1
+maxcount=`expr $count + 1`
+while [ $i != $maxcount ]; do
+ # run amq
+ @sbindir@/amq > /dev/null 2>&1
+ if [ $? != 0 ]
+ then
+ # amq failed to run (because amd is dead)
+ echo "wait4amd2die: amd is down!"
+ exit 0
+ fi
+ echo "wait4amd2die: delay $delay sec ($i of $count)..."
+ sleep $delay
+ i=`expr $i + 1`
+done
+echo "wait4amd2die: amd is still up..."
+exit 1
OpenPOWER on IntegriCloud