]> arthur.barton.de Git - netatalk.git/commitdiff
Initial revision
authorrufustfirefly <rufustfirefly>
Tue, 25 Jul 2000 21:08:58 +0000 (21:08 +0000)
committerrufustfirefly <rufustfirefly>
Tue, 25 Jul 2000 21:08:58 +0000 (21:08 +0000)
359 files changed:
BUGS [new file with mode: 0644]
CHANGES [new file with mode: 0644]
CONTRIBUTORS [new file with mode: 0644]
COPYRIGHT [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
INSTALL/README.AFS [new file with mode: 0644]
INSTALL/README.FREEBSD [new file with mode: 0644]
INSTALL/README.GENERIC [new file with mode: 0644]
INSTALL/README.LINUX [new file with mode: 0644]
INSTALL/README.NETBSD [new file with mode: 0644]
INSTALL/README.OPENBSD [new file with mode: 0644]
INSTALL/README.SOLARIS [new file with mode: 0644]
INSTALL/README.SUNOS [new file with mode: 0644]
INSTALL/README.ULTRIX [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
README.ASUN [new file with mode: 0644]
TODO [new file with mode: 0644]
VERSION [new file with mode: 0644]
bin/Makefile [new file with mode: 0644]
bin/adv1tov2/Makefile [new file with mode: 0644]
bin/adv1tov2/adv1tov2.c [new file with mode: 0644]
bin/aecho/Makefile [new file with mode: 0644]
bin/aecho/aecho.c [new file with mode: 0644]
bin/afppasswd/Makefile [new file with mode: 0644]
bin/afppasswd/afppasswd.c [new file with mode: 0644]
bin/getzones/Makefile [new file with mode: 0644]
bin/getzones/getzones.c [new file with mode: 0644]
bin/megatron/Makefile [new file with mode: 0644]
bin/megatron/asingle.c [new file with mode: 0644]
bin/megatron/hqx.c [new file with mode: 0644]
bin/megatron/macbin.c [new file with mode: 0644]
bin/megatron/megatron.c [new file with mode: 0644]
bin/megatron/megatron.h [new file with mode: 0644]
bin/megatron/nad.c [new file with mode: 0644]
bin/megatron/updcrc.c [new file with mode: 0644]
bin/nbp/Makefile [new file with mode: 0644]
bin/nbp/nbplkup.c [new file with mode: 0644]
bin/nbp/nbprgstr.c [new file with mode: 0644]
bin/nbp/nbpunrgstr.c [new file with mode: 0644]
bin/pap/Makefile [new file with mode: 0644]
bin/pap/pap.c [new file with mode: 0644]
bin/pap/papstatus.c [new file with mode: 0644]
bin/psorder/Makefile [new file with mode: 0644]
bin/psorder/pa.c [new file with mode: 0644]
bin/psorder/pa.h [new file with mode: 0644]
bin/psorder/psorder.c [new file with mode: 0644]
bin/psorder/psorder.h [new file with mode: 0644]
config/AppleVolumes.default [new file with mode: 0644]
config/AppleVolumes.system [new file with mode: 0644]
config/AppleVolumes.system.cobalt [new file with mode: 0644]
config/afpd.conf [new file with mode: 0644]
config/atalkd.conf [new file with mode: 0644]
config/atalkd.conf.cobalt [new file with mode: 0644]
config/netatalk.conf [new file with mode: 0644]
config/netatalk.conf.cobalt [new file with mode: 0644]
config/netatalk.pamd [new file with mode: 0644]
config/papd.conf [new file with mode: 0644]
contrib/ICDumpSuffixMap [new file with mode: 0644]
contrib/printing/add_netatalk_printer [new file with mode: 0644]
contrib/printing/netatalk.template [new file with mode: 0644]
contrib/printing/timeout.c [new file with mode: 0644]
distrib/initscripts/rc.atalk.bsd [new file with mode: 0755]
distrib/initscripts/rc.atalk.cobalt [new file with mode: 0644]
distrib/initscripts/rc.atalk.redhat [new file with mode: 0755]
distrib/initscripts/rc.atalk.sysv [new file with mode: 0755]
distrib/rpm/buildrpm [new file with mode: 0755]
distrib/rpm/netatalk-asun-cobalt.spec [new file with mode: 0644]
distrib/rpm/netatalk-asun.makefile.patch [new file with mode: 0644]
distrib/rpm/netatalk-asun.spec [new file with mode: 0644]
etc/Makefile [new file with mode: 0644]
etc/afpd/Makefile [new file with mode: 0644]
etc/afpd/afp_asp.c [new file with mode: 0644]
etc/afpd/afp_dsi.c [new file with mode: 0644]
etc/afpd/afp_options.c [new file with mode: 0644]
etc/afpd/afs.c [new file with mode: 0644]
etc/afpd/appl.c [new file with mode: 0644]
etc/afpd/auth.c [new file with mode: 0644]
etc/afpd/auth.h [new file with mode: 0644]
etc/afpd/codepage.c [new file with mode: 0644]
etc/afpd/codepage.h [new file with mode: 0644]
etc/afpd/config.c [new file with mode: 0644]
etc/afpd/config.h [new file with mode: 0644]
etc/afpd/desktop.c [new file with mode: 0644]
etc/afpd/desktop.h [new file with mode: 0644]
etc/afpd/directory.c [new file with mode: 0644]
etc/afpd/directory.h [new file with mode: 0644]
etc/afpd/enumerate.c [new file with mode: 0644]
etc/afpd/file.c [new file with mode: 0644]
etc/afpd/file.h [new file with mode: 0644]
etc/afpd/filedir.c [new file with mode: 0644]
etc/afpd/filedir.h [new file with mode: 0644]
etc/afpd/fork.c [new file with mode: 0644]
etc/afpd/fork.h [new file with mode: 0644]
etc/afpd/gettok.c [new file with mode: 0644]
etc/afpd/globals.h [new file with mode: 0644]
etc/afpd/icon.h [new file with mode: 0644]
etc/afpd/main.c [new file with mode: 0644]
etc/afpd/messages.c [new file with mode: 0644]
etc/afpd/misc.h [new file with mode: 0644]
etc/afpd/nfsquota.c [new file with mode: 0644]
etc/afpd/nls/Makefile [new file with mode: 0644]
etc/afpd/nls/makecode.c [new file with mode: 0644]
etc/afpd/nls/parsecode.c [new file with mode: 0644]
etc/afpd/ofork.c [new file with mode: 0644]
etc/afpd/passwd.c [new file with mode: 0644]
etc/afpd/quota.c [new file with mode: 0644]
etc/afpd/status.c [new file with mode: 0644]
etc/afpd/status.h [new file with mode: 0644]
etc/afpd/switch.c [new file with mode: 0644]
etc/afpd/switch.h [new file with mode: 0644]
etc/afpd/uam.c [new file with mode: 0644]
etc/afpd/uam_auth.h [new file with mode: 0644]
etc/afpd/unix.c [new file with mode: 0644]
etc/afpd/unix.h [new file with mode: 0644]
etc/afpd/volume.c [new file with mode: 0644]
etc/afpd/volume.h [new file with mode: 0644]
etc/atalkd/Makefile [new file with mode: 0644]
etc/atalkd/aep.c [new file with mode: 0644]
etc/atalkd/atserv.h [new file with mode: 0644]
etc/atalkd/config.c [new file with mode: 0644]
etc/atalkd/gate.h [new file with mode: 0644]
etc/atalkd/interface.h [new file with mode: 0644]
etc/atalkd/list.h [new file with mode: 0644]
etc/atalkd/main.c [new file with mode: 0644]
etc/atalkd/multicast.c [new file with mode: 0644]
etc/atalkd/multicast.h [new file with mode: 0644]
etc/atalkd/nbp.c [new file with mode: 0644]
etc/atalkd/nbp.h [new file with mode: 0644]
etc/atalkd/route.c [new file with mode: 0644]
etc/atalkd/rtmp.c [new file with mode: 0644]
etc/atalkd/rtmp.h [new file with mode: 0644]
etc/atalkd/zip.c [new file with mode: 0644]
etc/atalkd/zip.h [new file with mode: 0644]
etc/papd/Makefile [new file with mode: 0644]
etc/papd/comment.c [new file with mode: 0644]
etc/papd/comment.h [new file with mode: 0644]
etc/papd/file.c [new file with mode: 0644]
etc/papd/file.h [new file with mode: 0644]
etc/papd/headers.c [new file with mode: 0644]
etc/papd/lp.c [new file with mode: 0644]
etc/papd/magics.c [new file with mode: 0644]
etc/papd/main.c [new file with mode: 0644]
etc/papd/ppd.c [new file with mode: 0644]
etc/papd/ppd.h [new file with mode: 0644]
etc/papd/printcap.c [new file with mode: 0644]
etc/papd/printer.h [new file with mode: 0644]
etc/papd/queries.c [new file with mode: 0644]
etc/papd/session.c [new file with mode: 0644]
etc/psf/Makefile [new file with mode: 0644]
etc/psf/etc2ps.sh [new file with mode: 0644]
etc/psf/pagecount.ps [new file with mode: 0644]
etc/psf/psa.c [new file with mode: 0644]
etc/psf/psf.c [new file with mode: 0644]
etc/uams/Makefile [new file with mode: 0644]
etc/uams/uams_dhx_pam.c [new file with mode: 0644]
etc/uams/uams_dhx_passwd.c [new file with mode: 0644]
etc/uams/uams_guest.c [new file with mode: 0644]
etc/uams/uams_krb4/kuam.c [new file with mode: 0644]
etc/uams/uams_krb4/lifetime.c [new file with mode: 0644]
etc/uams/uams_krb4/send_to_kdc.c [new file with mode: 0644]
etc/uams/uams_krb4/uams_krb4.c [new file with mode: 0644]
etc/uams/uams_pam.c [new file with mode: 0644]
etc/uams/uams_passwd.c [new file with mode: 0644]
etc/uams/uams_pgp.c [new file with mode: 0644]
etc/uams/uams_randnum.c [new file with mode: 0644]
include/Makefile [new file with mode: 0644]
include/atalk/adouble.h [new file with mode: 0644]
include/atalk/aep.h [new file with mode: 0644]
include/atalk/afp.h [new file with mode: 0644]
include/atalk/asp.h [new file with mode: 0644]
include/atalk/atp.h [new file with mode: 0644]
include/atalk/cnid.h [new file with mode: 0644]
include/atalk/compat.h [new file with mode: 0644]
include/atalk/ddp.h [new file with mode: 0644]
include/atalk/dsi.h [new file with mode: 0644]
include/atalk/nbp.h [new file with mode: 0644]
include/atalk/netddp.h [new file with mode: 0644]
include/atalk/pap.h [new file with mode: 0644]
include/atalk/paths.h [new file with mode: 0644]
include/atalk/rtmp.h [new file with mode: 0644]
include/atalk/server_child.h [new file with mode: 0644]
include/atalk/uam.h [new file with mode: 0644]
include/atalk/util.h [new file with mode: 0644]
include/atalk/zip.h [new file with mode: 0644]
libatalk/Makefile [new file with mode: 0644]
libatalk/adouble/Makefile [new file with mode: 0644]
libatalk/adouble/ad_attr.c [new file with mode: 0644]
libatalk/adouble/ad_date.c [new file with mode: 0644]
libatalk/adouble/ad_flush.c [new file with mode: 0644]
libatalk/adouble/ad_lock.c [new file with mode: 0644]
libatalk/adouble/ad_mmap.c [new file with mode: 0644]
libatalk/adouble/ad_open.c [new file with mode: 0644]
libatalk/adouble/ad_private.h [new file with mode: 0644]
libatalk/adouble/ad_read.c [new file with mode: 0644]
libatalk/adouble/ad_sendfile.c [new file with mode: 0644]
libatalk/adouble/ad_size.c [new file with mode: 0644]
libatalk/adouble/ad_write.c [new file with mode: 0644]
libatalk/asp/Makefile [new file with mode: 0644]
libatalk/asp/asp_attn.c [new file with mode: 0644]
libatalk/asp/asp_child.h [new file with mode: 0644]
libatalk/asp/asp_close.c [new file with mode: 0644]
libatalk/asp/asp_cmdreply.c [new file with mode: 0644]
libatalk/asp/asp_getreq.c [new file with mode: 0644]
libatalk/asp/asp_getsess.c [new file with mode: 0644]
libatalk/asp/asp_init.c [new file with mode: 0644]
libatalk/asp/asp_shutdown.c [new file with mode: 0644]
libatalk/asp/asp_tickle.c [new file with mode: 0644]
libatalk/asp/asp_write.c [new file with mode: 0644]
libatalk/atp/Makefile [new file with mode: 0644]
libatalk/atp/atp_bprint.c [new file with mode: 0644]
libatalk/atp/atp_bufs.c [new file with mode: 0644]
libatalk/atp/atp_close.c [new file with mode: 0644]
libatalk/atp/atp_internals.h [new file with mode: 0644]
libatalk/atp/atp_open.c [new file with mode: 0644]
libatalk/atp/atp_packet.c [new file with mode: 0644]
libatalk/atp/atp_rreq.c [new file with mode: 0644]
libatalk/atp/atp_rresp.c [new file with mode: 0644]
libatalk/atp/atp_rsel.c [new file with mode: 0644]
libatalk/atp/atp_sreq.c [new file with mode: 0644]
libatalk/atp/atp_sresp.c [new file with mode: 0644]
libatalk/cnid/Makefile [new file with mode: 0644]
libatalk/cnid/README [new file with mode: 0644]
libatalk/cnid/cnid_add.c [new file with mode: 0644]
libatalk/cnid/cnid_close.c [new file with mode: 0644]
libatalk/cnid/cnid_delete.c [new file with mode: 0644]
libatalk/cnid/cnid_get.c [new file with mode: 0644]
libatalk/cnid/cnid_lookup.c [new file with mode: 0644]
libatalk/cnid/cnid_meta.c [new file with mode: 0644]
libatalk/cnid/cnid_meta.h [new file with mode: 0644]
libatalk/cnid/cnid_nextid.c [new file with mode: 0644]
libatalk/cnid/cnid_open.c [new file with mode: 0644]
libatalk/cnid/cnid_private.h [new file with mode: 0644]
libatalk/cnid/cnid_resolve.c [new file with mode: 0644]
libatalk/cnid/cnid_update.c [new file with mode: 0644]
libatalk/compat/Makefile [new file with mode: 0644]
libatalk/compat/flock.c [new file with mode: 0644]
libatalk/compat/getusershell.c [new file with mode: 0644]
libatalk/compat/inet_aton.c [new file with mode: 0644]
libatalk/compat/mktemp.c [new file with mode: 0644]
libatalk/compat/rquota_xdr.c [new file with mode: 0644]
libatalk/compat/strcasecmp.c [new file with mode: 0644]
libatalk/compat/strdup.c [new file with mode: 0644]
libatalk/compat/strstr.c [new file with mode: 0644]
libatalk/dsi/Makefile [new file with mode: 0644]
libatalk/dsi/README [new file with mode: 0644]
libatalk/dsi/dsi_attn.c [new file with mode: 0644]
libatalk/dsi/dsi_close.c [new file with mode: 0644]
libatalk/dsi/dsi_cmdreply.c [new file with mode: 0644]
libatalk/dsi/dsi_getsess.c [new file with mode: 0644]
libatalk/dsi/dsi_getstat.c [new file with mode: 0644]
libatalk/dsi/dsi_init.c [new file with mode: 0644]
libatalk/dsi/dsi_opensess.c [new file with mode: 0644]
libatalk/dsi/dsi_private.h [new file with mode: 0644]
libatalk/dsi/dsi_read.c [new file with mode: 0644]
libatalk/dsi/dsi_stream.c [new file with mode: 0644]
libatalk/dsi/dsi_tcp.c [new file with mode: 0644]
libatalk/dsi/dsi_tickle.c [new file with mode: 0644]
libatalk/dsi/dsi_write.c [new file with mode: 0644]
libatalk/nbp/Makefile [new file with mode: 0644]
libatalk/nbp/nbp_conf.h [new file with mode: 0644]
libatalk/nbp/nbp_lkup.c [new file with mode: 0644]
libatalk/nbp/nbp_rgstr.c [new file with mode: 0644]
libatalk/nbp/nbp_unrgstr.c [new file with mode: 0644]
libatalk/nbp/nbp_util.c [new file with mode: 0644]
libatalk/netddp/Makefile [new file with mode: 0644]
libatalk/netddp/netddp_open.c [new file with mode: 0644]
libatalk/netddp/netddp_recvfrom.c [new file with mode: 0644]
libatalk/netddp/netddp_sendto.c [new file with mode: 0644]
libatalk/pap/pap_child.h [new file with mode: 0644]
libatalk/pap/pap_close.c [new file with mode: 0644]
libatalk/pap/pap_init.c [new file with mode: 0644]
libatalk/pap/pap_open.c [new file with mode: 0644]
libatalk/pap/pap_read.c [new file with mode: 0644]
libatalk/pap/pap_sendstatus.c [new file with mode: 0644]
libatalk/pap/pap_slinit.c [new file with mode: 0644]
libatalk/pap/pap_tickle.c [new file with mode: 0644]
libatalk/util/Makefile [new file with mode: 0644]
libatalk/util/atalk_addr.c [new file with mode: 0644]
libatalk/util/bprint.c [new file with mode: 0644]
libatalk/util/getiface.c [new file with mode: 0644]
libatalk/util/module.c [new file with mode: 0644]
libatalk/util/server_child.c [new file with mode: 0644]
libatalk/util/server_lock.c [new file with mode: 0644]
libatalk/util/strdicasecmp.c [new file with mode: 0644]
lp2pap.sh [new file with mode: 0755]
man/Makefile [new file with mode: 0644]
man/man1/Makefile [new file with mode: 0644]
man/man1/aecho.1 [new file with mode: 0644]
man/man1/getzones.1 [new file with mode: 0644]
man/man1/hqx2bin.1 [new file with mode: 0644]
man/man1/macbinary.1 [new file with mode: 0644]
man/man1/megatron.1 [new file with mode: 0644]
man/man1/nbp.1 [new file with mode: 0644]
man/man1/nbplkup.1 [new file with mode: 0644]
man/man1/nbprgstr.1 [new file with mode: 0644]
man/man1/nbpunrgstr.1 [new file with mode: 0644]
man/man1/pap.1 [new file with mode: 0644]
man/man1/papstatus.1 [new file with mode: 0644]
man/man1/psorder.1 [new file with mode: 0644]
man/man1/single2bin.1 [new file with mode: 0644]
man/man1/unbin.1 [new file with mode: 0644]
man/man1/unhex.1 [new file with mode: 0644]
man/man1/unsingle.1 [new file with mode: 0644]
man/man3/Makefile [new file with mode: 0644]
man/man3/atalk_aton.3 [new file with mode: 0644]
man/man3/nbp_name.3 [new file with mode: 0644]
man/man4/Makefile [new file with mode: 0644]
man/man4/atalk.4 [new file with mode: 0644]
man/man8/Makefile [new file with mode: 0644]
man/man8/afpd.8 [new file with mode: 0644]
man/man8/atalkd.8 [new file with mode: 0644]
man/man8/pap.8 [new file with mode: 0644]
man/man8/papd.8 [new file with mode: 0644]
man/man8/papstatus.8 [new file with mode: 0644]
man/man8/psf.8 [new file with mode: 0644]
services.atalk [new file with mode: 0644]
sys/freebsd/Makefile [new file with mode: 0644]
sys/generic/Makefile [new file with mode: 0644]
sys/generic/sys/cdefs.h [new file with mode: 0644]
sys/linux/Makefile [new file with mode: 0644]
sys/netatalk/aarp.c [new file with mode: 0644]
sys/netatalk/aarp.h [new file with mode: 0644]
sys/netatalk/at.h [new file with mode: 0644]
sys/netatalk/at_control.c [new file with mode: 0644]
sys/netatalk/at_proto.c [new file with mode: 0644]
sys/netatalk/at_var.h [new file with mode: 0644]
sys/netatalk/ddp.h [new file with mode: 0644]
sys/netatalk/ddp_input.c [new file with mode: 0644]
sys/netatalk/ddp_output.c [new file with mode: 0644]
sys/netatalk/ddp_usrreq.c [new file with mode: 0644]
sys/netatalk/ddp_var.h [new file with mode: 0644]
sys/netatalk/endian.h [new file with mode: 0644]
sys/netatalk/phase2.h [new file with mode: 0644]
sys/netbsd/Makefile [new file with mode: 0644]
sys/netbsd/netatalk/endian.h [new file with mode: 0644]
sys/openbsd/Makefile [new file with mode: 0644]
sys/osx/Makefile [new file with mode: 0644]
sys/solaris/Makefile [new file with mode: 0644]
sys/solaris/aarp.c [new file with mode: 0644]
sys/solaris/ddp.c [new file with mode: 0644]
sys/solaris/ddp.conf [new file with mode: 0644]
sys/solaris/dlpi.c [new file with mode: 0644]
sys/solaris/if.c [new file with mode: 0644]
sys/solaris/if.h [new file with mode: 0644]
sys/solaris/ioc.c [new file with mode: 0644]
sys/solaris/ioc.h [new file with mode: 0644]
sys/solaris/linkage.c [new file with mode: 0644]
sys/solaris/rt.c [new file with mode: 0644]
sys/solaris/rt.h [new file with mode: 0644]
sys/solaris/sock.c [new file with mode: 0644]
sys/solaris/sock.h [new file with mode: 0644]
sys/solaris/tpi.c [new file with mode: 0644]
sys/sunos/Makefile [new file with mode: 0644]
sys/sunos/at_sun.c [new file with mode: 0644]
sys/ultrix/Makefile [new file with mode: 0644]
sys/ultrix/at_ultrix.c [new file with mode: 0644]
sys/ultrix/kpatch-4.1 [new file with mode: 0644]
sys/ultrix/kpatch-4.2 [new file with mode: 0644]

diff --git a/BUGS b/BUGS
new file mode 100644 (file)
index 0000000..db81977
--- /dev/null
+++ b/BUGS
@@ -0,0 +1,26 @@
+Please use this format when reporting bugs to netatalk@umich.edu.
+Assuming that you've not supplied a fix, our goal will be to reproduce
+your problem, here.  If we are missing information necessary to do
+that, we may need to ask you to provide it, thus lengthening the
+turn-around time for a fix.
+
+Subject: Short summary of the problem (please make this meaningful!)
+
+Environment:
+       The version of netatalk you're running, any changes you've made
+       to the configuration, e.g. AFS, what machine you're running
+       netatalk on, and the version of the operating system.  If Macs
+       are involved, the types of Mac, how they are connected and what
+       system software they are running.  If printers are involved,
+       the type of printer and how they are connected.  Any other
+       pertinent information.
+
+Description:
+       Detailed description of the problem, suggestion, or complaint.
+
+Repeat-By:
+       The sequence of events that causes the problem to occur.
+
+Fix:
+       Description of how to fix the problem.  If you don't know a fix
+       for the problem, don't include this section.
diff --git a/CHANGES b/CHANGES
new file mode 100644 (file)
index 0000000..8f2a8e4
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,247 @@
+Changes from the 1.4b1 release:
+
+    Fixed the maximum free/total volume size in afpd.
+
+    Made ~ the default volume in afpd.
+
+    Fixed pid file handling and changed setpgrp() to setpgid() in afpd,
+    papd, and atalkd.
+
+    Added code to afpd to set the Unix file time stamps with utime().
+
+    Fixed a bug in papd's printcap code which limited it to 15 or so
+    printers.
+
+    Fixed papd's handling of piped printers.
+
+    Fixed papd's handling of bad job names.
+
+    Fixed atalkd to send NBP LKUP packets from NBP port.
+
+    Added "sync;sync;sync" to Solaris kinstall to help with streams
+    file corruption.
+
+    Added nlocalrts to streams ddp.conf.  Thanks Thomas Tornblom.
+
+    Fixed signed extension infinite loop in Solaris module.
+
+    Moved all the config files to .../config.
+
+Changes from the 1.3.3 release:
+
+    Added code from Sun Microsystems, Inc (OPCOM) for Solaris support.
+    See COPYRIGHT.
+
+    Added support for FreeBSD, mostly changes by Mark Dawson and Julian
+    Elischer.
+
+    All sorts of other stuff.
+
+Changes from the 1.3.1 release:
+
+    Added options to psf's filter names to support accounting on HPs.
+    !!! NOTE:  The location of the filters has changed, see the man
+    page for where.
+
+    Added code from Alan Cox to support Linux.
+
+    Rewrote papd.  Now handles dropped connections better.
+    Configuration has been modernized.  !!! NOTE: The format of the
+    configuration file has changed, but NOT THE NAME.
+
+    Added Kerberos support to papd.
+
+    atalkd now removes routes on a SIGTERM.  Still can't just restart
+    it, but it's closer.
+
+    Changed atalkd and the kernel driver to remove a hack added to
+    support sending to 0.255.  Now the kernel will allow multiple open
+    sockets with the same port, so long as the addess associated with
+    the port is different.  atalkd now opens a socket for each port on
+    each interface.
+
+    atalkd now rewrites its configuration file.  If no configuration
+    file is given, one will be generated.  Permissions on the new
+    configuration file will be inherited from the old one.  If there is
+    no old one, permissions default to 644.  Won't rewrite the file if
+    the owner doesn't have write permission.
+
+    Removed support for the "AFS Kerberos UAM", in favor of the
+    "AuthMan UAM".  Kerberos support should now be much more
+    straight-forward.
+
+    Fixed a bug in afpd which would cause incorrect group calculations
+    on ultrix machines.
+
+    Fixed a bug in afpd which causes SimpleText and some other
+    applications to silently fail to write.  There's also a bug in
+    MacOS, but we can't fix that.
+
+    Fixed a bad interaction with afpd and AFS which would cause file
+    writes to not propogate between AFS clients.
+
+    !!! CHANGED the name(s) of afpd's config files.  The new files are
+    AppleVolumes.system and AppleVolumes.default.  If AppleVolumes.system
+    exists, it is always read, AppleVolumes.default is only read if the
+    user has no AppleVolumes file.  Included a flag "-u" to indicate
+    which file has precedence.  "-u" user wins, otherwise ".system"
+    wins.
+
+    Rewrote the AppleVolumes parsing code.  Now works.
+
+    Added a filename extension mapping to afpd.  User always takes
+    precedence, regardless of the "-u" flag.  Code to change the type
+    of all Unix files contributed by Kee Hinckley <nazgul@utopia.com>.
+
+    afpd now supports both UFS and AFS volumes simultaneously.  It also
+    uses access() to attempt to calculate reasonable Mac permissions
+    for AFS directories.
+
+    Changed reporting of file times.  Files that are written from Unix
+    now update the Mac's idea of the files modification time.  Unix
+    mtime is now reported instead of ctime.
+
+    Added support for a new UAM to afpd.  This requires that client
+    Macs have MacTCP and AuthMan installed.  Should make running afpd
+    for AFS easier.
+
+    Removed code so that otherwise valid volumes for which the mounting
+    user has no permission will appear in the volume selection dialog
+    on the Mac gray-ed out.
+
+    Added code from Chris Metcalf of MIT to the AppleDouble library
+    which improves permission inheritance.
+
+    Added code from G. Paul Ziemba of Alantec, Inc to better report
+    errors in psf.  Also changed psf to use syslog for errors that
+    users aren't interested in.
+
+    Added information to psf's man page to better explain the
+    interaction between psf, pap, and lpd.
+
+    Make psf/pap/psa do accounting when it's turnes on in
+    /etc/printcap.
+
+    Changed pap's error message when there is no printer specified on
+    the command line and no .paprc is found.  Also heavily modified
+    pap's man page to reflect changes in the "new" version of pap,
+    including moving it from section 8 to section 1.
+
+    Fixed a byte-order bug in pap's sequence numbers.  Doubt if pap has
+    ever worked right on little endian machines!
+
+    Added a flag to pap to optionally close before receiving EOF from
+    the printer.  Off by default.  psf calls pap with this option on.
+
+    Added timeouts to the nbp library calls.  This means that processes
+    won't hang when atalkd dies during boot, thus hanging your
+    machine.
+
+Changes from the 1.3 release:
+
+    Fixed a bug in afpd which would cause APPL mappings to contain both
+    mac and unix path names.  The fixed code will handle the old
+    (corrupted) database.
+
+    Fixed a *very* serious bug which would cause files to be corrupted
+    when copying to afpd.
+
+    Fixed a bug in afpd which would cause replies to icon writes to
+    contain the written icon.
+
+    Filled in the function code switch in afpd.  Previously, a hacker
+    could probably have used afpd to get unauthorized access to a
+    machine running afpd.
+
+    Fixed a bug in the asp portion of libatalk.a which could cause the
+    malloc()/free() database to be corrupted.
+
+    Fixed a bug in atalkd's zip query code.  With this bug, only the
+    first N % 255 nets get queried.  However, since nets bigger than
+    255 are usually pretty unstable, the unqueried for nets will
+    eventually get done, when N drops by one.
+
+    Suppressed a spurious error ("route: No such process") in atalkd.
+
+Changes from the 1.2.1 release:
+
+    atalkd is completely rewritten for phase 2 support.  atalkd.conf
+    from previous version will not work!
+
+    afpd now has better AFS support.  In particular, the configuration
+    for AFS was made much easier; a number of Kerberos-related
+    byte-ordering and time problems were found; clear-text passwords
+    were added (thanks to geeb@umich.edu).
+
+    afpd now handles Unix permissions much better (thanks to
+    metcalf@mit.edu).
+
+    There are many, many more changes, but most are small bug fixes.
+
+Changes from the 1.2 release:
+
+    The Sun support now uses loadable kernel modules (a la VDDRV)
+    instead of binary patches. As such, it should work on any sunos
+    greater than 4.1, and is confirmed to work under 4.1.1 and 4.1.2.
+
+    The DEC support no longer requires source. It also runs under
+    ultrix 4.1 and 4.2. It still requires patching your kernel, but the
+    patches are limited to those files available to binary-only sites
+    -- primarily hooks for things like netatalk.
+
+    The etc.rc script now uses changes made to nbprgstr (see below).
+
+    aecho now takes machine names on the command line.
+
+    nbplkup now takes a command line argument specifying the number of
+    responses to accept. It also takes its defaults from the NBPLKUP
+    environment variable.
+
+    nbprgstr may be used to register a name at any requested port.
+
+    afpd now logs if an illegal shell is used during login, instead of
+    silently denying service.
+
+    A bug in afpd which caused position information for the directory
+    children of the root of a volume to be ignored has been fixed.
+
+    Several typos in afpd which would cause include files necessary to
+    ultrix to be skipped have been fixed.
+
+    atalkd will no long propagate routes to networks whose zone
+    it doesn't know.
+
+    atalkd no longer dumps core if it receives a ZIP GetMyZone request
+    from a network whose zone it doesn't know. (Since this currently
+    can only happen from off net, it's not precisely a legal request.)
+
+    pap and papd (optionally) no longer check the connection id in PAP
+    DATA responses. Both also maintain the function code in non-first-packet
+    PAP DATA responses.  These changes are work-arounds to deal with
+    certain AppleTalk printer cards, notably the BridgePort LocalTalk
+    card for HP LJIIISIs.
+
+    pap no longer sends an EOF response to each PAP SENDDATA request,
+    only the first.
+
+    A bug in papd which would cause it to return a random value when
+    printing the procset to a piped printer has been fixed.
+
+    A bug relating to NBP on reverse-endian machines has been fixed.
+
+    atp_rsel() from libatalk now returns a correct value even if it
+    hasn't recieved anything yet.
+
+    atalk_addr() from libatalk no longer accepts addresses in octal
+    format, since AppleTalk addresses can have leading zeros. Also it
+    checks that the separator character is a '.'.
+
+    Pseudo man pages for nbplkup, nbprgstr, and nbpunrgstr, have been
+    added.
+
+    The example in the psf(8) man page is now correct.
+
+    The man pages for changed commands have been updated.
+
+    The README files for various machine have been updated
+    appropriately.
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
new file mode 100644 (file)
index 0000000..e1c268d
--- /dev/null
@@ -0,0 +1,37 @@
+GENERAL:
+-------
+Name:  Wesley Craig 
+Email: wesley.craig@umich.edu
+
+Name:  Adrian Sun
+Email: asun@cobaltnet.com
+
+
+MISCELLANEOUS PATCHES:
+---------------
+Name:    Steve Hirsch
+What:    ProDOS II support/initial Randnum support
+
+Name:    Robert Marinchick
+What:    initial rquota patch
+
+Name:    Frank Morton
+What:    megatron information patch
+
+TESTING:
+-------
+Name:    Tim Carlson
+What:    Solaris 
+
+
+DISTRIBUTIONS:
+-------------
+Name:    iNOUE Koichi
+What:    RPM for RedHat systems
+
+Name:   Stefan Bethke
+What:    FreeBSD
+
+ADVICE:
+------
+Name: Leland Wallace
diff --git a/COPYRIGHT b/COPYRIGHT
new file mode 100644 (file)
index 0000000..7400d7d
--- /dev/null
+++ b/COPYRIGHT
@@ -0,0 +1,46 @@
+Copyright (c) 1990,1996 Regents of The University of Michigan.
+All Rights Reserved.
+
+    Permission to use, copy, modify, and distribute this software and
+    its documentation for any purpose and without fee is hereby granted,
+    provided that the above copyright notice appears in all copies and
+    that both that copyright notice and this permission notice appear
+    in supporting documentation, and that the name of The University
+    of Michigan not be used in advertising or publicity pertaining to
+    distribution of the software without specific, written prior
+    permission. This software is supplied as is without expressed or
+    implied warranties of any kind.
+
+This product includes software developed by the University of
+California, Berkeley and its contributors.
+
+Solaris code is encumbered by the following:
+    Copyright (C) 1996 by Sun Microsystems Computer Co.
+    Permission to use, copy, modify, and distribute this software and
+    its documentation for any purpose and without fee is hereby
+    granted, provided that the above copyright notice appear in all
+    copies and that both that copyright notice and this permission
+    notice appear in supporting documentation.  This software is
+    provided "as is" without express or implied warranty.
+
+Modifications for Appleshare IP and other files copyrighted by Adrian
+Sun are under the following copyright:
+
+    Copyright (c) 1997,1998,1999,2000 Adrian Sun (asun@cobalt.com)
+    All Rights Reserved.
+
+    Permission to use, copy, modify, and distribute this software and
+    its documentation for any purpose and without fee is hereby granted,
+    provided that the above copyright notice appears in all copies and
+    that both that copyright notice and this permission notice appear
+    in supporting documentation. This software is supplied as is
+    without expressed or implied warranties of any kind.
+
+Research Systems Unix Group
+The University of Michigan
+c/o Wesley Craig
+535 W. William Street
+Ann Arbor, Michigan
++1-313-764-2278
+netatalk@umich.edu
diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..a5c5b5f
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,1823 @@
+2000-02-28  a sun  <asun@asun.cobalt.com>
+
+       * etc/afpd/directory.h (CNID_INODE): xor the inode a little
+       differently. 
+
+2000-02-23  a sun  <asun@asun.cobalt.com>
+
+       * etc/afpd/volume.c (creatvol): / is a special case. you can't
+       share it unless you give it a name.
+
+2000-02-21  a sun  <asun@asun.cobalt.com>
+
+       * distrib/initscripts/rc.atalk.redhat/cobalt: added changes to
+       make redhat 6.x happier.
+
+2000-02-17  a sun  <asun@asun.cobalt.com>
+
+       * libatalk/adouble/ad_lock.c (adf_unlock): off-by-one error with
+       lock removal. this + the log right below fix ragtime.
+
+2000-02-16  a sun  <asun@asun.cobalt.com>
+
+       * etc/afpd/fork.c (afp_bytelock): only error on bytelocks
+       positioned at 0x7FFFFFFF if there's no resource fork.
+
+2000-02-14  a sun  <asun@asun.cobalt.com>
+
+       * libatalk/adouble/ad_lock.c: re-wrote locking bits so that
+       allocations happen in blocks. added missing case that omnis
+       database triggers.
+
+2000-02-07  a sun  <asun@asun.cobalt.com>
+
+       * bin/nbp/Makefile (install): make nbprgstr/nbpunrgstr with 700
+       permissions. 
+
+       * include/atalk/adouble.h (sendfile): change to deal with
+       <sys/sendfile.h> 
+
+2000-01-25  a sun  <asun@asun.cobalt.com>
+
+       * etc/afpd/ofork.c: keep track of oforks being used for each
+       directory so that we can update them if the directory tree gets
+       modified. 
+
+       * etc/afpd/directory.c (deletecurdir): remove dangling symlinks on
+       delete. 
+
+2000-01-24  a sun  <asun@asun.cobalt.com>
+
+       * etc/afpd/directory.h (CNID): moved cnid assignment here along
+       with helpful macros.
+
+       * etc/afpd/directory.c: changed directory search to use red-black
+       trees to improve balance. parent-child tree changed to circular
+       doubly-linked list to speed up insert/remove times.  there's still
+       one obstacle to actually freeing red-black tree entries. i need to
+       add an ofork list to struct dir to minimize search times.
+
+2000-01-18  a sun  <asun@asun.cobalt.com>
+
+       * etc/afpd/directory.c (dirinsert): detect attempts to add
+       pre-existing entries as just symbolic links. 
+
+       * etc/afpd/filedir.h (CNID): moved inode-cnid assignment here and
+       extended to directories.
+
+2000-01-03  a sun  <asun@asun.cobalt.com>
+
+       * etc/uams/uams_pam.c (PAM_conv): surround PAM_BINARY_PROMPT with
+       an #ifdef.
+
+       * etc/afpd/status.c (status_init): fixed a bunch of problems here
+       that manifested under solaris 7.
+
+       * etc/afpd/main.c (main): use FD_SETSIZE instead of FD_SETSIZE +
+       1.
+       
+
+1999-12-27  a sun  <asun@asun.cobalt.com>
+
+       * libatalk/util/getiface.c: moved interface detection code to here
+       so that i can use if_nameindex() or getifconf() depending upon
+       what's available.
+
+1999-12-13  a sun  <asun@asun.cobalt.com>
+
+       * libatalk/dsi/dsi_tcp.c (dsi_tcp_init): added if_nameindex()
+       based interface code.
+
+       * etc/afpd/afp_options.c (afp_options_parseline): added
+       -server_quantum as an option. using hex would be a good idea.
+
+       * libatalk/dsi/dsi_opensess.c (dsi_opensession): added bits to set
+       the server quantum. by default, the server quantum is limited to
+       1MB due to a bug in the os 9 appleshare client.
+
+       * distrib/initscripts/rc.atalk.{cobalt,redhat}: surround nbp stuff
+       with double quotes.
+
+       * etc/uams/uams_dhx_pam.c (pam_changepw): added dhx-based password
+       changing for pam.
+
+1999-12-06  a sun  <asun@asun.cobalt.com>
+
+       * etc/afpd/directory.c (setdirparams): don't error if we can't set
+       the desktop owner/permisssions.
+
+1999-11-04  a sun  <asun@asun.cobaltnet.com>
+
+       * etc/afpd/fork.c (afp_openfork): had the ordering wrong on an
+       openfork. 
+
+1999-11-02  a sun  <asun@asun.cobaltnet.com>
+
+       * etc/afpd/afp_dsi.c (afp_over_dsi): flush data for unknown dsi
+       commands. 
+
+1999-10-28  a sun  <asun@asun.cobaltnet.com>
+
+       * etc/uams/*.c: return FPError_PARAM if the user is unknown. 
+
+1999-10-27  a sun  <asun@asun.cobaltnet.com>
+
+       * etc/afpd/fork.c (afp_read): if sendfile isn't supported, use the
+       old looping method.
+
+1999-10-25  a sun  <asun@asun.cobaltnet.com>
+
+       * libatalk/nbp/nbp_unrgstr.c (nbp_unrgstr): fix nbp unregisters.
+
+1999-10-21  a sun  <asun@asun.cobaltnet.com>
+
+       * etc/afpd/Makefile (install): moved install of afpd earlier per
+       suggestion by steven michaud.
+
+1999-10-05  a sun  <asun@asun.cobaltnet.com>
+
+       * etc/uams/uams_randnum.c (afppasswd): for ~/.passwd's, turn
+       ourselves into the user so that nfs is happy.
+
+1999-09-19  a sun  <asun@adrian5>
+
+       * libatalk/netddp/netddp_open.c, nbp/*.c: only use the bcast stuff
+       if it's on an os x server machine.
+
+1999-09-15  a sun  <asun@adrian5>
+
+       * libatalk/nbp/nbp_unrgstr.c,nbp_lkup.c,nbp_rgstr.c: os x server
+       wants ATADDR_BCAST. that probably means that i need to do
+       multihoming appletalk a little differently. bleah.
+
+1999-09-09    <asun@asun.cobaltnet.com>
+
+       * etc/afpd/directory.c (getdirparams), libatalk/adouble/ad_open.c
+       (ad_open): mondo lameness. i forgot that directory lookups can be
+       done with "." as the directory name. that was auto-hiding
+       them. bleah. i also figured out which bit was the invisible bit
+       for finderinfo information.
+
+1999-09-06  Adrian Sun  <asun@glazed.cobaltnet.com>
+
+       * etc/afpd/desktop.c (mtoupath): fixed a bug in codepage support
+       that accidentally crept in.
+
+1999-08-31  Adrian Sun  <asun@glazed.cobaltnet.com>
+
+       * etc/afpd/quota.c (getfsquota): use group quotas in quota
+       calculations if the user belongs to a single group. just use the
+       user quotas if the user belongs to multiple groups.
+
+       * etc/afpd/volume.c (getvolspace): added an options:limitsize to
+       restrict the available space to 2GB. this is for macs running
+       older versions of the operating system with newer versions of the
+       appleshare client. weird huh?
+
+       * etc/afpd/quota.c (uquota_getvolspace): bleah. 64-bit shifts
+       don't get promoted in the same way as arithmetic operations. added
+       some more casts to deal with that issue.
+
+1999-08-24  Adrian Sun  <asun@glazed.cobaltnet.com>
+
+       * man/man?/Makefile: don't re-build .tmp files if they already
+       exist. this gets the install phase to work correctly.
+
+1999-08-13  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/directory.c, file.c, filedir.c: illegal characters get
+       AFPERR_PARAM. also, reject names with /'s in them if the nohex
+       option is used.
+
+1999-08-12  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/filedir.c,file.c,directory.c: changed error for
+       illegal filenames to AFPERR_EXIST.
+
+1999-08-11  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/desktop.h (validupath): if usedots is set, .Apple* and
+       .Parent files are no longer valid file names.
+
+       * etc/afpd/volume.c (volset): added usedots and nohex as
+       options. usedots stops :hex translation of . files while nohex
+       stops :hex translation of everything but . files. in addition,
+       . files created on the unix side are by default hidden.
+
+       * libatalk/adouble/ad_open.c: initialize more bits.
+
+1999-08-10  a sun  <asun@hecate.darksunrising.blah>
+
+       * distrib/initscripts/rc.atalk.redhat (WORKSTATION): use the
+       actual name for nbp registration rather than ATALK_NAME.
+
+       * sys/solaris/Makefile (kernel): make sure osdefs and machinedefs
+       get used when building the kernel module.
+
+       * sys/solaris: changed strings.h to string.h
+
+1999-08-08  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/volume.c (readvolfile): changed volume options into an
+       array of structs to ease maintenance.
+
+1999-08-05  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/status.c (status_init): change the default icon
+       depending upon whether or not it's an ASIP or an AppleTalk
+       connection. 
+
+1999-08-04  Adrian Sun  <asun@glazed.cobaltnet.com>
+
+       * etc/atalkd/main.c (setaddr): made a failure with setaddr a 
+       little more informative.
+
+1999-08-03  Adrian Sun  <asun@glazed.cobaltnet.com>
+
+       * yippee. someone figured what was happening with the installation
+       of the man pages. i got rid of a duplicate entry.
+
+1999-08-02  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/volume.c (readvolfile): added a per-file way of setting
+       default options. it keys in on a :DEFAULT: label.
+
+1999-07-30  a sun  <asun@hecate.darksunrising.blah>
+
+       * moved rc.atalk.* scripts to distrib/initscripts.
+
+1999-07-27  a sun  <asun@hecate.darksunrising.blah>
+
+       * contrib/printing: added patch from job@uchicago.edu
+
+       * etc/afpd/file.c: forgot to initialize struct ad in
+       some places.
+
+       * etc/afpd/nls/makecode.c: added an empty mapping.
+
+       * etc/psf/Makefile (install): well cp -d didn't work either. just
+       use tar.
+
+1999-07-26  a sun  <asun@hecate.darksunrising.blah>
+
+       * sys/solaris/tpi.c (tpi_attach): changed DDI_NT_NET to DDI_PSEUDO
+       (from denny@geekworld.com).
+
+       * distrib/rpm/netatalk-asun.spec (Summary): incorporated new spec
+       and patch files from inoue.
+
+       * sys/linux/Makefile (install-sysv): fixed up a bit.
+
+       * etc/psf/Makefile (install): use cp -d instead of cp -a to make
+       *bsd happier.
+
+       * etc/afpd/afp_options.c (afp_options_parseline): reversed meaning
+       of -icon. now it means to use the yucky bitmap instead of the
+       apple icon.
+
+       * bin/afppasswd/Makefile (all): add -Iinclude/openssl for
+       afppasswd as well. 
+
+1999-07-18  a sun  <asun@hecate.darksunrising.blah>
+
+       * create links/mangle files in the compile rather than the install
+       phase so that rpm will be happier.
+
+1999-07-17  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/file.c (afp_createfile), directory (afp_createdir),
+         filedir.c (afp_rename, afp_moveandrename): don't allow the
+         creation/renaming of names with certain characters if mswindows
+         compatibility is enabled.
+
+1999-07-16  a sun  <asun@hecate.darksunrising.blah>
+
+       * rc.atalk.redhat: incorporated chkconfig from inoue.
+
+1999-07-15  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/atalkd/config.c (getifconf): wrap check against
+       IFF_MULTICAST behind an #ifdef IFF_MULTICAST.
+
+       * sys/netbsd/Makefile (LDSHAREDFLAGS): key in on machine type.
+
+1999-07-11  a sun  <asun@hecate.darksunrising.blah>
+
+       * contrib/ICDumpSuffixMap: added internet config perl script from
+       inoue. 
+
+       * contrib/printing: added contributed solaris printing scripts
+       from job@uchicago.edu.
+
+1999-07-10  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/atalkd/interface.h, rtmp.h: prototyped functions.
+
+       * etc/atalkd/zip.c: converted bcopy's to memcpy's.
+
+       * etc/atalkd/nbp.c,rtmp.c: added checks for the interface for
+       dontroute cases.
+
+       * etc/atalkd/main.c: converted bzero/bcopy to memset/memcpy.
+
+1999-07-08  a sun  <asun@hecate.darksunrising.blah>
+
+       * libatalk/nbp/nbp_rgstr.c (nbp_rgstr): return EADDRINUSE if the
+       address already exists.
+
+1999-07-06  a sun  <asun@hecate.darksunrising.blah>
+
+       * rc.atalk.redhat: changed netatalk.config to netatalk.conf
+
+1999-07-05  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/atalkd/nbp.c (nbp_packet): add interface to nbp struct. this
+       is so that we can filter by interface in the future. however, it
+       doesn't seem to work that well right now. bleah. 
+
+       * etc/atalkd/main.c: fixed up dontroute option so that it doesn't
+       screw up atalkd.conf. also, we need to do a bootaddr if dontroute
+       is set. 
+
+       * libatalk/atp,nbp,netddp; bin/aecho,nbp,getzones,pap;
+       etc/papd,afpd: accept -A <ddp address> as an option so that you
+       can specify the address to be used on a multihomed server. for
+       papd, you use the 'pa' option in papd.conf.
+
+1999-07-04  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/atalkd/config.c (parseline): initialize parseline properly
+       so that we don't get extraneous junk.
+
+       * etc/afpd/afp_options.c (afp_options_parseline): do
+       gethostbyaddr/gethostbyname's for -ipaddr and -fqdn.
+
+       * etc/atalkd/config.c (getifconf/readconf): check to see if the
+       supported device can support appletalk addresses. either continue
+       or exit depending upon whether or not it's auto-configed.
+
+1999-07-03  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/afp_options.c (afp_options_parse): -I (-[no]icon) will
+       toggle the volume icon so that it uses the apple icon instead.
+
+       * etc/afpd/config.c (AFPConfigInit): added more logic for the
+       -proxy option. here are the rules: -proxy will always try to
+       create a DDP server instance. by default, the proxy server will
+       still allow you to login with an appletalk connection. to prevent
+       that, just set the uamlist to an empty string.
+
+1999-07-02  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/status.c (status_netaddress): added support for fqdn
+       (not available in the appleshare client yet).
+
+1999-07-01  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/config.c (DSIConfigInit): application code for proxy
+       setup. it's the -proxy option.
+
+       * libatalk/dsi/dsi_init/tcp.c (dsi_init/dsi_tcp_init): added
+       support for proxy setup.
+
+1999-06-30  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/filedir.c (afp_rename): fixed up some error
+       codes. quark express should be happier.
+
+       * etc/afpd/uam.c (uam_afpserver_option): added
+       UAM_OPTION_HOSTNAME. use this to set PAM_RHOST. i just got a
+       report that setting that fixes pam on solaris machines.
+
+1999-06-28  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/ofork.c (of_alloc): report out of forks in syslog..
+
+       * etc/afpd/enumerate.c (afp_enumerate): close an opendir leak. 
+
+       * include/atalk/{dsi,asp}.h: make cmdlen and datalen ints.
+
+       * etc/afpd/fork.c (afp_write): fixed up error condition.
+
+1999-06-26  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/uams/Makefile (install): changed install location of uams.
+
+       * sys/linux/Makefile (install-sysv): always install redhat
+       script. netatalk.config script only gets installed if it's not
+       there already.
+
+1999-06-23  a sun  <asun@hecate.darksunrising.blah>
+
+       * rc.atalk.redhat: merged in redhat contrib rpm rc.atalk script.
+
+       * etc/afpd/afp_options.c (afp_options_init): changed default
+       maxusers to 20.
+
+1999-06-22  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/config.c (DSIConfigInit): truncate options->server to
+       just the server name here.
+
+       * etc/afpd/volume.c (volxlate): made $s return something
+       meaningful no matter what.
+
+       * libatalk/adouble/ad_sendfile.c (ad_readfile): freebsd sendfile
+       wants an off_t.
+
+1999-06-20  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/volume.c (volxlate): added variable substitution. if it
+       doesn't understand the variable, it just spits it back out.
+       
+       (creatvol): display truncated volume name if it's too long.
+
+       * sys/{generic,solaris}/Makefile: added NO_CRYPTLIB option to deal
+       with oses that have -lcrypt but shouldn't use it.
+
+1999-06-11  a sun  <asun@hecate.darksunrising.blah>
+
+       * include/atalk/afp.h: added comments to FPErrors.
+
+       * etc/afpd/enumerate.c (afp_enumerate): make FPEnumerate do some
+       more error checking.
+
+       * include/atalk/util.h: server_lock() returns pid_t. 
+
+1999-06-10  a sun  <asun@hecate.darksunrising.blah>
+
+       * README.ASUN: added location for both ssleay and openssl.
+
+       * etc/uams: moved install to LIBDIR/uams. "uams_*" now means "uam
+       server." in the future, there will be "uamc_*." changed the shared
+       library names to match.
+
+       * include/atalk/atp.h,nbp.h: forgot to include <sys/cdefs.h>
+
+       * etc/uams/Makefile: openssl-0.9.3c uses <openssl/*.h> so add that
+       to the include path.
+
+       * sys/{solaris,ultrix}/Makefile: just use -I../sys/generic instead
+       of doing a link.
+
+       * include/atalk/uam.h, etc/uams/uam_*.c, etc/afpd/uam.c: added uam
+       type field. do type check.
+
+       * etc/uams/uam_*pam.c: added a couple more error codes. 
+
+1999-06-08  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/nls/Makefile (codepage.h): make sure that a link to
+       codepage.h gets made.
+
+       * libatalk/*/Makefile: make sure that the profiled directory gets
+       created.
+       
+       * etc/afpd/directory.c (afp_mapname): removed an extraneous line
+       that was causing mapname to fail.
+
+1999-06-07  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/atalkd/main.c (main): added a note to check the syslog if
+       atalkd can't be setup.
+
+       * sys/linux/Makefile: added -DNEED_QUOTACTL_WRAPPER to the list of
+       auto-detected #defines.
+
+1999-06-06  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/fork.c (afp_write): argh. i moved things around a
+       little too much and ended up with an uninitialized eid. strangely,
+       the compiler didn't complain. simplified bits a little as
+       well. also, FPWrite was returning the wrong error messages. on
+       64-bit filesystems, the offset can wraparound. so, report a disk
+       full error if that's going to happen. egcs-19990602 gets one
+       memcpy right and another wrong on my udb. bleah.
+       
+       (afp_read): fixed the error messages here as well.
+
+1999-06-05  a sun  <asun@hecate.darksunrising.blah>
+
+       * Makefile, sys/generic, sys/{ultrix,solaris}/Makefile: create
+       some links on the fly if they're missing.
+
+       * etc/afpd/directory.c (copydir): fixed a leaking opendir and
+       re-arranged a little.
+
+1999-06-04  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd: prototyped everything here and moved the FP functions
+       into include files.
+
+       * libatalk/util/bprint.c: moved all of the bprints to here.
+
+       * libatalk/asp, include/atalk/asp.h: prototyped asp functions.
+
+       * include/atalk/atp.h, libatalk/atp: prototyped atp functions. 
+
+       * libatalk/nbp, include/atalk/nbp.h: added prototypes for nbp
+       functions. 
+
+       * bin/afppasswd/Makefile (afppasswd): fixed a misspelling in the
+       install phase.
+
+       * bin/afppasswd/afppasswd.c: added -a option so that root can add
+       new users. turned all of the options into bits. added newlines to
+        each entry.
+
+1999-06-03  a sun  <asun@hecate.darksunrising.blah>
+
+       * sys/freebsd/Makefile: turn on sendfile support if running on a
+       FreeBSD 3+ machine.
+
+1999-06-02  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/uams/uam_dhx_pam.c: fixed memory freeing part of pam
+       conversation function.
+
+       * sys/*/Makefile: check at make time to see if -lrpcsvc and
+       -lcrypt should be included in the appropriate places. 
+
+1999-05-28  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/file.c (deletefile): added more error checking here as
+       well. 
+
+       * etc/afpd/directory.c (renamedir): added a couple a few more
+       error bits. 
+
+       * sys/sunos/Makefile: sunos should really work now. 
+
+1999-05-27  a sun  <asun@hecate.darksunrising.blah>
+
+       * include/atalk/afp.h: added in a couple new error codes (one
+       deals with password setting policy, the other with maximum logins
+       by any user).
+
+       * etc/afpd/fork.c (afp_openfork): try to re-open files on
+       read-only volumes as read-only.
+
+1999-05-26  a sun  <asun@hecate.darksunrising.blah>
+
+       * sys/solaris/Makefile: fixed a few bobbles here. solaris uses
+       uname -p. other oses seem to use uname -m for the same information.
+
+       * etc/uams/uam_pam.c (pam_changepw): added check for same
+       password. 
+
+       * etc/uams/uam_randnum.c (randnum_changepw): added in cracklib and
+       same password checks.
+
+       * sys/osx/Makefile: moved the os x server stuff into its own build
+       directory. 
+
+       * sys/linux/Makefile, sys/solaris/Makefile: key in on OSVERSION
+       and MACHINETYPE for some stuff.  
+
+1999-05-25  a sun  <asun@hecate.darksunrising.blah>
+
+       * sys/sunos/Makefile: various bits to make stuff work with sunos
+       again. 
+
+1999-05-25  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/file.c (copyfile): only copy the header file if newname
+       is specified. 
+
+       * etc/afpd/directory.c (copydir): make sure to balk if the
+       directory already exists. in addition, make sure to preserve the
+       timestamps. 
+
+1999-05-24  a sun  <asun@hecate.darksunrising.blah>
+
+       * bin/afppasswd/afppasswd.c: global password updating utility for
+       the randnum authentication method. 
+
+1999-05-22  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/uams/uam_randnum.c (afppasswd): added in global password
+       file for the randnum authentication method. it looks for a .key
+       file as well to handle encryption.
+
+       * etc/afpd/afp_options.c (afp_options_parseline): added
+       -passwdfile as an option so that you can specify a global randnum
+       password file if desired.
+
+       * etc/afpd/volume.c (readvolfile): we now have rwlist and rolist
+       as an AppleVolumes.* option. if the user is in the rolist, the
+       volume gets set as readonly. if there's a rwlist, and the user
+       isn't in it, the volume also gets set as readonly.
+
+1999-05-21  a sun  <asun@hecate.darksunrising.blah>
+
+       * libatalk/adouble/ad_lock.c (ad_fcntl_lock): plug a leak if we
+       can't allocate the reference counting variable.
+
+       * etc/uams/uam_*.c: make sure that uam_setup returns an error
+       code. 
+
+1999-05-19  a sun  <asun@hecate.darksunrising.blah>
+
+       * include/atalk/paths.h (_PATH_LOCKDIR): added os x server's
+       /var/run as the lock file directory.
+
+       * etc/afpd/fork.c (afp_write): kanehara@tpk.toppan.co.jp reported
+       a problem with FPWrite getting a request count of 0. that's
+       fixed. 
+
+       * etc/afpd/Makefile: bleah. for some reason, pam doesn't like to
+       load itself from a shared library. i've compensated by linking it
+       into afpd again.
+
+       * etc/uams/uam_dhx_passwd.c: okay. DHX now works. something's
+       still screwy with the dhx_pam stuff though.
+
+1999-05-18  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/uam.c (uam_getname): i forgot that getname modified the
+       username to fit what's in pw->pw_name if necessary.
+
+1999-05-16  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/uams/uam_dhx_passwd/pam.c: almost ready versions of the DHX
+       authentication method. i'm still missing a little info to get it
+       all right.
+
+       * bin/megatron/nad.c (nad_header_read): if there isn't a mac name,
+        create it from the unix name. 
+
+       * bin/megatron/megatron.c (megatron): oops. need to turn fdCreator
+       and fdType into strings.
+
+1999-05-16  a sun  <asun@pelvetia>
+
+       * etc/afpd/uam.c (uam_afpserver_option): changed the interface a
+       little. now, you pass in an int * if you want to either get/set
+       the size of the option. added in UAM_OPTION_RANDNUM for generic 
+        (4-byte granularity) random number generation.
+
+       * etc/afpd/switch.c: added afp_logout to preauth_switch.
+
+1999-05-15  a sun  <asun@hecate.darksunrising.blah>
+
+       * bin/megatron/macbin.c (bin_open): make error message for
+       macbinary files more informative.
+       
+       (test_header): added more macbinary tests. it now has a workaround
+       for apple's incorrectly generated macbinary files.
+
+1999-05-14  a sun  <asun@hecate.darksunrising.blah>
+
+       * sys/solaris/Makefile: added shared library generation bits.
+
+       * etc/uams: moved server-side uams here. 
+
+       * include/netatalk/endian.h: fixed some solaris bits. 
+
+       * etc/afpd/config.c (configfree): don't do an asp_close. instead,
+       do an atp_close and free the asp object. oh yeah, as afpd needs
+        to export symbols to its modules, make sure you don't do anything
+        more exciting than strip --strip-debug with it.
+
+1999-05-12  a sun  <asun@hecate.darksunrising.blah>
+
+       * various places that use sigaction: zero out struct sigaction so
+        that we don't send something confusing. also make sure that we
+        don't set a timer unless we already have a sigaction set.
+
+       * etc/afpd/fork.c (afp_openfork): don't error on trying to open an
+       empty resource fork read-only. also, added back in the bit of code
+       that prevented locks from being attempted on non-existent resource
+       forks.
+
+       * etc/afpd/afp_options.c (getoption): added a uamlist commandline
+       option (-U list). 
+
+       * libatalk/netddp/netddp_open.c: don't bind if nothing was passed
+       in. 
+
+       * libatalk/nbp/nbp_unrgstr.c (nbp_unrgstr): oops. forgot to
+       convert this over to use by the netddp interface.
+
+1999-05-12  a sun  <asun@pelvetia>
+
+       * etc/afpd/uam.c: os x server's runtime library loader is
+       braindead. as a result, i've switched to using an exported struct
+       with the uam's name.
+
+       * bin/aecho,getzones: changed these to use the netddp interface.
+
+       * libatalk/nbp/nbp_rgstr.c,unrgstr.c: fixed more leaky bits.
+
+       * libatalk/netddp: abstracted the ddp interface to netddp. besides
+       the prior socket-driven interface, there's now an os x server
+       interface. so, instead of calling socket/sendto/recvfrom, you call
+       netddp_open/netddp_sendto/netddp_recvfrom.
+
+1999-05-11  a sun  <asun@pelvetia>
+
+       * libatalk/nbp/nbp_lkup.c: oh my. nbp_lookup was fd leaky if there
+       was a problem.
+
+       * etc/atalkd/main.c (main): make sure that if -dontroute is
+       selected for all but one interface, that interface also gets
+       -dontroute set.
+
+1999-05-10  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/auth.c: re-wrote to deal with plug-in uams. it's much
+       smaller than it used to be.
+
+1999-05-09  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/uams/uam_guest.c, uam_pam.c, uam_passwd.c,
+       uam_randnum.c: uam modules. these should probably be moved out of
+       afpd (and into something like etc/uam_server) when the printing
+       stuff gets uam support.
+
+1999-05-08  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/uam.c: interface to user authentication modules.
+       it should eventually be moved to libatalk, but that's not
+       necessary until the printing uam stuff is done. everything is from
+       the server-side perspective, but that's only because there aren't
+       any client-side uses right now.
+
+       * libatalk/util/module.c: generic interface to run-time library
+       loading functions. right now, the dlfcn family and os x server's
+       NS-style way of doing things are the ones understood. in addition,
+       there's a DLSYM_PREPEND_UNDERSCORE for those systems that need it.
+
+       * libatalk/asp/asp_write.c (asp_wrtcont): log both the read and
+       write part of write continuations.
+
+1999-05-07  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/atalkd: added the ability to turn off routing for particular
+       interfaces. specify -dontroute for each interface that you don't
+       want to route.
+
+1999-05-06  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/auth.c: got rid of global clrtxtname and switched to
+       using obj->username.
+
+1999-05-04  a sun  <asun@hecate.darksunrising.blah>
+
+       * libatalk/dsi/dsi_write.c (dsi_write): dsi_write could loop
+       forever if there's a problem while it's being used. that's fixed.
+
+1999-05-01  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/directory.c (renamedir,copydir,deletedir): added bits
+       so that renaming a directory works across filesystems.
+
+1999-04-27  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/fork.c (getforkparams): report mtime if it's > than
+       what's stored in the header file.
+
+       * config/afpd.conf: incorporated a patch by Stefan Bethke to make
+       afpd.conf more understandable.
+
+       * sys/solaris/if.c: many of the firstnet/lastnet bits weren't
+       endian converted. that's fixed.
+
+       * libatalk/adouble/ad_lock.c (adf_find(x)lock): F_RD/WRLCK aren't
+       necessarily ORable, so use ADLOCK_RD/WR instead.
+       
+       (ad_fcntl_unlock): erk. fixed a typo that had the resource fork
+       unlock accidentally getting the data fork locks.
+
+1999-04-24  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/fork.c (afp_openfork): always try to create a resource
+       fork if asked.
+
+1999-04-21  a sun  <asun@hecate.darksunrising.blah>
+
+       * libatalk/adouble/ad_open.c, ad_read.c/ad_write.c, ad_flush.c:
+       turned the mmapped header stuff into and #ifdef
+       USE_MMAPPED_HEADERS option.
+
+       * libatalk/adouble/ad_open.c (ad_header_read): darn. i forgot that
+       the hfs fs doesn't currently have mmappable header files. rather
+       than implement that, i just reverted back to a modified version
+       of the old way of reading headers.
+
+1999-04-15  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/fork.c (afp_bytelock): byte locks become read locks on
+       read-only files.
+       
+       (afp_openfork): deal with read-only data forks that don't have
+       corresponding .AppleDouble files. we can't really do anything with
+       deny locks in this case. just make sure that read locks are set.
+
+       * etc/afpd/file.c (getfilparams): oops. got the parentheses wrong
+       around FILPBIT_FINFO. 
+
+       * etc/afpd/fork.c (afp_read): as we share open files now, check
+       for fork type against of_flags instead of just checking to see if
+       the file is open. this fixes a bug that caused resource forks to
+       get filled with data fork information.
+
+1999-04-09  a sun  <asun@porifera.zoology.washington.edu>
+
+       * sys/generic/Makefile: AFP/tcp now compiles on irix with quota
+       support.
+
+1999-04-09  a sun  <asun@mead1.u.washington.edu>
+
+       * sys/generic/Makefile: AFP/tcp now compiles on aix with quota
+       support.
+
+1999-04-09  a sun  <asun@saul6.u.washington.edu>
+
+       * sys/generic/Makefile: AFP/tcp part now compiles on digital unix
+       with quota support enabled.
+
+1999-04-08  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/volume.c, fork.c, file.c, directory.c, filedir.c,
+       config/AppleVolumes.default: added read-only volume option.
+
+       * etc/afpd/quota.c (uquota_getvolspace): modified for os x
+       server. 
+
+1999-04-03  a sun  <asun@hecate.darksunrising.blah>
+
+       * bin/megatron/macbin.c (bin_write): only pad if we need to do so 
+       (from <jk@espy.org>).   
+       (bin_header_write/read): fixed up screwed up file date
+       generation/reading with macbinary files. 
+
+       * bin/megatron: changed all of the bcopy/bzero/bcmp's to 
+       memcpy/memset/memcmp's. added macbinary III support.
+
+       * bin/megatron/macbin.c (bin_open): added --stdout as an option so
+       that we can stream macbinary file creation to stdout.
+
+       * bin/megatron/megatron.c: incorporated information patch (--header
+          and --macheader) from <fmorton@base2inc.com>.
+
+
+1999-04-02  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd: whee! there are no more bcopy/bcmp's in this
+       directory. 
+
+       * libatalk: changed the bcopy/bzero's to memcpy/memset's. added in
+       dummy ints for some of the files that can get compiled to empty
+       objects. check for the type of msync() available as well.
+
+1999-03-31  a sun  <asun@hecate.darksunrising.blah>
+
+       * INSTALL/README.GENERIC: added information for a generic
+       architecture. It includes the information needed to get netatalk
+       to compile on a random unix platform.
+
+       * etc/afpd/quota.c: moved the quota stuff here so that we can
+       #ifdef it out on a machine without quota support.
+
+1999-03-30  a sun  <asun@hecate.darksunrising.blah>
+
+       * libatalk/adouble/ad_lock.c: reference count the locked ranges as
+       well. this prevents multiple read locks on the same byte range
+       from disappearing if one user disappears.
+       
+       (ad_fcntl_lock): here are the current rules for file
+       synchronization:
+          1) if there's a appledouble header, we use the beginning
+             of that for both data and resource forks.
+          2) if there isn't, we use the end of the data fork (or past the
+             end on 64-bit machines)
+       
+1999-03-28  a sun  <asun@hecate.darksunrising.blah>
+
+       * libatalk/adouble/ad_open.c (ad_refresh): okay. mmapping the
+       appledouble entry parts is done. 
+
+       * libatalk/cnid/cnid_add.c (cnid_add): prevent anyone from adding
+       in an illegal cnid.
+
+1999-03-27  a sun  <asun@hecate.darksunrising.blah>
+
+       * libatalk/adouble/ad_open.c (ad_refresh): started making the
+       appledouble header parsing more generic so that we can read in
+       arbitrary appledouble header files. i just mmap the parts that we
+       need. 
+
+1999-03-22  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/file.c (afp_copyfile): return the correct error
+       response on a failed copy. also, error if the file is already open
+       to prevent problems with locks. we really need to ad_lock
+        this during the copy
+
+1999-03-21  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/volume.c (readvolfile): switched volume options to
+       using ':' as a delimiter as that's one of the characters that's
+       not allowed as part of a mac filename.
+       (volset): changed access to allow/deny
+
+       * etc/afpd/auth.c (noauth_login): make sure that the username gets
+       set. 
+
+1999-03-17  a sun  <asun@hecate.darksunrising.blah>
+
+       * NOTE to myself: jeremy allison said that samba uses refcounts to
+       prevent close() from killing all the byte locks. so, i've started
+       converting afpd to using refcounting as well. luckily, we already
+       have of_findname, so we know when files are open. in cases where
+       files are already open, this will replace an ad_open with a lookup
+       into a hash table.
+
+       * etc/afpd/directory.c (getdirparams/getfilparams): check for
+       NULL names when getting directory/file names.
+
+       * etc/afpd/directory.{c,h} (DIRDID_ROOT/DIRDID_ROOT_PARENT): make
+       sure these are always in network byte order.
+
+1999-03-15  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/fork.c (afp_openfork): okay, fixed the file
+       synchronization bits. now, we use two bytes to do the read/write
+       stuff. when access is needed, a read lock is attempted. if a deny
+       lock is needed, a write lock is attempted. we even handle the
+       access None mode now by saving the access modes.
+
+       * etc/afpd/fork.h (AFPFORK_ACCMASK): started adding bits so that
+       we can obey all of the file synchronization rules.
+
+       * etc/afpd/fork.c (afp_bytelock): got the meaning of the clearbit
+       reversed. with helios lantest's lock/unlock 4000 times test, it
+       looks like i get <1 second overhead on my machine when using byte
+       locks. NOTE: this will get a little worse when serialization gets
+       added. in addition, 0x80000000 only works for 64-bit machines. i
+       reserve the last allowable bit for 32-bit machines. 
+
+       actually, both 64-bit machines and 32-bit machines use 0x7FFFFFFF
+       now as i'm able to trigger a kernel oops in linux with the 64-bit
+       code. 
+       
+       (afp_read/afp_write): make sure to use the same offset when doing
+       a tmplock.
+
+1999-03-14  a sun  <asun@hecate.darksunrising.blah>
+
+       * libatalk/adouble/ad_lock.c: i went and implemented a bunch of
+       stuff to get byte locks to work correctly (except for the
+       serialization) only to discover that files that use byte locks
+       also request a deny write mode. luckily, byte locks only cover up
+       to 0x7FFFFFFF. so, i'll just use 0x80000000 for the
+       synchronization locks.
+
+1999-03-08  a sun  <asun@hecate.darksunrising.blah>
+
+       * sys/{*bsd,ultrix,solaris,linux}/Makefile (depend): surround 
+       DEPS with double quotes so that multiple defines work.
+
+1999-03-06  a sun  <asun@hecate.darksunrising.blah>
+
+       * libatalk/adouble/ad_write.c, ad_read.c: make off off_t in size. 
+
+       * libatalk/adouble/ad_flush.c (adf_fcntl_relock), ad_lock.c
+       (adf_fcntl_locked): okay. fcntl locks now check against multiple
+       programs on the same machine opening the same file. current
+       problems with the mechanism that i don't want to fix unless
+       necessary: 
+               1) there's a race during the relock phase. serialization
+                  would solve that.
+               2) it assumes that each fd only locks a single contiguous
+                  range at a time. keeping a list of locked ranges would
+                  solve that. 
+       
+       also, i changed the default to using fcntl locks. if the above two
+       are really necessary, i'll probably switch to something a little
+       more featureful like the berkeley db's lock manager.
+
+       (note to myself: stuff new from asun2.1.3 from 1999-03-03)
+       
+1999-03-05  a sun  <asun@hecate.darksunrising.blah>
+
+       * libatalk/adouble/ad_lock.c: got rid of the endflag checks to
+       reduce system calls a little. 
+
+       * etc/afpd/auth.c (getname): do a case-insensitive compare on the
+       login name as well.
+
+       * sys/solaris/Makefile: added 64-bit solaris patch from
+       <jason@pattosoft.com.au>.
+
+1999-03-03  a sun  <asun@hecate.darksunrising.blah>
+
+       * include/netatalk/endian.h: make solaris 2.5 complain less.
+
+       * bin/adv1tov2/adv1tov2.c, libatalk/adouble/ad_open.c (ad_v1tov2):
+       fixed a couple problems with the adv1tov2 stuff.
+
+1999-02-26  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/volume.c (afp_openvol): erk. the volume password gets
+       aligned along an even boundary.
+
+1999-02-23  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/volume.c (readvolfile): added volume password support. 
+
+1999-02-14  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/atalkd/multicast.c (addmulti): added FreeBSD's data-link
+       layer multicast setting bits.
+
+       * libatalk/adouble/ad_open.c (ad_v1tov2): make sure to stick in
+       prodos field info when converting.
+
+       * rc.atalk.redhat: added pidof checking in case the machine
+       crashes. also added rc.atalk.wrapper to the redhat rc script
+       installation. 
+
+1999-02-07  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/fork.c (afp_setforkparams): make sure to do better
+       error detection here and more fully report error conditions.
+       
+       (flushfork): make sure to flush the header if necessary (rfork
+       length changed or modification date needs to be set).
+
+       (afp_write): ugh. this wasn't returning the right values for the
+       last byte written if the endflag was set. in addition, it was
+       setting the modification date. that should be left to FPCloseFork
+       and FPFlush(Fork). this fixes a problem that shows up with
+       QuarkXPress. 
+
+       NOTE: as of now, changes to the rfork info are the only things
+       that aren't flushed immediately when altered. 
+
+       * etc/afpd/fork.c (get/setforkparams), ofork.c: what ugliness. we
+       need to report bitmap errors if we try to fiddle with the wrong
+       fork. i added an of_flags field to keep things sorted.
+
+       * libatalk/adouble/ad_open.c (ad_v1tov2): oops. in all of the
+       movement, i forgot to make sure that the pre-asun2.2.0 features
+       still compile.
+
+1999-02-06  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/filedir.c (afp_moveandrename): make sure to save the
+       old name even when it's a directory.
+
+       * globals.h: added oldtmp and newtmp to AFPObj to reduce the
+       number of buffers used. use these when needed in afp_* calls.
+
+       * etc/afpd/directory.c (deletecurdir): delete stray .AppleDouble
+       files when deleting a directory.
+
+1999-02-05  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/file.c (afp_createfile): fixed a hard create error
+       check bug. 
+
+       * fixed up a few bobbles in the netatalk-990130 merge.
+
+       * the noadouble option should be pretty much implemented. here's
+       how it goes:
+               when a directory is created, the corresponding
+               .AppleDouble directory is not.
+               
+               comments, dates, and other file attributes will get
+               silently ignored and not result in the creation of a new
+               .AppleDouble directory.
+
+               however, if anything possessing a resource fork is copied
+               into that directory, the corresponding .AppleDouble
+               directory will be created. once this happens, any
+               other file in the directory can acquire an AppleDouble
+               header file in the future.
+               
+1999-02-03  a sun  <asun@hecate.darksunrising.blah>
+
+       * merged in the rest of netatalk-990130.
+
+       * sys/solaris: merged in netatalk-990130 changes.
+
+       * etc/afpd/file.c,fork.c,desktop.c libatalk/adouble/ad_sendfile.c:
+       tested and fixed the sendfile bits on linux. it looks like linux
+       doesn't permit socket -> file sendfiles yet.
+
+       * etc/afpd/fork.c (afp_read): we can't stream FPRead's with
+       newline character checking on.
+
+1999-02-02  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/fork.c (afp_flush), ofork.c (of_flush): FPFlush
+       operates on a per-volume basis.
+
+1999-01-31  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/file.c (copyfile): sendfile bits added here also.
+
+       * etc/afpd/desktop.c (afp_geticon): added sendfile bits here as
+       well. 
+
+       * libatalk/adouble/ad_sendfile.c (ad_writefile/ad_readfile):
+       implemented sendfile bits. currently, there's support for linux
+       and freebsd. unfortunately, freebsd's implementation doesn't allow
+       file->file or socket->file copies. bleah.
+
+1999-01-30  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/file.c (setfilparams), directory (setdirparams),
+       volume.c (volset): added in the beginnings of a NOADOUBLE option
+       for those that don't want AppleDouble headers to be created by
+       default. it doesn't really work that well right now.
+
+1999-01-29  a sun  <asun@hecate.darksunrising.blah>
+
+       * libatalk/adouble/ad_open.c (ad_v1tov2): separated v1tov2 bits
+       from ad_refresh. made broken v1 header checking the default. fixed
+       broken v1 date checking. now, it just checks to see if the v1
+       MDATE is > than st_mtime by 5 years.
+
+       * etc/afpd/directory.c: make date setting alter directory dates as
+       well. 
+
+1999-01-24  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/afp_dsi.c (alarm_handler,afp_over_dsi): added a
+       CHILD_RUNNING flag to prevent afpd from timing out on long copies.
+
+1999-01-21  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/volume.c (afp_openvol), libatalk/cnid/cnid_nextid.c:
+       shift the beginning of the fake did's if necessary.
+
+       * libatalk/adouble/ad_open.c (ad_refresh): fixed screwed-up date
+       detection code.
+
+       * libatalk/cnid/cnid_add.c,cnid_open.c,cnid_close.c: made some
+       changes so that the CNIDs will still work even when the .AppleDB
+       directory is read-only. if you're still allowed to create files on
+       these volumes, you'll get a temporary id for those.
+
+1999-01-20  a sun  <asun@hecate.darksunrising.blah>
+
+       * libatalk/cnid/{cnid_close.c,cnid_open.c}: added bits so that log
+       files get cleared out on cnid_close() if it's the last user for a
+       volume.
+
+1999-01-18  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/volume.c (afp_setvolparams): added FPSetVolParms. this
+       allows us to set the backup date on the server.
+
+       * etc/afpd/file.c (afp_exchangefiles): whee! we now have
+       FPExchangeFiles. we also have FPDeleteID, so that only leaves us
+       with FPCatSearch to do.
+
+1999-01-16  a sun  <asun@hecate.darksunrising.blah>
+
+       * sys/solaris/ddp.c (ddp_rput): added a couple htons()'s for the
+       net addresses.
+
+1999-01-11  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/volume.c (volset, afp_openvol): you can now specify a
+       dbpath= in AppleVolumes.* for the CNID database.
+
+       * libatalk/adouble/ad_open.c (ad_refresh): if we build in an
+       appledouble v1 environment, turn on v1compat by default.
+
+1999-01-10  a sun  <asun@hecate.darksunrising.blah>
+
+       * libatalk/adouble/ad_open.c (ad_refresh): added v1compat option
+       to handle broken ad headers.
+
+       * etc/afpd/file.c (setfilparams): we need to make sure that we
+       flush the file if we've created it even if there's an error.  the
+       magic number/version don't get saved if we don't.
+
+       * etc/afpd/appl.c, etc/afpd/directory.c, etc/afpd/desktop.c: added
+       DIRBITS to mkdirs.
+
+1998-12-30  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/volume.c (afp_openvol): got rid of unnecessary v_did.
+
+       * etc/afpd/file.c (afp_resolveid, afp_createid): added these two
+       in. 
+
+       * well, what do you know? the cnid stuff compiles in. 
+
+1998-12-29  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/fork.c, directory.c, file.c, filedir.c, volume.c,
+       enumerate.c: added in stubs for CNID database conditional on
+       AD_VERSION > AD_VERSION1.
+
+       * etc/afpd/nls/makecode.c: added iso8859-1 mapping.
+
+1998-12-27  a sun  <asun@hecate.darksunrising.blah>
+
+       * bin/adv1tov2/adv1tov2.c: turn non-printable ascii characters
+       into hex code as well.
+
+1998-12-21  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/auth.c: fixed FPChangePW for 2-way randnums.
+
+1998-12-15  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/fork.c (read_file/write_file): do crlf translation in
+       both directions so that we can recover from problems if
+       necessary. 
+
+1998-12-14  a sun  <asun@hecate.darksunrising.blah>
+
+       * bin/adv1tov2/adv1tov2.c: small utility program that recursively
+       descends a directory and converts everything it sees into
+       AppleDouble v2.
+
+1998-12-13  a sun  <asun@hecate.darksunrising.blah>
+
+       * libatalk/adouble/ad_flush.c (ad_rebuild_header): moved the
+       header rebuilding here so that ad_refresh can use it.
+
+       * libatalk/adouble/ad_open.c (ad_refresh): added locking to v1->v2
+       conversion.
+
+       * bin/megatron/asingle.c: yuk. removed all of
+       the duplicate stuff here and had it use the #defines in adouble.h.  
+
+       * libatalk/adouble/ad_open.c (ad_refresh): finished v1 -> v2
+       conversion routine. we still need a shortname creator and a cnid
+       database for the v2 features to be useful. 
+
+1998-12-11  a sun  <asun@hecate.darksunrising.blah>
+
+       * libatalk/adouble/ad_open.c (ad_refresh): punt if we get a file
+       that we don't understand.
+
+1998-12-10  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/desktop.c (utompath,mtoupath): simplified the codepage
+       stuff. also made sure to lower/upper the 8-bit characters as
+       well. 
+
+       * libatalk/util/strdicasecmp.c: the casemapping had a few wrong
+       characters. 
+
+       * etc/afpd/fork.c (getforkparams): make sure that the ROpen/DOpen
+       attribute bits are in the correct byte ordering.
+
+1998-12-09  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/volume.c (volset): made prodos an option to options=
+       flag. also added crlf as an option.
+
+       * libatalk/adouble/ad_open.c (ad_refresh): fix up times if
+       necessary. 
+       (ad_open): deal correctly with 0-length files by treating them as
+       newly created. 
+
+       * etc/afpd/volume.c (getvolparams), file.c (get/setfilparams),
+       fork.c (getforkparams), directory.c (get/setdirparams): finished
+       adding appledouble version 1 and 2 date conversion. also added
+       attribute setting. 
+       
+       * etc/afpd/volume.c (getvolparams): make sure to flush the header
+       file if we needed to fiddle with it. 
+
+       * libatalk/adouble/ad_date.c, ad_attr.c: date/attribute
+       setting/retrieval code. 
+
+       * libatalk/adouble/ad_open.c (ad_open): initialize date
+       structures here instead of elsewhere. 
+
+1998-12-07  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/directory.c, fork.c, volume.c, file.c: added unix<->afp
+       time conversion code. 
+
+1998-12-05  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/volume.c (volset): changed prodos setting to
+       prodos=true. 
+
+1998-12-04  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/volume.c (volset): okay. you now have the following
+       options to casefold: lowercase,uppercase,xlatelower,xlateupper
+               tolower    -> lowercases everything in both directions
+               toupper    -> uppercases everything in both directions
+               xlatelower -> client sees lowercase, server sees uppercase
+               xlateupper -> client sees uppercase, server sees lowercase
+
+       NOTE: you only should use this if you really need to do so. this
+       and the codepage option can cause mass confusion if applied
+       blindly to pre-existing directories.
+       
+1998-12-03  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/desktop.c (utompath,mtoupath), etc/afpd/volume.h: added
+       multiple options to casefold. bits 0 and 1 deal with MTOU, and
+       bits 2 and 3 deal with UTOM. i did it that way so that you can
+       casefold in one direction only or in both directions if
+       desired. needless to say, setting both bits for UTOM or MTOU
+       doesn't make any sense. right now, toupper takes precedence in
+       MTOU, and tolower takes precedence in UTOM.
+
+1998-12-02  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/afp_options.c (afp_options_*): added -uampath and
+       -codepagepath to the list of available options. they specify the
+       directories to look for uam modules and codepages,
+       respectively. currently, -uampath doesn't do anything.
+
+       * etc/afpd/volume.c (readvolfile): spruced up options to
+       AppleVolumes files. now you can have mtoufile=<codepage.x>,
+       utomfile=<codepage.y>, casefold=<num> for volumes.
+
+       * etc/afpd/desktop.c (utompath,mtoupath): added
+       codepage/casefolding support. casefold is currently an int that
+       could have multiple actions. right now, i just lowercase in
+       mtoupath and uppercase in utompath.
+
+       * etc/afpd/ofork.c (of_alloc, of_findname, of_rename): added vol
+       as an additional specifier so that we don't have problems with
+       the same path names on multiple volumes. 
+
+1998-11-29  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/volume.c (getvolparams): added AFP2.1 volume attribute
+       bits. 
+
+1998-11-24  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/atalkd/config.c (readconf, getifconf): added IFF_SLAVE to
+       prevent atalkd from trying to use a slave channel.
+
+1998-11-23  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/volume.c (getvolparams): we shouldn't set the custom
+       icon bit by default on the root directory. that screws up pre-OS 8
+       systems.
+
+1998-11-19  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * libatalk/dsi/dsi_getsess.c (dsi_getsession): ignore SIGPIPE's
+       so that we can gracefully shut down the server side.
+
+       * etc/afpd/afp_dsi.c (afp_over_dsi), etc/afpd/afp_options.c,
+       libatalk/dsi/dsi_getsess.c (dsi_getsession),
+       libatalk/asp/asp_getsess.c (asp_getsession): made the tickle timer
+       interval an option (-tickleval <sec>).
+       
+       * etc/afpd/afp_dsi.c (afp_dsi_timedown): added child.die so that
+       we don't stomp on a shutdown timer. libatalk/dsi/dsi_read.c,
+       dsi_write.c both save/restore the old timer, so they don't really
+       care what the actual value is. 
+       
+1998-11-18  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * due to the recent obsession with bug fixing and low-level dsi
+       cleanups, i've decided that this should really be asun2.1.1
+       instead of asun2.1.0a.
+
+1998-11-17  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * libatalk/dsi/dsi_tcp.c (dsi_tcp_open): moved the afpd connection
+       announcement here from etc/afpd/afp_dsi.c. 
+       
+       * libatalk/dsi/dsi_stream.c: moved all of the read/write functions
+       into here as they're pretty generic. now, the protocol specific
+       stuff only handles open and close. 
+
+       * etc/afpd/fork.c (afp_read/write), desktop.c (afp_geticon),
+       file.c (copyfile), include/atalk/dsi.h (dsi_writefile/readfile):
+       added initial stubs for sendfile support. i still need to go
+       through and calculate the appropriate offsets to use. 
+
+       * libatalk/dsi/dsi_read.c, dsi_write.c: disable the interval timer
+       instead of just ignoring it.
+
+       * etc/afpd/desktop.c (afp_geticon), etc/afpd/fork.c (afp_read),
+       libatalk/dsi/dsi_read.c (dsi_readinit, dsi_readinit): modified the
+       dsi_read interface to return errors so that i can kill things
+       gracefully. 
+
+1998-11-16  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * libatalk/dsi/dsi_tcp.c (dsi_tcp_send/dsi_tcp_write): erk. read()
+       and write() treat a return of 0 differently. 
+
+1998-11-16  a sun  <asun@hecate.darksunrising.blah>
+
+       * libatalk/dsi/dsi_read.c (dsi_readinit): make sure to stick in
+       the error code.
+
+1998-11-15  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/fork.c (afp_read): re-ordered some of the checks here
+       to return earlier on 0-sized files.
+
+1998-11-13  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/afp_dsi.c (afp_over_dsi): moved the dsi->noreply toggle
+       check to here from dsi_cmdreply.
+
+1998-11-11  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/atalkd/zip.c (zip_packet): make sure to copy multicast zone
+       back out. (reported by Michael Zuelsdorff <micha@dolbyco.han.de>)
+
+1998-11-09  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/directory.c (getdirparams): changed unknown bit reply
+       code to AFPERR_BITMAP instead of AFPERR_PARAM.
+
+1998-11-06  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/enumerate.c (afp_enumerate), directory.c (renamedir):
+       fixed a couple of failed realloc leaks.
+
+       * etc/afpd/filedir.c (afp_moveandrename, afp_rename): added bits
+       to deal with case-insensitive, case-preserving filesystems.
+
+1998-10-30  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/auth.c: fixed randnum password changing check. 
+
+1998-10-27  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/atalkd/main.c: add a check for SIOCATALKDIFADDR if
+       SIOCDIFADDR fails.
+       
+       * etc/afpd/volume.c (getvolparams): ad_open had the wrong
+       parameters. 
+
+       * etc/afpd/unix.c (setdeskowner): added a little extra space to
+       prevent buffer overflows here.
+
+1998-10-26  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * sys/linux/Makefile: fixed PAM message.
+
+       * sys/solaris/Makefile: make failure to ln -s a non-fatal error. 
+
+       * etc/papd/session.c, bin/pap/pap.c: changed sequence count to
+       wrap from 0 -> 1 instead of from 0xFFFF -> 1.
+
+       * etc/afpd/filedir.c (afp_rename, afp_moveandrename): actually, we
+       should check against the entire unix name.
+       
+1998-10-21  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/filedir.c (afp_rename, afp_moveandrename): make sure
+       to check against mac name.
+
+1998-10-19  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/auth.c (afp_changepw): make password changing deal
+       correctly with "real" user names. also, moved seteuid() to before
+       the pam_authenticate() bit as shadow passwords need that.
+
+       * etc/afpd/enumerate.c (afp_enumerate): make sure to check the mac
+       name against MACFILELEN.
+
+1998-10-16  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/file.c (renamefile), filedir.c (afp_rename),
+       directory.c (renamedir): use strndiacasecmp() for AFPERR_SAMEOBJ
+       checks. also make sure test occurs before checking to see if the
+       destination exists.
+
+1998-10-15  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/auth.c (afp_changepw): fixed a bit of brain damage. i
+       forgot that password changing needs root privileges to work.
+
+       * etc/afpd/auth.c (PAM_conversation): the supplied code was
+       incorrect. i cleaned it up a bit.
+
+       * sys/linux/Makefile: fixed the installation bits.
+
+1998-10-14  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/auth.c (afp_changepw): don't kill the connection here
+       if there's a problem.
+
+1998-10-10  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/enumerate.c, fork.c, ofork.c, file.c,
+       globals.h, directory.c, auth.c: #defined MACFILELEN and used
+       that. also made sure that files > MACFILELEN never show up.
+
+1998-09-25  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/{afpd,papd,atalkd}/bprint.c (bprint): got rid of the
+       spurious pointer dereference.
+
+       * etc/afpd/ofork.c (of_alloc/of_rename): allocate the max mac file
+       length so that we don't need to realloc.
+
+       * etc/afpd/filedir.c (afp_rename, afp_moveandrename): make sure to
+       return AFPERR_BUSY if the dest has an ofork open.
+
+       * etc/afpd/file.c (renamefile), directory.c (renamedir), filedir.c
+       (afp_rename): return AFPERR_SAMEOBJ if source == dest
+
+1998-09-21  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd: went through and looked for places that needed to use
+       curdir instead of dir. i think i have them all right now.
+
+       * etc/afpd/filedir.c (afp_moveandrename): wasn't keeping track of
+       curdir correctly. what this really means is that cname should be
+       fixed to return everything it changes as opposed to changing a
+       global variable. 
+
+1998-09-19  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/config.c (configinit): do the right thing if
+       AFPConfigInit fails.
+
+1998-09-18  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/config.c (ASP/DSIConfigInit, configfree): how 
+       embarrassing. i wasn't doing refcounts correctly.
+
+1998-09-17  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/nfsquota.c (getnfsquota): ultrix uses dqb_bwarn instead
+       of dqb_btimelimit.
+
+       * sys/ultrix/Makefile: ultrix understands the old rquota format. 
+
+       * etc/afpd/ofork.c (of_findname): erk. forgot to only search in
+       the current directory. 
+       (of_rename): erk. changed it to handle renaming a file that has
+       been opened multiple times.
+
+       * etc/atalkd: made sure that if you don't specify -router, things
+       are as they were before. 
+
+1998-09-13  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/status.c (status_flags): forgot to turn on password
+       setting if randnum passwords are being used.
+
+1998-09-11  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/unix.c (setdirmode): erk. make sure only to setgid on
+       directories. 
+
+       * bin/aecho/aecho.c (main): incorporated -c <num> (ala ping) patch
+       from "Fred Lindberg" <lindberg@id.wustl.edu>.
+
+1998-09-03  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/directory.c (afp_closedir, afp_opendir): added these in
+       for more AFP 2.0 compliance. unfortunately, apple's appleshare
+       client doesn't like non-fixed directory ids.
+
+1998-08-31  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/volume.c (accessvol): the accessible volume list can
+       now be controlled by groups and usernames. just use something of
+       the following form: @group,name,name2,@group2,name3
+
+       NOTE: you can't have any spaces, and the parser forces you to
+       include all options. in this case, there are some apple II options
+       that need to be entered. they need to go away soon anyway.
+
+       * etc/afpd/auth.c (getname): oops. i forgot to copy the gecos
+       field into a temporary buffer before calling strtok.
+
+1998-08-29  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/atalkd/main.c (as_timer), rtmp.c (rtmp_delzones): when the 
+       last router on an interface goes down, we need to delete the
+       interface's zone table.
+
+1998-08-28  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/status.c (afp_getsrvrinfo): although it's never used,
+       i've added this in to increase AFP compliance.
+
+       * etc/afpd/auth.c (afp_getuserinfo): added FPGetUserInfo -- this
+       should make afpd compatible with OS 8.5's Nav Services.
+
+       * etc/atalkd/config.c,main.c: -router now specifies router mode
+       with any number of interfaces.
+
+1998-08-27  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/atalkd/main.c (as_timer): well, i figured out how to set up
+       atalkd as a single-interface router. now, you can get zones with
+       only single interfaces! i'm only waiting on wes for the approved
+       configuration toggle.
+
+1998-08-26  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * libatalk/adouble/ad_lock.c, include/atalk/adouble.h: turned the
+       ADLOCK_* into real #defines and added translations in the
+       lock-type specific functions. this should make it easier to
+       recompile things without screwing up.
+
+1998-08-26  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/atalkd/nbp.c (nbp_packet): forgot to handle another local
+       zone case.
+
+1998-08-25  a sun  <asun@hecate.darksunrising.blah>
+
+       * etc/afpd/status.c (status_server): changed status_server to
+       use only the obj part of obj:type@zone-style names.
+
+       * etc/atalkd/nbp.c (nbp_packet): unregistering wasn't handling
+       zones properly. it was matching on anything but the actual zone.
+
+1998-08-18  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/auth.c (clrtxt_login): added pam_set_time(PAM_TTY) so
+       that solaris' pam session setup doesn't crap out.
+
+1998-08-17  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/atalkd/multicast.c (zone_bcast): fixed to do the right thing
+       with zip multicast info.
+
+1998-08-15  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/nfsquota.c: made the old-style rquota fields dependent
+       upon -DUSE_OLD_RQUOTA and defined that for sunos. also included
+       <sys/time.h> for ultrix breakage.
+
+1998-08-13  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/filedir.c (afp_rename), etc/afpd/ofork.c (of_rename): i
+       knew that speeding up of_findname would be useful. in any case, i
+       discovered the source of yet another small AFP non-compliance that
+       was confusing WordPerfect. on an afp_rename, we also need to
+       rename the corresponding ofork. i've added an of_rename() to do
+       this.
+
+1998-08-13  a sun  <asun@hecate>
+
+       * etc/afpd/ofork.c (of_dealloc,of_alloc): sped up dealloc by
+       sticking refnum in ofork.
+
+1998-08-12  a sun  <asun@hecate>
+
+       * etc/afpd/fork.c (afp_openfork): added already open attribute
+       bits.
+
+       * etc/afpd/ofork.c: added a hashed of_findname.
+
+1998-08-06  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/fork.c (afp_openfork): fixed a problem with opening
+       forks from read-only non-appledouble media.
+
+1998-07-23  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/afs.c (afs_getvolspace), etc/afpd/volume.c
+       (getvolspace): modified them to treak afs like the other
+       getvolspaces w.r.t. VolSpace.
+
+1998-07-21  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/unix.c (mountp): erk. i forgot that symlinks are often
+       used for filesystems. nfs quotas sometimes failed as a
+       result. that's fixed now.
+
+1998-07-20  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/auth.c (login): added a -DRUN_AS_USER #define so that
+       it's simple to run the server process as a non-root user.
+
+1998-07-17  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/nfsquota.c (callaurpc, getnfsquota), volume.h: it turns
+       out that i was opening lots of sockets with callaurpc. now, the
+       socket gets saved and reused.
+
+       NOTE: quota-1.55-9 from redhat 5.x doesn't return the correct size
+       for rquota's bsize. unless fixed, rquota will report incorrect
+       values. 
+       
+1998-07-08  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/uam/README: added some preliminary ideas on a
+       plug-in uam architecture. in addition, this should allow arbitrary
+       afp function replacement. eventually, auth.c should get a
+       bit cleaner.
+
+1998-07-07  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/nfsquota.c: added headers and redefined a couple
+       structure fields so that sunos4 compiles.
+
+       * libatalk/compat/rquota_xdr.c: compile if we're using glibc <
+       2. this should get redhat 4.2 machines. NOTE: they're still
+       missing librpcsvc.a, so they'll need to remove that from the
+       etc/afpd/Makefile. 
+
+1998-07-06  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * libatalk/compat/rquota_xdr.c: it turns out that solaris is
+       missing a couple functions needed for rquota support. here they
+       are. 
+
+       * etc/afpd/unix.c (mountp): fixed the nfs detection for
+       solaris. we still need bsd and ultrix.
+
+1998-07-05  a sun  <asun@hecate>
+
+       * include/atalk/adouble.h: marked out space for appledouble v2. 
+
+1998-07-04  a sun  <asun@hecate>
+
+       * etc/afpd: plugged up some ad_open leaks. made sure that we don't
+       get negative numbers for length fields and such.
+       
+1998-07-04  a sun  <asun@hecate>
+
+       * etc/afpd/nfsquota.c (getnfsquota): added nfs rquota
+       support. Robert J. Marinchick <rjm8m@majink1.itc.virginia.edu>
+       provided the initial bits from the bsd quota program. 
+
+       * etc/afpd/unix.c (getquota): made getquota call getfsquota or
+       getnfsquota depending upon the type of fs. 
+
+       * etc/afpd/unix.c (mountp/special): munged mountp and
+       special to return either the nfs mount point or the fs
+       device. set the vol->v_nfs flag if it's nfs.
+
+       * etc/afpd/volume.c (getvolspace): xbfree and xbtotal will now
+       honor results returned from uquota_getvolspace.
+       
+1998-06-29  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * etc/afpd/file.c (copyfile): mmapping the file copy only helps on
+       multiple copies. as that's not the case here, i've reverted to
+       just doing read + write.
+       
+1998-06-28  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * sys/linux/Makefile: fixed the redhat-style atalk
+       installation. also, it doesn't over-write an existing
+       /etc/rc.d/init.d/atalk file.
+
+       * etc/afpd, libatalk/adouble/ad_write.c: removed <sys/file.h> and
+       </usr/ucbinclude/sys/file.h> so that we rely upon adouble.h.
+
+1998-06-19  a sun  <asun@purgatorius.zoology.washington.edu>
+
+       * changed sys/linux/Makefile to install a redhat-style sysv atalk
+       script instead of the bsd one.
+
+       * include/atalk/adouble.h: moved same-name list stub to struct
+       ad_adf. 
+       
+Thu Jun 18 18:20:28 1998  a sun  <asun@purgatorius>
+
+       * changed to asunx.y.z notation as i was getting tired of
+       increasing numbers. as this version is undergoing a fairly
+       substantial overhaul, i bumped it to 2.1.0. don't ask why asun1.0
+       never existed. i just started at 2.0.
+
+       * ofork (etc/afpd/{ofork.c,ofork.h,fork.c}: put in skeleton code
+       for hashed-by-name oforks and oforks which group by name to help
+       with byte locks and of_findname.
+
+       * adouble (include/atalk/adouble.h): started implementing
+       appledouble v2. mostly, i added bits to headers. v2 has wonderful
+       bits that should make prodos support much less clunky, allow
+       persistent dids, and allow msdos support.
+
+       * finder info: added bits to directory.c and file.c describing the
+       actual contents of finder info (from IM: Toolbox
+       Essentials). also, set default directory view to an allowed value
+       thanks to a suggestion from the appledouble v2 specs. that should
+       help with read-only media.
+
+       * etc/afpd/{directory.c,volume.c,afs.c,directory.h}: added
+       DIRDID_ROOT and DIRDID_ROOT_PARENT so people know that these did's
+       are reserved.
+       
+Wed Jun 17 11:54:49 1998  a sun  <asun@purgatorius>
+
+       * well, i'm starting a changelog as i keep forgetting what i've
+       done. 
+
+       * locks: revamped them to be more in line with what should
+       happen. currently, i've reverted back to flock-style locks as i'll
+       need to stick in more code to get fcntl-style locks to work
+       properly. specifically, i think modifying of_alloc/of_dealloc to
+       keep track of locks, fds, and names should solve the problem with
+       fcntl locks being process-specific and not fd specific.
+
diff --git a/INSTALL/README.AFS b/INSTALL/README.AFS
new file mode 100644 (file)
index 0000000..3aae839
--- /dev/null
@@ -0,0 +1,51 @@
+This is the AFS README file for netatalk.
+
+NOTE:  You'll need to have the AFS server libraries and include files.
+These are different than the client libraries and include files.  Also,
+you will need the Kerberos v4 (patch level 10) libraries and include
+files.
+
+1.  CONFIGURE FOR AFS.  Edit the root Makefile to include the pathname
+    to both the AFS and Kerberos libraries and include files.  There
+    are brief instructions at the top of the Makefile.
+
+2.  ADD AFP PRINCIPAL TO YOUR SRVTAB.  afpd requires an entry in your
+    /etc/srvtab to do Kerberos authentication.  This entry is of the
+    form
+
+       afpserver.NBPNAME@REALM
+
+    So, if you want your afp server to be called "bob" and your realm
+    is "camelot", you would need a principal
+
+       afpserver.bob@camelot
+
+    If you don't have an incorrect /etc/srvtab, Macs attempting to
+    authenticate with "Kerberos IV UAM"/"AuthMan UAM" will get a
+    "remote configuration" error and afpd will log "krb4_logincont:
+    krb_rd_req:  Can't decode authenticator".  NOTE: nbpname MUST match
+    the kerberos principal name in order for authentication to
+    succeed.  If afpd's default nbpname (see the afpd man page) does
+    not match your site's method for generating principal names, then
+    you must use the "-n" option when starting afpd.
+
+3.  CLIENT SOFTWARE.  There are several pieces of Mac software that you
+    will need.  The first, "AuthMan UAM", is an alternate UAM which
+    does authentication with afpd for AFS.  AuthMan UAM requires the
+    second, AuthMan.  AuthMan requires MacTCP.  The third, AFS
+    Privileges, allows the Mac user to display the AFS acls on afp
+    mounted directories.  "AuthMan UAM" and "AFS Privileges" are
+    available via anonymous ftp from terminator.rs.itd.umich.edu in
+    ~ftp/unix/netatalk/kerberos-AFS.sit.hqx.  AuthMan is available via
+    anonymous ftp from monet.ccs.itd.umich.edu in ~ftp/pub.  The
+    revision as of this writing is 1.0.9A.
+
+4.  AFS HELP.  For more information about AFS, you should contact
+    afs-sales@transarc.com.
+
+Research Systems Unix Group
+The University of Michigan             netatalk@umich.edu
+c/o Wesley Craig                       +1-313-764-2278
+535 W. William St.
+Ann Arbor, Michigan
+48103-4943
diff --git a/INSTALL/README.FREEBSD b/INSTALL/README.FREEBSD
new file mode 100644 (file)
index 0000000..cb0368e
--- /dev/null
@@ -0,0 +1,18 @@
+This is the FreeBSD README file for netatalk.
+
+Note that kernel support for netatalk appears in FreeBSD 2.2-current
+dated after 12 September 1996.
+
+1.  KERNEL.  Add the line
+
+       options NETATALK
+
+    to the config file for your kernel.  Rebuild and install your
+    kernel.  Reboot.
+
+Research Systems Unix Group
+The University of Michigan             netatalk@umich.edu
+c/o Wes Craig                          +1-313-764-2278
+535 W. William St.
+Ann Arbor, Michigan
+48103-4943
diff --git a/INSTALL/README.GENERIC b/INSTALL/README.GENERIC
new file mode 100644 (file)
index 0000000..acb82c7
--- /dev/null
@@ -0,0 +1,49 @@
+The generic system profile is for use on systems that don't have native
+appletalk support. For those systems, it should still be possible to get
+the AFP/tcp portion of netatalk to still work.
+
+To do that, you will need the following information:
+        1) Endian order: If your machine does not specify the
+          byte-order in netinet/in.h, you may need to modify
+          netatalk/endian.h.
+
+        2) Integer sizes: If your machine does not define intX_t and
+          u_intX_t, you will need to define them in
+          netatalk/endian.h. To ease matters, you can specify
+          _ISOC9X_SOURCE if you have inttypes.h, HAVE_64BIT_LONGS for
+          64 bit machines, or HAVE_32BIT_LONGS for 32 bit
+          machines. NOTE: you should only use HAVE_32/64BIT_LONGS on
+          machines that don't have a header file somewhere with the
+          integer sizes. If you have a file with all the relevant
+          bits, modify netatalk/endian.h to include it.
+
+       3) Quota/statfs information: You may be able to get away with
+          either BSD4_4 or __svr4__, but that's unlikely if your os
+          is some bizarre hybrid. If you don't have quota support,
+          just specify NO_QUOTA_SUPPORT. In addition, if you'll need
+          to specify the include file that gets statfs() (usually
+          either USE_VFS_H or USE_STATFS_H although BSD4_4 and
+          __svr4__ bring in a set of include files for that). Look at
+          etc/afpd/quota.c, unix.c, and unix.h for more information.
+          Finally, if you have a really old version of rquota, you
+                  can define USE_OLD_RQUOTA as well.
+
+       4) path information for lock/spool/printer files. you'll need
+           to specify -D_PATH_LOCKDIR if include/atalk/paths.h doesn't
+           have the correct paths specified for printer info and lock
+           files. 
+
+Beyond that, you should make sure that your operating system looks and
+smells like a Un*x POSIXy operating system. The only operating systems
+that netatalk supports that don't quite fit that description are
+sunos 4 and ultrix. If your operating system is peculiar, you may need
+to add in compatibility routines (libatalk/compat,
+include/atalk/compat.h) to make it look more like the others. 
+
+If you would like native AppleTalk support, you will need kernel support 
+for your operating system. Look at the Solaris STREAMS module if your
+operating system supports that framework. Otherwise, look at the ddp
+code in FreeBSD, NetBSD, or OpenBSD if your operating system is BSDish
+in nature. If your operating system looks different than these two
+cases, you'll have to roll your own implementation.
+
diff --git a/INSTALL/README.LINUX b/INSTALL/README.LINUX
new file mode 100644 (file)
index 0000000..ce1b33b
--- /dev/null
@@ -0,0 +1,37 @@
+This is the Linux README file for netatalk.
+
+We no longer include linux kernel code with netatalk, since Linux now
+includes AppleTalk support.
+
+1.  UIO.H.  On older versions of Linux, you may need to make a link from
+    /usr/include/sys/uio.h to ../linux/uio.h.  In particular, this file
+    changed from linux 1.2.x to 1.3.x, so if, for instance, you've
+    installed Slackware 3.0.0, and upgraded your kernel to 1.3.x,
+    you'll need to fix this.
+
+2.  MAKE CONFIG.  Configure your kernel with "make config".  Answer yes
+    to "AppleTalk DDP" support.
+
+3.  INSTALL KERNEL.  Make and install your kernel.  Be sure to update
+    your boot blocks!
+
+4.  If you are using libc.so.5, you will need to comment out the
+    -lcrypt and the -lrpcsvc in sys/linux/Makefile. If you're using PAM, 
+    make sure you declare -DUSE_PAM and have -lpam -ldl.  
+5.  Quota support should work under linux now. If you're using glibc
+    2.x or libc > 5.4.34, you can comment out the
+    -DNEED_QUOTACTL_WRAPPER in sys/linux/Makefile.
+
+6.  Linux 2.2.x provides the sendfile() call. This reduces overhead
+    when sending/copying files. This option will be autoconfigured on
+    compile. NOTE: you might run into problems if you have this option
+    compiled in and you switch to a machine running an os < 2.2.x.
+
+
+Research Systems Unix Group
+The University of Michigan             netatalk@umich.edu
+c/o Wes Craig                          +1-313-764-2278
+535 W. William St.
+Ann Arbor, Michigan
+48103-4943
diff --git a/INSTALL/README.NETBSD b/INSTALL/README.NETBSD
new file mode 100644 (file)
index 0000000..cb32bfc
--- /dev/null
@@ -0,0 +1,18 @@
+This is the NetBSD README file for netatalk.
+
+Note that kernel support for netatalk appears in NetBSD 1.3, and in
+NetBSD 1.2D dated after April 5, 1997.
+
+1.  KERNEL.  If not already present, add the line
+
+       options NETATALK
+
+    to the config file for your kernel.  Rebuild and install your
+    kernel.  Reboot.
+
+Research Systems Unix Group
+The University of Michigan             netatalk@umich.edu
+c/o Wes Craig                          +1-313-764-2278
+535 W. William St.
+Ann Arbor, Michigan
+48103-4943
diff --git a/INSTALL/README.OPENBSD b/INSTALL/README.OPENBSD
new file mode 100644 (file)
index 0000000..c4d4a98
--- /dev/null
@@ -0,0 +1,18 @@
+This is the OpenBSD README file for netatalk.
+
+Note that kernel support for netatalk appears in OpenBSD 2.2, or
+openbsd-current dated after Aug 1, 1997.
+
+1.  KERNEL.  Add the line
+
+       options NETATALK
+
+    to the config file for your kernel.  Rebuild and install your
+    kernel.  Reboot.
+
+Research Systems Unix Group
+The University of Michigan             netatalk@umich.edu
+c/o Wes Craig                          +1-313-764-2278
+535 W. William St.
+Ann Arbor, Michigan
+48103-4943
diff --git a/INSTALL/README.SOLARIS b/INSTALL/README.SOLARIS
new file mode 100644 (file)
index 0000000..a51f1c8
--- /dev/null
@@ -0,0 +1,68 @@
+This is the Solaris README file for netatalk.
+
+1.  SELECT COMPILER.  This distribution is configured to build with
+    gcc.  It should also work with cc. At the present time only cc
+    v5.0 and above can build the 64-bit kernel module.
+
+1a. SELECT KERNEL TYPE.  Edit sys/solaris/Makefile and set KCFLAGS to
+     include sparcv9 support if you run a 64-bit kernel, or leave it
+     commented out for a 32-bit kernel. Only UltraSPARC systems
+     running Solaris 7 and above support a 64-bit kernel. If you're
+     not sure what kernel you use, run "isainfo -v". You're running a
+     64-bit kernel if the result includes 64-bit (sparcv9), otherwize
+     it's 32-bit.
+
+     NOTE: If you want both the 32-bit and 64-bit kernel modules to be
+     installed, first compile and install the version appropriate to
+     the kernel that you're currently running, then make clean,
+     compile and install the other version.
+
+2.  EDIT NETCONFIG.  Add the following line to /etc/netconfig:
+
+       ddp tpi_clts - appletalk ddp /dev/ddp -
+
+    This makes the socket library aware of the AppleTalk protocol
+    family.
+
+3.  INSTALL DRIVER.  Since the STREAMS ddp driver must be installed as
+    root, we've separated that portion of the build.  Type
+
+       make kinstall
+
+    to install the driver.  This copies the driver and it's config file
+    into /usr/kernel/drv and /usr/kernel/strmod, runs "add_drv ddp" to
+    make the kernel aware of the new driver, and adds an rc file to
+    /etc/rc?.d.
+
+4.  ATALKD.CONF.  Under Solaris, you must create atalkd.conf, since
+    Solaris provides no method for determining the names of the
+    available interfaces.  It is sufficent to name the available
+    interfaces in atalkd.conf, one per line.  E.g.
+
+       le0
+
+    on a line by itself, on many Suns.  See atalkd(8).
+
+5.  PRINTING.  To quote my Solaris documentation, "Setting up printing
+    services using the LP print service command-line interface is
+    complicated and error-prone."  See your Solaris documentation for
+    similarly helpful statements.
+
+    Presuming that the Solaris print filters are installed (see your
+    helpful Solaris documentation), the following command adds the
+    printer named "bob" your to system and enables printing:
+
+       lpadmin -p bob -i /usr/local/atalk/etc/lp2pap.sh \
+               -I postscript -v /dev/null -T PS
+       enable bob
+
+    This creates the directory /etc/lp/bob, in which you should create
+    a file called ".paprc" containing the NBP name of the printer.  See
+    the "pap" man page for more information.
+
+Research Systems Unix Group
+The University of Michigan             netatalk@umich.edu
+c/o Wes Craig                          +1-313-764-2278
+535 W. William St.
+Ann Arbor, Michigan
+48103-4943
diff --git a/INSTALL/README.SUNOS b/INSTALL/README.SUNOS
new file mode 100644 (file)
index 0000000..42aead0
--- /dev/null
@@ -0,0 +1,18 @@
+This is the SunOS README file for netatalk.
+
+1.  KERNEL MODULE. This version of netatalk requires that your kernel
+    be configured with
+
+       options VDDRV
+
+    The loadable kernel module is made and installed during the normal
+    make and make install.  NOTE:  Unloading the kernel module may not
+    work correctly and may cause your kernel to panic, hang, or do
+    other nasty things.
+
+Research Systems Unix Group
+The University of Michigan             netatalk@umich.edu
+c/o Wesley Craig                       +1-313-764-2278
+535 W. William St.
+Ann Arbor, Michigan
+48103-4943
diff --git a/INSTALL/README.ULTRIX b/INSTALL/README.ULTRIX
new file mode 100644 (file)
index 0000000..a674713
--- /dev/null
@@ -0,0 +1,85 @@
+This is the Ultrix README file for netatalk.
+
+1.  REMOVE PREVIOUS NETATALK.  If this is the first release of netatalk
+    you've used, skip to step (2).  If you've installed netatalk
+    before, you will need to remove the old patches.
+
+    There are two easy ways to remove the old patches.  The simplist,
+    is to apply the old patches in reverse.  E.g. for netatalk-1.3 or
+    later
+
+       patch -s -d /sys -R -p0 < sys/ultrix/kpatch-<vers>
+
+    Note that the old patches are not included in this distribution.
+
+    If you haven't saved the old patches, it is possible that the old
+    versions of the patched files are still in your file system.  When
+    patch changes a file, it saves the original file as <file>.orig.
+    netatalk-1.3 or later leaves these files
+
+       conf/files
+       data/af_data.c
+       data/if_to_proto_data.c
+       data/uipc_domain_data.c
+       net/net/conf_net.c
+       net/net/netisr.h
+
+    Finally, if there is no way to regenerate the old version of the
+    patched files, you can apply the new patches by hand, skipping the
+    next section.  There are two versions of the patches, kpatch-4.1
+    for Ultrix 4.1 and kpatch-4.2 for both 4.2 and 4.3.
+
+2.  PATCH FILES IN KERNEL BUILDING AREA.  The simplest way to install the
+    kernel patches is with the command
+
+       make kpatch
+
+    This command will determine your system type and apply patches to
+    the following files
+
+       conf/files
+       data/af_data.c
+       data/if_to_proto_data.c
+       data/uipc_domain_data.c
+       net/net/conf_net.c
+       net/net/netisr.h
+
+    To apply these patches by hand, type
+
+       patch -s -d /sys -p0 < sys/ultrix/kpatch-<vers>
+
+    where <vers> is the version of Ultrix you are running.
+
+3.  POPULATE /sys/net/netatalk. Do this with
+
+       make kinstall
+
+    This makes a directory called /sys/net/netatalk and copies all
+    pertinent files.  To do this by hand,
+
+       mkdir /sys/net/netatalk
+       cp sys/netatalk/*.[ch] /sys/net/netatalk
+       cp sys/ultrix/*.[ch] /sys/net/netatalk
+
+    These files are the complete source for AppleTalk in 4.3BSD kernels
+    and Ultrix.
+
+4.  CREATE KERNEL. You should probably start by copying the config file
+    for GENERIC to, for instance, NETATALK. You'll need to add the
+    lines
+
+       options ATALK
+       pseudo-device atalk
+
+    to the NETATALK config file, config the kernel, and then make.  You
+    don't have to call your kernel NETATALK, but be sure NOT to call it
+    ATALK, since the name of the kernel and kernel options are part of
+    the same name-space.  When your build is finished, save your old
+    kernel, install the new kernel, and reboot.
+
+Research Systems Unix Group
+The University of Michigan             netatalk@umich.edu
+c/o Wes Craig                          +1-313-764-2278
+535 W. William St.
+Ann Arbor, Michigan
+48103-4943
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..da9d5c2
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,124 @@
+# Root of installation. Subdirectories will be ${DESTDIR}/etc,
+# ${DESTDIR}/bin, and ${DESTDIR}/lib.
+DESTDIR=/usr/local/atalk
+
+# for system-level binaries
+SBINDIR=$(DESTDIR)/sbin
+# for user-level binaries
+BINDIR=$(DESTDIR)/bin
+# for program libraries (*.a)
+LIBDIR=$(DESTDIR)/lib
+# for machine-independent resources (pagecount.ps, etc.)
+RESDIR=$(DESTDIR)/etc
+# for configuration files (AppleVolumes.system, etc.)
+ETCDIR=$(DESTDIR)/etc
+# for include files
+INCDIR=$(DESTDIR)/include
+# Root of man pages.  Subdirectories will be
+# ${MANDIR}/man1, ${MANDIR}/man4, and ${MANDIR}/man8.
+MANDIR=$(DESTDIR)/man
+
+#INSTALL_PREFIX=
+#SBINDIR=${INSTALL_PREFIX}/usr/sbin
+#BINDIR=${INSTALL_PREFIX}/usr/bin
+#LIBDIR=${INSTALL_PREFIX}/usr/lib
+#RESDIR=${INSTALL_PREFIX}/usr/lib/atalk
+#ETCDIR=${INSTALL_PREFIX}/etc/atalk
+#INCDIR=${INSTALL_PREFIX}/usr/include
+#MANDIR=${INSTALL_PREFIX}/usr/man
+
+# Location of the Berkeley v2 db library and include files. 
+# NOTE: leave this commented out for now. it's a placeholder for a future
+# feature.
+#DB2DIR=/usr/local/BerkeleyDB
+
+# Location of the Diffie-Hellman library and include files. Uncomment
+# this out if you want DHX as an allowable UAM for afpd. Currently,
+# this is set up expecting libcrypto from the openssl project. As a
+# result, this option will enable all of the encrypted authentication
+# methods (including the Randnum Exchange ones). DHX expects cast.h,
+# dh.h, and bn.h in $CRYPTODIR/include with -lcrypto in
+# $CRYPTODIR/lib. NOTE: os x server will complain if you use both
+# randnum exchange and DHX.
+CRYPTODIR=/usr/local/ssl
+
+# Location of the DES library and include files. Uncomment this out if
+# you want Randnum Exchange and 2-Way Randnum Exchange as allowable
+# UAMs for afpd. We expect libdes.a in $DESDIR/lib and des.h in
+# $DESDIR/include.  This option will get overridden by CRYPTODIR.
+#DESDIR=/usr/local
+
+# Location of the tcp wrapper library and include files. Comment this out
+# if you don't want tcp wrapper support. having tcp wrapper support is
+# highly recommended.
+TCPWRAPDIR=/usr
+
+# Location of PAM support library and include files. Uncomment this if
+# you want to enable PAM support.
+#PAMDIR=/usr
+
+# Location of cracklib support library and include files. This is used
+# in the password changing routines. Uncomment this out if you want to
+# enable support.
+#CRACKDIR=/usr
+
+# Location of the AFS and Kerberos libraries and include files.  Uncomment
+# and edit these if you want to include AFS or Kerberos support in afpd
+# or Kerberos support in papd.
+#AFSDIR=/usr/local/afs
+#KRBDIR=/usr/local/kerberos
+
+##########################################################################
+all install depend clean tags kernel kinstall kpatch:  FRC
+       @case `uname -rs` in \
+           "SunOS 4"*) ARCH=sunos \
+               ;; \
+           "SunOS 5"*) ARCH=solaris \
+               ;; \
+           ULTRIX*) ARCH=ultrix \
+               ;; \
+           Linux*) ARCH=linux \
+               ;; \
+           FreeBSD*) ARCH=freebsd \
+               ;; \
+           NetBSD*) ARCH=netbsd \
+               ;; \
+           OpenBSD*) ARCH=openbsd \
+               ;; \
+           Rhapsody*) ARCH=osx \
+               ;; \
+           *) ARCH=generic \
+               ;; \
+       esac; \
+       echo "Making $@ for $$ARCH..."; \
+       cd sys/$$ARCH && ${MAKE} ${MFLAGS} \
+           SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}"\
+           ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+           DESTDIR="${DESTDIR}" MANDIR="${MANDIR}" \
+           TCPWRAPDIR="${TCPWRAPDIR}" PAMDIR="${PAMDIR}" DB2DIR="${DB2DIR}" \
+           AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" DESDIR="${DESDIR}" \
+           CRYPTODIR="${CRYPTODIR}" CRACKDIR="${CRACKDIR}" \
+           OSVERSION="`uname -r`" MACHINETYPE="`uname -m`" \
+           $@
+
+FRC:   include/netatalk
+
+include/netatalk:
+       -ln -s ../sys/netatalk include/netatalk
+
+SYS=sunos ultrix solaris
+VERSION=`date +%y%m%d`
+DISTDIR=../netatalk-${VERSION}
+
+sysclean : FRC
+       for i in ${SYS}; \
+           do (cd sys/$$i; ${MAKE} ${MFLAGS} sysclean); \
+       done
+
+dist : sysclean clean
+       mkdir ${DISTDIR}
+       tar cfFFX - EXCLUDE . | (cd ${DISTDIR}; tar xvf - )
+       chmod +w ${DISTDIR}/Makefile ${DISTDIR}/include/atalk/paths.h \
+               ${DISTDIR}/sys/solaris/Makefile
+       echo ${VERSION} > ${DISTDIR}/VERSION
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..1a82dc8
--- /dev/null
+++ b/README
@@ -0,0 +1,116 @@
+This is the README file for netatalk.
+
+Contents:
+           o A Brief Description
+           o BUILDING & INSTALLING
+           o Getting Help
+
+netatalk is an implementation of the AppleTalk Protocol Suite.  The
+current release contains support for EtherTalk Phase I and II, DDP,
+RTMP, NBP, ZIP, AEP, ATP, PAP, ASP, and AFP.  The complete stack looks
+like this on a BSD-derived system:
+
+    AFP
+     |
+    ASP    PAP
+      \   /
+       ATP RTMP NBP ZIP AEP
+       |    |   |   |   |
+   -+---------------------------------------------------+- (kernel boundary)
+    |                    Socket                         |
+    +-----------------------+------------+--------------+
+    |                       |     TCP    |    UDP       |
+    |          DDP          +------------+--------------+
+    |                       |           IP              |
+    +-----------------------+---------------------------+
+    |                Network-Interface                  |
+    +---------------------------------------------------+
+
+DDP is in the kernel.  "atalkd" implements RTMP, NBP, ZIP, and AEP.  It
+is the AppleTalk equivalent of Unix "routed".  There is also a
+client-stub library for NBP.  ATP and ASP are implemented as
+libraries.  "papd" allows Macs to spool to "lpd", and "pap" allows Unix
+machines to print to AppleTalk connected printers.  "psf" is a
+PostScript printer filter for "lpd", designed to use "pap".  "psorder"
+is a PostScript reverser, called by "psf" to reverse pages printed to
+face-up stacking printers.  "afpd" provides Macs with an interface to
+the Unix file system.  Refer to the appropriate man pages for
+operational information.
+
+netatalk runs on five operating systems:
+
+       OS      Versions        Hardware        Notes
+       --      --------        --------        -----
+       Solaris 2.5             Sparc
+       Linux   1.3.x,2.x       PC
+       FreeBSD 2.2-current     PC              after 12 Sept 96
+       SunOS   4.1+            Sparc           kernel must have VDDRV
+                                               option installed
+       Ultrix  4.[1-4]         3100,5000
+
+Instructions for installing the kernel portions of netatalk and system
+dependent FAQs are in the README file for your system, e.g.
+INSTALL/README.SUNOS, INSTALL/README.ULTRIX.
+
+Building netatalk:
+
+0.  To build afpd for use with an AFS filesystem, first follow the
+    instructions in INSTALL/README.AFS, then complete these
+    instructions.
+
+1.  Set DESTDIR in the root Makefile.  DESTDIR is the directory below
+    which all binaries will be installed.  Setting it causes all
+    installation-relative pathnames to be set correctly.  You may also
+    wish to set MANDIR.  (If you do not want all binaries to go under
+    DESTDIR, you can instead set SBINDIR, BINDIR, ETCDIR, and LIBDIR,
+    to control the locations of the individual sections.)
+
+2.  When you've completed the configuration, type "make" at the root of
+    the source.  This will make all binaries.
+
+Installing netatalk:
+
+1.  To install the binaries, type "make install" at the root of the
+    source tree.  This will install all of the binaries.
+
+2.  Sample config files for the daemons are in the config directory,
+    e.g. config/AppleVolumes.system.  Install these files, or a version
+    of these files, in ETCDIR (as distributed DESTDIR/etc), e.g.
+    ETCDIR/AppleVolumes.system.  See the daemon's man page for a
+    description of it's configuration file.
+
+3.  psf uses the script SBINDIR/etc2ps to convert anything it
+    doesn't understand to PostScript.  If you have a troff or dvi to
+    PostScript filter on your machine, you might wish to edit etc2ps,
+    to use your locally installed PostScript utilities.
+
+4.  Add the contents of services.atalk to your /etc/services database.
+    If you're using NIS (YP), add the contents of services.atalk to the
+    NIS master's maps and push them.
+
+5.  The file rc.atalk is installed in ETCDIR.  It should be called
+    from your /etc/rc file, e.g. "sh ETCDIR/rc.atalk".  For more
+    information on what this script does, read the man pages for the
+    appropriate commands.
+
+You might be interested in looking at the netatalk home page at
+http://www.umich.edu/~rsug/netatalk.  It has an archive of patches,
+trouble shooting hints, and some links to other netatalk and file
+service related sites.
+
+You may report bugs and get help by sending mail to the developers at
+netatalk@umich.edu.  If you're reporting bugs, you MUST use the format
+provided in the BUGS file!  We will do our best to help you.
+
+You may wish to join the netatalk-admins@umich.edu (moderated) mailing
+list.  It carries announcements of new releases and general
+discussion.  You can join (or resign from) this list by sending mail to
+netatalk-admins-request@umich.edu.  Submissions (NOT requests to join
+or resign) to this list should be sent to netatalk-admins@umich.edu.
+
+Research Systems Unix Group
+The University of Michigan             netatalk@umich.edu
+c/o Wesley Craig                       +1-313-764-2278
+535 W. William St.
+Ann Arbor, Michigan
+48103-4943
diff --git a/README.ASUN b/README.ASUN
new file mode 100644 (file)
index 0000000..ab6aec5
--- /dev/null
@@ -0,0 +1,164 @@
+this version of netatalk represents changes i have made to incorporate
+AFP 2.2 (AppleShare TCP/IP) support. it is based upon 1.4b2 and is not
+currently supported by umich. i hope to eventually get it incorporated
+into a future version. 
+
+i hope you find this code useful. as such, i am releasing my changes
+under a copyright similar to the rest of the netatalk code. 
+
+i would appreciate users of my patches letting me know of any problems
+or difficulties they have with it. i can only tested it on a limited
+number of machines. as a result, improved compatability and fixes can 
+only come if i hear of problems. you can find my patches at
+<ftp://ftp.cobaltnet.com/pub/users/asun>.
+
+the patches currently include the following features:
+       AFP/TCP
+       64-bit clean
+       large volume support -- you'll need at least 3.7.2seed3 
+                               and os > 7.6.1 for this to to be used.
+
+                               If your compiler can't generate 64-bit
+                               ints, you'll need to disable this
+                               feature. add -DNO_LARGE_VOL_SUPPORT to
+                               the DEFS line in your system's
+                               Makefile. NOTE: gcc can generate
+                               64-bit ints.
+
+                               ADDITIONAL NOTE: gcc sometimes has
+                               problems with 64-bit ints. i already
+                               have a workaround in the code to deal
+                               with this issue.
+
+       server messages -- at this point, there is no mechanism to send
+                          an arbitrary server message. 
+
+       all of AFP 2.2. All of AFP 2.1 except for FPCatSearch is 
+               is implemented if fixed id support is compiled in.
+
+       tcp wrapper support. if TCPWRAPDIR is uncommented in the
+               main Makefile, tcp wrapper support will get built.
+               i recommend building w/ it to enable host restrictions.
+
+       a number of bug fixes (SO_BROADCAST, server info, file/dir
+               case insensitive comparisons, and more probably)
+
+       working quota support for linux and bsd4.4. nfs rquota support
+       is also available. it hasn't been extensively tested on all
+       the platforms yet. NOTE: there's bug in the linux kernel code 
+        pre-2.2.8 and pre-2.0.37 that prevents quota support from working
+        properly under linux.
+
+       you can now specify server options in an afpd.conf file. it's
+       pretty useless unless you want to start multiple servers up.
+       anyways, look at config/afpd.conf to see what's available.
+       in addition, you can use kill -HUP to force a re-read of
+       afpd.conf. as the first kill -HUP turns off connections,
+       you'll have to send another one to force a re-read.
+
+       i've also merged a slightly modified version of redhat's pam
+       patches. you need to make sure that the PAMDIR entry in the main
+       Makefile is uncommented and pointing to the right directory for
+       this to work. in case you don't know what pam is, it stands for
+       pluggable authentication modules. for more information, here's
+       a web page: <http://www.redhat.com/linux-info/pam/>
+
+       i've merged in <shirsch@ibm.net>'s apple II ProDOS support. 
+
+       i've added Randnum and 2-Way Randnum support. part of the code is
+       compliments of<shirsch@ibm.net>. as afp doesn't do the
+       fallback thing in case of failure, Randnum and 2-Way Randnum
+       are only available via afpd.conf. To get them to work, each
+       user must have a ~/.passwd file (not read-/writeable by anyone
+       else) with a password. this is a potential security problem as
+       root can read the password. this may be compensated, to some
+       extent, by the fact that your password never goes onto the wire
+       when mounting a volume.
+
+       NOTE: you will need to get a copy of the des library if you
+       don't already have one for this option to work. i got mine
+       from <ftp://ftp.psy.uq.oz.au/pub/Crypto/DES/libdes-x.xx.tar.gz>
+
+        A Diffie-Hellman-based UAM is also available. This requires 
+       libcrypto from either the SSLeay package (available at the
+       above site) or OpenSSL (ftp.openssl.org).
+
+       ADDITIONAL NOTE: the absence of a /dev/urandom or running out
+       of entropy will result a non truly-random number being used as 
+       the challenge. you have been warned. for all intents and
+       purposes, however, linux' /dev/urandom should provide a
+       sufficiently random number to be considered secure even when
+       the entropy pool gets drained. it certainly does a much better
+       job than gettimeofday(); random().
+       
+       the bad file descriptor bug should now be fixed. thanks to
+       bsmith@h-e.com for tracking this down.
+
+       this patchset should not have a problem with "dancing icons."
+       if you are still having a problem with this, it's highly
+       likely that files in your .AppleDouble directory have gotten
+       corrupted.
+
+       you can now login in with your "real" user name as specified
+       in your password entry. if you don't want to do this, just add
+       -DNO_REAL_USER_NAME to your DEFS line.
+
+       byte locks should now work. if you want to enable the old way
+        of doing things, add -DUSE_FLOCK_LOCKS.
+
+       you can now specify whether or not you want uservolume files
+       to be read. add -nouservol to afpd.conf if you don't want user-
+       specified .AppleVolumes files to be read.
+
+       afpd now will report the number of kilobytes read/written during
+       a session (from the server's perspective).
+       
+       i have merged against netatalk-990130. this includes an
+       improved STREAMS driver and some changes to libatalk. the
+       STREAMS driver still doesn't do setsockopt correctly, but it's
+       supposed to be much more stable. contact the folks at umich if
+       you have questions about it.
+
+       fixed a problem with sys/netatalk/ddp_input.c reported by
+       <abs@anim.dreamworks.com>.
+       
+       AppleVolumes.* now has many more configuration options. You
+       can specify newline translation (crlf) on a per-volume basis,
+       utilize a codepage translation file for compatibility with
+       other file serving programs, and restrict access to particular
+       volumes. Please read config/AppleVolumes.default for more
+       information.
+
+platforms compiled on: 
+       linux/intel,sparc
+       linux/axp
+       *bsd
+       sunos4.1.4/sparc
+       ultrix/mips
+       solaris 2.5.x, 2.6, and 2.7. 
+
+problems with appletalk: 
+       certain ethernet card/drivers don't deal well with the fact
+       that appletalk aggressively uses hardware multicast. here are
+       a few ones that may cause problems:
+           ne2000 clones
+           3Com501 cards (maybe others)
+           intel etherexpress/pro 
+             set multicast_filter_limit=3 in linux if you're having
+             problems with this card. to do that, add the following
+             line to /etc/conf.modules: 
+                options eepro100 multicast_filter_limit=3
+
+Acknowledgements:
+       i would like to thank leland wallace at apple for a lot of
+       helpful advice on interpreting the appleshare ip documentation.
+       i would also like to thank the numerous people who have helped
+       test this program. they greatly improved the compatability of
+       the code.
+
+       REALM Information provided financial support for the
+       AppleDouble v2 and CNID database work.
+
+adrian sun
+asun@cobaltnet.com
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..53dbf64
--- /dev/null
+++ b/TODO
@@ -0,0 +1,82 @@
+desired features (in no particular order):
+    afpd:
+        return the right version-specific error codes
+        honor the readonly/deleteinhibit/renameinhibit/copyprotect bits.
+        via a database (that handles ro media, -db <path>):
+               Add afp_fileid capabilities - done
+               Add afp_catalogue
+               afp_enumerate optimization
+               cname expansion to handle 8+3 and 255-unicode.
+        server messages in useful places 
+        Other useful bits of AFP 2.0/2.1. are there any?
+       change afp/ddp child handling to be in line w/ generic?
+        administrative control program (using asip API?)
+       appledouble v2 (gets us persistent DIDs). we'll need a did
+               database for non-hfs volumes. i think mmapping a
+               bitmap with dids at the root level should work.
+               also, v2 gets us much better prodos support and
+               provides for shortname support. - done 
+       various toggles to afpd.conf (-timeout, etc.)
+       figure out more ways of optimizing things. current places
+               of interest:
+               1) reading/writing. currently there's character 
+                  replacement. we should have a better way of doing
+                  it that speeds things up the no-replacement case.
+                  i've already done that for the crlf case.
+               2) use of mmap where appropriate. NOTE: this will only
+                   be a win if we mmap heavily used files. likely
+                  candidates include file-based databases. mmapping 
+                  header files is actually pretty slow under linux.
+               3) change lookup algorithms where appropriate. 
+       ability to interact with hfs desktop databases (either by
+               changing hfs or changing afpd).
+               
+    papd: implementing uams would be a good idea
+        
+        
+things to fix:
+        cleaner separation of layers.
+       fcntl-style byte locks - almost done. it just needs to do a
+        little sorting to get around a race condition.
+       AFP <-> Unix permissions. there are a couple cases where they 
+               don't map perfectly. in particular, gid == 0 means
+               ignore group permissions while uid == 0 means anybody
+                can fiddle with this file. in addition, we need to be
+               able to still change permissions on a directory with u-a
+               set. finally, we need to adjust the offspring count
+               for directories based upon what permissions they
+               have. i.e., search -> can see directories. 
+                           read -> can see files.
+               we need to map permissions so that these semantics get
+                followed.
+        real variable DIDs for now: it turns out that afpd lies about
+                this and says that it's doing fixed dids. bad bad
+                bad. i just have to implement Open/CloseDir and fiddle
+                a little with did assignment. argh. apple's appleshare
+               client doesn't like non-fixed directory ids. 
+
+added features:
+       more of AFP 2.0!
+       sped up of_findname and of_dealloc.
+       increased AFP compliance w.r.t. open forks and renames:
+               wordperfect and simpletext now both work!
+        nfs quota support
+       solaris beta STREAMS driver added.
+       64-bit cleanup
+       cleaner startup/takedown
+        added debug messages to dsi_write areas.
+       fixed server info unexpected disconnects (due to OT bug).
+        afp/ddp and afp/tcp cohabitation. afp/ddp and afp/tcp can
+       operate separately now (-T turns off tcp, -D turns off ddp).
+       incorporated the netbsd patches 
+               [source: wrstuden@loki.stanford.edu (Bill Studenmund)]
+       added/fixed quota support for linux/bsd4.4
+       codepage support/casefolding on a per volume basis.
+       expanded per-volume options
+       added "generic" platform support for AFP/tcp.
+       dynamically loaded uams 
+       :ETCDIR:/afppasswd file for randnum passwds
+       can turn off .AppleDouble generation.
+       AppleVolumes variable substitions
+       atalkd: zones on single interfaces and the ability to control
+               which interfaces get routes between them.
diff --git a/VERSION b/VERSION
new file mode 100644 (file)
index 0000000..b3f2fbe
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+1.4b2+asun2.1.4
diff --git a/bin/Makefile b/bin/Makefile
new file mode 100644 (file)
index 0000000..842f044
--- /dev/null
@@ -0,0 +1,40 @@
+ALL=   aecho getzones nbp psorder megatron pap adv1tov2 afppasswd
+TAGSFILE=tags
+INSTALL=       install
+
+all:   ${ALL}
+
+${ALL}: FRC
+       cd $@; ${MAKE} ${MFLAGS} CC="${CC}" \
+           ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+           SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}"\
+           ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+           DESTDIR="${DESTDIR}"
+
+FRC:
+
+tags:
+       for i in ${ALL}; do \
+           (cd $$i; ${MAKE} ${MFLAGS} DEFS="${DEFS}" \
+               TAGSFILE=../${TAGSFILE} tags); \
+       done
+
+install:
+       -mkdir ${BINDIR}
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} CC="${CC}" \
+               ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+               SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+               ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+               DESTDIR="${DESTDIR}" INSTALL="${INSTALL}" install); \
+       done
+
+clean:
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} clean); \
+       done
+
+depend:
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} DEFS=${DEFS} depend); \
+       done
diff --git a/bin/adv1tov2/Makefile b/bin/adv1tov2/Makefile
new file mode 100644 (file)
index 0000000..331d24a
--- /dev/null
@@ -0,0 +1,49 @@
+SRC=   adv1tov2.c
+OBJ=   adv1tov2.o
+
+INCPATH=       -I../../include
+CFLAGS=        ${DEFS} ${OPTOPTS} ${INCPATH}
+LIBS=  -latalk ${ADDLIBS}
+TAGSFILE=      tags
+CC=    cc
+INSTALL=       install
+LIBDIRS=       -L../../libatalk
+
+TARGET=        adv1tov2
+
+all : ${TARGET}
+
+${TARGET} : ${OBJ}
+       ${CC} ${CFLAGS} -o ${TARGET} ${OBJ} ${LIBDIRS} ${LIBS}
+
+install : all
+       ${INSTALL} -c ${TARGET} ${BINDIR}
+clean :
+       rm -f a.out core* *.o *.bak *[Ee]rrs tags
+       rm -f ${TARGET}
+
+tags : ${SRC}
+       cwd=`pwd`; \
+       for i in ${SRC}; do \
+           ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+       done
+
+depend :
+       for i in ${SRC} ; do \
+           ${CC} -M ${DEFS} ${INCPATH} $$i | \
+           awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+               else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+               else rec = rec " " $$2 } } \
+               END { print rec } ' >> makedep; done
+       sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+       cat makedep >> Makefile.tmp
+       rm makedep
+       echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+       echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+       echo '# see make depend above' >> Makefile.tmp
+       rm -f Makefile.bak
+       cp Makefile Makefile.bak
+       mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
diff --git a/bin/adv1tov2/adv1tov2.c b/bin/adv1tov2/adv1tov2.c
new file mode 100644 (file)
index 0000000..2e8d12a
--- /dev/null
@@ -0,0 +1,135 @@
+/* v1tov2: given a root directory, run down and convert all the
+ * files/directories into appledouble v2.  */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+
+#include <atalk/adouble.h>
+
+#if AD_VERSION == AD_VERSION2
+/* translate characters */
+static int xlate(char *name, int flags) {
+  static const char hexdig[] = "0123456789abcdef";
+  char upath[MAXPATHLEN + 1];
+  char *m, *u;
+  int doit = 0;
+
+  m = name;
+  u = upath;
+  while (*m != '\0') {
+    if (isascii(*m) && !isprint(*m)) {
+      doit = 1;
+      *u++ = ':';
+      *u++ = hexdig[(*m & 0xf0) >> 4];
+      *u++ = hexdig[*m & 0x0f];
+    } else
+      *u++ = *m;
+    m++;
+  }
+  
+  if (doit) {
+    *u = '\0';
+    rename(name, upath);
+    if ((flags & ADFLAGS_DIR) == 0)
+      rename(ad_path(name, flags), ad_path(upath, flags)); /* rename rfork */
+  }
+}
+
+
+#define MAXDESCEND 0xFFFF
+/* recursively descend subdirectories. 
+ * oh the stack space we use up! */
+void descend(DIR *dp)
+{
+  DIR *dpnew;
+  struct dirent *de;
+  unsigned char *m, *u;
+  struct stat st;
+  struct adouble ad;
+  int flags;
+  static int count = 0;
+
+  if (count++ > MAXDESCEND) {
+    fprintf(stderr, "FAILURE: too many subdirectories! possible infinite recursion.");
+    return;
+  }
+    
+  putc('(', stderr);
+  for (de = readdir(dp); de; de = readdir(dp)) {
+    if (de->d_name[0] == '.') 
+      continue;
+
+    if (stat(de->d_name, &st) < 0) {
+      fprintf(stderr, "FAILURE: can't stat %s\n", de->d_name);
+      continue;
+    }
+
+    /* go down subdirectory */
+    flags = ADFLAGS_HF;
+    if (S_ISDIR(st.st_mode) && (dpnew = opendir(de->d_name))) {
+      chdir(de->d_name);
+      descend(dpnew);
+      closedir(dpnew);
+      chdir("..");
+      flags |= ADFLAGS_DIR;
+    }
+
+    if (ad_open(de->d_name, flags, O_RDWR, 0, &ad) < 0) {
+      fprintf(stderr, "FAILURE: can't convert %s\n", de->d_name);
+      continue;
+    }
+    ad_close(&ad, ADFLAGS_HF);
+    xlate(de->d_name, flags);
+    fputc('.', stderr);
+  }
+  putc(')', stderr);
+}
+
+
+int main(int argc, char **argv)
+{
+  DIR           *dp;
+  struct adouble ad;
+  if (argc != 2) {
+    fprintf(stderr, "%s <directory>\n", *argv);
+    return -1;
+  }
+    
+  /* convert main directory */
+  if (ad_open(argv[1], ADFLAGS_HF | ADFLAGS_DIR, O_RDWR, 0, 
+             &ad) < 0) {
+    fprintf(stderr, "FAILURE: can't convert %s\n", argv[1]);
+    return -1;
+  }
+
+  ad_close(&ad, ADFLAGS_HF);
+  if ((dp = opendir(argv[1])) == NULL) {
+    fprintf(stderr, "%s: unable to open %s\n", *argv, argv[1]);
+    return -1;
+  }
+
+  chdir(argv[1]);
+  descend(dp);
+  closedir(dp);
+  chdir("..");
+
+  xlate(argv[1], ADFLAGS_HF | ADFLAGS_DIR);
+  putc('\n', stderr);
+  return 0;
+}
+
+#else
+int main(int argc, char **argv)
+{
+  fprintf(stderr, "%s not built for v2 AppleDouble files.\n", *argv);
+  return -1;
+}
+#endif
diff --git a/bin/aecho/Makefile b/bin/aecho/Makefile
new file mode 100644 (file)
index 0000000..56f26ae
--- /dev/null
@@ -0,0 +1,48 @@
+SRC = aecho.c
+OBJ = aecho.o
+
+INCPATH=       -I../../include
+CFLAGS=        ${DEFS} ${OPTOPTS} ${INCPATH}
+LIBS=  -latalk ${ADDLIBS}
+TAGSFILE=      tags
+CC=    cc
+INSTALL=       install
+LIBDIRS=       -L../../libatalk
+
+all : aecho
+
+aecho : ${OBJ} ../../libatalk/libatalk.a
+       ${CC} ${CFLAGS} -o aecho ${OBJ} ${LIBDIRS} ${LIBS}
+
+install : all
+       ${INSTALL} -c aecho ${BINDIR}
+
+clean :
+       rm -f a.out core* *.o *.bak *[Ee]rrs tags
+       rm -f aecho
+
+tags : ${SRC}
+       cwd=`pwd`; \
+       for i in ${SRC}; do \
+           ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+       done
+
+depend :
+       for i in ${SRC} ; do \
+           ${CC} -M ${DEFS} ${INCPATH} $$i | \
+           awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+               else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+               else rec = rec " " $$2 } } \
+               END { print rec } ' >> makedep; done
+       sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+       cat makedep >> Makefile.tmp
+       rm makedep
+       echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+       echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+       echo '# see make depend above' >> Makefile.tmp
+       rm -f Makefile.bak
+       cp Makefile Makefile.bak
+       mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
diff --git a/bin/aecho/aecho.c b/bin/aecho/aecho.c
new file mode 100644 (file)
index 0000000..dc88e31
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+/*
+ * AppleTalk Echo Protocol Client
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <netdb.h>
+
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <atalk/compat.h>
+#include <atalk/aep.h>
+#include <atalk/nbp.h>
+#include <atalk/netddp.h>
+#include <atalk/ddp.h>
+
+/* FIXME/SOCKLEN_T: socklen_t is a unix98 feature */
+#ifndef SOCKLEN_T
+#define SOCKLEN_T unsigned int
+#endif
+
+struct sockaddr_at     target;
+int                    s, nsent = 0, nrecv = 0;
+time_t                 totalms = 0, minms = -1, maxms = -1;
+static unsigned int     pings = 0;
+
+#if !defined( ibm032 ) && !defined( _IBMR2 )
+    void
+#endif ibm032 _IBMR2
+done()
+{
+    if ( nsent > 0 ) {
+       printf( "\n----%d.%d AEP Statistics----\n",
+               ntohs( target.sat_addr.s_net ), target.sat_addr.s_node );
+       printf( "%d packets sent, %d packets received, %d%% packet loss\n",
+               nsent, nrecv, (( nsent - nrecv ) * 100 ) / nsent );
+       if ( nrecv > 0 ) {
+           printf( "round-trip (ms)  min/avg/max = %d/%d/%d\n",
+                   minms, totalms / nrecv, maxms );
+       }       
+    }
+    exit( 0 );
+}
+  
+#if !defined( ibm032 ) && !defined( _IBMR2 )
+    void
+#endif ibm032 _IBMR2
+aep_send()
+{
+    struct timeval     tv;
+    char               *p, buf[ 1024 ];
+    static unsigned int        seq = 0;
+
+    p = buf;
+    *p++ = DDPTYPE_AEP;
+    *p++ = AEPOP_REQUEST;
+    bcopy( &seq, p, sizeof( unsigned int ));
+    p += sizeof( unsigned int );
+    seq++;
+
+    if ( gettimeofday( &tv, (struct timezone *)0 ) < 0 ) {
+       perror( "gettimeofday" );
+       exit( 1 );
+    }
+    bcopy( &tv, p, sizeof( struct timeval ));
+    p += sizeof( struct timeval );
+
+    if ( netddp_sendto( s, buf, p - buf, 0, (struct sockaddr *) &target,
+           sizeof( struct sockaddr_at )) < 0 ) {
+       perror( "sendto" );
+       exit( 1 );
+    }
+    nsent++;
+    if (pings && nsent > pings) done();
+}
+
+main( ac, av )
+    int                ac;
+    char       **av;
+{
+    struct servent     *se;
+    struct sigaction   sv;
+    struct itimerval   it;
+    struct sockaddr_at sat, saddr;
+    struct timeval     tv, atv;
+    struct nbpnve      nn;
+    char               *obj = NULL, *type = "Workstation", *zone = "*";
+    int                        cc;
+    SOCKLEN_T          satlen;
+    unsigned int       seq;
+    time_t             ms;
+    char               buf[ 1024 ], *p;
+    unsigned char      port;
+
+    extern char                *optarg;
+    extern int         optind;
+  
+    memset(&saddr, 0, sizeof(saddr));
+    while (( cc = getopt( ac, av, "c:A:" )) != EOF ) {
+       switch ( cc ) {
+       case 'A':
+           if (!atalk_aton(optarg, &saddr.sat_addr)) {
+               fprintf(stderr, "Bad address.\n");
+               exit(1);
+           }
+         case 'c' :
+           pings = atoi( optarg );
+           break;
+
+       default :
+           usage( av[ 0 ] );
+           exit( 1 );
+       }
+    }
+    if ( ac - optind != 1 ) {
+       usage( av[ 0 ] );
+       exit( 1 );
+    }
+    
+    /*
+     * Save the port, since nbp_lookup calls getservbyname() to get the
+     * nbp port.
+     */
+    if (( se = getservbyname( "echo", "ddp" )) == NULL ) 
+       port = 4;
+    else
+       port = ntohs( se->s_port );
+
+    memset( &target, 0, sizeof( struct sockaddr_at ));
+#ifdef BSD4_4
+    target.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+    target.sat_family = AF_APPLETALK;
+    if ( !atalk_aton( av[ optind ], &target.sat_addr )) {
+       if ( nbp_name( av[ optind ], &obj, &type, &zone ) || !obj ) {
+           fprintf( stderr, "Bad name: %s\n", av[ optind ] );
+           exit( 1 );
+       }
+       if ( nbp_lookup( obj, type, zone, &nn, 1, &saddr.sat_addr) <= 0 ) {
+           fprintf( stderr, "Can't find: %s\n", av[ optind ] );
+           exit( 1 );
+       }
+       memcpy( &target, &nn.nn_sat, sizeof( struct sockaddr_at ));
+    }
+    target.sat_port = port;
+
+    if ((s = netddp_open(saddr.sat_addr.s_net || saddr.sat_addr.s_node ? 
+                        &saddr : NULL, NULL)) < 0) {
+       perror("ddp_open");
+       exit(1);
+    }        
+
+    sv.sa_handler = aep_send;
+    sigemptyset( &sv.sa_mask );
+    sigaddset( &sv.sa_mask, SIGINT );
+    sv.sa_flags = SA_RESTART;
+    if ( sigaction( SIGALRM, &sv, (struct sigaction *)0 ) < 0 ) {
+       perror( "sigaction" );
+       exit( 1 );
+    }
+
+    sv.sa_handler = done;
+    sigemptyset( &sv.sa_mask );
+    sigaddset( &sv.sa_mask, SIGALRM );
+    sv.sa_flags = SA_RESTART;
+    if ( sigaction( SIGINT, &sv, (struct sigaction *)0 ) < 0 ) {
+       perror( "sigaction" );
+       exit( 1 );
+    }
+
+    it.it_interval.tv_sec = 1L;
+    it.it_interval.tv_usec = 0L;
+    it.it_value.tv_sec = 1L;
+    it.it_value.tv_usec = 0L;
+
+    if ( setitimer( ITIMER_REAL, &it, (struct itimerval *)0 ) < 0 ) {
+       perror( "setitimer" );
+       exit( 1 );
+    }
+
+    for (;;) {
+       satlen = sizeof( struct sockaddr_at );
+       if (( cc = netddp_recvfrom( s, buf, sizeof( buf ), 0, 
+                                   (struct sockaddr *) &sat,
+                                   &satlen )) < 0 ) {
+           if ( errno == EINTR ) {
+               errno = 0;
+               continue;
+           } else {
+               perror( "recvfrom" );
+               exit( 1 );
+           }
+       }
+       p = buf;
+       if ( *p++ != DDPTYPE_AEP || *p++ != AEPOP_REPLY ) {
+           fprintf( stderr, "%s: bad packet!\n", av[ 0 ] );
+           continue;
+       }
+       if ( gettimeofday( &tv, (struct timezone *)0 ) < 0 ) {
+           perror( "gettimeofday" );
+           exit( 1 );
+       }
+       bcopy( p, &seq, sizeof( unsigned int ));
+       p += sizeof( unsigned int );
+       bcopy( p, &atv, sizeof( struct timeval ));
+       nrecv++;
+       ms = ( tv.tv_sec - atv.tv_sec ) * 1000 +
+               ( tv.tv_usec - atv.tv_usec ) / 1000;
+       totalms += ms;
+       if ( ms > maxms ) {
+           maxms = ms;
+       }
+       if ( ms < minms || minms == -1 ) {
+           minms = ms;
+       }
+       printf( "%d bytes from %u.%u: aep_seq=%d. time=%d. ms\n",
+               cc, ntohs( sat.sat_addr.s_net ), sat.sat_addr.s_node,
+               seq, ms );
+        if (pings && seq + 1 >= pings) done();
+    }
+}
+
+usage( av0 )
+{
+    fprintf( stderr, "usage:\t%s [-A source address ] [-c count] ( addr | nbpname )\n", av0 );
+    exit( 1 );
+}
diff --git a/bin/afppasswd/Makefile b/bin/afppasswd/Makefile
new file mode 100644 (file)
index 0000000..b4e417c
--- /dev/null
@@ -0,0 +1,83 @@
+SRC = afppasswd.c
+OBJ = afppasswd.o
+
+INCPATH=       -I../../include
+CFLAGS=        ${DEFS} ${OPTOPTS} ${INCPATH} ${CRYPTOINCPATH} ${CRYPTODEFS} \
+       ${CRACKINCPATH} ${CRACKDEFS}
+LIBS=  ${ADDLIBS} ${CRYPTOLIBS} ${CRACKLIBS}
+TAGSFILE=      tags
+CC=    cc
+INSTALL=       install
+LIBDIRS=       ${CRYPTOLIBDIRS} ${CRACKLIBDIRS}
+
+all::
+       if [ x"${DESDIR}" != x ]; then \
+           CRYPTOLIBS="-ldes"; \
+           if [ "${DESDIR}" != "/usr" ]; then \
+             CRYPTOLIBDIRS="-L${DESDIR}/lib"; \
+             CRYPTOINCPATH="-I${DESDIR}/include"; \
+           fi; \
+           CRYPTODEFS="-DUAM_RNDNUM"; \
+       fi; \
+       if [ x"${CRYPTODIR}" != x ]; then \
+           CRYPTOLIBS="-lcrypto"; \
+           if [ "${CRYPTODIR}" != "/usr" ]; then \
+             CRYPTOLIBDIRS="-L${CRYPTODIR}/lib"; \
+             CRYPTOINCPATH="-I${CRYPTODIR}/include -I${CRYPTODIR}/include/openssl"; \
+           fi; \
+           CRYPTODEFS="-DUAM_RNDNUM"; \
+       fi; \
+       if [ x"${CRACKDIR}" != x ]; then \
+           CRACKLIBS="-lcrack"; \
+           if [ "${CRACKDIR}" != "/usr" ]; then \
+             CRACKLIBDIRS="-L${CRACKDIR}/lib"; \
+             CRACKINCPATH="-I${CRACKDIR}/include"; \
+           fi; \
+           CRACKDEFS="-DUSE_CRACKLIB -D_PATH_CRACKLIB=\\\"/usr/lib/cracklib_dict\\\""; \
+       fi; \
+       ${MAKE} ${MFLAGS} CC="${CC}" ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" \
+           OPTOPTS="${OPTOPTS}" DESDIR="${DESDIR}" \
+           CRYPTOLIBS="$${CRYPTOLIBS}" CRYPTOLIBDIRS="$${CRYPTOLIBDIRS}" \
+           CRYPTLIB="$${CRYPTLIB}" \
+           CRYPTOINCPATH="$${CRYPTOINCPATH}" CRYPTODEFS="$${CRYPTODEFS}" \
+           CRACKLIBS="$${CRACKLIBS}" \
+           CRACKINCPATH="$${CRACKINCPATH}" CRACKDEFS="$${CRACKDEFS}" \
+           afppasswd
+
+afppasswd.o: afppasswd.c
+       ${CC} ${CFLAGS} -D_PATH_AFPDPWFILE=\"${RESDIR}/afppasswd\" -c $<
+afppasswd : ${OBJ} 
+       ${CC} ${CFLAGS} -o afppasswd ${OBJ} ${LIBDIRS} ${LIBS}
+
+install : all
+       ${INSTALL} -c afppasswd ${BINDIR}
+
+clean :
+       rm -f a.out core* *.o *.bak *[Ee]rrs tags
+       rm -f afppasswd
+
+tags : ${SRC}
+       cwd=`pwd`; \
+       for i in ${SRC}; do \
+           ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+       done
+
+depend :
+       for i in ${SRC} ; do \
+           ${CC} -M ${DEFS} ${INCPATH} $$i | \
+           awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+               else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+               else rec = rec " " $$2 } } \
+               END { print rec } ' >> makedep; done
+       sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+       cat makedep >> Makefile.tmp
+       rm makedep
+       echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+       echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+       echo '# see make depend above' >> Makefile.tmp
+       rm -f Makefile.bak
+       cp Makefile Makefile.bak
+       mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
diff --git a/bin/afppasswd/afppasswd.c b/bin/afppasswd/afppasswd.c
new file mode 100644 (file)
index 0000000..ae47e89
--- /dev/null
@@ -0,0 +1,329 @@
+/* 
+ * Copyright 1999 (c) Adrian Sun (asun@u.washington.edu)
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ * format of the password file:
+ * name:****************:****************:********
+ *      password         last login date  failed usage count
+ *
+ * ***'s are illegal. they're just place holders for hex values. hex
+ * values that represent actual numbers are in network byte order.
+ *
+ * last login date is currently a 4-byte number representing seconds
+ * since 1970 (UTC). there's enough space to extend it to 8 bytes.
+ *
+ * the last two fields aren't currently used by the randnum uams.
+ *
+ * root syntax: afppasswd [-c] [-a] [-p path] [-f] [username]
+ * user syntax: afppasswd 
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <pwd.h>
+
+#include <netatalk/endian.h>
+
+#ifdef UAM_RNDNUM
+#include <des.h>
+
+#ifdef USE_CRACKLIB
+#include <crack.h>
+#endif
+
+#define OPT_ISROOT  (1 << 0)
+#define OPT_CREATE  (1 << 1)
+#define OPT_FORCE   (1 << 2)
+#define OPT_ADDUSER (1 << 3)
+
+#define PASSWD_ILLEGAL '*'
+
+#define FORMAT  ":****************:****************:********\n"
+#define FORMAT_LEN 44
+#define OPTIONS "cafu:p:"
+#define UID_START 100
+
+#define HEXPASSWDLEN 16
+#define PASSWDLEN 8
+
+static char buf[MAXPATHLEN + 1];
+
+/* if newpwd is null, convert buf from hex to binary. if newpwd isn't
+ * null, convert newpwd to hex and save it in buf. */
+#define unhex(x)  (isdigit(x) ? (x) - '0' : toupper(x) + 10 - 'A')
+static void convert_passwd(char *buf, char *newpwd, const int keyfd)
+{
+  u_int8_t key[HEXPASSWDLEN];
+  Key_schedule schedule;
+  int i, j;
+
+  if (!newpwd) {
+    /* convert to binary */
+    for (i = j = 0; i < sizeof(key); i += 2, j++)
+      buf[j] = (unhex(buf[i]) << 4) | unhex(buf[i + 1]);
+    if (j <= DES_KEY_SZ)
+      memset(buf + j, 0, sizeof(key) - j);
+  }
+
+  if (keyfd > -1) {
+    lseek(keyfd, 0, SEEK_SET);
+    read(keyfd, key, sizeof(key));
+    /* convert to binary */
+    for (i = j = 0; i < sizeof(key); i += 2, j++)
+      key[j] = (unhex(key[i]) << 4) | unhex(key[i + 1]);
+    if (j <= DES_KEY_SZ)
+      memset(key + j, 0, sizeof(key) - j);
+    key_sched((C_Block *) key, schedule);
+    memset(key, 0, sizeof(key));   
+    if (newpwd) {
+       ecb_encrypt((C_Block *) newpwd, (C_Block *) newpwd, schedule,
+                   DES_ENCRYPT);
+    } else {
+      /* decrypt the password */
+      ecb_encrypt((C_Block *) buf, (C_Block *) buf, schedule, DES_DECRYPT);
+    }
+    memset(schedule, 0, sizeof(schedule));      
+  }
+
+  if (newpwd) {
+    const unsigned char hextable[] = "0123456789ABCDEF";
+
+    /* convert to hex */
+    for (i = j = 0; i < DES_KEY_SZ; i++, j += 2) {
+      buf[j] = hextable[(newpwd[i] & 0xF0) >> 4];
+      buf[j + 1] = hextable[newpwd[i] & 0x0F];
+    }
+  }
+}
+
+/* this matches the code in uam_randnum.c */
+static int update_passwd(const char *path, const char *name, int flags)
+{
+  char password[PASSWDLEN + 1], *p, *passwd;
+  FILE *fp;
+  off_t pos;
+  int keyfd = -1, err = 0;
+
+  if ((fp = fopen(path, "r+")) == NULL) {
+    fprintf(stderr, "afppasswd: can't open %s\n", path);
+    return -1;
+  }
+
+  /* open the key file if it exists */
+  strcpy(buf, path);
+  if (strlen(path) < sizeof(buf) - 5) {
+    strcat(buf, ".key");
+    keyfd = open(buf, O_RDONLY);
+  } 
+
+  pos = ftell(fp);
+  memset(buf, 0, sizeof(buf));
+  while (fgets(buf, sizeof(buf), fp)) {
+    if ((p = strchr(buf, ':'))) {
+      /* check for a match */
+      if (strncmp(buf, name, p - buf) == 0) {
+       p++;
+       if (!(flags & OPT_ISROOT) && (*p == PASSWD_ILLEGAL)) {
+         fprintf(stderr, "Your password is disabled. Please see your administrator.\n");
+         break;
+       }
+       goto found_entry;
+      }
+    }
+    pos = ftell(fp);
+    memset(buf, 0, sizeof(buf));
+  }
+
+  if (flags & OPT_ADDUSER) {
+    strcpy(buf, name);
+    strcat(buf, FORMAT);
+    p = strchr(buf, ':') + 1;
+    fwrite(buf, strlen(buf), 1, fp);
+  } else {
+    fprintf(stderr, "afppasswd: can't find %s in %s\n", name, path);
+    err = -1;
+    goto update_done;
+  }
+
+found_entry:
+  /* need to verify against old password */
+  if ((flags & OPT_ISROOT) == 0) {
+    passwd = getpass("Enter OLD AFP password: ");
+    convert_passwd(p, NULL, keyfd);
+    if (strncmp(passwd, p, PASSWDLEN)) {
+      fprintf(stderr, "afppasswd: invalid password.\n");
+      err = -1;
+      goto update_done;
+    }
+  }
+
+  /* new password */
+  passwd = getpass("Enter NEW AFP password: ");
+  memcpy(password, passwd, sizeof(password));
+  password[PASSWDLEN] = '\0';
+#ifdef USE_CRACKLIB
+  if ((passwd = FascistCheck(password, _PATH_CRACKLIB))) {
+      fprintf(stderr, "Error: %s\n", passwd);
+      err = -1;
+      goto update_done;
+  }
+#endif
+
+  passwd = getpass("Enter NEW AFP password again: ");
+  if (strcmp(passwd, password) == 0) {
+     struct flock lock;
+     int fd = fileno(fp);
+
+     convert_passwd(p, password, keyfd);
+     lock.l_type = F_WRLCK;
+     lock.l_start = pos;
+     lock.l_len = 1;
+     lock.l_whence = SEEK_SET;
+     fseek(fp, pos, SEEK_SET);
+     fcntl(fd, F_SETLKW, &lock);
+     fwrite(buf, p - buf + HEXPASSWDLEN, 1, fp);
+     lock.l_type = F_UNLCK;
+     fcntl(fd, F_SETLK, &lock);
+     printf("afppasswd: updated password.\n");
+
+  } else {
+    fprintf(stderr, "afppasswd: passwords don't match!\n");
+    err = -1;
+  }
+
+update_done:
+  if (keyfd > -1)
+    close(keyfd);
+  fclose(fp);
+  return err;
+}
+
+
+/* creates a file with all the password entries */
+static int create_file(const char *path, uid_t minuid)
+{
+  struct passwd *pwd;
+  int fd, len, err = 0;
+
+  
+  if ((fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0600)) < 0) {
+    fprintf(stderr, "afppasswd: can't create %s\n", path);
+    return -1;
+  }
+
+  setpwent();
+  while ((pwd = getpwent())) {
+    if (pwd->pw_uid < minuid)
+      continue;
+    /* a little paranoia */
+    if (strlen(pwd->pw_name) + FORMAT_LEN > sizeof(buf) - 1)
+      continue;
+    strcpy(buf, pwd->pw_name);
+    strcat(buf, FORMAT);
+    len = strlen(buf);
+    if (write(fd, buf, len) != len) {
+      fprintf(stderr, "afppasswd: problem writing to %s: %m\n", path);
+      err = -1;
+      break;
+    }
+  }
+  endpwent();
+  close(fd);
+
+  return err;
+}
+
+
+int main(int argc, char **argv)
+{
+  struct stat st;
+  int flags;
+  uid_t uid_min = UID_START, uid;
+  char *path = _PATH_AFPDPWFILE;
+  int i, err = 0;
+
+  extern char *optarg;
+  extern int optind, opterr;
+
+  flags = ((uid = getuid()) == 0) ? OPT_ISROOT : 0;
+
+  if (((flags & OPT_ISROOT) == 0) && (argc > 1)) {
+    fprintf(stderr, "Usage: afppasswd\n");
+    return -1;
+  }
+
+  while ((i = getopt(argc, argv, OPTIONS)) != EOF) {
+    switch (i) {
+    case 'c': /* create and initialize password file or specific user */
+      flags |= OPT_CREATE;
+      break;
+    case 'a': /* add a new user */
+      flags |= OPT_ADDUSER;
+    case 'f': /* force an action */
+      flags |= OPT_FORCE;
+      break;
+    case 'u':  /* minimum uid to use. default is 100 */
+      uid_min = atoi(optarg);
+      break;
+    case 'p': /* path to afppasswd file */
+      path = optarg;
+      break;
+    default:
+      err++;
+      break;
+    }
+  }
+
+  if (err || (optind + ((flags & OPT_CREATE) ? 0 : 
+                       (flags & OPT_ISROOT)) != argc)) {
+    fprintf(stderr, "Usage: afppasswd [-acf] [-u minuid] [-p path] [username]\n");
+    return -1;
+  }
+
+  i = stat(path, &st);
+  if (flags & OPT_CREATE) {
+    if ((flags & OPT_ISROOT) == 0) {
+      fprintf(stderr, "afppasswd: only root can create the password file.\n");
+      return -1;
+    }
+
+    if (!i && ((flags & OPT_FORCE) == 0)) {
+      fprintf(stderr, "afppasswd: password file already exists.\n");
+      return -1;
+    }
+    return create_file(path, uid_min);
+
+  } else {
+    struct passwd *pwd = NULL;
+
+    if (i < 0) {
+      fprintf(stderr, "afppasswd: %s doesn't exist.\n", path);
+      return -1;
+    }
+    
+    /* if we're root, we need to specify the username */
+    pwd = (flags & OPT_ISROOT) ? getpwnam(argv[optind]) : getpwuid(uid);
+    if (pwd)
+      return update_passwd(path, pwd->pw_name, flags);
+
+    fprintf(stderr, "afppasswd: can't get password entry.\n");
+    return -1;
+  }
+}
+#else
+
+main(int argc, char **argv)
+{
+  fprintf(stderr, "afppasswd is only useful if you're using centralized passwords\n");
+  fprintf(stderr, "for the Random Number authentication methods.\n");
+  return -1;
+}
+#endif
+
diff --git a/bin/getzones/Makefile b/bin/getzones/Makefile
new file mode 100644 (file)
index 0000000..1902023
--- /dev/null
@@ -0,0 +1,48 @@
+SRC = getzones.c
+OBJ = getzones.o
+
+INCPATH=       -I../../include
+CFLAGS=        ${DEFS} ${OPTOPTS} ${INCPATH}
+LIBS=  -latalk ${ADDLIBS}
+TAGSFILE=      tags
+CC=    cc
+INSTALL=       install
+LIBDIRS=       -L../../libatalk
+
+all : getzones
+
+getzones : ${OBJ} ../../libatalk/libatalk.a
+       ${CC} ${CFLAGS} -o getzones ${OBJ} ${LIBDIRS} ${LIBS}
+
+install : all
+       ${INSTALL} -c getzones ${BINDIR}
+
+clean :
+       rm -f a.out core* *.o *.bak *[Ee]rrs tags
+       rm -f getzones
+
+tags : ${SRC}
+       cwd=`pwd`; \
+       for i in ${SRC}; do \
+           ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+       done
+
+depend :
+       for i in ${SRC} ; do \
+           ${CC} -M ${DEFS} ${INCPATH} $$i | \
+           awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+               else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+               else rec = rec " " $$2 } } \
+               END { print rec } ' >> makedep; done
+       sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+       cat makedep >> Makefile.tmp
+       rm makedep
+       echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+       echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+       echo '# see make depend above' >> Makefile.tmp
+       rm -f Makefile.bak
+       cp Makefile Makefile.bak
+       mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
diff --git a/bin/getzones/getzones.c b/bin/getzones/getzones.c
new file mode 100644 (file)
index 0000000..604f1aa
--- /dev/null
@@ -0,0 +1,138 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <string.h>
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <atalk/atp.h>
+#include <atalk/zip.h>
+
+usage( s )
+    char *s;
+{
+    fprintf( stderr, "usage:\t%s [-m | -l] [address]\n", s );
+    exit( 1 );
+}
+
+main( argc, argv )
+    int                argc;
+    char       *argv[];
+{
+    struct atp_handle  *ah;
+    struct atp_block   atpb;
+    struct sockaddr_at saddr;
+    struct servent     *se;
+    char               reqdata[4], buf[ ATP_MAXDATA ];
+    struct iovec       iov;
+    short              temp, index = 0;
+    int                        c, myzoneflg = 0, localzonesflg = 0, errflg = 0;
+    extern int         optind;
+
+    reqdata[ 0 ] = ZIPOP_GETZONELIST;
+
+    while (( c = getopt( argc, argv, "ml" )) != EOF ) {
+       switch (c) {
+       case 'm':
+           if ( localzonesflg ) {
+               ++errflg;
+           }
+           ++myzoneflg;
+           reqdata[ 0 ] = ZIPOP_GETMYZONE;
+           break;
+       case 'l':
+           if ( myzoneflg ) {
+               ++errflg;
+           }
+           ++localzonesflg;
+           reqdata[ 0 ] = ZIPOP_GETLOCALZONES;
+           break;
+       default:
+           ++errflg;
+       }
+    }
+
+    if ( errflg || argc - optind > 1 ) {
+       usage( argv[ 0 ] );
+    }
+
+    memset( &saddr, 0, sizeof( struct sockaddr_at ));
+#ifdef BSD4_4
+    saddr.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+    saddr.sat_family = AF_APPLETALK;
+    if (( se = getservbyname( "zip", "ddp" )) == NULL )
+       saddr.sat_port = 6;
+    else 
+        saddr.sat_port = ntohs( se->s_port );
+
+    if ( argc == optind ) {
+       saddr.sat_addr.s_net = ATADDR_ANYNET;
+       saddr.sat_addr.s_node = ATADDR_ANYNODE;
+    } else {
+       if ( !atalk_aton( argv[ optind ], &saddr.sat_addr )) {
+           fprintf( stderr, "Bad address.\n" );
+           exit( 1 );
+       }
+    }
+
+    if (( ah = atp_open( ATADDR_ANYPORT, &saddr.sat_addr )) == NULL ) {
+       perror( "atp_open" );
+       exit( 1 );
+    }
+
+    index = ( myzoneflg ? 0 : 1 );
+    reqdata[1] = 0;
+
+    do {
+       atpb.atp_saddr = &saddr;
+       temp = htons( index );
+       memcpy( reqdata + 2, &temp, 2 );
+       atpb.atp_sreqdata = reqdata;
+       atpb.atp_sreqdlen = 4;
+       atpb.atp_sreqto = 2;
+       atpb.atp_sreqtries = 5;
+
+       /* send getzone request zones (or get my zone)
+       */
+       if ( atp_sreq( ah, &atpb, 1, 0 ) < 0 ) {
+           perror( "atp_sreq" );
+           exit( 1 );
+       }
+
+       iov.iov_base = buf;
+       iov.iov_len = ATP_MAXDATA;
+       atpb.atp_rresiov = &iov;
+       atpb.atp_rresiovcnt = 1;
+
+       if ( atp_rresp( ah, &atpb ) < 0 ) {
+           perror( "atp_rresp" );
+           exit( 1 );
+       }
+
+       memcpy( &temp, (char *) iov.iov_base + 2, 2 );
+       temp = ntohs( temp );
+       print_zones( temp, (char *) iov.iov_base+4 );
+       index += temp;
+    } while ( !myzoneflg && !((char *)iov.iov_base)[ 0 ] );
+
+    if ( atp_close( ah ) != 0 ) {
+       perror( "atp_close" );
+       exit( 1 );
+    }
+
+    exit( 0 );
+}
+
+
+print_zones( n, buf )
+    short      n;      /* number of zones in this packet */
+    char       *buf;   /* zone length/name pairs */
+{
+    for ( ; n--; buf += (*buf) + 1 ) {
+       printf( "%.*s\n", *buf, buf+1 );
+    }
+}
diff --git a/bin/megatron/Makefile b/bin/megatron/Makefile
new file mode 100644 (file)
index 0000000..5e4dd0c
--- /dev/null
@@ -0,0 +1,55 @@
+SRC=   hqx.c macbin.c megatron.c nad.c asingle.c updcrc.c
+OBJ=   hqx.o macbin.o megatron.o nad.o asingle.o updcrc.o
+
+INCPATH=       -I../../include
+CFLAGS=        ${DEFS} ${OPTOPTS} ${INCPATH}
+LIBS=  -latalk ${ADDLIBS}
+TAGSFILE=      tags
+CC=    cc
+INSTALL=       install
+LIBDIRS=       -L../../libatalk
+
+LINKS= unbin unhex unsingle hqx2bin single2bin macbinary binheader nadheader
+TARGET=        megatron
+
+all : ${TARGET}
+
+${TARGET} : ${OBJ}
+       ${CC} ${CFLAGS} -o ${TARGET} ${OBJ} ${LIBDIRS} ${LIBS}
+
+install : all
+       ${INSTALL} -c ${TARGET} ${BINDIR}
+       for i in ${LINKS} ; do \
+           rm -f ${BINDIR}/$$i; \
+           ln -s ${TARGET} ${BINDIR}/$$i; \
+       done
+
+clean :
+       rm -f a.out core* *.o *.bak *[Ee]rrs tags
+       rm -f ${TARGET}
+
+tags : ${SRC}
+       cwd=`pwd`; \
+       for i in ${SRC}; do \
+           ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+       done
+
+depend :
+       for i in ${SRC} ; do \
+           ${CC} -M ${DEFS} ${INCPATH} $$i | \
+           awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+               else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+               else rec = rec " " $$2 } } \
+               END { print rec } ' >> makedep; done
+       sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+       cat makedep >> Makefile.tmp
+       rm makedep
+       echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+       echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+       echo '# see make depend above' >> Makefile.tmp
+       rm -f Makefile.bak
+       cp Makefile Makefile.bak
+       mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
diff --git a/bin/megatron/asingle.c b/bin/megatron/asingle.c
new file mode 100644 (file)
index 0000000..bf0c0b3
--- /dev/null
@@ -0,0 +1,446 @@
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <string.h>
+#include <syslog.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <atalk/adouble.h>
+#include <netatalk/endian.h>
+#include "megatron.h"
+
+#define        DEBUG           0
+
+/*     String used to indicate standard input instead of a disk
+       file.  Should be a string not normally used for a file
+ */
+#ifndef        STDIN
+#      define  STDIN   "-"
+#endif
+
+/*     Yes and no
+ */
+#define NOWAY          0
+#define SURETHANG      1
+
+/*     This structure holds an entry description, consisting of three
+       four byte entities.  The first is the Entry ID, the second is
+       the File Offset and the third is the Length.
+ */
+
+
+/*     Both input and output routines use this struct and the
+       following globals; therefore this module can only be used
+       for one of the two functions at a time.
+ */
+struct single_file_data {
+    int                        filed;
+    char               path[ MAXPATHLEN + 1];
+    struct ad_entry    entry[ ADEID_MAX ];
+}              single;
+
+extern char    *forkname[];
+u_char         header_buf[ AD_HEADER_LEN ];
+
+/* 
+ * single_open must be called first.  pass it a filename that is supposed
+ * to contain a AppleSingle file.  an single struct will be allocated and
+ * somewhat initialized; single_filed is set.
+ */
+
+single_open( singlefile, flags, fh, options )
+    char               *singlefile;
+    int                        flags, options;
+    struct FHeader     *fh;
+{
+    int                        rc;
+
+    if ( flags == O_RDONLY ) {
+       if ( strcmp( singlefile, STDIN ) == 0 ) {
+           single.filed = fileno( stdin );
+       } else if (( single.filed = open( singlefile, flags )) < 0 ) {
+           perror( singlefile );
+           return( -1 );
+       }
+       strncpy( single.path, singlefile, MAXPATHLEN );
+#if DEBUG
+       fprintf( stderr, "opened %s for read\n", single.path );
+#endif
+       if ((( rc = single_header_test()) > 0 ) && 
+               ( single_header_read( fh, rc ) == 0 )) {
+           return( 0 );
+       }
+       single_close( KEEP );
+       return( -1 );
+    }
+}
+
+/* 
+ * single_close must be called before a second file can be opened using
+ * single_open.  Upon successful completion, a value of 0 is returned.  
+ * Otherwise, a value of -1 is returned.
+ */
+
+single_close( keepflag )
+    int                        keepflag;
+{
+    if ( keepflag == KEEP ) {
+       return( close( single.filed ));
+    } else if ( keepflag == TRASH ) {
+       if (( strcmp( single.path, STDIN ) != 0 ) && 
+               ( unlink( single.path ) < 0 )) {
+           perror ( single.path );
+       }
+       return( 0 );
+    } else return( -1 );
+}
+
+/* 
+ * single_header_read is called by single_open, and before any information
+ * can read from the fh substruct.  it must be called before any of the
+ * bytes of the other two forks can be read, as well.
+ */
+
+single_header_read( fh, version )
+    struct FHeader     *fh;
+    int                        version;
+{
+/*
+ * entry_buf is used for reading in entry descriptors, and for reading in
+ *     the actual entries of FILEINFO, FINDERINFO, and DATES.
+ */
+    u_char             entry_buf[ 16 ];
+    u_int32_t          entry_id;
+    u_int32_t          time_seconds;
+    u_short            mask = 0xfcee;
+    u_short            num_entries;
+    int                        n;
+    int                        readlen;
+    int                        date_entry;
+    off_t              pos;
+
+/*
+ * Go through and initialize the array of entry_info structs.  Read in the
+ * number of entries, and then read in the info for each entry and save it
+ * in the array.
+ */
+
+    for ( n = 0 ; n < ADEID_MAX; n++ ) {
+       single.entry[ n ].ade_off = 0;
+       single.entry[ n ].ade_len = 0;
+    }
+    memcpy( &num_entries, header_buf + 24, sizeof( num_entries ));
+    num_entries = ntohs( num_entries );
+#if DEBUG >= 2
+    fprintf( stderr, "The number of entries is %d\n", num_entries );
+#endif
+    for ( ; num_entries > 0 ; num_entries-- ) {
+       if ( read( single.filed, (char *)entry_buf, AD_ENTRY_LEN )
+               != AD_ENTRY_LEN ) {
+           perror( "Premature end of file :" );
+           return( -1 );
+       }
+       memcpy(&entry_id,  entry_buf, sizeof( entry_id ));
+       entry_id = ntohl( entry_id );
+       memcpy(&single.entry[ entry_id ].ade_off,
+              entry_buf + 4,
+              sizeof( single.entry[ entry_id ].ade_off ));
+       single.entry[ entry_id ].ade_off =
+               ntohl( single.entry[ entry_id ].ade_off );
+       memcpy(&single.entry[ entry_id ].ade_len,
+              entry_buf + 8,
+              sizeof( single.entry[ entry_id ].ade_len ));
+       single.entry[ entry_id ].ade_len =
+               ntohl( single.entry[ entry_id ].ade_len );
+#if DEBUG >= 2
+       fprintf( stderr, "entry_id\t%d\n", entry_id );
+       fprintf( stderr, "\toffset\t\t%d\n", single.entry[ entry_id ].ade_off );
+       fprintf( stderr, "\tlength\t\t%d\n", single.entry[ entry_id ].ade_len );
+#endif
+    }
+
+/*
+ * Now that the entries have been identified, check to make sure
+ * it is a Macintosh file if dealing with version two format file.
+ */
+
+    if ( version == 1 ) {
+       if ( single.entry[ ADEID_FILEI ].ade_len > 0 ) {
+           date_entry = ADEID_FILEI;
+       } else {
+           date_entry = 0;
+       }
+    } else if ( version == 2 ) {
+       if ( single.entry[ ADEID_FILEDATESI ].ade_len > 0 ) {
+           date_entry = ADEID_FILEDATESI;
+       } else {
+           date_entry = 0;
+       }
+    }
+#if DEBUG
+    fprintf( stderr, "date_entry = %d\n", date_entry );
+#endif
+
+/*
+ * Go through and copy all the information you can get from 
+ * the informational entries into the fh struct.  The ENTRYID_DATA
+ * must be the last one done, because it leaves the file pointer in
+ * the right place for the first read of the data fork.
+ */
+    if ( single.entry[ ADEID_NAME ].ade_off == 0 ) {
+       fprintf( stderr, "%s has no name for the mac file.\n", single.path );
+       return( -1 );
+    } else {
+       pos = lseek( single.filed, single.entry[ ADEID_NAME ].ade_off,
+               SEEK_SET );
+       readlen = single.entry[ ADEID_NAME ].ade_len > ADEDLEN_NAME ? 
+             ADEDLEN_NAME : single.entry[ ADEID_NAME ].ade_len;
+       if ( read( single.filed, (char *)fh->name, readlen ) != readlen ) {
+           perror( "Premature end of file :" );
+           return( -1 );
+       }
+    }
+    if (( single.entry[ ADEID_FINDERI ].ade_len < 16 ) ||
+           ( single.entry[ ADEID_FINDERI ].ade_off <= AD_HEADER_LEN )) {
+       fprintf( stderr, "%s has bogus FinderInfo.\n", single.path );
+       return( -1 );
+    } else {
+       pos = lseek( single.filed,
+               single.entry[ ADEID_FINDERI ].ade_off, SEEK_SET );
+       if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
+               sizeof( entry_buf )) {
+           perror( "Premature end of file :" );
+           return( -1 );
+       }
+       memcpy( &fh->finder_info.fdType, entry_buf + FINDERIOFF_TYPE,   
+               sizeof( fh->finder_info.fdType ));
+       memcpy( &fh->finder_info.fdCreator, entry_buf + FINDERIOFF_CREATOR,
+               sizeof( fh->finder_info.fdCreator ));
+       memcpy( &fh->finder_info.fdFlags, entry_buf +  FINDERIOFF_FLAGS,
+               sizeof( fh->finder_info.fdFlags ));
+       fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask;
+       memcpy( &fh->finder_info.fdLocation, entry_buf + FINDERIOFF_LOC,
+               sizeof( fh->finder_info.fdLocation ));
+       memcpy(&fh->finder_info.fdFldr, entry_buf + FINDERIOFF_FLDR,
+               sizeof( fh->finder_info.fdFldr ));
+       fh->finder_xinfo.fdScript = *(entry_buf + FINDERIOFF_SCRIPT);
+       fh->finder_xinfo.fdXFlags = *(entry_buf + FINDERIOFF_XFLAGS);
+
+#if DEBUG
+       {
+           char                type[5];
+           char                creator[5];
+           strncpy( type, &fh->finder_info.fdType, 4 );
+           strncpy( creator, &fh->finder_info.fdCreator, 4 );
+           type[4] = creator[4] = '\0';
+           fprintf( stderr, "type is %s, creator is %s\n", type, creator );
+       }
+#endif
+    }
+    if (( single.entry[ ADEID_COMMENT ].ade_len == 0 ) || 
+           ( single.entry[ ADEID_COMMENT ].ade_off <= AD_HEADER_LEN )) {
+       fh->comment[0] = '\0';
+    } else {
+       pos = lseek( single.filed, single.entry[ ADEID_COMMENT ].ade_off,
+               SEEK_SET );
+       readlen = single.entry[ ADEID_COMMENT ].ade_len > ADEDLEN_COMMENT
+               ? ADEDLEN_COMMENT : single.entry[ ADEID_COMMENT ].ade_len;
+       if ( read( single.filed, (char *)fh->comment, readlen ) != readlen ) {
+           perror( "Premature end of file :" );
+           return( -1 );
+       }
+    }
+/*
+ * If date_entry is 7, we have an AppleSingle version one, do the 
+ * appropriate stuff.  If it is 8, we have an AppleSingle version two,
+ * do the right thing.  If date_entry is neither, just use the current date.
+ * Unless I can't get the current date, in which case use time zero.
+ */
+    if (( date_entry < 7 ) || ( date_entry > 8 )) {
+       if (( time_seconds = time( NULL )) == -1 ) {
+           time_seconds = AD_DATE_START;
+       } else {
+           time_seconds = AD_DATE_FROM_UNIX(time_seconds);
+       }
+       memcpy(&fh->create_date, &time_seconds, sizeof( fh->create_date ));
+       memcpy(&fh->mod_date, &time_seconds, sizeof( fh->mod_date ));
+       fh->backup_date = AD_DATE_START;
+    } else if ( single.entry[ date_entry ].ade_len != 16 ) {
+       fprintf( stderr, "%s has bogus FileInfo or File Dates Info.\n", 
+               single.path );
+       return( -1 );
+    } else if ( date_entry == ADEID_FILEI ) {
+       pos = lseek( single.filed,
+               single.entry[ date_entry ].ade_off, SEEK_SET );
+       if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
+               sizeof( entry_buf )) {
+           perror( "Premature end of file :" );
+           return( -1 );
+       }
+       memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
+               sizeof( fh->create_date ));
+       memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
+               sizeof( fh->mod_date ));
+       memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
+               sizeof(fh->backup_date));
+    } else if ( date_entry == ADEID_FILEDATESI ) {
+       pos = lseek( single.filed,
+               single.entry[ date_entry ].ade_off, SEEK_SET );
+       if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
+               sizeof( entry_buf )) {
+           perror( "Premature end of file :" );
+           return( -1 );
+       }
+       memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
+               sizeof( fh->create_date ));
+       memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
+               sizeof( fh->mod_date ));
+       memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
+               sizeof(fh->backup_date));
+    }
+    if ( single.entry[ ADEID_RFORK ].ade_off == 0 ) {
+       fh->forklen[ ADEID_RFORK ] = 0;
+    } else {
+       fh->forklen[ ADEID_RFORK ] =
+               htonl( single.entry[ ADEID_RFORK ].ade_len );
+    }
+    if ( single.entry[ ADEID_DFORK ].ade_off == 0 ) {
+       fh->forklen[ DATA ] = 0;
+    } else {
+       fh->forklen[ DATA ] = htonl( single.entry[ ADEID_DFORK ].ade_len );
+       pos = lseek( single.filed, single.entry[ ADEID_DFORK ].ade_off, SEEK_SET );
+    }
+
+    return( 0 );
+}
+
+/*
+ * single_header_test is called from single_open.  It checks certain
+ * values of the file and determines if the file is an AppleSingle version
+ * one file something else, and returns a one, or negative one to indicate
+ * file type.
+ *
+ * The Magic Number of the file, the first four bytes, must be hex
+ * 0x00051600.  Bytes 4 through 7 are the version number and must be hex
+ * 0x00010000.  Bytes 8 through 23 identify the home file system, and we
+ * are only interested in files from Macs.  Therefore these bytes must
+ * contain hex 0x4d6163696e746f736820202020202020 which is ASCII
+ * "Macintosh       " (that is seven blanks of padding).
+ */
+#define MACINTOSH      "Macintosh       "
+u_char         sixteennulls[] = { 0, 0, 0, 0, 0, 0, 0, 0,
+                                   0, 0, 0, 0, 0, 0, 0, 0 };
+
+single_header_test()
+{
+    int                        cc;
+    u_int32_t          templong;
+
+    cc = read( single.filed, (char *)header_buf, sizeof( header_buf ));
+    if ( cc < sizeof( header_buf )) {
+       perror( "Premature end of file :" );
+       return( -1 );
+    }
+
+    memcpy( &templong, header_buf, sizeof( templong ));
+    if ( ntohl( templong ) != AD_APPLESINGLE_MAGIC ) {
+       fprintf( stderr, "%s is not an AppleSingle file.\n", single.path );
+       return( -1 );
+    }
+
+    memcpy(&templong,  header_buf +  4, sizeof( templong ));
+    templong = ntohl( templong );
+    if ( templong == AD_VERSION1 ) {
+       cc = 1;
+       if ( memcmp( MACINTOSH, header_buf + 8, sizeof( MACINTOSH ) - 1 ) 
+               != 0 ) {
+           fprintf( stderr, "%s is not a Macintosh AppleSingle file.\n", 
+                   single.path );
+           return( -1 );
+       }
+    } else if ( templong == AD_VERSION2 ) {
+       cc = 2;
+       if ( memcmp( sixteennulls, header_buf + 8, sizeof( sixteennulls ))
+               != 0 ) {
+           fprintf( stderr, 
+                   "Warning:  %s may be a corrupt AppleSingle file.\n",
+                   single.path );
+           return( -1 );
+       }
+    } else {
+       fprintf( stderr, "%s is a version of AppleSingle I don't understand!\n",
+               single.path );
+       return( -1 );
+    }
+
+    return( cc );
+}
+
+/*
+ * single_read is called until it returns zero for each fork.  When
+ * it returns zero for the first fork, it seeks to the proper place
+ * to read in the next, if there is one.  single_read must be called
+ * enough times to return zero for each fork and no more.
+ *
+ */
+
+single_read( fork, buffer, length )
+    int                        fork;
+    char               *buffer;
+    int                        length;
+{
+    u_int32_t          entry_id;
+    char               *buf_ptr;
+    int                        readlen;
+    int                        cc = 1;
+    off_t              pos;
+
+    switch ( fork ) {
+       case DATA :
+           entry_id = ADEID_DFORK;
+           break;
+       case RESOURCE :
+           entry_id = ADEID_RFORK;
+           break;
+       default :
+           return( -1 );
+           break;
+    }
+
+    if ( single.entry[ entry_id ].ade_len < 0 ) {
+       fprintf( stderr, "single_read: Trying to read past end of fork!\n" );
+       return( single.entry[ entry_id ].ade_len );
+    }
+    if ( single.entry[ entry_id ].ade_len == 0 ) {
+       if ( fork == DATA ) {
+           pos = lseek( single.filed,
+               single.entry[ ADEID_RFORK ].ade_off, SEEK_SET );
+       }
+       return( 0 );
+    }
+
+    if ( single.entry[ entry_id ].ade_len < length ) {
+       readlen = single.entry[ entry_id ].ade_len;
+    } else {
+       readlen = length;
+    }
+
+    buf_ptr = buffer;
+    while (( readlen > 0 ) && ( cc > 0 )) {
+       if (( cc = read( single.filed, buf_ptr, readlen )) > 0 ) {
+           readlen -= cc;
+           buf_ptr += cc;
+       }
+    }
+    if ( cc >= 0 ) {
+       cc = buf_ptr - buffer;
+       single.entry[ entry_id ].ade_len -= cc;
+    }
+
+    return( cc );
+}
diff --git a/bin/megatron/hqx.c b/bin/megatron/hqx.c
new file mode 100644 (file)
index 0000000..5ab164f
--- /dev/null
@@ -0,0 +1,692 @@
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#ifdef notdef
+#if BSD >= 199006
+# include <machine/endian.h>
+#else
+# include <netinet/in.h>
+#endif
+#endif notdef
+#include <fcntl.h>
+#include <string.h>
+#include <syslog.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <atalk/adouble.h>
+#include <netatalk/endian.h>
+#include "megatron.h"
+
+#define        DEBUG           0
+#define HEXOUTPUT      0
+
+/*     String used to indicate standard input instead of a disk
+       file.  Should be a string not normally used for a file
+ */
+#ifndef        STDIN
+#      define  STDIN   "-"
+#endif
+
+/*     Yes and no
+ */
+#define NOWAY          0
+#define SURETHANG      1
+
+/*     Looking for the first or any other line of a binhex file
+ */
+#define        FIRST           0
+#define OTHER          1
+
+/*     This is the binhex run length encoding character
+ */
+#define RUNCHAR                0x90
+
+/*     These are field sizes in bytes of various pieces of the
+       binhex header
+ */
+#define        VERSION         1
+#define        TCSIZ           8
+#define        FLAGSIZ         2
+#define        DATASIZ         4
+#define        RESSIZ          4
+#define CRCSIZ         2
+#define HEADSIZ                21
+
+u_short                updcrc();
+
+#if HEXOUTPUT
+FILE           *rawhex, *expandhex;
+#endif
+
+struct hqx_file_data {
+    u_int32_t          forklen[ NUMFORKS ];
+    u_short            forkcrc[ NUMFORKS ];
+    char               path[ MAXPATHLEN + 1];
+    u_short            headercrc;
+    int                        filed;
+}              hqx;
+
+extern char    *forkname[];
+u_char         hqx7_buf[8192];
+u_char         *hqx7_first;
+u_char         *hqx7_last;
+int            first_flag;
+
+/* 
+hqx_open must be called first.  pass it a filename that is supposed
+to contain a binhqx file.  an hqx struct will be allocated and
+somewhat initialized; hqx_fd is set.  skip_junk is called from
+here; skip_junk leaves hqx7_first and hqx7_last set.
+ */
+
+hqx_open( hqxfile, flags, fh, options )
+    char               *hqxfile;
+    int                        flags, options;
+    struct FHeader     *fh;
+{
+    int                        maxlen;
+
+#if DEBUG
+    fprintf( stderr, "megatron: entering hqx_open\n" );
+#endif
+    if ( flags == O_RDONLY ) {
+
+#if HEXOUTPUT
+       rawhex = fopen( "rawhex.unhex", "w" );
+       expandhex = fopen( "expandhex.unhex", "w" );
+#endif
+
+       first_flag = 0;
+
+       if ( strcmp( hqxfile, STDIN ) == 0 ) {
+           hqx.filed = fileno( stdin );
+       } else if (( hqx.filed = open( hqxfile, O_RDONLY )) < 0 ) {
+           perror( hqxfile );
+           return( -1 );
+       }
+
+       if ( skip_junk( FIRST ) == 0 ) {
+           if ( hqx_header_read( fh ) == 0 ) {
+#if DEBUG
+               off_t   pos;
+
+               pos = lseek( hqx.filed, 0, L_INCR );
+               fprintf( stderr, "megatron: current position is %ld\n", pos );
+#endif
+               return( 0 );
+           }
+       }
+       hqx_close( KEEP );
+       fprintf( stderr, "%s\n", hqxfile );
+       return( -1 );
+    } else {
+       maxlen = sizeof( hqx.path ) -1;
+       strncpy( hqx.path, fh->name, maxlen );
+       strncpy( hqx.path, mtoupath( hqx.path ), maxlen );
+       strncat( hqx.path, ".hqx", maxlen - strlen( hqx.path ));
+       if (( hqx.filed = open( hqx.path, flags, 0666 )) < 0 ) {
+           perror( hqx.path );
+           return( -1 );
+       }
+       if ( hqx_header_write( fh ) != 0 ) {
+           hqx_close( TRASH );
+           fprintf( stderr, "%s\n", hqx.path );
+           return( -1 );
+       }
+       return( 0 );
+    }
+}
+
+/* 
+ * hqx_close must be called before a second file can be opened using
+ * hqx_open.  Upon successful completion, a value of 0 is returned.  
+ * Otherwise, a value of -1 is returned.
+ */
+
+hqx_close( keepflag )
+    int                        keepflag;
+{
+    if ( keepflag == KEEP ) {
+       return( close( hqx.filed ));
+    } else if ( keepflag == TRASH ) {
+       if (( strcmp( hqx.path, STDIN ) != 0 ) && ( unlink( hqx.path ) < 0 )) {
+           perror( hqx.path );
+       }
+       return( 0 );
+    } else return( -1 );
+}
+
+/*
+ * hqx_read is called until it returns zero for each fork.  when it is
+ * and finds that there is zero left to give, it reads in and compares
+ * the crc with the calculated one, and returns zero if all is well.
+ * it returns negative is the crc was bad or if has been called too many
+ * times for the same fork.  hqx_read must be called enough times to
+ * return zero and no more than that.
+ */
+
+hqx_read( fork, buffer, length )
+    int                        fork;
+    char               *buffer;
+    int                        length;
+{
+    u_short            storedcrc;
+    int                        readlen;
+    int                        cc;
+
+#if DEBUG >= 3
+    {
+       off_t   pos;
+       pos = lseek( hqx.filed, 0, L_INCR );
+       fprintf( stderr, "hqx_read: current position is %ld\n", pos );
+    }
+    fprintf( stderr, "hqx_read: fork is %s\n", forkname[ fork ] );
+    fprintf( stderr, "hqx_read: remaining length is %d\n", hqx.forklen[fork] );
+#endif
+
+    if ( hqx.forklen[ fork ] < 0 ) {
+       fprintf( stderr, "This should never happen, dude!\n" );
+       return( hqx.forklen[ fork ] );
+    }
+
+    if ( hqx.forklen[ fork ] == 0 ) {
+       cc = hqx_7tobin( (char *)&storedcrc, sizeof( storedcrc ));
+       if ( cc == sizeof( storedcrc )) {
+           storedcrc = ntohs ( storedcrc );
+#if DEBUG >= 4
+    fprintf( stderr, "hqx_read: storedcrc\t\t%x\n", storedcrc );
+    fprintf( stderr, "hqx_read: observed crc\t\t%x\n\n", hqx.forkcrc[fork] );
+#endif
+           if ( storedcrc == hqx.forkcrc[ fork ] ) {
+               return( 0 );
+           }
+           fprintf( stderr, "hqx_read: Bad %s fork crc, dude\n", 
+                   forkname[ fork ] );
+       }
+       return( -1 );
+    }
+
+    if ( hqx.forklen[ fork ] < length ) {
+       readlen = hqx.forklen[ fork ];
+    } else {
+       readlen = length;
+    }
+#if DEBUG >= 3
+    fprintf( stderr, "hqx_read: readlen is %d\n", readlen );
+#endif
+
+    cc = hqx_7tobin( buffer, readlen );
+    if ( cc > 0 ) {
+       hqx.forkcrc[ fork ] = 
+               updcrc( hqx.forkcrc[ fork ], (u_char *)buffer, cc );
+       hqx.forklen[ fork ] -= cc;
+    }
+#if DEBUG >= 3
+    fprintf( stderr, "hqx_read: chars read is %d\n", cc );
+#endif
+    return( cc );
+}
+
+/* 
+ * hqx_header_read is called by hqx_open, and before any information can
+ * read from the hqx_header substruct.  it must be called before any
+ * of the bytes of the other two forks can be read, as well.
+ * returns a negative number if it was unable to pull enough information
+ * to fill the hqx_header fields.
+ */
+
+hqx_header_read( fh )
+    struct FHeader     *fh;
+{
+    char               *headerbuf, *headerptr;
+    u_int32_t          time_seconds;
+    u_short            mask;
+    u_short            header_crc;
+    char               namelen;
+
+#if HEXOUTPUT
+    int                headerfork;
+    headerfork = open( "headerfork", O_WRONLY|O_CREAT, 0622 );
+#endif
+
+    mask = htons( 0xfcee );
+    hqx.headercrc = 0;
+
+    if ( hqx_7tobin( &namelen, sizeof( namelen )) == 0 ) {
+       fprintf( stderr, "Premature end of file :" );
+       return( -2 );
+    }
+    hqx.headercrc = updcrc( hqx.headercrc, (u_char *)&namelen, 
+           sizeof( namelen ));
+
+#if HEXOUTPUT
+    write( headerfork, &namelen, sizeof( namelen ));
+#endif
+
+    if (( headerbuf = 
+           (char *)malloc( (unsigned int)( namelen + HEADSIZ ))) == 0 ) {
+       return( -1 );
+    }
+    if ( hqx_7tobin( headerbuf, ( namelen + HEADSIZ )) == 0 ) {
+       free( headerbuf );
+       fprintf( stderr, "Premature end of file :" );
+       return( -2 );
+    }
+    headerptr = headerbuf;
+    hqx.headercrc = updcrc( hqx.headercrc, 
+           (u_char *)headerbuf, ( namelen + HEADSIZ - CRCSIZ ));
+
+#if HEXOUTPUT
+    write( headerfork, headerbuf, ( namelen + HEADSIZ ));
+#endif
+
+/*
+ * stuff from the hqx file header
+ */
+
+    memcpy( fh->name, headerptr, (int)namelen );
+    headerptr += namelen;
+    headerptr += VERSION;
+    memcpy(&fh->finder_info,  headerptr, TCSIZ );
+    headerptr += TCSIZ;
+    memcpy(&fh->finder_info.fdFlags,  headerptr, FLAGSIZ );
+    fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask;
+    headerptr += FLAGSIZ;
+    memcpy(&fh->forklen[ DATA ],  headerptr, DATASIZ );
+    hqx.forklen[ DATA ] = ntohl( fh->forklen[ DATA ] );
+    headerptr += DATASIZ;
+    memcpy( &fh->forklen[ RESOURCE ], headerptr, RESSIZ );
+    hqx.forklen[ RESOURCE ] = ntohl( fh->forklen[ RESOURCE ] );
+    headerptr += RESSIZ;
+    memcpy(&header_crc,  headerptr, CRCSIZ );
+    headerptr += CRCSIZ;
+    header_crc = ntohs( header_crc );
+
+/*
+ * stuff that should be zero'ed out
+ */
+
+    fh->comment[0] = '\0';
+    fh->finder_info.fdLocation = 0;
+    fh->finder_info.fdFldr = 0;
+
+#if DEBUG >= 5
+    {
+       short           flags;
+
+       fprintf( stderr, "Values read by hqx_header_read\n" );
+       fprintf( stderr, "name length\t\t%d\n", namelen );
+       fprintf( stderr, "file name\t\t%s\n", fh->name );
+       fprintf( stderr, "get info comment\t%s\n", fh->comment );
+       fprintf( stderr, "type\t\t\t%.*s\n", sizeof( fh->finder_info.fdType ),
+               &fh->finder_info.fdType );
+       fprintf( stderr, "creator\t\t\t%.*s\n", 
+               sizeof( fh->finder_info.fdCreator ), 
+               &fh->finder_info.fdCreator );
+       memcpy( &flags, &fh->finder_info.fdFlags, sizeof( flags ));
+       flags = ntohs( flags );
+       fprintf( stderr, "flags\t\t\t%x\n", flags );
+       fprintf( stderr, "data fork length\t%ld\n", hqx.forklen[DATA] );
+       fprintf( stderr, "resource fork length\t%ld\n", hqx.forklen[RESOURCE] );
+       fprintf( stderr, "header_crc\t\t%x\n", header_crc );
+       fprintf( stderr, "observed crc\t\t%x\n", hqx.headercrc );
+       fprintf( stderr, "\n" );
+    }
+#endif
+
+/*
+ * create and modify times are figured from right now
+ */
+
+    time_seconds = AD_DATE_FROM_UNIX(time( NULL ));
+    memcpy( &fh->create_date, &time_seconds, 
+           sizeof( fh->create_date ));
+    memcpy( &fh->mod_date, &time_seconds, 
+           sizeof( fh->mod_date ));
+    fh->backup_date = AD_DATE_START;
+
+/*
+ * stuff that should be zero'ed out
+ */
+
+    fh->comment[0] = '\0';
+    memset( &fh->finder_info.fdLocation, 0, 
+           sizeof( fh->finder_info.fdLocation ));
+    memset( &fh->finder_info.fdFldr, 0, sizeof( fh->finder_info.fdFldr ));
+
+    hqx.forkcrc[ DATA ] = 0;
+    hqx.forkcrc[ RESOURCE ] = 0;
+
+    free( headerbuf );
+    if ( header_crc != hqx.headercrc ) {
+       fprintf( stderr, "Bad Header crc, dude :" );
+       return( -3 );
+    }
+    return( 0 );
+}
+
+/*
+ * hqx_header_write.
+ */
+
+hqx_header_write( fh )
+    struct FHeader     *fh;
+{
+    return( -1 );
+}
+
+/*
+ * hqx7_fill is called from skip_junk and hqx_7tobin.  it pulls from the
+ * binhqx file into the hqx7 buffer.  returns number of bytes read
+ * or a zero for end of file.
+ * it sets the pointers to the hqx7 buffer up to point to the valid data.
+ */
+
+hqx7_fill( hqx7_ptr )
+    u_char             *hqx7_ptr;
+{
+    int                        cc;
+    int                        cs;
+
+    cs = hqx7_ptr - hqx7_buf;
+    if ( cs >= sizeof( hqx7_buf )) return( -1 );
+    hqx7_first = hqx7_ptr;
+    cc = read( hqx.filed, (char *)hqx7_first, ( sizeof( hqx7_buf ) - cs ));
+    if ( cc < 0 ) {
+       perror( "" );
+       return( cc );
+    }
+    hqx7_last = ( hqx7_first + cc );
+    return( cc );
+}
+
+/*
+char tr[] = "!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr";
+            0 123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
+            0                1               2               3 
+Input characters are translated to a number between 0 and 63 by direct
+array lookup.  0xFF signals a bad character.  0xFE is signals a legal
+character that should be skipped, namely '\n', '\r'.  0xFD signals ':'.
+0xFC signals a whitespace character.
+*/
+
+u_char hqxlookup[] = {
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFC, 0xFE, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFC, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+    0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0xFF, 0xFF,
+    0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0xFF,
+    0x14, 0x15, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
+    0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0xFF,
+    0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0xFF,
+    0x2C, 0x2D, 0x2E, 0x2F, 0xFF, 0xFF, 0xFF, 0xFF,
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0xFF,
+    0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0xFF, 0xFF,
+    0x3D, 0x3E, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+};
+
+/*
+ * skip_junk is called from hqx_open.  it skips over junk in the file until
+ * it comes to a line containing a valid first line of binhqx encoded file.
+ * returns a 0 for success, negative if it never finds good data.
+ * pass a FIRST when looking for the first valid binhex line, a value of 
+ * OTHER when looking for any subsequent line.
+ */
+
+skip_junk( line )
+int                    line;
+{
+    int                        found = NOWAY;
+    int                        stopflag;
+    int                        nc = 0;
+    u_char             c;
+    u_char             prevchar;
+
+    if ( line == FIRST ) {
+       if ( hqx7_fill( hqx7_buf  ) <= 0 ) {
+           fprintf( stderr, "Premature end of file :" );
+           return( -1 );
+       }
+    }
+
+    while ( found == NOWAY ) {
+       if ( line == FIRST ) {
+           if ( *hqx7_first == ':' ) {
+               nc = c = 0;
+               stopflag = NOWAY;
+               hqx7_first++;
+               while (( stopflag == NOWAY ) && 
+                       ( nc < ( hqx7_last - hqx7_first ))) {
+                   switch ( c = hqxlookup[ hqx7_first[ nc ]] ) {
+                       case 0xFC :
+                       case 0xFF :
+                       case 0xFE :
+                       case 0xFD :
+                           stopflag = SURETHANG;
+                           break;
+                       default :
+                           nc++;
+                           break;
+                   }
+               }
+               if (( nc > 30 ) && ( nc < 64 ) &&
+                       (( c == 0xFE ) || ( c == 0xFD ))) found = SURETHANG;
+           } else {
+               hqx7_first++;
+           }
+       } else {
+           if (( prevchar = hqxlookup[ *hqx7_first ] ) == 0xFE ) {
+               nc = c = 0;
+               stopflag = NOWAY;
+               hqx7_first++;
+               while (( stopflag == NOWAY ) && 
+                       ( nc < ( hqx7_last - hqx7_first ))) {
+                   switch ( c = hqxlookup[ hqx7_first[ nc ]] ) {
+                       case 0xFC :
+                       case 0xFE :
+                           if (( prevchar == 0xFC ) || ( prevchar == 0xFE )) {
+                               nc++;
+                               break;
+                           }
+                       case 0xFF :
+                       case 0xFD :
+                           stopflag = SURETHANG;
+                           break;
+                       default :
+                           prevchar = c;
+                           nc++;
+                           break;
+                   }
+               }
+               if ( c == 0xFD ) {
+                   found = SURETHANG;
+               } else if (( nc > 30 ) && ( c == 0xFE )) {
+                   found = SURETHANG;
+               }
+           } else {
+               hqx7_first++;
+           }
+       }
+
+       if (( hqx7_last - hqx7_first ) == nc ) {
+           if ( line == FIRST ) {
+               *hqx7_buf = ':';
+           } else *hqx7_buf = '\n';
+           memcpy(hqx7_buf + 1, hqx7_first, nc );
+           hqx7_first = hqx7_buf + ( ++nc );
+           if ( hqx7_fill( hqx7_first ) <= 0 ) {
+               fprintf( stderr, "Premature end of file :" );
+               return( -1 );
+           }
+           hqx7_first = hqx7_buf;
+       }
+    }
+
+    return( 0 );
+}
+
+/* 
+ * hqx_7tobin is used to read the data, converted to binary.  It is
+ * called by hqx_header_read to get the header information, and must be
+ * called to get the data for each fork, and the crc data for each
+ * fork.  it has the same basic calling structure as unix read.  the
+ * number of valid bytes read is returned.  It does buffering so as to
+ * return the requested length of data every time, unless the end of
+ * file is reached.
+ */
+
+hqx_7tobin( outbuf, datalen ) 
+    char               *outbuf;
+    int                        datalen;
+{
+    static u_char      hqx8[3];
+    static char                hqx8i;
+    static u_char      prev_hqx8;
+    static u_char      prev_out;
+    static u_char      prev_hqx7;
+    static int         eofflag;
+    u_char             hqx7[4];
+    char               hqx7i = 0;
+    char               *out_first;
+    char               *out_last;
+
+#if DEBUG
+    fprintf( stderr, "hqx_7tobin: datalen entering %d\n", datalen );
+    fprintf( stderr, "hqx_7tobin: hqx8i entering %d\n", hqx8i );
+#endif
+
+    if ( first_flag == 0 ) {
+       prev_hqx8 = 0;
+       prev_hqx7 = 0;
+       prev_out = 0;
+       hqx8i = 3;
+       first_flag = 1;
+       eofflag = 0;
+    }
+
+#if DEBUG
+    fprintf( stderr, "hqx_7tobin: hqx8i entering %d\n", hqx8i );
+#endif
+
+    out_first = outbuf;
+    out_last = out_first + datalen;
+
+    while (( out_first < out_last ) && ( eofflag == 0 )) {
+
+       if ( hqx7_first == hqx7_last ) {
+           if ( hqx7_fill( hqx7_buf ) == 0 ) {
+               eofflag = 1;
+               continue;
+           }
+       }
+
+       if ( hqx8i > 2 ) {
+
+           while (( hqx7i < 4 ) && ( hqx7_first < hqx7_last )) {
+               hqx7[ hqx7i ] = hqxlookup[ *hqx7_first ];
+               switch ( hqx7[ hqx7i ] ) {
+                   case 0xFC :
+                       if (( prev_hqx7 == 0xFC ) || ( prev_hqx7 == 0xFE )) {
+                           hqx7_first++;
+                           break;
+                       }
+                   case 0xFD :
+                   case 0xFF :
+                       eofflag = 1;
+                       while ( hqx7i < 4 ) {
+                           hqx7[ hqx7i++ ] = 0;
+                       }
+                       break;
+                   case 0xFE :
+                       prev_hqx7 = hqx7[ hqx7i ];
+                       if ( skip_junk( OTHER ) < 0 ) {
+                           fprintf( stderr, "\n" );
+                           eofflag = 1;
+                           while ( hqx7i < 4 ) {
+                               hqx7[ hqx7i++ ] = 0; }
+                       }
+                       break;
+                   default :
+                       prev_hqx7 = hqx7[ hqx7i++ ];
+                       hqx7_first++;
+                       break;
+               }
+           }
+           
+           if ( hqx7i == 4 ) {
+               hqx8[ 0 ] = (( hqx7[ 0 ] << 2 ) | ( hqx7[ 1 ] >> 4 ));
+               hqx8[ 1 ] = (( hqx7[ 1 ] << 4 ) | ( hqx7[ 2 ] >> 2 ));
+               hqx8[ 2 ] = (( hqx7[ 2 ] << 6 ) | ( hqx7[ 3 ] ));
+               hqx7i = hqx8i = 0;
+           }
+       }
+
+       while (( hqx8i < 3 ) && ( out_first < out_last )) {
+
+#if HEXOUTPUT
+           putc( hqx8i, rawhex );
+            putc( hqx8[ hqx8i ], rawhex );
+#endif
+
+           if ( prev_hqx8 == RUNCHAR ) {
+               if ( hqx8[ hqx8i ] == 0 ) {
+                   *out_first = prev_hqx8;
+#if HEXOUTPUT
+                   putc( *out_first, expandhex );
+#endif
+                   prev_out = prev_hqx8;
+                   out_first++;
+               }
+               while (( out_first < out_last ) && ( hqx8[ hqx8i ] > 1 )) {
+                   *out_first = prev_out;
+#if HEXOUTPUT
+                   putc( *out_first, expandhex );
+#endif
+                   hqx8[ hqx8i ]--;
+                   out_first++;
+               }
+               if ( hqx8[ hqx8i ] < 2 ) {
+                   prev_hqx8 = hqx8[ hqx8i ];
+                   hqx8i++;
+               }
+               continue;
+           }
+
+           prev_hqx8 = hqx8[ hqx8i ];
+           if ( prev_hqx8 != RUNCHAR ) {
+               *out_first = prev_hqx8;
+#if HEXOUTPUT
+               putc( *out_first, expandhex );
+#endif
+               prev_out = prev_hqx8;
+               out_first++;
+           }
+           hqx8i++;
+
+       }
+
+    }
+    return( out_first - outbuf );
+}
diff --git a/bin/megatron/macbin.c b/bin/megatron/macbin.c
new file mode 100644 (file)
index 0000000..f149dc7
--- /dev/null
@@ -0,0 +1,560 @@
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <strings.h>
+#include <syslog.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <time.h>
+
+#include <atalk/adouble.h>
+#include <netatalk/endian.h>
+#include "megatron.h"
+
+#define        DEBUG           0
+
+/*     String used to indicate standard input instead of a disk
+       file.  Should be a string not normally used for a file
+ */
+#ifndef        STDIN
+#      define  STDIN   "-"
+#endif
+
+/*     Yes and no
+ */
+#define NOWAY          0
+#define SURETHANG      1
+
+/*     Size of a macbinary file header
+ */
+#define HEADBUFSIZ     128
+
+u_short                updcrc();
+
+/*     Both input and output routines use this struct and the
+       following globals; therefore this module can only be used
+       for one of the two functions at a time.
+ */
+struct bin_file_data {
+    u_int32_t          forklen[ NUMFORKS ];
+    char               path[ MAXPATHLEN + 1];
+    int                        filed;
+    u_short            headercrc;
+    time_t              gmtoff; /* to convert from/to localtime */
+}              bin;
+
+extern char    *forkname[];
+u_char         head_buf[HEADBUFSIZ];
+
+/* 
+ * bin_open must be called first.  pass it a filename that is supposed
+ * to contain a macbinary file.  an bin struct will be allocated and
+ * somewhat initialized; bin_filed is set.
+ */
+
+bin_open( binfile, flags, fh, options )
+    char               *binfile;
+    int                        flags, options;
+    struct FHeader     *fh;
+{
+    int                        maxlen;
+    int                        rc;
+    time_t              t;
+    struct tm           *tp;
+
+#if DEBUG
+    fprintf( stderr, "entering bin_open\n" );
+#endif
+
+    /* call localtime so that we get the timezone offset */
+    bin.gmtoff = 0;
+#ifndef NO_STRUCT_TM_GMTOFF
+    time(&t);
+    if (tp = localtime(&t))
+        bin.gmtoff = tp->tm_gmtoff;
+#endif
+
+    if ( flags == O_RDONLY ) { /* input */
+       if ( strcmp( binfile, STDIN ) == 0 ) {
+           bin.filed = fileno( stdin );
+       } else if (( bin.filed = open( binfile, flags )) < 0 ) {
+           perror( binfile );
+           return( -1 );
+       }
+#if DEBUG
+       fprintf( stderr, "opened %s for read\n", binfile );
+#endif
+       if ((( rc = test_header() ) > 0 ) && 
+               ( bin_header_read( fh, rc ) == 0 )) {
+           return( 0 );
+       }
+       fprintf( stderr, "%s is not a macbinary file.\n", binfile );
+       return( -1 );
+    } else { /* output */
+        if (options & OPTION_STDOUT) 
+         bin.filed = fileno(stdout);
+       else {
+         maxlen = sizeof( bin.path ) - 1;
+#if DEBUG
+         fprintf( stderr, "sizeof bin.path\t\t\t%d\n", sizeof( bin.path ));
+         fprintf( stderr, "maxlen \t\t\t\t%d\n", maxlen );
+#endif
+         strncpy( bin.path, fh->name, maxlen );
+         strncpy( bin.path, mtoupath( bin.path ), maxlen );
+         strncat( bin.path, ".bin", maxlen - strlen( bin.path ));
+         if (( bin.filed = open( bin.path, flags, 0666 )) < 0 ) {
+           perror( bin.path );
+           return( -1 );
+         }
+#if DEBUG
+         fprintf( stderr, "opened %s for write\n", 
+                  (options & OPTION_STDOUT) ? "(stdout)" : bin.path );
+#endif
+       }
+
+       if ( bin_header_write( fh ) != 0 ) {
+           bin_close( TRASH );
+           fprintf( stderr, "%s\n", bin.path );
+           return( -1 );
+       }
+       return( 0 );
+    }
+}
+
+/* 
+ * bin_close must be called before a second file can be opened using
+ * bin_open.  Upon successful completion, a value of 0 is returned.  
+ * Otherwise, a value of -1 is returned.
+ */
+
+bin_close( keepflag )
+    int                        keepflag;
+{
+#if DEBUG
+    fprintf( stderr, "entering bin_close\n" );
+#endif
+    if ( keepflag == KEEP ) {
+       return( close( bin.filed ));
+    } else if ( keepflag == TRASH ) {
+       if (( strcmp( bin.path, STDIN ) != 0 ) && 
+               ( unlink( bin.path ) < 0 )) {
+           perror ( bin.path );
+       }
+       return( 0 );
+    } else return( -1 );
+}
+
+/*
+ * bin_read is called until it returns zero for each fork.  when it is
+ * and finds that there is zero left to give, it seeks to the position
+ * of the next fork (if there is one ).
+ * bin_read must be called enough times to
+ * return zero and no more than that.
+ */
+
+bin_read( fork, buffer, length )
+    int                        fork;
+    char               *buffer;
+    int                        length;
+{
+    char               *buf_ptr;
+    int                        readlen;
+    int                        cc = 1;
+    off_t              pos;
+
+#if DEBUG >= 3
+    fprintf( stderr, "bin_read: fork is %s\n", forkname[ fork ] );
+    fprintf( stderr, "bin_read: remaining length is %d\n", bin.forklen[fork] );
+#endif
+
+    if ( bin.forklen[ fork ] < 0 ) {
+       fprintf( stderr, "This should never happen, dude!\n" );
+       return( bin.forklen[ fork ] );
+    }
+
+    if ( bin.forklen[ fork ] == 0 ) {
+       if ( fork == DATA ) {
+           pos = lseek( bin.filed, 0, SEEK_CUR );
+#if DEBUG
+           fprintf( stderr, "current position is %ld\n", pos );
+#endif
+           if (pos = pos % HEADBUFSIZ) {
+             pos = lseek( bin.filed, HEADBUFSIZ - pos, SEEK_CUR );
+           }
+#if DEBUG
+           fprintf( stderr, "current position is %ld\n", pos );
+#endif
+       }
+       return( 0 );
+    }
+
+    if ( bin.forklen[ fork ] < length ) {
+       readlen = bin.forklen[ fork ];
+    } else {
+       readlen = length;
+    }
+#if DEBUG >= 3
+    fprintf( stderr, "bin_read: readlen is %d\n", readlen );
+    fprintf( stderr, "bin_read: cc is %d\n", cc );
+#endif
+
+    buf_ptr = buffer;
+    while (( readlen > 0 ) && ( cc > 0 )) {
+       if (( cc = read( bin.filed, buf_ptr, readlen )) > 0 ) {
+#if DEBUG >= 3
+           fprintf( stderr, "bin_read: cc is %d\n", cc );
+#endif
+           readlen -= cc;
+           buf_ptr += cc;
+       }
+    }
+    if ( cc >= 0 ) {
+       cc = buf_ptr - buffer;
+       bin.forklen[ fork ] -= cc;
+    }
+
+#if DEBUG >= 3
+    fprintf( stderr, "bin_read: chars read is %d\n", cc );
+#endif
+    return( cc );
+}
+
+/*
+ * bin_write 
+ */
+
+bin_write( fork, buffer, length )
+    int                        fork;
+    char               *buffer;
+    int                        length;
+{
+    char               *buf_ptr;
+    int                        writelen;
+    int                        cc = 0;
+    off_t              pos;
+    u_char             padchar = 0;
+
+#if DEBUG >= 3
+    fprintf( stderr, "bin_write: fork is %s\n", forkname[ fork ] );
+    fprintf( stderr, "bin_write: remaining length is %d\n", bin.forklen[fork] );
+#endif
+
+    if (( fork == RESOURCE ) && ( bin.forklen[ DATA ] != 0 )) {
+       fprintf( stderr, "Forklength error.\n" );
+       return( -1 );
+    }
+
+    buf_ptr = (char *)buffer;
+    if ( bin.forklen[ fork ] >= length ) {
+       writelen = length;
+    } else {
+       fprintf( stderr, "Forklength error.\n" );
+       return( -1 );
+    }
+
+#if DEBUG >= 3
+    fprintf( stderr, "bin_write: write length is %d\n", writelen );
+#endif
+
+    while (( writelen > 0 ) && ( cc >= 0 )) {
+       cc = write( bin.filed, buf_ptr, writelen );
+       buf_ptr += cc;
+       writelen -= cc;
+    }
+    if ( cc < 0 ) {
+       perror( "Couldn't write to macbinary file:" );
+       return( cc );
+    }
+    bin.forklen[ fork ] -= length;
+
+    if ( bin.forklen[ fork ] < 0 ) {
+       fprintf( stderr, "This should never happen, dude!\n" );
+       return( bin.forklen[ fork ] );
+    }
+
+/*
+ * add the padding at end of data and resource forks
+ */
+
+    if ( bin.forklen[ fork ] == 0 ) {
+       pos = lseek( bin.filed, 0, SEEK_CUR );
+#if DEBUG
+       fprintf( stderr, "current position is %ld\n", pos );
+#endif
+       if (pos = pos % HEADBUFSIZ) { /* pad only if we need to */
+         pos = lseek( bin.filed, HEADBUFSIZ - pos - 1, SEEK_CUR );
+         if ( write( bin.filed, &padchar, 1 ) != 1 ) {
+           perror( "Couldn't write to macbinary file:" );
+           return( -1 );
+         }
+       }
+#if DEBUG
+         fprintf( stderr, "current position is %ld\n", pos );
+#endif
+    }
+
+#if DEBUG
+       fprintf( stderr, "\n" );
+#endif
+
+    return( length );
+}
+
+/* 
+ * bin_header_read is called by bin_open, and before any information can
+ * read from the fh substruct.  it must be called before any
+ * of the bytes of the other two forks can be read, as well.
+ */
+
+bin_header_read( fh, revision )
+    struct FHeader     *fh;
+    int                        revision;
+{
+    u_short            mask;
+
+/*
+ * Set the appropriate finder flags mask for the type of macbinary
+ * file it is, and copy the extra macbinary II stuff from the header.
+ * If it is not a macbinary file revision of I or II, then return
+ * negative.
+ */
+
+    switch ( revision ) {
+        case 3:
+       case 2 :
+           mask = htons( 0xfcee );
+           memcpy(&fh->finder_info.fdFlags + 1, head_buf + 101,1 );
+           break;
+       case 1 :
+           mask = htons( 0xfc00 );
+           break;
+       default :
+           return( -1 );
+           break;
+    }
+
+/*
+ * Go through and copy all the stuff you can get from the 
+ * MacBinary header into the fh struct.  What fun!
+ */
+
+    memcpy(fh->name, head_buf +  2, head_buf[ 1 ] );
+    memcpy(&fh->create_date, head_buf +  91, 4 );
+    fh->create_date = MAC_DATE_TO_UNIX(fh->create_date) - bin.gmtoff;
+    fh->create_date = AD_DATE_FROM_UNIX(fh->create_date);
+    memcpy( &fh->mod_date, head_buf +  95, 4 );
+    fh->mod_date = MAC_DATE_TO_UNIX(fh->mod_date) - bin.gmtoff;
+    fh->mod_date = AD_DATE_FROM_UNIX(fh->mod_date);
+    fh->backup_date = AD_DATE_START;
+    memcpy( &fh->finder_info, head_buf +  65, 8 );
+    memcpy( &fh->finder_info.fdFlags, head_buf + 73, 1 );
+    fh->finder_info.fdFlags &= mask;
+    memcpy(&fh->finder_info.fdLocation, head_buf + 75, 4 );
+    memcpy(&fh->finder_info.fdFldr, head_buf +  79, 2 );
+    memcpy(&fh->forklen[ DATA ],  head_buf + 83, 4 );
+    bin.forklen[ DATA ] = ntohl( fh->forklen[ DATA ] );
+    memcpy(&fh->forklen[ RESOURCE ],  head_buf +  87, 4 );
+    bin.forklen[ RESOURCE ] = ntohl( fh->forklen[ RESOURCE ] );
+    fh->comment[0] = '\0';
+
+    if (revision == 3) {
+      fh->finder_xinfo.fdScript = *(head_buf + 106);
+      fh->finder_xinfo.fdXFlags = *(head_buf + 107);
+    }
+
+#if DEBUG >= 5
+    {
+       short           flags;
+
+       fprintf( stderr, "Values read by bin_header_read\n" );
+       fprintf( stderr, "name length\t\t%d\n", head_buf[ 1 ] );
+       fprintf( stderr, "file name\t\t%s\n", fh->name );
+       fprintf( stderr, "get info comment\t%s\n", fh->comment );
+       fprintf( stderr, "type\t\t\t%.*s\n", sizeof( fh->finder_info.fdType ),
+               &fh->finder_info.fdType );
+       fprintf( stderr, "creator\t\t\t%.*s\n", 
+               sizeof( fh->finder_info.fdCreator ), 
+               &fh->finder_info.fdCreator );
+       memcpy( &flags, &fh->finder_info.fdFlags, sizeof( flags ));
+       flags = ntohs( flags );
+       fprintf( stderr, "flags\t\t\t%x\n", flags );
+       fprintf( stderr, "data fork length\t%ld\n", bin.forklen[DATA] );
+       fprintf( stderr, "resource fork length\t%ld\n", bin.forklen[RESOURCE] );
+       fprintf( stderr, "\n" );
+    }
+#endif
+
+    return( 0 );
+}
+
+/* 
+ * bin_header_write is called by bin_open, and relies on information
+ * from the fh substruct.  it must be called before any
+ * of the bytes of the other two forks can be written, as well.
+ * bin_header_write and bin_header_read are opposites.
+ */
+
+bin_header_write( fh )
+    struct FHeader     *fh;
+{
+    char               *write_ptr;
+    u_int32_t           t;
+    int                        wc;
+    int                        wr;
+
+    memset(head_buf, 0, sizeof( head_buf ));
+    head_buf[ 1 ] = (u_char)strlen( fh->name );
+    memcpy( head_buf + 2, fh->name, head_buf[ 1 ] );
+    memcpy( head_buf + 65, &fh->finder_info, 8 );
+    memcpy( head_buf + 73, &fh->finder_info.fdFlags, 1);
+    memcpy( head_buf + 75, &fh->finder_info.fdLocation, 4 );
+    memcpy( head_buf + 79, &fh->finder_info.fdFldr, 2 );
+    memcpy( head_buf + 83, &fh->forklen[ DATA ], 4 );
+    memcpy( head_buf + 87, &fh->forklen[ RESOURCE ], 4 );
+    t = AD_DATE_TO_UNIX(fh->create_date) + bin.gmtoff;
+    t = MAC_DATE_FROM_UNIX(t);
+    memcpy( head_buf + 91, &t, sizeof(t) );
+    t = AD_DATE_TO_UNIX(fh->mod_date) + bin.gmtoff;
+    t = MAC_DATE_FROM_UNIX(t);
+    memcpy( head_buf + 95, &t, sizeof(t) );
+    memcpy( head_buf + 101, &fh->finder_info.fdFlags + 1, 1);
+
+    /* macbinary III */
+    memcpy( head_buf + 102, "mBIN", 4);
+    *(head_buf + 106) = fh->finder_xinfo.fdScript;
+    *(head_buf + 107) = fh->finder_xinfo.fdXFlags;
+    head_buf[ 122 ] = 130;
+
+    head_buf[ 123 ] = 129;
+
+    bin.headercrc = htons( updcrc( (u_short) 0, head_buf, 124 ));
+    memcpy(head_buf + 124, &bin.headercrc, sizeof( bin.headercrc ));
+
+    bin.forklen[ DATA ] = ntohl( fh->forklen[ DATA ] );
+    bin.forklen[ RESOURCE ] = ntohl( fh->forklen[ RESOURCE ] );
+
+#if DEBUG >= 5
+    {
+       fprintf( stderr, "Values written by bin_header_write\n" );
+       fprintf( stderr, "name length\t\t%d\n", head_buf[ 1 ] );
+       fprintf( stderr, "file name\t\t%s\n", (char *)&head_buf[ 2 ] );
+       fprintf( stderr, "type\t\t\t%.4s\n", (char *)&head_buf[ 65 ] );
+       fprintf( stderr, "creator\t\t\t%.4s\n", (char *)&head_buf[ 69 ] );
+       fprintf( stderr, "data fork length\t%ld\n", bin.forklen[DATA] );
+       fprintf( stderr, "resource fork length\t%ld\n", bin.forklen[RESOURCE] );
+       fprintf( stderr, "\n" );
+    }
+#endif
+
+    write_ptr = (char *)head_buf;
+    wc = sizeof( head_buf );
+    wr = 0;
+    while (( wc > 0 ) && ( wr >= 0 )) {
+       wr = write( bin.filed, write_ptr, wc );
+       write_ptr += wr;
+       wc -= wr;
+    }
+    if ( wr < 0 ) {
+       perror( "Couldn't write macbinary header:" );
+       return( wr );
+    }
+
+    return( 0 );
+}
+
+/*
+ * test_header is called from bin_open.  it checks certain values of
+ * the first 128 bytes, determines if the file is a MacBinary,
+ * MacBinary II, MacBinary III, or non-MacBinary file, and returns a
+ * one, two, three or negative one to indicate the file type.
+ *
+ * If the signature at 102 is equal to "mBIN," then it's a MacBinary
+ * III file. Bytes 0 and 74 must be zero for the file to be any type
+ * of MacBinary.  If the crc of bytes 0 through 123 equals the value
+ * at offset 124 then it is a MacBinary II.  If not, then if byte 82
+ * is zero, byte 2 is a valid value for a mac filename length (between
+ * one and sixty-three), and bytes 101 through 125 are all zero, then
+ * the file is a MacBinary. 
+ *
+ * NOTE: apple's MacBinary II files have a non-zero value at byte 74.
+ * so, the check for byte 74 isn't very useful.
+ */
+
+test_header()
+{
+    const char          zeros[25] = "";
+    u_int32_t          cc;
+    u_short            header_crc;
+    u_char             namelen;
+
+#if DEBUG
+    fprintf( stderr, "entering test_header\n" );
+#endif
+
+    cc = read( bin.filed, (char *)head_buf, sizeof( head_buf ));
+    if ( cc < sizeof( head_buf )) {
+       perror( "Premature end of file :" );
+       return( -1 );
+    }
+
+#if DEBUG
+    fprintf( stderr, "was able to read HEADBUFSIZ bytes\n" );
+#endif
+
+    /* check for macbinary III header */
+    if (memcmp(head_buf + 102, "mBIN", 4) == 0)
+        return 3;
+
+    /* check for macbinary II even if only one of the bytes is zero */
+    if (( head_buf[ 0 ] == 0 ) || ( head_buf[ 74 ] == 0 )) {
+#if DEBUG
+      fprintf( stderr, "byte 0 and 74 are both zero\n" );
+#endif
+      bin.headercrc = updcrc( (u_short) 0, head_buf, 124 );
+      memcpy(&header_crc, head_buf + 124, sizeof( header_crc ));
+      header_crc = ntohs( header_crc );
+      if ( header_crc == bin.headercrc ) {
+       return( 2 );
+      }
+
+#if DEBUG
+      fprintf( stderr, "header crc didn't pan out\n" );
+#endif
+    }
+
+    /* now see if we have a macbinary file. */
+    if ( head_buf[ 82 ] != 0 ) {
+       return( -1 );
+    }
+    memcpy( &namelen, head_buf + 1, sizeof( namelen ));
+#if DEBUG
+    fprintf( stderr, "name length is %d\n", namelen );
+#endif
+    if (( namelen < 1 ) || ( namelen > 63 )) {
+       return( -1 );
+    }
+
+    /* bytes 101 - 125 should be zero */
+    if (memcmp(head_buf + 101, zeros, sizeof(zeros)) != 0)
+        return -1;
+
+    /* macbinary forks aren't larger than 0x7FFFFF */
+    memcpy(&cc, head_buf + 83, sizeof(cc));
+    cc = ntohl(cc);
+    if (cc > 0x7FFFFF)
+        return -1;
+    memcpy(&cc, head_buf + 87, sizeof(cc));
+    cc = ntohl(cc);
+    if (cc > 0x7FFFFF)
+        return -1;
+
+
+#if DEBUG
+    fprintf( stderr, "byte 82 is zero and name length is cool\n" );
+#endif
+
+    return( 1 );
+}
diff --git a/bin/megatron/megatron.c b/bin/megatron/megatron.c
new file mode 100644 (file)
index 0000000..61049ed
--- /dev/null
@@ -0,0 +1,340 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <netatalk/endian.h>
+#include "megatron.h"
+
+#define        DEBUG           0
+
+char           forkbuf[8192];
+char           *forkname[] = { "data", "resource" };
+char           *name[] = { "unhex",
+                           "unbin",
+                           "unsingle",
+                           "macbinary",
+                           "hqx2bin",
+                           "single2bin",
+                           "nadheader",
+                           "binheader",
+                           "megatron" };
+
+int from_open( un, file, fh, flags )
+    int                        un, flags;
+    char               *file;
+    struct FHeader     *fh;
+{
+    switch ( un ) {
+       case MEGATRON :
+       case HEX2NAD :
+       case HEX2BIN :
+           return( hqx_open( file, O_RDONLY, fh, flags ));
+           break;
+       case BIN2NAD :
+        case BINHEADER:
+           return( bin_open( file, O_RDONLY, fh, flags ));
+           break;
+       case NAD2BIN :
+        case NADHEADER:
+           return( nad_open( file, O_RDONLY, fh, flags ));
+           break;
+       case SINGLE2NAD :
+       case SINGLE2BIN :
+           return( single_open( file, O_RDONLY, fh, flags ));
+       default :
+           return( -1 );
+           break;
+    }
+}
+
+int from_read( un, fork, buf, len )
+    int                        un;
+    int                        fork;
+    char               *buf;
+    int                        len;
+{
+    switch ( un ) {
+       case MEGATRON :
+       case HEX2NAD :
+       case HEX2BIN :
+           return( hqx_read( fork, buf, len ));
+           break;
+       case BIN2NAD :
+           return( bin_read( fork, buf, len ));
+           break;
+       case NAD2BIN :
+           return( nad_read( fork, buf, len ));
+           break;
+       case SINGLE2NAD :
+       case SINGLE2BIN :
+           return( single_read( fork, buf, len ));
+       default :
+           return( -1 );
+           break;
+    }
+}
+
+int from_close( un )
+    int                        un;
+{
+    switch ( un ) {
+       case MEGATRON :
+       case HEX2NAD :
+       case HEX2BIN :
+           return( hqx_close( KEEP ));
+           break;
+       case BIN2NAD :
+           return( bin_close( KEEP ));
+           break;
+       case NAD2BIN :
+           return( nad_close( KEEP ));
+           break;
+       case SINGLE2NAD :
+       case SINGLE2BIN :
+           return( single_close( KEEP ));
+       default :
+           return( -1 );
+           break;
+    }
+}
+
+int to_open( to, file, fh, flags )
+    int                        to, flags;
+    char               *file;
+    struct FHeader     *fh;
+{
+    switch ( to ) {
+       case MEGATRON :
+       case HEX2NAD :
+       case BIN2NAD :
+       case SINGLE2NAD :
+           return( nad_open( file, O_RDWR|O_CREAT|O_EXCL, fh, flags ));
+           break;
+       case NAD2BIN :
+       case HEX2BIN :
+       case SINGLE2BIN :
+           return( bin_open( file, O_RDWR|O_CREAT|O_EXCL, fh, flags ));
+           break;
+       default :
+           return( -1 );
+           break;
+    }
+}
+
+int to_write( to, fork, bufc )
+    int                        to;
+    int                        fork;
+    int                        bufc;
+{
+    switch ( to ) {
+       case MEGATRON :
+       case HEX2NAD :
+       case BIN2NAD :
+       case SINGLE2NAD :
+           return( nad_write( fork, forkbuf, bufc ));
+           break;
+       case NAD2BIN :
+       case HEX2BIN :
+       case SINGLE2BIN :
+           return( bin_write( fork, forkbuf, bufc ));
+           break;
+       default :
+           return( -1 );
+           break;
+    }
+}
+
+int to_close( to, keepflag )
+    int                        to;
+    int                        keepflag;
+{
+    switch ( to ) {
+       case MEGATRON :
+       case HEX2NAD :
+       case BIN2NAD :
+       case SINGLE2NAD :
+           return( nad_close( keepflag ));
+           break;
+       case NAD2BIN :
+       case HEX2BIN :
+       case SINGLE2BIN :
+           return( bin_close( keepflag ));
+           break;
+       default :
+           return( -1 );
+           break;
+    }
+}
+
+int megatron( path, module, newname, flags )
+    char       *path, *newname;
+    int                module, flags;
+{
+    struct stat                st;
+    struct FHeader     fh;
+    int                        bufc;
+    int                        fork;
+    int                        forkred;
+
+/*
+ * If the source file is not stdin, make sure it exists and
+ * that it is not a directory.
+ */
+
+    if ( strcmp( path, STDIN ) != 0 ) {
+       if ( stat( path, &st ) < 0 ) {
+           perror( path );
+           return( -1 );
+       }
+       if ( S_ISDIR( st.st_mode )) {
+           fprintf( stderr, "%s is a directory.\n", path );
+           return( 0 );
+       }
+    }
+
+/*
+ * Open the source file and fill in the file header structure.
+ */
+
+    memset( &fh, 0, sizeof( fh ));
+    if ( from_open( module, path, &fh ) < 0 ) {
+       return( -1 );
+    }
+
+    if ( flags & OPTION_HEADERONLY ) {
+        time_t t;
+       char buf[5] = "";
+        int i;
+
+        printf("name:               %s\n",fh.name);
+        printf("comment:            %s\n",fh.comment);
+       memcpy(&buf, &fh.finder_info.fdCreator, sizeof(u_int32_t));
+       printf("creator:            '%4s'\n", buf);
+       memcpy(&buf, &fh.finder_info.fdType, sizeof(u_int32_t));
+       printf("type:               '%4s'\n", buf);
+        for(i=0; i < NUMFORKS; ++i) 
+         printf("fork length[%d]:     %u\n", i, ntohl(fh.forklen[i]));
+       t = AD_DATE_TO_UNIX(fh.create_date);
+        printf("creation date:      %s", ctime(&t));
+       t = AD_DATE_TO_UNIX(fh.mod_date);
+        printf("modification date:  %s", ctime(&t));
+       t = AD_DATE_TO_UNIX(fh.backup_date);
+        printf("backup date:        %s", ctime(&t));
+       return( from_close( module ));
+    }
+    
+/*
+ * Open the target file and write out the file header info.
+ * set the header to the new filename if it has been supplied.
+ */
+
+    if (*newname)
+        strcpy(fh.name, newname);
+
+    if ( to_open( module, path, &fh, flags ) < 0 ) {
+       (void)from_close( module );
+       return( -1 );
+    }
+
+/*
+ * Read in and write out the data and resource forks.
+ */
+
+    for ( fork = 0; fork < NUMFORKS ; fork++ ) {
+       forkred = 0;
+       while(( bufc = from_read( module, fork, forkbuf, sizeof( forkbuf )))
+               > 0 ) {
+           if ( to_write( module, fork, bufc ) != bufc ) {
+               fprintf( stderr, "%s: Probable write error\n", path );
+               to_close( module, TRASH );
+               (void)from_close( module );
+               return( -1 );
+           }
+           forkred += bufc;
+       }
+#if DEBUG
+       fprintf( stderr, "megatron: forkred is \t\t%d\n", forkred );
+       fprintf( stderr, "megatron: fh.forklen[%d] is \t%d\n", fork, 
+               ntohl( fh.forklen[ fork ] ));
+#endif
+       if (( bufc < 0 ) || ( forkred != ntohl( fh.forklen[ fork ] ))) {
+           fprintf( stderr, "%s: Problem with input, dude\n", path );
+           to_close( module, TRASH );
+           (void)from_close( module );
+           return( -1 );
+       }
+    }
+
+/*
+ * Close up the files, and get out of here.
+ */
+
+    if ( to_close( module, KEEP ) < 0 ) {
+       perror( "megatron:" );
+       (void)to_close( module, TRASH );
+    }
+    return( from_close( module ));
+}
+
+int main( argc, argv )
+    int                argc;
+    char       **argv;
+{
+    int                rc, c;
+    int                rv = 0;
+    int                converts = sizeof(name) / sizeof(char *);
+    int                module = -1;
+    int         flags = 0;
+    char       *progname, newname[ADEDLEN_NAME + 1];
+
+    progname = strrchr( argv[ 0 ], '/' );
+    if (( progname == NULL ) || ( progname == '\0' )) {
+       progname = argv[ 0 ];
+    } else progname++;
+
+#if DEBUG
+    if ( CONVERTS != converts ) {
+       fprintf( stderr, "megatron: list of program links messed up\n" );
+       return( -1 );
+    }
+#endif
+
+    for ( c = 0 ; (( c < converts ) && ( module < 0 )) ; ++c ) {
+       if ( strcmp( name[ c ], progname ) == 0 ) module = c;
+    }
+    if ( module == -1 ) module = ( converts - 1 );
+    if ((module == NADHEADER) || (module == BINHEADER))
+      flags |= OPTION_HEADERONLY;
+
+    if ( argc == 1 ) {
+       return( megatron( STDIN, module, newname, flags ));
+    }
+
+    *newname = '\0';
+    for ( c = 1 ; c < argc ; ++c ) {
+        if ( strcmp( argv [ c ], "--header" ) == 0 ) {
+           flags |= OPTION_HEADERONLY;
+           continue;
+       }
+       if ( strcmp( argv [ c ], "--filename" ) == 0 ) {
+         if(++c < argc) strncpy(newname,argv[c], ADEDLEN_NAME);
+         continue;
+       }
+       if (strcmp(argv[c], "--stdout") == 0) {
+         flags |= OPTION_STDOUT;
+         continue;
+       }
+         
+       if ( rc = megatron( argv[ c ], module, newname, flags) != 0 ) {
+           rv = rc;
+       }
+       *newname = '\0';
+    }
+    return( rv );
+}
+
diff --git a/bin/megatron/megatron.h b/bin/megatron/megatron.h
new file mode 100644 (file)
index 0000000..9190d49
--- /dev/null
@@ -0,0 +1,86 @@
+#include <atalk/adouble.h>
+
+#ifndef        STDIN
+#      define  STDIN   "-"
+#endif
+
+/*
+    Where it matters, data stored in either of these two structs is in
+    network byte order.  Any routines that need to interpret this data
+    locally would need to do the conversion.  Mostly this affects the 
+    fork length variables.  Time values are affected as well, if any
+    routines actually need to look at them.
+ */
+
+#define DATA           0
+#define RESOURCE       1
+#define NUMFORKS       2
+
+#define HEX2NAD                0       /* unhex */
+#define BIN2NAD                1       /* unbin */
+#define SINGLE2NAD     2       /* unsingle */
+#define NAD2BIN                3       /* macbinary */
+#define HEX2BIN                4       /* hqx2bin */
+#define SINGLE2BIN     5       /* single2bin */
+#define NADHEADER       6       /* header */
+#define BINHEADER       7       /* mac binary header */
+#define MEGATRON       8       /* megatron, default, usually HEX2NAD */
+#define CONVERTS       9       /* # conversions defined */
+
+#define OPTION_NONE       (0)
+#define OPTION_HEADERONLY (1 << 0)
+#define OPTION_STDOUT     (1 << 2)
+
+struct FInfo {
+    u_int32_t          fdType;
+    u_int32_t          fdCreator;
+    u_int16_t          fdFlags;
+    u_int32_t          fdLocation;
+    u_int16_t          fdFldr;
+};
+
+struct FXInfo {
+    u_int16_t           fdIconID;
+    u_int16_t           fdUnused[3];
+    u_int8_t            fdScript;
+    u_int8_t            fdXFlags;
+    u_int16_t           fdComment;
+    u_int32_t           fdPutAway;
+};
+
+struct FHeader {
+    char               name[ ADEDLEN_NAME ];
+    char               comment[ ADEDLEN_COMMENT ];
+    u_int32_t          forklen[ NUMFORKS ];
+    u_int32_t          create_date;
+    u_int32_t          mod_date;
+    u_int32_t          backup_date;
+    struct FInfo       finder_info;
+    struct FXInfo       finder_xinfo;
+};
+
+#define MAC_DATE_TO_UNIX(a)   (ntohl(a) - 2082844800U)
+#define MAC_DATE_FROM_UNIX(a) (htonl((a) + 2082844800U))
+#define AD_DATE_FROM_MAC(a)   (htonl(ntohl(a) - 3029529600U)) 
+#define AD_DATE_TO_MAC(a)     (htonl(ntohl(a) + 3029529600U))
+
+#define FILEIOFF_CREATE        0
+#define FILEIOFF_MODIFY        4
+#define FILEIOFF_BACKUP        8
+#define FILEIOFF_ATTR  14
+#define FINDERIOFF_TYPE                0
+#define FINDERIOFF_CREATOR     4
+#define FINDERIOFF_FLAGS       8
+#define FINDERIOFF_LOC         10
+#define FINDERIOFF_FLDR                14
+#define FINDERIOFF_SCRIPT       24
+#define FINDERIOFF_XFLAGS       25
+
+#define        TRASH           0
+#define        KEEP            1
+
+#ifndef S_ISDIR
+#      define S_ISDIR(s)       (( s & S_IFMT ) == S_IFDIR )
+#endif
+
+extern char *mtoupath( char *);
diff --git a/bin/megatron/nad.c b/bin/megatron/nad.c
new file mode 100644 (file)
index 0000000..b82a8d5
--- /dev/null
@@ -0,0 +1,435 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <dirent.h>
+#include <fcntl.h>
+
+#include <atalk/adouble.h>
+#include <netatalk/endian.h>
+#include "megatron.h"
+
+#define        DEBUG           0
+
+static char            hexdig[] = "0123456789abcdef";
+
+char *mtoupath( mpath )
+    char       *mpath;
+{
+    static char        upath[ MAXNAMLEN  + 1];
+    char       *m, *u;
+    int                i = 0;
+
+    m = mpath;
+    u = upath;
+    while ( *m != '\0' ) {
+#if AD_VERSION == AD_VERSION1
+       if ( !isascii( *m ) || *m == '/' || ( i == 0 && *m == '.' )) {
+#else
+       if (!isprint(*m) || *m == '/' || ( i == 0 && (*m == '.' ))) {
+#endif
+           *u++ = ':';
+           *u++ = hexdig[ ( *m & 0xf0 ) >> 4 ];
+           *u++ = hexdig[ *m & 0x0f ];
+       } else {
+#ifdef DOWNCASE
+           *u++ = ( isupper( *m )) ? tolower( *m ) : *m;
+#else DOWNCASE
+           *u++ = *m;
+#endif DOWNCASE
+       }
+       i++;
+       m++;
+    }
+    *u = '\0';
+    return( upath );
+}
+
+
+#define hextoint( c )  ( isdigit( c ) ? c - '0' : c + 10 - 'a' )
+#define islxdigit(x)   (!isupper(x)&&isxdigit(x))
+
+char *utompath( upath )
+    char       *upath;
+{
+    static char        mpath[ MAXNAMLEN  + 1];
+    char       *m, *u;
+    int h;
+
+    m = mpath;
+    u = upath;
+    while ( *u != '\0' ) {
+        if (*u == ':' && *(u + 1) != '\0' && islxdigit(*(u+1)) &&
+           *(u+2) != '\0' && islxdigit(*(u+2))) {
+         u++;
+         h = hextoint(*u) << 4;
+         u++;
+         h |= hextoint(*u);
+         *m = h;
+       } else {
+#ifdef DOWNCASE
+         *m = diatolower(*u);
+#else DOWNCASE
+         *m = *u;
+#endif DOWNCASE
+       }
+       u++;
+       m++;
+    }
+    *m = '\0';
+    return( mpath );
+}
+
+#if HEXOUTPUT
+    int                        hexfork[ NUMFORKS ];
+#endif
+
+struct nad_file_data {
+    char               macname[ MAXPATHLEN + 1 ];
+    char               adpath[ 2 ][ MAXPATHLEN + 1];
+    int                        offset[ NUMFORKS ];
+    struct adouble     ad;
+} nad;
+
+nad_open( path, openflags, fh, options )
+    char               *path;
+    int                        openflags, options;
+    struct FHeader     *fh;
+{
+    struct stat                st;
+    int                        fork;
+
+/*
+ * Depending upon openflags, set up nad.adpath for the open.  If it 
+ * is for write, then stat the current directory to get its mode.
+ * Open the file.  Either fill or grab the adouble information.
+ */
+    memset(&nad.ad, 0, sizeof(nad.ad));
+    if ( openflags == O_RDONLY ) {
+       strcpy( nad.adpath[0], path );
+       strcpy( nad.adpath[1], 
+               ad_path( nad.adpath[0], ADFLAGS_DF|ADFLAGS_HF ));
+       for ( fork = 0 ; fork < NUMFORKS ; fork++ ) {
+           if ( stat( nad.adpath[ fork ], &st ) < 0 ) {
+               if ( errno == ENOENT ) {
+                   fprintf( stderr, "%s is not an adouble file.\n", path );
+               } else {
+                   perror( "stat of adouble file failed" );
+               }
+               return( -1 );
+           }
+       }
+
+#if DEBUG
+    fprintf(stderr, "%s is adpath[0]\n", nad.adpath[0]);
+    fprintf(stderr, "%s is adpath[1]\n", nad.adpath[1]);
+#endif
+       if ( ad_open( nad.adpath[ 0 ], ADFLAGS_DF|ADFLAGS_HF,
+               openflags, (int)( st.st_mode & 0666 ), &nad.ad) < 0 ) {
+           perror( nad.adpath[ 0 ] );
+           return( -1 );
+       }
+       return( nad_header_read( fh ));
+
+    } else {
+       strcpy( nad.macname, fh->name );
+       strcpy( nad.adpath[0], mtoupath( nad.macname ));
+       strcpy( nad.adpath[1], 
+               ad_path( nad.adpath[0], ADFLAGS_DF|ADFLAGS_HF ));
+#if DEBUG
+    fprintf(stderr, "%s\n", nad.macname);
+    fprintf(stderr, "%s is adpath[0]\n", nad.adpath[0]);
+    fprintf(stderr, "%s is adpath[1]\n", nad.adpath[1]);
+#endif
+       if ( stat( ".", &st ) < 0 ) {
+           perror( "stat of . failed" );
+           return( -1 );
+       }
+       (void)umask( 0 );
+       if ( ad_open( nad.adpath[ 0 ], ADFLAGS_DF|ADFLAGS_HF,
+               openflags, (int)( st.st_mode & 0666 ), &nad.ad) < 0 ) {
+           perror( nad.adpath[ 0 ] );
+           return( -1 );
+       }
+       return( nad_header_write( fh ));
+    }
+}
+
+nad_header_read( fh )
+    struct FHeader     *fh;
+{
+    u_int32_t          temptime;
+    struct stat                st;
+
+    memcpy( nad.macname, ad_entry( &nad.ad, ADEID_NAME ), 
+           ad_getentrylen( &nad.ad, ADEID_NAME ));
+    nad.macname[ ad_getentrylen( &nad.ad, ADEID_NAME ) ] = '\0';
+    strcpy( fh->name, nad.macname );
+
+    /* just in case there's nothing in macname */
+    if (*fh->name == '\0')
+      strcpy(fh->name, utompath(nad.adpath[DATA]));
+
+    if ( stat( nad.adpath[ DATA ], &st ) < 0 ) {
+       perror( "stat of datafork failed" );
+       return( -1 );
+    }
+    fh->forklen[ DATA ] = htonl( st.st_size );
+    fh->forklen[ RESOURCE ] = htonl( ad_getentrylen( &nad.ad, ADEID_RFORK ));
+    fh->comment[0] = '\0';
+
+#if DEBUG
+    fprintf( stderr, "macname of file\t\t\t%.*s\n", strlen( fh->name ), 
+           fh->name );
+    fprintf( stderr, "size of data fork\t\t%d\n", 
+           ntohl( fh->forklen[ DATA ] ));
+    fprintf( stderr, "size of resource fork\t\t%d\n", 
+           ntohl( fh->forklen[ RESOURCE ] ));
+    fprintf( stderr, "get info comment\t\t\"%s\"\n", fh->comment );
+#endif
+
+    ad_getdate(&nad.ad, AD_DATE_CREATE, &temptime);
+    memcpy( &fh->create_date, &temptime, sizeof( temptime ));
+    ad_getdate(&nad.ad, AD_DATE_MODIFY, &temptime);
+    memcpy( &fh->mod_date, &temptime, sizeof( temptime ));
+    ad_getdate(&nad.ad, AD_DATE_BACKUP, &temptime);
+    memcpy( &fh->backup_date, &temptime, sizeof( temptime ));
+
+#if DEBUG
+    memcpy( &temptime, &fh->create_date, sizeof( temptime ));
+    temptime = AD_DATE_TO_UNIX(temptime);
+    fprintf( stderr, "create_date seconds\t\t%lu\n", temptime );
+    memcpy( &temptime, &fh->mod_date, sizeof( temptime ));
+    temptime = AD_DATE_TO_UNIX(temptime);
+    fprintf( stderr, "mod_date seconds\t\t%lu\n", temptime );
+    memcpy( &temptime, &fh->backup_date, sizeof( temptime ));
+    temptime = AD_DATE_TO_UNIX(temptime);
+    fprintf( stderr, "backup_date seconds\t\t%lu\n", temptime );
+    fprintf( stderr, "size of finder_info\t\t%d\n", sizeof( fh->finder_info ));
+#endif
+
+    memcpy(&fh->finder_info.fdType,
+           ad_entry( &nad.ad, ADEID_FINDERI ) + FINDERIOFF_TYPE,
+          sizeof( fh->finder_info.fdType ));
+    memcpy(&fh->finder_info.fdCreator,
+          ad_entry( &nad.ad, ADEID_FINDERI ) + FINDERIOFF_CREATOR,
+          sizeof( fh->finder_info.fdCreator ));
+    memcpy(&fh->finder_info.fdFlags,
+          ad_entry( &nad.ad, ADEID_FINDERI ) + FINDERIOFF_FLAGS,
+          sizeof( fh->finder_info.fdFlags ));
+    memcpy(&fh->finder_info.fdLocation,
+          ad_entry( &nad.ad, ADEID_FINDERI ) + FINDERIOFF_LOC,
+          sizeof( fh->finder_info.fdLocation ));
+    memcpy(&fh->finder_info.fdFldr,
+         ad_entry( &nad.ad, ADEID_FINDERI ) + FINDERIOFF_FLDR,
+         sizeof( fh->finder_info.fdFldr ));
+    memcpy(&fh->finder_xinfo.fdScript,
+          ad_entry( &nad.ad, ADEID_FINDERI ) + FINDERIOFF_SCRIPT,
+          sizeof(fh->finder_xinfo.fdScript));
+    memcpy(&fh->finder_xinfo.fdXFlags,
+          ad_entry( &nad.ad, ADEID_FINDERI ) + FINDERIOFF_XFLAGS,
+          sizeof(fh->finder_xinfo.fdXFlags));
+
+#if DEBUG
+    {
+       short           flags;
+       fprintf( stderr, "finder_info.fdType\t\t%.*s\n", 
+               sizeof( fh->finder_info.fdType ), &fh->finder_info.fdType );
+       fprintf( stderr, "finder_info.fdCreator\t\t%.*s\n", 
+               sizeof( fh->finder_info.fdCreator ),
+               &fh->finder_info.fdCreator );
+       fprintf( stderr, "nad type and creator\t\t%.*s\n\n", 
+               sizeof( fh->finder_info.fdType ) + 
+               sizeof( fh->finder_info.fdCreator ), 
+               ad_entry( &nad.ad, ADEID_FINDERI ));
+       memcpy(&flags, ad_entry( &nad.ad, ADEID_FINDERI ) + 
+              FINDERIOFF_FLAGS, sizeof( flags ));
+       fprintf( stderr, "nad.ad flags\t\t\t%x\n", flags );
+       fprintf( stderr, "fh flags\t\t\t%x\n", fh->finder_info.fdFlags );
+       fprintf(stderr, "fh script\t\t\t%x\n", fh->finder_xinfo.fdScript);
+       fprintf(stderr, "fh xflags\t\t\t%x\n", fh->finder_xinfo.fdXFlags);
+    }
+#endif
+
+    nad.offset[ DATA ] = nad.offset[ RESOURCE ] = 0;
+
+    return( 0 );
+
+}
+
+nad_header_write( fh )
+    struct FHeader     *fh;
+{
+    u_int32_t          temptime;
+
+    ad_setentrylen( &nad.ad, ADEID_NAME, strlen( nad.macname ));
+    memcpy( ad_entry( &nad.ad, ADEID_NAME ), nad.macname, 
+           ad_getentrylen( &nad.ad, ADEID_NAME ));
+    ad_setentrylen( &nad.ad, ADEID_COMMENT, strlen( fh->comment ));
+    memcpy( ad_entry( &nad.ad, ADEID_COMMENT ), fh->comment, 
+           ad_getentrylen( &nad.ad, ADEID_COMMENT ));
+    ad_setentrylen( &nad.ad, ADEID_RFORK, ntohl( fh->forklen[ RESOURCE ] ));
+
+#if DEBUG
+    fprintf( stderr, "ad_getentrylen\n" );
+    fprintf( stderr, "ADEID_FINDERI\t\t\t%d\n", 
+           ad_getentrylen( &nad.ad, ADEID_FINDERI ));
+    fprintf( stderr, "ADEID_RFORK\t\t\t%d\n", 
+           ad_getentrylen( &nad.ad, ADEID_RFORK ));
+    fprintf( stderr, "ADEID_NAME\t\t\t%d\n",
+           ad_getentrylen( &nad.ad, ADEID_NAME ));
+    fprintf( stderr, "ad_entry of ADEID_NAME\t\t%.*s\n",
+           ad_getentrylen( &nad.ad, ADEID_NAME ), 
+           ad_entry( &nad.ad, ADEID_NAME ));
+    fprintf( stderr, "ADEID_COMMENT\t\t\t%d\n",
+            ad_getentrylen( &nad.ad, ADEID_COMMENT ));
+#endif
+
+    memcpy( &temptime, &fh->create_date, sizeof( temptime ));
+    ad_setdate(&nad.ad, AD_DATE_CREATE, temptime);
+    memcpy( &temptime, &fh->mod_date, sizeof( temptime ));
+    ad_setdate(&nad.ad, AD_DATE_MODIFY, temptime);
+
+#if DEBUG
+    ad_getdate(&nad.ad, AD_DATE_CREATE, &temptime);
+    temptime = AD_DATE_TO_UNIX(temptime);
+    fprintf(stderr, "FILEIOFF_CREATE seconds\t\t%ld\n", temptime );
+    ad_getdate(&nad.ad, AD_DATE_MODIFY, &temptime);
+    temptime = AD_DATE_TO_UNIX(temptime);
+    fprintf(stderr, "FILEIOFF_MODIFY seconds\t\t%ld\n", temptime );
+#endif
+
+    memset( ad_entry( &nad.ad, ADEID_FINDERI ), 0, ADEDLEN_FINDERI );
+    memcpy( ad_entry( &nad.ad, ADEID_FINDERI ) + FINDERIOFF_TYPE, 
+           &fh->finder_info.fdType, sizeof( fh->finder_info.fdType ));
+    memcpy( ad_entry( &nad.ad, ADEID_FINDERI ) + FINDERIOFF_CREATOR,
+          &fh->finder_info.fdCreator, sizeof( fh->finder_info.fdCreator ));
+    memcpy( ad_entry( &nad.ad, ADEID_FINDERI ) + FINDERIOFF_FLAGS,
+           &fh->finder_info.fdFlags, sizeof( fh->finder_info.fdFlags ));
+    memcpy( ad_entry( &nad.ad, ADEID_FINDERI ) + FINDERIOFF_LOC,
+           &fh->finder_info.fdLocation,sizeof( fh->finder_info.fdLocation ));
+    memcpy( ad_entry( &nad.ad, ADEID_FINDERI ) + FINDERIOFF_FLDR,
+           &fh->finder_info.fdFldr, sizeof( fh->finder_info.fdFldr ));
+    memcpy( ad_entry( &nad.ad, ADEID_FINDERI ) + FINDERIOFF_SCRIPT,
+           &fh->finder_xinfo.fdScript, sizeof( fh->finder_xinfo.fdScript ));
+    memcpy( ad_entry( &nad.ad, ADEID_FINDERI ) + FINDERIOFF_XFLAGS,
+           &fh->finder_xinfo.fdXFlags, sizeof( fh->finder_xinfo.fdXFlags));
+
+
+#if DEBUG
+    {
+       short           flags;
+       memcpy(&flags, ( ad_entry( &nad.ad, ADEID_FINDERI ) + FINDERIOFF_FLAGS,
+               sizeof( flags ));
+       fprintf( stderr, "nad.ad flags\t\t\t%x\n", flags );
+       fprintf( stderr, "fh flags\t\t\t%x\n", fh->finder_info.fdFlags );
+       fprintf( stderr, "fh xflags\t\t\t%x\n", fh->finder_xinfo.fdXFlags );
+       fprintf( stderr, "type and creator\t\t%.*s\n\n", 
+               sizeof( fh->finder_info.fdType ) + 
+               sizeof( fh->finder_info.fdCreator ),
+               ad_entry( &nad.ad, ADEID_FINDERI ));
+    }
+#endif
+
+#if HEXOUTPUT
+    hexfork[ DATA ] = open( "datafork", O_WRONLY|O_CREAT, 0622 );
+    hexfork[ RESOURCE ] = open( "resfork", O_WRONLY|O_CREAT, 0622 );
+#endif
+
+    nad.offset[ DATA ] = nad.offset[ RESOURCE ] = 0;
+    ad_flush( &nad.ad, ADFLAGS_DF|ADFLAGS_HF );
+
+    return( 0 );
+}
+
+int                    forkeid[] = { ADEID_DFORK, ADEID_RFORK };
+
+nad_read( fork, forkbuf, bufc )
+    int                        fork;
+    char               *forkbuf;
+    int                        bufc;
+{
+    int                        cc = 0;
+
+#if DEBUG
+    fprintf( stderr, "Entering nad_read\n" );
+#endif
+
+    if (( cc = ad_read( &nad.ad, forkeid[ fork ], nad.offset[ fork ], 
+           forkbuf, bufc)) < 0 )  {
+       perror( "Reading the appledouble file:" );
+       return( cc );
+    }
+    nad.offset[ fork ] += cc;
+
+#if DEBUG
+    fprintf( stderr, "Exiting nad_read\n" );
+#endif
+
+    return( cc );
+}
+
+nad_write( fork, forkbuf, bufc )
+    int                        fork;
+    char               *forkbuf;
+    int                        bufc;
+{
+    char               *buf_ptr;
+    int                        writelen;
+    int                        cc = 0;
+
+#if DEBUG
+    fprintf( stderr, "Entering nad_write\n" );
+#endif
+
+#if HEXOUTPUT
+    write( hexfork[ fork ], forkbuf, bufc );
+#endif
+
+    writelen = bufc;
+    buf_ptr = forkbuf;
+
+    while (( writelen > 0 ) && ( cc >= 0 )) {
+       cc =  ad_write( &nad.ad, forkeid[ fork ], nad.offset[ fork ], 
+               0, buf_ptr, writelen);
+       nad.offset[ fork ] += cc;
+       buf_ptr += cc;
+       writelen -= cc;
+    }
+    if ( cc < 0 ) {
+       perror( "Writing the appledouble file:" );
+       return( cc );
+    }
+
+    return( bufc );
+}
+
+nad_close( status )
+int                    status;
+{
+    int                        rv;
+    if ( status == KEEP ) {
+       if (( rv = ad_flush( &nad.ad, ADFLAGS_DF|ADFLAGS_HF )) < 0 ) {
+           fprintf( stderr, "nad_close rv for flush %d\n", rv );
+           return( rv );
+       }
+       if (( rv = ad_close( &nad.ad, ADFLAGS_DF|ADFLAGS_HF )) < 0 ) {
+           fprintf( stderr, "nad_close rv for close %d\n", rv );
+           return( rv );
+       }
+    } else if ( status == TRASH ) {
+       if ( unlink( nad.adpath[ 0 ] ) < 0 ) {
+           perror ( nad.adpath[ 0 ] );
+       }
+       if ( unlink( nad.adpath[ 1 ] ) < 0 ) {
+           perror ( nad.adpath[ 1 ] );
+       }
+       return( 0 );
+    } else return( -1 );
+}
diff --git a/bin/megatron/updcrc.c b/bin/megatron/updcrc.c
new file mode 100644 (file)
index 0000000..75079fb
--- /dev/null
@@ -0,0 +1,191 @@
+/* updcrc(3), crc(1) - calculate crc polynomials
+ *
+ * Calculate, intelligently, the CRC of a dataset incrementally given a 
+ * buffer full at a time.
+ * 
+ * Usage:
+ *     newcrc = updcrc( oldcrc, bufadr, buflen )
+ *             unsigned int oldcrc, buflen;
+ *             char *bufadr;
+ *
+ * Compiling with -DTEST creates a program to print the CRC of stdin to stdout.
+ * Compile with -DMAKETAB to print values for crctab to stdout.  If you change
+ *     the CRC polynomial parameters, be sure to do this and change
+ *     crctab's initial value.
+ *
+ * Notes:
+ *  Regards the data stream as an integer whose MSB is the MSB of the first
+ *  byte recieved.  This number is 'divided' (using xor instead of subtraction)
+ *  by the crc-polynomial P.
+ *  XMODEM does things a little differently, essentially treating the LSB of
+ * the first data byte as the MSB of the integer. Define SWAPPED to make
+ * things behave in this manner.
+ *
+ * Author:     Mark G. Mendel, 7/86
+ *             UUCP: ihnp4!umn-cs!hyper!mark, GEnie: mgm
+ */
+
+/* The CRC polynomial.
+ * These 4 values define the crc-polynomial.
+ * If you change them, you must change crctab[]'s initial value to what is
+ * printed by initcrctab() [see 'compile with -DMAKETAB' above].
+ */
+    /* Value used by:                  CCITT   XMODEM  ARC     */
+#define        P        0x1021 /* the poly:    0x1021  0x1021  A001    */
+#define INIT_CRC 0L    /* init value:  -1      0       0       */
+/*#define SWAPPED      /* bit order:   undef   defined defined */
+#define W      16      /* bits in CRC:16       16      16      */
+
+    /* data type that holds a W-bit unsigned integer */
+#if W <= 16
+#  define WTYPE        unsigned short
+#else
+#  define WTYPE   u_int32_t
+#endif
+
+    /* the number of bits per char: don't change it. */
+#define B      8
+
+static WTYPE crctab[1<<B] = /* as calculated by initcrctab() */ {
+0x0,  0x1021,  0x2042,  0x3063,  0x4084,  0x50a5,  0x60c6,  0x70e7,
+0x8108,  0x9129,  0xa14a,  0xb16b,  0xc18c,  0xd1ad,  0xe1ce,  0xf1ef,
+0x1231,  0x210,  0x3273,  0x2252,  0x52b5,  0x4294,  0x72f7,  0x62d6,
+0x9339,  0x8318,  0xb37b,  0xa35a,  0xd3bd,  0xc39c,  0xf3ff,  0xe3de,
+0x2462,  0x3443,  0x420,  0x1401,  0x64e6,  0x74c7,  0x44a4,  0x5485,
+0xa56a,  0xb54b,  0x8528,  0x9509,  0xe5ee,  0xf5cf,  0xc5ac,  0xd58d,
+0x3653,  0x2672,  0x1611,  0x630,  0x76d7,  0x66f6,  0x5695,  0x46b4,
+0xb75b,  0xa77a,  0x9719,  0x8738,  0xf7df,  0xe7fe,  0xd79d,  0xc7bc,
+0x48c4,  0x58e5,  0x6886,  0x78a7,  0x840,  0x1861,  0x2802,  0x3823,
+0xc9cc,  0xd9ed,  0xe98e,  0xf9af,  0x8948,  0x9969,  0xa90a,  0xb92b,
+0x5af5,  0x4ad4,  0x7ab7,  0x6a96,  0x1a71,  0xa50,  0x3a33,  0x2a12,
+0xdbfd,  0xcbdc,  0xfbbf,  0xeb9e,  0x9b79,  0x8b58,  0xbb3b,  0xab1a,
+0x6ca6,  0x7c87,  0x4ce4,  0x5cc5,  0x2c22,  0x3c03,  0xc60,  0x1c41,
+0xedae,  0xfd8f,  0xcdec,  0xddcd,  0xad2a,  0xbd0b,  0x8d68,  0x9d49,
+0x7e97,  0x6eb6,  0x5ed5,  0x4ef4,  0x3e13,  0x2e32,  0x1e51,  0xe70,
+0xff9f,  0xefbe,  0xdfdd,  0xcffc,  0xbf1b,  0xaf3a,  0x9f59,  0x8f78,
+0x9188,  0x81a9,  0xb1ca,  0xa1eb,  0xd10c,  0xc12d,  0xf14e,  0xe16f,
+0x1080,  0xa1,  0x30c2,  0x20e3,  0x5004,  0x4025,  0x7046,  0x6067,
+0x83b9,  0x9398,  0xa3fb,  0xb3da,  0xc33d,  0xd31c,  0xe37f,  0xf35e,
+0x2b1,  0x1290,  0x22f3,  0x32d2,  0x4235,  0x5214,  0x6277,  0x7256,
+0xb5ea,  0xa5cb,  0x95a8,  0x8589,  0xf56e,  0xe54f,  0xd52c,  0xc50d,
+0x34e2,  0x24c3,  0x14a0,  0x481,  0x7466,  0x6447,  0x5424,  0x4405,
+0xa7db,  0xb7fa,  0x8799,  0x97b8,  0xe75f,  0xf77e,  0xc71d,  0xd73c,
+0x26d3,  0x36f2,  0x691,  0x16b0,  0x6657,  0x7676,  0x4615,  0x5634,
+0xd94c,  0xc96d,  0xf90e,  0xe92f,  0x99c8,  0x89e9,  0xb98a,  0xa9ab,
+0x5844,  0x4865,  0x7806,  0x6827,  0x18c0,  0x8e1,  0x3882,  0x28a3,
+0xcb7d,  0xdb5c,  0xeb3f,  0xfb1e,  0x8bf9,  0x9bd8,  0xabbb,  0xbb9a,
+0x4a75,  0x5a54,  0x6a37,  0x7a16,  0xaf1,  0x1ad0,  0x2ab3,  0x3a92,
+0xfd2e,  0xed0f,  0xdd6c,  0xcd4d,  0xbdaa,  0xad8b,  0x9de8,  0x8dc9,
+0x7c26,  0x6c07,  0x5c64,  0x4c45,  0x3ca2,  0x2c83,  0x1ce0,  0xcc1,
+0xef1f,  0xff3e,  0xcf5d,  0xdf7c,  0xaf9b,  0xbfba,  0x8fd9,  0x9ff8,
+0x6e17,  0x7e36,  0x4e55,  0x5e74,  0x2e93,  0x3eb2,  0xed1,  0x1ef0,
+} ;
+
+WTYPE
+updcrc( icrc, icp, icnt )
+    WTYPE icrc;
+    unsigned char *icp;
+    int icnt;
+{
+    register WTYPE crc = icrc;
+    register unsigned char *cp = icp;
+    register int cnt = icnt;
+
+    while( cnt-- ) {
+#ifndef SWAPPED
+       crc = (crc<<B) ^ crctab[(crc>>(W-B)) ^ *cp++];
+#else
+       crc = (crc>>B) ^ crctab[(crc & ((1<<B)-1)) ^ *cp++]; 
+#endif SWAPPED
+    }
+
+    return( crc );
+}
+
+#ifdef MAKETAB
+
+#include <stdio.h>
+main()
+{
+    initcrctab();
+}
+
+initcrctab()
+{
+    register  int b, i;
+    WTYPE v;
+
+    
+    for( b = 0; b <= (1<<B)-1; ++b ) {
+#ifndef SWAPPED
+       for( v = b<<(W-B), i = B; --i >= 0; )
+           v = v & ((WTYPE)1<<(W-1)) ? (v<<1)^P : v<<1;
+#else
+       for( v = b, i = B; --i >= 0; )
+           v = v & 1 ? (v>>1)^P : v>>1;
+#endif     
+       crctab[b] = v;
+
+       printf( "0x%lx,", v & ((1L<<W)-1L));
+       if( (b&7) == 7 )
+           printf("\n" );
+       else
+           printf("  ");
+    }
+}
+#endif
+
+#ifdef TEST
+
+#include <stdio.h>
+#include <fcntl.h>
+
+#define MAXBUF 4096
+
+
+
+main( ac, av )
+    int ac; char **av;
+{
+    int fd;
+    int nr;
+    int i;
+    char buf[MAXBUF];
+    WTYPE crc, crc2;
+
+    fd = 0;
+    if( ac > 1 )
+       if( (fd = open( av[1], O_RDONLY )) < 0 ) {
+           perror( av[1] );
+           exit( -1 );
+       }
+    crc = crc2 = INIT_CRC;
+
+    while( (nr = read( fd, buf, MAXBUF )) > 0 ) {
+       crc = updcrc( crc, buf, nr );
+    }
+
+    if( nr != 0 )
+       perror( "reading" );
+    else {
+       printf( "%lx\n", crc );
+    }
+
+#ifdef MAGICCHECK
+    /* tack one's complement of crc onto data stream, and
+       continue crc calculation.  Should get a constant (magic number)
+       dependent only on P, not the data.
+     */
+    crc2 = crc ^ -1L;
+    for( nr = W-B; nr >= 0; nr -= B ) {
+       buf[0] = (crc2 >> nr);
+       crc = updcrc(crc, buf, 1);
+    }
+
+    /* crc should now equal magic */
+    buf[0] = buf[1] = buf[2] = buf[3] = 0;
+    printf( "magic test: %lx =?= %lx\n", crc, updcrc(-1, buf, W/B));
+#endif MAGICCHECK
+}
+
+#endif
diff --git a/bin/nbp/Makefile b/bin/nbp/Makefile
new file mode 100644 (file)
index 0000000..44d1696
--- /dev/null
@@ -0,0 +1,56 @@
+TARGETS=       nbplkup nbprgstr nbpunrgstr
+SRC=   nbplkup.c nbprgstr.c nbpunrgstr.c
+
+INCPATH=       -I../../include
+CFLAGS=        ${DEFS} ${OPTOPTS} ${INCPATH}
+LIBS=  -latalk ${ADDLIBS}
+TAGSFILE=      tags
+CC=    cc
+INSTALL=       install
+LIBDIRS=       -L../../libatalk
+
+all : ${TARGETS}
+
+nbplkup        : nbplkup.o ../../libatalk/libatalk.a
+       ${CC} ${CFLAGS} -o nbplkup nbplkup.o ${LIBDIRS} ${LIBS}
+
+nbprgstr : nbprgstr.o ../../libatalk/libatalk.a
+       ${CC} ${CFLAGS} -o nbprgstr nbprgstr.o ${LIBDIRS} ${LIBS}
+
+nbpunrgstr : nbpunrgstr.o ../../libatalk/libatalk.a
+       ${CC} ${CFLAGS} -o nbpunrgstr nbpunrgstr.o ${LIBDIRS} ${LIBS}
+
+install        : all
+       for i in ${TARGETS}; do ${INSTALL} -c $$i ${BINDIR}; done
+       chmod 700 ${BINDIR}/nbpunrgstr
+       chmod 700 ${BINDIR}/nbprgstr
+
+clean:
+       rm -f a.out core* *.o *.bak *[Ee]rrs tags
+       rm -f ${TARGETS}
+
+tags : ${SRC}
+       cwd=`pwd`; \
+       for i in ${SRC}; do \
+           ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+       done
+
+depend :
+       for i in ${SRC} ; do \
+           ${CC} -M ${DEFS} ${INCPATH} $$i | \
+           awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+               else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+               else rec = rec " " $$2 } } \
+               END { print rec } ' >> makedep; done
+       sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+       cat makedep >> Makefile.tmp
+       rm makedep
+       echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+       echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+       echo '# see make depend above' >> Makefile.tmp
+       rm -f Makefile.bak
+       cp Makefile Makefile.bak
+       mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
diff --git a/bin/nbp/nbplkup.c b/bin/nbp/nbplkup.c
new file mode 100644 (file)
index 0000000..bdd1033
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#include <sys/types.h>
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <atalk/nbp.h>
+#include <atalk/util.h>
+#include <string.h>
+#include <stdio.h>
+#if !defined( sun ) || !defined( i386 )
+#include <stdlib.h>
+#endif sun i386
+
+char *Obj = "=";
+char *Type = "=";
+char *Zone = "*";
+
+Usage( av0 )
+    char       *av0;
+{
+    char       *p;
+
+    if (( p = strrchr( av0, '/' )) == 0 ) {
+       p = av0;
+    } else {
+       p++;
+    }
+
+    printf( "Usage:\t%s [ -A address ] [ -r responses] [ obj:type@zone ]\n", p );
+    exit( 1 );
+}
+
+main( ac, av )
+    int                ac;
+    char       **av;
+{
+    struct nbpnve      *nn;
+    char               *name;
+    int                        i, c, nresp = 1000;
+    struct at_addr      addr;
+
+    extern char                *optarg;
+    extern int         optind;
+
+    memset(&addr, 0, sizeof(addr));
+    while (( c = getopt( ac, av, "r:A:" )) != EOF ) {
+       switch ( c ) {
+       case 'A':
+           if (!atalk_aton(optarg, &addr)) {
+               fprintf(stderr, "Bad address.\n");
+               exit(1);
+           }
+           break;
+       case 'r' :
+           nresp = atoi( optarg );
+           break;
+
+       default :
+           Usage( av[ 0 ] );
+           exit( 1 );
+       }
+    }
+
+    if (( nn = (struct nbpnve *)malloc( nresp * sizeof( struct nbpnve )))
+           == NULL ) {
+       perror( "malloc" );
+       exit( 1 );
+    }
+
+    if ( ac - optind > 1 ) {
+       Usage( av[ 0 ] );
+       exit( 1 );
+    }
+
+    /*
+     * Get default values from the environment. We need to copy out
+     * the results, here, since nbp_name returns it's parameters
+     * in static space, and we'll clobber them when we call it again
+     * later.
+     */
+    if (( name = getenv( "NBPLKUP" )) != NULL ) {
+       if ( nbp_name( name, &Obj, &Type, &Zone )) {
+           fprintf( stderr,
+                   "Environment variable syntax error: NBPLKUP = %s\n",
+                   name );
+           exit( 1 );
+       }
+
+       if (( name = (char *)malloc( strlen( Obj ) + 1 )) == NULL ) {
+           perror( "malloc" );
+           exit( 1 );
+       }
+       strcpy( name, Obj );
+       Obj = name;
+
+       if (( name = (char *)malloc( strlen( Type ) + 1 )) == NULL ) {
+           perror( "malloc" );
+           exit( 1 );
+       }
+       strcpy( name, Type );
+       Type = name;
+
+       if (( name = (char *)malloc( strlen( Zone ) + 1 )) == NULL ) {
+           perror( "malloc" );
+           exit( 1 );
+       }
+       strcpy( name, Zone );
+       Zone = name;
+
+    }
+
+    if ( ac - optind == 1 ) {
+       if ( nbp_name( av[ optind ], &Obj, &Type, &Zone )) {
+           Usage( av[ 0 ] );
+           exit( 1 );
+       }
+    }
+
+    if (( c = nbp_lookup( Obj, Type, Zone, nn, nresp, &addr)) < 0 ) {
+       perror( "nbp_lookup" );
+       exit( -1 );
+    }
+    for ( i = 0; i < c; i++ ) {
+       printf( "%31.*s:%-34.*s %u.%u:%u\n",
+               nn[ i ].nn_objlen, nn[ i ].nn_obj,
+               nn[ i ].nn_typelen, nn[ i ].nn_type,
+               ntohs( nn[ i ].nn_sat.sat_addr.s_net ),
+               nn[ i ].nn_sat.sat_addr.s_node,
+               nn[ i ].nn_sat.sat_port );
+    }
+
+}
diff --git a/bin/nbp/nbprgstr.c b/bin/nbp/nbprgstr.c
new file mode 100644 (file)
index 0000000..c4f47e7
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <atalk/netddp.h>
+#include <atalk/util.h>
+
+Usage( av0 )
+    char       *av0;
+{
+    char       *p;
+
+    if (( p = strrchr( av0, '/' )) == 0 ) {
+       p = av0;
+    } else {
+       p++;
+    }
+
+    fprintf( stderr, "Usage: %s [ -A address ] obj:type@zone\n", p );
+    exit( 1 );
+}
+
+main( ac, av )
+    int                ac;
+    char       **av;
+{
+    struct sockaddr_at addr;
+    struct at_addr      ataddr;
+    char               *Obj = 0, *Type = 0, *Zone = 0;
+    int                        s, c, port = 0;
+    
+    extern char                *optarg;
+    extern int         optind;
+
+    memset(&ataddr, 0, sizeof(ataddr));
+    while (( c = getopt( ac, av, "p:A:" )) != EOF ) {
+       switch ( c ) {
+       case 'A':
+           if (!atalk_aton(optarg, &ataddr)) {
+               fprintf(stderr, "Bad address.\n");
+               exit(1);
+           }
+           break;
+
+       case 'p' :
+           port = atoi( optarg );
+           break;
+
+       default :
+           Usage( av[ 0 ] );
+       }
+    }
+
+    if ( ac - optind != 1 ) {
+       Usage( av[ 0 ] );
+    }
+
+    /*
+     * Get the name. If Type or Obj aren't specified, error.
+     */
+    if ( nbp_name( av[ optind ], &Obj, &Type, &Zone ) || !Obj || !Type ) {
+       Usage( av[ 0 ] );
+    }
+
+    memset(&addr, 0, sizeof(addr));
+    memcpy(&addr.sat_addr, &ataddr, sizeof(addr.sat_addr));
+    if ((s = netddp_open(&addr, NULL)) < 0)
+       return( -1 );
+
+    if ( port ) {
+       addr.sat_port = port;
+    }
+
+    if ( nbp_rgstr( &addr, Obj, Type, Zone ) < 0 ) {
+       perror( "nbp_rgstr" );
+       fprintf( stderr, "Can't register %s:%s@%s\n", Obj, Type,
+               Zone ? Zone : "*" );
+       exit( 1 );
+    }
+    netddp_close(s);
+}
diff --git a/bin/nbp/nbpunrgstr.c b/bin/nbp/nbpunrgstr.c
new file mode 100644 (file)
index 0000000..8b147bf
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#include  <sys/types.h>
+#include  <netatalk/endian.h>
+#include  <netatalk/at.h>
+#include  <stdio.h>
+#include  <string.h>
+
+Usage( av0 )
+    char       *av0;
+{
+    char       *p;
+
+    if (( p = strrchr( av0, '/' )) == 0 ) {
+       p = av0;
+    } else {
+       p++;
+    }
+
+    fprintf( stderr, "Usage: %s [ -A address ] obj:type@zone\n", p );
+    exit( 1 );
+}
+
+main( ac, av )
+    int                ac;
+    char       **av;
+{
+    char               *Obj = 0, *Type = 0, *Zone = 0;
+    struct at_addr      addr;
+    int                 c;
+
+    extern char                *optarg;
+    extern int         optind;
+    
+    memset(&addr, 0, sizeof(addr));
+    while ((c = getopt(ac, av, "A:")) != EOF) {
+      switch (c) {
+      case 'A':
+       if (!atalk_aton(optarg, &addr)) {
+         fprintf(stderr, "Bad address.\n");
+         exit(1);
+       }
+       break;
+      default:
+       Usage(av[0]);
+       break;
+      }
+    }
+
+    if (ac - optind != 1) {
+       Usage( av[ 0 ] );
+    }
+
+    /*
+     * Get the name. If Type or Obj aren't specified, error.
+     */
+    if ( nbp_name( av[optind], &Obj, &Type, &Zone ) || !Obj || !Type ) {
+       Usage( av[ 0 ] );
+    }
+
+    if ( nbp_unrgstr( Obj, Type, Zone, &addr ) < 0 ) {
+       fprintf( stderr, "Can't unregister %s:%s@%s\n", Obj, Type,
+               Zone ? Zone : "*" );
+       exit( 1 );
+    }
+}
diff --git a/bin/pap/Makefile b/bin/pap/Makefile
new file mode 100644 (file)
index 0000000..2dae36b
--- /dev/null
@@ -0,0 +1,52 @@
+TARGETS=       pap papstatus
+SRC = pap.c papstatus.c
+
+INCPATH=       -I../../include
+CFLAGS=        ${DEFS} ${OPTOPTS} ${INCPATH} -DZEROCONNID -DNONZEROSTATUS
+
+LIBS=  -latalk ${ADDLIBS}
+TAGSFILE=      tags
+CC=    cc
+INSTALL=       install
+LIBDIRS=       -L../../libatalk
+
+all : ${TARGETS}
+
+pap : pap.o ../../libatalk/libatalk.a
+       ${CC} ${CFLAGS} -o pap pap.o ${LIBDIRS} ${LIBS}
+
+papstatus : papstatus.o ../../libatalk/libatalk.a
+       ${CC} ${CFLAGS} -o papstatus papstatus.o ${LIBDIRS} ${LIBS}
+
+install : all
+       for i in ${TARGETS}; do ${INSTALL} -c $$i ${BINDIR}; done
+
+clean :
+       rm -f a.out core* *.o *.bak *[Ee]rrs tags
+       rm -f ${TARGETS}
+
+tags : ${SRC}
+       cwd=`pwd`; \
+       for i in ${SRC}; do \
+           ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+       done
+
+depend :
+       for i in ${SRC} ; do \
+           ${CC} -M ${DEFS} ${INCPATH} $$i | \
+           awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+               else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+               else rec = rec " " $$2 } } \
+               END { print rec } ' >> makedep; done
+       sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+       cat makedep >> Makefile.tmp
+       rm makedep
+       echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+       echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+       echo '# see make depend above' >> Makefile.tmp
+       rm -f Makefile.bak
+       cp Makefile Makefile.bak
+       mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
diff --git a/bin/pap/pap.c b/bin/pap/pap.c
new file mode 100644 (file)
index 0000000..fdf4e4e
--- /dev/null
@@ -0,0 +1,844 @@
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <atalk/atp.h>
+#include <atalk/pap.h>
+#include <atalk/nbp.h>
+#include <atalk/util.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <string.h>
+#include <errno.h>
+
+#define FUCKED
+
+#define _PATH_PAPRC    ".paprc"
+char   *nbpfailure = "AppleTalk printer offline";
+
+#ifdef EBUG
+#define DEBUG(x,y)     (x,y)
+#else /*EBUG*/
+#define DEBUG(x,y)
+#endif /*EBUG*/
+
+usage( path )
+    char       *path;
+{
+    char       *p;
+
+    if (( p = strrchr( path, '/' )) == NULL ) {
+       p = path;
+    } else {
+       p++;
+    }
+    fprintf( stderr,
+       "Usage:\t%s [ -A address ] [ -p printername ] [ -s statusfile ] [ file ] ...\n", p );
+    exit( 2 );
+}
+
+char *
+paprc()
+{
+    static char        s[ 32 + 1 + 32 + 1 + 32 ];
+    char       *name = NULL;
+    FILE       *f;
+
+    if (( f = fopen( _PATH_PAPRC, "r" )) == NULL ) {
+       if ( errno == ENOENT ) {
+           return( NULL );
+       } else {
+           perror( _PATH_PAPRC );
+           exit( 2 );
+       }
+    }
+    while ( fgets( s, sizeof( s ), f ) != NULL ) {
+       s[ strlen( s ) - 1 ] = '\0';    /* remove trailing newline */
+       if ( *s == '#' ) {
+           continue;
+       }
+       name = s;
+       break;
+    }
+    fclose( f );
+    return( name );
+}
+
+char                   *printer = NULL;
+char                   *status = NULL;
+int                    noeof = 0;
+int                    waitforprinter = 0;
+
+unsigned char          connid, quantum, oquantum = PAP_MAXQUANTUM;
+struct sockaddr_at     sat;
+
+char                   cbuf[ 8 ];
+struct nbpnve          nn;
+ATP                    satp;
+
+char           fbuf[ PAP_MAXQUANTUM ][ 4 + PAP_MAXDATA ];
+struct iovec   rfiov[ PAP_MAXQUANTUM ] = {
+    { fbuf[ 0 ] + 4,   0 },
+    { fbuf[ 1 ] + 4,   0 },
+    { fbuf[ 2 ] + 4,   0 },
+    { fbuf[ 3 ] + 4,   0 },
+    { fbuf[ 4 ] + 4,   0 },
+    { fbuf[ 5 ] + 4,   0 },
+    { fbuf[ 6 ] + 4,   0 },
+    { fbuf[ 7 ] + 4,   0 },
+};
+struct iovec   sniov[ PAP_MAXQUANTUM ] = {
+    { fbuf[ 0 ],       0 },
+    { fbuf[ 1 ],       0 },
+    { fbuf[ 2 ],       0 },
+    { fbuf[ 3 ],       0 },
+    { fbuf[ 4 ],       0 },
+    { fbuf[ 5 ],       0 },
+    { fbuf[ 6 ],       0 },
+    { fbuf[ 7 ],       0 },
+};
+
+char           nbuf[ PAP_MAXQUANTUM ][ 4 + PAP_MAXDATA ];
+struct iovec   rniov[ PAP_MAXQUANTUM ] = {
+    { nbuf[ 0 ],       0 },
+    { nbuf[ 1 ],       0 },
+    { nbuf[ 2 ],       0 },
+    { nbuf[ 3 ],       0 },
+    { nbuf[ 4 ],       0 },
+    { nbuf[ 5 ],       0 },
+    { nbuf[ 6 ],       0 },
+    { nbuf[ 7 ],       0 },
+};
+struct iovec   sfiov[ PAP_MAXQUANTUM ] = {
+    { nbuf[ 0 ] + 4,   0 },
+    { nbuf[ 1 ] + 4,   0 },
+    { nbuf[ 2 ] + 4,   0 },
+    { nbuf[ 3 ] + 4,   0 },
+    { nbuf[ 4 ] + 4,   0 },
+    { nbuf[ 5 ] + 4,   0 },
+    { nbuf[ 6 ] + 4,   0 },
+    { nbuf[ 7 ] + 4,   0 },
+};
+
+main( ac, av )
+    int                ac;
+    char       **av;
+{
+    ATP                        atp;
+    struct atp_block   atpb;
+    int                        c, err = 0, fd, cuts = 0;
+    char               *obj = NULL, *type = "LaserWriter", *zone = "*";
+    struct timeval     stv, tv;
+    char               rbuf[ ATP_MAXDATA ];
+    struct iovec       iov;
+    unsigned short     waiting, result;
+    int                        connattempts = 10;
+    int                        waitforidle = 0;
+    struct at_addr      addr;
+
+    extern char                *optarg;
+    extern int         optind;
+
+    memset(&addr, 0, sizeof(addr));
+    while (( c = getopt( ac, av, "Wwcep:s:EA:" )) != EOF ) {
+       switch ( c ) {
+#ifdef FUCKED
+       case 'w' :
+           waitforprinter = 1;
+           break;
+
+       case 'W' :
+           waitforidle = 1;
+           break;
+#endif /*FUCKED*/
+
+       case 'c' :
+           cuts++;
+           break;
+
+       case 'e' :      /* send stdout to stderr */
+           dup2( 2, 1 );
+           break;
+
+       case 'p' :
+           printer = optarg;
+           break;
+
+       case 's' :
+           status = optarg;
+           break;
+
+       case 'E' :
+           noeof = 1;
+           break;
+          
+       case 'A':
+           if (!atalk_aton(optarg, &addr)) {
+             fprintf(stderr, "Bad address.\n");
+             exit(1);
+           }
+           break;
+
+       default :
+           err++;
+       }
+    }
+    if ( err ) {
+       usage( *av );
+    }
+    if ( printer == NULL && (( printer = paprc()) == NULL )) {
+       fprintf( stderr, "No printer specified and ./.paprc not found.\n" );
+       exit( 2 );
+    }
+
+    /*
+     * Open connection.
+     */
+    if ( nbp_name( printer, &obj, &type, &zone ) < 0 ) {
+       fprintf( stderr, "%s: Bad name\n", printer );
+       exit( 2 );
+    }
+    if ( obj == NULL ) {
+       fprintf( stderr, "%s: Bad name\n", printer );
+       exit( 2 );
+    }
+
+    if ( nbp_lookup( obj, type, zone, &nn, 1, &addr ) <= 0 ) {
+       if ( errno != 0 ) {
+           perror( "nbp_lookup" );
+           exit( 2 );
+       }
+       fprintf( stderr, "%s:%s@%s: NBP Lookup failed\n", obj, type, zone );
+       exit( 1 );
+    }
+
+    if ( isatty( 0 )) {
+       printf( "Trying %u.%d:%d ...\n", ntohs( nn.nn_sat.sat_addr.s_net ),
+               nn.nn_sat.sat_addr.s_node, nn.nn_sat.sat_port );
+    }
+
+    if (( atp = atp_open( ATADDR_ANYPORT, &addr )) == NULL ) {
+       perror( "atp_open" );
+       exit( 2 );
+    }
+    if (( satp = atp_open( ATADDR_ANYPORT, &addr )) == NULL ) {
+       perror( "atp_open" );
+       exit( 2 );
+    }
+
+    while ( waitforidle ) {
+       char    st_buf[ 1024 ]; /* XXX too big */
+
+       cbuf[ 0 ] = 0;
+       cbuf[ 1 ] = PAP_SENDSTATUS;
+       cbuf[ 2 ] = cbuf[ 3 ] = 0;
+       atpb.atp_saddr = &nn.nn_sat;
+       atpb.atp_sreqdata = cbuf;
+       atpb.atp_sreqdlen = 4;  /* bytes in SendStatus request */
+       atpb.atp_sreqto = 2;            /* retry timer */
+       atpb.atp_sreqtries = 5;         /* retry count */
+       if ( atp_sreq( satp, &atpb, 1, 0 ) < 0 ) {
+           perror( "atp_sreq" );
+           exit( 1 );
+       }
+
+DEBUG( printf( "SENDSTATUS >\n" ), fflush( stdout ));
+
+       atpb.atp_saddr = &nn.nn_sat;
+       rniov[ 0 ].iov_len = PAP_MAXDATA + 4;
+       atpb.atp_rresiov = rniov;
+       atpb.atp_rresiovcnt = 1;
+       if ( atp_rresp( satp, &atpb ) < 0 ) {
+           perror( "atp_rresp" );
+           continue;
+       }
+
+#ifndef NONZEROSTATUS
+       /*
+        * The stinking LaserWriter IINTX puts crap in this
+        * field.
+        */
+       if ( ((char *)rniov[ 0 ].iov_base)[ 0 ] != 0 ) {
+           fprintf( stderr, "Bad status response!\n" );
+           exit( 1 );
+       }
+#endif /*NONZEROSTATUS*/
+
+       if ( ((char *)rniov[ 0 ].iov_base)[ 1 ] != PAP_STATUS ||
+               atpb.atp_rresiovcnt != 1 ) {
+           fprintf( stderr, "Bad status response!\n" );
+           exit( 1 );
+       }
+
+DEBUG( printf( "< STATUS\n" ), fflush( stdout ));
+
+       memcpy( st_buf, (char *) rniov[ 0 ].iov_base + 9, 
+               ((char *)rniov[ 0 ].iov_base)[ 8 ] );
+       st_buf[ ((char *)rniov[ 0 ].iov_base)[ 8 ]] = '\0';
+       if ( strstr( st_buf, "idle" ) != NULL ) {
+           waitforidle = 0;
+       } else {
+           updatestatus( (char *) rniov[ 0 ].iov_base + 9,
+                   ((char *)rniov[ 0 ].iov_base)[ 8 ] );
+           sleep( 5 );
+       }
+    }
+
+    cbuf[ 0 ] = connid = getpid() & 0xff;
+    cbuf[ 1 ] = PAP_OPEN;
+    cbuf[ 2 ] = cbuf[ 3 ] = 0;
+    cbuf[ 4 ] = atp_sockaddr( atp )->sat_port;
+    cbuf[ 5 ] = oquantum;      /* flow quantum */
+    if ( gettimeofday( &stv, 0 ) < 0 ) {
+       perror( "gettimeofday" );
+       exit( 2 );
+    }
+    for (;;) {
+       if ( cuts ) {
+           waiting = 0xffff;
+       } else {
+           if ( gettimeofday( &tv, 0 ) < 0 ) {
+               perror( "gettimeofday" );
+               exit( 2 );
+           }
+           waiting = htons( tv.tv_sec - stv.tv_sec );
+       }
+       memcpy(cbuf +  6, &waiting, sizeof( waiting ));
+
+       atpb.atp_saddr = &nn.nn_sat;
+       atpb.atp_sreqdata = cbuf;
+       atpb.atp_sreqdlen = 8;          /* bytes in OpenConn request */
+       atpb.atp_sreqto = 2;            /* retry timer */
+       atpb.atp_sreqtries = 5;         /* retry count */
+       if ( atp_sreq( atp, &atpb, 1, ATP_XO ) < 0 ) {
+           perror( "atp_sreq" );
+           exit( 1 );
+       }
+
+DEBUG( printf( "OPEN >\n" ), fflush( stdout ));
+
+       iov.iov_base = rbuf;
+       iov.iov_len = sizeof( rbuf );
+       atpb.atp_rresiov = &iov;
+       atpb.atp_rresiovcnt = 1;
+       if ( atp_rresp( atp, &atpb ) < 0 ) {
+           perror( "atp_rresp" );
+           if ( connattempts-- <= 0 ) {
+               fprintf( stderr, "Can't connect!\n" );
+               exit( 1 );
+           }
+           continue;
+       }
+
+       /* sanity */
+       if ( iov.iov_len < 8 || (unsigned char)rbuf[ 0 ] != connid ||
+               rbuf[ 1 ] != PAP_OPENREPLY ) {
+           fprintf( stderr, "Bad response!\n" );
+           continue;   /* This is weird, since TIDs must match... */
+       }
+
+DEBUG( printf( "< OPENREPLY\n" ), fflush( stdout ));
+
+       if ( isatty( 1 )) {
+           printf( "%.*s\n", iov.iov_len - 9, (char *) iov.iov_base + 9 );
+       }
+       updatestatus( (char *) iov.iov_base + 9, iov.iov_len - 9 );
+
+       memcpy( &result, rbuf +  6,  sizeof( result ));
+       if ( result != 0 ) {
+           sleep( 2 );
+       } else {
+           memcpy( &sat, &nn.nn_sat, sizeof( struct sockaddr_at ));
+           sat.sat_port = rbuf[ 4 ];
+           quantum = rbuf[ 5 ];
+           break;
+       }
+    }
+
+    if ( isatty( 1 )) {
+       printf( "Connected to %.*s:%.*s@%.*s.\n",
+               nn.nn_objlen, nn.nn_obj,
+               nn.nn_typelen, nn.nn_type,
+               nn.nn_zonelen, nn.nn_zone );
+    }
+
+    if ( optind == ac ) {
+       send_file( 0, atp, 1 );
+    } else {
+       for (; optind < ac; optind++ ) {
+           if ( strcmp( av[ optind ], "-" ) == 0 ) {
+               fd = 0;
+           } else if (( fd = open( av[ optind ], O_RDONLY )) < 0 ) {
+               perror( av[ optind ] );
+               continue;
+           }
+           send_file( fd, atp, ( optind == ac - 1 ) ? 1 : 0 );
+           if ( fd != 0 ) {
+               close( fd );
+           }
+       }
+    }
+
+    /*
+     * Close connection.
+     */
+    cbuf[ 0 ] = connid;
+    cbuf[ 1 ] = PAP_CLOSE;
+    cbuf[ 2 ] = cbuf[ 3 ] = 0;
+
+    atpb.atp_saddr = &sat;
+    atpb.atp_sreqdata = cbuf;
+    atpb.atp_sreqdlen = 4;             /* bytes in CloseConn request */
+    atpb.atp_sreqto = 2;               /* retry timer */
+    atpb.atp_sreqtries = 5;            /* retry count */
+    if ( atp_sreq( atp, &atpb, 1, ATP_XO ) < 0 ) {
+       perror( "atp_sreq" );
+       exit( 1 );
+    }
+
+DEBUG( printf( "CLOSE >\n" ), fflush( stdout ));
+
+    iov.iov_base = rbuf;
+    iov.iov_len = sizeof( rbuf );
+    atpb.atp_rresiov = &iov;
+    atpb.atp_rresiovcnt = 1;
+    if ( atp_rresp( atp, &atpb ) < 0 ) {
+       perror( "atp_rresp" );
+       exit( 1 );
+    }
+
+    /* sanity */
+    if ( iov.iov_len != 4 || rbuf[ 1 ] != PAP_CLOSEREPLY ) {
+       fprintf( stderr, "Bad response!\n" );
+       exit( 1 );
+    }
+
+#ifndef ZEROCONNID
+    /*
+     * The AGFA Viper Rip doesn't have the connection id in the close request.
+     */
+    if ((unsigned char)rbuf[ 0 ] != connid ) {
+       fprintf( stderr, "Bad connid in close!\n" );
+       exit( 1 );
+    }
+#endif /*ZEROCONNID*/
+
+DEBUG( printf( "< CLOSEREPLY\n" ), fflush( stdout ));
+
+    if ( isatty( 1 )) {
+       printf( "Connection closed.\n" );
+    }
+    exit( 0 );
+}
+
+int            data = 0;
+unsigned char  port;
+u_int16_t       seq = 0, rseq = 1;
+
+send_file( fd, atp, lastfile )
+    int                        fd;
+    ATP                        atp;
+    int                        lastfile;
+{
+    struct timeval     stv, tv;
+    struct sockaddr_at ssat;
+    struct atp_block   atpb;
+    fd_set             fds;
+    int                        fiovcnt = 0, eof = 0, senteof = 0, to = 0;
+    int                        cc, i;
+    unsigned short     netseq;
+
+    if ( gettimeofday( &stv, 0 ) < 0 ) {
+       perror( "gettimeofday" );
+       exit( 2 );
+    }
+
+    /*
+     * Ask for more data.
+     */
+    cbuf[ 0 ] = connid;
+    cbuf[ 1 ] = PAP_READ;
+    if ( ++seq == 0 ) seq = 1;
+    netseq = htons( seq );
+    memcpy( cbuf +  2, &netseq, sizeof( netseq ));
+    atpb.atp_saddr = &sat;
+    atpb.atp_sreqdata = cbuf;
+    atpb.atp_sreqdlen = 4;             /* bytes in SendData request */
+    atpb.atp_sreqto = 15;              /* retry timer */
+    atpb.atp_sreqtries = -1;           /* retry count */
+    if ( atp_sreq( atp, &atpb, oquantum, ATP_XO ) < 0 ) {
+       perror( "atp_sreq" );
+       exit( 1 );
+    }
+
+DEBUG( printf( "READ %d >\n", seq ), fflush( stdout ));
+
+    for (;;) {
+       if ( gettimeofday( &tv, 0 ) < 0 ) {
+           perror( "gettimeofday" );
+           exit( 2 );
+       }
+
+       if (( tv.tv_sec - stv.tv_sec ) >= 60 ) {
+           stv = tv;
+
+           /*
+            * Send a tickle.
+            */
+           cbuf[ 0 ] = connid;
+           cbuf[ 1 ] = PAP_TICKLE;
+           cbuf[ 2 ] = cbuf[ 3 ] = 0;
+           atpb.atp_saddr = &sat;
+           atpb.atp_sreqdata = cbuf;
+           atpb.atp_sreqdlen = 4;              /* bytes in Tickle request */
+           atpb.atp_sreqto = 0;                /* retry timer */
+           atpb.atp_sreqtries = 1;             /* retry count */
+           if ( atp_sreq( satp, &atpb, 0, 0 ) < 0 ) {
+               perror( "atp_sreq" );
+               exit( 1 );
+           }
+
+DEBUG( printf( "TICKLE >\n" ), fflush( stdout ));
+       }
+
+       tv.tv_sec = stv.tv_sec + 60 - tv.tv_sec;
+       tv.tv_usec = 0;
+
+       FD_ZERO( &fds );
+       if ( !waitforprinter && !eof && fiovcnt == 0 ) {
+           FD_SET( fd, &fds );
+       }
+       FD_SET( atp_fileno( atp ), &fds );
+
+       if (( cc = select( FD_SETSIZE, &fds, 0, 0, &tv )) < 0 ) {
+           perror( "select" );
+           exit( 2 );
+       }
+
+       /*
+        * A timeout has occured. Keep track of it.
+        */
+       if ( cc == 0 ) {
+           if ( to++ > 2 ) {
+               fprintf( stderr, "Connection timed out.\n" );
+               exit( 1 );
+           }
+           continue;
+       }
+
+       /*
+        * Read data.
+        */
+       if ( !fiovcnt && FD_ISSET( fd, &fds )) {
+           for ( i = 0; i < quantum; i++ ) {
+               rfiov[ i ].iov_len = PAP_MAXDATA;
+           }
+           if (( cc = readv( fd, rfiov, quantum )) < 0 ) {
+               perror( "readv" );
+               exit( 2 );
+           }
+           if ( cc == 0 ) {
+               eof = 1;
+           }
+           fiovcnt = cc / PAP_MAXDATA + ( cc % PAP_MAXDATA > 0 );
+           for ( i = 0; cc > 0; i++ ) {
+               rfiov[ i ].iov_len = ( cc > PAP_MAXDATA ) ? PAP_MAXDATA : cc;
+               cc -= ( cc > PAP_MAXDATA ) ? PAP_MAXDATA : cc;
+           }
+       }
+
+       if ( FD_ISSET( atp_fileno( atp ), &fds )) {
+           ssat = sat;
+           ssat.sat_port = ATADDR_ANYPORT;
+           switch( atp_rsel( atp, &ssat, ATP_TRESP | ATP_TREQ )) {
+           case ATP_TREQ :
+               atpb.atp_saddr = &ssat;
+               atpb.atp_rreqdata = cbuf;
+               atpb.atp_rreqdlen = sizeof( cbuf );
+               if ( atp_rreq( atp, &atpb ) < 0 ) {
+                   perror( "atp_rreq" );
+                   exit( 1 );
+               }
+
+               if ( (unsigned char)cbuf[ 0 ] != connid ) {
+                   break;
+               }
+
+               /* reset timeout counter for all valid requests */
+               to = 0;
+
+               switch ( cbuf[ 1 ] ) {
+               case PAP_READ :
+                   memcpy( cbuf +  2, &netseq, sizeof( netseq ));
+DEBUG( printf( "< READ %d\n", ntohs( netseq )), fflush( stdout ));
+#ifdef notdef
+                   if ( netseq != 0 ) {
+                       if ( rseq != ntohs( netseq )) {
+DEBUG( printf( "| DUP %d\n", rseq ), fflush( stdout ));
+                           break;
+                       }
+                       if ( rseq++ == 0xffff ) rseq = 1;
+                   }
+#endif /*notdef*/
+
+                   data = 1;
+                   port = ssat.sat_port;
+                   break;
+
+               case PAP_CLOSE :
+
+DEBUG( printf( "< CLOSE\n" ), fflush( stdout ));
+
+                   /*
+                    * Respond to the close request, and fail.
+                    */
+                   sniov[ 0 ].iov_len = 4;
+                   ((char *)sniov[ 0 ].iov_base)[ 0 ] = connid;
+                   ((char *)sniov[ 0 ].iov_base)[ 1 ] = PAP_CLOSEREPLY;
+                   ((char *)sniov[ 0 ].iov_base)[ 2 ] =
+                           ((char *)sniov[ 0 ].iov_base)[ 3 ] = 0;
+                   atpb.atp_sresiov = sniov;
+                   atpb.atp_sresiovcnt = 1;
+                   if ( atp_sresp( atp, &atpb ) < 0 ) {
+                       perror( "atp_sresp" );
+                       exit( 1 );
+                   }
+
+DEBUG( printf( "CLOSEREPLY >\n" ), fflush( stdout ));
+
+                   fprintf( stderr, "Connection closed by foreign host.\n" );
+                   exit( 1 );
+
+               case PAP_TICKLE :
+
+DEBUG( printf( "< TICKLE\n" ), fflush( stdout ));
+
+                   break;
+               default :
+                   fprintf( stderr, "Bad PAP request!\n" );
+                   exit( 1 );
+               }
+               break;
+
+           case ATP_TRESP :
+               /* reset timeout counter for all valid requests */
+               to = 0;
+
+               atpb.atp_saddr = &ssat;
+               for ( i = 0; i < oquantum; i++ ) {
+                   rniov[ i ].iov_len = PAP_MAXDATA + 4;
+               }
+               atpb.atp_rresiov = rniov;
+               atpb.atp_rresiovcnt = oquantum;
+               if ( atp_rresp( atp, &atpb ) < 0 ) {
+                   perror( "atp_rresp" );
+                   exit( 1 );
+               }
+
+#ifndef ZEROCONNID
+               /*
+                * The HP LJIIISI w/ BridgePort LocalTalk card sends
+                * zero instead of the connid.
+                */
+               if ( ((unsigned char *)rniov[ 0 ].iov_base)[ 0 ] != connid ) {
+                   fprintf( stderr, "Bad data response!\n" );
+                   exit( 1 );
+               }
+#endif /*ZEROCONNID*/
+               if ( ((char *)rniov[ 0 ].iov_base)[ 1 ] != PAP_DATA ) {
+                   fprintf( stderr, "Bad data response!\n" );
+                   exit( 1 );
+               }
+
+               for ( cc = 0, i = 0; i < atpb.atp_rresiovcnt; i++ ) {
+                   sfiov[ i ].iov_len = rniov[ i ].iov_len - 4;
+                   cc += sfiov[ i ].iov_len;
+               }
+               if ( cc && writev( 1, sfiov, atpb.atp_rresiovcnt ) < cc ) {
+                   perror( "writev" );
+                   exit( 2 );
+               }
+
+               /* eof */
+               if ( ((char *)rniov[ 0 ].iov_base)[ 2 ] ) {
+
+DEBUG( printf( "< DATA (eof)\n" ), fflush( stdout ));
+
+                   return( 0 );
+               }
+
+DEBUG( printf( "< DATA\n" ), fflush( stdout ));
+
+
+               /*
+                * Ask for more data.
+                */
+               cbuf[ 0 ] = connid;
+               cbuf[ 1 ] = PAP_READ;
+               if ( ++seq == 0 ) seq = 1;
+               netseq = htons( seq );
+               memcpy( cbuf +  2, &netseq, sizeof( netseq ));
+               atpb.atp_saddr = &sat;
+               atpb.atp_sreqdata = cbuf;
+               atpb.atp_sreqdlen = 4;          /* bytes in SendData request */
+               atpb.atp_sreqto = 15;           /* retry timer */
+               atpb.atp_sreqtries = -1;        /* retry count */
+               if ( atp_sreq( atp, &atpb, oquantum, ATP_XO ) < 0 ) {
+                   perror( "atp_sreq" );
+                   exit( 1 );
+               }
+
+DEBUG( printf( "READ %d >\n", seq ), fflush( stdout ));
+
+               break;
+
+           case 0:
+
+DEBUG( printf( "| RETRANS\n" ), fflush( stdout ));
+
+               break;
+
+           default:
+               perror( "atp_rsel" );
+               exit( 1 );
+           }
+       }
+
+       /*
+        * Send whatever is pending.
+        */
+       if ( !waitforprinter && !senteof && data && ( fiovcnt || eof )) {
+           ssat.sat_port = port;
+           atpb.atp_saddr = &ssat;
+           if ( fiovcnt ) {
+               for ( i = 0; i < fiovcnt; i++ ) {
+                   sniov[ i ].iov_len = rfiov[ i ].iov_len + 4;
+                   ((char *)sniov[ i ].iov_base)[ 0 ] = connid;
+                   ((char *)sniov[ i ].iov_base)[ 1 ] = PAP_DATA;
+                   senteof = ((char *)sniov[ i ].iov_base)[ 2 ] = eof;
+                   ((char *)sniov[ i ].iov_base)[ 3 ] = 0;
+               }
+           } else {
+               sniov[ 0 ].iov_len = 4;
+               ((char *)sniov[ 0 ].iov_base)[ 0 ] = connid;
+               ((char *)sniov[ 0 ].iov_base)[ 1 ] = PAP_DATA;
+               senteof = ((char *)sniov[ 0 ].iov_base)[ 2 ] = eof;
+               ((char *)sniov[ 0 ].iov_base)[ 3 ] = 0;
+           }
+           atpb.atp_sresiov = sniov;
+           atpb.atp_sresiovcnt = fiovcnt ? fiovcnt : 1;
+           if ( atp_sresp( atp, &atpb ) < 0 ) {
+               perror( "atp_sresp" );
+               exit( 1 );
+           }
+           data = fiovcnt = 0;
+
+DEBUG( printf( "DATA %s\n", eof ? "(eof) >" : ">" ), fflush( stdout ));
+
+           /*
+            * The Apple LaserWriter IIf, the HP LWIIISi, and IV, don't
+            * seem to send us an EOF on large jobs.  To work around
+            * this heinous protocol violation, we won't wait for their
+            * EOF before closing.
+            */
+           if ( eof && noeof && lastfile ) {
+               return( 0 );
+           }
+       } else {
+           /*
+            * If we can't send data right now, go ahead and get the
+            * status. This is cool, because we get here reliably
+            * if there is a problem.
+            */
+           cbuf[ 0 ] = 0;
+           cbuf[ 1 ] = PAP_SENDSTATUS;
+           cbuf[ 2 ] = cbuf[ 3 ] = 0;
+           atpb.atp_saddr = &nn.nn_sat;
+           atpb.atp_sreqdata = cbuf;
+           atpb.atp_sreqdlen = 4;      /* bytes in SendStatus request */
+           atpb.atp_sreqto = 2;                /* retry timer */
+           atpb.atp_sreqtries = 5;             /* retry count */
+           if ( atp_sreq( satp, &atpb, 1, 0 ) < 0 ) {
+               perror( "atp_sreq" );
+               exit( 1 );
+           }
+
+DEBUG( printf( "SENDSTATUS >\n" ), fflush( stdout ));
+
+           atpb.atp_saddr = &nn.nn_sat;
+           rniov[ 0 ].iov_len = PAP_MAXDATA + 4;
+           atpb.atp_rresiov = rniov;
+           atpb.atp_rresiovcnt = 1;
+           if ( atp_rresp( satp, &atpb ) < 0 ) {
+               perror( "atp_rresp" );
+               continue;
+           }
+
+#ifndef NONZEROSTATUS
+           /*
+            * The stinking LaserWriter IINTX puts crap in this
+            * field.
+            */
+           if ( ((char *)rniov[ 0 ].iov_base)[ 0 ] != 0 ) {
+               fprintf( stderr, "Bad status response!\n" );
+               exit( 1 );
+           }
+#endif /*NONZEROSTATUS*/
+
+           if ( ((char *)rniov[ 0 ].iov_base)[ 1 ] != PAP_STATUS ||
+                   atpb.atp_rresiovcnt != 1 ) {
+               fprintf( stderr, "Bad status response!\n" );
+               exit( 1 );
+           }
+
+DEBUG( printf( "< STATUS\n" ), fflush( stdout ));
+
+#ifdef FUCKED
+           if ( waitforprinter ) {
+               char    st_buf[ 1024 ]; /* XXX too big */
+
+               memcpy( st_buf, (char *) rniov[ 0 ].iov_base + 9, 
+                       ((char *)rniov[ 0 ].iov_base)[ 8 ] );
+               st_buf[ ((char *)rniov[ 0 ].iov_base)[ 8 ]] = '\0';
+               if ( strstr( st_buf, "waiting" ) != NULL ) {
+                   waitforprinter = 0;
+               }
+           }
+#endif /*FUCKED*/
+
+           updatestatus( (char *) rniov[ 0 ].iov_base + 9,
+                   ((char *)rniov[ 0 ].iov_base)[ 8 ] );
+       }
+    }
+}
+
+updatestatus( s, len )
+    char       *s;
+    int                len;
+{
+    int                        fd;
+    struct iovec       iov[ 2 ];
+
+    if ( !status ) {
+       return;
+    }
+
+    if (( fd = open( status, O_WRONLY|O_TRUNC )) < 0 ) {
+       perror( status );
+       status = NULL;
+       return;
+    }
+    iov[ 0 ].iov_base = s;
+    iov[ 0 ].iov_len = len;
+    iov[ 1 ].iov_base = "\n";
+    iov[ 1 ].iov_len = 1;
+    writev( fd, iov, 2 );
+    close( fd );
+}
diff --git a/bin/pap/papstatus.c b/bin/pap/papstatus.c
new file mode 100644 (file)
index 0000000..c9076bd
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/file.h>
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <atalk/atp.h>
+#include <atalk/pap.h>
+#include <atalk/nbp.h>
+#include <atalk/util.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#define _PATH_PAPRC    ".paprc"
+
+usage( path )
+    char       *path;
+{
+    char       *p;
+
+    if (( p = strrchr( path, '/' )) == NULL ) {
+       p = path;
+    } else {
+       p++;
+    }
+    fprintf( stderr,
+       "Usage:\t%s [ -A address ] [ -p printername ]\n", p );
+    exit( 1 );
+}
+
+char *
+paprc()
+{
+    static char        s[ 32 + 1 + 32 + 1 + 32 ];
+    char       *name = NULL;
+    FILE       *f;
+
+    if (( f = fopen( _PATH_PAPRC, "r" )) == NULL ) {
+       return( NULL );
+    }
+    while ( fgets( s, sizeof( s ), f ) != NULL ) {
+       s[ strlen( s ) - 1 ] = '\0';    /* remove trailing newline */
+       if ( *s == '#' ) {
+           continue;
+       }
+       name = s;
+       break;
+    }
+    fclose( f );
+    return( name );
+}
+
+char                   *printer = NULL;
+
+char                   cbuf[ 8 ];
+struct nbpnve          nn;
+
+main( ac, av )
+    int                ac;
+    char       **av;
+{
+    ATP                        atp;
+    int                        wait, c, err = 0;
+    char               *obj = NULL, *type = "LaserWriter", *zone = "*";
+    struct at_addr      addr;
+
+    extern char                *optarg;
+    extern int         optind;
+
+    memset(&addr, 0, sizeof(addr));
+    while (( c = getopt( ac, av, "p:s:A:" )) != EOF ) {
+       switch ( c ) {
+       case 'A':
+           if (!atalk_aton(optarg, &addr)) {
+             fprintf(stderr, "Bad address.\n");
+             exit(1);
+           }
+           break;
+       case 'p' :
+           printer = optarg;
+           break;
+
+       default :
+           fprintf( stderr, "Unknown option: '%c'\n", c );
+           err++;
+       }
+    }
+    if ( err ) {
+       usage( *av );
+    }
+    if ( printer == NULL && (( printer = paprc()) == NULL )) {
+       usage( *av );
+    }
+
+    /*
+     * Open connection.
+     */
+    if ( nbp_name( printer, &obj, &type, &zone ) < 0 ) {
+       fprintf( stderr, "%s: Bad name\n", printer );
+       exit( 1 );
+    }
+    if ( obj == NULL ) {
+       fprintf( stderr, "%s: Bad name\n", printer );
+       exit( 1 );
+    }
+    if ( nbp_lookup( obj, type, zone, &nn, 1, &addr ) <= 0 ) {
+       if ( errno != 0 ) {
+           perror( "nbp_lookup" );
+       } else {
+           fprintf( stderr, "%s:%s@%s: NBP Lookup failed\n", obj, type, zone );
+       }
+       exit( 1 );
+    }
+
+    if (( atp = atp_open( ATADDR_ANYPORT, &addr )) == NULL ) {
+       perror( "atp_open" );
+       exit( 1 );
+    }
+
+    if ( optind == ac ) {
+       getstatus( atp, &nn.nn_sat );
+       exit( 0 );
+    }
+    if ( optind - ac > 1 ) {
+       usage( *av );
+    }
+    wait = atoi( av[ optind ] );
+    for (;;) {
+       getstatus( atp, &nn.nn_sat );
+       sleep( wait );
+    }
+}
+
+getstatus( atp, sat )
+    ATP                        atp;
+    struct sockaddr_at *sat;
+{
+    struct iovec       iov;
+    struct atp_block   atpb;
+    char               rbuf[ ATP_MAXDATA ];
+
+    cbuf[ 0 ] = 0;
+    cbuf[ 1 ] = PAP_SENDSTATUS;
+    cbuf[ 2 ] = cbuf[ 3 ] = 0;
+
+    atpb.atp_saddr = sat;
+    atpb.atp_sreqdata = cbuf;
+    atpb.atp_sreqdlen = 4;             /* bytes in SendStatus request */
+    atpb.atp_sreqto = 2;               /* retry timer */
+    atpb.atp_sreqtries = 5;            /* retry count */
+    if ( atp_sreq( atp, &atpb, 1, ATP_XO ) < 0 ) {
+       perror( "atp_sreq" );
+       exit( 1 );
+    }
+
+    iov.iov_base = rbuf;
+    iov.iov_len = sizeof( rbuf );
+    atpb.atp_rresiov = &iov;
+    atpb.atp_rresiovcnt = 1;
+    if ( atp_rresp( atp, &atpb ) < 0 ) {
+       perror( "atp_rresp" );
+       exit( 1 );
+    }
+
+    /* sanity */
+    if ( iov.iov_len < 8 ||
+           rbuf[ 1 ] != PAP_STATUS ) {
+       fprintf( stderr, "Bad response!\n" );
+       return; /* This is weird, since TIDs must match... */
+    }
+
+    printf( "%.*s\n", iov.iov_len - 9, (char *) iov.iov_base + 9 );
+}
diff --git a/bin/psorder/Makefile b/bin/psorder/Makefile
new file mode 100644 (file)
index 0000000..ed7b933
--- /dev/null
@@ -0,0 +1,55 @@
+SRC = psorder.c pa.c
+OBJ = psorder.o pa.o
+
+INCPATH=       -I../../include
+CFLAGS=        ${DEFS} ${OPTOPTS} ${INCPATH}
+LIBS=  -latalk ${ADDLIBS}
+TAGSFILE=      tags
+CC=    cc
+INSTALL=       install
+LIBDIRS=       -L../../libatalk
+
+all : psorder 
+
+psorder : ${OBJ} ../../libatalk/libatalk.a
+       ${CC} ${CFLAGS} -o psorder ${OBJ} ${LIBDIRS} ${LIBS}
+
+install : all
+       ${INSTALL} -c psorder ${BINDIR}
+
+lint :
+       lint ${DEFS} ${INCPATH} ${SRC}
+
+clean :
+       rm -f a.out core* *.o *.bak *[Ee]rrs tags
+       rm -f psorder
+
+tags : ${SRC}
+       cwd=`pwd`; \
+       for i in ${SRC}; do \
+           ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+       done
+
+tar :
+       rm -f ../psorder.tar
+       (cd .. ; tar cf psorder.tar psorder ; mv psorder.tar psorder)
+
+depend :
+       for i in ${SRC} ; do \
+           ${CC} -M ${DEFS} ${INCPATH} $$i | \
+           awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+               else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+               else rec = rec " " $$2 } } \
+               END { print rec } ' >> makedep; done
+       sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+       cat makedep >> Makefile.tmp
+       rm makedep
+       echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+       echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+       echo '# see make depend above' >> Makefile.tmp
+       rm -f Makefile.bak
+       cp Makefile Makefile.bak
+       mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
diff --git a/bin/psorder/pa.c b/bin/psorder/pa.c
new file mode 100644 (file)
index 0000000..e78e5b5
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+/* This is used with pa.h */
+
+#include <stdlib.h>
+
+#include "pa.h"
+
+pa_buf_t *pa_init( fd )
+       int fd;
+{
+       pa_buf_t *h;
+       int rc;
+
+       h = (pa_buf_t *) malloc( sizeof( pa_buf_t ));
+       h->buf = (char *) malloc( PA_BUFBLK * 2 );
+       h->bufsz = PA_BUFBLK * 2;
+
+       if (( rc = read( fd, h->buf, PA_BUFBLK )) < 0 ) {
+               return( 0 );
+       }
+
+       h->cur = h->buf - 1;
+       h->end = h->buf + rc - 1;
+       h->state = PA_NORMAL;
+       h->fd = fd;
+
+       return( h );
+}
+
+char *pa_gettok( h )
+       pa_buf_t *h;
+{
+       h->state = PA_NORMAL;
+       h->tmp = *(h->cur);
+       *(h->cur) = 0;
+       return( h->mark );
+}
+
+char _pa_fixbuf( h )
+       pa_buf_t *h;
+{
+       int rc;
+       char *t;
+
+       if ( h->state == PA_NORMAL ) {
+               *(h->buf) = *(h->cur);
+               h->cur = h->buf;
+       } else {
+               bcopy( h->mark, h->buf, h->end - h->mark + 1 );
+               h->cur = h->buf + ( h->cur - h->mark );
+               h->end = h->buf + ( h->end - h->mark );
+               h->mark = h->buf;
+       }
+
+       if ( h->bufsz - (( h->cur - h->buf ) + 1 ) < PA_BUFBLK ) {
+               t = h->buf;
+               h->buf = (char *) realloc( h->buf, h->bufsz + PA_BUFBLK );
+               h->bufsz += PA_BUFBLK;
+               h->mark = h->buf + ( h->mark - t );
+               h->cur = h->buf + ( h->cur - t );
+               h->end = h->buf + ( h->end - t );
+       }
+
+       if (( rc = read( h->fd, h->cur + 1, PA_BUFBLK )) <= 0 ) {
+               return( (char) 0 );
+       }
+
+       h->end = h->cur + rc;
+       return( *(++(h->cur)) );
+}
diff --git a/bin/psorder/pa.h b/bin/psorder/pa.h
new file mode 100644 (file)
index 0000000..d7684e2
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+/*
+ * Functions to aid in parsing:
+ *
+ *     pa_init( fd )           Allocate and return a handle. Reads
+ *                             from fd. Call this first, always.
+ *     pa_getchar( h )         Return a single character or nul, 0,
+ *                             to indicate end-of-file.
+ *     pa_match( h )           Mark the character last read as the start
+ *                             of a match.
+ *     pa_gettok( h )          The match up to but excluding the last
+ *                             character is returned.
+ *     pa_cont( h )            Continue match with previous match. Call
+ *                             immediately after pa_gettok() to get
+ *                             correct behavior.
+ *     pa_cancel( h )          Cancel previous match start.
+ */
+
+#ifndef FILE_H
+#include <stdio.h>
+#endif
+
+#define PA_BUFBLK      1024
+
+#define PA_NORMAL      0
+#define PA_MATCHING    1
+
+typedef struct pa_buf_t {
+       char *buf;
+       char *cur;
+       char *mark;
+       char *end;
+       int fd;
+       int state;
+       unsigned bufsz;
+       char tmp;
+} pa_buf_t;
+
+extern pa_buf_t *pa_init();
+extern char _pa_fixbuf();
+extern char *pa_gettok();
+
+#define pa_getchar(h)  (((h)->cur==(h)->end)?(_pa_fixbuf(h)):\
+                       (*(++((h)->cur))))
+#define pa_match(h)    ((h)->state=PA_MATCHING,(h)->mark=(h)->cur)
+#define pa_cont(h)     (*((h)->cur)=(h)->tmp,(h)->state=PA_MATCHING)
+#define pa_cancel(h)   ((h)->state=PA_NORMAL)
+#define pa_back(h)     (--((h)->cur))
diff --git a/bin/psorder/psorder.c b/bin/psorder/psorder.c
new file mode 100644 (file)
index 0000000..3424851
--- /dev/null
@@ -0,0 +1,561 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/file.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <dirent.h>
+#include <unistd.h>
+#include "pa.h"
+#include "psorder.h"
+
+#include <atalk/paths.h>
+
+#define        DEBUG           0
+
+/*
+ *                     Global Variables
+ */
+
+u_char                 psbuf[ 8192 ];
+struct psinfo_st       psinfo;
+int                    orderflag, forceflag;
+
+main( argc, argv )
+    int                argc;
+    char       **argv;
+{
+    extern int optind;
+    char       *progname;
+    int                errflag = 0;
+    int                c;
+
+    while (( c = getopt( argc, argv, OPTSTR )) != -1 ) {
+       switch ( c ) {
+       case REVCHAR:
+           if ( orderflag ) errflag++;
+           else orderflag = REVERSE;
+           break;
+       case FORWCHAR:
+           if ( orderflag ) errflag++;
+           else orderflag = FORWARD;
+           break;
+       case FORCECHAR:
+           if ( forceflag ) errflag++;
+           else forceflag++;
+           break;
+       }
+    }
+    if ( errflag ) {
+       if (( progname = strrchr( argv[ 0 ], '/' )) == NULL ) {
+           progname = argv[ 0 ];
+       } else progname++;
+       fprintf( stderr, "usage: %s [-duf] [sourcefile]\n", progname );
+       return( -1 );
+    } else if ( !orderflag ) orderflag = FORWARD;
+
+    if ( optind >= argc ) {
+       return( psorder( STDIN ));
+    }
+    return( psorder( argv[ optind ] ));
+}
+
+int
+psorder( path )
+    char       *path;
+{
+    int                        tempfd;
+    int                        inputfd;
+    char               tempfile[MAXNAMLEN];
+
+    filesetup( path, &inputfd, tempfile, &tempfd );
+    readps( inputfd, tempfd, tempfile );
+    if ( lseek( tempfd, REWIND, SEEK_SET ) < 0 ) {
+       perror( tempfile );
+       filecleanup( -1, tempfd, tempfile );
+    }
+    writeps( tempfd, tempfile );
+    filecleanup( 0, tempfd, tempfile );
+    return( 0 );
+}
+
+void
+filesetup( inputfile, infd, tfile, tfd )
+    char       *inputfile;
+    int                *infd;
+    char       *tfile;
+    int                *tfd;
+{
+    struct stat                st;
+    char               *template = _PATH_TMPPAGEORDER;
+
+    if ( strcmp( inputfile, STDIN ) != 0 ) {
+       if ( stat( inputfile, &st ) < 0 ) {
+           perror( inputfile );
+           filecleanup( -1, -1, "" );
+       }
+       if ( st.st_mode & S_IFMT & S_IFDIR ) {
+           fprintf( stderr, "%s is a directory.\n", inputfile );
+           filecleanup( 0, -1, "" );
+       }
+       if (( *infd = open( inputfile, O_RDONLY, 0600 )) < 0 ) {
+           perror( inputfile );
+           filecleanup( -1, -1, "" );
+       }
+    } else {
+       *infd = 0;
+    }
+
+#if DEBUG
+    fprintf( stderr, "Input file or stdin and stdout opened.\n" );
+    fprintf( stderr, "Input file descriptor is %d .\n", *infd );
+#endif
+
+/*
+       make temporary file
+ */
+
+    (void *)strncpy( tfile, template, MAXNAMLEN );
+    if (( *tfd = mkstemp( tfile )) == -1 ) {
+       fprintf( stderr, "can't create temporary file %s\n", tfile );
+       filecleanup( -1, -1, "" );
+    }
+
+#if DEBUG
+    fprintf( stderr, "Temporary file %s created and opened.\n", tfile );
+    fprintf( stderr, "Temporary file descriptor is %d .\n", *tfd );
+#endif
+
+    psinfo.firstpage = NULL;
+    psinfo.lastpage = NULL;
+    psinfo.trailer = 0;
+    psinfo.pages.offset = 0;
+    psinfo.pages.end = 0;
+    psinfo.pages.num[0] = '\0';
+    psinfo.pages.order[0] = '\0';
+
+    return;
+}
+
+void
+readps( inputfd, tempfd, tempfile )
+    int                        inputfd;
+    int                        tempfd;
+    char               *tempfile;
+{
+    off_t              ccread = 0;
+    off_t              ccmatch;
+    char               *curtok = 0;
+    FILE               *tempstream;
+    pa_buf_t           *pb;
+    int                        n;
+    char               c = -1;
+    char               pc, cc = 0;
+
+    pb = pa_init( inputfd );
+    if (( tempstream = fdopen( tempfd, "w" )) == NULL ) {
+       perror( "fdopen fails for tempfile" );
+       filecleanup( -1, tempfd, tempfile );
+    }
+
+    if (( c = pa_getchar( pb )) != 0 ) {
+       ccread++;
+       (void)putc( c, tempstream );
+       pc = 0;
+       cc = c;
+       pa_match( pb );
+       n = strlen( PPSADOBE );
+       for ( ; ( n > 0 ) && (( c = pa_getchar( pb )) != 0 ) ; n-- ) {
+           ccread++ ;
+           (void)putc( c, tempstream );
+           pc = cc;
+           cc = c;
+       }
+       curtok = pa_gettok( pb );
+    }
+#if DEBUG
+    fprintf( stderr, "%s\n", curtok );
+#endif
+
+/*
+ * not postscript
+ */
+    if ( strcmp( curtok, PPSADOBE ) != 0 ) {
+#if DEBUG
+    fprintf( stderr, "in the not postscript section of readps\n" );
+#endif
+       while (( c = pa_getchar( pb )) != 0 ) {
+           ccread++;
+           (void)putc( c, tempstream );
+           pc = cc;
+           cc = c;
+       }
+
+       (void)fflush( tempstream );
+       return;
+    }
+
+/*
+ * postscript
+ */
+#if DEBUG
+    fprintf( stderr, "in the postscript section of readps\n" );
+#endif
+    while (( c = pa_getchar( pb )) != 0 ) {
+       ccread++;
+       (void)putc( c, tempstream );
+       pc = cc;
+       cc = c;
+       if ((( pc == '\r' ) || ( pc == '\n' )) && ( cc == '%' )) {
+#if DEBUG
+           fprintf( stderr, "supposed start of match, cc = %c\n", cc );
+#endif
+           pa_match( pb );
+           ccmatch = ccread - 1;
+           while ( c = pa_getchar( pb )) {
+               if ( c != 0 ) {
+                   ccread++;
+                   (void)putc( c, tempstream );
+                   pc = cc;
+                   cc = c;
+               }
+               if (( c == '\r' ) || ( c == '\n' ) || ( cc == '\0' )) {
+                   curtok = pa_gettok( pb );
+#if DEBUG
+                   fprintf( stderr, "%s\n", curtok );
+#endif
+                   if ( handletok( ccmatch, curtok ) < 0 ) {
+                       perror( "malloc died" );
+                       filecleanup( -1, tempfd, tempfile );
+                   }
+                   break;
+               }
+               if ( c == 0 ) break;
+           }
+           if ( c == 0 ) break;
+       }
+    }
+
+    (void)fflush( tempstream );
+    return;
+}
+
+int
+handletok( count, token )
+    off_t              count;
+    char               *token;
+{
+    int                        incdoc = 0;
+    struct pspage_st   *newpage;
+    char               *tmp;
+
+    if (( strncmp( PENDDOC, token, strlen( PENDDOC )) == 0 ) && incdoc ) {
+       incdoc--;
+#if DEBUG
+       fprintf( stderr, "found an EndDoc\n" );
+#endif
+
+    } else if ( strncmp( PBEGINDOC, token, strlen( PBEGINDOC )) == 0 ) {
+       incdoc++;
+#if DEBUG
+       fprintf( stderr, "found a BeginDoc\n" );
+#endif
+
+    } else if ( !incdoc && 
+           ( strncmp( PPAGE, token, strlen( PPAGE )) == 0 )) {
+#if DEBUG
+       fprintf( stderr, "found a Page\n" );
+#endif
+       if (( newpage = getpspage( count )) == NULL ) {
+           return( -1 );
+       }
+       if ( psinfo.firstpage == NULL ) {
+           newpage->prevpage = NULL;
+           psinfo.firstpage = newpage;
+       } else {
+           newpage->prevpage = psinfo.lastpage;
+           psinfo.lastpage->nextpage = newpage;
+       }
+       psinfo.lastpage = newpage;
+       while ( *token++ != ':' );
+       if (( tmp = strtok( token, WHITESPACE )) != NULL ) {
+           (void)strncpy( newpage->lable, tmp, NUMLEN );
+           if (( tmp = strtok( NULL, WHITESPACE )) != NULL ) {
+               (void)strncpy( newpage->ord, tmp, ORDLEN );
+           }
+       }
+#if DEBUG
+       fprintf( stderr, "page lable %s, page ord %s\n", newpage->lable,
+               newpage->ord );
+#endif
+
+    } else if ( !incdoc && 
+           ( strncmp( PPAGES, token, strlen( PPAGES )) == 0 )) {
+#if DEBUG
+       fprintf( stderr, "found a Pages\n" );
+#endif
+       psinfo.pages.offset = count;
+       psinfo.pages.end = strlen( token ) + count;
+       while ( *token++ != ':' );
+       while ( isspace( *token )) token++;
+       if ( strncmp( ATEND, token, strlen( ATEND )) == 0 ) {
+#if DEBUG
+           fprintf( stderr, "it is a Pages: (atend)\n" );
+#endif
+           psinfo.pages.offset = 0;
+           psinfo.pages.end = 0;
+       } else {
+           if (( tmp = strtok( token, WHITESPACE )) != NULL ) {
+               (void)strncpy( psinfo.pages.num, tmp, NUMLEN );
+               if (( tmp = strtok( NULL, WHITESPACE )) != NULL ) {
+                   (void)strncpy( psinfo.pages.order, tmp, ORDERLEN );
+               }
+           }
+#if DEBUG
+           fprintf( stderr, "number of pages %s\n", psinfo.pages.num );
+           fprintf( stderr, "order control number %s\n", psinfo.pages.order );
+#endif
+       }
+
+    } else if ( !incdoc && 
+           ( strncmp( PTRAILER, token, strlen( PTRAILER )) == 0 )) {
+#if DEBUG
+       fprintf( stderr, "found the Trailer\n" );
+#endif
+       if  ( psinfo.trailer == 0 ) {
+           psinfo.trailer = count;
+       }
+    }
+
+    return( 0 );
+}
+
+void
+writeps( tempfd, tempfile )
+    int                        tempfd;
+    char               *tempfile;
+{
+    struct stat                st;
+    off_t              endofpage;
+    int                        order;
+
+    if ( stat( tempfile, &st ) < 0 ) {
+       perror( "stat failed" );
+       filecleanup( -1, tempfd, tempfile );
+    }
+    if ( psinfo.trailer == 0 ) {
+       endofpage = st.st_size;
+    } else endofpage = psinfo.trailer;
+
+    if (( psinfo.firstpage == NULL ) || 
+           ( psinfo.firstpage == psinfo.lastpage )) {
+       order = FORWARD;
+    } else if ( psinfo.pages.offset == 0 ) {
+       order = orderflag;
+    } else if (( strncmp( psinfo.pages.order, "", ORDERLEN ) == 0 ) ||
+           ( strncmp( psinfo.pages.order, "1", ORDERLEN ) == 0 )) {
+       order = orderflag;
+       if ( order == REVERSE ) strcpy( psinfo.pages.order, "-1" );
+    } else if ( strncmp( psinfo.pages.order, "-1", ORDERLEN ) == 0 ) {
+       if ( orderflag == FORWARD ) {
+           order = REVERSE;
+           strcpy( psinfo.pages.order, "1" );
+       } else order = FORWARD;
+    } else if (( strncmp( psinfo.pages.order, "0", ORDERLEN ) == 0 ) &&
+           forceflag ) {
+       order = orderflag;
+    } else order = FORWARD;
+
+    if ( order == FORWARD ) {
+       temp2out( tempfd, tempfile, st.st_size );
+    } else {
+/*
+ *     output the header stuff and rewrite the $$Pages line
+ *     if it is in the header and not %%Pages: (atend)
+ */
+       if ( psinfo.firstpage->offset > 0 ) {
+           if (( psinfo.firstpage->offset > psinfo.pages.offset ) &&
+                   ( psinfo.pages.offset != 0 )) {
+               temp2out( tempfd, tempfile, psinfo.pages.offset );
+               writelable( tempfd, tempfile, PPAGES );
+               if ( lseek( tempfd, psinfo.pages.end, SEEK_SET ) < 0 ) {
+                   perror( tempfile );
+                   filecleanup( -1, tempfd, tempfile );
+               }
+               temp2out( tempfd, tempfile, 
+                       psinfo.firstpage->offset - psinfo.pages.end );
+           } else temp2out( tempfd, tempfile, psinfo.firstpage->offset );
+       }
+/*
+ *     output the pages, last to first
+ */
+       while ( psinfo.lastpage != NULL ) {
+           if ( lseek( tempfd, psinfo.lastpage->offset, SEEK_SET ) < 0 ) {
+               perror( tempfile );
+               filecleanup( -1, tempfd, tempfile );
+           }
+           temp2out( tempfd, tempfile, endofpage - psinfo.lastpage->offset );
+           endofpage = psinfo.lastpage->offset;
+           psinfo.lastpage = psinfo.lastpage->prevpage;
+           if ( psinfo.lastpage != NULL ) {
+               (void)free( psinfo.lastpage->nextpage );
+               psinfo.lastpage->nextpage = NULL;
+           }
+       }
+/*
+ *     output the trailer stuff and rewrite the $$Pages line
+ *     if it is in the trailer
+ */
+       if ( psinfo.trailer != 0 ) {
+           if ( lseek( tempfd, psinfo.trailer, SEEK_SET ) < 0 ) {
+               perror( tempfile );
+               filecleanup( -1, tempfd, tempfile );
+           }
+           if ( psinfo.trailer < psinfo.pages.offset ) {
+               temp2out( tempfd, tempfile,
+                       psinfo.pages.offset - psinfo.trailer );
+               writelable( tempfd, tempfile, PPAGES );
+               if ( lseek( tempfd, psinfo.pages.end, SEEK_SET ) < 0 ) {
+                   perror( tempfile );
+                   filecleanup( -1, tempfd, tempfile );
+               }
+               temp2out( tempfd, tempfile, st.st_size - psinfo.pages.end );
+           } else temp2out( tempfd, tempfile, st.st_size - psinfo.trailer );
+       }
+    }
+
+    return;
+}
+
+void
+writelable( tempfd, tempfile, lable )
+    int                        tempfd;
+    char               *tempfile;
+    char               *lable;
+{
+    char               line[256];
+    int                        ccwrite;
+    int                        linelen;
+    char               *argone;
+    char               *argtwo;
+
+    if ( strcmp( lable, PPAGES ) == 0 ) {
+       argone = psinfo.pages.num;
+       argtwo = psinfo.pages.order;
+    } else {
+       argone = argtwo = NULL;
+    }
+    (void)sprintf( line, "%s %s %s", lable, argone, argtwo );
+    linelen = strlen( line );
+
+    ccwrite = write( 1, line, linelen );
+    if ( ccwrite < 0 ) {
+       perror( "stdout" );
+       filecleanup( ccwrite, tempfd, tempfile );
+    } else {
+       linelen -= ccwrite;
+    }
+}
+
+void
+temp2out( tempfd, tempfile, length )
+    int                        tempfd;
+    char               *tempfile;
+    off_t              length;
+{
+    int                        ccread;
+    int                        ccwrite;
+    int                        size;
+
+    while ( length > 0 ) {
+       if ( length > sizeof( psbuf )) {
+           size = sizeof( psbuf );
+       } else size = length;
+       if (( ccread = read( tempfd, psbuf, size )) > 0 ) {
+           size = ccread;
+           while ( ccread > 0 ) {
+               ccwrite = write( 1, psbuf, ccread );
+               if ( ccwrite < 0 ) {
+                   perror( "stdout" );
+                   filecleanup( ccwrite, tempfd, tempfile );
+               } else {
+                   ccread -= ccwrite;
+               }
+           }
+       }
+       if ( ccread < 0 ) {
+           perror( "temporary file" );
+           filecleanup( ccread, tempfd, tempfile );
+       }
+       length -= size;
+    }
+}
+
+struct pspage_st
+*getpspage( off )
+    off_t              off;
+{
+    struct pspage_st   *newpspage;
+
+    newpspage = (struct pspage_st *)malloc( sizeof( struct pspage_st )); 
+    if ( newpspage != NULL ) {
+       newpspage->offset = off;
+       newpspage->nextpage = NULL;
+       *newpspage->lable = '\0';
+       *newpspage->ord = '\0';
+    }
+    return( newpspage );
+}
+
+void
+filecleanup( errorcode, tfd, tfile )
+    int                        errorcode;
+    int                        tfd;
+    char               *tfile;
+{
+
+/*
+       Close and unlink the temporary file.
+ */
+
+    if ( tfd != 0 ) {
+       if ( close( tfd ) != 0 ) {
+           perror( tfile );
+           exit( errorcode );
+       }
+       if ( unlink( tfile ) != 0 ) {
+           perror( tfile );
+           exit( errorcode );
+       }
+    }
+
+    exit( errorcode );
+}
diff --git a/bin/psorder/psorder.h b/bin/psorder/psorder.h
new file mode 100644 (file)
index 0000000..ab99076
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#ifndef        STDIN
+#      define  STDIN   "-"
+#endif
+#ifndef FALSE
+#      define  FALSE   0
+#      define  TRUE    1
+#endif
+
+#define REVCHAR                'd'
+#define FORWCHAR       'u'
+#define FORCECHAR      'f'
+#define OPTSTR         "duf"
+
+#define WHITESPACE     " \t"
+#define ATEND          "(atend)"
+#define PPSADOBE       "%!PS-Adobe-"
+#define        PPAGE           "%%Page:"
+#define PPAGES         "%%Pages:"
+#define PTRAILER       "%%Trailer"
+#define PBEGINDOC      "%%BeginDocument:"
+#define PENDDOC                "%%EndDocument"
+#define PBEGINBIN      "%%BeginBinary:"
+#define PENDBIN                "%%EndBinary"
+
+#define REWIND         0L
+#define REVERSE                1
+#define FORWARD                2
+
+#define LABELLEN       32
+#define ORDLEN         4
+struct pspage_st {
+    struct pspage_st   *nextpage;
+    struct pspage_st   *prevpage;
+    off_t              offset;
+    char               lable[ LABELLEN ];
+    char               ord[ ORDLEN ];
+};
+
+#define NUMLEN         10
+#define ORDERLEN       3
+struct pages_st {
+    off_t              offset;
+    off_t              end;
+    char               num[ NUMLEN ];
+    char               order[ ORDERLEN ];
+};
+
+struct psinfo_st {
+    struct pspage_st   *firstpage;
+    struct pspage_st   *lastpage;
+    off_t              trailer;
+    struct pages_st    pages;
+};
+
+int
+psorder();
+
+void
+filesetup();
+
+void
+readps();
+
+int
+handletok();
+
+void
+writeps();
+
+void
+writelable();
+
+void
+temp2out();
+
+struct pspage_st
+*getpspage();
+
+void
+filecleanup();
diff --git a/config/AppleVolumes.default b/config/AppleVolumes.default
new file mode 100644 (file)
index 0000000..02f7f18
--- /dev/null
@@ -0,0 +1,72 @@
+# This file looks empty when viewed with "vi".  In fact, there is one
+# '~', so users with no AppleVolumes file in their home directory get
+# their home directory by default.
+# 
+# volume format:
+# :DEFAULT: [all of the default options except volume name]
+# path [name] [casefold:x] [codepage:y] [options:z,l,j] \
+#   [allow:a,@b,c,d] [deny:a,@b,c,d] [dbpath:path] [password:p] \
+#   [rwlist:a,@b,c,d] [rolist:a,@b,c,d] [limitsize:value in bytes]
+#
+#
+# name:      volume name. it can't include the ':' character and is limited
+#            to 27 characters in length.
+#
+# variable substitutions:
+# you can use variables for both <path> and <name> now. here are the
+# rules:
+#     1) if you specify an unknown variable, it will not get converted. 
+#     2) if you specify a known variable, but that variable doesn't have
+#        a value, it will get ignored.
+#
+# the variables:
+# $c   -> client's ip or appletalk address
+# $f   -> full name (whatever's in the gecos field)
+# $g   -> group
+# $h   -> hostname 
+# $s   -> server name (can be the hostname)
+# $u   -> username (if guest, it's whatever user guest is running as)
+# $v   -> volume name (either ADEID_NAME or basename of path)
+# $z   -> zone (may not exist)
+# $$   -> $
+#
+# casefold options [syntax: casefold:option]:
+# tolower    -> lowercases names in both directions
+# toupper    -> uppercases names in both directions
+# xlatelower -> client sees lowercase, server sees uppercase
+# xlateupper -> client sees uppercase, server sees lowercase
+#
+# allow/deny/rwlist/rolist format [syntax: allow:user1,@group]:
+# user1,@group,user2  -> allows/denies access from listed users/groups
+#                        rwlist/rolist control whether or not the
+#                       volume is ro for those users.
+#
+# miscellaneous options [syntax: options:option1,option2]:
+# prodos              -> make compatible with appleII clients.
+# crlf                -> enable crlf translation for TEXT files.
+# noadouble           -> don't create .AppleDouble unless a resource
+#                        fork needs to be created.
+# ro                  -> mount the volume as read-only.
+# mswindows           -> enforce filename restrictions imposed by MS
+#                        Windows. this will also invoke a default
+#                       codepage (iso8859-1) if one isn't already 
+#                       specified.
+# nohex                      -> don't do :hex translations for anything
+#                       except dot files. specify usedots as well if
+#                       you want that turned off. note: this option
+#                       makes the / character illegal.
+# usedots             -> don't do :hex translation for dot files. note: when 
+#                        this option gets set, certain file names
+#                       become illegal. these are .Parent and
+#                       anything that starts with .Apple. also, dot
+#                       files created on the unix side are marked
+#                       invisible. 
+# limitsize           -> limit disk size reporting to 2GB. this is
+#                        here for older macintoshes using newer
+#                        appleshare clients. yucko.
+#
+# codepage:filename   -> load filename from nls directory.
+# dbpath:path         -> store the database stuff in the following path.
+# password:password   -> set a volume password (8 characters max)
+#
+~
diff --git a/config/AppleVolumes.system b/config/AppleVolumes.system
new file mode 100644 (file)
index 0000000..403ba24
--- /dev/null
@@ -0,0 +1,282 @@
+# Last Updated July 8, 1999
+# Use at your own risk.  No guarantees express or implied.
+#
+# Try to use MacPerl script 'ICDumpSuffixMap' included in /usr/doc 
+# to download file mapping list from your Internet Config Preference.
+#
+# inoue@ma.ns.musashi-tech.ac.jp.
+#
+
+.         "TEXT"  "ttxt"      ASCII Text                     SimpleText                text/plain
+
+.mf       "TEXT"  "*MF*"      Metafont                       Metafont
+.sty      "TEXT"  "*TEX"      TeX Style                      Textures
+.psd      "8BPS"  "8BIM"      PhotoShop Document             Photoshop
+.pxr      "PXR "  "8BIM"      Pixar Image                    Photoshop
+.sea      "APPL"  "????"      Self-Extracting Archive        Self Extracting Archive
+.apd      "TEXT"  "ALD3"      Aldus Printer Description      Aldus PageMaker
+.pm3      "ALB3"  "ALD3"      PageMaker 3 Document           PageMaker
+.pm4      "ALB4"  "ALD4"      PageMaker 4 Document           PageMaker
+.pt4      "ALT4"  "ALD4"      PageMaker 4 Template           PageMaker
+.pm5      "ALB5"  "ALD5"      PageMaker 5 Document           PageMaker
+.pt5      "ALT5"  "ALD5"      PageMaker 5 Template           PageMaker
+.pdx      "TEXT"  "ALD5"      Printer Description            PageMaker
+.ppd      "TEXT"  "ALD5"      Printer Description            PageMaker
+.dl       "DL  "  "AnVw"      DL Animation                   MacAnim Viewer
+.gl       "GL  "  "AnVw"      GL Animation                   MacAnim Viewer
+.url      "AURL"  "Arch"      URL Bookmark                   Anarchie                  message/external-body
+.zoo      "Zoo "  "Booz"      Zoo Archive                    MacBooz
+.pdf      "PDF "  "CARO"      Portable Document Format       Acrobat Reader            application/pdf
+.h        "TEXT"  "CWIE"      C Include File                 CodeWarrior
+.hp       "TEXT"  "CWIE"      C Include File                 CodeWarrior
+.hpp      "TEXT"  "CWIE"      C Include File                 CodeWarrior
+.c        "TEXT"  "CWIE"      C Source                       CodeWarrior
+.cp       "TEXT"  "CWIE"      C++ Source                     CodeWarrior
+.cpp      "TEXT"  "CWIE"      C++ Source                     CodeWarrior
+.class    "Clss"  "CWIE"      Java Class File                CodeWarrior
+.java     "TEXT"  "CWIE"      Java Source File               CodeWarrior
+.p        "TEXT"  "CWIE"      Pascal Source                  CodeWarrior
+.pas      "TEXT"  "CWIE"      Pascal Source                  CodeWarrior
+.cvs      "drw2"  "DAD2"      Canvas Drawing                 Canvas
+.arj      "BINA"  "DArj"      ARJ Archive                    DeArj
+.evy      "EVYD"  "ENVY"      Envoy Document                 Envoy
+.fm       "FMPR"  "FMPR"      FileMaker Pro Database         FileMaker Pro
+.dbf      "COMP"  "FOX+"      DBase Document                 FoxBase+
+.mif      "TEXT"  "Fram"      FrameMaker MIF                 FrameMaker                application/x-framemaker
+.arr      "ARR "  "GKON"      Amber ARR image                GraphicConverter
+.iff      "ILBM"  "GKON"      Amiga IFF Image                GraphicConverter
+.lbm      "ILBM"  "GKON"      Amiga IFF Image                GraphicConverter
+.ilbm     "ILBM"  "GKON"      Amiga ILBM Image               GraphicConverter
+.ani      "ANIi"  "GKON"      Animated NeoChrome             GraphicConverter
+.pcs      "PICS"  "GKON"      Animated PICTs                 GraphicConverter
+.pc1      "Dega"  "GKON"      Atari Degas Image              GraphicConverter
+.pc2      "Dega"  "GKON"      Atari Degas Image              GraphicConverter
+.pc3      "Dega"  "GKON"      Atari Degas Image              GraphicConverter
+.pi1      "Dega"  "GKON"      Atari Degas Image              GraphicConverter
+.pi2      "Dega"  "GKON"      Atari Degas Image              GraphicConverter
+.pi3      "Dega"  "GKON"      Atari Degas Image              GraphicConverter
+.ic1      "IMAG"  "GKON"      Atari Image                    GraphicConverter
+.ic2      "IMAG"  "GKON"      Atari Image                    GraphicConverter
+.ic3      "IMAG"  "GKON"      Atari Image                    GraphicConverter
+.neo      "NeoC"  "GKON"      Atari NeoChrome                GraphicConverter
+.pac      "STAD"  "GKON"      Atari STAD Image               GraphicConverter
+.spc      "Spec"  "GKON"      Atari Spectrum 512             GraphicConverter
+.tny      "TINY"  "GKON"      Atari TINY Bitmap              GraphicConverter
+.pm       "PMpm"  "GKON"      Bitmap from xv                 GraphicConverter
+.scg      "RIX3"  "GKON"      ColoRIX                        GraphicConverter
+.sci      "RIX3"  "GKON"      ColoRIX                        GraphicConverter
+.scp      "RIX3"  "GKON"      ColoRIX                        GraphicConverter
+.scr      "RIX3"  "GKON"      ColoRIX                        GraphicConverter
+.scu      "RIX3"  "GKON"      ColoRIX                        GraphicConverter
+.cgm      "CGMm"  "GKON"      Computer Graphics Meta         GraphicConverter
+.vff      "VFFf"  "GKON"      DESR VFF Greyscale Image       GraphicConverter
+.cut      "Halo"  "GKON"      Dr Halo Image                  GraphicConverter
+.art      "ART "  "GKON"      First Publisher                GraphicConverter
+.fts      "FITS"  "GKON"      Flexible Image Transport       GraphicConverter
+.fit      "FITS"  "GKON"      Flexible Image Transport       GraphicConverter          image/x-fits
+.gem      "GEM-"  "GKON"      GEM Metafile                   GraphicConverter
+.img      "IMGg"  "GKON"      GEM bit image/XIMG             GraphicConverter
+.grp      "GRPp"  "GKON"      GRP Image                      GraphicConverter
+.hpgl     "HPGL"  "GKON"      HP GL/2                        GraphicConverter
+.plt      "HPGL"  "GKON"      HP GL/2                        GraphicConverter
+.ief      "IEF "  "GKON"      IEF image                      GraphicConverter          image/ief
+.scc      "MSX "  "GKON"      MSX pitcure                    GraphicConverter
+.msp      "MSPp"  "GKON"      Microsoft Paint                GraphicConverter
+.pcx      "PCXx"  "GKON"      PC PaintBrush                  GraphicConverter
+.pbm      "PPGM"  "GKON"      Portable Bitmap                GraphicConverter          image/x-portable-bitmap
+.pgm      "PPGM"  "GKON"      Portable Graymap               GraphicConverter          image/x-portable-graymap
+.ppm      "PPGM"  "GKON"      Portable Pixmap                GraphicConverter          image/x-portable-pixmap
+.shp      "SHPp"  "GKON"      Printmaster Icon Library       GraphicConverter
+.qdv      "QDVf"  "GKON"      QDV image                      GraphicConverter
+.rif      "RIFF"  "GKON"      RIFF Graphic                   GraphicConverter
+.rle      "RLE "  "GKON"      RLE image                      GraphicConverter
+.raw      "BINA"  "GKON"      Raw Image                      GraphicConverter
+.bw       "SGI "  "GKON"      SGI Image                      GraphicConverter
+.rgb      "SGI "  "GKON"      SGI Image                      GraphicConverter          image/x-rgb
+.rgba     "SGI "  "GKON"      SGI Image                      GraphicConverter          image/x-rgb
+.six      "SIXE"  "GKON"      SIXEL image                    GraphicConverter
+.ct       "..CT"  "GKON"      Scitex-CT                      GraphicConverter
+.dcx      "DCXx"  "GKON"      Some PCX Images                GraphicConverter
+.sup      "SCRN"  "GKON"      StartupScreen                  GraphicConverter
+.sr       "SUNn"  "GKON"      Sun Raster Image               GraphicConverter
+.sun      "SUNn"  "GKON"      Sun Raster Image               GraphicConverter
+.targa    "TPIC"  "GKON"      Truevision Image               GraphicConverter
+.tga      "TPIC"  "GKON"      Truevision Image               GraphicConverter
+.clp      "CLPp"  "GKON"      Windows Clipboard              GraphicConverter
+.icn      "ICO "  "GKON"      Windows Icon                   GraphicConverter
+.ico      "ICO "  "GKON"      Windows Icon                   GraphicConverter
+.wmf      "WMF "  "GKON"      Windows Metafile               GraphicConverter
+.wpg      "WPGf"  "GKON"      WordPerfect Graphic            GraphicConverter
+.xbm      "XBM "  "GKON"      X-Windows Bitmap               GraphicConverter          image/x-xbm
+.x10      "XWDd"  "GKON"      X-Windows Dump                 GraphicConverter          image/x-xwd
+.x11      "XWDd"  "GKON"      X-Windows Dump                 GraphicConverter          image/x-xwd
+.xwd      "XWDd"  "GKON"      X-Windows Dump                 GraphicConverter          image/x-xwd
+.xpm      "XPM "  "GKON"      X-Windows Pixmap               GraphicConverter          image/x-xpm
+.htm      "TEXT"  "MOSS"      HyperText                      Netscape Communicator     text/html
+.html     "TEXT"  "MOSS"      HyperText                      Netscape Communicator     text/html
+.m2v      "MPG2"  "MPG2"      MPEG-2 IPB videostream         MPEG2decoder
+.for      "TEXT"  "MPS "      Fortran Source                 MPW Shell
+.wri      "WDBN"  "MSWD"      MS Write/Windows               Microsoft Word
+.mcw      "WDBN"  "MSWD"      Mac Word Document              Microsoft Word
+.rtf      "TEXT"  "MSWD"      Rich Text Format               Microsoft Word            application/rtf
+.doc      "WDBN"  "MSWD"      Word Document                  Microsoft Word            application/msword
+.dot      "sDBN"  "MSWD"      Word for Windows Template      Microsoft Word
+.mw       "MW2D"  "MWII"      MacWrite Document              MacWrite II               application/macwriteii
+.mwii     "MW2D"  "MWII"      MacWrite Document              MacWrite II               application/macwriteii
+.pl       "TEXT"  "McPL"      Perl Source                    MacPerl
+.asf      "ASF_"  "Ms01"      Netshow Player                 Netshow Server            video/x-ms-asf
+.asx      "ASX_"  "Ms01"      Netshow Player                 Netshow Server            video/x-ms-asf
+.oda      "ODIF"  "ODA "      ODA Document                   MacODA XTND Translator    application/oda
+.latex    "TEXT"  "OTEX"      Latex                          OzTex                     application/x-latex
+.texi     "TEXT"  "OTEX"      TeX Document                   OzTeX
+.tex      "TEXT"  "OTEX"      TeX Document                   OzTeX                     application/x-tex
+.texinfo  "TEXT"  "OTEX"      TeX Document                   OzTeX                     application/x-texinfo
+.diz      "TEXT"  "R*Ch"      BBS Descriptive Text           BBEdit
+.ml       "TEXT"  "R*ch"      ML Source                      BBEdit
+.mak      "TEXT"  "R*ch"      Makefile                       BBEdit
+.m2       "TEXT"  "R*ch"      Modula 2 Source                BBEdit
+.i3       "TEXT"  "R*ch"      Modula 3 Interface             BBEdit
+.m3       "TEXT"  "R*ch"      Modula 3 Source                BBEdit
+.prn      "TEXT"  "R*ch"      Printer Output File            BBEdit
+.rtx      "TEXT"  "R*ch"      Rich Text                      BBEdit                    text/richtext
+.rpl      "FRL!"  "REP!"      Replica Document               Replica
+.rib      "TEXT"  "RINI"      Renderman 3D Data              Renderman
+.rsc      "rsrc"  "RSED"      Resource File                  ResEdit
+.rsrc     "rsrc"  "RSED"      Resource File                  ResEdit
+.pdb      "TEXT"  "RSML"      Brookhaven PDB file            RasMac
+.mol      "TEXT"  "RSML"      MDL Molfile                    RasMac
+.bar      "BARF"  "S691"      Unix BAR Archive               SunTar
+.aif      "AIFF"  "SCPL"      AIFF Sound                     SoundApp                  audio/x-aiff
+.aiff     "AIFF"  "SCPL"      AIFF Sound                     SoundApp                  audio/x-aiff
+.aifc     "AIFC"  "SCPL"      AIFF Sound Compressed          SoundApp                  audio/x-aiff
+.al       "ALAW"  "SCPL"      ALAW Sound                     SoundApp
+.8svx     "8SVX"  "SCPL"      Amiga 8-bit sound              SoundApp
+.svx      "8SVX"  "SCPL"      Amiga IFF Sound                SoundApp
+.med      "STrk"  "SCPL"      Amiga MED Sound                SoundApp
+.8med     "STrk"  "SCPL"      Amiga OctaMed music            SoundApp
+.mod      "STrk"  "SCPL"      MOD Music                      SoundApp
+.nst      "STrk"  "SCPL"      MOD Music                      SoundApp
+.okt      "OKTA"  "SCPL"      Oktalyser MOD Music            SoundApp
+.wve      "BINA"  "SCPL"      PSION sound                    SoundApp
+.snd      "BINA"  "SCPL"      Sound of various types         SoundApp
+.hcom     "FSSD"  "SCPL"      SoundEdit Sound ex SOX         SoundApp
+.voc      "VOC "  "SCPL"      VOC Sound                      SoundApp
+.sf       "IRCM"  "SDHK"      IRCAM Sound                    SoundHack
+.pkg      "HBSF"  "SITx"      AppleLink Package              StuffIt Expander
+.hqx      "TEXT"  "SITx"      BinHex                         StuffIt Expanderª         application/mac-binhex40
+.sithqx   "TEXT"  "SITx"      BinHexed StuffIt Archive       StuffIt Expander          application/mac-binhex40
+.cpt      "PACT"  "SITx"      Compact Pro Archive            StuffIt Expander
+.taz      "ZIVU"  "SITx"      Compressed Tape ARchive        StuffIt Expander          application/x-compress
+.gz       "SIT!"  "SITx"      Gnu ZIP Archive                StuffIt Expander          application/x-gzip
+.tgz      "Gzip"  "SITx"      Gnu ZIPed Tape ARchive         StuffIt Expander          application/x-gzip
+.lha      "LHA "  "SITx"      LHArc Archive                  StuffIt Expander
+.lzh      "LHA "  "SITx"      LHArc Archive                  StuffIt Expander
+.mime     "TEXT"  "SITx"      MIME Message                   StuffIt Expander          message/rfc822
+.bin      "SIT!"  "SITx"      MacBinary                      StuffIt Expander          application/macbinary
+.arc      "mArc"  "SITx"      PC ARChive                     StuffIt Expander
+.zip      "ZIP "  "SITx"      PC ZIP Archive                 StuffIt Expander          application/zip
+.pit      "PIT "  "SITx"      PackIt Archive                 StuffIt Expander
+.pf       "CSIT"  "SITx"      Private File                   StuffIt Expander 
+.sit      "SIT!"  "SITx"      StuffIt 1.5.1 Archive          StuffIt Expander          application/x-stuffit
+.uu       "TEXT"  "SITx"      UUEncode                       StuffIt Expander
+.uue      "TEXT"  "SITx"      UUEncode                       StuffIt Expander
+.Z        "ZIVU"  "SITx"      Unix Compress Archive          StuffIt Expander          application/x-compress
+.tar      "TARF"  "SITx"      Unix Tape ARchive              StuffIt Expander          application/x-tar
+.669      "6669"  "SNPL"      669 MOD Music                  PlayerPro
+.xm       "XM  "  "SNPL"      FastTracker MOD Music          PlayerPro
+.mtm      "MTM "  "SNPL"      MultiMOD Music                 PlayerPro
+.s3m      "S3M "  "SNPL"      ScreamTracker 3 MOD            PlayerPro
+.com      "PCFA"  "SWIN"      MS-DOS Executable              SoftWindows
+.exe      "PCFA"  "SWIN"      MS-DOS Executable              SoftWindows
+.obj      "PCFL"  "SWIN"      Object (DOS/Windows)           SoftWindows
+.ovl      "PCFL"  "SWIN"      Overlay (DOS/Windows)          SoftWindows
+.dll      "PCFL"  "SWIN"      Windows DLL                    SoftWindows
+.dxf      "TEXT"  "SWVL"      AutoCAD 3D Data                Swivel Pro
+.avi      "VfW "  "TVOD"      AVI Movie                      QuickTime Player          video/avi
+.fli      "FLI "  "TVOD"      FLI Animation                  QuickTime Player
+.flc      "FLI "  "TVOD"      FLIC Animation                 QuickTime Player
+.mid      "Midi"  "TVOD"      MIDI Music                     MoviePlayer
+.midi     "Midi"  "TVOD"      MIDI Music                     MoviePlayer
+.mpe      "MPEG"  "TVOD"      MPEG Movie of some sort        MoviePlayer               video/mpeg
+.mpeg     "MPEG"  "TVOD"      MPEG Movie of some sort        MoviePlayer               video/mpeg
+.mpg      "MPEG"  "TVOD"      MPEG Movie of some sort        MoviePlayer               video/mpeg
+.m1v      "M1V "  "TVOD"      MPEG-1 IPB videostream         MoviePlayer               video/mpeg
+.m1a      "MPEG"  "TVOD"      MPEG-1 audiostream             MoviePlayer               audio/x-mpeg
+.mp2      "MPEG"  "TVOD"      MPEG-1 audiostream             MoviePlayer               audio/x-mpeg
+.mp3      "MPG3"  "TVOD"      MPEG-3 audiostream             MoviePlayer               audio/x-mpeg
+.mpa      "MPEG"  "TVOD"      MPEG-1 audiostream             MoviePlayer               audio/x-mpeg
+.m1s      "MPEG"  "TVOD"      MPEG-1 systemstream            MoviePlayer
+.ul       "ULAW"  "TVOD"      Mu-Law Sound                   MoviePlayer               audio/basic
+.moov     "MooV"  "TVOD"      QuickTime Movie                MoviePlayer               video/quicktime
+.mov      "MooV"  "TVOD"      QuickTime Movie                MoviePlayer               video/quicktime
+.qt       "MooV"  "TVOD"      QuickTime Movie                MoviePlayer               video/quicktime
+.au       "ULAW"  "TVOD"      Sun Sound                      QuickTime Player          audio/basic
+.wav      "WAVE"  "TVOD"      Windows WAV Sound              MoviePlayer               audio/x-wav
+.sha      "TEXT"  "UnSh"      Unix Shell Archive             UnShar                    application/x-shar
+.shar     "TEXT"  "UnSh"      Unix Shell Archive             UnShar                    application/x-shar
+.wpm      "WPD1"  "WPC2"      WordPerfect Mac                WordPerfect
+.wp4      ".WP4"  "WPC2"      WordPerfect PC 4.2 Doc         WordPerfect
+.w51      ".WP5"  "WPC2"      WordPerfect PC 5.1 Doc         WordPerfect               application/wordperfect5.1
+.wp       ".WP5"  "WPC2"      WordPerfect PC 5.1 Doc         WordPerfect               application/wordperfect5.1
+.wp5      ".WP5"  "WPC2"      WordPerfect PC 5.x Doc         WordPerfect               application/wordperfect5.1
+.wp6      ".WP6"  "WPC2"      WordPerfect PC 6.x Doc         WordPerfect
+.csv      "TEXT"  "XCEL"      Comma Separated Vars           Excel
+.dif      "TEXT"  "XCEL"      Data Interchange Format        Excel
+.xlc      "XLC "  "XCEL"      Excel Chart                    Excel
+.xlm      "XLM "  "XCEL"      Excel Macro                    Excel
+.xl       "XLS "  "XCEL"      Excel Spreadsheet              Excel
+.xls      "XLS "  "XCEL"      Excel Spreadsheet              Excel
+.xlw      "XLW "  "XCEL"      Excel Workspace                Excel
+.wks      "XLBN"  "XCEL"      Lotus Spreadsheet r1.x         Excel
+.wk1      "XLBN"  "XCEL"      Lotus Spreadsheet r2.1         Excel
+.slk      "TEXT"  "XCEL"      SYLK Spreadsheet               Excel
+.syk      "TEXT"  "XCEL"      SYLK Spreadsheet               Excel
+.sylk     "TEXT"  "XCEL"      SYLK Spreadsheet               Excel
+.tsv      "TEXT"  "XCEL"      Tab Separated Values           Excel                     text/tab-separated-values
+.qxd      "XDOC"  "XPR3"      QuarkXpress Document           QuarkXpress
+.qxt      "XTMP"  "XPR3"      QuarkXpress Template           QuarkXpress
+.image    "dImg"  "ddsk"      Apple DiskCopy Image           Disk Copy
+.etx      "TEXT"  "ezVu"      SEText                         Easy View                 text/x-setext
+.out      "BINA"  "hDmp"      Output File                    HexEdit
+.binary   "BINA"  "hDmp"      Untyped Binary Data            HexEdit                   application/octet-stream
+.gif      "GIFf"  "ogle"      GIF Picture                    PictureViewer             image/gif
+.jfif     "JPEG"  "ogle"      JFIF Image                     PictureViewer
+.jpe      "JPEG"  "ogle"      JPEG Picture                   PictureViewer             image/jpeg
+.jpeg     "JPEG"  "ogle"      JPEG Picture                   PictureViewer             image/jpeg
+.jpg      "JPEG"  "ogle"      JPEG Picture                   PictureViewer             image/jpeg
+.pntg     "PNTG"  "ogle"      Macintosh Painting             PictureViewer
+.bga      "BMPp"  "ogle"      OS/2 Bitmap                    PictureViewer
+.vga      "BMPp"  "ogle"      OS/2 Bitmap                    PictureViewer
+.pict     "PICT"  "ogle"      PICT Picture                   PictureViewer             image/x-macpict
+.mac      "PICT"  "ogle"      PICT Picture                   PictureViewer             image/x-pict
+.pct      "PICT"  "ogle"      PICT Picture                   PictureViewer             image/x-pict
+.pic      "PICT"  "ogle"      PICT Picture                   PictureViewer             image/x-pict
+.png      "PNG "  "ogle"      Portable Network Graphic       PictureViewer
+.sgi      ".SGI"  "ogle"      SGI Image                      PictureViewer
+.tif      "TIFF"  "ogle"      TIFF Picture                   PictureViewer             image/tiff
+.tiff     "TIFF"  "ogle"      TIFF Picture                   PictureViewer             image/tiff
+.bmp      "BMPp"  "ogle"      Windows Bitmap                 PictureViewer
+.tx8      "TEXT"  "ttxt"      8-bit ASCII Text               SimpleText
+.asc      "TEXT"  "ttxt"      ASCII Text                     SimpleText                text/plain
+.ascii    "TEXT"  "ttxt"      ASCII Text                     SimpleText                text/plain
+.text     "TEXT"  "ttxt"      ASCII Text                     SimpleText                text/plain
+.txt      "TEXT"  "ttxt"      ASCII Text                     SimpleText                text/plain
+.faq      "TEXT"  "ttxt"      ASCII Text                     SimpleText                text/x-usenet-faq
+.a        "TEXT"  "ttxt"      Assembly Source                SimpleText
+.asm      "TEXT"  "ttxt"      Assembly Source                SimpleText
+.bas      "TEXT"  "ttxt"      BASIC Source                   SimpleText
+.boo      "TEXT"  "ttxt"      BOO encoded                    SimpleText
+.bib      "TEXT"  "ttxt"      BibTex Bibliography            SimpleText
+.bst      "TEXT"  "ttxt"      BibTex Style                   SimpleText
+.nfo      "TEXT"  "ttxt"      Info Text                      SimpleText                application/text
+.bat      "TEXT"  "ttxt"      MS-DOS Batch File              SimpleText
+.cmd      "TEXT"  "ttxt"      OS/2 Batch File                SimpleText
+.me       "TEXT"  "ttxt"      Text Readme                    SimpleText
+.rme      "TEXT"  "ttxt"      Text Readme                    SimpleText
+.1st      "TEXT"  "ttxt"      Text Readme                    SimpleText                application/text
+.readme   "TEXT"  "ttxt"      Text Readme                    SimpleText                application/text
+.ini      "TEXT"  "ttxt"      Windows INI File               SimpleText
+.ps       "TEXT"  "vgrd"      PostScript                     LaserWriter 8             application/postscript
+.eps      "EPSF"  "vgrd"      Postscript                     LaserWriter 8             application/postscript
+.epsf     "EPSF"  "vgrd"      Postscript                     LaserWriter 8             application/postscript
+.dvi      "ODVI"  "xdvi"      TeX DVI Document               xdvi                      application/x-dvi
diff --git a/config/AppleVolumes.system.cobalt b/config/AppleVolumes.system.cobalt
new file mode 100644 (file)
index 0000000..bfcaa8d
--- /dev/null
@@ -0,0 +1,285 @@
+# Last Updated July 8, 1999
+# Use at your own risk.  No guarantees express or implied.
+#
+# Try to use MacPerl script 'ICDumpSuffixMap' included in /usr/doc 
+# to download file mapping list from your Internet Config Preference.
+#
+# inoue@ma.ns.musashi-tech.ac.jp.
+#
+
+:DEFAULT: options:nohex,usedots
+# Cobalt config start (do not remove this line!)
+# Cobalt config stop (do not remove this line!)
+
+.         "TEXT"  "ttxt"      ASCII Text                     SimpleText                text/plain
+
+.mf       "TEXT"  "*MF*"      Metafont                       Metafont
+.sty      "TEXT"  "*TEX"      TeX Style                      Textures
+.psd      "8BPS"  "8BIM"      PhotoShop Document             Photoshop
+.pxr      "PXR "  "8BIM"      Pixar Image                    Photoshop
+.sea      "APPL"  "????"      Self-Extracting Archive        Self Extracting Archive
+.apd      "TEXT"  "ALD3"      Aldus Printer Description      Aldus PageMaker
+.pm3      "ALB3"  "ALD3"      PageMaker 3 Document           PageMaker
+.pm4      "ALB4"  "ALD4"      PageMaker 4 Document           PageMaker
+.pt4      "ALT4"  "ALD4"      PageMaker 4 Template           PageMaker
+.pm5      "ALB5"  "ALD5"      PageMaker 5 Document           PageMaker
+.pt5      "ALT5"  "ALD5"      PageMaker 5 Template           PageMaker
+.pdx      "TEXT"  "ALD5"      Printer Description            PageMaker
+.ppd      "TEXT"  "ALD5"      Printer Description            PageMaker
+.dl       "DL  "  "AnVw"      DL Animation                   MacAnim Viewer
+.gl       "GL  "  "AnVw"      GL Animation                   MacAnim Viewer
+.url      "AURL"  "Arch"      URL Bookmark                   Anarchie                  message/external-body
+.zoo      "Zoo "  "Booz"      Zoo Archive                    MacBooz
+.pdf      "PDF "  "CARO"      Portable Document Format       Acrobat Reader            application/pdf
+.h        "TEXT"  "CWIE"      C Include File                 CodeWarrior
+.hp       "TEXT"  "CWIE"      C Include File                 CodeWarrior
+.hpp      "TEXT"  "CWIE"      C Include File                 CodeWarrior
+.c        "TEXT"  "CWIE"      C Source                       CodeWarrior
+.cp       "TEXT"  "CWIE"      C++ Source                     CodeWarrior
+.cpp      "TEXT"  "CWIE"      C++ Source                     CodeWarrior
+.class    "Clss"  "CWIE"      Java Class File                CodeWarrior
+.java     "TEXT"  "CWIE"      Java Source File               CodeWarrior
+.p        "TEXT"  "CWIE"      Pascal Source                  CodeWarrior
+.pas      "TEXT"  "CWIE"      Pascal Source                  CodeWarrior
+.cvs      "drw2"  "DAD2"      Canvas Drawing                 Canvas
+.arj      "BINA"  "DArj"      ARJ Archive                    DeArj
+.evy      "EVYD"  "ENVY"      Envoy Document                 Envoy
+.fm       "FMPR"  "FMPR"      FileMaker Pro Database         FileMaker Pro
+.dbf      "COMP"  "FOX+"      DBase Document                 FoxBase+
+.mif      "TEXT"  "Fram"      FrameMaker MIF                 FrameMaker                application/x-framemaker
+.arr      "ARR "  "GKON"      Amber ARR image                GraphicConverter
+.iff      "ILBM"  "GKON"      Amiga IFF Image                GraphicConverter
+.lbm      "ILBM"  "GKON"      Amiga IFF Image                GraphicConverter
+.ilbm     "ILBM"  "GKON"      Amiga ILBM Image               GraphicConverter
+.ani      "ANIi"  "GKON"      Animated NeoChrome             GraphicConverter
+.pcs      "PICS"  "GKON"      Animated PICTs                 GraphicConverter
+.pc1      "Dega"  "GKON"      Atari Degas Image              GraphicConverter
+.pc2      "Dega"  "GKON"      Atari Degas Image              GraphicConverter
+.pc3      "Dega"  "GKON"      Atari Degas Image              GraphicConverter
+.pi1      "Dega"  "GKON"      Atari Degas Image              GraphicConverter
+.pi2      "Dega"  "GKON"      Atari Degas Image              GraphicConverter
+.pi3      "Dega"  "GKON"      Atari Degas Image              GraphicConverter
+.ic1      "IMAG"  "GKON"      Atari Image                    GraphicConverter
+.ic2      "IMAG"  "GKON"      Atari Image                    GraphicConverter
+.ic3      "IMAG"  "GKON"      Atari Image                    GraphicConverter
+.neo      "NeoC"  "GKON"      Atari NeoChrome                GraphicConverter
+.pac      "STAD"  "GKON"      Atari STAD Image               GraphicConverter
+.spc      "Spec"  "GKON"      Atari Spectrum 512             GraphicConverter
+.tny      "TINY"  "GKON"      Atari TINY Bitmap              GraphicConverter
+.pm       "PMpm"  "GKON"      Bitmap from xv                 GraphicConverter
+.scg      "RIX3"  "GKON"      ColoRIX                        GraphicConverter
+.sci      "RIX3"  "GKON"      ColoRIX                        GraphicConverter
+.scp      "RIX3"  "GKON"      ColoRIX                        GraphicConverter
+.scr      "RIX3"  "GKON"      ColoRIX                        GraphicConverter
+.scu      "RIX3"  "GKON"      ColoRIX                        GraphicConverter
+.cgm      "CGMm"  "GKON"      Computer Graphics Meta         GraphicConverter
+.vff      "VFFf"  "GKON"      DESR VFF Greyscale Image       GraphicConverter
+.cut      "Halo"  "GKON"      Dr Halo Image                  GraphicConverter
+.art      "ART "  "GKON"      First Publisher                GraphicConverter
+.fts      "FITS"  "GKON"      Flexible Image Transport       GraphicConverter
+.fit      "FITS"  "GKON"      Flexible Image Transport       GraphicConverter          image/x-fits
+.gem      "GEM-"  "GKON"      GEM Metafile                   GraphicConverter
+.img      "IMGg"  "GKON"      GEM bit image/XIMG             GraphicConverter
+.grp      "GRPp"  "GKON"      GRP Image                      GraphicConverter
+.hpgl     "HPGL"  "GKON"      HP GL/2                        GraphicConverter
+.plt      "HPGL"  "GKON"      HP GL/2                        GraphicConverter
+.ief      "IEF "  "GKON"      IEF image                      GraphicConverter          image/ief
+.scc      "MSX "  "GKON"      MSX pitcure                    GraphicConverter
+.msp      "MSPp"  "GKON"      Microsoft Paint                GraphicConverter
+.pcx      "PCXx"  "GKON"      PC PaintBrush                  GraphicConverter
+.pbm      "PPGM"  "GKON"      Portable Bitmap                GraphicConverter          image/x-portable-bitmap
+.pgm      "PPGM"  "GKON"      Portable Graymap               GraphicConverter          image/x-portable-graymap
+.ppm      "PPGM"  "GKON"      Portable Pixmap                GraphicConverter          image/x-portable-pixmap
+.shp      "SHPp"  "GKON"      Printmaster Icon Library       GraphicConverter
+.qdv      "QDVf"  "GKON"      QDV image                      GraphicConverter
+.rif      "RIFF"  "GKON"      RIFF Graphic                   GraphicConverter
+.rle      "RLE "  "GKON"      RLE image                      GraphicConverter
+.raw      "BINA"  "GKON"      Raw Image                      GraphicConverter
+.bw       "SGI "  "GKON"      SGI Image                      GraphicConverter
+.rgb      "SGI "  "GKON"      SGI Image                      GraphicConverter          image/x-rgb
+.rgba     "SGI "  "GKON"      SGI Image                      GraphicConverter          image/x-rgb
+.six      "SIXE"  "GKON"      SIXEL image                    GraphicConverter
+.ct       "..CT"  "GKON"      Scitex-CT                      GraphicConverter
+.dcx      "DCXx"  "GKON"      Some PCX Images                GraphicConverter
+.sup      "SCRN"  "GKON"      StartupScreen                  GraphicConverter
+.sr       "SUNn"  "GKON"      Sun Raster Image               GraphicConverter
+.sun      "SUNn"  "GKON"      Sun Raster Image               GraphicConverter
+.targa    "TPIC"  "GKON"      Truevision Image               GraphicConverter
+.tga      "TPIC"  "GKON"      Truevision Image               GraphicConverter
+.clp      "CLPp"  "GKON"      Windows Clipboard              GraphicConverter
+.icn      "ICO "  "GKON"      Windows Icon                   GraphicConverter
+.ico      "ICO "  "GKON"      Windows Icon                   GraphicConverter
+.wmf      "WMF "  "GKON"      Windows Metafile               GraphicConverter
+.wpg      "WPGf"  "GKON"      WordPerfect Graphic            GraphicConverter
+.xbm      "XBM "  "GKON"      X-Windows Bitmap               GraphicConverter          image/x-xbm
+.x10      "XWDd"  "GKON"      X-Windows Dump                 GraphicConverter          image/x-xwd
+.x11      "XWDd"  "GKON"      X-Windows Dump                 GraphicConverter          image/x-xwd
+.xwd      "XWDd"  "GKON"      X-Windows Dump                 GraphicConverter          image/x-xwd
+.xpm      "XPM "  "GKON"      X-Windows Pixmap               GraphicConverter          image/x-xpm
+.htm      "TEXT"  "MOSS"      HyperText                      Netscape Communicator     text/html
+.html     "TEXT"  "MOSS"      HyperText                      Netscape Communicator     text/html
+.m2v      "MPG2"  "MPG2"      MPEG-2 IPB videostream         MPEG2decoder
+.for      "TEXT"  "MPS "      Fortran Source                 MPW Shell
+.wri      "WDBN"  "MSWD"      MS Write/Windows               Microsoft Word
+.mcw      "WDBN"  "MSWD"      Mac Word Document              Microsoft Word
+.rtf      "TEXT"  "MSWD"      Rich Text Format               Microsoft Word            application/rtf
+.doc      "WDBN"  "MSWD"      Word Document                  Microsoft Word            application/msword
+.dot      "sDBN"  "MSWD"      Word for Windows Template      Microsoft Word
+.mw       "MW2D"  "MWII"      MacWrite Document              MacWrite II               application/macwriteii
+.mwii     "MW2D"  "MWII"      MacWrite Document              MacWrite II               application/macwriteii
+.pl       "TEXT"  "McPL"      Perl Source                    MacPerl
+.asf      "ASF_"  "Ms01"      Netshow Player                 Netshow Server            video/x-ms-asf
+.asx      "ASX_"  "Ms01"      Netshow Player                 Netshow Server            video/x-ms-asf
+.oda      "ODIF"  "ODA "      ODA Document                   MacODA XTND Translator    application/oda
+.latex    "TEXT"  "OTEX"      Latex                          OzTex                     application/x-latex
+.texi     "TEXT"  "OTEX"      TeX Document                   OzTeX
+.tex      "TEXT"  "OTEX"      TeX Document                   OzTeX                     application/x-tex
+.texinfo  "TEXT"  "OTEX"      TeX Document                   OzTeX                     application/x-texinfo
+.diz      "TEXT"  "R*Ch"      BBS Descriptive Text           BBEdit
+.ml       "TEXT"  "R*ch"      ML Source                      BBEdit
+.mak      "TEXT"  "R*ch"      Makefile                       BBEdit
+.m2       "TEXT"  "R*ch"      Modula 2 Source                BBEdit
+.i3       "TEXT"  "R*ch"      Modula 3 Interface             BBEdit
+.m3       "TEXT"  "R*ch"      Modula 3 Source                BBEdit
+.prn      "TEXT"  "R*ch"      Printer Output File            BBEdit
+.rtx      "TEXT"  "R*ch"      Rich Text                      BBEdit                    text/richtext
+.rpl      "FRL!"  "REP!"      Replica Document               Replica
+.rib      "TEXT"  "RINI"      Renderman 3D Data              Renderman
+.rsc      "rsrc"  "RSED"      Resource File                  ResEdit
+.rsrc     "rsrc"  "RSED"      Resource File                  ResEdit
+.pdb      "TEXT"  "RSML"      Brookhaven PDB file            RasMac
+.mol      "TEXT"  "RSML"      MDL Molfile                    RasMac
+.bar      "BARF"  "S691"      Unix BAR Archive               SunTar
+.aif      "AIFF"  "SCPL"      AIFF Sound                     SoundApp                  audio/x-aiff
+.aiff     "AIFF"  "SCPL"      AIFF Sound                     SoundApp                  audio/x-aiff
+.aifc     "AIFC"  "SCPL"      AIFF Sound Compressed          SoundApp                  audio/x-aiff
+.al       "ALAW"  "SCPL"      ALAW Sound                     SoundApp
+.8svx     "8SVX"  "SCPL"      Amiga 8-bit sound              SoundApp
+.svx      "8SVX"  "SCPL"      Amiga IFF Sound                SoundApp
+.med      "STrk"  "SCPL"      Amiga MED Sound                SoundApp
+.8med     "STrk"  "SCPL"      Amiga OctaMed music            SoundApp
+.mod      "STrk"  "SCPL"      MOD Music                      SoundApp
+.nst      "STrk"  "SCPL"      MOD Music                      SoundApp
+.okt      "OKTA"  "SCPL"      Oktalyser MOD Music            SoundApp
+.wve      "BINA"  "SCPL"      PSION sound                    SoundApp
+.snd      "BINA"  "SCPL"      Sound of various types         SoundApp
+.hcom     "FSSD"  "SCPL"      SoundEdit Sound ex SOX         SoundApp
+.voc      "VOC "  "SCPL"      VOC Sound                      SoundApp
+.sf       "IRCM"  "SDHK"      IRCAM Sound                    SoundHack
+.pkg      "HBSF"  "SITx"      AppleLink Package              StuffIt Expander
+.hqx      "TEXT"  "SITx"      BinHex                         StuffIt Expanderª         application/mac-binhex40
+.sithqx   "TEXT"  "SITx"      BinHexed StuffIt Archive       StuffIt Expander          application/mac-binhex40
+.cpt      "PACT"  "SITx"      Compact Pro Archive            StuffIt Expander
+.taz      "ZIVU"  "SITx"      Compressed Tape ARchive        StuffIt Expander          application/x-compress
+.gz       "SIT!"  "SITx"      Gnu ZIP Archive                StuffIt Expander          application/x-gzip
+.tgz      "Gzip"  "SITx"      Gnu ZIPed Tape ARchive         StuffIt Expander          application/x-gzip
+.lha      "LHA "  "SITx"      LHArc Archive                  StuffIt Expander
+.lzh      "LHA "  "SITx"      LHArc Archive                  StuffIt Expander
+.mime     "TEXT"  "SITx"      MIME Message                   StuffIt Expander          message/rfc822
+.bin      "SIT!"  "SITx"      MacBinary                      StuffIt Expander          application/macbinary
+.arc      "mArc"  "SITx"      PC ARChive                     StuffIt Expander
+.zip      "ZIP "  "SITx"      PC ZIP Archive                 StuffIt Expander          application/zip
+.pit      "PIT "  "SITx"      PackIt Archive                 StuffIt Expander
+.pf       "CSIT"  "SITx"      Private File                   StuffIt Expander 
+.sit      "SIT!"  "SITx"      StuffIt 1.5.1 Archive          StuffIt Expander          application/x-stuffit
+.uu       "TEXT"  "SITx"      UUEncode                       StuffIt Expander
+.uue      "TEXT"  "SITx"      UUEncode                       StuffIt Expander
+.Z        "ZIVU"  "SITx"      Unix Compress Archive          StuffIt Expander          application/x-compress
+.tar      "TARF"  "SITx"      Unix Tape ARchive              StuffIt Expander          application/x-tar
+.669      "6669"  "SNPL"      669 MOD Music                  PlayerPro
+.xm       "XM  "  "SNPL"      FastTracker MOD Music          PlayerPro
+.mtm      "MTM "  "SNPL"      MultiMOD Music                 PlayerPro
+.s3m      "S3M "  "SNPL"      ScreamTracker 3 MOD            PlayerPro
+.com      "PCFA"  "SWIN"      MS-DOS Executable              SoftWindows
+.exe      "PCFA"  "SWIN"      MS-DOS Executable              SoftWindows
+.obj      "PCFL"  "SWIN"      Object (DOS/Windows)           SoftWindows
+.ovl      "PCFL"  "SWIN"      Overlay (DOS/Windows)          SoftWindows
+.dll      "PCFL"  "SWIN"      Windows DLL                    SoftWindows
+.dxf      "TEXT"  "SWVL"      AutoCAD 3D Data                Swivel Pro
+.avi      "VfW "  "TVOD"      AVI Movie                      QuickTime Player          video/avi
+.fli      "FLI "  "TVOD"      FLI Animation                  QuickTime Player
+.flc      "FLI "  "TVOD"      FLIC Animation                 QuickTime Player
+.mid      "Midi"  "TVOD"      MIDI Music                     MoviePlayer
+.midi     "Midi"  "TVOD"      MIDI Music                     MoviePlayer
+.mpe      "MPEG"  "TVOD"      MPEG Movie of some sort        MoviePlayer               video/mpeg
+.mpeg     "MPEG"  "TVOD"      MPEG Movie of some sort        MoviePlayer               video/mpeg
+.mpg      "MPEG"  "TVOD"      MPEG Movie of some sort        MoviePlayer               video/mpeg
+.m1v      "M1V "  "TVOD"      MPEG-1 IPB videostream         MoviePlayer               video/mpeg
+.m1a      "MPEG"  "TVOD"      MPEG-1 audiostream             MoviePlayer               audio/x-mpeg
+.mp2      "MPEG"  "TVOD"      MPEG-1 audiostream             MoviePlayer               audio/x-mpeg
+.mpa      "MPEG"  "TVOD"      MPEG-1 audiostream             MoviePlayer               audio/x-mpeg
+.m1s      "MPEG"  "TVOD"      MPEG-1 systemstream            MoviePlayer
+.ul       "ULAW"  "TVOD"      Mu-Law Sound                   MoviePlayer               audio/basic
+.moov     "MooV"  "TVOD"      QuickTime Movie                MoviePlayer               video/quicktime
+.mov      "MooV"  "TVOD"      QuickTime Movie                MoviePlayer               video/quicktime
+.qt       "MooV"  "TVOD"      QuickTime Movie                MoviePlayer               video/quicktime
+.au       "ULAW"  "TVOD"      Sun Sound                      QuickTime Player          audio/basic
+.wav      "WAVE"  "TVOD"      Windows WAV Sound              MoviePlayer               audio/x-wav
+.sha      "TEXT"  "UnSh"      Unix Shell Archive             UnShar                    application/x-shar
+.shar     "TEXT"  "UnSh"      Unix Shell Archive             UnShar                    application/x-shar
+.wpm      "WPD1"  "WPC2"      WordPerfect Mac                WordPerfect
+.wp4      ".WP4"  "WPC2"      WordPerfect PC 4.2 Doc         WordPerfect
+.w51      ".WP5"  "WPC2"      WordPerfect PC 5.1 Doc         WordPerfect               application/wordperfect5.1
+.wp       ".WP5"  "WPC2"      WordPerfect PC 5.1 Doc         WordPerfect               application/wordperfect5.1
+.wp5      ".WP5"  "WPC2"      WordPerfect PC 5.x Doc         WordPerfect               application/wordperfect5.1
+.wp6      ".WP6"  "WPC2"      WordPerfect PC 6.x Doc         WordPerfect
+.csv      "TEXT"  "XCEL"      Comma Separated Vars           Excel
+.dif      "TEXT"  "XCEL"      Data Interchange Format        Excel
+.xlc      "XLC "  "XCEL"      Excel Chart                    Excel
+.xlm      "XLM "  "XCEL"      Excel Macro                    Excel
+.xl       "XLS "  "XCEL"      Excel Spreadsheet              Excel
+.xls      "XLS "  "XCEL"      Excel Spreadsheet              Excel
+.xlw      "XLW "  "XCEL"      Excel Workspace                Excel
+.wks      "XLBN"  "XCEL"      Lotus Spreadsheet r1.x         Excel
+.wk1      "XLBN"  "XCEL"      Lotus Spreadsheet r2.1         Excel
+.slk      "TEXT"  "XCEL"      SYLK Spreadsheet               Excel
+.syk      "TEXT"  "XCEL"      SYLK Spreadsheet               Excel
+.sylk     "TEXT"  "XCEL"      SYLK Spreadsheet               Excel
+.tsv      "TEXT"  "XCEL"      Tab Separated Values           Excel                     text/tab-separated-values
+.qxd      "XDOC"  "XPR3"      QuarkXpress Document           QuarkXpress
+.qxt      "XTMP"  "XPR3"      QuarkXpress Template           QuarkXpress
+.image    "dImg"  "ddsk"      Apple DiskCopy Image           Disk Copy
+.etx      "TEXT"  "ezVu"      SEText                         Easy View                 text/x-setext
+.out      "BINA"  "hDmp"      Output File                    HexEdit
+.binary   "BINA"  "hDmp"      Untyped Binary Data            HexEdit                   application/octet-stream
+.gif      "GIFf"  "ogle"      GIF Picture                    PictureViewer             image/gif
+.jfif     "JPEG"  "ogle"      JFIF Image                     PictureViewer
+.jpe      "JPEG"  "ogle"      JPEG Picture                   PictureViewer             image/jpeg
+.jpeg     "JPEG"  "ogle"      JPEG Picture                   PictureViewer             image/jpeg
+.jpg      "JPEG"  "ogle"      JPEG Picture                   PictureViewer             image/jpeg
+.pntg     "PNTG"  "ogle"      Macintosh Painting             PictureViewer
+.bga      "BMPp"  "ogle"      OS/2 Bitmap                    PictureViewer
+.vga      "BMPp"  "ogle"      OS/2 Bitmap                    PictureViewer
+.pict     "PICT"  "ogle"      PICT Picture                   PictureViewer             image/x-macpict
+.mac      "PICT"  "ogle"      PICT Picture                   PictureViewer             image/x-pict
+.pct      "PICT"  "ogle"      PICT Picture                   PictureViewer             image/x-pict
+.pic      "PICT"  "ogle"      PICT Picture                   PictureViewer             image/x-pict
+.png      "PNG "  "ogle"      Portable Network Graphic       PictureViewer
+.sgi      ".SGI"  "ogle"      SGI Image                      PictureViewer
+.tif      "TIFF"  "ogle"      TIFF Picture                   PictureViewer             image/tiff
+.tiff     "TIFF"  "ogle"      TIFF Picture                   PictureViewer             image/tiff
+.bmp      "BMPp"  "ogle"      Windows Bitmap                 PictureViewer
+.tx8      "TEXT"  "ttxt"      8-bit ASCII Text               SimpleText
+.asc      "TEXT"  "ttxt"      ASCII Text                     SimpleText                text/plain
+.ascii    "TEXT"  "ttxt"      ASCII Text                     SimpleText                text/plain
+.text     "TEXT"  "ttxt"      ASCII Text                     SimpleText                text/plain
+.txt      "TEXT"  "ttxt"      ASCII Text                     SimpleText                text/plain
+.faq      "TEXT"  "ttxt"      ASCII Text                     SimpleText                text/x-usenet-faq
+.a        "TEXT"  "ttxt"      Assembly Source                SimpleText
+.asm      "TEXT"  "ttxt"      Assembly Source                SimpleText
+.bas      "TEXT"  "ttxt"      BASIC Source                   SimpleText
+.boo      "TEXT"  "ttxt"      BOO encoded                    SimpleText
+.bib      "TEXT"  "ttxt"      BibTex Bibliography            SimpleText
+.bst      "TEXT"  "ttxt"      BibTex Style                   SimpleText
+.nfo      "TEXT"  "ttxt"      Info Text                      SimpleText                application/text
+.bat      "TEXT"  "ttxt"      MS-DOS Batch File              SimpleText
+.cmd      "TEXT"  "ttxt"      OS/2 Batch File                SimpleText
+.me       "TEXT"  "ttxt"      Text Readme                    SimpleText
+.rme      "TEXT"  "ttxt"      Text Readme                    SimpleText
+.1st      "TEXT"  "ttxt"      Text Readme                    SimpleText                application/text
+.readme   "TEXT"  "ttxt"      Text Readme                    SimpleText                application/text
+.ini      "TEXT"  "ttxt"      Windows INI File               SimpleText
+.ps       "TEXT"  "vgrd"      PostScript                     LaserWriter 8             application/postscript
+.eps      "EPSF"  "vgrd"      Postscript                     LaserWriter 8             application/postscript
+.epsf     "EPSF"  "vgrd"      Postscript                     LaserWriter 8             application/postscript
+.dvi      "ODVI"  "xdvi"      TeX DVI Document               xdvi                      application/x-dvi
diff --git a/config/afpd.conf b/config/afpd.conf
new file mode 100644 (file)
index 0000000..74594d6
--- /dev/null
@@ -0,0 +1,125 @@
+#
+# CONFIGURATION FOR AFPD
+#
+# Each line defines a virtual server that should be available.
+# Empty lines and lines beginning with `#' are ignored.
+# Options in this file will override both compiled-in defaults
+# and command line options.
+#
+# Format:
+#  - [options]               to specify options for the default server
+#  "Server name" [options]   to specify an additional server
+#
+# The following options are available:
+#   Transport Protocols:
+#     -[no]tcp       Make AFP-over-TCP [not] available
+#     -[no]ddp       Make AFP over AppleTalk [not] available. if you
+#                    have -proxy specified, specify -uamlist "" to 
+#                    prevent ddp connections from working.
+#
+#     -transall      Make both available (default)
+#
+#   Transport Options:
+#     -ipaddr <w.x.y.z>   Specifies the IP address the server should respond
+#                         to (default is the first IP address of the system)
+#                         This option also allows one machine to advertise
+#                         TCP/IP for another machine.
+#     -server_quantum <number> 
+#                         Specifies the DSI server quantum. The minimum
+#                         value is 1MB. The max value is 0xFFFFFFFF. If you 
+#                         specify a value that is out of range, you'll get 
+#                         the default value (currently the minimum).
+#     -ddpaddr x.y        Specifies the DDP address of the server. the 
+#                        default is to auto-assign an address
+#                         (0.0). this is only useful if you're running
+#                        on a multihomed host.
+#     -port <number>      Specifies the TCP port the server should respond
+#                         to (default is 548)
+#     -fqdn <name:port>   specify a fully-qualified domain name (+
+#                         optional port). this gets discarded if the
+#                        server can't resolve it. this is not honored
+#                         by appleshare clients <= 3.8.3 (default: none)
+#     -proxy              Run an AppleTalk proxy server for specified AFP/TCP
+#                         server (if address/port aren't given, then
+#                        first IP address of the system/548 will be used).
+#                         if you don't want the proxy server to act as
+#                         a ddp server as well, set -uamlist to an
+#                        empty string.
+#
+#
+#   Authentication Methods:
+#     -uampath <path>  Use this path to look for User Authentication Modules.
+#                     (default: :RESDIR:/uams)
+#     -uamlist <a,b,c> Comma-separated list of UAMs. (default:
+#                     uams_guest.so,uams_clrtxt.so,uams_dhx.so) 
+#
+#                     some commonly available UAMs:
+#                      uams_guest.so: Allow guest logins
+#
+#                     uams_clrtxt.so: (uams_pam.so or uams_passwd.so)
+#                                    Allow logins with passwords
+#                                    transmitted in the clear. 
+#
+#                     uams_randnum.so: Allow Random Number and Two-Way
+#                                     Random Number exchange for
+#                                     authentication.
+#
+#                     uams_dhx.so: (uams_dhx_pam.so or uams_dhx_passwd.so)
+#                                  Allow Diffie-Hellman eXchange
+#                                  (DHX) for authentication.
+#
+#   Password Options:
+#     -[no]savepassword   [Don't] Allow clients to save password locally
+#     -passwdfile <path>  Use this path to store Randnum
+#                        passwords. (default: ~/.passwd. the only other
+#                         userful value is :RESDIR:/etc/afppasswd.)
+#     -passwdminlen <#>   minimum password length. may be ignored.
+#     -[no]setpassword    [Don't] Allow clients to change their passwords.
+#     -loginmaxfail <#>   maximum number of failed logins. this may be
+#                        ignored if the uam can't handle it.
+#
+#   AppleVolumes files:
+#     -defaultvol <path>  Specifies path to AppleVolumes.default file
+#                         (default :ETCDIR:/AppleVolumes.default,
+#                         same as -f on command line)
+#     -systemvol <path>   Specifies path to AppleVolumes.system file
+#                         (default :ETCDIR:/AppleVolumes.system,
+#                         same as -s on command line)
+#     -[no]uservolfirst   [Don't] read the user's ~/AppleVolumes or
+#                         ~/.AppleVolumes before reading
+#                         :ETCDIR:/AppleVolumes.default
+#                         (same as -u on command line)
+#     -[no]uservol        [Don't] Read the user's volume file
+#
+#     -nlspath <path>     Prepend this path to each code page filename
+#                         in volume options (default: :RESDIR:/nls).
+#
+#   Miscellaneous:
+#     -guestname "user"   Specifies the user name for the guest login
+#                         (default "nobody", same as -g on command line)
+#     -loginmesg "Message"  Client will display "Message" upon logging in
+#                         (no default, same as -l "Message" on commandline)
+#     -nodebug            Switch off debugging
+#     -tickleval <number> Specify the tickle timeout interval (in seconds)
+#     -icon               Use the platform-specific icon.
+#              
+# Some examples:
+#
+#      The simplest case is to not have an afpd.conf.
+#
+#      4 servers w/ names server1-3 and one w/ the hostname. servers
+#       1-3 get routed to different ports with server 3 being bound 
+#       specifically to address 192.168.1.3
+#              -
+#              server1 -port 12000
+#              server2 -port 12001
+#              server3 -port 12002 -ipaddr 192.168.1.3
+#
+#      a dedicated guest server, a user server, and a special
+#      ddp-only server:
+#              "Guest Volume" -uamlist uams_guest.so -loginmesg "Welcome guest!"
+#              "User Volume" -uamlist uams_clrtxt.so -port 12000
+#              "special" -notcp -defaultvol <path> -systemvol <path>
+#
+# default:
+# - -transall -uamlist uams_guest.so,uams_clrtxt.so,uams_dhx.so -nosavepassword
diff --git a/config/atalkd.conf b/config/atalkd.conf
new file mode 100644 (file)
index 0000000..4648e3a
--- /dev/null
@@ -0,0 +1,37 @@
+#
+# Format of lines in this file:
+#
+#    interface [ -seed ] [ -router | -dontroute ] 
+#       [ -phase { 1 | 2 } ] [ -addr net.node ]
+#      [ -net first[-last] ] [ -zone ZoneName ] ...
+#
+# -seed only works if you have multi-interfaces.  Any missing arguments are
+# automatically configured from the network.  Note: lines can't actually be
+# split, tho it's a good idea.
+#
+# -router is like -seed but it allows single-interface routing. -dontroute 
+# disables routing for the specified interface.
+#
+# Some examples:
+#
+#      The simplest case is no atalkd.conf.  This works on most platforms
+#      (notably not Solaris), since atalkd can discover the local interfaces
+#      on the machine.
+#
+#      Very slightly more complicated:
+#
+#              le0
+#      or
+#              eth0
+#
+#      for Solaris/SunOS or Linux.
+#
+#      A much more complicated example:
+#
+#              le0 -phase 1
+#              le1 -seed -phase 2 -addr 66.6 -net 66-67 -zone "No Parking"
+#
+#      This turns on transition routing between the le0 and le1
+#      interfaces on a Sun.  It also causes atalkd to fail if other
+#      routers disagree about it's configuration of le1.
+#
diff --git a/config/atalkd.conf.cobalt b/config/atalkd.conf.cobalt
new file mode 100644 (file)
index 0000000..1363a71
--- /dev/null
@@ -0,0 +1,38 @@
+#
+# Format of lines in this file:
+#
+#    interface [ -seed ] [ -router | -dontroute ] 
+#       [ -phase { 1 | 2 } ] [ -addr net.node ]
+#      [ -net first[-last] ] [ -zone ZoneName ] ...
+#
+# -seed only works if you have multi-interfaces.  Any missing arguments are
+# automatically configured from the network.  Note: lines can't actually be
+# split, tho it's a good idea.
+#
+# -router is like -seed but it allows single-interface routing. -dontroute 
+# disables routing for the specified interface.
+#
+# Some examples:
+#
+#      The simplest case is no atalkd.conf.  This works on most platforms
+#      (notably not Solaris), since atalkd can discover the local interfaces
+#      on the machine.
+#
+#      Very slightly more complicated:
+#
+#              le0
+#      or
+#              eth0
+#
+#      for Solaris/SunOS or Linux.
+#
+#      A much more complicated example:
+#
+#              le0 -phase 1
+#              le1 -seed -phase 2 -addr 66.6 -net 66-67 -zone "No Parking"
+#
+#      This turns on transition routing between the le0 and le1
+#      interfaces on a Sun.  It also causes atalkd to fail if other
+#      routers disagree about it's configuration of le1.
+#
+eth0
diff --git a/config/netatalk.conf b/config/netatalk.conf
new file mode 100644 (file)
index 0000000..1e94cc6
--- /dev/null
@@ -0,0 +1,25 @@
+# Appletalk configuration
+# Change this to increase the maximum number of clients that can connect:
+AFPD_MAX_CLIENTS=20
+
+# Change this to set the machine's atalk name and zone.
+# NOTE: if you're zone has spaces in it, you're better off specifying
+#       it in afpd.conf
+#ATALK_ZONE=@zone
+ATALK_NAME=`echo ${HOSTNAME}|cut -d. -f1`
+
+# specify this if you don't want guest, clrtxt, and dhx
+# available options: uams_guest.so, uams_clrtxt.so, uams_dhx.so, 
+#                   uams_randnum.so
+#AFPD_UAMLIST="-U uams_clrtxt.so,uams_randnum.so"
+
+# Change this to set the id of the guest user
+AFPD_GUEST=nobody
+
+# Set which daemons to run (papd is dependent upon atalkd):
+ATALKD_RUN=yes
+PAPD_RUN=yes
+AFPD_RUN=yes
+
+# Control whether the daemons are started in the background
+ATALK_BGROUND=yes
diff --git a/config/netatalk.conf.cobalt b/config/netatalk.conf.cobalt
new file mode 100644 (file)
index 0000000..6c1f362
--- /dev/null
@@ -0,0 +1,23 @@
+# Appletalk configuration
+# Change this to increase the maximum number of clients that can connect:
+AFPD_MAX_CLIENTS=100
+
+# Change this to set the machine's atalk name:
+#ATALK_ZONE=@zone
+ATALK_NAME=`hostname|sed 's/\..*$//'`
+
+# specify this if you don't want guest, clrtxt, and dhx
+# available options: uams_guest.so, uams_clrtxt.so, uams_dhx.so, 
+#                   uams_randnum.so
+AFPD_UAMLIST="-U uams_clrtxt.so,uams_dhx.so"
+
+# Change this to set the id of the guest user
+AFPD_GUEST=nobody
+
+# Set which daemons to run:
+PAPD_RUN=no
+AFPD_RUN=yes
+
+# Control whether the daemons are started in the background
+ATALK_BGROUND=yes
+
diff --git a/config/netatalk.pamd b/config/netatalk.pamd
new file mode 100644 (file)
index 0000000..4577627
--- /dev/null
@@ -0,0 +1,6 @@
+#%PAM-1.0
+auth       required    /lib/security/pam_pwdb.so shadow
+account    required    /lib/security/pam_pwdb.so 
+#password   required   /lib/security/pam_cracklib.so
+#password   required   /lib/security/pam_pwdb.so shadow use_authtok
+session    required    /lib/security/pam_pwdb.so 
diff --git a/config/papd.conf b/config/papd.conf
new file mode 100644 (file)
index 0000000..09f453a
--- /dev/null
@@ -0,0 +1,20 @@
+# Attributes are:
+#
+#      Name Type Default       Description
+#      pd   str  ".ppd"        Pathname to ppd file.
+#      pr   str  "lp"          LPD printer name.
+#       pa   str  "0.0"         AppleTalk address (not usually needed).
+#      op   str  "operator"    Operator name, for LPD spooling.
+#
+# Some examples:
+#
+#      On many systems (notably not Solaris), no papd.conf is required,
+#      since papd shares the same defaults as lpd.
+#
+#      A simple example:
+#
+#              terminator:\
+#                      :pr=lp:op=wes:\
+#                      :pd=/usr/share/lib/ppd/HPLJ_4M.PPD:
+#
+#      Note also that papd.conf can list several printers.
diff --git a/contrib/ICDumpSuffixMap b/contrib/ICDumpSuffixMap
new file mode 100644 (file)
index 0000000..3a8283f
--- /dev/null
@@ -0,0 +1 @@
+#!perl\r#\r# ICDumpMap\r#     ---  Dump suffix mappings from your Internet Config extension.\r#\r# iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>\r#\r\ruse Mac::InternetConfig;\r\ropen MAP, ">AppleVolumes";\rprintf MAP "%-9s \"%4s\"  \"%4s\"      %-30s %-25s %-15s\n\n",\r".", "TEXT", "ttxt", "ASCII Text", "SimpleText", "text/plain";\rprint MAP "\# The following lines are extracted from Internet Config Preference.\n\n";\rfor my $entry (keys %InternetConfigMap) {\r   next unless $entry->extension =~ /^\./;\r   $_ = sprintf "%-9s \"%4s\"  \"%4s\"      %-30s %-25s %-15s",\r      $entry->extension, $entry->file_type, $entry->file_creator,\r      $entry->entry_name, $entry->creator_app_name,\r      $entry->MIME_type;\r   s/\s*$/\n/;\r   print MAP;\r}\rclose MAP;\r
\ No newline at end of file
diff --git a/contrib/printing/add_netatalk_printer b/contrib/printing/add_netatalk_printer
new file mode 100644 (file)
index 0000000..71436db
--- /dev/null
@@ -0,0 +1,364 @@
+#!/bin/sh
+#
+# A lovely script to add netatalk printers 
+#
+#ident  "@(#)netatalk   0.5     99/06/22 job@uchicago.edu"  /* Netatalk 1.4*/
+#
+# This File is released under the Perl Artistic Licence.
+# See http://www.perl.org for details
+#
+# Or you can use it under the licence that accompanies Netatalk 1.3 =)
+#
+# This file is maintained by Job Bogan <job@uchicago.edu>
+# Please contact me at this address for bug reports, problems, etc.
+# Please do not bother the netatalk  maintainers with problems from
+# this program.
+#
+#
+#
+# Exit Codes ...  1 for missed path or user input
+# 2 for an aborted run (by not saying yes to a 'are you sure?'
+# 3 for failed lpadmin commands
+# 4 for possible security problem
+
+# make base name for temp files.
+
+TMP_STUFF="/tmp/`basename $0`.$$"
+export TMP_STUFF
+rm -rf ${TMP_STUFF}
+mkdir -m 700 ${TMP_STUFF} || exit 4
+trap 'rm -f $TMP_STUFF > /dev/null 2>&1' 0
+trap 'exit' 1 2 3
+
+# grab the pathname the script was called w/
+# if it was run by ./add...  then make the path == `pwd`
+
+RUNHOME=`dirname $0`
+
+if [ "$RUNHOME" = "." ]; then
+       RUNHOME=`pwd`
+fi
+
+# allow for the env NETATALKHOME to override the guessed one from above
+
+NETATALKHOME=${NETATALKHOME:-$RUNHOME}
+export NETATALKHOME
+
+PATH=/bin:${PATH}:${NETATALKHOME}/bin:${NETATALKHOME}/etc:${NETATALKHOME}/etc/filters:/usr/lib:/usr/sbin
+
+if [ ! -x ${NETATALKHOME}/bin/pap ]; then
+       echo "OPPS:     I don't see ${NETATALKHOME}/bin/pap ,"
+       echo '  Check that it is executable or set ${NETATALKHOME}'
+       echo '  so i can find pap at ${NETATALKHOME}/bin/pap'
+       echo '[We are guessing the location of the binary from the'
+       exho 'path that was used to run this sctipt or $NETATALKHOME]'
+       exit 1
+fi
+
+# get the user to tell us where to look for things, and list things there.
+
+echo ''
+echo "Enter Appletalk zone [default: local]: \c"
+
+read ZONE
+
+echo ""
+echo "Looking for LaserWriters in Zone ${ZONE} ..."
+$NETATALKHOME/bin/nbplkup ":LaserWriter@${ZONE}"
+
+echo ""
+echo "Enter AppleTalk printer name: \c"
+
+read DEST
+
+if [ "$DEST" = "" ]; then
+       echo "OPPS: you need to tell me a printer name"
+       exit 1
+fi
+
+echo "checking nbplkup ${DEST}:LaserWriter@${ZONE}"
+echo ""
+TestDEST=`$NETATALKHOME/bin/nbplkup "${DEST}:LaserWriter@${ZONE}"`
+echo "${TestDEST}"
+echo ""
+
+if [ "${TestDEST}" = "" ]; then
+    echo "I don't see that printer on the network.  You may have entered an incorrect"
+    echo "printer name - if so, exit and restart."
+    echo "[You should only enter the printer name, not the :LaserWriter portion]"
+    sleep 3
+fi
+
+unset TestDEST
+
+NBPNAME="${DEST}@${ZONE}"
+
+echo ""
+echo ""
+
+# scrunch all of the whitespace and / out of the appletalk name and suggest
+# that as the unix name
+
+SUGGEST=`echo "$DEST" | cut -d: -f1 | sed 's/ //g' | sed 's#/##g` ; export SUGGEST
+
+# truncate the suggested name to 14 chars to conform to lp specs.
+
+SUGGEST1=`expr ${SUGGEST} : '\(..............\)'`
+SUGGEST=${SUGGEST1:-$SUGGEST}
+
+echo "Enter Unix printer name - [default: ${SUGGEST}] : \c"
+read UNIXPRINT
+echo ''
+if [ "${UNIXPRINT}" = "" ]; then
+       UNIXPRINT="${SUGGEST}"
+fi
+export UNIXPRINT
+
+#####
+# Here we check for legal names again. >14 chars
+#####
+
+UNIXPRINT1=`expr "${UNIXPRINT}" : '\(..............\)'`
+
+# if UNIXPRINT is shorter than 14chars, UNIXPRINT1 ends up null ("")
+# if UNIXPRINT was longer, then we get a 14char version of it.  bleah.
+
+if [ "${UNIXPRINT1}" = "" ]; then
+       UNIXPRINT1="${UNIXPRINT}"
+else
+#if [ "${UNIXPRINT1}" != "${UNIXPRINT}" ]; then
+    echo "Opps, that name was too long...  Truncating to 14 chars."
+    echo "setting printer name to '${UNIXPRINT}'"
+    UNIXPRINT="${UNIXPRINT1}"
+fi
+
+echo "Enter a Description for this printer.  [The Appletalk name will"
+echo "be included by default, and must be included for the printing"
+echo "filters to work.] : \c"
+read DESC
+
+DESC="${DESC} [${NBPNAME}]"
+
+echo ""
+echo "Do you want all printjobs to print out in reversed page order?"
+echo "This is usually only desired if this printer stacks jobs"
+echo "face up in the output bin. [N/y] \c"
+read REV
+case ${REV} in         [Yy]*)  REV="Netatalk-R" ;;
+                       *)      REV="Netatalk"   ;;
+esac
+export REV
+
+if lpstat -p ${UNIXPRINT} >/dev/null 2>&1 ; then
+       echo '\a'
+       echo "OPPS:     There already exists a printer named '${UNIXPRINT}'"
+       echo "          here are the comments from /etc/lp/printers/${UNIXPRINT}/comment"
+       cat /etc/lp/printers/${UNIXPRINT}/comment
+       echo ""
+       echo "Do you still want to do this? This will flush "
+       echo "all pending jobs!  [Y/n]? \c"
+       read DOTHIS
+       case ${DOTHIS} in       [Yy]*)         ;;
+                               *)      exit 2 ;;
+       esac
+
+       echo "Rejecting all new jobs for ${UNIXPRINT}"
+       reject -r "Making ${UNIXPRINT} a netatalk printer" ${UNIXPRINT}
+       echo "Disabling and Flushing the ${UNIXPRINT} queue"
+       disable -c -r "Making ${UNIXPRINT} a netatalk printer" ${UNIXPRINT}
+fi
+
+#####
+# Check if we need to do silly tricks to share the printer... only on 5.5.1 or older
+#####
+
+if [ "`uname -r`" -lt "5.6" ]; then
+    echo ""
+    echo "Do you want to share this printer w/ other machines [Y/n]?\c"
+    read SHARE
+else
+    echo "You're running SunOS 5.6 or higher... skipping 'sacadm' and 'pmadm'"
+fi
+
+if nistest printers.org_dir; then
+    echo "Do you want to add this printer to your NIS printer map? [y/N]"
+    echo "(You will need to share this printer w/ other machines)"
+    read NIS
+    case ${NIS} in     [Yy]*)    ADD_NIS=1 ;;
+                           *)              ;;
+    esac
+fi
+
+echo "Do you want to save the error messages from pap in"
+echo "'/var/spool/lp/tmp/Netatalk/${UNIXPRINT}' [y/N]"
+echo "(If you answer N, error messages will go to /dev/null.)"
+read SAVEerr
+case ${SAVEerr} in      [Yy]*)     SAVEerr=1 ;;
+                           *)     unset SAVEerr ;;
+esac
+
+echo "Making ${UNIXPRINT} print to ${DEST} via netatalk"
+
+if [ "${SAVEerr}" = "" ]; then
+    LOCKDEV=/dev/null
+else
+    LOCKS=/var/spool/lp/tmp/Netatalk
+    if [ ! -d ${LOCKS} ]; then
+           mkdir -m 0771       ${LOCKS}
+           chown lp    ${LOCKS}
+           chgrp lp    ${LOCKS}
+    fi
+    LOCKDEV=${LOCKS}/${UNIXPRINT}
+    touch              ${LOCKDEV}
+    chown lp:lp        ${LOCKDEV}
+    chmod 600  ${LOCKDEV}
+fi
+
+echo ""
+
+if [ ! -r /etc/lp/filter.table ]; then
+        echo "Setting up the existing print filters..."
+        for i in `ls /etc/lp/fd | sed 's/.fd$//'`; do
+                lpfilter -f ${i} -F/etc/lp/fd/${i}.fd
+        done
+fi
+if lpfilter -f netatalk -l > /dev/null 2>&1 ; then
+       echo 'Looks like you have a Netatalk printer filter defined. Good!'
+else
+       echo "Humm... You need a Netatalk printer filter..."
+       echo "... making you one ..."
+       cat > /etc/lp/fd/netatalk.fd << EOF
+#ident  "@(#)netatalk.fd 0.2 97/09/04 job@uchicago.edu"      /* Netatalk 1.4 */
+
+Input types: postscript
+Output types: PS
+Printer types: Netatalk
+Printers: any
+Filter type: fast
+Command: ${NETATALKHOME}/etc/filters/ifpap 2>&1 > /dev/null
+EOF
+       chown lp:lp /etc/lp/fd/netatalk.fd
+       chmod 664   /etc/lp/fd/netatalk.fd
+       lpfilter -f netatalk -F /etc/lp/fd/netatalk.fd
+fi
+
+if lpfilter -f netatalk-r -l 2>&1 > /dev/null ; then
+       echo 'Looks like you have a Reverse Netatalk printer filter defined. Good!'
+else
+       echo "Humm... You need a Reverse Netatalk printer filter..."
+       echo "... making you one ..."
+       cat > /etc/lp/fd/netatalk-r.fd << EOF
+#ident  "@(#)netatalk-r.fd 0.2 97/09/04 job@uchicago.edu"      /* Netatalk 1.4 */
+
+Input types: postscript
+Output types: PS
+Printer types: Netatalk-R
+Printers: any
+Filter type: fast
+Command: "/usr/lib/lp/postscript/postreverse | ${NETATALKHOME}/etc/filters/ifpap 2>&1 >/dev/null"
+EOF
+       chown lp:lp /etc/lp/fd/netatalk-r.fd
+       chmod 664   /etc/lp/fd/netatalk-r.fd
+       lpfilter -f netatalk-r -F /etc/lp/fd/netatalk-r.fd
+fi
+
+if [ ! -r /usr/share/lib/terminfo/N/Netatalk ]; then
+       echo "Making a Terminfo entry for Netatalk printers"
+       cat > ${TMP_STUFF}/terminfo << EOF
+Netatalk,
+       cols#80, lines#66,
+       cpi=null, csnm=^D, lpi=null, scs=^D, slines=^D, u9=^D,
+EOF
+       tic ${TMP_STUFF}/terminfo
+       chown bin:bin /usr/share/lib/terminfo/N/Netatalk
+       chmod 644     /usr/share/lib/terminfo/N/Netatalk
+fi
+
+if [ ! -r /usr/share/lib/terminfo/N/Netatalk-R ]; then
+       echo "Making a Terminfo entry for Reversed Netatalk printers"
+       cat > ${TMP_STUFF}/terminfoR << EOF
+Netatalk-R,
+       cols#80, lines#66,
+       cpi=null, csnm=^D, lpi=null, scs=^D, slines=^D, u9=^D,
+EOF
+       tic "${TMP_STUFF}/terminfoR"
+       chown bin:bin /usr/share/lib/terminfo/N/Netatalk-R
+       chmod 644     /usr/share/lib/terminfo/N/Netatalk-R
+fi
+
+###
+# this is old cruft...  we should not have done this in the 1st place.
+# we need to edit the template interface file to point to the local netatalk bins
+#cat ${RUNHOME}/etc/netatalk.template | sed "s#DEFAULT_NETATALK_HOME#${RUNHOME}#g" \
+#      > "/etc/lp/interfaces/${UNIXPRINT}"
+#chown lp:lp   /etc/lp/interfaces/${UNIXPRINT}
+#chmod 0775    /etc/lp/interfaces/${UNIXPRINT}
+# below is the correct way.
+###
+
+echo "Setting up ${UNIXPRINT} ...  Edit options later as you see fit."
+lpadmin -p ${UNIXPRINT} -D "${DESC}" -T ${REV} \
+    -i ${RUNHOME}/netatalk.template -I PS -v ${LOCKDEV} -A none || exit 3
+accept ${UNIXPRINT} || exit 3
+enable ${UNIXPRINT} || exit 3
+
+case ${SHARE} in       [Nn]*)  exit; ;;
+                       *)            ;;
+esac
+
+echo "Setting up network sharing for ${UNIXPRINT}"
+
+# from p925 in the Solaris Administration Guide, Vol. II (2.5)
+
+echo ""
+if sacadm -l -p tcp > /dev/null; then
+   echo "already running a tcp listener"
+else
+   echo "Defining tcp listener ..."
+   sacadm -a -p tcp -t listen -c "/usr/lib/saf/listen tcp" \
+          -v `nlsadmin -V` -n 999
+fi
+
+# this is extra, but a good idea...
+
+LPD_ADDR="\\x`lpsystem -A`" ; export LPD_ADDR
+ADDR_0=`echo $LPD_ADDR | sed -e 's/\\x00020203/\\x00020ACE/g'`; export ADDR_0
+
+if pmadm -l -p tcp -s 0 > /dev/null; then
+   echo "<0> service is already defined."
+else
+   echo "Defining <0>/tcp service ..."
+   pmadm -a -p tcp -s 0 -i root \
+         -m `nlsadmin -c /usr/lib/saf/nlps_server -A ${ADDR_0}` \
+         -v `nlsadmin -V`
+fi
+
+# again from the solaris book noted above.
+
+if pmadm -l -p tcp -s lp > /dev/null; then
+   echo "<lp> service is already defined."
+else
+   echo "Defining <lp>/tcp service ..."
+   pmadm -a -p tcp -s lp -i root \
+         -m `nlsadmin -o /var/spool/lp/fifos/listenS5` \
+         -v `nlsadmin -V`
+fi
+
+if pmadm -l -p tcp -s lpd > /dev/null; then
+   echo "<lpd> service is already defined."
+else
+   echo "Defining <lpd>/tcp service ..."
+   pmadm -a -p tcp -s lpd -i root \
+         -m `nlsadmin -o /var/spool/lp/fifos/listenBSD -A ${LPD_ADDR}` \
+         -v `nlsadmin -V`
+fi
+
+if [ "${ADD_NIS}" = 1 ] ; then
+    if nistest "[printer_name=${UNIXPRINT}]printers.org_dir"; then
+       nistbladm -m    printer_host=`uname -n` \
+       "[printer_name=${UNIXPRINT}]printers.org_dir"  description="$DESC"
+    else
+       nistbladm -a    printer_name=${UNIXPRINT} \
+       description="$DESC" printer_host=`uname -n` printers.org_dir
+    fi
+fi
diff --git a/contrib/printing/netatalk.template b/contrib/printing/netatalk.template
new file mode 100644 (file)
index 0000000..f639914
--- /dev/null
@@ -0,0 +1,610 @@
+#ident "@(#)netatalk   0.7     99/06/22 job@uchicago.edu"  /* Netatalk 1.4*/
+
+#####
+# User configuration:
+#
+# Set timeout for pap($time) and papstatus($time2).  both are in seconds.
+# extraneous if you do not have NETATALKHOME/bin/timeout
+# set how many times to loop before we just abort entirely ($attempts)
+# what flags pap is run w/. -c makes pap claim to have been waiting forever
+#####
+
+time=1800
+time2=60
+attempts=3
+pap_flags="-c"
+
+#####
+# this should get "fixed" to something like
+# NETATALKHOME=/opt ; export NETATALKHOME
+# by the add_netatalk_printer script
+#
+# DO NOT use the user's env for this or the PATH above.
+#####
+
+NETATALKHOME=DEFAULT_NETATALK_HOME ; export NETATALKHOME
+#NETATALKHOME=/opt ; export NETATALKHOME
+
+if [ "${NETATALKHOME}" = "DEFAULT_NETATALK_HOME" ]; then
+   echo "bleah, NETATALKHOME not set, exiting..." ; exit 5
+fi
+
+#####
+# BUGS/TODO:
+# move all TMP stuff to use a directory for security reasons
+# run nbplkup to check if the reason we cannot print, is that it's not on 
+#      the net
+# move to "${foo}" from $foo and ${foo}
+# add debuging info that gets sent to "logger lpd.debug"
+# if we timeout while printing the banner page, we do not keep trying to print.
+# perhaps filter_timeout should only complain once per job; like badfile.
+# check if the timeout messages are duplicated by the lp system
+# psa will not drop in for pap to use accting.  perhaps lp does not need it?
+# make pap print all the files at once; kill for file in ($files)
+# move badfile error to printfile function
+#####
+
+# This File is released under the Perl Artistic Licence.  
+# See http://www.perl.org for details
+#
+# Or you can use it under the licence that accompanies Netatalk 1.3 =)
+
+###########
+## Netatalk printer interface.  Heavily modified from 
+## /usr/lib/lp/model/standard on Sparc Solaris 2.5.1 (May 97)
+##
+## Meant to be used w/ add_netatalk_printer
+###########
+
+#####
+# This program is invoked as
+#
+# ${SPOOLDIR}/.../printer request-id user title copies options files...
+#
+# The first three arguments are simply reprinted on the banner page,
+# the fourth (copies) is used to control the number of copies to print,
+# the fifth (options) is a blank separated list (in a single argument)
+# of user or Spooler supplied options (without the -o prefix),
+# and the last argument(s) is/are the file(s) to print.
+#####
+
+#####
+#
+# The protocol between the interface program and the Spooler
+# is fairly simple:
+#
+#      All standard error output is assumed to indicate a
+#      fault WITH THE REQUEST. The output is mailed to the
+#      user who submitted the print request and the print
+#      request is finished.
+#
+#      If the interface program sets a zero exit code,
+#      it is assumed that the file printed correctly.
+#      If the interface program sets a non-zero exit code
+#      less than 128, it is assumed that the file did not
+#      print correctly, and the user will be notified.
+#      In either case the print request is finished.
+#
+#      If the interface program sets an exit code greater
+#      than 128, it is assumed that the file did not print
+#      because of a printer fault. If an alert isn't already
+#      active (see below) one will be activated. (Exit code
+#      128 should not be used at all. The shell, which executes
+#      this program, turns SIGTERM, used to kill this program
+#      for a cancellation or disabling, into exit 128. The
+#      Spooler thus interpretes 128 as SIGTERM.)
+#
+#      A message sent to the standard input of the ${LPTELL}
+#      program is assumed to describe a fault WITH THE PRINTER.
+#      The output is used in an alert (if alerts are defined).
+#      If the fault recovery is "wait" or "begin", the printer
+#      is disabled (killing the interface program if need be),
+#      and the print request is left on the queue.
+#      If the fault recovery is "continue", the interface program
+#      is allowed to wait for the printer fault to be cleared so
+#      it can resume printing.
+#
+#####
+
+###########################################################################
+#
+# Set up the basic traps. and other important things
+#
+###########################################################################
+
+#####
+# For the time being, just exit if we are poked.
+#####
+
+# SIGTERM handler
+
+trap 'exit' 15
+
+#####
+# We can be clever about getting a hangup or interrupt, though, at least
+# until the filter runs. Do this early, even though $LPTELL
+# isn't defined, so that we're covered.
+#####
+
+trap 'catch_hangup; exit_code=129 exit 129' 1
+trap 'catch_interrupt; exit_code=129 exit 129' 2 3
+
+#####
+# VARIBLE DECLARED - put here so we don't ever run the trap below w/o
+# TMPPREFIX defined.  We hard code /tmp for the moment, but fix that later
+#
+# Use ${TMPPREFIX} as the prefix for all temporary files, so
+# that cleanup is easy. The prefix may be up to 13 characters
+# long, so you only have space for one more character to make
+# a file name. If necessary, make a directory using this prefix
+# for better management of unique temporary file names.
+#####
+
+TMPPREFIX=/tmp/`uname -n`$$
+
+#####
+# Before exiting, set ${exit_code} to the value with which to exit.
+# Otherwise, the exit from this script will be 0.
+#####
+
+trap 'rm -fr ${TMPPREFIX}*; exit ${exit_code}' 0
+
+catch_hangup () {
+       if [ -n "${LPTELL}" ]
+       then
+               echo \
+    "Humm, we got a SIGHUP.  Not sure what it means, but... we'll keep going anyway" \
+          | ${LPTELL} "${printer}"
+       fi
+       return 0
+}
+
+catch_interrupt () {
+       if [ -n "${LPTELL}" ]
+       then
+           echo \
+       "Received an interrupt from the printer.  The reason is unknown." \
+           | ${LPTELL} "${printer}"
+       fi
+       return 0
+}
+
+#####
+# Most of the time we don't want the standard error to be captured
+# by the Spooler, mainly to avoid "Terminated" messages that the
+# shell puts out when we get a SIGTERM. We'll save the standard
+# error channel under another number, so we can use it when it
+# should be captured.
+#
+# Open another channel to the printer port, for use when the
+# regular standard output won't be directed there, such as in
+# command substitution (`cmd`).
+#####
+
+exec 5>&2 2>/dev/null 3>&1
+
+###########################################################################
+#
+# Define local varibles and such
+#
+###########################################################################
+
+#####
+# There is one more varible set by the shell that execs us.
+# FILTER       The filter to run ; we ignore this directive
+#####
+
+#####
+# Use the user set env, or else default to standard values.
+#####
+
+: ${SPOOLDIR:=/usr/spool/lp}
+: ${TMPDIR:=/tmp} ; export TMPDIR
+: ${LOCALPATH:=${SPOOLDIR}/bin} ; export LOCALPATH
+
+PATH="/bin:/usr/bin:${LOCALPATH}:${NETATALKHOME}/bin:${NETATALKHOME}/etc"
+export PATH
+
+TMPPREFIX=${TMPDIR}/`uname -n`$$
+
+#####
+# Error levels for the errmsg() func.
+#####
+
+LP_ERR_LABEL="UX:lp" ; export LP_ERR_LABEL
+
+E_IP_ARGS=1
+E_IP_OPTS=2
+E_IP_UNKNOWN=5
+E_IP_BADFILE=6
+
+#####
+# Error message formatter:
+#
+# Invoke as
+#
+#      errmsg severity message-number problem help
+#
+# where severity is "ERROR" or "WARNING", message-number is
+# a unique identifier, problem is a short description of the
+# problem, and help is a short suggestion for fixing the problem.
+#####
+
+errmsg () {
+        case $1 in
+        ERROR )
+                sev="  ERROR";
+                ;;
+        WARNING )
+                sev="WARNING";
+                ;;
+        esac
+        echo "${LP_ERR_LABEL}: ${sev}: $3
+        TO FIX: $4" >&5
+}
+
+parse () {
+        echo "`expr \"$1\" : \"^[^=]*=\(.*\)\"`"
+}
+
+#####
+# die quickly if we do not have the right number of arguments.
+#####
+
+if [ $# -lt 5 ]
+then
+       errmsg ERROR ${E_IP_ARGS} \
+               "wrong number of arguments to interface program" \
+               "consult your system administrator"
+       exit 1
+fi
+
+printer=`basename $0`
+request_id=$1
+
+# this will formated be machine!username, so we want to split that up...
+
+user_name=$2
+machine=`echo $user_name | cut -d! -f1`
+user_name=`echo $user_name | cut -d! -f2`
+
+title=$3
+copies=$4
+option_list=$5
+
+shift 5
+files="$*"
+
+nobanner="yes"
+
+inlist=
+
+for i in ${option_list}
+do
+case "${inlist}${i}" in
+    nobanner )
+       nobanner="yes" ;;
+    banner )
+       nobanner="no" ;;
+#####
+#
+# If you want to add simple options (e.g. -o simple)
+# identify them here.
+#####
+#    simple )
+#      simple="yes" ;;
+
+#####
+# These get ignored, but would matter little anyway since all we see 
+# here is PS anyway.
+#####
+       cpi=* )
+#          cpi=`parse ${i}` ;;
+           true ;;
+       lpi=* )
+#          lpi=`parse ${i}` ;;
+           true ;;
+       length=* )
+#          length=`parse ${i}` ;;
+           true ;;
+       width=* )
+#          width=`parse ${i}` ;;
+           true ;;
+
+       #####
+       # If you want to add simple-value options (e.g. -o value=a)
+       # identify them here.
+       #####
+       #value=* )
+       #    value=`parse ${i}` ;;
+
+       flist=* )
+           flist=`parse ${i}` ;;
+       input* )
+           true ;;
+       * )
+           errmsg WARNING ${E_IP_OPTS} \
+           "unrecognized \"-o ${i}\" option" \
+           "check the option, resubmit if necessary
+           printing continues" ;;
+
+       esac
+done
+
+#####
+# A bit ugly, but grabs the appletalk printer name from the lp system printer
+# description, so it's right up there in admintool.  the appletalk name must 
+# be delimited by [ and ].
+#
+# eg - 'this is the printer [hp-mrsec-l114:lasershared@Research Insitutes] that i use.'
+#####
+
+PAPDEST="`lpstat -D -p "${printer}" | grep -i descrip | sed 's/.*Description:.*\[//g' | sed 's/\].*//g'`"
+
+export PAPDEST
+
+###########################################################################
+#
+# Define our local functions  (parse is declared above option parsing)
+#
+###########################################################################
+
+banner () {
+    echo "#####    User: ${user_name}"
+    echo ""
+    echo "##### Machine: ${user_name}"
+    echo ""
+
+   if [ -n "${title}" ]
+   then
+    echo "#####   Title: ${title}"
+    echo ""
+   fi
+
+    echo "#####   Files: ${flist}"
+    echo ""
+
+#####
+# this should deal w/ the year 2000 ok.  But will die in 2038. =)
+#####
+
+    YEAR=`date '+%y'`
+    YEAR=`expr 1900 + ${YEAR}`
+
+    echo "#####    Date: `date '+%a %H:%M %h %d,'` ${YEAR}"
+    echo ""
+    echo "#####     Job: ${request_id}"
+    echo ""
+
+}
+
+print_banner() {
+    if [ -x ${NETATALKHOME}/bin/timeout ]
+    then
+       banner | ${NETATALKHOME}/etc/psf \
+           | ${NETATALKHOME}/bin/timeout "${time}" ${NETATALKHOME}/bin/pap -c -p "${PAPDEST}"
+    else
+       banner | ${NETATALKHOME}/etc/psf \
+           | ${NETATALKHOME}/bin/pap -c -p "{PAPDEST}"
+    fi
+
+    if [ ${?} -ne 0 ]
+    then
+       filter_timeout
+    fi 
+}
+
+#####
+# ${LPTELL} is the name of a program that will send its
+# standard input to the Spooler. It is used to forward
+# the description of a printer fault to the Spooler,
+# which uses it in an alert to the administrator.
+#####
+if [ ! -x "${LPTELL:=${LOCALPATH}/lp.tell}" ]
+then
+    fake_lptell(){
+       header="no"
+       while read line
+       do
+           if [ "no" = "${header}" ]
+           then
+               errmsg ERROR ${E_IP_UNKNOWN} \
+               "unknown printer/interface failure" \
+               "consult your system administrator; \
+               reasons for failure (if any) follow:"
+               header=yes
+           fi
+           echo "${line}" >&2
+       done
+    return 1
+    }
+    LPTELL=fake_lptell
+fi
+
+
+#####
+# timeout catcher for the printing filter
+#####
+
+filter_timeout() {
+
+       cat > ${TMPPREFIX}D <<EOF
+
+The printer ${printer} either timed out at ${time} seconds or pap exited
+abnormally.  As well, we may have exceeded ${print_tries} print attempts.
+The job ${request_id} from ${user_name} on ${machine} was
+printing when this happened.
+
+It may be that the only problem is the size of the job and the speed
+of the printer.
+
+Here is what $NETATALKHOME/bin/papstatus reports as the current 
+state of the printer:
+
+EOF
+#####
+# We don't need to test for timeout, since we cannot get here w/o it.
+#####
+
+    ${NETATALKHOME}/bin/timeout ${time2} ${NETATALKHOME}/bin/papstatus -p "${PAPDEST}" 2>&1 >> ${TMPPREFIX}D 
+    paperr=${?}
+
+    errmsg WARNING ${E_IP_UNKNOWN} "`cat ${TMPPREFIX}D`" "printing continues"
+#####
+# This ought to deal w/ the problem of nonexistent appletalk names, but...
+# for the moment, it calls filter_death.  But it sends the  papstatus 
+# info to LPTELL anyhow, so you should be able to see the error.
+#####
+
+    if [ ${paperr} -ne 0 -o ${too_many} = "1" ]; then
+       paperr=
+       filter_death
+    fi
+    paperr=
+    echo "serverdict begin 0 exitserver systemdict /quit get exec" | \
+                ${NETATALKHOME}/bin/pap -c -p "${PAPDEST}"
+    return 0
+}
+
+#####
+# Death catcher for filter_timeout
+#####
+
+filter_death() {
+
+    cat > ${TMPPREFIX}Z <<EOF
+Excessive delays w/ the printer ${printer}!
+
+While processing on printer ${printer} the job ${request_id} 
+from ${user_name} on ${machine} timed out at ${time} seconds.  
+
+Then while cleaning that timeout, the cleanup operation failed as
+well.
+
+EOF
+    errmsg ERROR ${E_IP_UNKNOWN} "`cat ${TMPPREFIX}Z`"
+    exit 129
+#####
+# Exit and fault the printer.
+#####
+}
+
+#####
+# Print the job
+#####
+printfile() {
+    trap '' 1  # Let the filter handle a hangup
+    trap '' 2 3        # and interrupts
+#####
+# We use timeout so as to not hang the print queue indefinately.  (pap does not
+# timeout on it's own.)
+#
+# Put the 0<${files} before the "eval" to keep clever users from giving 
+# a file name that evaluates as something to execute.
+#####
+    if [ "${TERM}" == "Netatalk-R" ]; then
+       if [ -x ${NETATALKHOME}/bin/timeout ]; then
+           0<${file} /usr/lib/lp/postscript/postreverse | ${NETATALKHOME}/bin/timeout ${time} ${NETATALKHOME}/bin/pap -c -p "${PAPDEST}"
+       else
+           0<${file} /usr/lib/lp/postscript/postreverse | ${NETATALKHOME}/bin/pap -c -p "${PAPDEST}"
+       fi
+    else
+       if [ -x ${NETATALKHOME}/bin/timeout ]; then
+           0<${file} ${NETATALKHOME}/bin/timeout ${time} ${NETATALKHOME}/bin/pap -c -p "${PAPDEST}"
+       else
+           0<${file} ${NETATALKHOME}/bin/pap -c -p "${PAPDEST}"
+       fi
+    fi
+    paperr=${?}
+    print_tries=`expr "${print_tries}" + 1`
+    if [ "${paperr}" != "0" -a "${print_tries}" -gt "${attempts}" ]; then
+       too_many=1
+    fi
+    trap 'catch_hangup; exit_code=129 exit 129' 1
+    trap 'catch_interrupt; exit_code=129 exit 129' 2 3
+    return ${paperr}
+}
+
+#####
+# Some basic sanity checking:
+#####
+
+if [ ! -x $NETATALKHOME/bin/pap ]
+then
+    echo "Opps, cannot find $NETATALKHOME/bin/pap, so I don't know how to"
+    echo "print things"
+    # exit w/ less than 128 to mark an error w/ the job, and call it done
+    exit 1 
+fi
+
+###########################################################################
+#
+# Start the main section of the program.
+#
+###########################################################################
+
+#####
+# Here i should have a "job canceled" page ready in a trap in case of getting killed
+# but, alas, that would most likely muck up the PS.  So we just drop the job on the 
+# floor.
+#####
+
+#####
+# If you want a custom banner, change the code up in the functions section.
+#####
+
+if [ "no" = "${nobanner}" -a "${TERM}" != "Netatalk-R" ]
+then
+    print_banner
+fi
+
+#####
+# Print some copies of the file(s)
+#####
+
+badfileyet=
+i=1
+while [ $i -le $copies ]
+do
+    for file in ${files}
+    do
+       if [ -r "${file}" ]
+        then
+            print_tries=0
+           until printfile
+           do
+               filter_timeout;
+           done
+       else
+#####
+# Don't complain about not being able to read a file on second and
+# subsequent copies, unless we've not complained yet. This removes
+# repeated messages about the same file yet reduces the chance that the
+# user can remove a file and not know that we had trouble finding it.
+#####
+           if [ "${i}" -le 1 -o -z "${badfileyet}" ]
+           then
+               errmsg WARNING ${E_IP_BADFILE} \
+                   "cannot read file \"${file}\" " \
+                   "see if the file still exists and is readable by the user\
+                   lp (or world), or consult your system administrator; \
+                   We will keep trying to print the other files or copies"
+               badfileyet=yes
+           fi
+       fi
+    done
+    i=`expr $i + 1`
+done
+
+#####
+# print the banner page if we are a reversed queue
+#####
+if [ "no" = "${nobanner}" -a "${TERM}" == "Netatalk-R" ]
+then
+    print_banner
+fi
+
+echo "serverdict begin 0 exitserver systemdict /quit get exec" | \
+                ${NETATALKHOME}/bin/pap -c -p "${PAPDEST}"
+
+Exit_code=0 exit 0
diff --git a/contrib/printing/timeout.c b/contrib/printing/timeout.c
new file mode 100644 (file)
index 0000000..6e79f34
--- /dev/null
@@ -0,0 +1,234 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <ctype.h>
+#include <unistd.h>
+
+char **av;
+
+struct slist {
+       char *name;
+       int num;
+} sigs[] = {
+#ifdef SIGHUP
+       "HUP",          SIGHUP,
+#endif
+#ifdef SIGINT
+       "INT",          SIGINT,
+#endif
+#ifdef SIGQUIT
+       "QUIT",         SIGQUIT,
+#endif
+#ifdef SIGILL
+       "ILL",          SIGILL,
+#endif
+#ifdef SIGTRAP
+       "TRAP",         SIGTRAP,
+#endif
+#ifdef SIGABRT
+       "ABRT",         SIGABRT,
+#endif
+#ifdef SIGIOT
+       "IOT",          SIGIOT,
+#endif
+#ifdef SIGEMT
+       "EMT",          SIGEMT,
+#endif
+#ifdef SIGFPE
+       "FPE",          SIGFPE,
+#endif
+#ifdef SIGKILL
+       "KILL",         SIGKILL,
+#endif
+#ifdef SIGBUS
+       "BUS",          SIGBUS,
+#endif
+#ifdef SIGSEGV
+       "SEGV",         SIGSEGV,
+#endif
+#ifdef SIGSYS
+       "SYS",          SIGSYS,
+#endif
+#ifdef SIGPIPE
+       "PIPE",         SIGPIPE,
+#endif
+#ifdef SIGALRM
+       "ALRM",         SIGALRM,
+#endif
+#ifdef SIGTERM
+       "TERM",         SIGTERM,
+#endif
+#ifdef SIGURG
+       "URG",          SIGURG,
+#endif
+#ifdef SIGSTOP
+       "STOP",         SIGSTOP,
+#endif
+#ifdef SIGTSTP
+       "TSTP",         SIGTSTP,
+#endif
+#ifdef SIGCONT
+       "CONT",         SIGCONT,
+#endif
+#ifdef SIGCHLD
+       "CHLD",         SIGCHLD,
+#endif
+#ifdef SIGCLD
+       "CLD",          SIGCLD,
+#endif
+#ifdef SIGTTIN
+       "TTIN",         SIGTTIN,
+#endif
+#ifdef SIGTTOU
+       "TTOU",         SIGTTOU,
+#endif
+#ifdef SIGIO
+       "IO",           SIGIO,
+#endif
+#ifdef SIGXCPU
+       "XCPU",         SIGXCPU,
+#endif
+#ifdef SIGXFSZ
+       "XFSZ",         SIGXFSZ,
+#endif
+#ifdef SIGVTALRM
+       "VTALRM",       SIGVTALRM,
+#endif
+#ifdef SIGPROF
+       "PROF",         SIGPROF,
+#endif
+#ifdef SIGWINCH
+       "WINCH",        SIGWINCH,
+#endif
+#ifdef SIGINFO
+       "INFO",         SIGINFO,
+#endif
+#ifdef SIGUSR1
+       "USR1",         SIGUSR1,
+#endif
+#ifdef SIGUSR2
+       "USR2",         SIGUSR2,
+#endif
+#ifdef SIGPWR
+       "PWR",          SIGPWR,
+#endif
+       0,              0
+};
+
+void
+usage()
+{
+       int i;
+
+       fprintf (stderr, "Usage: %s [-s signal] seconds program [args]\n\n",
+                *av);
+       fprintf (stderr, "You can use a numerical signal, or one of these:\n");
+
+#define COLS 8
+
+       for (i = 0; sigs[i].name; i++) {
+               if (i % COLS == 0)
+                       fprintf (stderr, "\n\t");
+
+               fprintf (stderr, "%s", sigs[i].name);
+
+               if ((i + 1) % COLS != 0)
+                       fprintf (stderr, "\t");
+       }
+
+       fprintf (stderr, "\n\n");
+}
+
+int
+main (argc, argv)
+       int argc;
+       char **argv;
+{
+       int i;
+       int whatsig = SIGTERM;
+       extern char *optarg;
+       extern int optind;
+       int pid;
+
+       av = argv;
+
+       while ((i = getopt (argc, argv, "s:")) != -1) {
+               switch (i) {
+               case 's':
+                       if (isdigit (*optarg)) {
+                               whatsig = atoi (optarg);
+                       } else {
+                               for (i = 0; sigs[i].name; i++) {
+                                       if (!strcmp (sigs[i].name, optarg)) {
+                                               whatsig = sigs[i].num;
+                                               break;
+                                       }
+                               }
+
+                               if (!sigs[i].name) {
+                                       fprintf (stderr, 
+                                                "%s: Unknown signal %s\n",
+                                                *av, optarg);
+                                       usage();
+                                       exit (1);
+                               }
+                       }
+
+                       break;
+
+               default:
+                       usage();
+                       exit (1);
+               }
+       }
+
+       if (optind > argc - 2) {
+               usage();
+               exit (1);
+       }
+
+       if (!isdigit (*argv[optind])) {
+               fprintf (stderr, "%s: \"seconds\" must be numeric, not %s\n",
+                        *av, argv[optind]);
+               usage();
+               exit (1);
+       }
+
+       pid = fork();
+
+       if (pid < 0) {
+               fprintf (stderr, "%s: fork failed: ", *av);
+               perror ("fork");
+               exit (1);
+       } else if (pid == 0) {
+               int seconds = atoi (argv[optind]);
+
+               pid = getppid();
+
+               fclose (stdin);
+               fclose (stdout);
+               fclose (stderr);
+
+               while (seconds-- > 0) {
+                       /*
+                        * too bad there's no SIGPARENT so we have to keep
+                        * polling to find out if it's still there
+                        */
+
+                       if (kill (pid, 0) != 0)
+                               exit (0);
+
+                       sleep (1);
+               }
+
+               kill (pid, whatsig);
+               exit (0);
+       } else {
+               execvp (argv[optind + 1], argv + optind + 1);
+
+               fprintf (stderr, "%s: can't execute ", *av);
+               perror (argv[optind + 1]);
+               exit (1);
+       }
+}
diff --git a/distrib/initscripts/rc.atalk.bsd b/distrib/initscripts/rc.atalk.bsd
new file mode 100755 (executable)
index 0000000..de1bdc3
--- /dev/null
@@ -0,0 +1,39 @@
+#
+# AppleTalk daemons. Make sure not to start atalkd in the background:
+# its data structures must have time to stablize before running the
+# other processes.
+#
+
+#
+# SUNOS: UNCOMMENT THESE LINES TO LOAD THE KERNEL MODULE.  Note that
+# modunload-ing netatalk may cause your machine to panic or hang.
+#
+##echo -n 'loading netatalk: '
+##if [ -f :ETCDIR:/netatalk.o ]; then
+##     /usr/etc/modload -sym :ETCDIR:/netatalk.o;
+##fi
+
+echo -n 'starting appletalk daemons:'
+if [ -x :SBINDIR:/atalkd ]; then
+       :SBINDIR:/atalkd;               echo -n ' atalkd'
+fi
+
+if [ -x :BINDIR:/nbprgstr ]; then
+       :BINDIR:/nbprgstr -p 4 `hostname|sed 's/\..*$//'`:Workstation
+       :BINDIR:/nbprgstr -p 4 `hostname|sed 's/\..*$//'`:netatalk
+                                       echo -n ' nbprgstr'
+fi
+
+if [ -x :SBINDIR:/papd ]; then
+       :SBINDIR:/papd;         echo -n ' papd'
+fi
+
+if [ -x :SBINDIR:/afpd ]; then
+       :SBINDIR:/afpd;         echo -n ' afpd'
+fi
+
+if [ -x :SBINDIR:/timelord ]; then
+       :SBINDIR:/timelord;             echo -n ' timelord'
+fi
+
+                                       echo '.'
diff --git a/distrib/initscripts/rc.atalk.cobalt b/distrib/initscripts/rc.atalk.cobalt
new file mode 100644 (file)
index 0000000..7ccbb5b
--- /dev/null
@@ -0,0 +1,148 @@
+#!/bin/sh
+#
+# chkconfig: 345 91 35
+# description: Starts and stops the atalk, afpd & papd daemons for
+#              providing AppleTalk networking services.
+#
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+# set lcd stuff up if necessary
+if [ x"$LCD_STOP" = x"" ]; then
+        LCD_STOP="/sbin/stoplcd"
+fi
+if [ x"$LCD_SWRITE" = x"" ]; then
+        LCD_SWRITE="/sbin/swritelcd"
+fi
+
+# set up i18n stuff if necessary
+if [ -x /usr/local/sbin/getmsg ]; then
+       GETMSG=/usr/local/sbin/getmsg
+       START_MSG1=atalkStart1
+       START_MSG2=atalkStart2
+       STOP_MSG1=atalkStop1
+       STOP_MSG2=atalkStop2
+else
+       GETMSG=getmsg
+       START_MSG1=atalk_start_1
+       START_MSG2=atalk_start_2
+       STOP_MSG1=atalk_stop_1
+       STOP_MSG2=atalk_stop_2
+fi
+
+# Source networking configuration.
+. /etc/sysconfig/network
+
+test -x /usr/sbin/atalkd || exit 0
+
+test -f /etc/atalk/netatalk.conf || exit 0
+
+# read in netatalk configuration
+. /etc/atalk/netatalk.conf
+
+# Check that networking is up.
+[ ${NETWORKING} = "no" ] && exit 0
+
+# initialize return values
+RETVAL=1
+RETVAL_ATALKD=0
+RETVAL_PAPD=0
+RETVAL_AFPD=0
+
+# startup code for everything
+atalk_startup() {
+    if [ x"${ATALKD_RUN}" != x"no" ]; then 
+       daemon /usr/sbin/atalkd
+       RETVAL_ATALKD=$?
+
+       if [ -x /usr/bin/nbprgstr ]; then       
+           /usr/bin/nbprgstr -p 4 "${ATALK_NAME}:Workstation${ATALK_ZONE}"
+           /usr/bin/nbprgstr -p 4 "${ATALK_NAME}:netatalk${ATALK_ZONE}"
+       fi      
+           
+       if [ x"${PAPD_RUN}" = x"yes"  -a -x /usr/sbin/papd ]; then
+           daemon /usr/sbin/papd
+           RETVAL_PAPD=$?
+       fi
+
+       if [ -x /usr/sbin/timelord ]; then
+           daemon /usr/sbin/timelord
+       fi
+    fi
+
+    if [ x"${AFPD_RUN}" = x"yes" -a -x /usr/sbin/afpd ] ; then
+           daemon /usr/sbin/afpd ${AFPD_UAMLIST} -g ${AFPD_GUEST} \
+               -c ${AFPD_MAX_CLIENTS} -n "${ATALK_NAME}${ATALK_ZONE}"
+           RETVAL_AFPD=$?
+    fi
+
+    if [ $RETVAL_ATALKD -eq 0 -a $RETVAL_PAPD -eq 0 -a $RETVAL_AFPD -eq 0 ]; then
+        RETVAL=0
+       touch /var/lock/subsys/atalk || RETVAL=1
+    fi
+}
+
+case "$1" in
+'start')
+       LINE1=`$GETMSG $START_MSG1`
+        LINE2=`$GETMSG $START_MSG2`
+        $LCD_STOP
+        $LCD_SWRITE "$LINE1" "$LINE2" &>/dev/null &
+       echo -n 'Starting AppleTalk services: '
+       if [ x"${ATALK_BGROUND}" = x"yes" ]; then 
+           echo -n "(backgrounded)"
+           atalk_startup >& /dev/null &
+       else
+           atalk_startup
+       fi
+       echo 
+       touch /var/lock/subsys/atalk
+       ;;
+'stop')
+       LINE1=`$GETMSG $STOP_MSG1`
+        LINE2=`$GETMSG $STOP_MSG2`
+        $LCD_STOP
+        $LCD_SWRITE "$LINE1" "$LINE2" &>/dev/null &
+       echo -n 'Shutting down AppleTalk services: '
+       if [ x"${ATALKD_RUN}" != x"no" ]; then
+           if [ x"${PAPD_RUN}" = x"yes" -a -x /usr/sbin/papd ]; then
+               killproc papd
+               RETVAL_PAPD=$?
+           fi
+
+           /usr/bin/nbpunrgstr "${ATALK_NAME}:Workstation${ATALK_ZONE}"
+           /usr/bin/nbpunrgstr "${ATALK_NAME}:netatalk${ATALK_ZONE}"
+
+           # kill atalkd last, since without it the plumbing goes away.
+           if [ -x /usr/sbin/atalkd ]; then
+               killproc atalkd
+               RETVAL_ATALKD=$?
+           fi
+       fi          
+
+       # kill this separately as we also do AFP/tcp
+       if [ x"${AFPD_RUN}" = x"yes" -a -x /usr/sbin/afpd ]; then
+           killproc afpd
+           RETVAL_AFPD=$?
+       fi
+
+       if [ $RETVAL_ATALKD -eq 0 -a $RETVAL_PAPD -eq 0 -a $RETVAL_AFPD -eq 0 ] ; then
+           RETVAL=0
+           rm -f /var/lock/subsys/atalk || RETVAL=1
+       fi
+       echo ""
+       ;;
+  restart|reload)
+       $0 stop
+       $0 start
+       ;;
+  status)
+       status atalkd
+       ;;
+  *)
+       echo "Usage: atalk {start|stop|restart|status}"
+       exit 1
+esac
+
+exit $RETVAL
diff --git a/distrib/initscripts/rc.atalk.redhat b/distrib/initscripts/rc.atalk.redhat
new file mode 100755 (executable)
index 0000000..1c36ccf
--- /dev/null
@@ -0,0 +1,127 @@
+#! /bin/sh
+# chkconfig: 345 91 35
+# description: This package enables Linux to talk to Macintosh
+#              computers via the AppleTalk networking protocol and 
+#              provides printer, file sharing, and AppleTalk routing 
+#              services.   
+#
+# AppleTalk daemons. Make sure not to start atalkd in the background:
+# its data structures must have time to stablize before running the
+# other processes.
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+# Source networking configuration.
+. /etc/sysconfig/network
+
+test -x :SBINDIR:/atalkd || exit 0
+
+test -f :ETCDIR:/netatalk.conf || exit 0
+
+# read in netatalk configuration
+. :ETCDIR:/netatalk.conf
+
+# Check that networking is up.
+[ ${NETWORKING} = "no" ] && exit 0
+
+# initialize return values
+RETVAL=1
+RETVAL_ATALKD=0
+RETVAL_PAPD=0
+RETVAL_AFPD=0
+
+# startup code for everything
+atalk_startup() {
+    if [ x"${ATALKD_RUN}" != x"no" ]; then 
+       daemon :SBINDIR:/atalkd
+       RETVAL_ATALKD=$?
+
+       if [ -x :BINDIR:/nbprgstr ]; then       
+           :BINDIR:/nbprgstr -p 4 "${ATALK_NAME}:Workstation${ATALK_ZONE}"
+           :BINDIR:/nbprgstr -p 4 "${ATALK_NAME}:netatalk${ATALK_ZONE}"
+       fi      
+
+       if [ x"${PAPD_RUN}" = x"yes"  -a -x :SBINDIR:/papd ]; then
+           daemon :SBINDIR:/papd
+           RETVAL_PAPD=$?
+       fi
+
+       if [ -x :SBINDIR:/timelord ]; then
+           daemon :SBINDIR:/timelord
+       fi
+
+    fi
+
+    if [ x"${AFPD_RUN}" = x"yes" -a -x :SBINDIR:/afpd ] ; then
+           daemon :SBINDIR:/afpd ${AFPD_UAMLIST} -g ${AFPD_GUEST} \
+               -c ${AFPD_MAX_CLIENTS} -n "${ATALK_NAME}${ATALK_ZONE}"
+           RETVAL_AFPD=$?
+    fi
+
+    if [ $RETVAL_ATALKD -eq 0 -a $RETVAL_PAPD -eq 0 -a $RETVAL_AFPD -eq 0 ]; then
+        RETVAL=0
+       touch /var/lock/subsys/atalk || RETVAL=1
+    fi
+}
+
+case "$1" in
+'start')
+       echo -n 'Starting AppleTalk services: '
+       if [ x"${ATALK_BGROUND}" = x"yes" ]; then 
+           echo -n "(backgrounded)"
+           atalk_startup >& /dev/null &
+       else
+           atalk_startup
+       fi
+       echo 
+       ;;
+'stop')
+       echo -n 'Shutting down AppleTalk services: '
+       if [ x"${ATALKD_RUN}" != x"no" ]; then
+           if [ x"${PAPD_RUN}" = x"yes" -a -x :SBINDIR:/papd ]; then
+               killproc papd
+               RETVAL_PAPD=$?
+           fi
+
+           if [ -x :SBINDIR:/timelord ]; then
+               killproc timelord
+           fi
+
+           :BINDIR:/nbpunrgstr "${ATALK_NAME}:Workstation${ATALK_ZONE}"
+           :BINDIR:/nbpunrgstr "${ATALK_NAME}:netatalk${ATALK_ZONE}"
+
+           # kill atalkd last, since without it the plumbing goes away.
+           if [ -x :SBINDIR:/atalkd ]; then
+               killproc atalkd
+               RETVAL_ATALKD=$?
+           fi
+       fi          
+
+       # kill this separately as we also do AFP/tcp
+       if [ x"${AFPD_RUN}" = x"yes" -a -x :SBINDIR:/afpd ]; then
+           killproc afpd
+           RETVAL_AFPD=$?
+       fi
+
+       if [ $RETVAL_ATALKD -eq 0 -a $RETVAL_PAPD -eq 0 -a $RETVAL_AFPD -eq 0 ] ; then
+           RETVAL=0
+           rm -f /var/lock/subsys/atalk || RETVAL=1
+       fi
+       echo ""
+       ;;
+  'restart'|'reload')
+       $0 stop
+       $0 start
+       RETVAL=$?
+       ;;
+  'status')
+       status atalkd
+       RETVAL=$?
+       ;;
+  *)
+       echo "Usage: atalk {start|stop|restart|status}"
+       exit 1
+esac
+
+exit $RETVAL
diff --git a/distrib/initscripts/rc.atalk.sysv b/distrib/initscripts/rc.atalk.sysv
new file mode 100755 (executable)
index 0000000..3845f8e
--- /dev/null
@@ -0,0 +1,91 @@
+#! /bin/sh
+#
+# Start/stop the AppleTalk daemons.
+#
+# AppleTalk daemons. Make sure not to start atalkd in the background:
+# its data structures must have time to stablize before running the
+# other processes.
+#
+
+#
+# kill the named process(es)
+#
+killproc() {
+       pid=`/usr/bin/ps -e |
+            /usr/bin/grep $1 |
+            /usr/bin/sed -e 's/^  *//' -e 's/ .*//'`
+       [ "$pid" != "" ] && kill $pid
+}
+
+case "$1" in
+
+#
+# Start the appletalk server processes.
+#
+
+'start')
+
+       echo 'starting appletalk daemons: \c'
+       if [ -x :SBINDIR:/atalkd ]; then
+               :SBINDIR:/atalkd;               echo ' atalkd\c'
+       fi
+
+       if [ -x :BINDIR:/nbprgstr ]; then
+               :BINDIR:/nbprgstr -p 4 `hostname|sed 's/\..*$//'`:Workstation
+               :BINDIR:/nbprgstr -p 4 `hostname|sed 's/\..*$//'`:netatalk
+                                               echo ' nbprgstr\c'
+       fi
+
+       if [ -x :SBINDIR:/papd ]; then
+               :SBINDIR:/papd;                 echo ' papd\c'
+       fi
+
+       if [ -x :SBINDIR:/afpd ]; then
+               :SBINDIR:/afpd;                 echo ' afpd\c'
+       fi
+
+       if [ -x :SBINDIR:/timelord ]; then
+               :SBINDIR:/timelord;             echo ' timelord\c'
+       fi
+
+       echo '.'
+
+       ;;
+
+#
+# Stop the appletalk server processes.
+#
+
+'stop')
+
+       echo 'stopping appletalk daemons:\c'
+
+       if [ -x :SBINDIR:/papd ]; then
+               killproc papd;                  echo ' papd\c'
+       fi
+
+       if [ -x :SBINDIR:/afpd ]; then
+               killproc afpd;                  echo ' afpd\c'
+       fi
+
+       if [ -x :SBINDIR:/timelord ]; then
+               killproc timelord;              echo ' timelord\c'
+       fi
+
+       # kill atalkd last, since without it the plumbing goes away.
+       if [ -x :SBINDIR:/atalkd ]; then
+               killproc atalkd;                echo ' atalkd\c'
+       fi
+
+       echo '.'
+       ;;
+
+#
+# Usage statement.
+#
+
+*)
+       echo "usage: $0 {start|stop}"
+       exit 1
+       ;;
+esac
diff --git a/distrib/rpm/buildrpm b/distrib/rpm/buildrpm
new file mode 100755 (executable)
index 0000000..37e0438
--- /dev/null
@@ -0,0 +1,26 @@
+#!/bin/sh
+#
+# buildrpm
+# $Id: buildrpm,v 1.1 2000-07-25 21:08:59 rufustfirefly Exp $
+#
+# automates the process of building the netatalk rpm
+
+REDHAT_DIR=/usr/src/redhat
+CVSNAME=netatalk+asun
+
+# copy spec and patch into place
+cp netatalk-asun-cobalt.spec ${REDHAT_DIR}/SPECS
+cp netatalk-asun.makefile.patch ${REDHAT_DIR}/SOURCES
+
+# clean out any object files
+cd ../../
+make clean
+
+# tar up the archive
+cd ../
+tar cvfz ${REDHAT_DIR}/SOURCES/netatalk+asun.tar.gz \
+       --exclude="*/CVS" --exclude="*~" $CVSNAME
+
+# build the rpm
+rpm -ba ${REDHAT_DIR}/SPECS/netatalk-asun-cobalt.spec
+
diff --git a/distrib/rpm/netatalk-asun-cobalt.spec b/distrib/rpm/netatalk-asun-cobalt.spec
new file mode 100644 (file)
index 0000000..ebd1569
--- /dev/null
@@ -0,0 +1,287 @@
+Summary: AppleTalk networking programs
+Name: netatalk
+Version: 1.4b2+asun2.1.4
+Release: pre39
+Packager: iNOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+Copyright: BSD
+Group: Networking
+Source0: netatalk+asun.tar.gz
+Patch0: netatalk-asun.makefile.patch
+Requires: pam >= 0.56
+BuildRoot: /var/tmp/atalk
+
+%description
+This package enables Linux to talk to Macintosh computers via the
+AppleTalk networking protocol. It includes a daemon to allow Linux
+to act as a file server over AppleTalk or IP for Mac's.
+
+%package devel
+Summary: Headers and static libraries for Appletalk development
+Group: Development/Libraries
+
+%description devel
+This packge contains the header files, and static libraries for building
+Appletalk networking programs.
+
+%prep
+%setup -n netatalk+asun
+%patch0 -p1
+
+%build
+make OPTOPTS="$RPM_OPT_FLAGS -fomit-frame-pointer -fsigned-char" OSVERSION=2.0
+
+%install
+rm -rf $RPM_BUILD_ROOT
+mkdir -p $RPM_BUILD_ROOT/etc/atalk
+mkdir -p $RPM_BUILD_ROOT/etc/pam.d
+mkdir -p $RPM_BUILD_ROOT/etc/rc.d/init.d
+for i in 0 1 2 3 4 5 6; do
+       mkdir -p $RPM_BUILD_ROOT/etc/rc.d/rc$i.d
+done
+mkdir -p $RPM_BUILD_ROOT/usr/lib/atalk
+
+make install INSTALL_PREFIX=$RPM_BUILD_ROOT
+
+for i in aecho getzones megatron nbplkup nbprgstr nbpunrgstr pap \
+       papstatus psorder; do
+       strip $RPM_BUILD_ROOT/usr/bin/$i
+done
+for i in afpd atalkd psf psa papd; do
+       strip $RPM_BUILD_ROOT/usr/sbin/$i
+done
+
+#install -m644 config/AppleVolumes.system.cobalt $RPM_BUILD_ROOT/etc/atalk/AppleVolumes.system
+#install -m644 config/AppleVolumes.default $RPM_BUILD_ROOT/etc/atalk/AppleVolumes.default
+install -m644 config/atalkd.conf.cobalt $RPM_BUILD_ROOT/etc/atalk/atalkd.conf
+install -m644 config/papd.conf $RPM_BUILD_ROOT/etc/atalk/papd.conf
+install -m755 distrib/initscripts/rc.atalk.cobalt $RPM_BUILD_ROOT/etc/rc.d/init.d/atalk
+install -m644 config/netatalk.conf.cobalt $RPM_BUILD_ROOT/etc/atalk/netatalk.conf
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+/sbin/chkconfig --add atalk
+ldconfig
+# Do only for the first install
+if [ "$1" = 1 ] ; then
+  # Add the ddp lines to /etc/services
+  if (grep '[0-9][0-9]*/ddp' /etc/services >/dev/null); then
+    cat <<'_EOD1_' >&2
+warning: The DDP services appear to be present in /etc/services.
+warning: Please check them against services.atalk in the documentation.
+_EOD1_
+    true
+  else
+    cat <<'_EOD2_' >>/etc/services
+# start of DDP services
+#
+# Everything between the 'start of DDP services' and 'end of DDP services'
+# lines will be automatically deleted when the netatalk package is removed.
+#
+rtmp           1/ddp           # Routing Table Maintenance Protocol
+nbp            2/ddp           # Name Binding Protocol
+echo           4/ddp           # AppleTalk Echo Protocol
+zip            6/ddp           # Zone Information Protocol
+
+afpovertcp     548/tcp         # AFP over TCP
+afpovertcp     548/udp
+# end of DDP services
+_EOD2_
+  fi
+fi
+
+%postun
+# Do only for the last un-install
+if [ "$1" = 0 ] ; then
+  /sbin/chkconfig --del atalk
+  # remove the ddp lines from /etc/services
+  if (grep '^# start of DDP services$' /etc/services >/dev/null && \
+      grep '^# end of DDP services$' /etc/services >/dev/null ); then
+    sed -e '/^# start of DDP services$/,/^# end of DDP services$/d' \
+       </etc/services >/tmp/services.tmp$$
+    cat /tmp/services.tmp$$ >/etc/services
+    rm /tmp/services.tmp$$
+  else
+    cat <<'_EOD3_' >&2
+warning: Unable to find the lines `# start of DDP services' and
+warning: `# end of DDP services' in the file /etc/services.
+warning: You should remove the DDP services from /etc/service manually.
+_EOD3_
+  fi
+fi
+
+%files
+%doc BUGS CHANGES CONTRIBUTORS COPYRIGHT ChangeLog INSTALL/ README* TODO VERSION contrib/ services.atalk
+%dir /etc/atalk
+#%config /etc/atalk/AppleVolumes.default
+#%config /etc/atalk/AppleVolumes.system
+%config /etc/atalk/netatalk.conf
+%config /etc/atalk/afpd.conf
+%config /etc/atalk/atalkd.conf
+%config /etc/atalk/papd.conf
+%config /etc/rc.d/init.d/atalk
+%config /etc/pam.d/netatalk
+/usr/sbin/afpd
+/usr/sbin/atalkd
+/usr/sbin/papd
+/usr/sbin/psa
+/usr/sbin/etc2ps
+/usr/sbin/psf
+/usr/bin/adv1tov2
+/usr/bin/aecho
+/usr/bin/afppasswd
+/usr/bin/binheader
+/usr/bin/getzones
+/usr/bin/hqx2bin
+/usr/bin/macbinary
+/usr/bin/megatron
+/usr/bin/nadheader
+/usr/bin/nbplkup
+/usr/bin/nbprgstr
+/usr/bin/nbpunrgstr
+/usr/bin/pap
+/usr/bin/papstatus
+/usr/bin/psorder
+/usr/bin/single2bin
+/usr/bin/unbin
+/usr/bin/unhex
+/usr/bin/unsingle
+%dir /usr/lib/atalk
+/usr/lib/atalk/filters/
+/usr/lib/atalk/nls/
+/usr/lib/atalk/pagecount.ps
+/usr/lib/atalk/uams/
+/usr/man/man1/aecho.1
+/usr/man/man1/getzones.1
+/usr/man/man1/hqx2bin.1
+/usr/man/man1/macbinary.1
+/usr/man/man1/megatron.1
+/usr/man/man1/nbp.1
+/usr/man/man1/nbplkup.1
+/usr/man/man1/nbprgstr.1
+/usr/man/man1/pap.1
+/usr/man/man1/papstatus.1
+/usr/man/man1/psorder.1
+/usr/man/man1/single2bin.1
+/usr/man/man1/unbin.1
+/usr/man/man1/unhex.1
+/usr/man/man1/unsingle.1
+/usr/man/man3/atalk_aton.3
+/usr/man/man3/nbp_name.3
+/usr/man/man4/atalk.4
+/usr/man/man8/afpd.8
+/usr/man/man8/atalkd.8
+/usr/man/man8/papd.8
+/usr/man/man8/psf.8
+
+%files devel
+/usr/lib/libatalk.a
+/usr/lib/libatalk_p.a
+/usr/include/atalk/
+/usr/include/netatalk/
+
+%changelog
+* Fri Aug 13 1999 John D. Blair <jdblair@cobaltnet.com>
+  - updated to latest build from Adrian
+
+* Thu Jul 22 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- /etc/atalk/netatalk.config -> /etc/atalk/netatalk.conf
+  Many parts of patch are merged into the original source code.
+* Tue Jul 13 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- AppleVolumes.system is merged into the original source code.
+  /etc/atalk/config -> /etc/atalk/netatalk.config.
+  Merge original rc.atalk.redhat and /etc/rc.d/init.d/atalk.
+  Remove last sample line of patched afpd.conf.
+* Fri Jul 9 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.4-30]
+* Sun Jun 20 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.4-28]
+* Thu Jun 3 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.4-22]
+* Wed May 19 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.4-15]
+  Make BerkleyDB=/usr.
+* Sun May 2 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.4-11]
+  Integrate three patches into netatalk-asun.makefile.patch.
+  Change /etc/uams dir to /usr/lib/atalk/uams.
+  Add configuration line to /etc/atalk/afpd.conf and remove needless 
+  variables from /etc/atalk/config and /etc/rc.d/init.d/atalk.
+* Wed Apr 21 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.4-9]
+  Move %chengelog section last.
+* Wed Mar 31 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- Comment out -DNEED_QUOTA_WRAPPER in sys/linux/Makefile.
+* Sat Mar 20 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- Correct symbolic links to psf.
+  Remove asciize function from nbplkup so as to display Japanese hostname.
+* Thu Mar 11 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- Included MacPerl 5 script ICDumpSuffixMap which dumps suffix mapping
+  containd in Internet Config Preference.
+* Tue Mar 2 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [asun2.1.3]
+* Mon Feb 15 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.2-8]
+* Sun Feb 7 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.2-6]
+* Mon Jan 25 1999 iNOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.2-3]
+* Thu Dec 17 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.2]
+  Remove crlf patch. It is now a server's option.
+* Thu Dec 3 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use stable version source netatalk-1.4b2+asun2.1.1.tar.gz
+  Add uams directory
+* Sat Nov 28 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.1-3 source.
+* Mon Nov 23 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.1-2 source.
+* Mon Nov 16 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Fix rcX.d's symbolic links.
+* Wed Oct 28 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.0a-2 source. Remove '%exclusiveos linux' line.
+* Sat Oct 24 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use stable version source netatalk-1.4b2+asun2.1.0.tar.gz.
+* Mon Oct 5 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.0-10a source.
+* Thu Sep 19 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.0-8 source. Add chkconfig support.
+* Sat Sep 12 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Comment out -DCRLF. Use RPM_OPT_FLAGS.
+* Mon Sep 8 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.0-7 source. Rename atalk.init to atalk.
+* Mon Aug 22 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.0-6 source.
+* Mon Jul 27 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.0-5 source.
+* Tue Jul 21 1998 INOUE Koichi <inoue@ma.ns.musashi-techa.c.jp>
+- Use pre-asun2.1.0-3 source.
+* Tue Jul 7 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Add afpovertcp entries to /etc/services
+- Remove BuildRoot in man8 pages
+* Mon Jun 29 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use modified sources 1.4b2+asun2.1.0 produced by Adrian Sun
+  <asun@saul9.u.washington.edu> to provide an AppleShareIP file server
+- Included AppleVolumes.system file maintained by Johnson
+  <johnson@stpt.usf.edu>
+* Mon Aug 25 1997 David Gibson <D.Gibson@student.anu.edu.au>
+- Used a buildroot
+- Use RPM_OPT_FLAGS
+- Moved configuration parameters/files from atalk.init to /etc/atalk
+- Separated devel package
+- Built with shared libraries
+* Sun Jul 13 1997 Paul H. Hargrove <hargrove@sccm.Stanford.EDU>
+- Updated sources from 1.3.3 to 1.4b2
+- Included endian patch for Linux/SPARC
+- Use all the configuration files supplied in the source.  This has the
+  following advantages over the ones in the previous rpm release:
+       + The printer 'lp' isn't automatically placed in papd.conf
+       + The default file conversion is binary rather than text.
+- Automatically add and remove DDP services from /etc/services
+- Placed the recommended /etc/services in the documentation
+- Changed atalk.init to give daemons a soft kill
+- Changed atalk.init to make configuration easier
+
+* Wed May 28 1997 Mark Cornick <mcornick@zorak.gsfc.nasa.gov>
+Updated for /etc/pam.d
diff --git a/distrib/rpm/netatalk-asun.makefile.patch b/distrib/rpm/netatalk-asun.makefile.patch
new file mode 100644 (file)
index 0000000..42e70a1
--- /dev/null
@@ -0,0 +1,65 @@
+--- netatalk-1.4b2+asun2.1.4.orig/Makefile.base        Fri Jul 30 09:43:38 1999
++++ netatalk-1.4b2+asun2.1.4.orig/Makefile     Fri Jul 30 09:44:29 1999
+@@ -1,31 +1,31 @@
+ # Root of installation. Subdirectories will be ${DESTDIR}/etc,
+ # ${DESTDIR}/bin, and ${DESTDIR}/lib.
+-DESTDIR=/usr/local/atalk
++#DESTDIR=/usr/local/atalk
+ # for system-level binaries
+-SBINDIR=$(DESTDIR)/sbin
++#SBINDIR=$(DESTDIR)/sbin
+ # for user-level binaries
+-BINDIR=$(DESTDIR)/bin
++#BINDIR=$(DESTDIR)/bin
+ # for program libraries (*.a)
+-LIBDIR=$(DESTDIR)/lib
++#LIBDIR=$(DESTDIR)/lib
+ # for machine-independent resources (pagecount.ps, etc.)
+-RESDIR=$(DESTDIR)/etc
++#RESDIR=$(DESTDIR)/etc
+ # for configuration files (AppleVolumes.system, etc.)
+-ETCDIR=$(DESTDIR)/etc
++#ETCDIR=$(DESTDIR)/etc
+ # for include files
+-INCDIR=$(DESTDIR)/include
++#INCDIR=$(DESTDIR)/include
+ # Root of man pages.  Subdirectories will be
+ # ${MANDIR}/man1, ${MANDIR}/man4, and ${MANDIR}/man8.
+-MANDIR=$(DESTDIR)/man
++#MANDIR=$(DESTDIR)/man
+-#INSTALL_PREFIX=
+-#SBINDIR=${INSTALL_PREFIX}/usr/sbin
+-#BINDIR=${INSTALL_PREFIX}/usr/bin
+-#LIBDIR=${INSTALL_PREFIX}/usr/lib
+-#RESDIR=${INSTALL_PREFIX}/usr/lib/atalk
+-#ETCDIR=${INSTALL_PREFIX}/etc/atalk
+-#INCDIR=${INSTALL_PREFIX}/usr/include
+-#MANDIR=${INSTALL_PREFIX}/usr/man
++INSTALL_PREFIX=
++SBINDIR=${INSTALL_PREFIX}/usr/sbin
++BINDIR=${INSTALL_PREFIX}/usr/bin
++LIBDIR=${INSTALL_PREFIX}/usr/lib
++RESDIR=${INSTALL_PREFIX}/usr/lib/atalk
++ETCDIR=${INSTALL_PREFIX}/etc/atalk
++INCDIR=${INSTALL_PREFIX}/usr/include
++MANDIR=${INSTALL_PREFIX}/usr/man
+ # Location of the Berkeley v2 db library and include files. 
+ # NOTE: leave this commented out for now. it's a placeholder for a future
+@@ -55,12 +55,12 @@
+ # Location of PAM support library and include files. Uncomment this if
+ # you want to enable PAM support.
+-#PAMDIR=/usr
++PAMDIR=/usr
+ # Location of cracklib support library and include files. This is used
+ # in the password changing routines. Uncomment this out if you want to
+ # enable support.
+-#CRACKDIR=/usr
++CRACKDIR=/usr
+  
+ # Location of the AFS and Kerberos libraries and include files.  Uncomment
diff --git a/distrib/rpm/netatalk-asun.spec b/distrib/rpm/netatalk-asun.spec
new file mode 100644 (file)
index 0000000..4e3d992
--- /dev/null
@@ -0,0 +1,297 @@
+Summary: AppleTalk networking programs
+Name: netatalk
+Version: 1.4b2+asun2.1.4
+Release: pre39
+Packager: iNOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+Copyright: BSD
+Group: Networking
+Source0: ftp://ftp.cobaltnet.com/pub/users/asun/testing/pre-asun2.1.4-36.tar.gz
+Patch0: netatalk-asun.makefile.patch
+Requires: pam >= 0.56
+BuildRoot: /var/tmp/atalk
+
+%description
+This package enables Linux to talk to Macintosh computers via the
+AppleTalk networking protocol. It includes a daemon to allow Linux
+to act as a file server over AppleTalk or IP for Mac's.
+
+%package devel
+Summary: Headers and static libraries for Appletalk development
+Group: Development/Libraries
+
+%description devel
+This packge contains the header files, and static libraries for building
+Appletalk networking programs.
+
+%prep
+%setup
+%patch0 -p1
+
+%build
+make OPTOPTS="$RPM_OPT_FLAGS -fomit-frame-pointer -fsigned-char" OSVERSION=2.0
+
+%install
+rm -rf $RPM_BUILD_ROOT
+mkdir -p $RPM_BUILD_ROOT/etc/atalk
+mkdir -p $RPM_BUILD_ROOT/etc/pam.d
+mkdir -p $RPM_BUILD_ROOT/etc/rc.d/init.d
+for i in 0 1 2 3 4 5 6; do
+       mkdir -p $RPM_BUILD_ROOT/etc/rc.d/rc$i.d
+done
+mkdir -p $RPM_BUILD_ROOT/usr/lib/atalk
+
+make install INSTALL_PREFIX=$RPM_BUILD_ROOT
+
+for i in aecho getzones megatron nbplkup nbprgstr nbpunrgstr pap \
+       papstatus psorder; do
+       strip $RPM_BUILD_ROOT/usr/bin/$i
+done
+for i in afpd atalkd psf psa papd; do
+       strip $RPM_BUILD_ROOT/usr/sbin/$i
+done
+
+install -m644 config/AppleVolumes.system $RPM_BUILD_ROOT/etc/atalk/AppleVolumes.system
+install -m644 config/AppleVolumes.default $RPM_BUILD_ROOT/etc/atalk/AppleVolumes.default
+install -m644 config/atalkd.conf $RPM_BUILD_ROOT/etc/atalk/atalkd.conf
+install -m644 config/papd.conf $RPM_BUILD_ROOT/etc/atalk/papd.conf
+
+# This is not necessary because chkconfig will make the links.
+for i in 0 1 2 6; do
+       ln -sf ../init.d/atalk $RPM_BUILD_ROOT/etc/rc.d/rc$i.d/K35atalk
+done
+for i in 3 4 5; do
+       ln -sf ../init.d/atalk $RPM_BUILD_ROOT/etc/rc.d/rc$i.d/S91atalk
+done
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+/sbin/chkconfig --add atalk
+ldconfig
+# Do only for the first install
+if [ "$1" = 1 ] ; then
+  # Add the ddp lines to /etc/services
+  if (grep '[0-9][0-9]*/ddp' /etc/services >/dev/null); then
+    cat <<'_EOD1_' >&2
+warning: The DDP services appear to be present in /etc/services.
+warning: Please check them against services.atalk in the documentation.
+_EOD1_
+    true
+  else
+    cat <<'_EOD2_' >>/etc/services
+# start of DDP services
+#
+# Everything between the 'start of DDP services' and 'end of DDP services'
+# lines will be automatically deleted when the netatalk package is removed.
+#
+rtmp           1/ddp           # Routing Table Maintenance Protocol
+nbp            2/ddp           # Name Binding Protocol
+echo           4/ddp           # AppleTalk Echo Protocol
+zip            6/ddp           # Zone Information Protocol
+
+afpovertcp     548/tcp         # AFP over TCP
+afpovertcp     548/udp
+# end of DDP services
+_EOD2_
+  fi
+fi
+
+%postun
+# Do only for the last un-install
+if [ "$1" = 0 ] ; then
+  /sbin/chkconfig --del atalk
+  # remove the ddp lines from /etc/services
+  if (grep '^# start of DDP services$' /etc/services >/dev/null && \
+      grep '^# end of DDP services$' /etc/services >/dev/null ); then
+    sed -e '/^# start of DDP services$/,/^# end of DDP services$/d' \
+       </etc/services >/tmp/services.tmp$$
+    cat /tmp/services.tmp$$ >/etc/services
+    rm /tmp/services.tmp$$
+  else
+    cat <<'_EOD3_' >&2
+warning: Unable to find the lines `# start of DDP services' and
+warning: `# end of DDP services' in the file /etc/services.
+warning: You should remove the DDP services from /etc/service manually.
+_EOD3_
+  fi
+fi
+
+%files
+%doc BUGS CHANGES CONTRIBUTORS COPYRIGHT ChangeLog INSTALL/ README* TODO VERSION contrib/ services.atalk
+%dir /etc/atalk
+%config /etc/atalk/AppleVolumes.default
+%config /etc/atalk/AppleVolumes.system
+%config /etc/atalk/netatalk.conf
+%config /etc/atalk/afpd.conf
+%config /etc/atalk/atalkd.conf
+%config /etc/atalk/papd.conf
+%config /etc/rc.d/init.d/atalk
+%config /etc/pam.d/netatalk
+/etc/rc.d/rc0.d/K35atalk
+/etc/rc.d/rc1.d/K35atalk
+/etc/rc.d/rc2.d/K35atalk
+/etc/rc.d/rc3.d/S91atalk
+/etc/rc.d/rc4.d/S91atalk
+/etc/rc.d/rc5.d/S91atalk
+/etc/rc.d/rc6.d/K35atalk
+/usr/sbin/afpd
+/usr/sbin/atalkd
+/usr/sbin/papd
+/usr/sbin/psa
+/usr/sbin/etc2ps
+/usr/sbin/psf
+/usr/bin/adv1tov2
+/usr/bin/aecho
+/usr/bin/afppasswd
+/usr/bin/binheader
+/usr/bin/getzones
+/usr/bin/hqx2bin
+/usr/bin/macbinary
+/usr/bin/megatron
+/usr/bin/nadheader
+/usr/bin/nbplkup
+/usr/bin/nbprgstr
+/usr/bin/nbpunrgstr
+/usr/bin/pap
+/usr/bin/papstatus
+/usr/bin/psorder
+/usr/bin/single2bin
+/usr/bin/unbin
+/usr/bin/unhex
+/usr/bin/unsingle
+%dir /usr/lib/atalk
+/usr/lib/atalk/filters/
+/usr/lib/atalk/nls/
+/usr/lib/atalk/pagecount.ps
+/usr/lib/atalk/uams/
+/usr/man/man1/aecho.1
+/usr/man/man1/getzones.1
+/usr/man/man1/hqx2bin.1
+/usr/man/man1/macbinary.1
+/usr/man/man1/megatron.1
+/usr/man/man1/nbp.1
+/usr/man/man1/nbplkup.1
+/usr/man/man1/nbprgstr.1
+/usr/man/man1/pap.1
+/usr/man/man1/papstatus.1
+/usr/man/man1/psorder.1
+/usr/man/man1/single2bin.1
+/usr/man/man1/unbin.1
+/usr/man/man1/unhex.1
+/usr/man/man1/unsingle.1
+/usr/man/man3/atalk_aton.3
+/usr/man/man3/nbp_name.3
+/usr/man/man4/atalk.4
+/usr/man/man8/afpd.8
+/usr/man/man8/atalkd.8
+/usr/man/man8/papd.8
+/usr/man/man8/psf.8
+
+%files devel
+/usr/lib/libatalk.a
+/usr/lib/libatalk_p.a
+/usr/include/atalk/
+/usr/include/netatalk/
+
+%changelog
+* Thu Jul 22 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- /etc/atalk/netatalk.config -> /etc/atalk/netatalk.conf
+  Many parts of patch are merged into the original source code.
+* Tue Jul 13 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- AppleVolumes.system is merged into the original source code.
+  /etc/atalk/config -> /etc/atalk/netatalk.config.
+  Merge original rc.atalk.redhat and /etc/rc.d/init.d/atalk.
+  Remove last sample line of patched afpd.conf.
+* Fri Jul 9 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.4-30]
+* Sun Jun 20 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.4-28]
+* Thu Jun 3 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.4-22]
+* Wed May 19 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.4-15]
+  Make BerkleyDB=/usr.
+* Sun May 2 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.4-11]
+  Integrate three patches into netatalk-asun.makefile.patch.
+  Change /etc/uams dir to /usr/lib/atalk/uams.
+  Add configuration line to /etc/atalk/afpd.conf and remove needless 
+  variables from /etc/atalk/config and /etc/rc.d/init.d/atalk.
+* Wed Apr 21 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.4-9]
+  Move %chengelog section last.
+* Wed Mar 31 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- Comment out -DNEED_QUOTA_WRAPPER in sys/linux/Makefile.
+* Sat Mar 20 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- Correct symbolic links to psf.
+  Remove asciize function from nbplkup so as to display Japanese hostname.
+* Thu Mar 11 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- Included MacPerl 5 script ICDumpSuffixMap which dumps suffix mapping
+  containd in Internet Config Preference.
+* Tue Mar 2 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [asun2.1.3]
+* Mon Feb 15 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.2-8]
+* Sun Feb 7 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.2-6]
+* Mon Jan 25 1999 iNOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.2-3]
+* Thu Dec 17 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- [pre-asun2.1.2]
+  Remove crlf patch. It is now a server's option.
+* Thu Dec 3 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use stable version source netatalk-1.4b2+asun2.1.1.tar.gz
+  Add uams directory
+* Sat Nov 28 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.1-3 source.
+* Mon Nov 23 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.1-2 source.
+* Mon Nov 16 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Fix rcX.d's symbolic links.
+* Wed Oct 28 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.0a-2 source. Remove '%exclusiveos linux' line.
+* Sat Oct 24 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use stable version source netatalk-1.4b2+asun2.1.0.tar.gz.
+* Mon Oct 5 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.0-10a source.
+* Thu Sep 19 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.0-8 source. Add chkconfig support.
+* Sat Sep 12 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Comment out -DCRLF. Use RPM_OPT_FLAGS.
+* Mon Sep 8 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.0-7 source. Rename atalk.init to atalk.
+* Mon Aug 22 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.0-6 source.
+* Mon Jul 27 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use pre-asun2.1.0-5 source.
+* Tue Jul 21 1998 INOUE Koichi <inoue@ma.ns.musashi-techa.c.jp>
+- Use pre-asun2.1.0-3 source.
+* Tue Jul 7 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Add afpovertcp entries to /etc/services
+- Remove BuildRoot in man8 pages
+* Mon Jun 29 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
+- Use modified sources 1.4b2+asun2.1.0 produced by Adrian Sun
+  <asun@saul9.u.washington.edu> to provide an AppleShareIP file server
+- Included AppleVolumes.system file maintained by Johnson
+  <johnson@stpt.usf.edu>
+* Mon Aug 25 1997 David Gibson <D.Gibson@student.anu.edu.au>
+- Used a buildroot
+- Use RPM_OPT_FLAGS
+- Moved configuration parameters/files from atalk.init to /etc/atalk
+- Separated devel package
+- Built with shared libraries
+* Sun Jul 13 1997 Paul H. Hargrove <hargrove@sccm.Stanford.EDU>
+- Updated sources from 1.3.3 to 1.4b2
+- Included endian patch for Linux/SPARC
+- Use all the configuration files supplied in the source.  This has the
+  following advantages over the ones in the previous rpm release:
+       + The printer 'lp' isn't automatically placed in papd.conf
+       + The default file conversion is binary rather than text.
+- Automatically add and remove DDP services from /etc/services
+- Placed the recommended /etc/services in the documentation
+- Changed atalk.init to give daemons a soft kill
+- Changed atalk.init to make configuration easier
+
+* Wed May 28 1997 Mark Cornick <mcornick@zorak.gsfc.nasa.gov>
+Updated for /etc/pam.d
diff --git a/etc/Makefile b/etc/Makefile
new file mode 100644 (file)
index 0000000..4806719
--- /dev/null
@@ -0,0 +1,46 @@
+ALL=   afpd uams psf atalkd papd 
+TAGSFILE=tags
+CC=cc
+INSTALL=       install
+
+all:   ${ALL}
+
+${ALL}: FRC
+       cd $@; ${MAKE} ${MFLAGS} CC="${CC}" \
+           ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+           SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+           ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+           DESTDIR="${DESTDIR}" AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" \
+           DESDIR="${DESDIR}" TCPWRAPDIR="${TCPWRAPDIR}" PAMDIR="${PAMDIR}" \
+           CRYPTODIR="${CRYPTODIR}" DB2DIR="${DB2DIR}" all
+
+FRC:
+
+tags:
+       for i in ${ALL}; do \
+           (cd $$i; ${MAKE} ${MFLAGS} DEFS="${DEFS}" \
+               TAGSFILE=../${TAGSFILE} tags); \
+       done
+
+install:
+       -mkdir ${ETCDIR}
+       -mkdir ${SBINDIR}
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} CC="${CC}" \
+               ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+               SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+               ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+               DESTDIR="${DESTDIR}" AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" \
+               TCPWRAPDIR="${TCPWRAPDIR}" PAMDIR="${PAMDIR}" \
+               INSTALL="${INSTALL}" DESDIR="${DESDIR}" install); \
+       done
+
+clean:
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} clean); \
+       done
+
+depend:
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} DEFS=${DEFS} depend); \
+       done
diff --git a/etc/afpd/Makefile b/etc/afpd/Makefile
new file mode 100644 (file)
index 0000000..3bff97e
--- /dev/null
@@ -0,0 +1,141 @@
+
+##########################################################################
+
+SRC = unix.c ofork.c main.c switch.c auth.c volume.c directory.c file.c \
+       enumerate.c desktop.c filedir.c fork.c appl.c gettok.c \
+       status.c afp_options.c afp_asp.c afp_dsi.c messages.c config.c \
+       nfsquota.c codepage.c quota.c uam.c afs.c
+
+OBJ = unix.o ofork.o main.o switch.o auth.o volume.o directory.o file.o \
+       enumerate.o desktop.o filedir.o fork.o appl.o gettok.o \
+       status.o afp_options.o afp_asp.o afp_dsi.o messages.o config.o \
+       nfsquota.o codepage.o quota.o uam.o afs.o
+
+INCPATH=       -I../../include ${AFSINCPATH}
+CFLAGS=        ${DEFS} ${AFSDEFS} ${OPTOPTS} ${INCPATH} -DAPPLCNAME
+LIBS = -latalk ${AFSLIBS} ${ADDLIBS} ${TCPWRAPLIBS} ${DB2LIBS} \
+       ${RPCSVCLIB} ${AFPLIBS} ${PAMLIBS} ${LIBSHARED} 
+LIBDIRS=       -L../../libatalk ${AFSLIBDIRS} ${TCPWRAPLIBDIRS} \
+       ${DB2LIBDIRS} ${PAMLIBDIRS}
+TAGSFILE=      tags
+CC=    cc
+INSTALL=       install
+
+SUBDIRS = nls
+
+all : ${SUBDIRS}
+       if [ x"${TCPWRAPDIR}" != x ]; then \
+           TCPWRAPLIBS="-lwrap -lnsl"; \
+           if [ "${TCPWRAPDIR}" != "/usr" ]; then \
+             TCPWRAPLIBDIRS="-L${TCPWRAPDIR}/lib"; \
+           fi; \
+       fi; \
+       if [ x"${DB2DIR}" != x ]; then \
+           DB2LIBS="-ldb"; \
+           if [ "${DB2DIR}" != "/usr" ]; then \
+             DB2LIBDIRS="-L${DB2DIR}/lib"; \
+             DB2INCPATH="-I${DB2DIR}/include"; \
+           fi; \
+       fi; \
+       if [ x"${PAMDIR}" != x ]; then \
+           PAMLIBS="-lpam"; \
+           if [ "${PAMDIR}" != "/usr" ]; then \
+             PAMLIBDIRS="-L${PAMDIR}/lib"; \
+             PAMINCPATH="-I${PAMDIR}/include"; \
+           fi; \
+           PAMDEFS="-DUSE_PAM"; \
+       fi; \
+       if [ -f /usr/lib/librpcsvc.a -o -f /lib/librpcsvc.a ]; then \
+           RPCSVCLIB=-lrpcsvc; \
+       fi; \
+       ${MAKE} ${MFLAGS} CC="${CC}" ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" \
+           OPTOPTS="${OPTOPTS}" DESTDIR="${DESTDIR}" DESDIR="${DESDIR}" \
+           SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+           ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+           TCPWRAPDIR="${TCPWRAPDIR}" DB2DIR="${DB2DIR}" \
+           AFSLIBS="$${AFSLIBS}" AFSLIBDIRS="$${AFSLIBDIRS}" \
+           TCPWRAPLIBS="$${TCPWRAPLIBS}" TCPWRAPLIBDIRS="$${TCPWRAPLIBDIRS}" \
+           DB2LIBS="$${DB2LIBS}" DB2LIBDIRS="$${DB2LIBDIRS}" \
+           LIBSHARED="$${LIBSHARED}" PAMLIBS="$${PAMLIBS}" \
+           PAMLIBDIR="$${PAMLIBDIR}" RPCSVCLIB="$${RPCSVCLIB}" \
+           AFSINCPATH="$${AFSINCPATH}" AFSDEFS="$${AFSDEFS}" \
+           DB2INCPATH="$${DB2INCPATH}" \
+           afpd
+
+${SUBDIRS}: FRC
+       cd $@; ${MAKE} ${MFLAGS} CC="${CC}" DEFS="${DEFS}" \
+           OPTOPTS="${OPTOPTS}" DESTDIR="${DESTDIR}" DESDIR="${DESDIR}" \
+           SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+           ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+           TCPWRAPDIR="${TCPWRAPDIR}" \
+           AFSLIBS="$${AFSLIBS}" AFSLIBDIRS="$${AFSLIBDIRS}" \
+           AFSINCPATH="$${AFSINCPATH}" AFSDEFS="$${AFSDEFS}" 
+
+FRC:
+
+afpd : ${OBJ} ../../libatalk/libatalk.a
+       ${CC} ${CFLAGS} ${LDFLAGS_EXPORT} -o afpd ${OBJ} ${LIBDIRS} ${LIBS}
+
+afp_options.o : afp_options.c
+       ${CC} ${CFLAGS} \
+           -D_PATH_AFPDDEFVOL=\"${ETCDIR}/AppleVolumes.default\" \
+           -D_PATH_AFPDSYSVOL=\"${ETCDIR}/AppleVolumes.system\" \
+           -D_PATH_AFPDPWFILE=\"~/.passwd\" \
+           -D_PATH_AFPDCONF=\"${ETCDIR}/afpd.conf\" \
+           -D_PATH_AFPDUAMPATH=\"${RESDIR}/uams/\" \
+           -D_PATH_AFPDNLSPATH=\"${RESDIR}/nls/\" \
+           ${CPPFLAGS} -c $<
+
+config.o: config.c
+       ${CC} ${CFLAGS} \
+           -DVERSION=\"`cat ../../VERSION`\" \
+           ${CPPFLAGS} -c $< 
+
+install : all
+       ${INSTALL} -c afpd ${SBINDIR}
+       for i in ${SUBDIRS}; \
+       do (cd $$i; ${MAKE} ${MFLAGS} CC="${CC}" \
+               ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+               SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+               ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+               DESTDIR="${DESTDIR}" INSTALL="${INSTALL}" \
+               install); \
+       done
+
+clean :
+       for i in ${SUBDIRS}; \
+       do (cd $$i; ${MAKE} ${MFLAGS} CC="${CC}" \
+               ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+               SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+               ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+               DESTDIR="${DESTDIR}" INSTALL="${INSTALL}" \
+               clean); \
+       done
+       rm -f a.out core* *.o *.bak *[Ee]rrs tags
+       rm -f afpd
+
+tags : ${SRC}
+       cwd=`pwd`; \
+       for i in ${SRC}; do \
+           ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+       done
+
+depend :
+       for i in ${SRC} ; do \
+           ${CC} -M ${DEFS} ${INCPATH} $$i | \
+           awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+               else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+               else rec = rec " " $$2 } } \
+               END { print rec } ' >> makedep; done
+       sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+       cat makedep >> Makefile.tmp
+       rm makedep
+       echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+       echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+       echo '# see make depend above' >> Makefile.tmp
+       rm -f Makefile.bak
+       cp Makefile Makefile.bak
+       mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
diff --git a/etc/afpd/afp_asp.c b/etc/afpd/afp_asp.c
new file mode 100644 (file)
index 0000000..73f46e8
--- /dev/null
@@ -0,0 +1,223 @@
+/* 
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ *
+ * modified from main.c. this handles afp over asp. 
+ */
+
+#ifndef NO_DDP
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <syslog.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include <netatalk/endian.h>
+#include <atalk/atp.h>
+#include <atalk/asp.h>
+#include <atalk/compat.h>
+#include <atalk/util.h>
+
+#include "globals.h"
+#include "switch.h"
+#include "auth.h"
+#include "fork.h"
+
+extern struct oforks   *writtenfork;
+
+static AFPObj *child;
+
+static __inline__ void afp_asp_close(AFPObj *obj)
+{
+    ASP asp = obj->handle;
+    
+    if (obj->logout)
+      (*obj->logout)();
+
+    asp_close( asp );
+    syslog(LOG_INFO, "%.2fKB read, %.2fKB written",
+          asp->read_count / 1024.0, asp->write_count / 1024.0);
+}
+
+static void afp_asp_die(const int sig)
+{
+    ASP asp = child->handle;
+
+    asp_attention(asp, AFPATTN_SHUTDOWN);
+    if ( asp_shutdown( asp ) < 0 ) {
+       syslog( LOG_ERR, "afp_die: asp_shutdown: %m" );
+    }
+
+    afp_asp_close(child);
+    if (sig == SIGTERM || sig == SIGALRM)
+      exit( 0 );
+    else
+      exit(sig);
+}
+
+static void afp_asp_timedown()
+{
+    struct sigaction   sv;
+    struct itimerval   it;
+
+    /* shutdown and don't reconnect. server going down in 5 minutes. */
+    asp_attention(child->handle, AFPATTN_SHUTDOWN | AFPATTN_NORECONNECT |
+                 AFPATTN_TIME(5));
+
+    it.it_interval.tv_sec = 0;
+    it.it_interval.tv_usec = 0;
+    it.it_value.tv_sec = 300;
+    it.it_value.tv_usec = 0;
+    if ( setitimer( ITIMER_REAL, &it, 0 ) < 0 ) {
+       syslog( LOG_ERR, "afp_timedown: setitimer: %m" );
+       afp_asp_die(1);
+    }
+
+    memset(&sv, 0, sizeof(sv));
+    sv.sa_handler = afp_asp_die;
+    sigemptyset( &sv.sa_mask );
+    sv.sa_flags = SA_RESTART;
+    if ( sigaction( SIGALRM, &sv, 0 ) < 0 ) {
+       syslog( LOG_ERR, "afp_timedown: sigaction: %m" );
+       afp_asp_die(1);
+    }
+}
+
+void afp_over_asp(AFPObj *obj)
+{
+    ASP asp;
+    struct sigaction  action;
+    int                func, ccnt = 0, reply = 0;
+
+    obj->exit = afp_asp_die;
+    obj->reply = (int (*)()) asp_cmdreply;
+    obj->attention = (int (*)(void *, AFPUserBytes)) asp_attention;
+    child = obj;
+    asp = (ASP) obj->handle;
+
+    /* install signal handlers */
+    memset(&action, 0, sizeof(action));
+    action.sa_handler = afp_asp_timedown;
+    sigemptyset( &action.sa_mask );
+    action.sa_flags = SA_RESTART;
+    if ( sigaction( SIGHUP, &action, 0 ) < 0 ) {
+       syslog( LOG_ERR, "afp_over_asp: sigaction: %m" );
+       afp_asp_die(1);
+    }
+
+    action.sa_handler = afp_asp_die;
+    sigemptyset( &action.sa_mask );
+    action.sa_flags = SA_RESTART;
+    if ( sigaction( SIGTERM, &action, 0 ) < 0 ) {
+       syslog( LOG_ERR, "afp_over_asp: sigaction: %m" );
+       afp_asp_die(1);
+    }
+
+    syslog( LOG_INFO, "session from %u.%u:%u on %u.%u:%u",
+           ntohs( asp->asp_sat.sat_addr.s_net ),
+           asp->asp_sat.sat_addr.s_node, asp->asp_sat.sat_port,
+           ntohs( atp_sockaddr( asp->asp_atp )->sat_addr.s_net ),
+           atp_sockaddr( asp->asp_atp )->sat_addr.s_node,
+           atp_sockaddr( asp->asp_atp )->sat_port );
+
+    while ((reply = asp_getrequest(asp))) {
+      switch (reply) {
+      case ASPFUNC_CLOSE :
+       afp_asp_close(obj);
+       syslog( LOG_INFO, "done" );
+       if ( obj->options.flags & OPTION_DEBUG ) {
+         printf( "done\n" );
+       }
+       return;
+       break;
+
+      case ASPFUNC_CMD :
+#ifdef AFS
+       if ( writtenfork ) {
+         if ( flushfork( writtenfork ) < 0 ) {
+           syslog( LOG_ERR, "main flushfork: %m" );
+         }
+         writtenfork = NULL;
+       }
+#endif AFS
+       func = (u_char) asp->commands[0];
+       if ( obj->options.flags & OPTION_DEBUG ) {
+         printf( "command: %d\n", func );
+         bprint( asp->commands, asp->cmdlen );
+       }
+       if ( afp_switch[ func ] != NULL ) {
+         /*
+          * The function called from afp_switch is expected to
+          * read its parameters out of buf, put its
+          * results in replybuf (updating rbuflen), and
+          * return an error code.
+                */
+         asp->datalen = ASP_DATASIZ;
+         reply = (*afp_switch[ func ])(obj,
+                                       asp->commands, asp->cmdlen,
+                                       asp->data, &asp->datalen);
+       } else {
+         syslog( LOG_ERR, "bad function %X", func );
+         asp->datalen = 0;
+         reply = AFPERR_NOOP;
+       }
+       if ( obj->options.flags & OPTION_DEBUG ) {
+         printf( "reply: %d, %d\n", reply, ccnt++ );
+         bprint( asp->data, asp->datalen );
+       }
+
+       if ( asp_cmdreply( asp, reply ) < 0 ) {
+         syslog( LOG_ERR, "asp_cmdreply: %m" );
+         afp_asp_die(1);
+       }
+       break;
+
+      case ASPFUNC_WRITE :
+       func = (u_char) asp->commands[0];
+       if ( obj->options.flags & OPTION_DEBUG ) {
+         printf( "(write) command: %d\n", func );
+         bprint( asp->commands, asp->cmdlen );
+       }
+       if ( afp_switch[ func ] != NULL ) {
+         asp->datalen = ASP_DATASIZ;
+         reply = (*afp_switch[ func ])(obj, 
+                                       asp->commands, asp->cmdlen,
+                                       asp->data, &asp->datalen);
+       } else {
+         syslog( LOG_ERR, "(write) bad function %X", func );
+         asp->datalen = 0;
+         reply = AFPERR_NOOP;
+       }
+       if ( obj->options.flags & OPTION_DEBUG ) {
+         printf( "(write) reply code: %d, %d\n", reply, ccnt++ );
+         bprint( asp->data, asp->datalen );
+       }
+       if ( asp_wrtreply( asp, reply ) < 0 ) {
+         syslog( LOG_ERR, "asp_wrtreply: %m" );
+         afp_asp_die(1);
+       }
+       break;
+      default:
+       /*
+          * Bad asp packet.  Probably should have asp filter them,
+          * since they are typically things like out-of-order packet.
+          */
+       syslog( LOG_INFO, "main: asp_getrequest: %d", reply );
+       break;
+      }
+
+      if ( obj->options.flags & OPTION_DEBUG ) {
+#ifdef notdef
+       pdesc( stdout );
+#endif notdef
+       of_pforkdesc( stdout );
+       fflush( stdout );
+      }
+    }
+}
+
+#endif
diff --git a/etc/afpd/afp_dsi.c b/etc/afpd/afp_dsi.c
new file mode 100644 (file)
index 0000000..176921b
--- /dev/null
@@ -0,0 +1,280 @@
+/* 
+ * Copyright (c) 1999 Adrian Sun (asun@zoology.washington.edu)
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ *
+ * modified from main.c. this handles afp over tcp.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <syslog.h>
+
+#include <atalk/dsi.h>
+#include <atalk/compat.h>
+#include <atalk/util.h>
+
+#include "globals.h"
+#include "switch.h"
+#include "auth.h"
+#include "fork.h"
+
+extern struct oforks   *writtenfork;
+
+#define CHILD_DIE         (1 << 0)
+#define CHILD_RUNNING     (1 << 1)
+
+static struct {
+  AFPObj *obj;
+  unsigned char tickle, flags;
+} child;
+
+
+static __inline__ void afp_dsi_close(AFPObj *obj)
+{
+    DSI *dsi = obj->handle;
+
+    if (obj->logout)
+      (*obj->logout)();
+                     
+    dsi_close(dsi);
+    syslog(LOG_INFO, "%.2fKB read, %.2fKB written",
+          dsi->read_count/1024.0, dsi->write_count/1024.0);
+}
+
+/* a little bit of code duplication. */
+static void afp_dsi_die(int sig)
+{
+    dsi_attention(child.obj->handle, AFPATTN_SHUTDOWN);
+    afp_dsi_close(child.obj);
+    if (sig == SIGTERM || sig == SIGALRM)
+      exit( 0 );
+    else
+      exit(sig);
+}
+
+static void afp_dsi_timedown()
+{
+    struct sigaction   sv;
+    struct itimerval   it;
+
+    child.flags |= CHILD_DIE;
+    /* shutdown and don't reconnect. server going down in 5 minutes. */
+    setmessage("The server is going down for maintenance.");
+    dsi_attention(child.obj->handle, AFPATTN_SHUTDOWN | AFPATTN_NORECONNECT |
+                 AFPATTN_MESG | AFPATTN_TIME(5));
+
+    it.it_interval.tv_sec = 0;
+    it.it_interval.tv_usec = 0;
+    it.it_value.tv_sec = 300;
+    it.it_value.tv_usec = 0;
+    if ( setitimer( ITIMER_REAL, &it, 0 ) < 0 ) {
+       syslog( LOG_ERR, "afp_timedown: setitimer: %m" );
+       afp_dsi_die(1);
+    }
+
+    memset(&sv, 0, sizeof(sv));
+    sv.sa_handler = afp_dsi_die;
+    sigemptyset( &sv.sa_mask );
+    sigaddset(&sv.sa_mask, SIGHUP);
+    sigaddset(&sv.sa_mask, SIGTERM);
+    sv.sa_flags = SA_RESTART;
+    if ( sigaction( SIGALRM, &sv, 0 ) < 0 ) {
+       syslog( LOG_ERR, "afp_timedown: sigaction: %m" );
+       afp_dsi_die(1);
+    }
+}
+
+static void alarm_handler()
+{
+  /* if we're in the midst of processing something,
+     don't die. we'll allow 3 missed tickles before we die (2 minutes) */
+  if ((child.flags & CHILD_RUNNING) || (child.tickle++ < 4)) {
+    dsi_tickle(child.obj->handle);
+  } else { /* didn't receive a tickle. close connection */
+    syslog(LOG_ERR, "afp_alarm: child timed out");
+    afp_dsi_die(1);
+  }
+}
+
+/* afp over dsi. this never returns. */
+void afp_over_dsi(AFPObj *obj)
+{
+  DSI *dsi = (DSI *) obj->handle;
+  u_int32_t err, cmd;
+  u_int8_t function;
+  struct sigaction action;
+
+  obj->exit = afp_dsi_die;
+  obj->reply = (int (*)()) dsi_cmdreply;
+  obj->attention = (int (*)(void *, AFPUserBytes)) dsi_attention;
+  
+  child.obj = obj;
+  child.tickle = child.flags = 0;
+
+  /* install SIGTERM and SIGHUP */
+  memset(&action, 0, sizeof(action));
+  action.sa_handler = afp_dsi_timedown;
+  sigemptyset( &action.sa_mask );
+  sigaddset(&action.sa_mask, SIGALRM);
+  sigaddset(&action.sa_mask, SIGTERM);
+  action.sa_flags = SA_RESTART;
+  if ( sigaction( SIGHUP, &action, 0 ) < 0 ) {
+    syslog( LOG_ERR, "afp_over_dsi: sigaction: %m" );
+    afp_dsi_die(1);
+  }
+
+  action.sa_handler = afp_dsi_die;
+  sigemptyset( &action.sa_mask );
+  sigaddset(&action.sa_mask, SIGALRM);
+  sigaddset(&action.sa_mask, SIGHUP);
+  action.sa_flags = SA_RESTART;
+  if ( sigaction( SIGTERM, &action, 0 ) < 0 ) {
+    syslog( LOG_ERR, "afp_over_dsi: sigaction: %m" );
+    afp_dsi_die(1);
+  }
+
+  /* tickle handler */
+  action.sa_handler = alarm_handler;
+  sigemptyset(&action.sa_mask);
+  sigaddset(&action.sa_mask, SIGHUP);
+  sigaddset(&action.sa_mask, SIGTERM);
+  action.sa_flags = SA_RESTART;
+  if ((sigaction(SIGALRM, &action, NULL) < 0) ||
+      (setitimer(ITIMER_REAL, &dsi->timer, NULL) < 0)) {
+    afp_dsi_die(1);
+  }
+
+  /* get stuck here until the end */
+  while ((cmd = dsi_receive(dsi))) {
+    child.tickle = 0;
+
+    if (cmd == DSIFUNC_TICKLE) {
+      /* so we don't get killed on the client side. */
+      if (child.flags & CHILD_DIE) 
+       dsi_tickle(dsi);
+      continue;
+    } else if (!(child.flags & CHILD_DIE)) /* reset tickle timer */
+      setitimer(ITIMER_REAL, &dsi->timer, NULL);
+
+    switch(cmd) {
+    case DSIFUNC_CLOSE:
+      afp_dsi_close(obj);
+      syslog(LOG_INFO, "done");
+      if (obj->options.flags & OPTION_DEBUG ) 
+       printf("done\n");
+      return;
+      break;
+
+    case DSIFUNC_CMD:
+#ifdef AFS
+      if ( writtenfork ) {
+       if ( flushfork( writtenfork ) < 0 ) {
+         syslog( LOG_ERR, "main flushfork: %m" );
+       }
+       writtenfork = NULL;
+      }
+#endif AFS
+
+      function = (u_char) dsi->commands[0];
+      if (obj->options.flags & OPTION_DEBUG ) {
+       printf("command: %d\n", function);
+       bprint(dsi->commands, dsi->cmdlen);
+      }
+
+      /* send off an afp command. in a couple cases, we take advantage
+       * of the fact that we're a stream-based protocol. */
+      if (afp_switch[function]) {
+       dsi->datalen = DSI_DATASIZ;
+       child.flags |= CHILD_RUNNING;
+       err = (*afp_switch[function])(obj,
+                                     dsi->commands, dsi->cmdlen,
+                                     dsi->data, &dsi->datalen);
+       child.flags &= ~CHILD_RUNNING;
+      } else {
+       syslog(LOG_ERR, "bad function %X", function);
+       dsi->datalen = 0;
+       err = AFPERR_NOOP;
+      }
+
+      /* single shot toggle that gets set by dsi_readinit. */
+      if (dsi->noreply) {
+       dsi->noreply = 0;
+       break;
+      }
+
+      if (obj->options.flags & OPTION_DEBUG ) {
+       printf( "reply: %d, %d\n", err, dsi->clientID);
+       bprint(dsi->data, dsi->datalen);
+      }
+
+      if (!dsi_cmdreply(dsi, err)) {
+       syslog(LOG_ERR, "dsi_cmdreply(%d): %m", dsi->socket);
+       afp_dsi_die(1);
+      }
+      break;
+
+    case DSIFUNC_WRITE: /* FPWrite and FPAddIcon */
+      function = (u_char) dsi->commands[0];
+      if ( obj->options.flags & OPTION_DEBUG ) {
+       printf("(write) command: %d, %ld\n", function, dsi->cmdlen);
+       bprint(dsi->commands, dsi->cmdlen);
+      }
+
+      if ( afp_switch[ function ] != NULL ) {
+       dsi->datalen = DSI_DATASIZ;
+       child.flags |= CHILD_RUNNING;
+       err = (*afp_switch[function])(obj, dsi->commands, dsi->cmdlen,
+                                     dsi->data, &dsi->datalen);
+       child.flags &= ~CHILD_RUNNING;
+      } else {
+       syslog( LOG_ERR, "(write) bad function %x", function);
+       dsi->datalen = 0;
+       err = AFPERR_NOOP;
+      }
+
+      if (obj->options.flags & OPTION_DEBUG ) {
+       printf( "(write) reply code: %d, %d\n", err, dsi->clientID);
+       bprint(dsi->data, dsi->datalen);
+      }
+
+      if (!dsi_wrtreply(dsi, err)) {
+       syslog( LOG_ERR, "dsi_wrtreply: %m" );
+       afp_dsi_die(1);
+      }
+      break;
+
+    case DSIFUNC_ATTN: /* attention replies */
+      continue;
+      break;
+
+      /* error. this usually implies a mismatch of some kind
+       * between server and client. if things are correct,
+       * we need to flush the rest of the packet if necessary. */
+    default: 
+      syslog(LOG_INFO,"afp_dsi: spurious command %d", cmd);
+      dsi_writeinit(dsi, dsi->data, DSI_DATASIZ);
+      dsi_writeflush(dsi);
+      break;
+    }
+        
+    if ( obj->options.flags & OPTION_DEBUG ) {
+#ifdef notdef
+      pdesc( stdout );
+#endif notdef
+      of_pforkdesc( stdout );
+      fflush( stdout );
+    }
+  }
+
+  /* error */
+  afp_dsi_die(1);
+}
diff --git a/etc/afpd/afp_options.c b/etc/afpd/afp_options.c
new file mode 100644 (file)
index 0000000..13ccf7f
--- /dev/null
@@ -0,0 +1,331 @@
+/* 
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ *
+ * modified from main.c. this handles afp options.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <syslog.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <atalk/paths.h>
+#include "globals.h"
+#include "status.h"
+#include "auth.h"
+
+#include <atalk/compat.h>
+
+#ifndef MIN
+#define MIN(a, b)  ((a) < (b) ? (a) : (b))
+#endif
+
+#define OPTIONS "dn:f:s:uc:g:P:ptDS:TL:F:U:I"
+#define LENGTH 512
+
+/* return an option. this uses an internal array, so it's necessary
+ * to duplicate it if you want to hold it for long. this is probably
+ * non-optimal. */
+static char *getoption(char *buf, const char *option)
+{
+  static char string[LENGTH + 1];
+  char *end;
+  int len;
+
+  if (option && (buf = strstr(buf, option)))
+    buf = strpbrk(buf, " \t");
+
+  while (buf && isspace(*buf))
+    buf++;
+      
+  if (!buf)
+    return NULL;
+
+  /* search for any quoted stuff */
+  if (*buf == '"' && (end = strchr(buf + 1, '"'))) { 
+    buf++;
+    len = MIN(end - buf, LENGTH);
+  } else if ((end = strpbrk(buf, " \t\n"))) /* option or eoln */
+    len = MIN(end - buf, LENGTH);
+  else
+    len = MIN(strlen(buf), LENGTH);
+  
+  strncpy(string, buf, len);
+  string[len] = '\0';
+  return string;
+}
+
+/* get rid of any allocated afp_option buffers. */
+void afp_options_free(struct afp_options *opt, 
+                     const struct afp_options *save)
+{
+  if (opt->defaultvol && (opt->defaultvol != save->defaultvol))
+    free(opt->defaultvol);
+  if (opt->systemvol && (opt->systemvol != save->systemvol))
+    free(opt->systemvol);
+  if (opt->loginmesg && (opt->loginmesg != save->loginmesg))
+    free(opt->loginmesg);
+  if (opt->guest && (opt->guest != save->guest)) 
+    free(opt->guest);
+  if (opt->server && (opt->server != save->server))
+    free(opt->server);
+  if (opt->ipaddr && (opt->ipaddr != save->ipaddr))
+    free(opt->ipaddr);
+  if (opt->fqdn && (opt->fqdn != save->fqdn))
+    free(opt->fqdn);
+  if (opt->uampath && (opt->uampath != save->uampath))
+    free(opt->uampath);
+  if (opt->uamlist && (opt->uamlist != save->uamlist))
+    free(opt->uamlist);
+  if (opt->nlspath && (opt->nlspath != save->nlspath))
+    free(opt->nlspath);
+  if (opt->passwdfile && (opt->passwdfile != save->passwdfile))
+    free(opt->passwdfile);
+}
+
+/* initialize options */
+void afp_options_init(struct afp_options *options)
+{
+  memset(options, 0, sizeof(struct afp_options));
+  options->connections = 20;
+  options->pidfile = _PATH_AFPDLOCK;
+  options->defaultvol = _PATH_AFPDDEFVOL;
+  options->systemvol = _PATH_AFPDSYSVOL;
+  options->configfile = _PATH_AFPDCONF;
+  options->nlspath = _PATH_AFPDNLSPATH;
+  options->uampath = _PATH_AFPDUAMPATH;
+  options->uamlist = "uams_guest.so,uams_clrtxt.so,uams_dhx.so";
+  options->guest = "nobody";
+  options->loginmesg = "";
+  options->transports = AFPTRANS_ALL;
+  options->passwdfile = _PATH_AFPDPWFILE;
+  options->tickleval = 30;
+}
+
+/* parse an afpd.conf line. i'm doing it this way because it's
+ * easy. it is, however, massively hokey. sample afpd.conf:
+ * server:AFPServer@zone -loginmesg "blah blah blah" -nodsi 
+ * "private machine"@zone2 -noguest -port 11012
+ * server2 -nocleartxt -nodsi
+ *
+ * NOTE: this ignores unknown options 
+ */
+int afp_options_parseline(char *buf, struct afp_options *options)
+{
+  char *c, *opt;
+  
+  /* handle server */
+  if (*buf != '-' && (c = getoption(buf, NULL)) && (opt = strdup(c)))
+    options->server = opt;
+
+  /* parse toggles */
+  if (strstr(buf, " -nodebug"))
+    options->flags &= ~OPTION_DEBUG;
+
+  if (strstr(buf, " -nouservolfirst"))
+    options->flags &= ~OPTION_USERVOLFIRST;
+  if (strstr(buf, " -uservolfirst"))
+    options->flags |= OPTION_USERVOLFIRST;
+  if (strstr(buf, " -nouservol"))
+    options->flags |= OPTION_NOUSERVOL;
+  if (strstr(buf, " -uservol"))
+    options->flags &= ~OPTION_NOUSERVOL;
+  if (strstr(buf, " -proxy"))
+    options->flags |= OPTION_PROXY;
+  if (strstr(buf, " -noicon"))
+    options->flags &= ~OPTION_CUSTOMICON;
+  if (strstr(buf, " -icon"))
+    options->flags |= OPTION_CUSTOMICON;
+
+  /* passwd bits */
+  if (strstr(buf, " -nosavepassword"))
+    options->passwdbits |= PASSWD_NOSAVE;
+  if (strstr(buf, " -savepassword"))
+    options->passwdbits &= ~PASSWD_NOSAVE;
+  if (strstr(buf, " -nosetpassword"))
+    options->passwdbits &= ~PASSWD_SET;
+  if (strstr(buf, " -setpassword"))
+    options->passwdbits |= PASSWD_SET;
+  
+  /* transports */
+  if (strstr(buf, " -transall"))
+    options->transports = AFPTRANS_ALL;
+  if (strstr(buf, " -notransall"))
+    options->transports = AFPTRANS_NONE;
+  if (strstr(buf, " -tcp"))
+    options->transports |= AFPTRANS_TCP;
+  if (strstr(buf, " -notcp"))
+    options->transports &= ~AFPTRANS_TCP;
+  if (strstr(buf, " -ddp"))
+    options->transports |= AFPTRANS_DDP;
+  if (strstr(buf, " -noddp"))
+    options->transports &= ~AFPTRANS_DDP;
+
+  /* figure out options w/ values. currently, this will ignore the setting
+   * if memory is lacking. */
+  if ((c = getoption(buf, "-defaultvol")) && (opt = strdup(c)))
+    options->defaultvol = opt;
+  if ((c = getoption(buf, "-systemvol")) && (opt = strdup(c)))
+    options->systemvol = opt;
+  if ((c = getoption(buf, "-loginmesg")) && (opt = strdup(c)))
+    options->loginmesg = opt;
+  if ((c = getoption(buf, "-guestname")) && (opt = strdup(c)))
+    options->guest = opt;
+  if ((c = getoption(buf, "-passwdfile")) && (opt = strdup(c)))
+    options->passwdfile = opt;
+  if ((c = getoption(buf, "-passwdminlen")))
+    options->passwdminlen = MIN(1, atoi(c));
+  if ((c = getoption(buf, "-loginmaxfail")))
+    options->loginmaxfail = atoi(c);
+  if ((c = getoption(buf, "-tickleval")))
+    options->tickleval = atoi(c);
+
+  if (c = getoption(buf, "-server_quantum"))
+    options->server_quantum = strtoul(c, NULL, 0);
+
+
+  if ((c = getoption(buf, "-uampath")) && (opt = strdup(c)))
+    options->uampath = opt;
+  if ((c = getoption(buf, "-uamlist")) && (opt = strdup(c)))
+    options->uamlist = opt;
+  if ((c = getoption(buf, "-nlspath")) && (opt = strdup(c)))
+    options->nlspath = opt;
+
+  if (c = getoption(buf, "-ipaddr")) {
+    struct in_addr inaddr;
+    if (inet_aton(c, &inaddr) && (opt = strdup(c))) { 
+      if (!gethostbyaddr((const char *) &inaddr, sizeof(inaddr), AF_INET)) 
+       syslog(LOG_INFO, "WARNING: can't find %s\n", opt);
+      options->ipaddr = opt;
+    }
+  }
+
+  if ((c = getoption(buf, "-port")))
+    options->port = atoi(c);
+  if (c = getoption(buf, "-ddpaddr"))
+    atalk_aton(c, &options->ddpaddr);
+
+  /* do a little checking for the domain name. */
+  if (c = getoption(buf, "-fqdn")) {
+    char *p = strchr(c, ':');
+    if (p)
+      *p = '\0';
+    if (gethostbyname(c)) {
+      if (p)
+       *p = ':';
+      if (opt = strdup(c))
+       options->fqdn = opt;
+    }
+  }
+
+  return 1;
+}
+
+int afp_options_parse(int ac, char **av, struct afp_options *options)
+{
+  extern char *optarg;
+  extern int optind;
+  
+  char *p;
+  int c, err = 0;
+
+  if (gethostname(options->hostname, sizeof(options->hostname )) < 0 ) {
+    perror( "gethostname" );
+    return 0;
+  }
+    if (( p = strchr(options->hostname, '.' )) != 0 ) {
+       *p = '\0';
+    }
+
+    if (( p = strrchr( av[ 0 ], '/' )) == NULL ) {
+       p = av[ 0 ];
+    } else {
+       p++;
+    }
+
+    while (( c = getopt( ac, av, OPTIONS )) != EOF ) {
+       switch ( c ) {
+       case 'd' :
+           options->flags |= OPTION_DEBUG;
+           break;
+       case 'n' :
+           options->server = optarg;
+           break;
+       case 'f' :
+           options->defaultvol = optarg;
+           break;
+       case 's' :
+           options->systemvol = optarg;
+           break;
+       case 'u' :
+           options->flags |= OPTION_USERVOLFIRST;
+           break;
+       case 'c' :
+           options->connections = atoi( optarg );
+           break;
+       case 'g' :
+           options->guest = optarg;
+           break;
+
+       case 'P' :
+           options->pidfile = optarg;
+           break;
+
+       case 'p':
+           options->passwdbits |= PASSWD_NOSAVE;
+           break;
+       case 't':
+           options->passwdbits |= PASSWD_SET;
+           break;
+
+       case 'D':
+           options->transports &= ~AFPTRANS_DDP;
+           break;
+       case 'S':
+           options->port = atoi(optarg);
+           break;
+       case 'T':
+           options->transports &= ~AFPTRANS_TCP;
+           break;
+       case 'L':
+           options->loginmesg = optarg;
+           break;
+       case 'F':
+           options->configfile = optarg;
+           break;
+       case 'U':
+           options->uamlist = optarg;
+           break;
+       case 'I':
+           options->flags |= OPTION_CUSTOMICON;
+       default :
+           err++;
+       }
+    }
+    if ( err || optind != ac ) {
+       fprintf( stderr,
+               "Usage:\t%s [ -dpDTIt ] [ -n nbpname ] [ -f defvols ] \
+[ -P pidfile ] [ -s sysvols ] \n", p );
+       fprintf( stderr,
+               "\t[ -u ] [ -c maxconn ] [ -g guest ] \
+[ -S port ] [ -L loginmesg ] [ -F configfile ] [ -U uamlist ]\n" );
+       return 0;
+    }
+
+#ifdef ultrix
+    openlog( p, LOG_PID );
+#else ultrix
+    openlog( p, LOG_NDELAY|LOG_PID, LOG_DAEMON );
+#endif ultrix
+
+    return 1;
+}
diff --git a/etc/afpd/afs.c b/etc/afpd/afs.c
new file mode 100644 (file)
index 0000000..82f35d9
--- /dev/null
@@ -0,0 +1,338 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#ifdef AFS
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/syslog.h>
+#include <netatalk/endian.h>
+#include <netinet/in.h>
+#include <afs/venus.h>
+#include <afs/afsint.h>
+#include <atalk/afp.h>
+#include <unistd.h>
+
+#include "globals.h"
+#include "directory.h"
+#include "volume.h"
+#include "misc.h"
+
+afs_getvolspace( vol, bfree, btotal, bsize )
+    struct vol *vol;
+    VolSpace   *bfree, *btotal;
+    u_int32_t   *bsize;
+{
+    struct ViceIoctl   vi;
+    struct VolumeStatus        *vs;
+    char               venuspace[ sizeof( struct VolumeStatus ) + 3 ];
+    int                        total, free;
+
+    vi.in_size = 0;
+    vi.out_size = sizeof( venuspace );
+    vi.out = venuspace;
+    if ( pioctl( vol->v_path, VIOCGETVOLSTAT, &vi, 1 ) < 0 ) {
+       return( AFPERR_PARAM );
+    }
+
+    vs = (struct VolumeStatus *)venuspace;
+
+    if ( vs->PartBlocksAvail > 0 ) {
+       if ( vs->MaxQuota != 0 ) {
+#define min(x,y)       (((x)<(y))?(x):(y))
+           free = min( vs->MaxQuota - vs->BlocksInUse, vs->PartBlocksAvail );
+       } else {
+           free = vs->PartBlocksAvail;
+       }
+    } else {
+       free = 0;
+    }
+
+    if ( vs->MaxQuota != 0 ) {
+       total = free + vs->BlocksInUse;
+    } else {
+       total = vs->PartMaxBlocks;
+    }
+
+    *bsize = 1024;
+    *bfree = (VolSpace) free * 1024;
+    *btotal = (VolSpace) total * 1024;
+
+    return( AFP_OK );
+}
+
+afp_getdiracl(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct ViceIoctl   vi;
+    struct vol         *vol;
+    struct dir         *dir;
+    char               *path;
+    u_int32_t          did;
+    u_int16_t          vid;
+
+    ibuf += 2;
+    memcpy( &vid, ibuf, sizeof( vid ));
+    ibuf += sizeof( short );
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       *rbuflen = 0;
+       return( AFPERR_PARAM );
+    }
+
+    memcpy( &did, ibuf, sizeof( did ));
+    ibuf += sizeof( int );
+    if (( dir = dirsearch( vol, did )) == NULL ) {
+       *rbuflen = 0;
+       return( AFPERR_NOOBJ );
+    }
+
+    if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+       *rbuflen = 0;
+       return( AFPERR_NOOBJ );
+    }
+    if ( *path != '\0' ) {
+       *rbuflen = 0;
+       return( AFPERR_BITMAP );
+    }
+
+    vi.in_size = 0;
+    vi.out_size = *rbuflen;
+    vi.out = rbuf;
+    if ( pioctl( ".", VIOCGETAL, &vi, 1 ) < 0 ) {
+       *rbuflen = 0;
+       return( AFPERR_PARAM );
+    }
+    *rbuflen = strlen( vi.out ) + 1;
+    return( AFP_OK );
+}
+
+/*
+ * Calculate the mode for a directory in AFS.  First, make sure the
+ * directory is in AFS.  Could probably use something less heavy than
+ * VIOCGETAL.  If the directory is on AFS, use access() calls to
+ * estimate permission, a la mdw.
+ */
+afsmode( path, ma, dir )
+    char               *path;
+    struct maccess     *ma;
+    struct dir         *dir;
+{
+    struct ViceIoctl   vi;
+    char               buf[ 1024 ];
+
+    if (( dir->d_flags & DIRF_FSMASK ) == DIRF_NOFS ) {
+       vi.in_size = 0;
+       vi.out_size = sizeof( buf );
+       vi.out = buf;
+       if ( pioctl( path, VIOCGETAL, &vi, 1 ) < 0 ) {
+           dir->d_flags |= DIRF_UFS;
+       } else {
+           dir->d_flags |= DIRF_AFS;
+       }
+    }
+
+    if (( dir->d_flags & DIRF_FSMASK ) != DIRF_AFS ) {
+       return;
+    }
+
+    if ( access( path, R_OK|W_OK|X_OK ) == 0 ) {
+       ma->ma_user = AR_UREAD|AR_UWRITE|AR_USEARCH|AR_UOWN;
+       ma->ma_owner = AR_UREAD|AR_UWRITE|AR_USEARCH;
+    } else if ( access( path, R_OK|X_OK ) == 0 ) {
+       ma->ma_user = AR_UREAD|AR_USEARCH;
+       ma->ma_owner = AR_UREAD|AR_USEARCH;
+    } else {
+       ma->ma_user = ma->ma_owner = 0;
+       if ( access( path, R_OK ) == 0 ) {
+           ma->ma_user |= AR_UREAD;
+           ma->ma_owner |= AR_UREAD;
+       }
+       if ( access( path, X_OK ) == 0 ) {
+           ma->ma_user |= AR_USEARCH;
+           ma->ma_owner |= AR_USEARCH;
+       }
+       if ( access( path, W_OK ) == 0 ) {
+           ma->ma_user |= AR_UWRITE|AR_UOWN;
+           ma->ma_owner |= AR_UWRITE;
+       }
+    }
+
+    return;
+}
+
+extern struct dir      *curdir;
+/*
+ * cmd | 0 | vid | did | pathtype | pathname | 0 | acl
+ */
+afp_setdiracl(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct ViceIoctl   vi;
+    struct vol         *vol;
+    struct dir         *dir;
+    char               *path, *iend;
+    u_int32_t          did;
+    u_int16_t          vid;
+
+    *rbuflen = 0;
+    iend = ibuf + ibuflen;
+    ibuf += 2;
+    memcpy( &vid, ibuf, sizeof( vid ));
+    ibuf += sizeof( short );
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       *rbuflen = 0;
+       return( AFPERR_PARAM );
+    }
+
+    memcpy( &did, ibuf, sizeof( did ));
+    ibuf += sizeof( int );
+    if (( dir = dirsearch( vol, did )) == NULL ) {
+       *rbuflen = 0;
+       return( AFPERR_NOOBJ );
+    }
+
+    if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+       *rbuflen = 0;
+       return( AFPERR_NOOBJ );
+    }
+    if ( *path != '\0' ) {
+       *rbuflen = 0;
+       return( AFPERR_BITMAP );
+    }
+
+    if ((int)ibuf & 1 ) {
+       ibuf++;
+    }
+
+    vi.in_size = iend - ibuf;
+    vi.in = ibuf;
+    vi.out_size = 0;
+
+    if ( pioctl( ".", VIOCSETAL, &vi, 1 ) < 0 ) {
+       *rbuflen = 0;
+       return( AFPERR_PARAM );
+    }
+    pioctl( ".AppleDouble", VIOCSETAL, &vi, 1 );
+    if ( curdir->d_did  == DIRDID_ROOT ) {
+       pioctl( ".AppleDesktop", VIOCSETAL, &vi, 1 );
+    }
+
+    return( AFP_OK );
+}
+
+
+#ifdef UAM_AFSKRB
+
+#include <krb.h>
+#include <des.h>
+#include <afs/kauth.h>
+#include <afs/kautils.h>
+
+extern C_Block         seskey;
+extern Key_schedule    seskeysched;
+
+afp_afschangepw(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    char       name[ MAXKTCNAMELEN ], instance[ MAXKTCNAMELEN ];
+    char       realm[ MAXKTCREALMLEN ];
+    char       oldpw[ 9 ], newpw[ 9 ];
+    int                len, rc;
+    u_int16_t  clen;
+    struct ktc_encryptionKey   oldkey, newkey;
+    struct ktc_token           adtok;
+    struct ubik_client         *conn;
+
+    *rbuflen = 0;
+    ++ibuf;
+    len = (unsigned char) *ibuf++;
+    ibuf[ len ] = '\0';
+    *name = *instance = *realm = '\0';
+    ka_ParseLoginName( ibuf, name, instance, realm );
+    ucase( realm );
+    if ( *realm == '\0' ) {
+       if ( krb_get_lrealm( realm, 1 ) != KSUCCESS ) {
+           syslog( LOG_ERR, "krb_get_lrealm failed" );
+           return( AFPERR_BADUAM );
+       }
+    }
+
+    if ( strlen( name ) < 2 || strlen( name ) > 18 ) {
+       return( AFPERR_PARAM );
+    }
+    ibuf += len;
+
+    memcpy( &clen, ibuf, sizeof( clen ));
+    clen = ntohs( clen );
+    if ( clen % 8 != 0 ) {
+       return( AFPERR_PARAM );
+    }
+
+    ibuf += sizeof( short );
+    pcbc_encrypt((C_Block *)ibuf, (C_Block *)ibuf,
+           clen, seskeysched, seskey, DES_DECRYPT );
+
+    len = (unsigned char) *ibuf++;
+    if ( len > 8 ) {
+       return( AFPERR_PARAM );
+    }
+    memset( oldpw, 0, sizeof( oldpw ));
+    memcpy( oldpw, ibuf, len );
+    ibuf += len;
+    oldpw[ len ] = '\0';
+
+    len = (unsigned char) *ibuf++;
+    if ( len > 8 ) {
+       return( AFPERR_PARAM );
+    }
+    memset( newpw, 0, sizeof( newpw ));
+    memcpy( newpw, ibuf, len );
+    ibuf += len;
+    newpw[ len ] = '\0';
+
+    syslog( LOG_INFO,
+       "changing password for <%s>.<%s>@<%s>", name, instance, realm );
+
+    ka_StringToKey( oldpw, realm, &oldkey );
+    memset( oldpw, 0, sizeof( oldpw ));
+    ka_StringToKey( newpw, realm, &newkey );
+    memset( newpw, 0, sizeof( newpw ));
+
+    rc = ka_GetAdminToken( name, instance, realm, &oldkey, 60, &adtok, 0 );
+    memset( &oldkey, 0, sizeof( oldkey ));
+    switch ( rc ) {
+       case 0:
+           break;
+       case KABADREQUEST:
+           memset( &newkey, 0, sizeof( newkey ));
+           return( AFPERR_NOTAUTH );
+       default:
+           memset( &newkey, 0, sizeof( newkey ));
+           return( AFPERR_BADUAM );
+    }
+    if ( ka_AuthServerConn( realm, KA_MAINTENANCE_SERVICE, &adtok, &conn )
+               != 0 ) {
+       memset( &newkey, 0, sizeof( newkey ));
+       return( AFPERR_BADUAM );
+    }
+
+    rc = ka_ChangePassword( name, instance, conn, 0, &newkey );
+    memset( &newkey, 0, sizeof( newkey ));
+    if ( rc != 0 ) {
+       return( AFPERR_BADUAM );
+    }
+
+    syslog( LOG_DEBUG, "password changed succeeded" );
+    return( AFP_OK );
+}
+
+#endif UAM_AFSKRB
+#endif AFS
diff --git a/etc/afpd/appl.c b/etc/afpd/appl.c
new file mode 100644 (file)
index 0000000..dcefdaf
--- /dev/null
@@ -0,0 +1,452 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/syslog.h>
+#include <errno.h>
+
+#include <netatalk/endian.h>
+#include <atalk/adouble.h>
+#include <atalk/afp.h>
+
+#include "volume.h"
+#include "globals.h"
+#include "directory.h"
+#include "file.h"
+#include "desktop.h"
+
+static struct savedt   sa = { { 0, 0, 0, 0 }, -1, 0 };
+
+static __inline__ int pathcmp( p, plen, q, qlen )
+    char       *p;
+    int        plen;
+    char       *q;
+    int        qlen;
+{
+    return (( plen == qlen && memcmp( p, q, plen ) == 0 ) ? 0 : 1 );
+}
+
+static int applopen( vol, creator, flags, mode )
+    struct vol *vol;
+    u_char     creator[ 4 ];
+{
+    char       *dtf, *adt, *adts;
+
+    if ( sa.sdt_fd != -1 ) {
+       if ( !(flags & ( O_RDWR | O_WRONLY )) &&
+               memcmp( sa.sdt_creator, creator, sizeof( CreatorType )) == 0 &&
+               sa.sdt_vid == vol->v_vid ) {
+           return( AFP_OK );
+       }
+       close( sa.sdt_fd );
+       sa.sdt_fd = -1;
+    }
+
+    dtf = dtfile( vol, creator, ".appl" );
+
+    if (( sa.sdt_fd = open( dtf, flags, ad_mode( dtf, mode ))) < 0 ) {
+       if ( errno == ENOENT && ( flags & O_CREAT )) {
+           if (( adts = strrchr( dtf, '/' )) == NULL ) {
+               return( AFPERR_PARAM );
+           }
+           *adts = '\0';
+           if (( adt = strrchr( dtf, '/' )) == NULL ) {
+               return( AFPERR_PARAM );
+           }
+           *adt = '\0';
+           (void) ad_mkdir( dtf, DIRBITS | 0777 );
+           *adt = '/';
+           (void) ad_mkdir( dtf, DIRBITS | 0777 );
+           *adts = '/';
+
+           if (( sa.sdt_fd = open( dtf, flags, ad_mode( dtf, mode ))) < 0 ) {
+               return( AFPERR_PARAM );
+           }
+       } else {
+           return( AFPERR_PARAM );
+       }
+    }
+    memcpy( sa.sdt_creator, creator, sizeof( CreatorType ));
+    sa.sdt_vid = vol->v_vid;
+    sa.sdt_index = 0;
+    return( AFP_OK );
+}
+
+/*
+ * copy appls to new file, deleting any matching (old) appl entries
+ */
+static int copyapplfile( sfd, dfd, mpath, mplen )
+    int                sfd;
+    int                dfd;
+    char       *mpath;
+    u_short    mplen;
+{
+    int                cc;
+    char       *p;
+    u_int16_t  len;
+    u_char     appltag[ 4 ];
+    char       buf[ MAXPATHLEN ];
+
+    while (( cc = read( sfd, buf, sizeof(appltag) + sizeof( u_short ))) > 0 ) {
+       p = buf + sizeof(appltag);
+       memcpy( &len, p, sizeof(len));
+       len = ntohs( len );
+       p += sizeof( len );
+       if (( cc = read( sa.sdt_fd, p, len )) < len ) {
+           break;
+       }
+       if ( pathcmp( mpath, mplen, p, len ) != 0 ) {
+           p += len;
+           if ( write( dfd, buf, p - buf ) != p - buf ) {
+               cc = -1;
+               break;
+           }
+       }
+    }
+    return( cc );
+}
+
+/*
+ * build mac. path (backwards) by traversing the directory tree
+ *
+ * The old way: dir and path refer to an app, path is a mac format
+ * pathname.  makemacpath() builds something that looks like a cname,
+ * but uses upaths instead of mac format paths.
+ *
+ * The new way: dir and path refer to an app, path is a mac format
+ * pathname.  makemacpath() builds a cname.
+ *
+ * See afp_getappl() for the backward compatiblity code.
+ */
+static char *
+makemacpath( mpath, mpathlen, dir, path )
+    char       *mpath;
+    int                mpathlen;
+    struct dir *dir;
+    char       *path;
+{
+    char       *p;
+
+    p = mpath + mpathlen;
+    p -= strlen( path );
+    strncpy( p, path, strlen( path ));
+
+    while ( dir->d_parent != NULL ) {
+       p -= strlen( dir->d_name ) + 1;
+       strcpy( p, dir->d_name );
+       dir = dir->d_parent;
+    }
+    return( p ); 
+}
+
+
+int afp_addappl(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct vol         *vol;
+    struct dir         *dir;
+    int                        tfd, cc; 
+    u_int32_t           did;
+    u_int16_t          vid, mplen;
+    char               *path, *dtf, *p, *mp;
+    u_char             creator[ 4 ];
+    u_char             appltag[ 4 ];
+    char               *mpath, *tempfile;
+
+    *rbuflen = 0;
+    ibuf += 2;
+
+    memcpy( &vid, ibuf, sizeof( vid ));
+    ibuf += sizeof( vid );
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    memcpy( &did, ibuf, sizeof( did ));
+    ibuf += sizeof( did );
+    if (( dir = dirsearch( vol, did )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    memcpy( creator, ibuf, sizeof( creator ));
+    ibuf += sizeof( creator );
+
+    memcpy( appltag, ibuf, sizeof( appltag ));
+    ibuf += sizeof( appltag );
+
+    if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+    if ( *path == '\0' ) {
+       return( AFPERR_BADTYPE );
+    }
+
+    if ( applopen( vol, creator, O_RDWR|O_CREAT, 0666 ) != AFP_OK ) {
+       return( AFPERR_PARAM );
+    }
+    if ( lseek( sa.sdt_fd, 0L, SEEK_SET ) < 0 ) {
+       return( AFPERR_PARAM );
+    }
+    dtf = dtfile( vol, creator, ".appl.temp" );
+    tempfile = obj->oldtmp;
+    strcpy( tempfile, dtf );
+    if (( tfd = open( tempfile, O_RDWR|O_CREAT, 0666 )) < 0 ) {
+       return( AFPERR_PARAM );
+    }
+    mpath = obj->newtmp;
+    mp = makemacpath( mpath, AFPOBJ_TMPSIZ, curdir, path );
+    mplen =  mpath + AFPOBJ_TMPSIZ - mp;
+
+    /* write the new appl entry at start of temporary file */
+    p = mp - sizeof( u_short );
+    mplen = htons( mplen );
+    memcpy( p, &mplen, sizeof( mplen ));
+    mplen = ntohs( mplen );
+    p -= sizeof( appltag );
+    memcpy(p, appltag, sizeof( appltag ));
+    cc = mpath + AFPOBJ_TMPSIZ - p;
+    if ( write( tfd, p, cc ) != cc ) {
+       unlink( tempfile );
+       return( AFPERR_PARAM );
+    }
+    cc = copyapplfile( sa.sdt_fd, tfd, mp, mplen );
+    close( tfd );
+    close( sa.sdt_fd );
+    sa.sdt_fd = -1;
+
+    if ( cc < 0 ) {
+       unlink( tempfile );
+       return( AFPERR_PARAM );
+    }
+    if ( rename( tempfile, dtfile( vol, creator, ".appl" )) < 0 ) {
+       return( AFPERR_PARAM );
+    }
+    return( AFP_OK );
+}
+
+int afp_rmvappl(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct vol         *vol;
+    struct dir         *dir;
+    int                        tfd, cc;
+    u_int32_t           did;
+    u_int16_t          vid, mplen;
+    char               *path, *dtf, *mp;
+    u_char             creator[ 4 ];
+    char                *tempfile, *mpath;
+
+    *rbuflen = 0;
+    ibuf += 2;
+
+    memcpy( &vid, ibuf, sizeof( vid ));
+    ibuf += sizeof( vid );
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    memcpy( &did, ibuf, sizeof( did ));
+    ibuf += sizeof( did );
+    if (( dir = dirsearch( vol, did )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    memcpy( creator, ibuf, sizeof( creator ));
+    ibuf += sizeof( creator );
+
+    if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+    if ( *path == '.' ) {
+       return( AFPERR_BADTYPE );
+    }
+
+    if ( applopen( vol, creator, O_RDWR, 0666 ) != AFP_OK ) {
+       return( AFPERR_NOOBJ );
+    }
+    if ( lseek( sa.sdt_fd, 0L, SEEK_SET ) < 0 ) {
+       return( AFPERR_PARAM );
+    }
+    dtf = dtfile( vol, creator, ".appl.temp" );
+    tempfile = obj->oldtmp;
+    strcpy( tempfile, dtf );
+    if (( tfd = open( tempfile, O_RDWR|O_CREAT, 0666 )) < 0 ) {
+       return( AFPERR_PARAM );
+    }
+    mpath = obj->newtmp;
+    mp = makemacpath( mpath, AFPOBJ_TMPSIZ, curdir, path );
+    mplen =  mpath + AFPOBJ_TMPSIZ - mp;
+    cc = copyapplfile( sa.sdt_fd, tfd, mp, mplen );
+    close( tfd );
+    close( sa.sdt_fd );
+    sa.sdt_fd = -1;
+
+    if ( cc < 0 ) {
+       unlink( tempfile );
+       return( AFPERR_PARAM );
+    }
+    if ( rename( tempfile, dtfile( vol, creator, ".appl" )) < 0 ) {
+       return( AFPERR_PARAM );
+    }
+    return( AFP_OK );
+}
+
+int afp_getappl(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct stat                st;
+    struct vol         *vol;
+    char               *p, *q;
+    int                        cc, buflen;
+    u_int16_t          vid, aindex, bitmap, len;
+    u_char             creator[ 4 ];
+    u_char             appltag[ 4 ];
+    char                *buf, *cbuf;
+
+    ibuf += 2;
+
+    memcpy( &vid, ibuf, sizeof( vid ));
+    ibuf += sizeof( vid );
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       *rbuflen = 0;
+       return( AFPERR_PARAM );
+    }
+
+    memcpy( creator, ibuf, sizeof( creator ));
+    ibuf += sizeof( creator );
+
+    memcpy( &aindex, ibuf, sizeof( aindex ));
+    ibuf += sizeof( aindex );
+    aindex = ntohs( aindex );
+    if ( aindex ) { /* index 0 == index 1 */
+       --aindex;
+    }
+    
+    memcpy( &bitmap, ibuf, sizeof( bitmap ));
+    bitmap = ntohs( bitmap );
+    ibuf += sizeof( bitmap );
+
+    if ( applopen( vol, creator, O_RDONLY, 0666 ) != AFP_OK ) {
+       *rbuflen = 0;
+       return( AFPERR_NOITEM );
+    }
+    if ( aindex < sa.sdt_index ) {
+       if ( lseek( sa.sdt_fd, 0L, SEEK_SET ) < 0 ) {
+           *rbuflen = 0;
+           return( AFPERR_PARAM );
+       }
+       sa.sdt_index = 0;
+    }
+
+    /* position to correct spot within appl file */
+    buf = obj->oldtmp;
+    while (( cc = read( sa.sdt_fd, buf, sizeof( appltag )
+           + sizeof( u_short ))) > 0 ) {
+       p = buf + sizeof( appltag );
+       memcpy( &len, p, sizeof( len ));
+       len = ntohs( len );
+       p += sizeof( u_short );
+       if (( cc = read( sa.sdt_fd, p, len )) < len ) {
+           break;
+       }
+       if ( sa.sdt_index == aindex ) {
+           break;
+       }
+       sa.sdt_index++;
+    }
+    if ( cc <= 0 || sa.sdt_index != aindex ) {
+       *rbuflen = 0;
+       return( AFPERR_NOITEM );
+    }
+    sa.sdt_index++;
+
+#ifdef APPLCNAME
+    /*
+     * Check to see if this APPL mapping has an mpath or a upath.  If
+     * there are any ':'s in the name, it is a upath and must be converted
+     * to an mpath.  Hopefully, this code will go away.
+     */
+    {
+#define hextoint( c )  ( isdigit( c ) ? c - '0' : c + 10 - 'a' )
+#define islxdigit(x)   (!isupper(x)&&isxdigit(x))
+
+       char    utomname[ MAXPATHLEN + 1];
+       char            *u, *m;
+       int             i, h;
+
+       u = p;
+       m = utomname;
+       i = len;
+       while ( i ) {
+           if ( *u == ':' && *(u+1) != '\0' && islxdigit( *(u+1)) &&
+                   *(u+2) != '\0' && islxdigit( *(u+2))) {
+               ++u, --i;
+               h = hextoint( *u ) << 4;
+               ++u, --i;
+               h |= hextoint( *u );
+               *m++ = h;
+           } else {
+               *m++ = *u;
+           }
+           ++u, --i;
+       }
+
+       len = m - utomname;
+       p = utomname;
+
+       if ( p[ len - 1 ] == '\0' ) {
+           len--;
+       }
+    }
+#endif APPLCNAME
+
+    /* fake up a cname */
+    cbuf = obj->newtmp;
+    q = cbuf;
+    *q++ = 2;  /* long path type */
+    *q++ = (unsigned char)len;
+    memcpy( q, p, len );
+    q = cbuf;
+
+    if (( p = cname( vol, vol->v_dir, &q )) == NULL ) {
+       *rbuflen = 0;
+       return( AFPERR_NOITEM );
+    }
+
+    if ( stat( mtoupath(vol, p), &st ) < 0 ) {
+       *rbuflen = 0;
+       return( AFPERR_NOITEM );
+    }
+    buflen = *rbuflen - sizeof( bitmap ) - sizeof( appltag );
+    if ( getfilparams(vol, bitmap, p, curdir, &st, rbuf + sizeof( bitmap ) +
+           sizeof( appltag ), &buflen ) != AFP_OK ) {
+       *rbuflen = 0;
+       return( AFPERR_BITMAP );
+    }
+
+    *rbuflen = buflen + sizeof( bitmap ) + sizeof( appltag );
+    bitmap = htons( bitmap );
+    memcpy( rbuf, &bitmap, sizeof( bitmap ));
+    rbuf += sizeof( bitmap );
+    memcpy( rbuf, appltag, sizeof( appltag ));
+    rbuf += sizeof( appltag );
+    return( AFP_OK );
+}
+
+
diff --git a/etc/afpd/auth.c b/etc/afpd/auth.c
new file mode 100644 (file)
index 0000000..3f22928
--- /dev/null
@@ -0,0 +1,411 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <netatalk/endian.h>
+#include <atalk/afp.h>
+#include <atalk/compat.h>
+#include <atalk/util.h>
+#include <limits.h>
+#include <string.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <grp.h>
+#include <syslog.h>
+
+#include "globals.h"
+#include "auth.h"
+#include "uam_auth.h"
+#include "switch.h"
+#include "status.h"
+
+int    afp_version = 11;
+uid_t  uuid;
+#if defined( __svr4__ ) && !defined( NGROUPS )
+#define NGROUPS NGROUPS_MAX
+#endif __svr4__ NGROUPS
+#if defined( sun ) && !defined( __svr4__ ) || defined( ultrix )
+int    groups[ NGROUPS ];
+#else sun __svr4__ ultrix
+#if defined( __svr4__ ) && !defined( NGROUPS )
+#define NGROUPS        NGROUPS_MAX
+#endif __svr4__ NGROUPS
+gid_t  groups[ NGROUPS ];
+#endif sun ultrix
+int    ngroups;
+
+/*
+ * These numbers are scattered throughout the code.
+ */
+static struct afp_versions     afp_versions[] = {
+    { "AFPVersion 1.1",        11 },
+    { "AFPVersion 2.0",        20 },
+    { "AFPVersion 2.1",        21 },
+    { "AFP2.2",        22 }
+};
+
+static struct uam_mod uam_modules = {NULL, NULL, &uam_modules, &uam_modules};
+static struct uam_obj uam_login = {"", "", 0, {{NULL}}, &uam_login,
+                                  &uam_login};
+static struct uam_obj uam_changepw = {"", "", 0, {{NULL}}, &uam_changepw, 
+                                     &uam_changepw};
+
+static struct uam_obj *afp_uam = NULL;
+
+void status_versions( data )
+    char       *data;
+{
+    char                *start = data;
+    u_int16_t           status;
+    int                        len, num, i;
+
+    memcpy(&status, start + AFPSTATUS_VERSOFF, sizeof(status));
+    num = sizeof( afp_versions ) / sizeof( afp_versions[ 0 ] );
+    data += ntohs( status );
+    *data++ = num;
+    for ( i = 0; i < num; i++ ) {
+       len = strlen( afp_versions[ i ].av_name );
+       *data++ = len;
+       memcpy( data, afp_versions[ i ].av_name , len );
+       data += len;
+    }
+    status = htons( data - start );
+    memcpy(start + AFPSTATUS_UAMSOFF, &status, sizeof(status));
+}
+
+void status_uams(char *data, const char *authlist)
+{
+    char                *start = data;
+    u_int16_t           status;
+    struct uam_obj      *uams;
+    int                        len, num = 0;
+
+    memcpy(&status, start + AFPSTATUS_UAMSOFF, sizeof(status));
+    uams = &uam_login;
+    while ((uams = uams->uam_prev) != &uam_login) {
+      if (strstr(authlist, uams->uam_path))
+       num++;
+    }
+
+    data += ntohs( status );
+    *data++ = num;
+    while ((uams = uams->uam_prev) != &uam_login) {
+      if (strstr(authlist, uams->uam_path)) {
+       syslog(LOG_INFO, "uam: \"%s\" available", uams->uam_name);
+       len = strlen( uams->uam_name);
+       *data++ = len;
+       memcpy( data, uams->uam_name, len );
+       data += len;
+      }
+    }
+
+    /* icon offset */
+    status = htons(data - start);
+    memcpy(start + AFPSTATUS_ICONOFF, &status, sizeof(status));
+}
+
+/* handle errors by closing the connection. this is only needed
+ * by the afp_* functions. */
+static int send_reply(const AFPObj *obj, const int err)
+{
+  if ((err == AFP_OK) || (err == AFPERR_AUTHCONT))
+    return err;
+
+  obj->reply(obj->handle, err);
+  obj->exit(0);
+}
+
+static int login(AFPObj *obj, struct passwd *pwd, void (*logout)(void))
+{
+    if ( pwd->pw_uid == 0 ) {  /* don't allow root login */
+       syslog( LOG_ERR, "login: root login denied!" );
+       return AFPERR_NOTAUTH;
+    }
+
+    syslog( LOG_INFO, "login %s (uid %d, gid %d)", pwd->pw_name,
+           pwd->pw_uid, pwd->pw_gid );
+    if (initgroups( pwd->pw_name, pwd->pw_gid ) < 0) {
+#ifdef RUN_AS_USER
+      syslog(LOG_INFO, "running with uid %d", geteuid());
+#else
+      syslog(LOG_ERR, "login: %m");
+      return AFPERR_BADUAM;
+#endif
+    }
+    
+    if (setegid( pwd->pw_gid ) < 0 || seteuid( pwd->pw_uid ) < 0) {
+       syslog( LOG_ERR, "login: %m" );
+       return AFPERR_BADUAM;
+    }
+
+    if (( ngroups = getgroups( NGROUPS, groups )) < 0 ) {
+       syslog( LOG_ERR, "login: getgroups: %m" );
+       return AFPERR_BADUAM;
+    }
+    uuid = pwd->pw_uid;
+
+    afp_switch = postauth_switch;
+    obj->logout = logout;
+    return( AFP_OK );
+}
+
+int afp_login(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct passwd *pwd = NULL;
+    int                len, i, num;
+
+    *rbuflen = 0;
+
+    if ( nologin & 1) 
+       return send_reply(obj, AFPERR_SHUTDOWN );
+
+    ibuf++;
+    len = (unsigned char) *ibuf++;
+    num = sizeof( afp_versions ) / sizeof( afp_versions[ 0 ]);
+    for ( i = 0; i < num; i++ ) {
+       if ( strncmp( ibuf, afp_versions[ i ].av_name , len ) == 0 ) {
+           afp_version = afp_versions[ i ].av_number;
+           break;
+       }
+    }
+    if ( i == num )                            /* An inappropo version */
+       return send_reply(obj, AFPERR_BADVERS );
+    ibuf += len;
+
+    len = (unsigned char) *ibuf++;
+    if ((afp_uam = auth_uamfind(UAM_SERVER_LOGIN, ibuf, len)) == NULL)
+      return send_reply(obj, AFPERR_BADUAM);
+    ibuf += len;
+
+    i = afp_uam->u.uam_login.login(obj, &pwd, ibuf, ibuflen, rbuf, rbuflen);
+    if (i || !pwd) 
+      return send_reply(obj, i);
+      
+    return send_reply(obj, login(obj, pwd, afp_uam->u.uam_login.logout));
+}
+
+
+int afp_logincont(obj, ibuf, ibuflen, rbuf, rbuflen)
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct passwd *pwd = NULL;
+    int err;
+
+    if ( afp_uam == NULL || afp_uam->u.uam_login.logincont == NULL ) {
+       *rbuflen = 0;
+       return send_reply(obj, AFPERR_NOTAUTH );
+    }
+
+    ibuf += 2;
+    err = afp_uam->u.uam_login.logincont(obj, &pwd, ibuf, ibuflen,
+                                        rbuf, rbuflen);
+    if (err || !pwd)
+      return send_reply(obj, err);
+
+    return send_reply(obj, login(obj, pwd, afp_uam->u.uam_login.logout));
+}
+
+
+int afp_logout(obj, ibuf, ibuflen, rbuf, rbuflen)
+     AFPObj     *obj;
+     char       *ibuf, *rbuf;
+     int        ibuflen, *rbuflen;
+{
+  syslog(LOG_INFO, "logout %s", obj->username);
+  obj->exit(0);
+}
+
+
+
+/* change password  --
+ * NOTE: an FPLogin must already have completed successfully for this
+ *       to work. this also does a little pre-processing before it hands
+ *       it off to the uam. 
+ */
+int afp_changepw(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+  char username[MACFILELEN + 1], *start = ibuf;
+  struct uam_obj *uam;
+  struct passwd *pwd;
+  int len;
+
+  *rbuflen = 0;
+  ibuf += 2; 
+
+  /* make sure we can deal w/ this uam */
+  len = (unsigned char) *ibuf++;
+  if ((uam = auth_uamfind(UAM_SERVER_CHANGEPW, ibuf, len)) == NULL)
+    return AFPERR_BADUAM;
+
+  ibuf += len;
+  if ((len + 1) & 1) /* pad byte */
+    ibuf++;
+
+  len = (unsigned char) *ibuf++;
+  if ( len > sizeof(username) - 1) {
+    return AFPERR_PARAM;
+  }
+  memcpy(username, ibuf, len);
+  username[ len ] = '\0';
+  ibuf += len;
+  if ((len + 1) & 1) /* pad byte */
+    ibuf++;
+  
+  syslog(LOG_INFO, "changing password for <%s>", username);
+
+  if (( pwd = uam_getname( username, sizeof(username))) == NULL )
+    return AFPERR_PARAM;
+
+  /* send it off to the uam. we really don't use ibuflen right now. */
+  ibuflen -= (ibuf - start);
+  len = uam->u.uam_changepw(obj, username, pwd, ibuf, ibuflen,
+                           rbuf, rbuflen);
+  syslog(LOG_INFO, "password change %s.", 
+        (len == AFPERR_AUTHCONT) ? "continued" :
+        (len ? "failed" : "succeeded"));
+  return len;
+}
+
+
+/* FPGetUserInfo */
+int afp_getuserinfo(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    u_int8_t  thisuser;
+    u_int32_t id;
+    u_int16_t bitmap;
+    
+    *rbuflen = 0;
+    ibuf++;
+    thisuser = *ibuf++;
+    ibuf += sizeof(id); /* userid is not used in AFP 2.0 */
+    memcpy(&bitmap, ibuf, sizeof(bitmap));
+    bitmap = ntohs(bitmap);
+    
+    /* deal with error cases. we don't have to worry about 
+     * AFPERR_ACCESS or AFPERR_NOITEM as geteuid and getegid always
+     * succeed. */
+    if (!thisuser) 
+      return AFPERR_PARAM;
+    if ((bitmap & USERIBIT_ALL) != bitmap)
+      return AFPERR_BITMAP;
+    
+    /* copy the bitmap back to reply buffer */
+    memcpy(rbuf, ibuf, sizeof(bitmap));
+    rbuf += sizeof(bitmap);
+    *rbuflen = sizeof(bitmap);
+
+    /* copy the user/group info */
+    if (bitmap & USERIBIT_USER) {
+      id = htonl(geteuid());
+      memcpy(rbuf, &id, sizeof(id));
+      rbuf += sizeof(id);
+      *rbuflen += sizeof(id);
+    }
+    
+    if (bitmap & USERIBIT_GROUP) {
+      id = htonl(getegid());
+      memcpy(rbuf, &id, sizeof(id));
+      rbuf += sizeof(id);
+      *rbuflen += sizeof(id);
+    }
+    
+    return AFP_OK;
+}    
+
+#define UAM_LIST(type) (((type) == UAM_SERVER_LOGIN) ? &uam_login : \
+                       (((type) == UAM_SERVER_CHANGEPW) ? \
+                        &uam_changepw : NULL))
+
+/* just do a linked list search. this could be sped up with a hashed
+ * list, but i doubt anyone's going to have enough uams to matter. */
+struct uam_obj *auth_uamfind(const int type, const char *name, 
+                            const int len)
+{
+  struct uam_obj *prev, *start;
+
+  if (!name || !(start = UAM_LIST(type)))
+    return NULL;
+
+  prev = start;
+  while ((prev = prev->uam_prev) != start) 
+    if (strndiacasecmp(prev->uam_name, name, len) == 0)
+      return prev;
+
+  return NULL;
+}
+
+int auth_register(const int type, struct uam_obj *uam)
+{
+  struct uam_obj *start;
+
+  if (!uam || !uam->uam_name || (*uam->uam_name == '\0'))
+    return -1;
+
+  if (!(start = UAM_LIST(type)))
+    return 0; /* silently fail */
+
+  uam_attach(start, uam);
+  return 0;
+}
+
+/* load all of the modules */
+int auth_load(const char *path, const char *list)
+{
+  char name[MAXPATHLEN + 1], buf[MAXPATHLEN + 1], *p; 
+  struct uam_mod *mod;
+  struct stat st;
+  int len;
+  
+  if (!path || !list || (len = strlen(path)) > sizeof(name) - 2)
+    return -1;
+
+  strncpy(buf, list, sizeof(buf));
+  if ((p = strtok(buf, ",")) == NULL)
+    return -1;
+
+  strcpy(name, path);
+  if (name[len - 1] != '/') {
+    strcat(name, "/");
+    len++;
+  }
+
+  while (p) {
+    strncpy(name + len, p, sizeof(name) - len);
+    if ((stat(name, &st) == 0) && (mod = uam_load(name, p))) {
+      uam_attach(&uam_modules, mod);
+      syslog(LOG_INFO, "uam: %s loaded", p);
+    }
+    p = strtok(NULL, ",");
+  }
+}
+
+/* get rid of all of the uams */
+void auth_unload()
+{
+  struct uam_mod *mod, *prev, *start = &uam_modules;
+
+  prev = start->uam_prev;
+  while ((mod = prev) != start) {
+    prev = prev->uam_prev;
+    uam_detach(mod);
+    uam_unload(mod);
+  }
+}
diff --git a/etc/afpd/auth.h b/etc/afpd/auth.h
new file mode 100644 (file)
index 0000000..4a5894b
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#ifndef AFPD_AUTH_H
+#define AFPD_AUTH_H 1
+
+#include <limits.h>
+#include <sys/cdefs.h>
+#include "globals.h"
+
+struct afp_versions {
+    char       *av_name;
+    int                av_number;
+};
+
+/* for GetUserInfo */
+#define USERIBIT_USER  (1 << 0)
+#define USERIBIT_GROUP (1 << 1)
+#define USERIBIT_ALL   (USERIBIT_USER | USERIBIT_GROUP)
+
+extern uid_t    uuid;
+#if defined( __svr4__ ) && !defined( NGROUPS )
+#define NGROUPS        NGROUPS_MAX
+#endif /*__svr4__ NGROUPS*/
+#if defined( sun ) && !defined( __svr4__ ) || defined( ultrix )
+extern int     groups[ NGROUPS ];
+#else /*sun __svr4__ ultrix*/
+extern gid_t   groups[ NGROUPS ];
+#endif /*sun __svr4__ ultrix*/
+extern int     ngroups;
+
+/* FP functions */
+extern int     afp_login __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_logincont __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_changepw __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_logout __P((AFPObj *, char *, int, char *, int *));
+extern int      afp_getuserinfo __P((AFPObj *, char *, int, char *, int *));
+#endif /* auth.h */
diff --git a/etc/afpd/codepage.c b/etc/afpd/codepage.c
new file mode 100644 (file)
index 0000000..b2d0e5a
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2000 Adrian Sun
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ * codepage support (based initially on some code from
+ * julian@whistle.com)
+ *
+ * the strategy:
+ * for single-byte maps, the codepage support devolves to a lookup
+ * table for all possible 8-bit values. for double-byte maps, 
+ * the first byte is used as a hash index followed by a linked list of
+ * values.
+ *
+ * the badumap specifies illegal characters. these are 8-bit values
+ * with an associated rule field. here are the rules:
+ *   
+ *
+ * illegal values: 0 is the only illegal value. no translation will
+ * occur in those cases.  
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <syslog.h>
+
+#include <netatalk/endian.h>
+
+#include "globals.h"
+#include "volume.h"
+#include "codepage.h"
+
+#define MAPSIZE 256
+
+/* deal with linked lists */
+#define CP_INIT(a) (a)->next = (a)->prev = (a)
+#define CP_ADD(a, b) do { \
+  (b)->next = (a)->next; \
+  (b)->prev = (a); \
+  (a)->next = (b); \
+} while (0)
+#define CP_REMOVE(a) do {  \
+  (a)->prev->next = (a)->next; \
+  (a)->next->prev = (a)->prev; \
+} while (0)
+
+/* search for stuff */
+#if 0
+static __inline__ unsigned char *codepage_find(struct codepage *page,
+                                              unsigned char *from)
+{
+}
+#endif
+
+static int add_code(struct codepage *page, unsigned char *from,
+                   unsigned char *to)
+{
+  union codepage_val *ptr;
+
+  if (page->quantum < 1) /* no quantum given. don't do anything */
+    return 1;
+
+  if (page->quantum == 1) {
+    page->map[*from].value = *to;
+    return 0;
+  }
+
+#if 0
+  if (ptr = codepage_find(page->map, from)) {
+    
+  } else {
+    unsigned char *space;
+    ptr = map[*from].hash;
+
+    space = (unsigned char *) malloc(sizeof(unsigned char)*quantum);
+    if (!ptr->from) {
+    } else {
+    }
+    
+    map[*from].hash
+  }
+#endif
+}
+
+static struct codepage *init_codepage(const int quantum)
+{
+    struct codepage *cp;
+
+    cp = (struct codepage *) malloc(sizeof(struct codepage));
+    if (!cp)
+      return NULL;
+
+    if ((cp->map = (union codepage_val *)
+        calloc(MAPSIZE, sizeof(union codepage_val))) == NULL) {
+      free(cp);
+      return NULL;
+    }
+
+    cp->quantum = quantum;
+    return cp;
+}
+
+
+static void free_codepage(struct codepage *cp)
+{
+  int i;
+
+  if (!cp)
+    return;
+
+  if (cp->map) {
+    if (cp->quantum > 1) {
+      /* deal with any linked lists that may exist */
+      for (i = 0; i < MAPSIZE; i++) {
+       struct codepage_hash *ptr, *h;
+       
+       h = &cp->map[i].hash; /* we don't free this one */
+       while ((ptr = h->prev) != h) {
+         CP_REMOVE(ptr);
+         free(ptr);
+       }
+      }
+    }
+    free(cp->map);
+  }
+  free(cp);
+}
+
+
+
+/* this is used by both codepages and generic mapping utilities. we
+ * allocate enough space to map all 8-bit characters if necessary.
+ * for double-byte mappings, we just use the table as a hash lookup.
+ * if we don't match, we don't convert.
+ */
+int codepage_init(struct vol *vol, const int rules, 
+                 const int quantum)
+{
+  if ((rules & CODEPAGE_RULE_MTOU) && !vol->v_utompage) {
+    vol->v_utompage = init_codepage(quantum);
+    if (!vol->v_utompage)
+      return -1;
+  }
+
+  if ((rules & CODEPAGE_RULE_UTOM) && !vol->v_mtoupage) {
+    vol->v_mtoupage = init_codepage(quantum);
+    if (!vol->v_mtoupage) {
+      goto err_utompage;
+    }
+  }
+
+  if ((rules & CODEPAGE_RULE_BADU)  && !vol->v_badumap) {
+    vol->v_badumap = init_codepage(quantum);
+    if (!vol->v_badumap)
+      goto err_mtoupage;
+  }
+  return 0;
+
+err_mtoupage:
+    free_codepage(vol->v_mtoupage);
+    vol->v_mtoupage = NULL;
+
+err_utompage:
+    free_codepage(vol->v_utompage);
+    vol->v_utompage = NULL;
+    return -1;
+}
+
+void codepage_free(struct vol *vol)
+{
+  if (vol->v_utompage) {
+    free_codepage(vol->v_utompage);
+    vol->v_utompage = NULL;
+  }
+
+  if (vol->v_mtoupage) {
+    free_codepage(vol->v_mtoupage);
+    vol->v_mtoupage = NULL;
+  }
+
+  if (vol->v_badumap) {
+    free_codepage(vol->v_badumap);
+    vol->v_badumap = NULL;
+  }
+}
+
+
+int codepage_read(struct vol *vol, const char *path)
+{
+  unsigned char buf[CODEPAGE_FILE_HEADER_SIZE], *cur;
+  u_int16_t id;
+  int fd, i, quantum, rules;
+  
+  if ((fd = open(path, O_RDONLY)) < 0) {
+    syslog(LOG_ERR, "%s: failed to open codepage", path);
+    return -1;
+  }
+  
+  /* Read the codepage file header. */
+  if(read(fd, buf, sizeof(buf)) != sizeof(buf)) {
+    syslog( LOG_ERR, "%s: failed to read codepage header", path);
+    goto codepage_fail;
+  }
+
+  /* Check the file id */
+  cur = buf;
+  memcpy(&id, cur, sizeof(id));
+  cur += sizeof(id);
+  id = ntohs(id);
+  if (id != CODEPAGE_FILE_ID) {
+      syslog( LOG_ERR, "%s: not a codepage", path);
+      goto codepage_fail;
+  } 
+
+  /* check the version number */
+  if (*cur++ != CODEPAGE_FILE_VERSION) {
+      syslog( LOG_ERR, "%s: codepage version not supported", path);
+      goto codepage_fail;
+  } 
+
+  /* find out the data quantum size. default to 1 if nothing's given. */
+  quantum = *cur ? *cur : 1;
+  cur++;
+  
+  /* rules used in this file. */
+  rules = *cur++;
+
+  if (codepage_init(vol, rules, quantum) < 0) {
+    syslog( LOG_ERR, "%s: Unable to allocate memory", path);
+    goto codepage_fail;
+  }
+
+  /* offset to data */
+
+  /* skip to the start of the data */
+  memcpy(&id, cur , sizeof(id));
+  id = ntohs(id);
+  lseek(fd, id, SEEK_SET);
+
+  /* mtoupage is the the equivalent of samba's unix2dos. utompage is
+   * the equivalent of dos2unix. it's a little confusing due to a
+   * desire to match up with mtoupath and utompath. 
+   * NOTE: we allow codepages to specify 7-bit mappings if they want. 
+   */
+  i = 1 + 2*quantum;
+  while (read(fd, buf, i) == i) {
+    if (*buf & CODEPAGE_RULE_MTOU) {
+      if (add_code(vol->v_mtoupage, buf + 1, buf + 1 + quantum) < 0) {
+       syslog(LOG_ERR, "unable to allocate memory for mtoupage");
+       break;
+      }
+    }
+
+    if (*buf & CODEPAGE_RULE_UTOM) {
+      if (add_code(vol->v_utompage, buf + 1 + quantum, buf + 1) < 0) {
+       syslog(LOG_ERR, "unable to allocate memory for utompage");
+       break;
+      }
+    }
+      
+    /* we only look at the first character here. if we need to 
+     * do so, we can always use the quantum to expand the 
+     * available flags. */
+    if (*buf & CODEPAGE_RULE_BADU) 
+      vol->v_badumap->map[*(buf + 1)].value = *(buf + 1 + quantum);
+  }
+  close(fd);
+  return 0;
+
+codepage_fail:
+  close(fd);
+  return -1;
+}
diff --git a/etc/afpd/codepage.h b/etc/afpd/codepage.h
new file mode 100644 (file)
index 0000000..130e0ce
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef AFPD_CODEPAGE_H
+#define AFPD_CODEPAGE_H 1
+
+/* 
+ * header of a codepage --
+ * file magic:                   2 bytes
+ * file version:                 1 byte
+ * size of name:                 1 byte
+ * quantum:                      1 byte
+ * rules:                        1 byte
+ * offset to data:               2 bytes (network byte order)
+ * size of data:                 2 bytes (network byte order)
+ * name:                         strlen(name) bytes (padded)
+ * 
+ * data for a version 2 codepage --
+ * mapping rule: 1 byte
+ * mac/bad code:     <quantum> bytes
+ * xlate/rule code:  <quantum> bytes
+ * ...
+ */
+
+#define CODEPAGE_FILE_ID 0xCFAF
+#define CODEPAGE_FILE_VERSION 0x02
+#define CODEPAGE_FILE_HEADER_SIZE 10
+
+/* m->u and u->m mappings */
+#define CODEPAGE_RULE_MTOU       (1 << 0)
+#define CODEPAGE_RULE_UTOM       (1 << 1)
+#define CODEPAGE_RULE_BADU       (1 << 2)
+
+/* rules for bad character assignment.
+ *
+ * bit assignments:
+ * bit 0 set:  it's an illegal character
+ *     unset:  it's okay
+ *
+ * bit 1 set:  bad anywhere
+ *     unset:  only check at the beginning of each quantum. 
+ *
+ * bit 2 set:  all quanta are the same length
+ *     unset:  quanta beginning with ascii characters are 1-byte long  
+ *
+ */
+#define CODEPAGE_BADCHAR_SET      (1 << 0) 
+#define CODEPAGE_BADCHAR_ANYWHERE (1 << 1)  
+#define CODEPAGE_BADCHAR_QUANTUM  (1 << 2) 
+#endif
diff --git a/etc/afpd/config.c b/etc/afpd/config.c
new file mode 100644 (file)
index 0000000..d57b22d
--- /dev/null
@@ -0,0 +1,353 @@
+/* 
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <syslog.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <atalk/dsi.h>
+#include <atalk/atp.h>
+#include <atalk/asp.h>
+#include <atalk/nbp.h>
+#include <atalk/afp.h>
+#include <atalk/compat.h>
+#include <atalk/server_child.h>
+
+#include "globals.h"
+#include "config.h"
+#include "uam_auth.h"
+#include "status.h"
+
+#define LINESIZE 1024  
+
+/* get rid of unneeded configurations. i use reference counts to deal
+ * w/ multiple configs sharing the same afp_options. oh, to dream of
+ * garbage collection ... */
+void configfree(AFPConfig *configs, const AFPConfig *config)
+{
+  AFPConfig *p, *q;
+
+  for (p = configs; p; p = q) {
+    q = p->next;
+    if (p == config)
+      continue;
+
+    /* do a little reference counting */
+    if (--(*p->optcount) < 1) {
+      afp_options_free(&p->obj.options, p->defoptions);
+      free(p->optcount);
+    }
+
+    switch (p->obj.proto) {
+#ifndef NO_DDP
+    case AFPPROTO_ASP:
+      free(p->obj.Obj);
+      free(p->obj.Type);
+      free(p->obj.Zone);
+      atp_close(((ASP) p->obj.handle)->asp_atp);
+      free(p->obj.handle);
+      break;
+#endif /* no afp/asp */
+    case AFPPROTO_DSI:
+      close(p->fd);
+      free(p->obj.handle);
+      break;
+    }
+    free(p);
+  }
+}
+
+#ifndef NO_DDP
+static void asp_cleanup(const AFPConfig *config)
+{
+  nbp_unrgstr(config->obj.Obj, config->obj.Type, config->obj.Zone,
+             &config->obj.options.ddpaddr);
+}
+
+/* these two are almost identical. it should be possible to collapse them
+ * into one with minimal junk. */
+static int asp_start(AFPConfig *config, AFPConfig *configs, 
+                    server_child *server_children) 
+{
+  ASP asp;
+
+  if (!(asp = asp_getsession(config->obj.handle, server_children, 
+                            config->obj.options.tickleval))) {
+    syslog( LOG_ERR, "main: asp_getsession: %m" );
+    exit( 1 );
+  } 
+  
+  if (asp->child) {
+    configfree(configs, config); /* free a bunch of stuff */
+    afp_over_asp(&config->obj);
+    exit (0);
+  }
+}
+#endif /* no afp/asp */
+
+static int dsi_start(AFPConfig *config, AFPConfig *configs,
+                    server_child *server_children)
+{
+  DSI *dsi;
+
+  if (!(dsi = dsi_getsession(config->obj.handle, server_children,
+                            config->obj.options.tickleval))) {
+    syslog( LOG_ERR, "main: dsi_getsession: %m" );
+    exit( 1 );
+  } 
+  
+  /* we've forked. */
+  if (dsi->child) {
+    configfree(configs, config);
+    afp_over_dsi(&config->obj); /* start a session */
+    exit (0);
+  }
+}
+
+#ifndef NO_DDP
+static AFPConfig *ASPConfigInit(const struct afp_options *options,
+                               unsigned char *refcount)
+{
+  AFPConfig *config;
+  ATP atp;
+  ASP asp;
+  char *Obj, *Type = "AFPServer", *Zone = "*";
+  
+  if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL)
+    return NULL;
+  
+  if ((atp = atp_open(ATADDR_ANYPORT, &options->ddpaddr)) == NULL)  {
+    syslog( LOG_ERR, "main: atp_open: %m");
+    free(config);
+    return NULL;
+  }
+  
+  if ((asp = asp_init( atp )) == NULL) {
+    syslog( LOG_ERR, "main: asp_init: %m" );
+    atp_close(atp);
+    free(config);
+    return NULL;
+  }
+  
+  /* register asp server */
+  Obj = (char *) options->hostname;
+  if (nbp_name(options->server, &Obj, &Type, &Zone )) {
+    syslog( LOG_ERR, "main: can't parse %s", options->server );
+    goto serv_free_return;
+  }
+
+  /* dup Obj, Type and Zone as they get assigned to a single internal
+   * buffer by nbp_name */
+  if ((config->obj.Obj  = strdup(Obj)) == NULL) 
+    goto serv_free_return;
+
+  if ((config->obj.Type = strdup(Type)) == NULL) {
+    free(config->obj.Obj);
+    goto serv_free_return;
+  }
+
+  if ((config->obj.Zone = strdup(Zone)) == NULL) {
+    free(config->obj.Obj);
+    free(config->obj.Type);
+    goto serv_free_return;
+  }
+  
+  /* make sure we're not registered */
+  nbp_unrgstr(Obj, Type, Zone, &options->ddpaddr); 
+  if (nbp_rgstr( atp_sockaddr( atp ), Obj, Type, Zone ) < 0 ) {
+    syslog( LOG_ERR, "Can't register %s:%s@%s", Obj, Type, Zone );
+    free(config->obj.Obj);
+    free(config->obj.Type);
+    free(config->obj.Zone);
+    goto serv_free_return;
+  }
+
+  syslog( LOG_INFO, "%s:%s@%s started on %u.%u:%u (%s)", Obj, Type, Zone,
+         ntohs( atp_sockaddr( atp )->sat_addr.s_net ),
+         atp_sockaddr( atp )->sat_addr.s_node,
+         atp_sockaddr( atp )->sat_port, VERSION );
+  
+  config->fd = atp_fileno(atp);
+  config->obj.handle = asp;
+  config->obj.config = config;
+  config->obj.proto = AFPPROTO_ASP;
+
+  memcpy(&config->obj.options, options, sizeof(struct afp_options));
+  config->optcount = refcount;
+  (*refcount)++;
+
+  config->server_start = asp_start;
+  config->server_cleanup = asp_cleanup;
+
+  return config;
+
+serv_free_return:
+  asp_close(asp);
+  free(config);
+  return NULL;
+}
+#endif /* no afp/asp */
+
+
+static AFPConfig *DSIConfigInit(const struct afp_options *options,
+                               unsigned char *refcount,
+                               const dsi_proto protocol) 
+{
+  AFPConfig *config;
+  DSI *dsi;
+  char *p, *q;
+
+  if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL) {
+    syslog( LOG_ERR, "DSIConfigInit: malloc(config): %m" );
+    return NULL;
+  }
+
+  if ((dsi = dsi_init(protocol, "afpd", options->hostname,
+                     options->ipaddr, options->port, 
+                     options->flags & OPTION_PROXY, 
+                     options->server_quantum)) == NULL) {
+    syslog( LOG_ERR, "main: dsi_init: %m" );
+    free(config);
+    return NULL;
+  }
+
+  if (options->flags & OPTION_PROXY) {
+    syslog(LOG_INFO, "ASIP proxy initialized for %s:%d (%s)",
+          inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port), 
+          VERSION);
+  } else {
+    syslog(LOG_INFO, "ASIP started on %s:%d(%d) (%s)",
+          inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port), 
+          dsi->serversock, VERSION);
+  }
+
+  config->fd = dsi->serversock;
+  config->obj.handle = dsi;
+  config->obj.config = config;
+  config->obj.proto = AFPPROTO_DSI;
+
+  memcpy(&config->obj.options, options, sizeof(struct afp_options));
+  /* get rid of any appletalk info. we use the fact that the DSI
+   * stuff is done after the ASP stuff. */
+  p = config->obj.options.server;
+  if (p && (q = strchr(p, ':')))
+    *q = '\0';
+    
+  config->optcount = refcount;
+  (*refcount)++;
+
+  config->server_start = dsi_start;
+  return config;
+}
+
+/* allocate server configurations. this should really store the last
+ * entry in config->last or something like that. that would make
+ * supporting multiple dsi transports easier. */
+static AFPConfig *AFPConfigInit(const struct afp_options *options,
+                               const struct afp_options *defoptions)
+{
+  AFPConfig *config = NULL, *next = NULL;
+  unsigned char *refcount;
+
+  if ((refcount = (unsigned char *) 
+       calloc(1, sizeof(unsigned char))) == NULL) {
+    syslog( LOG_ERR, "AFPConfigInit: calloc(refcount): %m" );
+    return NULL;
+  }
+
+#ifndef NO_DDP
+  /* handle asp transports */
+  if ((options->transports & AFPTRANS_DDP) && 
+      (config = ASPConfigInit(options, refcount)))
+    config->defoptions = defoptions;
+#endif
+
+  /* handle dsi transports and dsi proxies. we only proxy
+   * for DSI connections. */
+
+  /* this should have something like the following:
+   * for (i=mindsi; i < maxdsi; i++)
+   *   if (options->transports & (1 << i) && 
+   *     (next = DSIConfigInit(options, refcount, i)))
+   *     next->defoptions = defoptions;
+   */
+  if ((options->transports & AFPTRANS_TCP) &&
+      (((options->flags & OPTION_PROXY) == 0) ||
+       ((options->flags & OPTION_PROXY) && config))
+      && (next = DSIConfigInit(options, refcount, DSI_TCPIP)))
+    next->defoptions = defoptions;
+
+  /* load in all the authentication modules. we can load the same
+     things multiple times if necessary. however, loading different
+     things with the same names will cause complaints. by not loading
+     in any uams with proxies, we prevent ddp connections from succeeding.
+  */
+  auth_load(options->uampath, options->uamlist);
+
+  /* this should be able to accept multiple dsi transports. i think
+   * the only thing that gets affected is the net addresses. */
+  status_init(config, next, options);
+
+  /* attach dsi config to tail of asp config */
+  if (config) {
+    config->next = next;
+    return config;
+  } 
+    
+  return next;
+}
+
+/* fill in the appropriate bits for each interface */
+AFPConfig *configinit(struct afp_options *cmdline)
+{
+  FILE *fp;
+  char buf[LINESIZE + 1], *p, have_option = 0;
+  struct afp_options options;
+  AFPConfig *config, *first = NULL;
+
+  /* if config file doesn't exist, load defaults */
+  if ((fp = fopen(cmdline->configfile, "r")) == NULL) 
+    return AFPConfigInit(cmdline, cmdline);
+
+  /* scan in the configuration file */
+  while (!feof(fp)) {
+    if (!fgets(buf, sizeof(buf), fp) || buf[0] == '#')
+      continue;
+    
+    /* a little pre-processing to get rid of spaces and end-of-lines */
+    p = buf;
+    while (p && isspace(*p)) 
+      p++;
+    if (!p || (*p == '\0'))
+      continue;
+
+    have_option = 1;
+
+    memcpy(&options, cmdline, sizeof(options));
+    if (!afp_options_parseline(p, &options))
+      continue;
+
+    /* this should really get a head and a tail to simplify things. */
+    if (!first) {
+      if ((first = AFPConfigInit(&options, cmdline)))
+       config = first->next ? first->next : first;
+    } else if ((config->next = AFPConfigInit(&options, cmdline))) {
+      config = config->next->next ? config->next->next : config->next;
+    }
+  }
+
+  fclose(fp);
+
+  if (!have_option)
+    return AFPConfigInit(cmdline, cmdline);
+
+  return first;
+}
diff --git a/etc/afpd/config.h b/etc/afpd/config.h
new file mode 100644 (file)
index 0000000..74c4940
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef AFPD_CONFIG_H
+#define AFPD_CONFIG_H 1
+
+#include <sys/cdefs.h>
+#include <atalk/server_child.h>
+#include <atalk/atp.h>
+#include "globals.h"
+
+typedef struct AFPConfig {
+  AFPObj obj;
+  int fd, statuslen;
+  unsigned char *optcount;
+  char status[ATP_MAXDATA];
+  const void *defoptions, *signature;
+  int (*server_start) __P((struct AFPConfig *, struct AFPConfig *,
+                          server_child *));
+  void (*server_cleanup) __P((const struct AFPConfig *));
+  struct AFPConfig *next;
+} AFPConfig;
+
+extern AFPConfig *configinit __P((struct afp_options *));
+extern void configfree __P((AFPConfig *, const AFPConfig *));
+#endif
diff --git a/etc/afpd/desktop.c b/etc/afpd/desktop.c
new file mode 100644 (file)
index 0000000..6316ee3
--- /dev/null
@@ -0,0 +1,873 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#include <sys/syslog.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netatalk/at.h>
+#include <netatalk/endian.h>
+#include <atalk/dsi.h>
+#include <atalk/atp.h>
+#include <atalk/asp.h>
+#include <atalk/afp.h>
+#include <atalk/adouble.h>
+#include <atalk/util.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#include "volume.h"
+#include "directory.h"
+#include "fork.h"
+#include "globals.h"
+#include "desktop.h"
+
+int afp_opendt(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct vol *vol;
+    u_int16_t  vid;
+
+    ibuf += 2;
+
+    memcpy( &vid, ibuf, sizeof(vid));
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       *rbuflen = 0;
+       return( AFPERR_PARAM );
+    }
+
+    memcpy( rbuf, &vid, sizeof(vid));
+    *rbuflen = sizeof(vid);
+    return( AFP_OK );
+}
+
+int afp_closedt(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    *rbuflen = 0;
+    return( AFP_OK );
+}
+
+struct savedt  si = { { 0, 0, 0, 0 }, -1, 0 };
+
+static int iconopen( vol, creator, flags, mode )
+    struct vol *vol;
+    u_char     creator[ 4 ];
+{
+    char       *dtf, *adt, *adts;
+
+    if ( si.sdt_fd != -1 ) {
+       if ( memcmp( si.sdt_creator, creator, sizeof( CreatorType )) == 0 &&
+               si.sdt_vid == vol->v_vid ) {
+           return 0;
+       }
+       close( si.sdt_fd );
+       si.sdt_fd = -1;
+    }
+
+    dtf = dtfile( vol, creator, ".icon" );
+
+    if (( si.sdt_fd = open( dtf, flags, ad_mode( dtf, mode ))) < 0 ) {
+       if ( errno == ENOENT && ( flags & O_CREAT )) {
+           if (( adts = strrchr( dtf, '/' )) == NULL ) {
+               return -1;
+           }
+           *adts = '\0';
+           if (( adt = strrchr( dtf, '/' )) == NULL ) {
+               return -1;
+           }
+           *adt = '\0';
+           (void) ad_mkdir( dtf, DIRBITS | 0777 );
+           *adt = '/';
+           (void) ad_mkdir( dtf, DIRBITS | 0777 );
+           *adts = '/';
+
+           if (( si.sdt_fd = open( dtf, flags, ad_mode( dtf, mode ))) < 0 ) {
+               syslog( LOG_ERR, "iconopen: open %s: %m", dtf );
+               return -1;
+           }
+       } else {
+           return -1;
+       }
+    }
+
+    memcpy( si.sdt_creator, creator, sizeof( CreatorType ));
+    si.sdt_vid = vol->v_vid;
+    si.sdt_index = 1;
+    return 0;
+}
+
+int afp_addicon(obj, ibuf, ibuflen, rbuf, rbuflen)
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct vol         *vol;
+    struct iovec       iov[ 2 ];
+    u_char             fcreator[ 4 ], imh[ 12 ], irh[ 12 ], *p;
+    int                        itype, cc = AFP_OK, iovcnt = 0, buflen;
+    u_int32_t           ftype, itag;
+    u_int16_t          bsize, rsize, vid;
+
+    buflen = *rbuflen;
+    *rbuflen = 0;
+    ibuf += 2;
+
+    memcpy( &vid, ibuf, sizeof( vid ));
+    ibuf += sizeof( vid );
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       cc = AFPERR_PARAM;
+       goto addicon_err;
+    }
+    
+    memcpy( fcreator, ibuf, sizeof( fcreator ));
+    ibuf += sizeof( fcreator );
+      
+    memcpy( &ftype, ibuf, sizeof( ftype ));
+    ibuf += sizeof( ftype );
+      
+    itype = (unsigned char) *ibuf;
+    ibuf += 2;
+      
+    memcpy( &itag, ibuf, sizeof( itag ));
+    ibuf += sizeof( itag );
+      
+    memcpy( &bsize, ibuf, sizeof( bsize ));
+    bsize = ntohs( bsize );
+      
+    if ( si.sdt_fd != -1 ) {
+      (void)close( si.sdt_fd );
+      si.sdt_fd = -1;
+    }
+
+    if (iconopen( vol, fcreator, O_RDWR|O_CREAT, 0666 ) < 0) {
+      cc = AFPERR_NOITEM;
+      goto addicon_err;
+    }
+
+    if (lseek( si.sdt_fd, (off_t) 0L, SEEK_SET ) < 0) {
+        close(si.sdt_fd);
+       si.sdt_fd = -1;
+       syslog( LOG_ERR, "afp_addicon: lseek: %m" );
+       cc = AFPERR_PARAM;
+       goto addicon_err;
+    }
+
+    /*
+     * Read icon elements until we find a match to replace, or
+     * we get to the end to insert.
+     */
+    p = imh;
+    memcpy( p, &itag, sizeof( itag ));
+    p += sizeof( itag );
+    memcpy( p, &ftype, sizeof( ftype ));
+    p += sizeof( ftype );
+    *p++ = itype;
+    *p++ = 0;
+    bsize = htons( bsize );
+    memcpy( p, &bsize, sizeof( bsize ));
+    bsize = ntohs( bsize );
+    while (( cc = read( si.sdt_fd, irh, sizeof( irh ))) > 0 ) {
+      memcpy( &rsize, irh + 10, sizeof( rsize ));
+      rsize = ntohs( rsize );
+      /*
+       * Is this our set of headers?
+       */
+      if ( memcmp( irh, imh, sizeof( irh ) - sizeof( u_short )) == 0 ) {
+       /*
+        * Is the size correct?
+        */
+       if ( bsize != rsize ) 
+         cc = AFPERR_ITYPE;
+       break;
+      }
+
+      if ( lseek( si.sdt_fd, (off_t) rsize, SEEK_CUR ) < 0 ) {
+       syslog( LOG_ERR, "afp_addicon: lseek: %m" );
+       cc = AFPERR_PARAM;
+      }
+    }
+    
+    /*
+     * Some error occurred, return.
+     */
+addicon_err:
+    if ( cc < 0 ) {
+      syslog( LOG_ERR, "afp_addicon: %m" );
+      if (obj->proto == AFPPROTO_DSI) {
+       dsi_writeinit(obj->handle, rbuf, buflen);
+       dsi_writeflush(obj->handle);
+      }
+      return cc;
+    }
+
+
+    switch (obj->proto) {
+#ifndef NO_DDP
+    case AFPPROTO_ASP:
+      buflen = bsize;
+      if ((asp_wrtcont(obj->handle, rbuf, &buflen) < 0) || buflen != bsize)
+       return( AFPERR_PARAM );
+      
+      if (obj->options.flags & OPTION_DEBUG) {
+       printf("(write) len: %d\n", buflen);
+       bprint(rbuf, buflen);
+      }
+
+      /*
+       * We're at the end of the file, add the headers, etc.  */
+      if ( cc == 0 ) {
+       iov[ 0 ].iov_base = (caddr_t)imh;
+       iov[ 0 ].iov_len = sizeof( imh );
+       iov[ 1 ].iov_base = rbuf;
+       iov[ 1 ].iov_len = bsize;
+       iovcnt = 2;
+      }
+
+      /*
+       * We found an icon to replace.
+       */
+      if ( cc > 0 ) {
+       iov[ 0 ].iov_base = rbuf;
+       iov[ 0 ].iov_len = bsize;
+       iovcnt = 1;
+      }
+      
+      if ( writev( si.sdt_fd, iov, iovcnt ) < 0 ) {
+       syslog( LOG_ERR, "afp_addicon: writev: %m" );
+       return( AFPERR_PARAM );
+      }
+      break;
+#endif /* no afp/asp */      
+    case AFPPROTO_DSI: 
+      {
+       DSI *dsi = obj->handle;
+
+       iovcnt = dsi_writeinit(dsi, rbuf, buflen);
+
+       /* add headers at end of file */
+       if ((cc == 0) && (write(si.sdt_fd, imh, sizeof(imh)) < 0)) {
+         syslog(LOG_ERR, "afp_addicon: write: %m");
+         dsi_writeflush(dsi);
+         return AFPERR_PARAM;
+       }
+       
+       if ((cc = write(si.sdt_fd, rbuf, iovcnt)) < 0) {
+         syslog(LOG_ERR, "afp_addicon: write: %m");
+         dsi_writeflush(dsi);
+         return AFPERR_PARAM;
+       }
+       
+       while ((iovcnt = dsi_write(dsi, rbuf, buflen))) {
+         if ( obj->options.flags & OPTION_DEBUG ) {
+           printf("(write) command cont'd: %d\n", iovcnt);
+           bprint(rbuf, iovcnt);
+         }
+         
+         if ((cc = write(si.sdt_fd, rbuf, iovcnt)) < 0) {
+           syslog(LOG_ERR, "afp_addicon: write: %m");
+           dsi_writeflush(dsi);
+           return AFPERR_PARAM;
+         }
+       }
+      }
+    break;
+    }
+
+    close( si.sdt_fd );
+    si.sdt_fd = -1;
+    return( AFP_OK );
+}
+
+u_char utag[] = { 0, 0, 0, 0 };
+u_char ucreator[] = { 'U', 'N', 'I', 'X' };
+u_char utype[] = { 'T', 'E', 'X', 'T' };
+short  usize = 256;
+u_char uicon[] = {
+0x1F, 0xFF, 0xFC, 0x00, 0x10, 0x00, 0x06, 0x00,
+0x10, 0x00, 0x05, 0x00, 0x10, 0x00, 0x04, 0x80,
+0x10, 0x00, 0x04, 0x40, 0x10, 0x00, 0x04, 0x20,
+0x10, 0x00, 0x07, 0xF0, 0x10, 0x00, 0x00, 0x10,
+0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10,
+0x10, 0x00, 0x00, 0x10, 0x10, 0x80, 0x02, 0x10,
+0x11, 0x80, 0x03, 0x10, 0x12, 0x80, 0x02, 0x90,
+0x12, 0x80, 0x02, 0x90, 0x14, 0x80, 0x02, 0x50,
+0x14, 0x87, 0xC2, 0x50, 0x14, 0x58, 0x34, 0x50,
+0x14, 0x20, 0x08, 0x50, 0x12, 0x16, 0xD0, 0x90,
+0x11, 0x01, 0x01, 0x10, 0x12, 0x80, 0x02, 0x90,
+0x12, 0x9C, 0x72, 0x90, 0x14, 0x22, 0x88, 0x50,
+0x14, 0x41, 0x04, 0x50, 0x14, 0x49, 0x24, 0x50,
+0x14, 0x55, 0x54, 0x50, 0x14, 0x5D, 0x74, 0x50,
+0x14, 0x5D, 0x74, 0x50, 0x12, 0x49, 0x24, 0x90,
+0x12, 0x22, 0x88, 0x90, 0x1F, 0xFF, 0xFF, 0xF0,
+0x1F, 0xFF, 0xFC, 0x00, 0x1F, 0xFF, 0xFE, 0x00,
+0x1F, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, 0xFF, 0x80,
+0x1F, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xE0,
+0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
+0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
+0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
+0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
+0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
+0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
+0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
+0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
+0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
+0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
+0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
+0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
+0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
+};
+
+int afp_geticoninfo(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct vol *vol;
+    u_char     fcreator[ 4 ], ih[ 12 ];
+    u_int16_t  vid, iindex, bsize;
+
+    *rbuflen = 0;
+    ibuf += 2;
+
+    memcpy( &vid, ibuf, sizeof( vid ));
+    ibuf += sizeof( vid );
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    memcpy( fcreator, ibuf, sizeof( fcreator ));
+    ibuf += sizeof( fcreator );
+    memcpy( &iindex, ibuf, sizeof( iindex ));
+    iindex = ntohs( iindex );
+
+    if ( memcmp( fcreator, ucreator, sizeof( ucreator )) == 0 ) {
+       if ( iindex > 1 ) {
+           return( AFPERR_NOITEM );
+       }
+       memcpy( ih, utag, sizeof( utag ));
+       memcpy( ih + sizeof( utag ), utype, sizeof( utype ));
+       *( ih + sizeof( utag ) + sizeof( utype )) = 1;
+       *( ih + sizeof( utag ) + sizeof( utype ) + 1 ) = 0;
+       memcpy( ih + sizeof( utag ) + sizeof( utype ) + 2, &usize, 
+               sizeof( usize ));
+       memcpy( rbuf, ih, sizeof( ih ));
+       *rbuflen = sizeof( ih );
+       return( AFP_OK );
+    }
+
+    if ( iconopen( vol, fcreator, O_RDONLY, 0 ) < 0) {
+       return( AFPERR_NOITEM );
+    }
+
+    if ( iindex < si.sdt_index ) {
+       if ( lseek( si.sdt_fd, (off_t) 0L, SEEK_SET ) < 0 ) {
+           return( AFPERR_PARAM );
+       }
+       si.sdt_index = 1;
+    }
+
+    /*
+     * Position to the correct spot.
+     */
+    for (;;) {
+       if ( read( si.sdt_fd, ih, sizeof( ih )) != sizeof( ih )) {
+           close( si.sdt_fd );
+           si.sdt_fd = -1;
+           return( AFPERR_NOITEM );
+       }
+       memcpy( &bsize, ih + 10, sizeof( bsize ));
+       bsize = ntohs(bsize);
+       if ( lseek( si.sdt_fd, (off_t) bsize, SEEK_CUR ) < 0 ) {
+           syslog( LOG_ERR, "afp_iconinfo: lseek: %m" );
+           return( AFPERR_PARAM );
+       }
+       if ( si.sdt_index == iindex ) {
+           memcpy( rbuf, ih, sizeof( ih ));
+           *rbuflen = sizeof( ih );
+           return( AFP_OK );
+       }
+       si.sdt_index++;
+    }
+}
+
+
+int afp_geticon(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct vol *vol;
+    off_t       offset;
+    int                rc, buflen;
+    u_char     fcreator[ 4 ], ftype[ 4 ], itype, ih[ 12 ];
+    u_int16_t  vid, bsize, rsize;
+
+    buflen = *rbuflen;
+    *rbuflen = 0;
+    ibuf += 2;
+
+    memcpy( &vid, ibuf, sizeof( vid ));
+    ibuf += sizeof( vid );
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    memcpy( fcreator, ibuf, sizeof( fcreator ));
+    ibuf += sizeof( fcreator );
+    memcpy( ftype, ibuf, sizeof( ftype ));
+    ibuf += sizeof( ftype );
+    itype = (unsigned char) *ibuf++;
+    ibuf++;
+    memcpy( &bsize, ibuf, sizeof( bsize ));
+    bsize = ntohs( bsize );
+
+    if ( memcmp( fcreator, ucreator, sizeof( ucreator )) == 0 &&
+           memcmp( ftype, utype, sizeof( utype )) == 0 &&
+           itype == 1 &&
+           bsize <= usize ) {
+       memcpy( rbuf, uicon, bsize);
+       *rbuflen = bsize;
+       return( AFP_OK );
+    }
+
+    if ( iconopen( vol, fcreator, O_RDONLY, 0 ) < 0) {
+       return( AFPERR_NOITEM );
+    }
+
+    if ( lseek( si.sdt_fd, (off_t) 0L, SEEK_SET ) < 0 ) {
+       close(si.sdt_fd);
+       si.sdt_fd = -1;
+       syslog(LOG_ERR, "afp_geticon: lseek: %m");
+       return( AFPERR_PARAM );
+    }
+
+    si.sdt_index = 1;
+    offset = 0;
+    while (( rc = read( si.sdt_fd, ih, sizeof( ih ))) > 0 ) {
+       si.sdt_index++;
+       offset += sizeof(ih);
+       if ( memcmp( ih + sizeof( int ), ftype, sizeof( ftype )) == 0 &&
+               *(ih + sizeof( int ) + sizeof( ftype )) == itype ) {
+           break;
+       }
+       memcpy( &rsize, ih + 10, sizeof( rsize ));
+       rsize = ntohs( rsize );
+       if ( lseek( si.sdt_fd, (off_t) rsize, SEEK_CUR ) < 0 ) {
+           syslog( LOG_ERR, "afp_geticon: lseek: %m" );
+           return( AFPERR_PARAM );
+       }
+       offset += rsize;
+    }
+
+    if ( rc < 0 ) {
+        syslog(LOG_ERR, "afp_geticon: read: %m");
+       return( AFPERR_PARAM );
+    }
+
+    if ( rc == 0 ) {
+       return( AFPERR_NOITEM );
+    }
+
+    memcpy( &rsize, ih + 10, sizeof( rsize ));
+    rsize = ntohs( rsize );
+#define min(a,b)       ((a)<(b)?(a):(b))
+    rc = min( bsize, rsize );
+
+    if ((obj->proto == AFPPROTO_DSI) && (buflen < rc)) {
+      DSI *dsi = obj->handle;
+      struct stat st;
+      off_t size;
+      
+      size = (fstat(si.sdt_fd, &st) < 0) ? 0 : st.st_size;
+      if (size < rc + offset) {
+       return AFPERR_PARAM;
+      }
+      
+      if ((*rbuflen = dsi_readinit(dsi, rbuf, buflen, rc, AFP_OK)) < 0) 
+       goto geticon_exit;
+
+      /* do to the streaming nature, we have to exit if we encounter 
+       * a problem. much confusion results otherwise. */
+      while (*rbuflen > 0) {
+#if defined(SENDFILE_FLAVOR_LINUX) || defined(SENDFILE_FLAVOR_BSD)
+       if (!obj->options.flags & OPTION_DEBUG) {
+#ifdef SENDFILE_FLAVOR_LINUX
+         if (sendfile(dsi->socket, si.sdt_fd, &offset, dsi->datasize) < 0)
+           goto geticon_exit;
+#endif
+         
+#ifdef SENDFILE_FLAVOR_BSD
+         if (sendfile(si.sdt_fd, dsi->socket, offset, rc, NULL, NULL, 0) < 0)
+           goto geticon_exit;
+#endif
+         
+         goto geticon_done;
+       }
+#endif
+
+       buflen = read(si.sdt_fd, rbuf, *rbuflen);
+       if (buflen < 0) 
+         goto geticon_exit;
+
+       if (obj->options.flags & OPTION_DEBUG) {
+         printf( "(read) reply: %d, %d\n", buflen, dsi->clientID);
+         bprint(rbuf, buflen);
+       }
+       
+       /* dsi_read() also returns buffer size of next allocation */
+       buflen = dsi_read(dsi, rbuf, buflen); /* send it off */
+       if (buflen < 0) 
+         goto geticon_exit;
+
+       *rbuflen = buflen;
+      }
+
+geticon_done:
+      dsi_readdone(dsi);
+      return AFP_OK;
+      
+geticon_exit:
+      syslog(LOG_INFO, "afp_geticon: %m");
+      dsi_readdone(dsi);
+      obj->exit(1);
+
+    } else {
+      if ( read( si.sdt_fd, rbuf, rc ) < rc ) {
+       return( AFPERR_PARAM );
+      }
+      *rbuflen = rc;
+      return AFP_OK;
+    }
+}
+
+
+static char            hexdig[] = "0123456789abcdef";
+char *dtfile(const struct vol *vol, u_char creator[], char *ext )
+{
+    static char        path[ MAXPATHLEN + 1];
+    char       *p;
+    int                i;
+
+    strcpy( path, vol->v_path );
+    strcat( path, "/.AppleDesktop/" );
+    for ( p = path; *p != '\0'; p++ )
+       ;
+
+    if ( !isprint( creator[ 0 ] ) || creator[ 0 ] == '/' ) {
+       *p++ = hexdig[ ( creator[ 0 ] & 0xf0 ) >> 4 ];
+       *p++ = hexdig[ creator[ 0 ] & 0x0f ];
+    } else {
+       *p++ = creator[ 0 ];
+    }
+
+    *p++ = '/';
+
+    for ( i = 0; i < sizeof( CreatorType ); i++ ) {
+       if ( !isprint( creator[ i ] ) || creator[ i ] == '/' ) {
+           *p++ = hexdig[ ( creator[ i ] & 0xf0 ) >> 4 ];
+           *p++ = hexdig[ creator[ i ] & 0x0f ];
+       } else {
+           *p++ = creator[ i ];
+       }
+    }
+    *p = '\0';
+    strcat( path, ext );
+
+    return( path );
+}
+
+char *mtoupath(const struct vol *vol, char *mpath)
+{
+    static unsigned char upath[ MAXPATHLEN + 1];
+    unsigned char *m, *u;
+    int                i = 0;
+
+    if ( *mpath == '\0' ) {
+       return( "." );
+    }
+
+    m = mpath;
+    u = upath;
+    while ( *m != '\0' ) {
+        /* handle case conversion first */
+       if (vol->v_casefold & AFPVOL_MTOUUPPER)
+           *m = diatoupper( *m );
+        else if (vol->v_casefold & AFPVOL_MTOULOWER)
+           *m = diatolower( *m );
+       
+       /* we have a code page. we only use the ascii range
+        * if we have map ascii specified. */
+#if 0
+       if (vol->v_mtoupage && ((*m > 0x7F) ||
+                               vol->v_flags & AFPVOL_MAPASCII)) {
+           *u = vol->v_mtoupage[*m];
+       } else
+#endif
+#if AD_VERSION == AD_VERSION1
+         if ((((vol->v_flags & AFPVOL_NOHEX) == 0) && 
+                  (!isascii(*m) || *m == '/')) || 
+                  (((vol->v_flags & AFPVOL_USEDOTS) == 0) &&
+                   ( i == 0 && (*m == '.' )))) {
+#else
+          if ((((vol->v_flags & AFPVOL_NOHEX) == 0) && 
+                  (!isprint(*m) || *m == '/')) || 
+                  (((vol->v_flags & AFPVOL_USEDOTS) == 0) &&
+                   ( i == 0 && (*m == '.' )))) {
+#endif
+           /* do hex conversion. */ 
+           *u++ = ':';
+           *u++ = hexdig[ ( *m & 0xf0 ) >> 4 ];
+           *u = hexdig[ *m & 0x0f ];
+       } else 
+           *u = *m;
+       u++;
+       i++;
+       m++;
+    }
+    *u = '\0';
+
+    return( upath );
+}
+
+#define hextoint( c )  ( isdigit( c ) ? c - '0' : c + 10 - 'a' )
+#define islxdigit(x)   (!isupper(x)&&isxdigit(x))
+
+char *utompath(const struct vol *vol, char *upath)
+{
+    static unsigned char mpath[ MAXPATHLEN + 1];
+    unsigned char      *m, *u;
+    int                h;
+
+    /* do the hex conversion */
+    u = upath;
+    m = mpath;
+    while ( *u != '\0' ) {
+        /* we have a code page */
+#if 0
+        if (vol->v_utompage && ((*u > 0x7F) ||
+                               (vol->v_flags & AFPVOL_MAPASCII))) {
+           *m = vol->v_utompage[*u];
+       } else 
+#endif
+         if ( *u == ':' && *(u+1) != '\0' && islxdigit( *(u+1)) &&
+                 *(u+2) != '\0' && islxdigit( *(u+2))) {
+           ++u;
+           h = hextoint( *u ) << 4;
+           ++u;
+           h |= hextoint( *u );
+           *m = h;
+       } else 
+           *m = *u;
+
+       /* handle case conversion */
+        if (vol->v_casefold & AFPVOL_UTOMLOWER)
+           *m = diatolower( *m );
+       else if (vol->v_casefold & AFPVOL_UTOMUPPER)
+           *m = diatoupper( *m );
+
+       u++;
+       m++;
+    }
+    *m = '\0';
+    return( mpath );
+}
+
+int afp_addcomment(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct adouble     ad, *adp;
+    struct vol         *vol;
+    struct dir         *dir;
+    struct ofork        *of;
+    char               *path, *name;
+    int                        clen;
+    u_int32_t           did;
+    u_int16_t          vid;
+
+    *rbuflen = 0;
+    ibuf += 2;
+
+    memcpy( &vid, ibuf, sizeof( vid ));
+    ibuf += sizeof( vid );
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    memcpy( &did, ibuf, sizeof( did ));
+    ibuf += sizeof( did );
+    if (( dir = dirsearch( vol, did )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    if ((u_long)ibuf & 1 ) {
+       ibuf++;
+    }
+
+    clen = (u_char)*ibuf++;
+    clen = min( clen, 199 );
+
+    if ((*path == '\0') || !(of = of_findname(vol, curdir, path))) {
+      memset(&ad, 0, sizeof(ad));
+      adp = &ad;
+    } else
+      adp = of->of_ad;
+    if (ad_open( mtoupath( vol, path ), vol_noadouble(vol) | 
+                (( *path == '\0' ) ? ADFLAGS_HF|ADFLAGS_DIR : ADFLAGS_HF),
+                O_RDWR|O_CREAT, 0666, adp) < 0 ) {
+       return( AFPERR_ACCESS );
+    }
+
+    if ( ad_getoflags( adp, ADFLAGS_HF ) & O_CREAT ) {
+       if ( *path == '\0' ) {
+           name = curdir->d_name;
+       } else {
+           name = path;
+       }
+       ad_setentrylen( adp, ADEID_NAME, strlen( name ));
+       memcpy( ad_entry( adp, ADEID_NAME ), name, 
+               ad_getentrylen( adp, ADEID_NAME ));
+    }
+
+    ad_setentrylen( adp, ADEID_COMMENT, clen );
+    memcpy( ad_entry( adp, ADEID_COMMENT ), ibuf, clen );
+    ad_flush( adp, ADFLAGS_HF );
+    ad_close( adp, ADFLAGS_HF );
+    return( AFP_OK );
+}
+
+int afp_getcomment(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct adouble     ad, *adp;
+    struct vol         *vol;
+    struct dir         *dir;
+    struct ofork        *of;
+    char               *path;
+    u_int32_t          did;
+    u_int16_t          vid;
+
+    *rbuflen = 0;
+    ibuf += 2;
+
+    memcpy( &vid, ibuf, sizeof( vid ));
+    ibuf += sizeof( vid );
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    memcpy( &did, ibuf, sizeof( did ));
+    ibuf += sizeof( did );
+    if (( dir = dirsearch( vol, did )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    if ((*path == '\0') || !(of = of_findname(vol, curdir, path))) {
+      memset(&ad, 0, sizeof(ad));
+      adp = &ad;
+    } else
+      adp = of->of_ad;
+    if ( ad_open( mtoupath( vol, path ), 
+                 (( *path == '\0' ) ? ADFLAGS_HF|ADFLAGS_DIR : ADFLAGS_HF), 
+                 O_RDONLY, 0666, adp) < 0 ) {
+       return( AFPERR_NOITEM );
+    }
+
+    /*
+     * Make sure the AD file is not bogus.
+     */
+    if ( ad_getentrylen( adp, ADEID_COMMENT ) < 0 ||
+           ad_getentrylen( adp, ADEID_COMMENT ) > 199 ) {
+       ad_close( adp, ADFLAGS_HF );
+       return( AFPERR_NOITEM );
+    }
+
+    *rbuf++ = ad_getentrylen( adp, ADEID_COMMENT );
+    memcpy( rbuf, ad_entry( adp, ADEID_COMMENT ), 
+           ad_getentrylen( adp, ADEID_COMMENT ));
+    *rbuflen = ad_getentrylen( adp, ADEID_COMMENT ) + 1;
+    ad_close( adp, ADFLAGS_HF );
+    return( AFP_OK );
+}
+
+int afp_rmvcomment(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct adouble     ad, *adp;
+    struct vol         *vol;
+    struct dir         *dir;
+    struct ofork        *of;
+    char               *path;
+    u_int32_t          did;
+    u_int16_t          vid;
+
+    *rbuflen = 0;
+    ibuf += 2;
+
+    memcpy( &vid, ibuf, sizeof( vid ));
+    ibuf += sizeof( vid );
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    memcpy( &did, ibuf, sizeof( did ));
+    ibuf += sizeof( did );
+    if (( dir = dirsearch( vol, did )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    if ((*path == '\0') || !(of = of_findname(vol, curdir, path))) {
+      memset(&ad, 0, sizeof(ad));
+      adp = &ad;
+    } else
+      adp = of->of_ad;
+    if ( ad_open( mtoupath( vol, path ), 
+                 (( *path == '\0' ) ? ADFLAGS_HF|ADFLAGS_DIR : ADFLAGS_HF),
+                 O_RDWR, 0, adp) < 0 ) {
+       switch ( errno ) {
+       case ENOENT :
+           return( AFPERR_NOITEM );
+       case EACCES :
+           return( AFPERR_ACCESS );
+       default :
+           return( AFPERR_PARAM );
+       }
+    }
+
+    ad_setentrylen( adp, ADEID_COMMENT, 0 );
+    ad_flush( adp, ADFLAGS_HF );
+    ad_close( adp, ADFLAGS_HF );
+    return( AFP_OK );
+}
diff --git a/etc/afpd/desktop.h b/etc/afpd/desktop.h
new file mode 100644 (file)
index 0000000..0427a18
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#ifndef AFPD_DESKTOP_H
+#define AFPD_DESKTOP_H 1
+
+#include <sys/cdefs.h>
+#include "globals.h"
+#include "volume.h"
+
+/* various finder info bits */
+#define FINDERINFO_FRFLAGOFF   8
+#define FINDERINFO_FRVIEWOFF  14 
+#define FINDERINFO_INVISIBLE  (1<<14)
+#define FINDERINFO_CLOSEDVIEW 0x100   
+
+struct savedt {
+    u_char     sdt_creator[ 4 ];
+    int                sdt_fd;
+    int                sdt_index;
+    short      sdt_vid;
+};
+
+typedef unsigned char CreatorType[4];
+
+extern char    *dtfile __P((const struct vol *, u_char [], char *));
+extern char    *mtoupath __P((const struct vol *, char *));
+extern char    *utompath __P((const struct vol *, char *));
+extern u_char  ucreator[];
+
+#define validupath(vol, name) ((((vol)->v_flags & AFPVOL_USEDOTS) ? \
+   (strncasecmp((name),".Apple", 6) && \
+    strcasecmp((name), ".Parent")) : (name)[0] != '.'))
+
+/* FP functions */
+extern int     afp_opendt __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_addcomment __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_getcomment __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_rmvcomment __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_addappl __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_rmvappl __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_getappl __P((AFPObj *, char *, int, char *, int *));
+extern int      afp_closedt __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_addicon __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_geticoninfo __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_geticon __P((AFPObj *, char *, int, char *, int *));
+#endif
diff --git a/etc/afpd/directory.c b/etc/afpd/directory.c
new file mode 100644 (file)
index 0000000..6d849ea
--- /dev/null
@@ -0,0 +1,1773 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ *
+ * 19 jan 2000 implemented red-black trees for directory lookups
+ * (asun@cobalt.com).
+ */
+
+#include <sys/syslog.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <netatalk/endian.h>
+#include <atalk/adouble.h>
+#include <atalk/afp.h>
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <utime.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <string.h>
+
+#include "directory.h"
+#include "desktop.h"
+#include "volume.h"
+#include "fork.h"
+#include "file.h"
+#include "globals.h"
+#include "unix.h"
+
+struct dir     *curdir;
+
+#define SENTINEL (&sentinel)
+static struct dir sentinel = { SENTINEL, SENTINEL, NULL, DIRTREE_COLOR_BLACK,
+                              NULL, NULL, NULL, NULL, NULL, 0, 0, NULL };
+static struct dir      rootpar = { SENTINEL, SENTINEL, NULL, 0,
+                                   NULL, NULL, NULL, NULL, NULL, 0, 0, NULL };
+
+/* (from IM: Toolbox Essentials)
+ * dirFinderInfo (DInfo) fields:
+ * field        bytes
+ * frRect       8    folder's window rectangle
+ * frFlags      2    flags
+ * frLocation   4    folder's location in window
+ * frView       2    folder's view (default == closedView (256))
+ *
+ * extended dirFinderInfo (DXInfo) fields:
+ * frScroll     4    scroll position
+ * frOpenChain: 4    directory ID chain of open folders
+ * frScript:    1    script flag and code
+ * frXFlags:    1    reserved
+ * frComment:   2    comment ID
+ * frPutAway:   4    home directory ID
+ */
+
+/*
+ * redid did assignment for directories. now we use red-black trees.
+ * how exciting.
+ */
+    struct dir *
+dirsearch( vol, did )
+    const struct vol   *vol;
+    u_int32_t  did;
+{
+    struct dir *dir;
+    
+
+    /* check for 0 did */
+    if (!did)
+        return NULL;
+
+    if ( did == DIRDID_ROOT_PARENT ) {
+        if (!rootpar.d_did)
+         rootpar.d_did = DIRDID_ROOT_PARENT;
+       rootpar.d_child = vol->v_dir;
+       return( &rootpar );
+    }
+
+    dir = vol->v_root;
+    while ( dir != SENTINEL ) {
+           if (dir->d_did == did)
+               return dir->d_name ? dir : NULL;
+       dir = (dir->d_did > did) ? dir->d_left : dir->d_right;
+    }
+    return NULL;
+}
+
+
+/* rotate the tree to the left */
+static void dir_leftrotate(vol, dir)
+     struct vol *vol;
+     struct dir *dir;
+{
+    struct dir *right = dir->d_right;
+
+    /* whee. move the right's left tree into dir's right tree */
+    dir->d_right = right->d_left; 
+    if (right->d_left != SENTINEL)
+      right->d_left->d_back = dir;
+
+    if (right != SENTINEL) {
+      right->d_back = dir->d_back;
+      right->d_left = dir;
+    }
+
+    if (!dir->d_back) /* no parent. move the right tree to the top. */
+      vol->v_root = right;
+    else if (dir == dir->d_back->d_left) /* we were on the left */
+      dir->d_back->d_left = right;
+    else
+      dir->d_back->d_right = right; /* we were on the right */
+    
+    /* re-insert dir on the left tree */
+    if (dir != SENTINEL)
+      dir->d_back = right;
+}
+
+
+
+/* rotate the tree to the right */
+static void dir_rightrotate(vol, dir)
+     struct vol *vol;
+     struct dir *dir;
+{
+    struct dir *left = dir->d_left;
+
+    /* whee. move the left's right tree into dir's left tree */
+    dir->d_left = left->d_right; 
+    if (left->d_right != SENTINEL)
+      left->d_right->d_back = dir;
+
+    if (left != SENTINEL) {
+      left->d_back = dir->d_back;
+      left->d_right = dir;
+    }
+
+    if (!dir->d_back) /* no parent. move the left tree to the top. */
+      vol->v_root = left;
+    else if (dir == dir->d_back->d_right) /* we were on the right */
+      dir->d_back->d_right = left;
+    else
+      dir->d_back->d_left = left; /* we were on the left */
+    
+    /* re-insert dir on the right tree */
+    if (dir != SENTINEL)
+      dir->d_back = left;
+}
+
+/* recolor after a removal */
+static struct dir *dir_rmrecolor(vol, dir)
+     struct vol *vol;
+     struct dir *dir;
+{
+  struct dir *leaf;
+
+  while ((dir != vol->v_root) && (dir->d_color == DIRTREE_COLOR_BLACK)) {
+    /* are we on the left tree? */
+    if (dir == dir->d_back->d_left) {
+      leaf = dir->d_back->d_right; /* get right side */
+      if (leaf->d_color == DIRTREE_COLOR_RED) {
+       /* we're red. we need to change to black. */
+       leaf->d_color = DIRTREE_COLOR_BLACK;
+       dir->d_back->d_color = DIRTREE_COLOR_RED;
+       dir_leftrotate(vol, dir->d_back);
+       leaf = dir->d_back->d_right;
+      } 
+
+      /* right leaf has black end nodes */
+      if ((leaf->d_left->d_color == DIRTREE_COLOR_BLACK) &&
+         (leaf->d_right->d_color = DIRTREE_COLOR_BLACK)) {
+       leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
+       dir = dir->d_back; /* ascend */
+      } else {
+       if (leaf->d_right->d_color == DIRTREE_COLOR_BLACK) {
+         leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
+         leaf->d_color = DIRTREE_COLOR_RED;
+         dir_rightrotate(vol, leaf);
+         leaf = dir->d_back->d_right;
+       }
+       leaf->d_color = dir->d_back->d_color;
+       dir->d_back->d_color = DIRTREE_COLOR_BLACK;
+       leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
+       dir_leftrotate(vol, dir->d_back);
+       dir = vol->v_root;
+      }
+    } else { /* right tree */
+      leaf = dir->d_back->d_left; /* left tree */
+      if (leaf->d_color == DIRTREE_COLOR_RED) {
+       leaf->d_color = DIRTREE_COLOR_BLACK;
+       dir->d_back->d_color = DIRTREE_COLOR_RED;
+       dir_rightrotate(vol, dir->d_back);
+       leaf = dir->d_back->d_left;
+      }
+
+      /* left leaf has black end nodes */
+      if ((leaf->d_right->d_color == DIRTREE_COLOR_BLACK) &&
+         (leaf->d_left->d_color = DIRTREE_COLOR_BLACK)) {
+       leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
+       dir = dir->d_back; /* ascend */
+      } else {
+       if (leaf->d_left->d_color == DIRTREE_COLOR_BLACK) {
+         leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
+         leaf->d_color = DIRTREE_COLOR_RED;
+         dir_leftrotate(vol, leaf);
+         leaf = dir->d_back->d_left;
+       }
+       leaf->d_color = dir->d_back->d_color;
+       dir->d_back->d_color = DIRTREE_COLOR_BLACK;
+       leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
+       dir_rightrotate(vol, dir->d_back);
+       dir = vol->v_root;
+      }
+    }
+  }
+  dir->d_color = DIRTREE_COLOR_BLACK;
+}
+
+
+/* remove the node from the tree. this is just like insertion, but
+ * different. actually, it has to worry about a bunch of things that
+ * insertion doesn't care about. */
+static void dir_remove( vol, dir )
+    struct vol *vol;
+    struct dir *dir;
+{
+#ifdef REMOVE_NODES
+    struct ofork *of, *last;
+    struct dir *node, *leaf;
+#endif
+
+    if (!dir || (dir == SENTINEL))
+        return;
+
+/* i'm not sure if it really helps to delete stuff. */
+#ifndef REMOVE_NODES 
+    free(dir->d_name);
+    dir->d_name = NULL;
+#else
+
+    /* go searching for a node with at most one child */
+    if ((dir->d_left == SENTINEL) || (dir->d_right == SENTINEL)) {
+      node = dir;
+    } else {
+      node = dir->d_right;
+      while (node->d_left != SENTINEL)
+       node = node->d_left;
+    }
+    
+    /* get that child */
+    leaf = (node->d_left != SENTINEL) ? node->d_left : node->d_right;
+
+    /* detach node */
+    leaf->d_back = node->d_back;
+    if (!node->d_back) {
+      vol->v_root = leaf;
+    } else if (node == node->d_back->d_left) { /* left tree */
+      node->d_back->d_left = leaf;
+    } else {
+      node->d_back->d_right = leaf;
+    }
+
+    /* we want to free node, but we also want to free the data in dir.
+     * currently, that's d_name and the directory traversal bits. 
+     * we just copy the necessary bits and then fix up all the 
+     * various pointers to the directory. needless to say, there are
+     * a bunch of places that store the directory struct. */
+    if (node != dir) {
+      struct dir save, *tmp;
+
+      memcpy(&save, dir, sizeof(save));
+      memcpy(dir, node, sizeof(struct dir));
+
+      /* restore the red-black bits */
+      dir->d_left = save.d_left;
+      dir->d_right = save.d_right;
+      dir->d_back = save.d_back;
+      dir->d_color = save.d_color;
+
+      if (node == vol->v_dir) {/* we may need to fix up this pointer */
+       vol->v_dir = dir;
+       rootpar.d_child = vol->v_dir;
+      } else { 
+         /* if we aren't the root directory, we have parents and
+         * siblings to worry about */
+        if (dir->d_parent->d_child == node)
+                dir->d_parent->d_child = dir;
+        dir->d_next->d_prev = dir;
+        dir->d_prev->d_next = dir;
+      }
+
+      /* fix up children. */
+      tmp = dir->d_child;
+      while (tmp) {
+       tmp->d_parent = dir;
+       tmp = (tmp == dir->d_child->d_prev) ? NULL : tmp->d_next;
+      }
+
+      if (node == curdir) /* another pointer to fixup */
+       curdir = dir;
+
+      /* we also need to fix up oforks. bleah */
+      if ((of = dir->d_ofork)) {
+       last = of->of_d_prev;
+       while (of) {
+         of->of_dir = dir;
+         of = (last == of) ? NULL : of->of_d_next;
+       }
+      }
+
+      /* set the node's d_name */
+      node->d_name = save.d_name;
+    }
+
+    if (node->d_color == DIRTREE_COLOR_BLACK)
+      dir_rmrecolor(vol, leaf);
+    free(node->d_name);
+    free(node);
+#endif
+}
+
+
+static struct dir *dir_insert(vol, dir)
+    const struct vol *vol;
+    struct dir *dir;
+{
+    struct dir *pdir;
+
+    pdir = vol->v_root;
+    while (pdir->d_did != dir->d_did ) {
+       if ( pdir->d_did > dir->d_did ) {
+           if ( pdir->d_left == SENTINEL ) {
+               pdir->d_left = dir;
+               dir->d_back = pdir;
+               return NULL;
+           }
+           pdir = pdir->d_left;
+       } else {
+           if ( pdir->d_right == SENTINEL ) {
+               pdir->d_right = dir;
+               dir->d_back = pdir;
+               return NULL;
+           }
+           pdir = pdir->d_right;
+       }
+    }
+    return pdir;
+}
+
+
+/*
+ * attempt to extend the current dir. tree to include path
+ * as a side-effect, movecwd to that point and return the new dir
+ */
+
+static struct dir *
+extenddir( vol, dir, path )
+    struct vol *vol;
+    struct dir *dir;
+    char       *path;
+{
+    char       *p;
+    struct stat        st;
+
+    p = mtoupath(vol, path );
+    if ( stat( p, &st ) != 0 ) {
+       return( NULL );
+    }
+    if (!S_ISDIR(st.st_mode)) {
+       return( NULL );
+    }
+
+    if (( dir = adddir( vol, dir, path, strlen( path ), p, strlen(p),
+                       &st)) == NULL ) {
+       return( NULL );
+    }
+
+    if ( movecwd( vol, dir ) < 0 ) {
+       return( NULL );
+    }
+
+    return( dir );
+}
+
+static int deletedir(char *dir)
+{
+  char path[MAXPATHLEN + 1];
+  DIR *dp;
+  struct dirent        *de;
+  struct stat st;
+  int len, err;
+  
+  if ((len = strlen(dir)) > sizeof(path))
+    return AFPERR_PARAM;
+
+  /* already gone */
+  if ((dp = opendir(dir)) == NULL)
+    return AFP_OK;
+
+  strcpy(path, dir);
+  strcat(path, "/");
+  len++;
+  while ((de = readdir(dp))) {
+    /* skip this and previous directory */
+    if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+      continue;
+
+    strncpy(path + len, de->d_name, sizeof(path) - len);
+    if (stat(path, &st) == 0) {
+      if (S_ISDIR(st.st_mode)) {
+       if ((err = deletedir(path)) < 0) {
+           closedir(dp);
+           return err;
+       }
+      } else if (unlink(path) < 0) {
+       switch (errno) {
+       case ENOENT :
+         continue; /* somebody went and deleted it behind our backs. */
+       case EROFS:
+         err = AFPERR_VLOCK;
+       case EPERM:
+       case EACCES :
+         err = AFPERR_ACCESS;
+       default :
+         err = AFPERR_PARAM;
+       }
+       closedir(dp);
+       return err;
+      }
+    }
+  }
+  closedir(dp);
+
+  /* okay. the directory is empty. delete it. note: we already got rid
+     of .AppleDouble.  */
+  if (rmdir(dir) < 0) {
+    switch ( errno ) {
+    case ENOENT :
+      break;
+    case ENOTEMPTY : /* should never happen */
+      return( AFPERR_DIRNEMPT );
+    case EPERM:
+    case EACCES :
+      return( AFPERR_ACCESS );
+    case EROFS:
+       return AFPERR_VLOCK;
+    default :
+      return( AFPERR_PARAM );
+    }
+  }
+  return AFP_OK;
+}
+
+/* do a recursive copy. */
+static int copydir(char *src, char *dst, int noadouble)
+{
+  char spath[MAXPATHLEN + 1], dpath[MAXPATHLEN + 1];
+  DIR *dp;
+  struct dirent        *de;
+  struct stat st;
+  struct utimbuf      ut;
+  int slen, dlen, err;
+  
+  /* doesn't exist or the path is too long. */
+  if (((slen = strlen(src)) > sizeof(spath) - 2) || 
+      ((dlen = strlen(dst)) > sizeof(dpath) - 2) ||
+      ((dp = opendir(src)) == NULL))
+    return AFPERR_PARAM;
+
+  /* try to create the destination directory */
+  if (ad_mkdir(dst, DIRBITS | 0777) < 0) {
+      closedir(dp);
+      switch ( errno ) {
+      case ENOENT :
+       return( AFPERR_NOOBJ );
+      case EROFS :
+       return( AFPERR_VLOCK );
+      case EPERM:
+      case EACCES :
+       return( AFPERR_ACCESS );
+      case EEXIST :
+       return( AFPERR_EXIST );
+      case ENOSPC :
+      case EDQUOT :
+       return( AFPERR_DFULL );
+      default :
+       return( AFPERR_PARAM );
+      }
+  }
+
+  /* set things up to copy */
+  strcpy(spath, src);
+  strcat(spath, "/");
+  slen++;
+  strcpy(dpath, dst);
+  strcat(dpath, "/");
+  dlen++;
+  err = AFP_OK;
+  while ((de = readdir(dp))) {
+    /* skip this and previous directory */
+    if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+      continue;
+
+    strncpy(spath + slen, de->d_name, sizeof(spath) - slen);
+    if (stat(spath, &st) == 0) {
+      strncpy(dpath + dlen, de->d_name, sizeof(dpath) - dlen);
+
+      if (S_ISDIR(st.st_mode)) {
+       if ((err = copydir(spath, dpath, noadouble)) < 0) 
+         goto copydir_done;
+      } else if ((err = copyfile(spath, dpath, NULL, noadouble)) < 0) {
+       goto copydir_done;
+
+      } else {
+       /* keep the same time stamp. */   
+       ut.actime = ut.modtime = st.st_mtime;
+       utime(dpath, &ut);
+      }
+    }
+  }
+
+  /* keep the same time stamp. */
+  if (stat(src, &st) == 0) {
+    ut.actime = ut.modtime = st.st_mtime;
+    utime(dst, &ut);
+  }
+
+copydir_done:
+  closedir(dp);
+  return err;
+}
+
+
+/* --- public functions follow --- */
+
+/* NOTE: we start off with at least one node (the root directory). */
+struct dir *dirinsert( vol, dir )
+    struct vol *vol;
+    struct dir *dir;
+{
+    struct dir *node;
+
+    if ((node = dir_insert(vol, dir)))
+      return node;
+
+    /* recolor the tree. the current node is red. */
+    dir->d_color = DIRTREE_COLOR_RED;
+
+    /* parent of this node has to be black. if the parent node
+    * is red, then we have a grandparent. */
+    while ((dir != vol->v_root) && 
+          (dir->d_back->d_color == DIRTREE_COLOR_RED)) {
+      /* are we on the left tree? */
+      if (dir->d_back == dir->d_back->d_back->d_left) {
+       node = dir->d_back->d_back->d_right;  /* get the right node */
+       if (node->d_color == DIRTREE_COLOR_RED) {
+         /* we're red. we need to change to black. */
+         dir->d_back->d_color = DIRTREE_COLOR_BLACK;
+         node->d_color = DIRTREE_COLOR_BLACK;
+         dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
+         dir = dir->d_back->d_back; /* finished. go up. */
+       } else {
+         if (dir == dir->d_back->d_right) {
+           dir = dir->d_back;
+           dir_leftrotate(vol, dir);
+         }
+         dir->d_back->d_color = DIRTREE_COLOR_BLACK;
+         dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
+         dir_rightrotate(vol, dir->d_back->d_back);
+       }
+      } else {
+       node = dir->d_back->d_back->d_left; 
+       if (node->d_color == DIRTREE_COLOR_RED) {
+         /* we're red. we need to change to black. */
+         dir->d_back->d_color = DIRTREE_COLOR_BLACK;
+         node->d_color = DIRTREE_COLOR_BLACK;
+         dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
+         dir = dir->d_back->d_back; /* finished. ascend */
+       } else {
+         if (dir == dir->d_back->d_left) {
+           dir = dir->d_back;
+           dir_rightrotate(vol, dir);
+         }
+         dir->d_back->d_color = DIRTREE_COLOR_BLACK;
+         dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
+         dir_leftrotate(vol, dir->d_back->d_back);
+       }
+      }
+    }
+
+    vol->v_root->d_color = DIRTREE_COLOR_BLACK;
+    return NULL;
+}
+
+/* free everything down. we don't bother to recolor as this is only
+ * called to free the entire tree */
+void dirfree( dir )
+    struct dir *dir;
+{
+    if (!dir || (dir == SENTINEL))
+      return;
+
+    if ( dir->d_left != SENTINEL ) {
+       dirfree( dir->d_left );
+    }
+    if ( dir->d_right != SENTINEL ) {
+       dirfree( dir->d_right );
+    }
+
+    if (dir != SENTINEL) {
+      free( dir->d_name );
+      free( dir );
+    }
+}
+
+
+struct dir *dirnew(const int len)
+{
+   struct dir *dir;
+
+   dir = (struct dir *) calloc(1, sizeof( struct dir ));
+   if (!dir)
+     return NULL;
+
+   if ((dir->d_name = (char *) malloc(sizeof(char)*len)) == NULL) {
+     free(dir);
+     return NULL;
+   }
+   
+   dir->d_left = dir->d_right = SENTINEL;
+   dir->d_next = dir->d_prev = dir;
+   return dir;
+}
+
+
+/* XXX: this needs to be changed to handle path types */
+    char *
+cname( vol, dir, cpath )
+    const struct vol   *vol;
+    struct dir *dir;
+    char       **cpath;
+{
+    struct dir         *cdir;
+    static char                path[ MAXPATHLEN + 1];
+    char               *data, *p;
+    int                        extend = 0;
+    int                        len;
+
+    data = *cpath;
+    if ( *data++ != 2 ) {                      /* path type */
+       return( NULL );
+    }
+    len = (unsigned char) *data++;
+    *cpath += len + 2;
+    *path = '\0';
+
+    for ( ;; ) {
+       if ( len == 0 ) {
+           if ( !extend && movecwd( vol, dir ) < 0 ) {
+               return( NULL );
+           }
+           return( path );
+       }
+
+       if ( *data == '\0' ) {
+           data++;
+           len--;
+       }
+
+       while ( *data == '\0' && len > 0 ) {
+           if ( dir->d_parent == NULL ) {
+               return( NULL );
+           }
+           dir = dir->d_parent;
+           data++;
+           len--;
+       }
+
+       /* would this be faster with strlen + strncpy? */
+       p = path;
+       while ( *data != '\0' && len > 0 ) {
+           *p++ = *data++;
+           len--;
+       }
+
+       /* short cut bits by chopping off a trailing \0. this also
+           makes the traversal happy w/ filenames at the end of the
+           cname. */
+       if (len == 1) 
+         len--;
+
+#ifdef notdef
+       /*
+        * Dung Nguyen <ntd@adb.fr>
+        *
+        * AFPD cannot handle paths with "::" if the "::" notation is
+        * not at the beginning of the path. The following path will not
+        * be interpreted correctly:
+        *
+        * :a:b:::c: (directory c at the same level as directory a) */
+       if ( len > 0 ) {
+           data++;
+           len--;
+       }
+#endif notdef
+       *p = '\0';
+
+       if ( p != path ) { /* we got something */
+           if ( !extend ) {
+               cdir = dir->d_child;
+               while (cdir) {
+                   if ( strcasecmp( cdir->d_name, path ) == 0 ) {
+                       break;
+                   }
+                   cdir = (cdir == dir->d_child->d_prev) ? NULL :
+                           cdir->d_next;
+               }
+               if ( cdir == NULL ) {
+                   ++extend;
+                   if ( movecwd( vol, dir ) < 0 ) {
+                       return( NULL );
+                   }
+                   cdir = extenddir( vol, dir, path );
+               }
+
+           } else {
+               cdir = extenddir( vol, dir, path );
+           }
+
+           if ( cdir == NULL ) {
+               if ( len > 0 ) {
+                   return( NULL );
+               }
+
+           } else {
+               dir = cdir;
+               *path = '\0';
+           }
+       }
+    }
+}
+
+/*
+ * Move curdir to dir, with a possible chdir()
+ */
+int movecwd( vol, dir)
+    const struct vol   *vol;
+    struct dir *dir;
+{
+    char path[MAXPATHLEN + 1];
+    struct dir *d;
+    char       *p, *u;
+    int                n;
+
+    if ( dir == curdir ) {
+       return( 0 );
+    }
+    if ( dir->d_did == DIRDID_ROOT_PARENT) {
+       return( -1 );
+    }
+
+    p = path + sizeof(path) - 1;
+    *p-- = '\0';
+    *p = '.';
+    for ( d = dir; d->d_parent != NULL && d != curdir; d = d->d_parent ) {
+       *--p = '/';
+       u = mtoupath(vol, d->d_name );
+       n = strlen( u );
+       p -= n;
+       strncpy( p, u, n );
+    }
+    if ( d != curdir ) {
+       *--p = '/';
+       n = strlen( vol->v_path );
+       p -= n;
+       strncpy( p, vol->v_path, n );
+    }
+    if ( chdir( p ) < 0 ) {
+       return( -1 );
+    }
+    curdir = dir;
+    return( 0 );
+}
+
+int getdirparams(vol, bitmap, upath, dir, st, buf, buflen )
+    const struct vol          *vol;
+    u_int16_t          bitmap;
+    char               *upath;
+    struct dir         *dir;
+    struct stat                *st;
+    char               *buf;
+    int                        *buflen;
+{
+    struct maccess     ma;
+    struct adouble     ad;
+    char               *data, *nameoff = NULL;
+    DIR                        *dp;
+    struct dirent      *de;
+    int                        bit = 0, isad = 1;
+    u_int32_t           aint;
+    u_int16_t          ashort;
+
+    memset(&ad, 0, sizeof(ad));
+    if ( ad_open( upath, ADFLAGS_HF|ADFLAGS_DIR, O_RDONLY, 
+                 DIRBITS | 0777, &ad) < 0 ) {
+       isad = 0;
+    } 
+
+    data = buf;
+    while ( bitmap != 0 ) {
+       while (( bitmap & 1 ) == 0 ) {
+           bitmap = bitmap>>1;
+           bit++;
+       }
+
+       switch ( bit ) {
+       case DIRPBIT_ATTR :
+           if ( isad ) {
+               ad_getattr(&ad, &ashort);
+           } else if (*upath == '.' && strcmp(upath, ".") && 
+                      strcmp(upath, "..")) {
+               ashort = htons(ATTRBIT_INVISIBLE);
+           } else
+               ashort = 0;
+           memcpy( data, &ashort, sizeof( ashort ));
+           data += sizeof( ashort );
+           break;
+
+       case DIRPBIT_PDID :
+           if ( dir->d_did == DIRDID_ROOT) {
+               aint = DIRDID_ROOT_PARENT;
+           } else if (dir->d_did == DIRDID_ROOT_PARENT) {
+               aint = 0;
+           } else {
+               aint = dir->d_parent->d_did;
+           }
+           memcpy( data, &aint, sizeof( aint ));
+           data += sizeof( aint );
+           break;
+
+       case DIRPBIT_CDATE :
+           if (!isad || (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0)) 
+               aint = AD_DATE_FROM_UNIX(st->st_mtime);
+           memcpy( data, &aint, sizeof( aint ));
+           data += sizeof( aint );
+           break;
+
+       case DIRPBIT_MDATE :
+           aint = AD_DATE_FROM_UNIX(st->st_mtime);
+           memcpy( data, &aint, sizeof( aint ));
+           data += sizeof( aint );
+           break;
+
+       case DIRPBIT_BDATE :
+           if (!isad || (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0))
+               aint = AD_DATE_START;
+           memcpy( data, &aint, sizeof( aint ));
+           data += sizeof( aint );
+           break;
+
+       case DIRPBIT_FINFO :
+           if ( isad ) {
+               memcpy( data, ad_entry( &ad, ADEID_FINDERI ), 32 );
+           } else { /* no appledouble */
+               memset( data, 0, 32 );
+               /* set default view -- this also gets done in ad_open() */
+               ashort = htons(FINDERINFO_CLOSEDVIEW);
+               memcpy(data + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
+
+               /* dot files are by default invisible */
+               if (*upath == '.' && strcmp(upath, ".") &&
+                   strcmp(upath, "..")) {
+                 ashort = htons(FINDERINFO_INVISIBLE);
+                 memcpy(data + FINDERINFO_FRFLAGOFF,
+                        &ashort, sizeof(ashort));
+               }
+           }
+           data += 32;
+           break;
+
+       case DIRPBIT_LNAME :
+           if (dir->d_name) /* root of parent can have a null name */
+             nameoff = data;
+           else 
+             memset(data, 0, sizeof(u_int16_t));
+           data += sizeof( u_int16_t );
+           break;
+
+       case DIRPBIT_SNAME :
+           memset(data, 0, sizeof(u_int16_t));
+           data += sizeof( u_int16_t );
+           break;
+
+       case DIRPBIT_DID :
+           memcpy( data, &dir->d_did, sizeof( aint ));
+           data += sizeof( aint );
+           break;
+
+       case DIRPBIT_OFFCNT :
+           ashort = 0;
+           /* this needs to handle current directory access rights */
+           if ((dp = opendir( upath ))) {
+               while (( de = readdir( dp )) != NULL ) {
+                 if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, "."))
+                   continue;
+
+                 if (!validupath(vol, de->d_name)) 
+                   continue;
+
+                 /* now check against too long a filename */
+                 if (strlen(utompath(vol, de->d_name)) > MACFILELEN)
+                   continue;
+
+                 ashort++;
+               }
+               closedir( dp );
+           }
+           ashort = htons( ashort );
+           memcpy( data, &ashort, sizeof( ashort ));
+           data += sizeof( ashort );
+           break;
+
+       case DIRPBIT_UID :
+           aint = st->st_uid;
+           memcpy( data, &aint, sizeof( aint ));
+           data += sizeof( aint );
+           break;
+
+       case DIRPBIT_GID :
+           aint = st->st_gid;
+           memcpy( data, &aint, sizeof( aint ));
+           data += sizeof( aint );
+           break;
+
+       case DIRPBIT_ACCESS :
+           utommode( st, &ma );
+#ifdef AFS
+           afsmode( upath, &ma, dir );
+#endif AFS
+           *data++ = ma.ma_user;
+           *data++ = ma.ma_world;
+           *data++ = ma.ma_group;
+           *data++ = ma.ma_owner;
+           break;
+
+           /* Client has requested the ProDOS information block.
+              Just pass back the same basic block for all
+              directories. <shirsch@ibm.net> */
+       case DIRPBIT_PDINFO :                     /* ProDOS Info Block */
+           *data++ = 0x0f;
+           *data++ = 0;
+           ashort = htons( 0x0200 );
+           memcpy( data, &ashort, sizeof( ashort ));
+           data += sizeof( ashort );
+           memset( data, 0, sizeof( ashort ));
+           data += sizeof( ashort );
+           break;
+
+       default :
+           if ( isad ) {
+             ad_close( &ad, ADFLAGS_HF );
+           }
+           return( AFPERR_BITMAP );
+       }
+       bitmap = bitmap>>1;
+       bit++;
+    }
+    if ( nameoff ) {
+        ashort = htons( data - buf );
+       memcpy( nameoff, &ashort, sizeof( ashort ));
+
+       if ((aint = strlen( dir->d_name )) > MACFILELEN)
+         aint = MACFILELEN;
+         
+       *data++ = aint;
+       memcpy( data, dir->d_name, aint );
+       data += aint;
+    }
+    if ( isad ) {
+        ad_close( &ad, ADFLAGS_HF );
+    }
+    *buflen = data - buf;
+    return( AFP_OK );
+}
+
+int afp_setdirparams(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct vol *vol;
+    struct dir *dir;
+    char       *path;
+    u_int16_t  vid, bitmap;
+    u_int32_t   did;
+    int                rc;
+
+    *rbuflen = 0;
+    ibuf += 2;
+    memcpy( &vid, ibuf, sizeof( vid ));
+    ibuf += sizeof( vid );
+
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    if (vol->v_flags & AFPVOL_RO)
+        return AFPERR_VLOCK;
+
+    memcpy( &did, ibuf, sizeof( did ));
+    ibuf += sizeof( int );
+
+    if (( dir = dirsearch( vol, did )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    memcpy( &bitmap, ibuf, sizeof( bitmap ));
+    bitmap = ntohs( bitmap );
+    ibuf += sizeof( bitmap );
+
+    if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    /*
+     * If ibuf is odd, make it even.
+     */
+    if ((u_long)ibuf & 1 ) {
+       ibuf++;
+    }
+
+    if (( rc = setdirparams(vol, path, bitmap, ibuf )) == AFP_OK ) {
+       setvoltime(obj, vol );
+    }
+    return( rc );
+}
+
+int setdirparams(vol, path, bitmap, buf )
+    const struct vol          *vol;
+    char               *path, *buf;
+    u_int16_t          bitmap;
+{
+    struct maccess     ma;
+    struct adouble     ad;
+    struct utimbuf      ut;
+    char                *upath;
+    int                        bit = 0, aint, isad = 1;
+    u_int16_t          ashort, bshort;
+    int                 err = AFP_OK;
+
+    upath = mtoupath(vol, path);
+    memset(&ad, 0, sizeof(ad));
+    if (ad_open( upath, vol_noadouble(vol)|ADFLAGS_HF|ADFLAGS_DIR, 
+                O_RDWR|O_CREAT, 0666, &ad) < 0) {
+       /*
+        * Check to see what we're trying to set.  If it's anything
+        * but ACCESS, UID, or GID, give an error.  If it's any of those
+        * three, we don't need the ad to be open, so just continue.
+        *
+        * note: we also don't need to worry about mdate. also, be quiet
+        *       if we're using the noadouble option.
+        */
+       if (!vol_noadouble(vol) && (bitmap &
+               ~((1<<DIRPBIT_ACCESS)|(1<<DIRPBIT_UID)|(1<<DIRPBIT_GID)|
+                 (1<<DIRPBIT_MDATE)|(1<<DIRPBIT_PDINFO))))
+         return AFPERR_ACCESS;
+
+       isad = 0;
+    } else {
+       /*
+        * Check to see if a create was necessary. If it was, we'll want
+        * to set our name, etc.
+        */
+       if ( ad_getoflags( &ad, ADFLAGS_HF ) & O_CREAT ) {
+           ad_setentrylen( &ad, ADEID_NAME, strlen( curdir->d_name ));
+           memcpy( ad_entry( &ad, ADEID_NAME ), curdir->d_name, 
+                   ad_getentrylen( &ad, ADEID_NAME ));
+       }
+    }
+
+    while ( bitmap != 0 ) {
+       while (( bitmap & 1 ) == 0 ) {
+           bitmap = bitmap>>1;
+           bit++;
+       }
+
+       switch( bit ) {
+       case DIRPBIT_ATTR :
+           if (isad) {
+             memcpy( &ashort, buf, sizeof( ashort ));
+             ad_getattr(&ad, &bshort);
+             if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
+               bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
+             } else {
+               bshort &= ~ashort;
+             }
+             ad_setattr(&ad, bshort);
+           }
+           buf += sizeof( ashort );
+           break;
+
+       case DIRPBIT_CDATE :
+           if (isad) {
+             memcpy(&aint, buf, sizeof(aint));
+             ad_setdate(&ad, AD_DATE_CREATE, aint);
+           }
+           buf += sizeof( aint );
+           break;
+
+       case DIRPBIT_MDATE :
+           memcpy(&aint, buf, sizeof(aint));
+           if (isad)
+             ad_setdate(&ad, AD_DATE_MODIFY, aint);
+           ut.actime = ut.modtime = AD_DATE_TO_UNIX(aint);
+           utime(upath, &ut);
+           buf += sizeof( aint );
+           break;
+
+       case DIRPBIT_BDATE :
+           if (isad) {
+             memcpy(&aint, buf, sizeof(aint));
+             ad_setdate(&ad, AD_DATE_BACKUP, aint);
+           }
+           buf += sizeof( aint );
+           break;
+
+       case DIRPBIT_FINFO :
+           /*
+            * Alright, we admit it, this is *really* sick!
+            * The 4 bytes that we don't copy, when we're dealing
+            * with the root of a volume, are the directory's
+            * location information. This eliminates that annoying
+            * behavior one sees when mounting above another mount
+            * point.
+            */
+           if (isad) {
+             if (  curdir->d_did == DIRDID_ROOT ) {
+               memcpy( ad_entry( &ad, ADEID_FINDERI ), buf, 10 );
+               memcpy( ad_entry( &ad, ADEID_FINDERI ) + 14, buf + 14, 18 );
+             } else {
+               memcpy( ad_entry( &ad, ADEID_FINDERI ), buf, 32 );
+             }
+           } 
+           buf += 32;
+           break;
+
+       case DIRPBIT_UID :      /* What kind of loser mounts as root? */
+           buf += sizeof( int );
+           break;
+
+       case DIRPBIT_GID :
+           memcpy( &aint, buf, sizeof( aint ));
+           buf += sizeof( aint );
+           if (curdir->d_did == DIRDID_ROOT)
+             setdeskowner( -1, aint );
+
+#if 0       /* don't error if we can't set the desktop owner. */
+               switch ( errno ) {
+               case EPERM :
+               case EACCES :
+                   err = AFPERR_ACCESS;
+                   goto setdirparam_done;
+                   break;
+               case EROFS :
+                   err = AFPERR_VLOCK;
+                   goto setdirparam_done;
+                   break;
+               default :
+                   syslog( LOG_ERR, "setdirparam: setdeskowner: %m" );
+                   if (!isad) {
+                     err = AFPERR_PARAM;
+                     goto setdirparam_done;
+                   }
+                   break;
+               }
+#endif
+
+           if ( setdirowner( -1, aint, vol_noadouble(vol) ) < 0 ) {
+               switch ( errno ) {
+               case EPERM :
+               case EACCES :
+                   err = AFPERR_ACCESS;
+                   goto setdirparam_done;
+                   break;
+               case EROFS :
+                   err = AFPERR_VLOCK;
+                   goto setdirparam_done;
+                   break;
+               default :
+                   syslog( LOG_ERR, "setdirparam: setdirowner: %m" );
+                   break;
+               }
+           }
+           break;
+
+       case DIRPBIT_ACCESS :
+           ma.ma_user = *buf++;
+           ma.ma_world = *buf++;
+           ma.ma_group = *buf++;
+           ma.ma_owner = *buf++;
+
+           if (curdir->d_did == DIRDID_ROOT) 
+                setdeskmode(mtoumode( &ma ));
+#if 0 /* don't error if we can't set the desktop mode */
+               switch ( errno ) {
+               case EPERM :
+               case EACCES :
+                   err = AFPERR_ACCESS;
+                   goto setdirparam_done;
+               case EROFS :
+                   err = AFPERR_VLOCK;
+                   goto setdirparam_done;
+               default :
+                   syslog( LOG_ERR, "setdirparam: setdeskmode: %m" );
+                   break;
+                   err = AFPERR_PARAM;
+                   goto setdirparam_done;
+               }
+           }
+#endif
+
+           if ( setdirmode( mtoumode( &ma ), vol_noadouble(vol)) < 0 ) {
+               switch ( errno ) {
+               case EPERM :
+               case EACCES :
+                   err = AFPERR_ACCESS;
+                   goto setdirparam_done;
+               case EROFS :
+                   err = AFPERR_VLOCK;
+                   goto setdirparam_done;
+               default :
+                   syslog( LOG_ERR, "setdirparam: setdirmode: %m" );
+                   err = AFPERR_PARAM;
+                   goto setdirparam_done;
+               }
+           }
+           break;
+           
+           /* Ignore what the client thinks we should do to the
+              ProDOS information block.  Skip over the data and
+              report nothing amiss. <shirsch@ibm.net> */
+       case DIRPBIT_PDINFO :
+           buf += 6;
+           break;
+
+       default :
+           err = AFPERR_BITMAP;
+           goto setdirparam_done;
+           break;
+       }
+
+       bitmap = bitmap>>1;
+       bit++;
+    }
+
+
+setdirparam_done:
+    if ( isad ) {
+       ad_flush( &ad, ADFLAGS_HF );
+       ad_close( &ad, ADFLAGS_HF );
+    }
+
+    return err;
+}
+
+int afp_createdir(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct adouble     ad;
+    struct stat         st;
+    struct vol         *vol;
+    struct dir         *dir;
+    char               *path, *upath;
+    u_int32_t          did;
+    u_int16_t          vid;
+
+    *rbuflen = 0;
+    ibuf += 2;
+
+    memcpy( &vid, ibuf, sizeof( vid ));
+    ibuf += sizeof( vid );
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    if (vol->v_flags & AFPVOL_RO)
+        return AFPERR_VLOCK;
+
+    memcpy( &did, ibuf, sizeof( did ));
+    ibuf += sizeof( did );
+    if (( dir = dirsearch( vol, did )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    /* check for illegal bits */
+    if ((vol->v_flags & AFPVOL_MSWINDOWS) && 
+       strpbrk(path, MSWINDOWS_BADCHARS))
+        return AFPERR_PARAM;
+
+    upath = mtoupath(vol, path);
+
+    if ((vol->v_flags & AFPVOL_NOHEX) && strchr(upath, '/'))
+      return AFPERR_PARAM;
+
+    if (!validupath(vol, upath))
+      return AFPERR_EXIST;
+
+    if ( ad_mkdir( upath, DIRBITS | 0777 ) < 0 ) {
+       switch ( errno ) {
+       case ENOENT :
+           return( AFPERR_NOOBJ );
+       case EROFS :
+           return( AFPERR_VLOCK );
+       case EACCES :
+           return( AFPERR_ACCESS );
+       case EEXIST :
+           return( AFPERR_EXIST );
+       case ENOSPC :
+       case EDQUOT :
+           return( AFPERR_DFULL );
+       default :
+           return( AFPERR_PARAM );
+       }
+    }
+
+    if (stat(upath, &st) < 0)
+      return AFPERR_MISC;
+
+    if ((dir = adddir( vol, curdir, path, strlen( path ), upath,
+                      strlen(upath), &st)) == NULL)
+      return AFPERR_MISC;
+
+    if ( movecwd( vol, dir ) < 0 ) {
+       return( AFPERR_PARAM );
+    }
+
+    memset(&ad, 0, sizeof(ad));
+    if (ad_open( "", vol_noadouble(vol)|ADFLAGS_HF|ADFLAGS_DIR,
+                O_RDWR|O_CREAT, 0666, &ad ) < 0)  {
+      if (vol_noadouble(vol))
+         goto createdir_done;
+      return( AFPERR_ACCESS );
+    }
+    
+    ad_setentrylen( &ad, ADEID_NAME, strlen( path ));
+    memcpy( ad_entry( &ad, ADEID_NAME ), path, 
+          ad_getentrylen( &ad, ADEID_NAME ));
+    ad_flush( &ad, ADFLAGS_HF );
+    ad_close( &ad, ADFLAGS_HF );
+
+createdir_done:
+    memcpy( rbuf, &dir->d_did, sizeof( u_int32_t ));
+    *rbuflen = sizeof( u_int32_t );
+    setvoltime(obj, vol );
+    return( AFP_OK );
+}
+
+
+int renamedir(src, dst, dir, newparent, newname, noadouble)
+    char       *src, *dst, *newname;
+    struct dir *dir, *newparent;
+    const int noadouble;
+{
+    struct adouble     ad;
+    struct dir         *parent;
+    char                *buf;
+    int                        len, err;
+
+    /* existence check moved to afp_moveandrename */
+    if ( rename( src, dst ) < 0 ) {
+       switch ( errno ) {
+       case ENOENT :
+           return( AFPERR_NOOBJ );
+       case EACCES :
+           return( AFPERR_ACCESS );
+       case EROFS:
+           return AFPERR_VLOCK;
+       case EINVAL: 
+           /* tried to move directory into a subdirectory of itself */
+           return AFPERR_CANTMOVE;
+       case EXDEV:
+         /* this needs to copy and delete. bleah. that means we have
+          * to deal with entire directory hierarchies. */
+           if ((err = copydir(src, dst, noadouble)) < 0) {
+              deletedir(dst);
+              return err;
+           }
+           if ((err = deletedir(src)) < 0)
+              return err;
+            break;
+       default : 
+           return( AFPERR_PARAM );
+       }
+    }
+
+    memset(&ad, 0, sizeof(ad));
+    if ( ad_open( dst, ADFLAGS_HF|ADFLAGS_DIR, O_RDWR, 0, &ad) < 0 ) {
+       switch ( errno ) {
+       case ENOENT :
+           if (noadouble) {
+             len = strlen(newname);
+             goto renamedir_done;
+           }
+           return( AFPERR_NOOBJ );
+       case EACCES :
+           return( AFPERR_ACCESS );
+       default : 
+           return( AFPERR_PARAM );
+       }
+    }
+    len = strlen( newname );
+    ad_setentrylen( &ad, ADEID_NAME, len );
+    memcpy( ad_entry( &ad, ADEID_NAME ), newname, len );
+    ad_flush( &ad, ADFLAGS_HF );
+    ad_close( &ad, ADFLAGS_HF );
+
+renamedir_done:
+    if ((buf = (char *) realloc( dir->d_name, len + 1 )) == NULL ) {
+       syslog( LOG_ERR, "renamedir: realloc: %m" );
+       return AFPERR_MISC;
+    }
+    dir->d_name = buf;
+    strcpy( dir->d_name, newname );
+
+    if (( parent = dir->d_parent ) == NULL ) {
+       return( AFP_OK );
+    }
+    if ( parent == newparent ) {
+       return( AFP_OK );
+    }
+
+    /* detach from old parent and add to new one. */
+    dirchildremove(parent, dir);
+    dir->d_parent = newparent;
+    dirchildadd(newparent, dir);
+    return( AFP_OK );
+}
+
+#define DOT_APPLEDOUBLE_LEN 13
+/* delete an empty directory */
+int deletecurdir( vol, path, pathlen )
+    const struct vol   *vol;
+    char *path;
+    int pathlen;
+{
+    struct dirent *de;
+    struct stat st;
+    struct dir *fdir;
+    DIR *dp;
+
+    if ( curdir->d_parent == NULL ) {
+       return( AFPERR_ACCESS );
+    }
+
+    if ( curdir->d_child != NULL ) {
+       return( AFPERR_DIRNEMPT );
+    }
+
+    fdir = curdir;
+
+    /* delete stray .AppleDouble files. this happens to get .Parent files
+       as well. */
+    if ((dp = opendir(".AppleDouble"))) {
+      strcpy(path, ".AppleDouble/");
+      while ((de = readdir(dp))) {
+       /* skip this and previous directory */
+       if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+         continue;
+
+       /* bail if the file exists in the current directory.
+        * note: this will not fail with dangling symlinks */
+       if (stat(de->d_name, &st) == 0) {
+         closedir(dp);
+         return AFPERR_DIRNEMPT;
+       }
+
+       strcpy(path + DOT_APPLEDOUBLE_LEN, de->d_name);
+       if (unlink(path) < 0) {
+         closedir(dp);
+         switch (errno) {
+         case EPERM:
+         case EACCES :
+           return( AFPERR_ACCESS );
+         case EROFS:
+           return AFPERR_VLOCK;
+         case ENOENT :
+           continue;
+         default :
+           return( AFPERR_PARAM );
+         }
+       }
+      }
+      closedir(dp);
+    }
+    
+    if ( rmdir( ".AppleDouble" ) < 0 ) {
+       switch ( errno ) {
+       case ENOENT :
+           break;
+       case ENOTEMPTY :
+           return( AFPERR_DIRNEMPT );
+       case EROFS:
+           return AFPERR_VLOCK;
+       case EPERM:
+       case EACCES :
+           return( AFPERR_ACCESS );
+       default :
+           return( AFPERR_PARAM );
+       }
+    }
+
+    /* now get rid of dangling symlinks */
+    if ((dp = opendir("."))) {
+      while ((de = readdir(dp))) {
+       /* skip this and previous directory */
+       if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+         continue;
+
+       /* bail if it's not a symlink */
+       if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode))
+         return AFPERR_DIRNEMPT;
+
+       if (unlink(de->d_name) < 0) {
+         switch (errno) {
+         case EPERM:
+         case EACCES :
+           return( AFPERR_ACCESS );
+         case EROFS:
+           return AFPERR_VLOCK;
+         case ENOENT :
+           continue;
+         default :
+           return( AFPERR_PARAM );
+         }
+       }
+      }
+      closedir(dp);
+    }
+
+    if ( movecwd( vol, curdir->d_parent ) < 0 ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    if ( rmdir(mtoupath(vol, fdir->d_name)) < 0 ) {
+       switch ( errno ) {
+       case ENOENT :
+           return( AFPERR_NOOBJ );
+       case ENOTEMPTY :
+           return( AFPERR_DIRNEMPT );
+       case EPERM:
+       case EACCES :
+           return( AFPERR_ACCESS );
+       case EROFS:
+           return AFPERR_VLOCK;
+       default : 
+           return( AFPERR_PARAM );
+       }
+    }
+
+    dirchildremove(curdir, fdir);
+#if AD_VERSION > AD_VERSION1
+    cnid_delete(vol->v_db, fdir->d_did);
+#endif
+    dir_remove( vol, fdir );
+
+    return( AFP_OK );
+}
+
+int afp_mapid(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct passwd      *pw;
+    struct group       *gr;
+    char               *name;
+    u_int32_t           id;
+    int                        len, sfunc;
+
+    ibuf++;
+    sfunc = (unsigned char) *ibuf++;
+    memcpy( &id, ibuf, sizeof( id ));
+
+    if ( id != 0 ) {
+       switch ( sfunc ) {
+       case 1 :
+           if (( pw = getpwuid( id )) == NULL ) {
+               *rbuflen = 0;
+               return( AFPERR_NOITEM );
+           }
+           name = pw->pw_name;
+           break;
+
+       case 2 :
+           if (( gr = (struct group *)getgrgid( id )) == NULL ) {
+               *rbuflen = 0;
+               return( AFPERR_NOITEM );
+           }
+           name = gr->gr_name;
+           break;
+
+       default :
+           *rbuflen = 0;
+           return( AFPERR_PARAM );
+       }
+
+       len = strlen( name );
+
+    } else {
+       len = 0;
+       name = NULL;
+    }
+
+    *rbuf++ = len;
+    if ( len > 0 ) {
+       memcpy( rbuf, name, len );
+    }
+    *rbuflen = len + 1;
+    return( AFP_OK );
+}
+
+int afp_mapname(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct passwd      *pw;
+    struct group       *gr;
+    int                        len, sfunc;
+    u_int32_t           id;
+
+    ibuf++;
+    sfunc = (unsigned char) *ibuf++;
+    len = (unsigned char) *ibuf++;
+    ibuf[ len ] = '\0';
+
+    if ( len != 0 ) {
+       switch ( sfunc ) {
+       case 3 :
+           if (( pw = (struct passwd *)getpwnam( ibuf )) == NULL ) {
+               *rbuflen = 0;
+               return( AFPERR_NOITEM );
+           }
+           id = pw->pw_uid;
+           break;
+
+       case 4 :
+           if (( gr = (struct group *)getgrnam( ibuf )) == NULL ) {
+               *rbuflen = 0;
+               return( AFPERR_NOITEM );
+           }
+           id = gr->gr_gid;
+           break;
+       default :
+           *rbuflen = 0;
+           return( AFPERR_PARAM );
+       }
+    } else {
+       id = 0;
+    }
+
+    memcpy( rbuf, &id, sizeof( id ));
+    *rbuflen = sizeof( id );
+    return( AFP_OK );
+}
+
+/* variable DID support */
+int afp_closedir(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+#if 0
+    struct vol   *vol;
+    struct dir   *dir;
+    u_int16_t    vid;
+    u_int32_t    did;
+#endif
+
+   *rbuflen = 0;
+
+  /* do nothing as dids are static for the life of the process. */
+#if 0
+    ibuf += 2;
+
+    memcpy(&vid,  ibuf, sizeof( vid ));
+    ibuf += sizeof( vid );
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    memcpy( &did, ibuf, sizeof( did ));
+    ibuf += sizeof( did );
+    if (( dir = dirsearch( vol, did )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    /* dir_remove -- deletedid */
+#endif
+
+    return AFP_OK;
+}
+
+/* did creation gets done automatically */
+int afp_opendir(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct vol         *vol;
+    struct dir         *dir, *parentdir;
+    struct stat         st;
+    char               *path, *upath;
+    u_int32_t          did;
+    u_int16_t          vid;
+
+    *rbuflen = 0;
+    ibuf += 2;
+
+    memcpy(&vid, ibuf, sizeof(vid));
+    ibuf += sizeof( vid );
+
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    memcpy(&did, ibuf, sizeof(did));
+    ibuf += sizeof(did);
+
+    if (( parentdir = dirsearch( vol, did )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    if (( path = cname( vol, parentdir, &ibuf )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    /* see if we already have the directory. */
+    upath = mtoupath(vol, path);
+    if ( stat( upath, &st ) < 0 ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    dir = parentdir->d_child;
+    while (dir) {
+      if (strdiacasecmp(dir->d_name, path) == 0) {
+       memcpy(rbuf, &dir->d_did, sizeof(dir->d_did));
+       *rbuflen = sizeof(dir->d_did);
+       return AFP_OK;
+      }    
+      dir = (dir == parentdir->d_child->d_prev) ? NULL : dir->d_next;
+    }
+
+    /* we don't already have a did. add one in. */
+    if ((dir = adddir(vol, parentdir, path, strlen(path), 
+                     upath, strlen(upath), &st)) == NULL) 
+      return AFPERR_MISC;
+
+    memcpy(rbuf, &dir->d_did, sizeof(dir->d_did));
+    *rbuflen = sizeof(dir->d_did);
+    return AFP_OK;
+}
diff --git a/etc/afpd/directory.h b/etc/afpd/directory.h
new file mode 100644 (file)
index 0000000..87acca8
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#ifndef AFPD_DIRECTORY_H
+#define AFPD_DIRECTORY_H 1
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+/*#include <sys/stat.h>*/ /* including it here causes some confusion */
+#include <netatalk/endian.h>
+
+/* sys/types.h usually snarfs in major/minor macros. if they don't
+ * try this file. */
+#ifndef major
+#include <sys/sysmacros.h>
+#endif
+
+#include "globals.h"
+#include "volume.h"
+
+/* the did tree is now a red-black tree while the parent/child
+ * tree is a circular doubly-linked list. how exciting. */
+struct dir {
+    struct dir *d_left, *d_right, *d_back; /* for red-black tree */
+    int                d_color;
+    struct dir  *d_parent, *d_child; /* parent-child */
+    struct dir  *d_prev, *d_next;    /* siblings */
+    void        *d_ofork;            /* oforks using this directory. */
+    u_int32_t   d_did;
+    int                d_flags;
+    char       *d_name;
+};
+
+/* child addition/removal macros */
+#define dirchildadd(a, b) do { \
+       if (!(a)->d_child) \
+               (a)->d_child = (b); \
+       else { \
+               (b)->d_next = (a)->d_child; \
+               (b)->d_prev = (b)->d_next->d_prev; \
+               (b)->d_next->d_prev = (b); \
+               (b)->d_prev->d_next = (b); \
+       } \
+} while (0)
+
+#define dirchildremove(a,b) do { \
+       if ((a)->d_child == (b)) \
+               (a)->d_child = ((b) == (b)->d_next) ? NULL : (b)->d_next; \
+       (b)->d_next->d_prev = (b)->d_prev; \
+       (b)->d_prev->d_next = (b)->d_next; \
+        (b)->d_next = (b)->d_prev = (b); \
+} while (0)
+
+#define DIRTREE_COLOR_RED    0
+#define DIRTREE_COLOR_BLACK  1
+
+/* setgid directories */
+#ifndef DIRBITS
+#define DIRBITS S_ISGID
+#endif
+
+#define DIRF_FSMASK    (3<<0)
+#define DIRF_NOFS      (0<<0)
+#define DIRF_AFS       (1<<0)
+#define DIRF_UFS       (2<<0)
+
+#define AFPDIR_READ    (1<<0)
+
+/* directory bits */
+#define DIRPBIT_ATTR   0
+#define DIRPBIT_PDID   1
+#define DIRPBIT_CDATE  2
+#define DIRPBIT_MDATE  3
+#define DIRPBIT_BDATE  4
+#define DIRPBIT_FINFO  5
+#define DIRPBIT_LNAME  6
+#define DIRPBIT_SNAME  7
+#define DIRPBIT_DID    8
+#define DIRPBIT_OFFCNT 9
+#define DIRPBIT_UID    10
+#define DIRPBIT_GID    11
+#define DIRPBIT_ACCESS 12
+#define DIRPBIT_PDINFO  13         /* ProDOS Info */
+
+/* directory attribute bits (see file.h for other bits) */
+#define ATTRBIT_EXPFOLDER   (1 << 1) /* shared point */
+#define ATTRBIT_MOUNTED     (1 << 3) /* mounted share point by non-admin */
+#define ATTRBIT_INEXPFOLDER (1 << 4) /* folder in a shared area */
+
+#define FILDIRBIT_ISDIR        (1 << 7) /* is a directory */
+#define FILDIRBIT_ISFILE       (0)      /* is a file */
+
+/* reserved directory id's */
+#define DIRDID_ROOT_PARENT    htonl(1)  /* parent directory of root */
+#define DIRDID_ROOT           htonl(2)  /* root directory */
+
+/* file/directory ids. what a mess. we scramble things in a vain attempt
+ * to get something meaningful */
+#ifndef AFS
+#define CNID_XOR(a)  (((a) >> 16) ^ (a))
+#define CNID_DEV(a)   ((((CNID_XOR(major((a)->st_dev)) & 0xf) << 3) | \
+       (CNID_XOR(minor((a)->st_dev)) & 0x7)) << 24)
+#define CNID_INODE(a) (((a)->st_ino ^ (((a)->st_ino & 0xff000000) >> 8)) \
+                                      & 0x00ffffff)
+#define CNID_FILE(a)  (((a) & 0x1) << 31)
+#define CNID(a,b)     (CNID_DEV(a) | CNID_INODE(a) | CNID_FILE(b))
+#else
+#define CNID(a,b)     (((a)->st_ino & 0x7fffffff) | CNID_FILE(b))
+#endif
+
+
+struct maccess {
+    u_char     ma_user;
+    u_char     ma_world;
+    u_char     ma_group;
+    u_char     ma_owner;
+};
+
+#define        AR_USEARCH      (1<<0)
+#define        AR_UREAD        (1<<1)
+#define        AR_UWRITE       (1<<2)
+#define        AR_UOWN         (1<<7)
+
+extern struct dir       *dirnew __P((const int));
+extern void             dirfree __P((struct dir *));
+extern struct dir      *dirsearch __P((const struct vol *, u_int32_t));
+extern struct dir      *adddir __P((struct vol *, struct dir *, char *,
+                                    int, char *, int, struct stat *));
+extern struct dir       *dirinsert __P((struct vol *, struct dir *));
+extern int              movecwd __P((const struct vol *, struct dir *)); 
+extern int              deletecurdir __P((const struct vol *, char *, int));
+extern char            *cname __P((const struct vol *, struct dir *,
+                                   char **));
+extern mode_t           mtoumode __P((struct maccess *));
+extern void             utommode __P((struct stat *, struct maccess *));
+extern int getdirparams __P((const struct vol *, u_int16_t, char *,
+                            struct dir *, struct stat *, char *, int *));
+extern int setdirparams __P((const struct vol *, char *, u_int16_t, char *));
+extern int renamedir __P((char *, char *, struct dir *, 
+                         struct dir *, char *, const int));
+
+
+/* FP functions */
+extern int     afp_createdir __P((AFPObj *, char *, int, char *, int *));
+extern int      afp_opendir __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_setdirparams __P((AFPObj *, char *, int, char *, int *));
+extern int      afp_closedir __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_mapid __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_mapname __P((AFPObj *, char *, int, char *, int *));
+
+/* from enumerate.c */
+extern int     afp_enumerate __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_catsearch __P((AFPObj *, char *, int, char *, int *));
+
+#endif
diff --git a/etc/afpd/enumerate.c b/etc/afpd/enumerate.c
new file mode 100644 (file)
index 0000000..64b97d4
--- /dev/null
@@ -0,0 +1,428 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <errno.h>
+
+#include <sys/syslog.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/param.h>
+
+#include <netatalk/endian.h>
+#include <atalk/afp.h>
+#include <atalk/adouble.h>
+#include <atalk/cnid.h>
+
+#include "desktop.h"
+#include "directory.h"
+#include "volume.h"
+#include "globals.h"
+#include "file.h"
+
+
+struct dir *
+adddir( vol, dir, name, namlen, upath, upathlen, st )
+    struct vol *vol;
+    struct dir *dir;
+    char       *name, *upath;
+    int                namlen, upathlen;
+    struct stat *st;
+{
+    struct dir *cdir, *edir;
+#if AD_VERSION > AD_VERSION1
+    struct adouble ad;
+#endif
+
+#ifndef USE_LASTDID
+    struct stat lst, *lstp;
+#endif
+
+    if ((cdir = dirnew(namlen + 1)) == NULL) {
+       syslog( LOG_ERR, "adddir: malloc: %m" );
+       return NULL;
+    }
+    strcpy( cdir->d_name, name );
+    cdir->d_name[namlen] = '\0';
+
+#if AD_VERSION > AD_VERSION1
+    /* find out if we have a fixed did already */
+    if (!(cdir->d_did = cnid_lookup(vol->v_db, st, dir->d_did, upath,
+                                   upathlen))) {
+      memset(&ad, 0, sizeof(ad));
+      if (ad_open(upath, ADFLAGS_HF|ADFLAGS_DIR, O_RDONLY, 0, &ad) < 0)
+       cdir->d_did = 0;
+      else {
+       memcpy(&cdir->d_did, ad_entry(&ad, ADEID_DID), sizeof(cdir->d_did));
+       ad_close(&ad, ADFLAGS_HF);
+      }
+      
+      if (!(cdir->d_did = cnid_add(vol->v_db, st, dir->d_did, upath, 
+                                  upathlen, cdir->d_did))) {
+#ifdef USE_LASTDID           
+       cdir->d_did = htonl( vol->v_lastdid++ );
+#else
+       lstp = lstat(upath, &lst) < 0 ? st : &lst;
+       cdir->d_did = htonl( CNID(lstp, 0) );
+#endif
+      }
+    }
+#else
+
+#ifdef USE_LASTDID
+      cdir->d_did = htonl( vol->v_lastdid++ );
+#else
+      lstp = lstat(upath, &lst) < 0 ? st : &lst;
+      cdir->d_did = htonl( CNID(lstp, 0) );
+#endif
+#endif
+
+    if (edir = dirinsert( vol, cdir )) {
+           if (edir->d_name) {
+                   if (strcmp(edir->d_name, cdir->d_name)) {
+                           syslog(LOG_INFO, "WARNING: DID conflict for '%s' and '%s'. Are these the same file?", edir->d_name, cdir->d_name);
+                   }
+                   free(cdir->d_name);
+                   free(cdir);
+                   return edir;
+           }
+           edir->d_name = cdir->d_name;
+           free(cdir);
+           cdir = edir;
+    }
+
+    /* parent/child directories */
+    cdir->d_parent = dir;
+    dirchildadd(dir, cdir);
+    return( cdir );
+}
+
+/*
+ * Struct to save directory reading context in. Used to prevent
+ * O(n^2) searches on a directory.
+ */
+struct savedir {
+    u_short    sd_vid;
+    int                sd_did;
+    int                sd_buflen;
+    char       *sd_buf;
+    char       *sd_last;
+    int                sd_sindex;
+};
+#define SDBUFBRK       1024
+
+int afp_enumerate(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct stat                        st;
+    static struct savedir      sd = { 0, 0, 0, NULL, NULL, 0 };
+    struct vol                 *vol;
+    struct dir                 *dir;
+    struct dirent              *de;
+    DIR                                *dp;
+    int                                did, ret, esz, len, first = 1;
+    char                       *path, *data, *end, *start;
+    u_int16_t                  vid, fbitmap, dbitmap, reqcnt, actcnt = 0;
+    u_int16_t                  sindex, maxsz, sz = 0;
+
+    if ( sd.sd_buflen == 0 ) {
+       if (( sd.sd_buf = (char *)malloc( SDBUFBRK )) == NULL ) {
+           syslog( LOG_ERR, "afp_enumerate: malloc: %m" );
+           *rbuflen = 0;
+           return AFPERR_MISC;
+       }
+       sd.sd_buflen = SDBUFBRK;
+    }
+
+    ibuf += 2;
+
+    memcpy( &vid, ibuf, sizeof( vid ));
+    ibuf += sizeof( vid );
+
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       *rbuflen = 0;
+       return( AFPERR_PARAM );
+    }
+
+    memcpy( &did, ibuf, sizeof( did ));
+    ibuf += sizeof( did );
+
+    if (( dir = dirsearch( vol, did )) == NULL ) {
+       *rbuflen = 0;
+       return( AFPERR_NODIR );
+    }
+
+    memcpy( &fbitmap, ibuf, sizeof( fbitmap ));
+    fbitmap = ntohs( fbitmap );
+    ibuf += sizeof( fbitmap );
+
+    memcpy( &dbitmap, ibuf, sizeof( dbitmap ));
+    dbitmap = ntohs( dbitmap );
+    ibuf += sizeof( dbitmap );
+
+    /* check for proper bitmaps -- the stuff in comments is for
+     * variable directory ids. */
+    if (!(fbitmap || dbitmap) 
+       /*|| (fbitmap & (1 << FILPBIT_PDID)) || 
+         (dbitmap & (1 << DIRPBIT_PDID))*/) {
+      *rbuflen = 0;
+      return AFPERR_BITMAP;
+    }
+
+    memcpy( &reqcnt, ibuf, sizeof( reqcnt ));
+    reqcnt = ntohs( reqcnt );
+    ibuf += sizeof( reqcnt );
+
+    memcpy( &sindex, ibuf, sizeof( sindex ));
+    sindex = ntohs( sindex );
+    ibuf += sizeof( sindex );
+
+    memcpy( &maxsz, ibuf, sizeof( maxsz ));
+    maxsz = ntohs( maxsz );
+    ibuf += sizeof( maxsz );
+
+    if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+       *rbuflen = 0;
+       return( AFPERR_NODIR );
+    }
+    data = rbuf + 3 * sizeof( u_int16_t );
+    sz = 3 * sizeof( u_int16_t );
+
+    /*
+     * Read the directory into a pre-malloced buffer, stored
+     *         len <name> \0
+     * The end is indicated by a len of 0.
+     */
+    if ( sindex == 1 || curdir->d_did != sd.sd_did || vid != sd.sd_vid ) {
+       sd.sd_last = sd.sd_buf;
+
+       if (( dp = opendir( mtoupath(vol, path ))) == NULL ) {
+           *rbuflen = 0;
+           return (errno == ENOTDIR) ? AFPERR_BADTYPE : AFPERR_NODIR;
+       }
+
+       end = sd.sd_buf + sd.sd_buflen;
+       for ( de = readdir( dp ); de != NULL; de = readdir( dp )) {
+           if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, "."))
+             continue;
+
+           if (!(validupath(vol, de->d_name)))
+             continue;
+
+           /* now check against too big a file */
+           if (strlen(utompath(vol, de->d_name)) > MACFILELEN)
+             continue;
+
+           len = strlen(de->d_name);
+           *(sd.sd_last)++ = len;
+
+           if ( sd.sd_last + len + 2 > end ) {
+               char *buf;
+
+               start = sd.sd_buf;
+               if ((buf = (char *) realloc( sd.sd_buf, sd.sd_buflen + 
+                                            SDBUFBRK )) == NULL ) {
+                   syslog( LOG_ERR, "afp_enumerate: realloc: %m" );
+                   closedir(dp);
+                   *rbuflen = 0;
+                   return AFPERR_MISC;
+               }
+               sd.sd_buf = buf;
+               sd.sd_buflen += SDBUFBRK;
+               sd.sd_last = ( sd.sd_last - start ) + sd.sd_buf;
+               end = sd.sd_buf + sd.sd_buflen;
+           }
+
+           memcpy( sd.sd_last, de->d_name, len + 1 );
+           sd.sd_last += len + 1;
+       }
+       *sd.sd_last = 0;
+
+       sd.sd_last = sd.sd_buf;
+       sd.sd_sindex = 1;
+
+       closedir( dp );
+       sd.sd_vid = vid;
+       sd.sd_did = did;
+    }
+
+    /*
+     * Position sd_last as dictated by sindex.
+     */
+    if ( sindex < sd.sd_sindex ) {
+       sd.sd_sindex = 1;
+       sd.sd_last = sd.sd_buf;
+    }
+    while ( sd.sd_sindex < sindex ) {
+       len = *(sd.sd_last)++;
+       if ( len == 0 ) {
+           sd.sd_did = -1;     /* invalidate sd struct to force re-read */
+           *rbuflen = 0;
+           return( AFPERR_NOOBJ );
+       }
+       sd.sd_last += len + 1;
+       sd.sd_sindex++;
+    }
+
+    while (( len = *(sd.sd_last)) != 0 ) {
+       /*
+        * If we've got all we need, send it.
+        */
+       if ( actcnt == reqcnt ) {
+           break;
+       }
+
+       /*
+        * Save the start position, in case we exceed the buffer
+        * limitation, and have to back up one.
+        */
+       start = sd.sd_last;
+       sd.sd_last++;
+
+       if ( stat( sd.sd_last, &st ) < 0 ) {
+           syslog( LOG_DEBUG, "afp_enumerate: stat %s: %m", sd.sd_last );
+           sd.sd_last += len + 1;
+           continue;
+       }
+
+       /*
+        * If a fil/dir is not a dir, it's a file. This is slightly
+        * inaccurate, since that means /dev/null is a file, /dev/printer
+        * is a file, etc.
+        */
+       if ( S_ISDIR(st.st_mode)) {
+           if ( dbitmap == 0 ) {
+               sd.sd_last += len + 1;
+               continue;
+           }
+           path = utompath(vol, sd.sd_last);
+           dir = curdir->d_child; 
+           while (dir) {
+               if ( strcmp( dir->d_name, path ) == 0 ) {
+                   break;
+               }
+               dir = (dir == curdir->d_child->d_prev) ? NULL : dir->d_next;
+           }
+           if (!dir && ((dir = adddir( vol, curdir, path, strlen( path ),
+                                       sd.sd_last, len, &st)) == NULL)) {
+             *rbuflen = 0;
+             return AFPERR_MISC;
+           }
+             
+
+           if (( ret = getdirparams(vol, dbitmap, sd.sd_last, dir,
+                   &st, data + 2 * sizeof( u_char ), &esz )) != AFP_OK ) {
+               *rbuflen = 0;
+               return( ret );
+           }
+
+       } else {
+           if ( fbitmap == 0 ) {
+               sd.sd_last += len + 1;
+               continue;
+           }
+           
+           if (( ret = getfilparams(vol, fbitmap, utompath(vol, sd.sd_last),
+                   curdir, &st, data + 2 * sizeof( u_char ), &esz )) !=
+                   AFP_OK ) {
+               *rbuflen = 0;
+               return( ret );
+           }
+       }
+
+       /*
+        * Make sure entry is an even length, possibly with a null
+        * byte on the end.
+        */
+       if ( esz & 1 ) {
+           *(data + 2 * sizeof( u_char ) + esz ) = '\0';
+           esz++;
+       }
+
+       /*
+        * Check if we've exceeded the size limit.
+        */
+       if ( maxsz < sz + esz + 2 * sizeof( u_char )) {
+           if (first) { /* maxsz can't hold a single reply */
+             *rbuflen = 0; 
+             return AFPERR_PARAM;
+           }
+           sd.sd_last = start;
+           break;
+       }
+
+       if (first)
+         first = 0;
+
+       sz += esz + 2 * sizeof( u_char );
+       *data++ = esz + 2 * sizeof( u_char );
+       *data++ = S_ISDIR(st.st_mode) ? FILDIRBIT_ISDIR : FILDIRBIT_ISFILE;
+       data += esz;
+       actcnt++;
+       sd.sd_last += len + 1;
+    }
+
+    if ( actcnt == 0 ) {
+       *rbuflen = 0;
+       sd.sd_did = -1;         /* invalidate sd struct to force re-read */
+       return( AFPERR_NOOBJ );
+    }
+    sd.sd_sindex = sindex + actcnt;
+
+    /*
+     * All done, fill in misc junk in rbuf
+     */
+    fbitmap = htons( fbitmap );
+    memcpy( rbuf, &fbitmap, sizeof( fbitmap ));
+    rbuf += sizeof( fbitmap );
+    dbitmap = htons( dbitmap );
+    memcpy( rbuf, &dbitmap, sizeof( dbitmap ));
+    rbuf += sizeof( dbitmap );
+    actcnt = htons( actcnt );
+    memcpy( rbuf, &actcnt, sizeof( actcnt ));
+    rbuf += sizeof( actcnt );
+    *rbuflen = sz;
+    return( AFP_OK );
+}
+
+
+/* why is this here? well, FPCatSearch is essentially an FPEnumerate
+ * with filters. */
+int afp_catsearch(AFPObj *obj, char *ibuf, int ibuflen, 
+                 char *rbuf, int *rbuflen)
+{
+    struct vol *vol;
+    u_int16_t   vid;
+
+    ibuf += 2;
+    memcpy(&vid, ibuf, sizeof(vid));
+    ibuf += sizeof(vid);
+
+    *rbuflen = 0;
+    if ((vol = getvolbyvid(vid)) == NULL)
+       return AFPERR_PARAM;
+
+    /* the ritual:
+     * do a breadth-first search of directories:
+     *   lookup did/name info.
+     *   add to result if match
+     *   check to see if we've exceeded our timelimit
+     *     if yes, return current position
+     *     if not, continue
+     * 
+     *   we keep a copy of our current position in struct vol.
+     *   if the next catsearch request for that volume isn't at
+     *   at the current position, bail and return catchanged.
+     */
+
+    /* eof when done */
+    return AFPERR_EOF;
+}     
diff --git a/etc/afpd/file.c b/etc/afpd/file.c
new file mode 100644 (file)
index 0000000..af6da8b
--- /dev/null
@@ -0,0 +1,1489 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <utime.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/mman.h>
+#include <errno.h>
+
+#include <sys/syslog.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <netatalk/endian.h>
+#include <atalk/adouble.h>
+#include <atalk/afp.h>
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+
+#include "directory.h"
+#include "desktop.h"
+#include "volume.h"
+#include "fork.h"
+#include "file.h"
+#include "filedir.h"
+#include "globals.h"
+
+/* the format for the finderinfo fields (from IM: Toolbox Essentials):
+ * field         bytes        subfield    bytes
+ * 
+ * files:
+ * ioFlFndrInfo  16      ->       type    4  type field
+ *                             creator    4  creator field
+ *                               flags    2  finder flags: 
+ *                                          alias, bundle, etc.
+ *                            location    4  location in window
+ *                              folder    2  window that contains file
+ * 
+ * ioFlXFndrInfo 16      ->     iconID    2  icon id
+ *                              unused    6  reserved 
+ *                              script    1  script system
+ *                              xflags    1  reserved
+ *                           commentID    2  comment id
+ *                           putawayID    4  home directory id
+ */
+
+const u_char ufinderi[] = {
+    'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X',
+    0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0
+};
+
+int getfilparams(vol, bitmap, path, dir, st, buf, buflen )
+    struct vol  *vol;
+    u_int16_t  bitmap;
+    char       *path;
+    struct dir *dir;
+    struct stat        *st;
+    char       *buf;
+    int                *buflen;
+{
+    struct stat                hst, lst, *lstp;
+    struct adouble     ad, *adp;
+    struct ofork        *of;
+    struct extmap      *em;
+    char               *data, *nameoff = NULL, *upath;
+    int                        bit = 0, isad = 1, aint;
+    u_int16_t          ashort;
+    u_char              achar, fdType[4];
+
+    upath = mtoupath(vol, path);
+    if ((of = of_findname(vol, curdir, path))) {
+      adp = of->of_ad;
+    } else {
+      memset(&ad, 0, sizeof(ad));
+      adp = &ad;
+    }
+
+    if ( ad_open( upath, ADFLAGS_HF, O_RDONLY, 0, adp) < 0 ) {
+       isad = 0;
+    } else if ( fstat( ad_hfileno( adp ), &hst ) < 0 ) {
+           syslog( LOG_ERR, "getfilparams fstat: %m" );
+    }
+
+    data = buf;
+    while ( bitmap != 0 ) {
+       while (( bitmap & 1 ) == 0 ) {
+           bitmap = bitmap>>1;
+           bit++;
+       }
+
+       switch ( bit ) {
+       case FILPBIT_ATTR :
+           if ( isad ) {
+               ad_getattr(adp, &ashort);
+           } else if (*upath == '.') {
+               ashort = htons(ATTRBIT_INVISIBLE);
+           } else
+               ashort = 0;
+           memcpy(data, &ashort, sizeof( ashort ));
+           data += sizeof( u_short );
+           break;
+
+       case FILPBIT_PDID :
+           memcpy(data, &dir->d_did, sizeof( int ));
+           data += sizeof( int );
+           break;
+
+       case FILPBIT_CDATE :
+           if (!isad || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
+               aint = AD_DATE_FROM_UNIX(st->st_mtime);
+           memcpy(data, &aint, sizeof( aint ));
+           data += sizeof( aint );
+           break;
+
+       case FILPBIT_MDATE :
+           if ( isad && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
+               if ((st->st_mtime > AD_DATE_TO_UNIX(aint)) && 
+                   (hst.st_mtime < st->st_mtime)) {
+                   aint = AD_DATE_FROM_UNIX(st->st_mtime);
+               }
+           } else {
+               aint = AD_DATE_FROM_UNIX(st->st_mtime);
+           }
+           memcpy(data, &aint, sizeof( int ));
+           data += sizeof( int );
+           break;
+
+       case FILPBIT_BDATE :
+           if (!isad || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
+               aint = AD_DATE_START;
+           memcpy(data, &aint, sizeof( int ));
+           data += sizeof( int );
+           break;
+
+       case FILPBIT_FINFO :
+           if (isad) 
+             memcpy(data, ad_entry(adp, ADEID_FINDERI), 32);
+           else {
+             memcpy(data, ufinderi, 32);
+             if (*upath == '.') { /* make it invisible */
+               ashort = htons(FINDERINFO_INVISIBLE);
+               memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
+             }
+           } 
+
+           if ((!isad || (memcmp(ad_entry(adp, ADEID_FINDERI),
+                                 ufinderi, 8 ) == 0)) &&
+               (em = getextmap( path ))) {
+             memcpy(data, em->em_type, sizeof( em->em_type ));
+             memcpy(data + 4, em->em_creator, sizeof(em->em_creator));
+           }
+           data += 32;
+           break;
+
+       case FILPBIT_LNAME :
+           nameoff = data;
+           data += sizeof( u_int16_t );
+           break;
+
+       case FILPBIT_SNAME :
+           memset(data, 0, sizeof(u_int16_t));
+           data += sizeof( u_int16_t );
+           break;
+
+       case FILPBIT_FNUM :
+#if AD_VERSION > AD_VERSION1
+         /* use the CNID database if we're using AD v2 */
+           if (isad)
+             memcpy(&aint, ad_entry(adp, ADEID_DID), sizeof(aint));
+           else
+             aint = 0;
+
+           if (!(aint = cnid_add(vol->v_db, st, dir->d_did, upath, 
+                                 strlen(upath), aint))) {
+#endif
+           /*
+            * What a fucking mess.  First thing:  DID and FNUMs are
+            * in the same space for purposes of enumerate (and several
+            * other wierd places).  While we consider this Apple's bug,
+            * this is the work-around:  In order to maintain constant and
+            * unique DIDs and FNUMs, we monotonically generate the DIDs
+            * during the session, and derive the FNUMs from the filesystem.
+            * Since the DIDs are small, we insure that the FNUMs are fairly
+            * large by setting thier high bits to the device number.
+            *
+            * AFS already does something very similar to this for the
+            * inode number, so we don't repeat the procedure.
+            *
+            * new algorithm:
+            * due to complaints over did's being non-persistent,
+            * here's the current hack to provide semi-persistent
+            * did's: 
+            *      1) we reserve the first bit for file ids.
+            *      2) the next 7 bits are for the device.
+            *      3) the remaining 24 bits are for the inode.
+            *
+            * both the inode and device information are actually hashes
+            * that are then truncated to the requisite bit length.
+            *
+            * it should be okay to use lstat to deal with symlinks.
+            */
+             lstp = (lstat(upath, &lst) < 0) ? st : &lst;
+             aint = htonl(CNID(lstp, 1));
+#if AD_VERSION > AD_VERSION1
+           }
+#endif
+           memcpy(data, &aint, sizeof( aint ));
+           data += sizeof( aint );
+           break;
+
+       case FILPBIT_DFLEN :
+           aint = htonl( st->st_size );
+           memcpy(data, &aint, sizeof( aint ));
+           data += sizeof( aint );
+           break;
+
+       case FILPBIT_RFLEN :
+           if ( isad ) {
+               aint = htonl( ad_getentrylen( adp, ADEID_RFORK ));
+           } else {
+               aint = 0;
+           }
+           memcpy(data, &aint, sizeof( aint ));
+           data += sizeof( aint );
+           break;
+
+           /* Current client needs ProDOS info block for this file.
+              Use simple heuristic and let the Mac "type" string tell
+              us what the PD file code should be.  Everything gets a
+              subtype of 0x0000 unless the original value was hashed
+              to "pXYZ" when we created it.  See IA, Ver 2.
+              <shirsch@ibm.net> */
+       case FILPBIT_PDINFO :
+           if ( isad ) {
+             memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
+             
+             if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
+               achar = '\x04';
+               ashort = 0x0000;
+             }
+             else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
+               achar = '\xff';
+               ashort = 0x0000;
+             }
+             else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
+               achar = '\xb3';
+               ashort = 0x0000;
+             }
+             else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
+               achar = '\x00';
+               ashort = 0x0000;
+             }
+             else if ( fdType[0] == 'p' ) {
+               achar = fdType[1];
+               ashort = (fdType[2] * 256) + fdType[3];
+             } 
+             else {
+               achar = '\x00';
+               ashort = 0x0000;
+             }
+           } 
+           else {
+             achar = '\x00';
+             ashort = 0x0000;
+           }
+           
+           *data++ = achar;
+           *data++ = 0;
+           memcpy(data, &ashort, sizeof( ashort ));
+           data += sizeof( ashort );
+           memset(data, 0, sizeof( ashort ));
+           data += sizeof( ashort );
+           break;
+
+       default :
+           if ( isad ) {
+               ad_close( adp, ADFLAGS_HF );
+           }
+           return( AFPERR_BITMAP );
+       }
+       bitmap = bitmap>>1;
+       bit++;
+    }
+    if ( nameoff ) {
+       ashort = htons( data - buf );
+       memcpy(nameoff, &ashort, sizeof( ashort ));
+       if ((aint = strlen( path )) > MACFILELEN)
+         aint = MACFILELEN;
+       *data++ = aint;
+       memcpy(data, path, aint );
+       data += aint;
+    }
+    if ( isad ) {
+        ad_close( adp, ADFLAGS_HF );
+    }
+    *buflen = data - buf;
+    return( AFP_OK );
+}
+
+int afp_createfile(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct stat         st;
+    struct adouble     ad, *adp;
+    struct vol         *vol;
+    struct dir         *dir;
+    struct ofork        *of;
+    char               *path, *upath;
+    int                        creatf, did, openf;
+    u_int16_t          vid;
+
+    *rbuflen = 0;
+    ibuf++;
+    creatf = (unsigned char) *ibuf++;
+
+    memcpy(&vid, ibuf, sizeof( vid ));
+    ibuf += sizeof( vid );
+
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    if (vol->v_flags & AFPVOL_RO)
+        return AFPERR_VLOCK;
+
+    memcpy(&did, ibuf, sizeof( did));
+    ibuf += sizeof( did );
+
+    if (( dir = dirsearch( vol, did )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    if ((vol->v_flags & AFPVOL_MSWINDOWS) && 
+       strpbrk(path, MSWINDOWS_BADCHARS))
+        return AFPERR_PARAM;
+
+    upath = mtoupath(vol, path);
+
+    if ((vol->v_flags & AFPVOL_NOHEX) && strchr(upath, '/'))
+      return AFPERR_PARAM;
+
+    if (!validupath(vol, upath))
+      return AFPERR_EXIST;
+
+    if ((of = of_findname(vol, curdir, path))) {
+      adp = of->of_ad;
+    } else {
+      memset(&ad, 0, sizeof(ad));
+      adp = &ad;
+    }
+    if ( creatf) {
+        /* on a hard create, fail if file exists and is open */
+        if ((stat(upath, &st) == 0) && of)
+         return AFPERR_BUSY;
+       openf = O_RDWR|O_CREAT|O_TRUNC;
+    } else {
+       openf = O_RDWR|O_CREAT|O_EXCL;
+    }
+
+    if ( ad_open( upath, vol_noadouble(vol)|ADFLAGS_DF|ADFLAGS_HF,
+                 openf, 0666, adp) < 0 ) {
+      switch ( errno ) {
+       case EEXIST :
+           return( AFPERR_EXIST );
+       case EACCES :
+           return( AFPERR_ACCESS );
+        case ENOENT:
+           /* on noadouble volumes, just creating the data fork is ok */
+           if (vol_noadouble(vol) && (stat(upath, &st) == 0))
+             goto createfile_done;
+           /* fallthrough */
+       default :
+           return( AFPERR_PARAM );
+       }
+    }
+
+    ad_setentrylen( adp, ADEID_NAME, strlen( path ));
+    memcpy(ad_entry( adp, ADEID_NAME ), path, 
+          ad_getentrylen( adp, ADEID_NAME ));
+    ad_flush( adp, ADFLAGS_DF|ADFLAGS_HF );
+    ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
+
+createfile_done:
+    setvoltime(obj, vol );
+    return AFP_OK;
+}
+
+int afp_setfilparams(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct vol *vol;
+    struct dir *dir;
+    char       *path;
+    int                did, rc;
+    u_int16_t  vid, bitmap;
+
+    *rbuflen = 0;
+    ibuf += 2;
+
+    memcpy(&vid, ibuf, sizeof( vid ));
+    ibuf += sizeof( vid );
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    if (vol->v_flags & AFPVOL_RO)
+        return AFPERR_VLOCK;
+
+    memcpy(&did, ibuf, sizeof( did ));
+    ibuf += sizeof( did );
+    if (( dir = dirsearch( vol, did )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    memcpy(&bitmap, ibuf, sizeof( bitmap ));
+    bitmap = ntohs( bitmap );
+    ibuf += sizeof( bitmap );
+
+    if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    if ((u_long)ibuf & 1 ) {
+       ibuf++;
+    }
+
+    if (( rc = setfilparams(vol, path, bitmap, ibuf )) == AFP_OK ) {
+       setvoltime(obj, vol );
+    }
+
+    return( rc );
+}
+
+
+int setfilparams(vol, path, bitmap, buf )
+    struct vol  *vol;
+    char       *path, *buf;
+    u_int16_t  bitmap;
+{
+    struct adouble     ad, *adp;
+    struct ofork        *of;
+    struct extmap      *em;
+    int                        bit = 0, isad = 1, err = AFP_OK;
+    char                *upath;
+    u_char              achar, *fdType, xyy[4];
+    u_int16_t          ashort, bshort;
+    u_int32_t          aint;
+    struct utimbuf     ut;
+
+    upath = mtoupath(vol, path);
+    if ((of = of_findname(vol, curdir, path))) {
+      adp = of->of_ad;
+    } else {
+      memset(&ad, 0, sizeof(ad));
+      adp = &ad;
+    }
+    if (ad_open( upath, vol_noadouble(vol) | ADFLAGS_HF, 
+                O_RDWR|O_CREAT, 0666, adp) < 0) {
+      /* for some things, we don't need an adouble header */
+      if (bitmap & ~(1<<FILPBIT_MDATE)) {
+       return vol_noadouble(vol) ? AFP_OK : AFPERR_ACCESS;
+      }
+      isad = 0;
+    } else if ((ad_getoflags( adp, ADFLAGS_HF ) & O_CREAT) ) {
+       ad_setentrylen( adp, ADEID_NAME, strlen( path ));
+       memcpy(ad_entry( adp, ADEID_NAME ), path, 
+              ad_getentrylen( adp, ADEID_NAME ));
+    }
+
+    while ( bitmap != 0 ) {
+       while (( bitmap & 1 ) == 0 ) {
+           bitmap = bitmap>>1;
+           bit++;
+       }
+
+       switch(  bit ) {
+       case FILPBIT_ATTR :
+           memcpy(&ashort, buf, sizeof( ashort ));
+           ad_getattr(adp, &bshort);
+           if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
+             bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
+           } else {
+             bshort &= ~ashort;
+           }
+           ad_setattr(adp, bshort);
+           buf += sizeof( ashort );
+           break;
+
+       case FILPBIT_CDATE :
+           memcpy(&aint, buf, sizeof(aint));
+           ad_setdate(adp, AD_DATE_CREATE, aint);
+           buf += sizeof( aint );
+           break;
+
+       case FILPBIT_MDATE :
+           memcpy(&aint, buf, sizeof( aint ));
+           if (isad)
+             ad_setdate(adp, AD_DATE_MODIFY, aint);
+           ut.actime = ut.modtime = AD_DATE_TO_UNIX(aint);
+           utime(upath, &ut);
+           buf += sizeof( aint );
+           break;
+
+       case FILPBIT_BDATE :
+           memcpy(&aint, buf, sizeof(aint));
+           ad_setdate(adp, AD_DATE_BACKUP, aint);
+           buf += sizeof( aint );
+           break;
+
+       case FILPBIT_FINFO :
+           if ((memcmp( ad_entry( adp, ADEID_FINDERI ), ufinderi, 8 ) == 0)
+               && (em = getextmap( path )) && 
+               (memcmp(buf, em->em_type, sizeof( em->em_type )) == 0) &&
+               (memcmp(buf + 4, em->em_creator,
+                       sizeof( em->em_creator )) == 0)) {
+             memcpy(buf, ufinderi, 8 );
+           }
+           memcpy(ad_entry( adp, ADEID_FINDERI ), buf, 32 );
+           buf += 32;
+           break;
+
+           /* Client needs to set the ProDOS file info for this file.
+              Use defined strings for the simple cases, and convert
+              all else into pXYY per Inside Appletalk.  Always set
+              the creator as "pdos". <shirsch@ibm.net> */
+       case FILPBIT_PDINFO :
+           achar = *buf;
+           buf += 2;
+           memcpy(&ashort, buf, sizeof( ashort ));
+           ashort = ntohs( ashort );
+           buf += 2;
+           switch ( (unsigned int) achar )
+             {
+             case 0x04 :
+               fdType = ( u_char *) "TEXT";
+               break;
+               
+             case 0xff :
+               fdType = ( u_char *) "PSYS";
+               break;
+             case 0xb3 :
+               fdType = ( u_char *) "PS16";
+               break;
+             case 0x00 :
+               fdType = ( u_char *) "BINA";
+               break;
+             default :
+               xyy[0] = ( u_char ) 'p';
+               xyy[1] = achar;
+               xyy[2] = ( u_char ) ( ashort >> 8 ) & 0xff;
+               xyy[3] = ( u_char ) ashort & 0xff;
+               fdType = xyy;
+               break;
+             }
+           
+           memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
+           memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
+           break;
+
+       default :
+           err = AFPERR_BITMAP;
+           goto setfilparam_done;
+       }
+
+       bitmap = bitmap>>1;
+       bit++;
+    }
+
+setfilparam_done:
+    if (isad) {
+      ad_flush( adp, ADFLAGS_HF );
+      ad_close( adp, ADFLAGS_HF );
+    }
+    return err;
+}
+
+/*
+ * renamefile and copyfile take the old and new unix pathnames
+ * and the new mac name.
+ * NOTE: if we have to copy a file instead of renaming it, locks
+ *       will break.
+ */
+int renamefile(src, dst, newname, noadouble )
+    char       *src, *dst, *newname;
+    const int         noadouble;
+{
+    struct adouble     ad;
+    char               adsrc[ MAXPATHLEN + 1];
+    int                        len, rc;
+
+    /*
+     * Note that this is only checking the existance of the data file,
+     * not the header file.  The thinking is that if the data file doesn't
+     * exist, but the header file does, the right thing to do is remove
+     * the data file silently.
+     */
+
+    /* existence check moved to afp_moveandrename */
+
+    if ( rename( src, dst ) < 0 ) {
+       switch ( errno ) {
+       case ENOENT :
+           return( AFPERR_NOOBJ );
+       case EPERM:
+       case EACCES :
+           return( AFPERR_ACCESS );
+       case EROFS:
+           return AFPERR_VLOCK;
+       case EXDEV :                    /* Cross device move -- try copy */
+           if (( rc = copyfile(src, dst, newname, noadouble )) != AFP_OK ) {
+               deletefile( dst );
+               return( rc );
+           }
+           return deletefile( src );
+       default :
+           return( AFPERR_PARAM );
+       }
+    }
+
+    strcpy( adsrc, ad_path( src, 0 ));
+    rc = 0;
+rename_retry:
+    if (rename( adsrc, ad_path( dst, 0 )) < 0 ) {
+        struct stat st;
+
+       switch ( errno ) {
+       case ENOENT :
+         /* check for a source appledouble header. if it exists, make
+          * a dest appledouble directory and do the rename again. */
+         memset(&ad, 0, sizeof(ad));
+         if (rc || stat(adsrc, &st) ||
+             (ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad) < 0))
+           return AFP_OK;
+         rc++;
+         ad_close(&ad, ADFLAGS_HF);
+         goto rename_retry;
+       case EPERM:
+       case EACCES :
+           return( AFPERR_ACCESS );
+       case EROFS:
+           return AFPERR_VLOCK;
+       default :
+           return( AFPERR_PARAM );
+       }
+    }
+
+    memset(&ad, 0, sizeof(ad));
+    if ( ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, &ad) < 0 ) {
+       switch ( errno ) {
+       case ENOENT :
+           return( AFPERR_NOOBJ );
+       case EACCES :
+           return( AFPERR_ACCESS );
+       case EROFS:
+           return AFPERR_VLOCK;
+       default :
+           return( AFPERR_PARAM );
+       }
+    }
+
+    len = strlen( newname );
+    ad_setentrylen( &ad, ADEID_NAME, len );
+    memcpy(ad_entry( &ad, ADEID_NAME ), newname, len );
+    ad_flush( &ad, ADFLAGS_HF );
+    ad_close( &ad, ADFLAGS_HF );
+
+    return( AFP_OK );
+}
+
+int afp_copyfile(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct vol *vol;
+    struct dir *dir;
+    char       *newname, *path, *p;
+    u_int32_t  sdid, ddid;
+    int                plen, err;
+    u_int16_t  svid, dvid;
+
+    *rbuflen = 0;
+    ibuf += 2;
+
+    memcpy(&svid, ibuf, sizeof( svid ));
+    ibuf += sizeof( svid );
+    if (( vol = getvolbyvid( svid )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    memcpy(&sdid, ibuf, sizeof( sdid ));
+    ibuf += sizeof( sdid );
+    if (( dir = dirsearch( vol, sdid )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    memcpy(&dvid, ibuf, sizeof( dvid ));
+    ibuf += sizeof( dvid );
+    memcpy(&ddid, ibuf, sizeof( ddid ));
+    ibuf += sizeof( ddid );
+
+    if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+    if ( *path == '\0' ) {
+       return( AFPERR_BADTYPE );
+    }
+
+    /* don't allow copies when the file is open.
+     * XXX: the spec only calls for read/deny write access.
+     *      however, copyfile doesn't have any of that info,
+     *      and locks need to stay coherent. as a result,
+     *      we just balk if the file is opened already. */
+    if (of_findname(vol, curdir, path))
+      return AFPERR_DENYCONF;
+    
+    newname = obj->newtmp;
+    strcpy( newname, path );
+
+    p = ctoupath( vol, curdir, newname );
+
+    if (( vol = getvolbyvid( dvid )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    if (vol->v_flags & AFPVOL_RO)
+        return AFPERR_VLOCK;
+
+    if (( dir = dirsearch( vol, ddid )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+    if ( *path != '\0' ) {
+       return( AFPERR_BADTYPE );
+    }
+
+    /* one of the handful of places that knows about the path type */
+    if ( *ibuf++ != 2 ) {
+       return( AFPERR_PARAM );
+    }
+    if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
+       strncpy( newname, ibuf, plen );
+       newname[ plen ] = '\0';
+    }
+
+    if ( (err = copyfile(p, mtoupath(vol, newname ), newname, 
+                 vol_noadouble(vol))) < 0 ) {
+       return err;
+    }
+
+    setvoltime(obj, vol );
+    return( AFP_OK );
+}
+
+
+static __inline__ int copy_all(const int dfd, const void *buf,
+                              size_t buflen)
+{
+  ssize_t cc;
+
+  while (buflen > 0) {
+    if ((cc = write(dfd, buf, buflen)) < 0) {
+      switch (errno) {
+      case EINTR:
+       continue;
+      case EDQUOT:
+      case EFBIG:
+      case ENOSPC:
+       return AFPERR_DFULL;
+      case EROFS:
+       return AFPERR_VLOCK;
+      default:
+       return AFPERR_PARAM;
+      }
+    }
+    buflen -= cc;
+  }
+
+  return 0;
+}
+
+/* XXX: this needs to use ad_open and ad_lock. so, we need to
+ * pass in vol and path */
+int copyfile(src, dst, newname, noadouble )
+    char       *src, *dst, *newname;
+    const int   noadouble;
+{
+    struct adouble     ad;
+    struct stat         st;
+    char               filebuf[8192];
+    int                        sfd, dfd, len, err = AFP_OK;
+    ssize_t             cc;
+
+
+    if (newname) { 
+      if ((sfd = open( ad_path( src, ADFLAGS_HF ), O_RDONLY, 0 )) < 0 ) {
+       switch ( errno ) {
+       case ENOENT :
+           break; /* just copy the data fork */
+       case EACCES :
+           return( AFPERR_ACCESS );
+       default :
+           return( AFPERR_PARAM );
+       }
+      } else {
+       if (( dfd = open( ad_path( dst, ADFLAGS_HF ), O_WRONLY|O_CREAT,
+               ad_mode( ad_path( dst, ADFLAGS_HF ), 0666 ))) < 0 ) {
+           close( sfd );
+           switch ( errno ) {
+           case ENOENT :
+               return( AFPERR_NOOBJ );
+           case EACCES :
+               return( AFPERR_ACCESS );
+           case EROFS:
+               return AFPERR_VLOCK;
+           default :
+               return( AFPERR_PARAM );
+           }
+       }
+
+       /* copy the file */
+#ifdef SENDFILE_FLAVOR_LINUX
+       if (fstat(sfd, &st) == 0) {
+         if ((cc = sendfile(dfd, sfd, NULL, st.st_size)) < 0) {
+           switch (errno) {
+           case EDQUOT:
+           case EFBIG:
+           case ENOSPC:
+             err = AFPERR_DFULL;
+             break;
+           case EROFS:
+             err = AFPERR_VLOCK;
+             break;
+           default:
+             err = AFPERR_PARAM;
+           }
+         }
+         goto copyheader_done;
+       }
+#endif
+       while (1) {
+         if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
+           if (errno == EINTR) 
+             continue;
+           err = AFPERR_PARAM;
+           break;
+         }
+
+         if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0))
+           break;
+       }
+
+copyheader_done:
+       close(sfd);
+       close(dfd);
+       if (err < 0) {
+         unlink(ad_path(dst, ADFLAGS_HF));
+         return err;
+       }
+      }
+    }
+
+    /* data fork copying */
+    if (( sfd = open( src, O_RDONLY, 0 )) < 0 ) {
+       switch ( errno ) {
+       case ENOENT :
+           return( AFPERR_NOOBJ );
+       case EACCES :
+           return( AFPERR_ACCESS );
+       default :
+           return( AFPERR_PARAM );
+       }
+    }
+
+    if (( dfd = open( dst, O_WRONLY|O_CREAT, ad_mode( dst, 0666 ))) < 0 ) {
+       close( sfd );
+       switch ( errno ) {
+       case ENOENT :
+           return( AFPERR_NOOBJ );
+       case EACCES :
+           return( AFPERR_ACCESS );
+       case EROFS:
+           return AFPERR_VLOCK;
+       default :
+           return( AFPERR_PARAM );
+       }
+    }
+
+#ifdef SENDFILE_FLAVOR_LINUX
+    if (fstat(sfd, &st) == 0) {
+      if ((cc = sendfile(dfd, sfd, NULL, st.st_size)) < 0) {
+       switch (errno) {
+       case EDQUOT:
+       case EFBIG:
+       case ENOSPC:
+         err = AFPERR_DFULL;
+         break;
+       default:
+         err = AFPERR_PARAM;
+       }
+      }
+      goto copydata_done;
+    }
+#endif
+
+    while (1) {
+      if ((cc = read( sfd, filebuf, sizeof( filebuf ))) < 0) {
+       if (errno == EINTR)
+         continue;
+       
+       err = AFPERR_PARAM;
+       break;
+      }
+      
+      if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
+       break;
+      }
+    }
+    
+copydata_done:
+    close(sfd);
+    close(dfd);
+    if (err < 0) {
+      unlink(ad_path(dst, ADFLAGS_HF));
+      unlink(dst);
+      return err;
+    }
+    
+    if (newname) {
+      memset(&ad, 0, sizeof(ad));
+      if ( ad_open( dst, noadouble | ADFLAGS_HF, O_RDWR|O_CREAT,
+                   0666, &ad) < 0 ) {
+       switch ( errno ) {
+       case ENOENT :
+         return noadouble ? AFP_OK : AFPERR_NOOBJ;
+       case EACCES :
+         return( AFPERR_ACCESS );
+       case EROFS:
+         return AFPERR_VLOCK;
+       default :
+         return( AFPERR_PARAM );
+       }
+      }
+
+      len = strlen( newname );
+      ad_setentrylen( &ad, ADEID_NAME, len );
+      memcpy(ad_entry( &ad, ADEID_NAME ), newname, len );
+      ad_flush( &ad, ADFLAGS_HF );
+      ad_close( &ad, ADFLAGS_HF );
+    }
+    
+    return( AFP_OK );
+}
+
+
+int deletefile( file )
+    char               *file;
+{
+    struct adouble     ad;
+    int                        adflags, err = AFP_OK;
+
+    /* try to open both at once */
+    adflags = ADFLAGS_DF|ADFLAGS_HF;
+    memset(&ad, 0, sizeof(ad));
+    if ( ad_open( file, adflags, O_RDWR, 0, &ad ) < 0 ) {
+         switch (errno) {
+         case ENOENT:
+           adflags = ADFLAGS_DF;
+           /* that failed. now try to open just the data fork */
+           memset(&ad, 0, sizeof(ad));
+           if ( ad_open( file, adflags, O_RDWR, 0, &ad ) < 0 ) {
+             switch (errno) {
+             case ENOENT:
+               return AFPERR_NOOBJ;
+             case EACCES:
+               return AFPERR_ACCESS;
+             default:
+               return AFPERR_PARAM;
+             }
+           }
+           break;
+
+         case EACCES:
+           return( AFPERR_ACCESS );
+         case EROFS:
+           return AFPERR_VLOCK;
+         default:
+           return( AFPERR_PARAM );
+         }
+    }
+
+    if ((adflags & ADFLAGS_HF) &&
+       (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR, 0, 0) < 0 )) {
+      ad_close( &ad, adflags );
+      return( AFPERR_BUSY );
+    }
+
+    if (ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0 ) < 0) {
+      err = AFPERR_BUSY;
+      goto delete_unlock;
+    }
+
+    if ( unlink( ad_path( file, ADFLAGS_HF )) < 0 ) {
+       switch ( errno ) {
+       case EPERM:
+       case EACCES :
+           err = AFPERR_ACCESS;
+           goto delete_unlock;
+       case EROFS:
+           err = AFPERR_VLOCK;
+           goto delete_unlock;
+       case ENOENT :
+           break;
+       default :
+           err = AFPERR_PARAM;
+            goto delete_unlock;
+       }
+    }
+
+    if ( unlink( file ) < 0 ) {
+       switch ( errno ) {
+       case EPERM:
+       case EACCES :
+           err = AFPERR_ACCESS;
+           break;
+       case EROFS:
+           err = AFPERR_VLOCK;
+           break;
+       case ENOENT :
+           break;
+       default :
+           err = AFPERR_PARAM;
+           break;
+       }
+    }
+
+delete_unlock:
+    if (adflags & ADFLAGS_HF)
+      ad_tmplock(&ad, ADEID_RFORK, ADLOCK_CLR, 0, 0);
+    ad_tmplock(&ad, ADEID_DFORK, ADLOCK_CLR, 0, 0);
+    ad_close( &ad, adflags );
+    return err;
+}
+
+
+#if AD_VERSION > AD_VERSION1
+/* return a file id */
+int afp_createid(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct stat         st;
+    struct adouble     ad;
+    struct vol         *vol;
+    struct dir         *dir;
+    char               *path, *upath;
+    int                 len;
+    cnid_t             did, id;
+    u_short            vid;
+    
+
+    *rbuflen = 0;
+    ibuf += 2;
+
+    memcpy(&vid, ibuf, sizeof(vid));
+    ibuf += sizeof(vid);
+
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       return( AFPERR_PARAM);
+    }
+
+    if (vol->v_flags & AFPVOL_RO)
+        return AFPERR_VLOCK;
+
+    memcpy(&did, ibuf, sizeof( did ));
+    ibuf += sizeof(did);
+
+    if (( dir = dirsearch( vol, did )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    if ( *path == '\0' ) {
+       return( AFPERR_BADTYPE );
+    }
+
+    upath = mtoupath(vol, path);
+    if (stat(upath, &st) < 0) {
+      switch (errno) {
+      case EPERM:
+      case EACCES:
+       return AFPERR_ACCESS;
+      case ENOENT:
+       return AFPERR_NOOBJ;
+      default:
+       return AFPERR_PARAM;
+      }
+    }
+
+    if (id = cnid_lookup(vol->v_db, &st, did, upath, len = strlen(upath))) {
+      memcpy(rbuf, &id, sizeof(id));
+      *rbuflen = sizeof(id);
+      return AFPERR_EXISTID;
+    }
+
+    memset(&ad, 0, sizeof(ad));
+    if (ad_open( upath, ADFLAGS_HF, O_RDONLY, 0, &ad ) < 0)
+      id = 0;
+    else {
+      memcpy(&id, ad_entry(&ad, ADEID_DID), sizeof(id));
+      ad_close(upath, ADFLAGS_HF);
+    }
+
+    if (id = cnid_add(vol->v_db, &st, did, upath, len, id)) {
+      memcpy(rbuf, &id, sizeof(id));
+      *rbuflen = sizeof(id);
+      return AFP_OK;
+    }
+
+    switch (errno) {
+    case EROFS:
+      return AFPERR_VLOCK;
+      break;
+    case EPERM:
+    case EACCES:
+      return AFPERR_ACCESS;
+      break;
+    default:
+      syslog(LOG_ERR, "afp_createid: cnid_add: %m");
+      return AFPERR_PARAM;
+    }
+}
+
+/* resolve a file id */
+int afp_resolveid(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct stat         st;
+    struct vol         *vol;
+    struct dir         *dir;
+    char               *upath;
+    int                 err, buflen;
+    cnid_t             id;
+    u_int16_t          vid, bitmap;
+    
+
+    *rbuflen = 0;
+    ibuf += 2;
+
+    memcpy(&vid, ibuf, sizeof(vid));
+    ibuf += sizeof(vid);
+
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       return( AFPERR_PARAM);
+    }
+
+    memcpy(&id, ibuf, sizeof( id ));
+    ibuf += sizeof(id);
+
+    if ((upath = cnid_resolve(vol->v_db, &id)) == NULL) {
+      return AFPERR_BADID;
+    }
+
+    if (( dir = dirsearch( vol, id )) == NULL ) {
+      return( AFPERR_PARAM );
+    }
+
+    if ((movecwd(vol, dir) < 0) || (stat(upath, &st) < 0)) {
+      switch (errno) {
+      case EACCES:
+      case EPERM:
+       return AFPERR_ACCESS;
+      case ENOENT:
+       return AFPERR_NOID;
+      default:
+       return AFPERR_PARAM;
+      }
+    }
+
+    /* directories are bad */
+    if (S_ISDIR(st.st_mode))
+      return AFPERR_BADTYPE;
+
+    memcpy(&bitmap, ibuf, sizeof(bitmap));
+    bitmap = ntohs( bitmap );
+
+    if ((err = getfilparams(vol, bitmap, utompath(vol, upath), curdir, &st,
+                          rbuf + sizeof(bitmap), &buflen)) != AFP_OK)
+      return err;
+
+    *rbuflen = buflen + sizeof(bitmap);
+    memcpy(rbuf, ibuf, sizeof(bitmap));
+    return AFP_OK;
+}
+
+int afp_deleteid(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct stat         st;
+    struct vol         *vol;
+    struct dir         *dir;
+    char                *upath;
+    int                 err;
+    cnid_t             id;
+    u_short            vid;
+    
+
+    *rbuflen = 0;
+    ibuf += 2;
+
+    memcpy(&vid, ibuf, sizeof(vid));
+    ibuf += sizeof(vid);
+
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       return( AFPERR_PARAM);
+    }
+
+    if (vol->v_flags & AFPVOL_RO)
+        return AFPERR_VLOCK;
+
+    memcpy(&id, ibuf, sizeof( id ));
+    ibuf += sizeof(id);
+
+    if ((upath = cnid_resolve(vol->v_db, &id)) == NULL) {
+      return AFPERR_NOID;
+    }
+
+    if (( dir = dirsearch( vol, id )) == NULL ) {
+      return( AFPERR_PARAM );
+    }
+
+    err = AFP_OK;
+    if ((movecwd(vol, dir) < 0) || (stat(upath, &st) < 0)) {
+      switch (errno) {
+      case EACCES:
+      case EPERM:
+       return AFPERR_ACCESS;
+      case ENOENT:
+       /* still try to delete the id */
+       err = AFPERR_NOOBJ;
+       break;
+      default:
+       return AFPERR_PARAM;
+      }
+    }
+
+    /* directories are bad */
+    if (S_ISDIR(st.st_mode))
+      return AFPERR_BADTYPE;
+
+    if (cnid_delete(vol->v_db, id)) {
+      switch (errno) {
+      case EROFS:
+       return AFPERR_VLOCK;
+      case EPERM:
+      case EACCES:
+       return AFPERR_ACCESS;
+      default:
+       return AFPERR_PARAM;
+      }
+    }
+
+    return err;
+}
+#endif
+
+#define APPLETEMP ".AppleTempXXXXXX"
+int afp_exchangefiles(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct stat         srcst, destst;
+    struct vol         *vol;
+    struct dir         *dir, *sdir;
+    char               *spath, temp[17], *path, *p;
+    char                *supath, *upath;
+    int                 err;
+#if AD_VERSION > AD_VERSION1
+    int                 slen, dlen;
+#endif
+    cnid_t             sid, did;
+    u_int16_t          vid;
+    
+
+    *rbuflen = 0;
+    ibuf += 2;
+
+    memcpy(&vid, ibuf, sizeof(vid));
+    ibuf += sizeof(vid);
+
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       return( AFPERR_PARAM);
+    }
+
+    if (vol->v_flags & AFPVOL_RO)
+        return AFPERR_VLOCK;
+
+    /* source and destination dids */
+    memcpy(&sid, ibuf, sizeof(sid));
+    ibuf += sizeof(sid);
+    memcpy(&did, ibuf, sizeof(did));
+    ibuf += sizeof(did);
+
+    /* source file */
+    if ((dir = dirsearch( vol, sid )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    if ( *path == '\0' ) {
+       return( AFPERR_BADTYPE );
+    }
+
+    upath = mtoupath(vol, path);
+    if (stat(upath, &srcst) < 0) {
+      switch (errno) {
+      case ENOENT:
+       return AFPERR_NOID;
+      case EPERM:
+      case EACCES:
+       return AFPERR_ACCESS;
+      default:
+       return AFPERR_PARAM;
+      }
+    }
+
+    /* save some stuff */
+    sdir = curdir;
+    spath = obj->oldtmp;
+    supath = obj->newtmp;
+    strcpy(spath, path);
+    strcpy(supath, upath); /* this is for the cnid changing */
+    p = ctoupath( vol, sdir, spath);
+
+    /* look for the source cnid. if it doesn't exist, don't worry about
+     * it. */
+#if AD_VERSION > AD_VERSION1
+    sid = cnid_lookup(vol->v_db, &srcst, sdir->d_did, supath, 
+                     slen = strlen(supath));
+#endif
+
+    if (( dir = dirsearch( vol, did )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    if ( *path == '\0' ) {
+       return( AFPERR_BADTYPE );
+    }
+
+    /* FPExchangeFiles is the only call that can return the SameObj
+     * error */
+    if ((curdir == sdir) && strcmp(spath, path) == 0)
+      return AFPERR_SAMEOBJ;
+
+    upath = mtoupath(vol, path);
+    if (stat(upath, &destst) < 0) {
+      switch (errno) {
+      case ENOENT:
+       return AFPERR_NOID;
+      case EPERM:
+      case EACCES:
+       return AFPERR_ACCESS;
+      default:
+       return AFPERR_PARAM;
+      }
+    }
+
+#if AD_VERSION > AD_VERSION1
+    /* look for destination id. */
+    did = cnid_lookup(vol->v_db, &destst, curdir->d_did, upath, 
+                     dlen = strlen(upath));
+#endif
+
+    /* construct a temp name. 
+     * NOTE: the temp file will be in the dest file's directory. it
+     * will also be inaccessible from AFP. */
+    memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
+    if (!mktemp(temp))
+      return AFPERR_MISC;
+
+    /* now, quickly rename the file. we error if we can't. */
+    if ((err = renamefile(p, temp, temp, vol_noadouble(vol))) < 0)
+      goto err_exchangefile;
+    of_rename(vol, sdir, spath, curdir, temp);
+
+    /* rename destination to source */
+    if ((err = renamefile(path, p, spath, vol_noadouble(vol))) < 0) 
+      goto err_src_to_tmp;
+    of_rename(vol, curdir, path, sdir, spath);
+
+    /* rename temp to destination */
+    if ((err = renamefile(temp, upath, path, vol_noadouble(vol))) < 0) 
+      goto err_dest_to_src;
+    of_rename(vol, curdir, temp, curdir, path);
+    
+#if AD_VERSION > AD_VERSION1
+    /* id's need switching. src -> dest and dest -> src. */
+    if (sid && (cnid_update(vol->v_db, sid, &destst, curdir->d_did, 
+                           upath, dlen) < 0)) {
+      switch (errno) {
+      case EPERM:
+      case EACCES:
+       err = AFPERR_ACCESS;
+      default:
+       err = AFPERR_PARAM;
+      }
+      goto err_temp_to_dest;
+    }
+
+    if (did && (cnid_update(vol->v_db, did, &srcst, sdir->d_did,
+                           supath, slen) < 0)) {
+      switch (errno) {
+      case EPERM:
+      case EACCES:
+       err = AFPERR_ACCESS;
+      default:
+       err = AFPERR_PARAM;
+      }
+
+      if (sid)
+       cnid_update(vol->v_db, sid, &srcst, sdir->d_did, supath, slen);
+      goto err_temp_to_dest;
+    }
+#endif
+    return AFP_OK;
+
+
+    /* all this stuff is so that we can unwind a failed operation 
+     * properly. */
+err_temp_to_dest:
+    /* rename dest to temp */
+    renamefile(upath, temp, temp, vol_noadouble(vol));
+    of_rename(vol, curdir, upath, curdir, temp);
+
+err_dest_to_src:
+    /* rename source back to dest */
+    renamefile(p, upath, path, vol_noadouble(vol));
+    of_rename(vol, sdir, spath, curdir, path);
+
+err_src_to_tmp:
+    /* rename temp back to source */
+    renamefile(temp, p, spath, vol_noadouble(vol));
+    of_rename(vol, curdir, temp, sdir, spath);
+
+err_exchangefile:
+    return err;
+}
diff --git a/etc/afpd/file.h b/etc/afpd/file.h
new file mode 100644 (file)
index 0000000..4848b50
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#ifndef AFPD_FILE_H 
+#define AFPD_FILE_H 1
+
+/*#include <sys/stat.h>*/ /* including it here causes some confusion */
+#include <sys/param.h>
+#include <sys/cdefs.h>
+#include <netatalk/endian.h>
+#include <atalk/adouble.h>
+
+#include "globals.h"
+#include "volume.h"
+#include "directory.h"
+
+extern const u_char    ufinderi[];
+
+#define FILPBIT_ATTR   0
+#define FILPBIT_PDID   1
+#define FILPBIT_CDATE  2
+#define FILPBIT_MDATE  3
+#define FILPBIT_BDATE  4
+#define FILPBIT_FINFO  5
+#define FILPBIT_LNAME  6
+#define FILPBIT_SNAME  7
+#define FILPBIT_FNUM   8
+#define FILPBIT_DFLEN  9
+#define FILPBIT_RFLEN  10
+#define FILPBIT_PDINFO  13    /* ProDOS Info */
+
+/* attribute bits. (d) = directory attribute bit as well. */
+#define ATTRBIT_INVISIBLE (1<<0)  /* invisible (d) */
+#define ATTRBIT_MULTIUSER (1<<1)  /* multiuser */
+#define ATTRBIT_SYSTEM    (1<<2)  /* system (d) */
+#define ATTRBIT_DOPEN     (1<<3)  /* data fork already open */
+#define ATTRBIT_ROPEN     (1<<4)  /* resource fork already open */
+#define ATTRBIT_NOWRITE   (1<<5)  /* write inhibit(v2)/read-only(v1) bit */
+#define ATTRBIT_BACKUP    (1<<6)  /* backup needed (d) */
+#define ATTRBIT_NORENAME  (1<<7)  /* rename inhibit (d) */
+#define ATTRBIT_NODELETE  (1<<8)  /* delete inhibit (d) */
+#define ATTRBIT_NOCOPY    (1<<10) /* copy protect */
+#define ATTRBIT_SETCLR   (1<<15) /* set/clear bits (d) */
+
+struct extmap {
+    struct extmap      *em_next;
+    char               em_ext[ MAXPATHLEN + 1];
+    char               em_creator[ 4 ];
+    char               em_type[ 4 ];
+};
+
+extern struct extmap   *extmap;
+extern struct extmap   *getextmap __P((const char *));
+
+extern int getfilparams __P((struct vol *, u_int16_t, char *, 
+                            struct dir *, struct stat *, char *buf, 
+                            int *));
+extern int setfilparams __P((struct vol *, char *, u_int16_t, char *));
+extern int renamefile   __P((char *, char *, char *, const int));
+extern int copyfile     __P((char *, char *, char *, const int));
+extern int deletefile   __P((char *));
+
+/* FP functions */
+extern int      afp_exchangefiles __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_setfilparams __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_copyfile __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_createfile __P((AFPObj *, char *, int, char *, int *));
+#if AD_VERSION > AD_VERSION1
+extern int      afp_createid __P((AFPObj *, char *, int, char *, int *));
+extern int      afp_resolveid __P((AFPObj *, char *, int, char *, int *));
+extern int      afp_deleteid __P((AFPObj *, char *, int, char *, int *));
+#else
+#define afp_createid      afp_null
+#define afp_resolveid     afp_null
+#define afp_deleteid      afp_null
+#endif
+
+#endif
diff --git a/etc/afpd/filedir.c b/etc/afpd/filedir.c
new file mode 100644 (file)
index 0000000..f342b78
--- /dev/null
@@ -0,0 +1,556 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#include <errno.h>
+#include <sys/syslog.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <netatalk/endian.h>
+#include <atalk/adouble.h>
+#include <atalk/afp.h>
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <string.h>
+
+#include "directory.h"
+#include "desktop.h"
+#include "volume.h"
+#include "fork.h"
+#include "file.h"
+#include "globals.h"
+#include "filedir.h"
+
+int afp_getfildirparams(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct stat                st;
+    struct vol         *vol;
+    struct dir         *dir;
+    u_int32_t           did;
+    int                        buflen, ret;
+    char               *path;
+    u_int16_t          fbitmap, dbitmap, vid;
+
+    *rbuflen = 0;
+    ibuf += 2;
+
+    memcpy( &vid, ibuf, sizeof( vid ));
+    ibuf += sizeof( vid );
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    memcpy( &did, ibuf, sizeof( did ));
+    ibuf += sizeof( did );
+
+    if (( dir = dirsearch( vol, did )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    memcpy( &fbitmap, ibuf, sizeof( fbitmap ));
+    fbitmap = ntohs( fbitmap );
+    ibuf += sizeof( fbitmap );
+    memcpy( &dbitmap, ibuf, sizeof( dbitmap ));
+    dbitmap = ntohs( dbitmap );
+    ibuf += sizeof( dbitmap );
+
+    if (( path = cname( vol, dir, &ibuf )) == NULL) {
+       return( AFPERR_NOOBJ );
+    }
+
+    if ( stat( mtoupath(vol, path ), &st ) < 0 ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    buflen = 0;
+    if (S_ISDIR(st.st_mode)) {
+       if (dbitmap && ( ret = getdirparams(vol, dbitmap, ".", curdir,
+               &st, rbuf + 3 * sizeof( u_int16_t ), &buflen )) != AFP_OK ) {
+           return( ret );
+       }
+        /* this is a directory */
+       *(rbuf + 2 * sizeof( u_int16_t )) = FILDIRBIT_ISDIR; 
+    } else {
+       if (fbitmap && ( ret = getfilparams(vol, fbitmap, path, curdir, &st,
+               rbuf + 3 * sizeof( u_int16_t ), &buflen )) != AFP_OK ) {
+           return( ret );
+       }
+        /* this is a file */
+       *(rbuf + 2 * sizeof( u_int16_t )) = FILDIRBIT_ISFILE;
+    }
+    *rbuflen = buflen + 3 * sizeof( u_int16_t );
+    fbitmap = htons( fbitmap );
+    memcpy( rbuf, &fbitmap, sizeof( fbitmap ));
+    rbuf += sizeof( fbitmap );
+    dbitmap = htons( dbitmap );
+    memcpy( rbuf, &dbitmap, sizeof( dbitmap ));
+    rbuf += sizeof( dbitmap ) + sizeof( u_char );
+    *rbuf = 0;
+
+    return( AFP_OK );
+}
+
+int afp_setfildirparams(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct stat        st;
+    struct vol *vol;
+    struct dir *dir;
+    char       *path;
+    u_int16_t  vid, bitmap;
+    int                did, rc;
+
+    *rbuflen = 0;
+    ibuf += 2;
+    memcpy( &vid, ibuf, sizeof(vid));
+    ibuf += sizeof( vid );
+
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    if (vol->v_flags & AFPVOL_RO)
+        return AFPERR_VLOCK;
+
+    memcpy( &did, ibuf, sizeof( did));
+    ibuf += sizeof( did);
+
+    if (( dir = dirsearch( vol, did )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    memcpy( &bitmap, ibuf, sizeof( bitmap ));
+    bitmap = ntohs( bitmap );
+    ibuf += sizeof( bitmap );
+
+    if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    if ( stat( mtoupath(vol, path ), &st ) < 0 ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    /*
+     * If ibuf is odd, make it even.
+     */
+    if ((u_long)ibuf & 1 ) {
+       ibuf++;
+    }
+
+    if (S_ISDIR(st.st_mode)) {
+       rc = setdirparams(vol, path, bitmap, ibuf );
+    } else {
+       rc = setfilparams(vol, path, bitmap, ibuf );
+    }
+    if ( rc == AFP_OK ) {
+       setvoltime(obj, vol );
+    }
+    return( rc );
+}
+
+int afp_rename(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct adouble     ad;
+    struct stat                st;
+    struct vol         *vol;
+    struct dir         *dir, *odir = NULL;
+    char               *path, *buf, *upath, *newpath;
+    char               *newadpath;
+    u_int32_t          did;
+    int                        plen;
+    u_int16_t          vid;
+#if AD_VERSION > AD_VERSION1
+    cnid_t              id;
+#endif
+
+    *rbuflen = 0;
+    ibuf += 2;
+
+    memcpy( &vid, ibuf, sizeof( vid ));
+    ibuf += sizeof( vid );
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    if (vol->v_flags & AFPVOL_RO)
+        return AFPERR_VLOCK;
+
+    memcpy( &did, ibuf, sizeof( did ));
+    ibuf += sizeof( did );
+    if (( dir = dirsearch( vol, did )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    /* another place where we know about the path type */
+    if ( *ibuf++ != 2 ) {
+       return( AFPERR_PARAM );
+    }
+    plen = (unsigned char) *ibuf++;
+    *( ibuf + plen ) = '\0';
+
+    if ( *path == '\0' ) {
+        if ( curdir->d_parent == NULL ) { /* root directory */
+           return( AFPERR_NORENAME );
+       }
+       odir = curdir;
+       path = curdir->d_name;
+       if ( movecwd( vol, curdir->d_parent ) < 0 ) {
+           return( AFPERR_NOOBJ );
+       }
+    }
+
+#ifdef notdef
+    if ( strcasecmp( path, ibuf ) == 0 ) {
+       return( AFP_OK );
+    }
+#endif notdef
+
+    /* if a curdir/newname ofork exists, return busy */
+    if (of_findname(vol, curdir, ibuf))
+        return AFPERR_BUSY;
+
+    /* source == destination. just say okay. */
+    if (strcmp(path, ibuf) == 0)
+        return AFP_OK;
+
+    /* check for illegal characters */
+    if ((vol->v_flags & AFPVOL_MSWINDOWS) && 
+       strpbrk(ibuf, MSWINDOWS_BADCHARS))
+        return AFPERR_PARAM;
+
+    newpath = obj->oldtmp;
+    strcpy( newpath, mtoupath(vol, ibuf ));
+
+    if ((vol->v_flags & AFPVOL_NOHEX) && strchr(newpath, '/'))
+      return AFPERR_PARAM;
+
+    if (!validupath(vol, newpath))
+      return AFPERR_EXIST;
+
+    /* the strdiacasecmp deals with case-insensitive, case preserving
+       filesystems */
+    if (stat( newpath, &st ) == 0 && strdiacasecmp(path, ibuf))
+       return( AFPERR_EXIST );
+
+    upath = mtoupath(vol, path);
+
+#if AD_VERSION > AD_VERSION1
+    id = cnid_get(vol->v_db, curdir->d_did, upath, strlen(upath));
+#endif
+
+    if ( rename( upath, newpath ) < 0 ) {
+       switch ( errno ) {
+       case ENOENT :
+           return( AFPERR_NOOBJ );
+       case EACCES :
+           return( AFPERR_ACCESS );
+       default :
+           return( AFPERR_PARAM );
+       }
+    }
+
+#if AD_VERSION > AD_VERSION1
+    if (stat(newpath, &st) < 0) /* this shouldn't fail */
+      return AFPERR_MISC;
+    cnid_update(vol->v_db, id, &st, curdir->d_did, newpath, strlen(newpath));
+#endif
+
+    if ( !odir ) {
+        newadpath = obj->newtmp;
+       strcpy( newadpath, ad_path( newpath, 0 ));
+       if ( rename( ad_path( upath, 0 ), newadpath ) < 0 ) {
+           if ( errno == ENOENT ) {    /* no adouble header file */
+               if (( unlink( newadpath ) < 0 ) && ( errno != ENOENT )) {
+                   return( AFPERR_PARAM );
+               }
+               goto out;
+           }
+           return( AFPERR_PARAM );
+       }
+
+       memset(&ad, 0, sizeof(ad));
+       if ( ad_open( newpath, ADFLAGS_HF, O_RDWR|O_CREAT, 0666,
+                     &ad) < 0 ) {
+           return( AFPERR_PARAM );
+       }
+    } else {
+        int isad = 1;
+
+       memset(&ad, 0, sizeof(ad));
+       if ( ad_open( newpath, vol_noadouble(vol)|ADFLAGS_HF|ADFLAGS_DIR, 
+                     O_RDWR|O_CREAT, 0666, &ad) < 0 ) {
+           if (!((errno == ENOENT) && vol_noadouble(vol)))
+             return( AFPERR_PARAM );
+           isad = 0;
+       }
+       if ((buf = realloc( odir->d_name, plen + 1 )) == NULL ) {
+           syslog( LOG_ERR, "afp_rename: realloc: %m" );
+           if (isad) {
+             ad_flush(&ad, ADFLAGS_HF); /* in case of create */
+             ad_close(&ad, ADFLAGS_HF);
+           }
+           return AFPERR_MISC;
+       }
+       odir->d_name = buf;
+       strcpy( odir->d_name, ibuf );
+       if (!isad)
+         goto out;
+    }
+
+    ad_setentrylen( &ad, ADEID_NAME, plen );
+    memcpy( ad_entry( &ad, ADEID_NAME ), ibuf, plen );
+    ad_flush( &ad, ADFLAGS_HF );
+    ad_close( &ad, ADFLAGS_HF );
+
+out:
+    setvoltime(obj, vol );
+
+    /* if it's still open, rename the ofork as well. */
+    if (of_rename(vol, curdir, path, curdir, ibuf) < 0)
+       return AFPERR_MISC;
+
+    return( AFP_OK );
+}
+
+
+int afp_delete(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct vol         *vol;
+    struct dir         *dir;
+    char               *path, *upath;
+    int                        did, rc;
+    u_int16_t          vid;
+
+    *rbuflen = 0;
+    ibuf += 2;
+
+    memcpy( &vid, ibuf, sizeof( vid ));
+    ibuf += sizeof( vid );
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    if (vol->v_flags & AFPVOL_RO)
+        return AFPERR_VLOCK;
+
+    memcpy( &did, ibuf, sizeof( did ));
+    ibuf += sizeof( int );
+    if (( dir = dirsearch( vol, did )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    if ( *path == '\0' ) {
+       rc = deletecurdir( vol, obj->oldtmp, AFPOBJ_TMPSIZ);
+    } else if (of_findname(vol, curdir, path)) {
+        rc = AFPERR_BUSY;
+    } else if ((rc = deletefile( upath = mtoupath(vol, path ))) == AFP_OK) {
+#if AD_VERSION > AD_VERSION1 /* get rid of entry */
+        cnid_t id = cnid_get(vol->v_db, curdir->d_did, upath, strlen(upath));
+       cnid_delete(vol->v_db, id);
+#endif
+    }
+    if ( rc == AFP_OK ) {
+       setvoltime(obj, vol );
+    }
+    return( rc );
+}
+
+char *ctoupath( vol, dir, name )
+    const struct vol   *vol;
+    struct dir *dir;
+    char       *name;
+{
+    struct dir *d;
+    static char        path[ MAXPATHLEN + 1];
+    char       *p, *u;
+    int                len;
+
+    p = path + sizeof( path ) - 1;
+    *p = '\0';
+    u = mtoupath(vol, name );
+    len = strlen( u );
+    p -= len;
+    strncpy( p, u, len );
+    for ( d = dir; d->d_parent; d = d->d_parent ) {
+       *--p = '/';
+       u = mtoupath(vol, d->d_name );
+       len = strlen( u );
+       p -= len;
+       strncpy( p, u, len );
+    }
+    *--p = '/';
+    len = strlen( vol->v_path );
+    p -= len;
+    strncpy( p, vol->v_path, len );
+
+    return( p );
+}
+
+
+int afp_moveandrename(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct vol *vol;
+    struct dir *sdir, *ddir, *odir = NULL;
+    struct stat st;
+    char       *oldname, *newname;
+    char        *path, *p, *upath; 
+    int                did, rc;
+    int                plen;
+    u_int16_t  vid;
+#if AD_VERSION > AD_VERSION1
+    cnid_t      id;
+#endif
+
+    *rbuflen = 0;
+    ibuf += 2;
+
+    memcpy( &vid, ibuf, sizeof( vid ));
+    ibuf += sizeof( vid );
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    if (vol->v_flags & AFPVOL_RO)
+        return AFPERR_VLOCK;
+
+    /* source did followed by dest did */
+    memcpy( &did, ibuf, sizeof( did ));
+    ibuf += sizeof( int );
+    if (( sdir = dirsearch( vol, did )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    memcpy( &did, ibuf, sizeof( did ));
+    ibuf += sizeof( int );
+
+    /* source pathname */
+    if (( path = cname( vol, sdir, &ibuf )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    sdir = curdir;
+    newname = obj->newtmp;
+    oldname = obj->oldtmp;
+    if ( *path != '\0' ) {
+        /* not a directory */
+       strcpy(newname, path);
+       strcpy(oldname, path); /* an extra copy for of_rename */
+#if AD_VERSION > AD_VERSION1
+       p = mtoupath(vol, path);
+       id = cnid_get(vol->v_db, sdir->d_did, p, strlen(p));
+#endif
+       p = ctoupath( vol, sdir, newname );
+    } else {
+       odir = curdir;
+       strcpy( newname, odir->d_name );
+       strcpy(oldname, odir->d_name); 
+       p = ctoupath( vol, odir->d_parent, newname );
+#if AD_VERSION > AD_VERSION1
+       id = curdir->d_did; /* we already have the CNID */
+#endif
+    }
+    /*
+     * p now points to the full pathname of the source fs object.
+     */
+
+    /* get the destination directory */
+    if (( ddir = dirsearch( vol, did )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+    if (( path = cname( vol, ddir, &ibuf )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+    if ( *path != '\0' ) {
+       return( AFPERR_BADTYPE );
+    }
+
+    /* one more place where we know about path type */
+    if ( *ibuf++ != 2 ) {
+       return( AFPERR_PARAM );
+    }
+
+    if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
+       strncpy( newname, ibuf, plen );
+       newname[ plen ] = '\0';
+    }
+
+    /* check for illegal characters */
+    if ((vol->v_flags & AFPVOL_MSWINDOWS) && 
+       strpbrk(newname, MSWINDOWS_BADCHARS))
+        return AFPERR_PARAM;
+
+    upath = mtoupath(vol, newname);
+
+    if ((vol->v_flags & AFPVOL_NOHEX) && strchr(upath, '/'))
+      return AFPERR_PARAM;
+
+    if (!validupath(vol, upath))
+      return AFPERR_EXIST;
+
+    /* source == destination. we just silently accept this. */
+    if (curdir == sdir) {
+      if (strcmp(oldname, newname) == 0)
+       return AFP_OK;
+      
+      /* deal with case insensitive, case-preserving filesystems. */
+      if ((stat(upath, &st) == 0) && strdiacasecmp(oldname, newname)) 
+       return AFPERR_EXIST;
+      
+    } else if (stat(upath, &st ) == 0)
+      return( AFPERR_EXIST );
+      
+    if ( !odir ) {
+      if (of_findname(vol, curdir, newname)) {
+       rc = AFPERR_BUSY;
+      } else if ((rc = renamefile( p, upath, newname, 
+                                  vol_noadouble(vol) )) == AFP_OK) {
+       /* if it's still open, rename the ofork as well. */
+       rc = of_rename(vol, sdir, oldname, curdir, newname);
+      }
+    } else {
+       rc = renamedir(p, upath, odir, curdir, newname, vol_noadouble(vol));
+    }
+
+    if ( rc == AFP_OK ) {
+#if AD_VERSION > AD_VERSION1
+        /* renaming may have moved the file/dir across a filesystem */
+        if (stat(upath, &st) < 0) 
+         return AFPERR_MISC;
+       
+       /* fix up the catalog entry */
+       cnid_update(vol->v_db, id, &st, curdir->d_did, upath, strlen(upath));
+#endif      
+       setvoltime(obj, vol );
+    }
+    return( rc );
+}
+
diff --git a/etc/afpd/filedir.h b/etc/afpd/filedir.h
new file mode 100644 (file)
index 0000000..c20bc61
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef AFPD_FILEDIR_H
+#define AFPD_FILEDIR_H 1
+
+#include <sys/cdefs.h>
+#include "globals.h"
+#include "volume.h"
+
+extern char            *ctoupath __P((const struct vol *, struct dir *, 
+                                      char *));
+
+/* FP functions */
+extern int     afp_moveandrename __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_rename __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_delete __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_getfildirparams __P((AFPObj *, char *, int, char *, 
+                                        int *));
+extern int     afp_setfildirparams __P((AFPObj *, char *, int, char *, 
+                                        int *));
+#endif
diff --git a/etc/afpd/fork.c b/etc/afpd/fork.c
new file mode 100644 (file)
index 0000000..7b1b8ab
--- /dev/null
@@ -0,0 +1,1297 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <string.h>
+#include <errno.h>
+#include <syslog.h>
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+
+#include <atalk/dsi.h>
+#include <atalk/atp.h>
+#include <atalk/asp.h>
+#include <atalk/afp.h>
+#include <atalk/adouble.h>
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+
+#include "fork.h"
+#include "file.h"
+#include "globals.h"
+#include "directory.h"
+#include "desktop.h"
+#include "volume.h"
+
+#define BYTELOCK_MAX 0x7FFFFFFFU
+
+struct ofork           *writtenfork;
+
+static int getforkparams(ofork, bitmap, buf, buflen, attrbits )
+    struct ofork       *ofork;
+    u_int16_t          bitmap;
+    char               *buf;
+    int                        *buflen;
+    const u_int16_t     attrbits;
+{
+    struct stat                st;
+    struct extmap      *em;
+    char               *data, *nameoff = NULL, *upath;
+    int                        bit = 0, isad = 1;
+    u_int32_t           aint;
+    u_int16_t          ashort;
+
+    if ( ad_hfileno( ofork->of_ad ) == -1 ) {
+       isad = 0;
+    } else {
+       aint = ad_getentrylen( ofork->of_ad, ADEID_RFORK );
+       if ( ad_refresh( ofork->of_ad ) < 0 ) {
+           syslog( LOG_ERR, "getforkparams: ad_refresh: %m");
+           return( AFPERR_PARAM );
+       }
+       /* See afp_closefork() for why this is bad */
+       ad_setentrylen( ofork->of_ad, ADEID_RFORK, aint );
+    }
+
+    /* can only get the length of the opened fork */
+    if (((bitmap & (1<<FILPBIT_DFLEN)) && (ofork->of_flags & AFPFORK_RSRC)) ||
+       ((bitmap & (1<<FILPBIT_RFLEN)) && (ofork->of_flags & AFPFORK_DATA))) {
+       return( AFPERR_BITMAP );
+    }
+
+    if ( bitmap & ( 1<<FILPBIT_DFLEN | 1<<FILPBIT_FNUM |
+                   (1 << FILPBIT_CDATE) | (1 << FILPBIT_MDATE) |
+                   (1 << FILPBIT_BDATE))) {
+        upath = mtoupath(ofork->of_vol, ofork->of_name);
+       if ( ad_dfileno( ofork->of_ad ) == -1 ) {
+           if ( stat( upath, &st ) < 0 )
+               return( AFPERR_NOOBJ );
+       } else {
+           if ( fstat( ad_dfileno( ofork->of_ad ), &st ) < 0 ) {
+               return( AFPERR_BITMAP );
+           }
+       }
+    }
+
+    data = buf;
+    while ( bitmap != 0 ) {
+       while (( bitmap & 1 ) == 0 ) {
+           bitmap = bitmap>>1;
+           bit++;
+       }
+
+       switch ( bit ) {
+       case FILPBIT_ATTR :
+           if ( isad ) {
+               ad_getattr(ofork->of_ad, &ashort);
+           } else {
+               ashort = 0;
+           }
+           if (attrbits)
+             ashort = htons(ntohs(ashort) | attrbits);
+           memcpy(data, &ashort, sizeof( ashort ));
+           data += sizeof( ashort );
+           break;
+
+       case FILPBIT_PDID :
+           memcpy(data, &ofork->of_dir->d_did, sizeof( aint ));
+           data += sizeof( aint );
+           break;
+
+       case FILPBIT_CDATE :
+           if (!isad ||
+                (ad_getdate(ofork->of_ad, AD_DATE_CREATE, &aint) < 0))
+               aint = AD_DATE_FROM_UNIX(st.st_mtime);
+           memcpy(data, &aint, sizeof( aint ));
+           data += sizeof( aint );
+           break;
+
+       case FILPBIT_MDATE :
+           if (!isad || 
+               (ad_getdate(ofork->of_ad, AD_DATE_MODIFY, &aint) < 0) ||
+               (AD_DATE_TO_UNIX(aint) < st.st_mtime))
+               aint = AD_DATE_FROM_UNIX(st.st_mtime);
+           memcpy(data, &aint, sizeof( aint ));
+           data += sizeof( aint );
+           break;
+
+       case FILPBIT_BDATE :
+           if (!isad ||
+               (ad_getdate(ofork->of_ad, AD_DATE_BACKUP, &aint) < 0))
+               aint = AD_DATE_START;
+           memcpy(data, &aint, sizeof( aint ));
+           data += sizeof( aint );
+           break;
+
+       case FILPBIT_FINFO :
+           memcpy(data, isad ? 
+                  (void *) ad_entry(ofork->of_ad, ADEID_FINDERI) :
+                  (void *) ufinderi, 32);
+           if ( !isad || 
+                memcmp( ad_entry( ofork->of_ad, ADEID_FINDERI ),
+                        ufinderi, 8 ) == 0 ) {
+               memcpy(data, ufinderi, 8 );
+               if (( em = getextmap( ofork->of_name )) != NULL ) {
+                   memcpy(data, em->em_type, sizeof( em->em_type ));
+                   memcpy(data + 4, em->em_creator, 
+                           sizeof( em->em_creator ));
+               }
+           }
+           data += 32;
+           break;
+
+       case FILPBIT_LNAME :
+           nameoff = data;
+           data += sizeof(u_int16_t);
+           break;
+
+       case FILPBIT_SNAME :
+           memset(data, 0, sizeof(u_int16_t));
+           data += sizeof(u_int16_t);
+           break;
+
+       case FILPBIT_FNUM :
+           /*
+            * See file.c getfilparams() for why this is done this
+            * way.
+            */
+#if AD_VERSION > AD_VERSION1
+           if (isad) 
+             memcpy(&aint, ad_entry(ofork->of_ad, ADEID_DID), sizeof(aint));
+           else
+             aint = 0;
+           
+           if (!(aint = cnid_add(ofork->of_vol->v_db, &st, 
+                                 ofork->of_dir->d_did, 
+                                 upath, strlen(upath), aint))) {
+#endif
+#ifdef AFS
+             aint = st.st_ino;
+#else AFS
+             aint = ( st.st_dev << 16 ) | ( st.st_ino & 0x0000ffff );
+#endif AFS
+#if AD_VERSION > AD_VERSION1
+           }
+#endif
+           memcpy(data, &aint, sizeof( aint ));
+           data += sizeof( aint );
+           break;
+
+       case FILPBIT_DFLEN :
+           aint = htonl( st.st_size );
+           memcpy(data, &aint, sizeof( aint ));
+           data += sizeof( aint );
+           break;
+
+       case FILPBIT_RFLEN :
+           if ( isad ) {
+               aint = htonl( ad_getentrylen( ofork->of_ad, ADEID_RFORK ));
+           } else {
+               aint = 0;
+           }
+           memcpy(data, &aint, sizeof( aint ));
+           data += sizeof( aint );
+           break;
+
+       default :
+           return( AFPERR_BITMAP );
+       }
+       bitmap = bitmap>>1;
+       bit++;
+    }
+
+    if ( nameoff ) {
+       ashort = htons( data - buf );
+       memcpy(nameoff, &ashort, sizeof( ashort ));
+       aint = strlen( ofork->of_name );
+       aint = ( aint > MACFILELEN ) ? MACFILELEN : aint;
+       *data++ = aint;
+       memcpy(data, ofork->of_name, aint );
+       data += aint;
+    }
+
+    *buflen = data - buf;
+    return( AFP_OK );
+}
+
+int afp_openfork(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct vol         *vol;
+    struct dir         *dir;
+    struct ofork       *ofork, *opened;
+    struct adouble      *adsame = NULL;
+    int                        buflen, ret, adflags, eid, lockop;
+    u_int32_t           did;
+    u_int16_t          vid, bitmap, access, ofrefnum, attrbits = 0;
+    char               fork, *path, *upath; 
+
+    ibuf++;
+    fork = *ibuf++;
+    memcpy(&vid, ibuf, sizeof( vid ));
+    ibuf += sizeof(vid);
+
+    *rbuflen = 0;
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    memcpy(&did, ibuf, sizeof( did ));
+    ibuf += sizeof( int );
+
+    if (( dir = dirsearch( vol, did )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    memcpy(&bitmap, ibuf, sizeof( bitmap ));
+    bitmap = ntohs( bitmap );
+    ibuf += sizeof( bitmap );
+    memcpy(&access, ibuf, sizeof( access ));
+    access = ntohs( access );
+    ibuf += sizeof( access );
+
+    if ((vol->v_flags & AFPVOL_RO) && (access & OPENACC_WR)) {
+        return AFPERR_VLOCK;
+    }
+
+    if (( path = cname( vol, dir, &ibuf )) == NULL ) {
+       return( AFPERR_NOOBJ );
+    }
+
+    if ( fork == OPENFORK_DATA ) {
+       eid = ADEID_DFORK;
+       adflags = ADFLAGS_DF|ADFLAGS_HF;
+    } else {
+        eid = ADEID_RFORK;
+       adflags = ADFLAGS_HF;
+    }
+       
+    /* XXX: this probably isn't the best way to do this. the already
+       open bits should really be set if the fork is opened by any
+       program, not just this one. however, that's problematic to do
+       if we can't write lock files somewhere. opened is also passed to 
+       ad_open so that we can keep file locks together. */
+    if ((opened = of_findname(vol, curdir, path))) {
+      attrbits = ((opened->of_flags & AFPFORK_RSRC) ? ATTRBIT_ROPEN : 0) | 
+       ((opened->of_flags & AFPFORK_DATA) ? ATTRBIT_DOPEN : 0);
+      adsame = opened->of_ad;
+    }
+
+    if (( ofork = of_alloc(vol, curdir, path, &ofrefnum, eid,
+                          adsame)) == NULL ) {
+       return( AFPERR_NFILE );
+    }
+
+    /* try opening in read-write mode with a fallback to read-only 
+     * if we don't need write access. */
+    upath = mtoupath(vol, path);
+    ret = AFPERR_NOOBJ;
+    if (ad_open(upath, adflags, O_RDWR, 0, ofork->of_ad) < 0) {
+       switch ( errno ) {
+       case EROFS:
+        ret = AFPERR_VLOCK;
+       case EACCES: 
+        if (access & OPENACC_WR)
+          goto openfork_err;
+        
+        if (ad_open(upath, adflags, O_RDONLY, 0, ofork->of_ad) < 0) {
+          /* check for a read-only data fork */
+          if ((adflags != ADFLAGS_HF) &&
+              (ad_open(upath, ADFLAGS_DF, O_RDONLY, 0, ofork->of_ad) < 0))
+            goto openfork_err;
+
+          adflags = ADFLAGS_DF;               
+        }
+        break;
+       case ENOENT:
+        { 
+          struct stat st;
+          
+          /* see if client asked for the data fork */
+          if (fork == OPENFORK_DATA) {
+            if (((access & OPENACC_WR) && 
+                 (ad_open(upath, ADFLAGS_DF, O_RDWR, 0, ofork->of_ad) < 0)) 
+                || (ad_open(upath, ADFLAGS_DF, O_RDONLY, 0, 
+                            ofork->of_ad) < 0)) {
+              goto openfork_err;
+            }
+            adflags = ADFLAGS_DF;
+            
+          } else if (stat(upath, &st) == 0) {
+            /* here's the deal. we only try to create the resource
+             * fork if the user wants to open it for write access. */
+            if ((access & OPENACC_WR) && 
+                (ad_open(upath, adflags, O_RDWR | O_CREAT, 
+                         0666, ofork->of_ad) < 0))
+              goto openfork_err;
+          } else 
+            goto openfork_err;
+        }
+        break;
+       case EMFILE :
+       case ENFILE :
+        ret = AFPERR_NFILE;
+        goto openfork_err;
+        break;
+       case EISDIR :
+        ret = AFPERR_BADTYPE;
+        goto openfork_err;
+        break;
+       default:
+        syslog( LOG_ERR, "afp_openfork: ad_open: %m" );
+        ret = AFPERR_PARAM;
+        goto openfork_err;
+        break;
+       }
+    }
+
+    if ((adflags & ADFLAGS_HF) &&
+       (ad_getoflags( ofork->of_ad, ADFLAGS_HF ) & O_CREAT)) {
+       ad_setentrylen( ofork->of_ad, ADEID_NAME, strlen( path ));
+       memcpy(ad_entry( ofork->of_ad, ADEID_NAME ), path, 
+              ad_getentrylen( ofork->of_ad, ADEID_NAME ));
+       ad_flush( ofork->of_ad, adflags );
+    }
+
+    if (( ret = getforkparams(ofork, bitmap, rbuf + 2 * sizeof( u_int16_t ),
+                             &buflen, attrbits )) != AFP_OK ) {
+       ad_close( ofork->of_ad, adflags );
+       goto openfork_err;
+    }
+
+    *rbuflen = buflen + 2 * sizeof( u_int16_t );
+    bitmap = htons( bitmap );
+    memcpy(rbuf, &bitmap, sizeof( u_int16_t ));
+    rbuf += sizeof( u_int16_t );
+
+    /*
+     * synchronization locks:
+     *
+     * here's the ritual:
+     *  1) attempt a read lock to see if we have read or write
+     *     privileges.
+     *  2) if that succeeds, set a write lock to correspond to the
+     *     deny mode requested.
+     *  3) whenever a file is read/written, locks get set which
+     *     prevent conflicts.
+     */
+
+    /* don't try to lock non-existent rforks. */
+    if ((eid == ADEID_DFORK) || (ad_hfileno(ofork->of_ad) != -1)) {
+
+      /* try to see if we have access. */
+      ret = 0;
+      if (access & OPENACC_WR) {
+       ofork->of_flags |= AFPFORK_ACCWR;
+       ret = ad_lock(ofork->of_ad, eid, ADLOCK_RD | ADLOCK_FILELOCK, 
+                     AD_FILELOCK_WR, 1, ofrefnum);
+      } 
+
+      if (!ret && (access & OPENACC_RD)) {
+       ofork->of_flags |= AFPFORK_ACCRD;
+       ret = ad_lock(ofork->of_ad, eid, ADLOCK_RD | ADLOCK_FILELOCK, 
+                     AD_FILELOCK_RD, 1, ofrefnum); 
+      }
+    
+      /* can we access the fork? */
+      if (ret < 0) {
+       ad_close( ofork->of_ad, adflags );
+       of_dealloc( ofork );
+       ofrefnum = 0;
+       memcpy(rbuf, &ofrefnum, sizeof(ofrefnum));
+       return (AFPERR_DENYCONF);
+      }
+      
+      /* now try to set the deny lock. if the fork is open for read or
+       * write, a read lock will already have been set. otherwise, we upgrade
+       * our lock to a write lock. 
+       *
+       * NOTE: we can't write lock a read-only file. on those, we just
+       * make sure that we have a read lock set. that way, we at least prevent
+       * someone else from really setting a deny read/write on the file. */
+      lockop = (ad_getoflags(ofork->of_ad, eid) & O_RDWR) ? 
+       ADLOCK_WR : ADLOCK_RD;
+      ret = (access & OPENACC_DWR) ? ad_lock(ofork->of_ad, eid, 
+                                            lockop | ADLOCK_FILELOCK |
+                                            ADLOCK_UPGRADE, AD_FILELOCK_WR, 1,
+                                            ofrefnum) : 0;
+      if (!ret && (access & OPENACC_DRD))
+       ret = ad_lock(ofork->of_ad, eid, lockop | ADLOCK_FILELOCK |
+                     ADLOCK_UPGRADE, AD_FILELOCK_RD, 1, ofrefnum);
+      
+      if (ret < 0) {
+       ret = errno;
+       ad_close( ofork->of_ad, adflags );
+       of_dealloc( ofork );
+       switch (ret) {
+       case EAGAIN: /* return data anyway */
+       case EACCES:
+       case EINVAL:
+         ofrefnum = 0;
+         memcpy(rbuf, &ofrefnum, sizeof(ofrefnum));
+         return( AFPERR_DENYCONF );
+         break;
+       default:
+         *rbuflen = 0;
+         syslog( LOG_ERR, "afp_openfork: ad_lock: %m" );
+         return( AFPERR_PARAM );
+       }
+      }
+    }
+
+    memcpy(rbuf, &ofrefnum, sizeof(ofrefnum));
+    return( AFP_OK );
+
+openfork_err:
+    of_dealloc( ofork );
+    if (errno == EACCES)
+      return (access & OPENACC_WR) ? AFPERR_LOCK : AFPERR_ACCESS;
+    return ret;
+}
+
+int afp_setforkparams(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct ofork       *ofork;
+    int32_t            size;
+    u_int16_t          ofrefnum, bitmap;
+    int                 err;
+
+    ibuf += 2;
+    memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
+    ibuf += sizeof( ofrefnum );
+    memcpy(&bitmap, ibuf, sizeof(bitmap));
+    bitmap = ntohs(bitmap);
+    ibuf += sizeof( bitmap );
+    memcpy(&size, ibuf, sizeof( size ));
+    size = ntohl( size );
+
+    *rbuflen = 0;
+    if (( ofork = of_find( ofrefnum )) == NULL ) {
+       syslog( LOG_ERR, "afp_setforkparams: of_find: %m" );
+       return( AFPERR_PARAM );
+    }
+
+    if (ofork->of_vol->v_flags & AFPVOL_RO)
+        return AFPERR_VLOCK;
+
+    if ((ofork->of_flags & AFPFORK_ACCWR) == 0)
+        return AFPERR_ACCESS;
+
+    if (size < 0)
+        return AFPERR_PARAM;
+      
+    if ((bitmap == (1<<FILPBIT_DFLEN)) && (ofork->of_flags & AFPFORK_DATA)) {
+       err = ad_dtruncate( ofork->of_ad, size );
+       if (err < 0)
+         goto afp_setfork_err;
+    } else if ((bitmap == (1<<FILPBIT_RFLEN)) &&
+              (ofork->of_flags & AFPFORK_RSRC)) {
+       ad_refresh( ofork->of_ad );
+       err = ad_rtruncate(ofork->of_ad, size);
+       if (err < 0)
+         goto afp_setfork_err;
+       
+       if (ad_flush( ofork->of_ad, ADFLAGS_HF ) < 0) {
+           syslog( LOG_ERR, "afp_setforkparams: ad_flush: %m" );
+           return( AFPERR_PARAM );
+       }
+    } else
+      return AFPERR_BITMAP;
+
+#ifdef AFS
+    if ( flushfork( ofork ) < 0 ) {
+       syslog( LOG_ERR, "afp_setforkparams: flushfork: %m" );
+    }
+#endif AFS
+
+    return( AFP_OK );
+
+afp_setfork_err:
+    if (err == -2) 
+      return AFPERR_LOCK;
+    else {
+      switch (errno) {
+      case EROFS:
+       return AFPERR_VLOCK;
+      case EPERM:
+      case EACCES:
+       return AFPERR_ACCESS;
+      case EDQUOT:
+      case EFBIG:
+      case ENOSPC:
+       return AFPERR_DFULL;
+      default:
+       return AFPERR_PARAM;
+      }
+    }
+}
+
+/* for this to work correctly, we need to check for locks before each
+ * read and write. that's most easily handled by always doing an
+ * appropriate check before each ad_read/ad_write. other things
+ * that can change files like truncate are handled internally to those
+ * functions. 
+ */
+#define ENDBIT(a)  ((a) & 0x80)
+#define UNLOCKBIT(a) ((a) & 0x01)
+int afp_bytelock(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct ofork       *ofork;
+    int32_t             offset, length;
+    int                 eid;
+    u_int16_t          ofrefnum;
+    u_int8_t            flags;
+
+    *rbuflen = 0;
+
+    /* figure out parameters */
+    ibuf++;
+    flags = *ibuf; /* first bit = endflag, lastbit = lockflag */
+    ibuf++;
+    memcpy(&ofrefnum, ibuf, sizeof(ofrefnum));
+    ibuf += sizeof(ofrefnum);
+
+    if (( ofork = of_find( ofrefnum )) == NULL ) {
+       syslog( LOG_ERR, "afp_bytelock: of_find: %m" );
+       return( AFPERR_PARAM );
+    }
+
+    if ( ofork->of_flags & AFPFORK_DATA) {
+       eid = ADEID_DFORK;
+    } else if (ofork->of_flags & AFPFORK_RSRC) {
+       eid = ADEID_RFORK;
+    } else 
+        return AFPERR_PARAM;
+
+    memcpy(&offset, ibuf, sizeof( offset ));
+    offset = ntohl(offset);
+    ibuf += sizeof(offset);
+
+    memcpy(&length, ibuf, sizeof( length ));
+    length = ntohl(length);
+    if (length == 0xFFFFFFFF) 
+      length = BYTELOCK_MAX;
+    else if (length <= 0) {
+      return AFPERR_PARAM;
+    } else if ((length >= AD_FILELOCK_BASE) && 
+              (ad_hfileno(ofork->of_ad) == -1))
+      return AFPERR_LOCK;
+
+    if (ENDBIT(flags)) 
+      offset += ad_size(ofork->of_ad, eid);
+
+    if (offset < 0)    /* error if we have a negative offset */
+      return AFPERR_PARAM;
+
+    /* if the file is a read-only file, we use read locks instead of
+     * write locks. that way, we can prevent anyone from initiating
+     * a write lock. */
+    if (ad_lock(ofork->of_ad, eid, UNLOCKBIT(flags) ? ADLOCK_CLR : 
+               ((ad_getoflags(ofork->of_ad, eid) & O_RDWR) ? 
+                ADLOCK_WR : ADLOCK_RD), offset, length, 
+               ofork->of_refnum) < 0) {
+      switch (errno) {
+      case EACCES:
+      case EAGAIN:
+       return UNLOCKBIT(flags) ? AFPERR_NORANGE : AFPERR_LOCK;
+       break;
+      case ENOLCK:
+       return AFPERR_NLOCK;
+       break;
+      case EINVAL: 
+       return UNLOCKBIT(flags) ? AFPERR_NORANGE : AFPERR_RANGEOVR;
+       break;
+      case EBADF:
+      default: 
+       return AFPERR_PARAM;
+       break;
+      }
+    }
+
+    offset = htonl(offset);
+    memcpy(rbuf, &offset, sizeof( offset ));
+    *rbuflen = sizeof( offset );
+    return( AFP_OK );
+}
+#undef UNLOCKBIT
+
+
+static __inline__ int crlf( of )
+    struct ofork       *of;
+{
+    struct extmap      *em;
+
+    if ( ad_hfileno( of->of_ad ) == -1 ||
+        memcmp( ufinderi, ad_entry( of->of_ad, ADEID_FINDERI ), 
+                8) == 0 ) {
+       if (( em = getextmap( of->of_name )) == NULL ||
+               memcmp( "TEXT", em->em_type, sizeof( em->em_type )) == 0 ) {
+           return( 1 );
+       } else {
+           return( 0 );
+       }
+    } else {
+      if ( memcmp( ufinderi, 
+                  ad_entry( of->of_ad, ADEID_FINDERI ), 4 ) == 0 ) {
+           return( 1 );
+       } else {
+           return( 0 );
+       }
+    }
+}
+
+
+static __inline__ ssize_t read_file(struct ofork *ofork, int eid,
+                                   int offset, u_char nlmask, 
+                                   u_char nlchar, char *rbuf, 
+                                   int *rbuflen, const int xlate)
+{ 
+    ssize_t cc;
+    int eof = 0;
+    char *p, *q;
+
+    cc = ad_read(ofork->of_ad, eid, offset, rbuf, *rbuflen);
+    if ( cc < 0 ) {
+       syslog( LOG_ERR, "afp_read: ad_read: %m" );
+       *rbuflen = 0;
+       return( AFPERR_PARAM );
+    }
+    if ( cc < *rbuflen ) {
+        eof = 1;
+    }
+
+    /*
+     * Do Newline check.
+     */
+    if ( nlmask != 0 ) {
+       for ( p = rbuf, q = p + cc; p < q; ) {
+           if (( *p++ & nlmask ) == nlchar ) {
+               break;
+           }
+       }
+       if ( p != q ) {
+           cc = p - rbuf;
+           eof = 0;
+       }
+    }
+
+    /*
+     * If this file is of type TEXT, then swap \012 to \015.
+     */
+    if (xlate) {
+       for ( p = rbuf, q = p + cc; p < q; p++ ) {
+           if ( *p == '\012' ) {
+               *p = '\015';
+           } else if ( *p == '\015' ) {
+               *p = '\012';
+           }
+             
+       }
+    }
+
+    *rbuflen = cc;
+    if ( eof ) {
+       return( AFPERR_EOF );
+    }
+    return AFP_OK;
+}
+
+int afp_read(obj, ibuf, ibuflen, rbuf, rbuflen)
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct ofork       *ofork;
+    off_t              size;
+    int32_t            offset, saveoff, reqcount;
+    int                        cc, err, eid, xlate = 0;
+    u_int16_t          ofrefnum;
+    u_char             nlmask, nlchar;
+
+    ibuf += 2;
+    memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
+    ibuf += sizeof( u_short );
+
+    if (( ofork = of_find( ofrefnum )) == NULL ) {
+       syslog( LOG_ERR, "afp_read: of_find: %m" );
+       err = AFPERR_PARAM;
+       goto afp_read_err;
+    }
+
+    if ((ofork->of_flags & AFPFORK_ACCRD) == 0) {
+       err = AFPERR_ACCESS;
+       goto afp_read_err;
+    }
+
+    memcpy(&offset, ibuf, sizeof( offset ));
+    offset = ntohl( offset );
+    ibuf += sizeof( offset );
+    memcpy(&reqcount, ibuf, sizeof( reqcount ));
+    reqcount = ntohl( reqcount );
+    ibuf += sizeof( reqcount );
+
+    nlmask = *ibuf++;
+    nlchar = *ibuf++;
+
+    /* if we wanted to be picky, we could add in the following
+     * bit: if (afp_version == 11 && !(nlmask == 0xFF || !nlmask))
+     */
+    if (reqcount < 0 || offset < 0) {
+      err = AFPERR_PARAM;
+      goto afp_read_err;
+    }
+
+    if ( ofork->of_flags & AFPFORK_DATA) {
+       eid = ADEID_DFORK;
+       xlate = (ofork->of_vol->v_flags & AFPVOL_CRLF) ? crlf(ofork) : 0;
+    } else if (ofork->of_flags & AFPFORK_RSRC) {
+       eid = ADEID_RFORK;
+    } else { /* fork wasn't opened. this should never really happen. */
+        err = AFPERR_ACCESS;
+       goto afp_read_err;
+    }
+
+    /* zero request count */
+    if (!reqcount) {
+      err = AFP_OK;
+      goto afp_read_err;
+    }
+
+    /* reqcount isn't always truthful. we need to deal with that. */
+    if ((size = ad_size(ofork->of_ad, eid)) == 0) {
+      err = AFPERR_EOF;
+      goto afp_read_err;
+    }
+
+    saveoff = offset;
+    if (ad_tmplock(ofork->of_ad, eid, ADLOCK_RD, saveoff, reqcount) < 0) {
+      err = AFPERR_LOCK;
+      goto afp_read_err;
+    }      
+
+#define min(a,b)       ((a)<(b)?(a):(b))
+    *rbuflen = min( reqcount, *rbuflen );
+    err = read_file(ofork, eid, offset, nlmask, nlchar, rbuf, rbuflen,
+                   xlate);
+    if (err < 0) 
+      goto afp_read_done;
+
+    /* dsi can stream requests. we can only do this if we're not checking
+     * for an end-of-line character. oh well. */
+    if ((obj->proto == AFPPROTO_DSI) && (*rbuflen < reqcount) && !nlmask) {
+      DSI *dsi = obj->handle;
+      
+      /* subtract off the offset */
+      size -= offset;
+      if (reqcount > size) {
+       reqcount = size;
+       err = AFPERR_EOF;
+      }
+
+      if (obj->options.flags & OPTION_DEBUG) {
+       printf( "(read) reply: %d/%d, %d\n", *rbuflen,
+               reqcount, dsi->clientID);
+       bprint(rbuf, *rbuflen);
+      }
+
+      offset += *rbuflen;
+
+      /* dsi_readinit() returns size of next read buffer. by this point,
+       * we know that we're sending some data. if we fail, something
+       * horrible happened. */
+      if ((*rbuflen = dsi_readinit(dsi, rbuf, *rbuflen, reqcount, err)) < 0) 
+       goto afp_read_exit;
+      
+      /* due to the nature of afp packets, we have to exit if we get
+         an error. we can't do this with translation on. */
+#ifdef HAVE_SENDFILE_READ
+      if (!(xlate || (obj->options.flags & OPTION_DEBUG))) {
+       if (ad_readfile(ofork->of_ad, eid, dsi->socket, offset, 
+                       dsi->datasize) < 0) {
+         if (errno == EINVAL)
+           goto afp_read_loop;
+         else {
+           syslog(LOG_ERR, "afp_read: ad_readfile: %m");
+           goto afp_read_exit;
+         }
+       }
+
+       dsi_readdone(dsi);
+       goto afp_read_done;
+      }
+
+afp_read_loop:
+#endif
+
+      /* fill up our buffer. */
+      while (*rbuflen > 0) {
+       cc = read_file(ofork, eid, offset, nlmask, nlchar, rbuf, 
+                      rbuflen, xlate);
+       if (cc < 0) 
+         goto afp_read_exit;
+
+       offset += *rbuflen;
+       if (obj->options.flags & OPTION_DEBUG) {
+         printf( "(read) reply: %d, %d\n", *rbuflen, dsi->clientID);
+         bprint(rbuf, *rbuflen);
+       }
+
+       /* dsi_read() also returns buffer size of next allocation */
+       cc = dsi_read(dsi, rbuf, *rbuflen); /* send it off */
+       if (cc < 0) 
+         goto afp_read_exit;
+       *rbuflen = cc;
+      }
+      dsi_readdone(dsi);
+      goto afp_read_done;
+
+afp_read_exit:
+      syslog(LOG_ERR, "afp_read: %m");
+      dsi_readdone(dsi);
+      ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount);
+      obj->exit(1);
+    }
+
+afp_read_done:
+    ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount);
+    return err;
+
+afp_read_err:
+    *rbuflen = 0;
+    return err;
+}
+
+int afp_flush(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct vol *vol;
+    u_int16_t vid;
+
+    *rbuflen = 0;
+    ibuf += 2;
+
+    memcpy(&vid, ibuf, sizeof(vid));
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    of_flush(vol);
+    return( AFP_OK );
+}
+
+int afp_flushfork(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct ofork       *ofork;
+    u_int16_t          ofrefnum;
+
+    *rbuflen = 0;
+    ibuf += 2;
+    memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
+
+    if (( ofork = of_find( ofrefnum )) == NULL ) {
+       syslog( LOG_ERR, "afp_flushfork: of_find: %m" );
+       return( AFPERR_PARAM );
+    }
+
+    if ( flushfork( ofork ) < 0 ) {
+       syslog( LOG_ERR, "afp_flushfork: %m" );
+    }
+
+    return( AFP_OK );
+}
+
+/* this is very similar to closefork */
+int flushfork( ofork )
+    struct ofork       *ofork;
+{
+    struct timeval tv;
+    int len, err = 0, doflush = 0;
+
+    if ( ad_dfileno( ofork->of_ad ) != -1 &&
+           fsync( ad_dfileno( ofork->of_ad )) < 0 ) {
+       syslog( LOG_ERR, "flushfork: dfile(%d) %m", 
+               ad_dfileno(ofork->of_ad) );
+       err = -1;
+    }
+
+    if ( ad_hfileno( ofork->of_ad ) != -1 ) {
+        if (ofork->of_flags & AFPFORK_RSRC) {
+         len = ad_getentrylen(ofork->of_ad, ADEID_RFORK);
+         ad_refresh(ofork->of_ad);
+         if (len != ad_getentrylen(ofork->of_ad, ADEID_RFORK)) {
+           ad_setentrylen(ofork->of_ad, ADEID_RFORK, len);
+           doflush++;
+         }
+       }
+
+        if ((ofork->of_flags & AFPFORK_DIRTY) &&
+           (gettimeofday(&tv, NULL) == 0)) {
+         ad_setdate(ofork->of_ad, AD_DATE_MODIFY|AD_DATE_UNIX, tv.tv_sec);
+         ofork->of_flags &= ~AFPFORK_DIRTY;
+         doflush++;
+       }
+
+       /* flush the header. */
+       if (doflush && (ad_flush(ofork->of_ad, ADFLAGS_HF) < 0)) 
+         err = -1;
+         
+       if (fsync( ad_hfileno( ofork->of_ad )) < 0)
+         err = -1;
+
+       if (err < 0)
+         syslog( LOG_ERR, "flushfork: hfile(%d) %m", 
+                 ad_hfileno(ofork->of_ad) );
+    }
+
+    return( err );
+}
+
+int afp_closefork(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct ofork       *ofork;
+    struct timeval      tv;
+    int                        adflags, aint, doflush = 0;
+    u_int16_t          ofrefnum;
+
+    *rbuflen = 0;
+    ibuf += 2;
+    memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
+
+    if (( ofork = of_find( ofrefnum )) == NULL ) {
+       syslog( LOG_ERR, "afp_closefork: of_find: %m" );
+       return( AFPERR_PARAM );
+    }
+
+    adflags = 0;
+    if ((ofork->of_flags & AFPFORK_DATA) &&
+       (ad_dfileno( ofork->of_ad ) != -1)) {
+       adflags |= ADFLAGS_DF;
+    }
+
+    if ( ad_hfileno( ofork->of_ad ) != -1 ) {
+       adflags |= ADFLAGS_HF;
+
+       aint = ad_getentrylen( ofork->of_ad, ADEID_RFORK );
+       ad_refresh( ofork->of_ad );
+       if ((ofork->of_flags & AFPFORK_DIRTY) &&
+           (gettimeofday(&tv, NULL) == 0)) {
+           ad_setdate(ofork->of_ad, AD_DATE_MODIFY | AD_DATE_UNIX,
+                      tv.tv_sec);
+           doflush++;
+       }
+
+       /*
+        * Only set the rfork's length if we're closing the rfork.
+        */
+       if ((ofork->of_flags & AFPFORK_RSRC) && aint !=
+               ad_getentrylen( ofork->of_ad, ADEID_RFORK )) {
+           ad_setentrylen( ofork->of_ad, ADEID_RFORK, aint );
+           doflush++;
+       }
+       if ( doflush ) {
+           ad_flush( ofork->of_ad, adflags );
+       }
+    }
+
+    if ( ad_close( ofork->of_ad, adflags ) < 0 ) {
+       syslog( LOG_ERR, "afp_closefork: ad_close: %m" );
+       return( AFPERR_PARAM );
+    }
+
+    of_dealloc( ofork );
+    return( AFP_OK );
+}
+
+
+static __inline__ ssize_t write_file(struct ofork *ofork, int eid,
+                                    off_t offset, char *rbuf, 
+                                    size_t rbuflen, const int xlate)
+{
+    char *p, *q;
+    ssize_t cc;
+
+    /*
+     * If this file is of type TEXT, swap \015 to \012.
+     */
+    if (xlate) {
+       for ( p = rbuf, q = p + rbuflen; p < q; p++ ) {
+           if ( *p == '\015' ) {
+               *p = '\012';
+           } else if ( *p == '\012' ) {
+               *p = '\015';
+           }
+       }
+    }
+
+    if (( cc = ad_write(ofork->of_ad, eid, offset, 0, 
+                        rbuf, rbuflen)) < 0 ) {
+       switch ( errno ) {
+       case EDQUOT :
+       case EFBIG :
+       case ENOSPC :
+           return( AFPERR_DFULL );
+       default :
+           syslog( LOG_ERR, "afp_write: ad_write: %m" );
+           return( AFPERR_PARAM );
+       }
+    }
+    
+    return cc;
+}
+
+/* FPWrite. NOTE: on an error, we always use afp_write_err as
+ * the client may have sent us a bunch of data that's not reflected 
+ * in reqcount et al. */
+int afp_write(obj, ibuf, ibuflen, rbuf, rbuflen)
+    AFPObj              *obj;
+    char                *ibuf, *rbuf;
+    int                 ibuflen, *rbuflen;
+{
+    struct ofork       *ofork;
+    int32_t            offset, saveoff, reqcount;
+    int                        endflag, eid, xlate = 0, err = AFP_OK;
+    u_int16_t          ofrefnum;
+    ssize_t             cc;
+
+    /* figure out parameters */
+    ibuf++;
+    endflag = ENDBIT(*ibuf);
+    ibuf++;
+    memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
+    ibuf += sizeof( ofrefnum );
+    memcpy(&offset, ibuf, sizeof( offset ));
+    offset = ntohl( offset );
+    ibuf += sizeof( offset );
+    memcpy(&reqcount, ibuf, sizeof( reqcount ));
+    reqcount = ntohl( reqcount );
+    ibuf += sizeof( reqcount );
+
+    if (( ofork = of_find( ofrefnum )) == NULL ) {
+       syslog( LOG_ERR, "afp_write: of_find: %m" );
+       err = AFPERR_PARAM;
+       goto afp_write_err;
+    }
+
+    if ((ofork->of_flags & AFPFORK_ACCWR) == 0) {
+       err = AFPERR_ACCESS;
+       goto afp_write_err;
+    }
+
+#ifdef AFS
+    writtenfork = ofork;
+#endif AFS
+
+    if ( ofork->of_flags & AFPFORK_DATA) {
+       eid = ADEID_DFORK;
+       xlate = (ofork->of_vol->v_flags & AFPVOL_CRLF) ? crlf(ofork) : 0;
+    } else if (ofork->of_flags & AFPFORK_RSRC) {
+       eid = ADEID_RFORK;
+    } else {
+        err = AFPERR_ACCESS; /* should never happen */
+       goto afp_write_err;
+    }
+
+    if (endflag) 
+      offset += ad_size(ofork->of_ad, eid);
+      
+    /* handle bogus parameters */
+    if (reqcount < 0 || offset < 0) {
+      err = AFPERR_PARAM;
+      goto afp_write_err;
+    }
+
+    /* offset can overflow on 64-bit capable filesystems.
+     * report disk full if that's going to happen. */
+    if (offset + reqcount < 0) {
+      err = AFPERR_DFULL;
+      goto afp_write_err;
+    }
+
+    if (!reqcount) { /* handle request counts of 0 */
+      err = AFP_OK;
+      offset = htonl(offset);
+      memcpy(rbuf, &offset, sizeof(offset));
+      goto afp_write_err;
+    }
+
+    saveoff = offset;
+    if (ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, saveoff, 
+                           reqcount) < 0) {
+        err = AFPERR_LOCK;
+        goto afp_write_err;
+    }
+
+    /* this is yucky, but dsi can stream i/o and asp can't */
+    switch (obj->proto) {
+#ifndef NO_DDP
+    case AFPPROTO_ASP:
+      if (asp_wrtcont(obj->handle, rbuf, rbuflen) < 0) {
+       *rbuflen = 0;
+       syslog( LOG_ERR, "afp_write: asp_wrtcont: %m" );
+       return( AFPERR_PARAM );
+      }
+
+      if (obj->options.flags & OPTION_DEBUG) {
+       printf("(write) len: %d\n", *rbuflen);
+       bprint(rbuf, *rbuflen);
+      }
+
+      if ((cc = write_file(ofork, eid, offset, rbuf, *rbuflen,
+                          xlate)) < 0) {
+       *rbuflen = 0;
+       ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount);
+       return cc;
+      }
+      offset += cc;
+      break;
+#endif /* no afp/asp */
+
+    case AFPPROTO_DSI:
+      {
+       DSI *dsi = obj->handle;
+
+       /* find out what we have already and write it out. */
+       cc = dsi_writeinit(dsi, rbuf, *rbuflen);
+       if (!cc ||
+           (cc = write_file(ofork, eid, offset, rbuf, cc, xlate)) < 0) {
+         dsi_writeflush(dsi);
+         *rbuflen = 0;
+         ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount);
+         return cc;
+       }
+       offset += cc;
+       
+#if 0 /*def HAVE_SENDFILE_WRITE*/
+       if (!(xlate || obj->options.flags & OPTION_DEBUG)) {
+         if ((cc = ad_writefile(ofork->of_ad, eid, dsi->socket,
+                                offset, dsi->datasize)) < 0) {
+           switch (errno) {
+           case EDQUOT :
+           case EFBIG :
+           case ENOSPC :
+             cc = AFPERR_DFULL;
+             break;
+           default :
+             syslog( LOG_ERR, "afp_write: ad_writefile: %m" );
+             goto afp_write_loop;
+           }
+           dsi_writeflush(dsi);
+           *rbuflen = 0;
+           ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, 
+                      reqcount);
+           return cc;
+         }
+
+         offset += cc;
+         goto afp_write_done;
+       }
+#endif
+
+       /* loop until everything gets written. currently
+         * dsi_write handles the end case by itself. */
+afp_write_loop:
+       while ((cc = dsi_write(dsi, rbuf, *rbuflen))) {
+         if ( obj->options.flags & OPTION_DEBUG ) {
+           printf("(write) command cont'd: %d\n", cc);
+           bprint(rbuf, cc);
+         }
+
+         if ((cc = write_file(ofork, eid, offset, rbuf, cc, xlate)) < 0) {
+           dsi_writeflush(dsi);
+           *rbuflen = 0;
+           ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, 
+                      reqcount);
+           return cc;
+         }
+         offset += cc;
+       }
+      }
+      break;
+    }
+
+afp_write_done:
+    ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount);
+    if ( ad_hfileno( ofork->of_ad ) != -1 ) 
+      ofork->of_flags |= AFPFORK_DIRTY;
+
+    offset = htonl( offset );
+#if defined(__GNUC__) && defined(HAVE_GCC_MEMCPY_BUG)
+    bcopy(&offset, rbuf, sizeof(offset));
+#else
+    memcpy(rbuf, &offset, sizeof(offset));
+#endif
+    *rbuflen = sizeof(offset);
+    return( AFP_OK );
+
+afp_write_err:
+    if (obj->proto == AFPPROTO_DSI) {
+      dsi_writeinit(obj->handle, rbuf, *rbuflen);
+      dsi_writeflush(obj->handle);
+    }
+
+    *rbuflen = (err == AFP_OK) ? sizeof(offset) : 0;
+    return err;
+}
+
+
+int afp_getforkparams(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct ofork       *ofork;
+    int                        buflen, ret;
+    u_int16_t          ofrefnum, bitmap;
+
+    ibuf += 2;
+    memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
+    ibuf += sizeof( ofrefnum );
+    memcpy(&bitmap, ibuf, sizeof( bitmap ));
+    bitmap = ntohs( bitmap );
+    ibuf += sizeof( bitmap );
+
+    *rbuflen = 0;
+    if (( ofork = of_find( ofrefnum )) == NULL ) {
+       syslog( LOG_ERR, "afp_getforkparams: of_find: %m" );
+       return( AFPERR_PARAM );
+    }
+
+    if (( ret = getforkparams( ofork, bitmap,
+           rbuf + sizeof( u_short ), &buflen, 0 )) != AFP_OK ) {
+       return( ret );
+    }
+
+    *rbuflen = buflen + sizeof( u_short );
+    bitmap = htons( bitmap );
+    memcpy(rbuf, &bitmap, sizeof( bitmap ));
+    return( AFP_OK );
+}
+
diff --git a/etc/afpd/fork.h b/etc/afpd/fork.h
new file mode 100644 (file)
index 0000000..78f07cc
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#ifndef AFPD_FORK_H
+#define AFPD_FORK_H 1
+
+#include <stdio.h>
+#include <sys/cdefs.h>
+
+#include <netatalk/endian.h>
+#include <atalk/adouble.h>
+#include "volume.h"
+#include "directory.h"
+
+struct ofork {
+    struct adouble     *of_ad;
+    struct vol          *of_vol;
+    struct dir         *of_dir;
+    char               *of_name;
+    int                 of_namelen;
+    u_int16_t           of_refnum;
+    int                 of_flags;
+    struct ofork        **prevp, *next;
+    struct ofork        *of_d_prev, *of_d_next;
+};
+
+#define OPENFORK_DATA  (0)
+#define OPENFORK_RSCS  (1<<7)
+
+#define OPENACC_RD     (1<<0)
+#define OPENACC_WR     (1<<1)
+#define OPENACC_DRD    (1<<4)
+#define OPENACC_DWR    (1<<5)
+
+#define AFPFORK_OPEN   (1<<0)
+#define AFPFORK_RSRC   (1<<1)
+#define AFPFORK_DATA   (1<<2)
+#define AFPFORK_DIRTY   (1<<3)
+#define AFPFORK_ACCRD   (1<<4)
+#define AFPFORK_ACCWR   (1<<5)
+#define AFPFORK_ACCMASK (AFPFORK_ACCRD | AFPFORK_ACCWR)
+
+/* in ofork.c */
+extern struct ofork *of_alloc    __P((struct vol *, struct dir *, 
+                                     char *, u_int16_t *, const int, 
+                                     struct adouble *));
+extern void         of_dealloc   __P((struct ofork *));
+extern struct ofork *of_find     __P((const u_int16_t));
+extern struct ofork *of_findname __P((const struct vol *, const struct dir *,
+                                     const char *));
+extern int          of_rename    __P((const struct vol *, 
+                                     struct dir *, const char *,
+                                     struct dir *, const char *));
+extern int          of_flush     __P((const struct vol *));
+extern void         of_pforkdesc __P((FILE *));
+
+/* in fork.c */
+extern int          flushfork    __P((struct ofork *));
+
+/* FP functions */
+extern int     afp_openfork __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_bytelock __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_getforkparams __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_setforkparams __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_read __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_write __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_flushfork __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_flush __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_closefork __P((AFPObj *, char *, int, char *, int *));
+#endif
diff --git a/etc/afpd/gettok.c b/etc/afpd/gettok.c
new file mode 100644 (file)
index 0000000..ea0a8e3
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#include <sys/param.h>
+#include <string.h>
+#include <ctype.h>
+#include <pwd.h>
+
+#include "globals.h"
+
+static char    *l_curr;
+static char    *l_end;
+
+void initline( len, line )
+    int                len;
+    char       *line;
+{
+    l_curr = line;
+    l_end = line + len;
+}
+
+#define ST_QUOTE       0
+#define ST_WORD                1
+#define ST_BEGIN       2
+
+    int
+parseline( len, token )
+    int                len;
+    char       *token;
+{
+    char       *p, *e;
+    int                state;
+
+    state = ST_BEGIN;
+    p = token;
+    e = token + len;
+
+    for (;;) {
+       if ( l_curr > l_end ) {                 /* end of line */
+           *token = '\0';
+           return( -1 );
+       }
+
+       switch ( *l_curr ) {
+       case '"' :
+           if ( state == ST_QUOTE ) {
+               state = ST_WORD;
+           } else {
+               state = ST_QUOTE;
+           }
+           break;
+
+       case '\0' :
+       case '\t' :
+       case '\n' :
+       case ' ' :
+           if ( state == ST_WORD ) {
+               *p = '\0';
+               return( p - token );
+           }
+           if ( state != ST_QUOTE ) {
+               break;
+           }
+           /* FALL THROUGH */
+
+       default :
+           if ( state == ST_BEGIN ) {
+               state = ST_WORD;
+           }
+           if ( p > e ) {                      /* end of token */
+               *token = '\0';
+               return( -1 );
+           }
+           *p++ = *l_curr;
+           break;
+       }
+
+       l_curr++;
+    }
+}
+
+#ifdef notdef
+void parseline( token, user )
+    char       *token, *user;
+{
+    char               *p = pos, *t = token, *u, *q, buf[ MAXPATHLEN ];
+    struct passwd      *pwent;
+    int                        quoted = 0;
+
+    while ( isspace( *p )) {
+       p++;
+    }
+
+    /*
+     * If we've reached the end of the line, or a comment,
+     * don't return any more tokens.
+     */
+    if ( *p == '\0' || *p == '#' ) {
+       *token = '\0';
+       return;
+    }
+
+    if ( *p == '"' ) {
+       p++;
+       quoted = 1;
+    }
+    while ( *p != '\0' && ( quoted || !isspace( *p ))) {
+       if ( *p == '"' ) {
+           if ( quoted ) {
+               *t = '\0';
+               break;
+           }
+           quoted = 1;
+           p++;
+       } else {
+           *t++ = *p++;
+       }
+    }
+    pos = p;
+    *t = '\0';
+
+    /*
+     * We got to the end of the line without closing an open quote
+     */
+    if ( *p == '\0' && quoted ) {
+       *token = '\0';
+       return;
+    }
+
+    t = token;
+    if ( *t == '~' ) {
+       t++;
+       if ( *t == '\0' || *t == '/' ) {
+           u = user;
+           if ( *t == '/' ) {
+               t++;
+           }
+       } else {
+           u = t;
+           if (( q = strchr( t, '/' )) == NULL ) {
+               t = "";
+           } else {
+               *q = '\0';
+               t = q + 1;
+           }
+       }
+       if ( u == NULL || ( pwent = getpwnam( u )) == NULL ) {
+           *token = '\0';
+           return;
+       }
+       strcpy( buf, pwent->pw_dir );
+       if ( *t != '\0' ) {
+           strcat( buf, "/" );
+           strcat( buf, t );
+       }
+       strcpy( token, buf );
+    }
+    return;
+}
+#endif notdef
diff --git a/etc/afpd/globals.h b/etc/afpd/globals.h
new file mode 100644 (file)
index 0000000..e4d6891
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#ifndef AFPD_GLOBALS_H
+#define AFPD_GLOBALS_H 1
+
+#include <sys/param.h>
+#include <sys/cdefs.h>
+#include <netdb.h>  /* this isn't header-protected under ultrix */
+#include <netatalk/at.h>
+#include <atalk/afp.h>
+#include <atalk/compat.h>
+
+/* test for inline */
+#ifndef __inline__
+#define __inline__
+#endif
+
+#define MACFILELEN 31
+
+
+#define OPTION_DEBUG         (1 << 0)
+#define OPTION_USERVOLFIRST  (1 << 1)
+#define OPTION_NOUSERVOL     (1 << 2)
+#define OPTION_PROXY         (1 << 3)
+#define OPTION_CUSTOMICON    (1 << 4)
+
+/* a couple of these options could get stuck in unions to save
+ * space. */
+struct afp_options {
+  int connections, port, transports, tickleval, flags;
+  unsigned char passwdbits, passwdminlen, loginmaxfail;
+  u_int32_t server_quantum;
+  char hostname[MAXHOSTNAMELEN + 1], *server, *ipaddr, *configfile;
+  struct at_addr ddpaddr;
+  char *uampath, *nlspath, *fqdn;
+  char *pidfile, *defaultvol, *systemvol;
+  char *guest, *loginmesg, *keyfile, *passwdfile;
+  char *uamlist;
+};
+
+#define AFPOBJ_TMPSIZ (MAXPATHLEN)
+typedef struct AFPObj {
+  int proto;
+  unsigned long servernum;
+  void *handle, *config;
+  struct afp_options options;
+  char *Obj, *Type, *Zone;
+  char username[MACFILELEN + 1];
+  void (*logout)(void), (*exit)(int);
+  int (*reply)(void *, int);
+  int (*attention)(void *, AFPUserBytes);
+  /* to prevent confusion, only use these in afp_* calls */
+  char oldtmp[AFPOBJ_TMPSIZ + 1], newtmp[AFPOBJ_TMPSIZ + 1];
+  void *uam_cookie; /* cookie for uams */
+} AFPObj;
+
+extern int             afp_version;
+extern unsigned char   nologin;
+extern struct vol      *volumes;
+extern struct dir      *curdir;
+extern char            getwdbuf[];
+
+extern void afp_options_init __P((struct afp_options *));
+extern int afp_options_parse __P((int, char **, struct afp_options *));
+extern int afp_options_parseline __P((char *, struct afp_options *));
+extern void afp_options_free __P((struct afp_options *, 
+                                 const struct afp_options *));
+extern void setmessage __P((const char *));
+
+/* gettok.c */
+extern void initline   __P((int, char *));
+extern int  parseline  __P((int, char *));
+
+#ifndef NO_DDP
+extern void afp_over_asp __P((AFPObj *));
+#endif
+extern void afp_over_dsi __P((AFPObj *));
+
+#endif /* globals.h */
diff --git a/etc/afpd/icon.h b/etc/afpd/icon.h
new file mode 100644 (file)
index 0000000..0383980
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#ifndef AFPD_ICON_H
+#define AFPD_ICON_H 1
+
+#include <sys/cdefs.h>
+#include "globals.h"
+
+const unsigned char apple_atalk_icon[] = { /* default appletalk icon */
+ 0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  
+ 0x00,  0x01,  0x00,  0x00,  0x00,  0x02,  0x9F,  0xE0,  
+ 0x00,  0x04,  0x50,  0x30,  0x00,  0x08,  0x30,  0x28,  
+ 0x00,  0x10,  0x10,  0x3C,  0x07,  0xA0,  0x08,  0x04,  
+ 0x18,  0x7F,  0x04,  0x04,  0x10,  0x00,  0x82,  0x04,  
+ 0x10,  0x00,  0x81,  0x04,  0x10,  0x00,  0x82,  0x04,  
+ 0x10,  0x00,  0x84,  0x04,  0x10,  0x00,  0x88,  0x04,  
+ 0x10,  0x00,  0x90,  0x04,  0x10,  0x00,  0xB0,  0x04,  
+ 0x10,  0x00,  0xD0,  0x04,  0xFF,  0xFF,  0xFF,  0xFF,  
+ 0x40,  0x00,  0x00,  0x02,  0x3F,  0xFF,  0xFF,  0xFC,  
+ 0x00,  0x00,  0x07,  0x00,  0x00,  0x00,  0x05,  0x00,  
+ 0x00,  0x00,  0x05,  0x00,  0x00,  0x00,  0x05,  0x00,  
+ 0x00,  0x00,  0x0F,  0x80,  0x00,  0x00,  0x08,  0x80,  
+ 0x00,  0x00,  0x08,  0x80,  0x00,  0x00,  0x0F,  0x80,  
+ 0x00,  0x00,  0x0A,  0x80,  0xBF,  0xFF,  0xF2,  0x74,  
+ 0x00,  0x00,  0x05,  0x00,  0xBF,  0xFF,  0xF8,  0xF4,  
+ 0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  
+ 0x00,  0x01,  0x00,  0x00,  0x00,  0x03,  0x9F,  0xE0,  
+ 0x00,  0x07,  0xDF,  0xF0,  0x00,  0x0F,  0xFF,  0xF8,  
+ 0x00,  0x1F,  0xFF,  0xFC,  0x07,  0xBF,  0xFF,  0xFC,  
+ 0x1F,  0xFF,  0xFF,  0xFC,  0x1F,  0xFF,  0xFF,  0xFC,  
+ 0x1F,  0xFF,  0xFF,  0xFC,  0x1F,  0xFF,  0xFF,  0xFC,  
+ 0x1F,  0xFF,  0xFF,  0xFC,  0x1F,  0xFF,  0xFF,  0xFC,  
+ 0x1F,  0xFF,  0xFF,  0xFC,  0x1F,  0xFF,  0xFF,  0xFC,  
+ 0x1F,  0xFF,  0xFF,  0xFC,  0xFF,  0xFF,  0xFF,  0xFF,  
+ 0x7F,  0xFF,  0xFF,  0xFE,  0x3F,  0xFF,  0xFF,  0xFC,  
+ 0x00,  0x00,  0x07,  0x00,  0x00,  0x00,  0x07,  0x00,  
+ 0x00,  0x00,  0x07,  0x00,  0x00,  0x00,  0x07,  0x00,  
+ 0x00,  0x00,  0x0F,  0x80,  0x00,  0x00,  0x0F,  0x80,  
+ 0x00,  0x00,  0x0F,  0x80,  0x00,  0x00,  0x0F,  0x80,  
+ 0x00,  0x00,  0x0F,  0x80,  0xBF,  0xFF,  0xFF,  0xF4,  
+ 0xBF,  0xFF,  0xFD,  0xF4,  0xBF,  0xFF,  0xF8,  0xF4  
+};
+
+const unsigned char apple_tcp_icon[] = { /* default asip icon */
+ 0x30,  0x00,  0x8f,  0xf8,  0xcc,  0x01,  0x48,  0x0c, 
+ 0xb3,  0x32,  0x28,  0x0a,  0x8c,  0xcc,  0x7c,  0x0f, 
+ 0x83,  0x02,  0xff,  0x01,  0x80,  0xc3,  0xc3,  0x81, 
+ 0x80,  0x33,  0xe3,  0xc1,  0x80,  0x0b,  0xd3,  0xc1, 
+ 0x80,  0x0b,  0xb1,  0x61,  0x80,  0x0b,  0xe0,  0xe1, 
+ 0x80,  0x0b,  0xe1,  0xe1,  0x80,  0x0b,  0xd1,  0xe1, 
+ 0xc0,  0x0a,  0xc0,  0xe1,  0x70,  0x0b,  0x78,  0xc1, 
+ 0x1c,  0x0b,  0x79,  0xc1,  0x17,  0x0b,  0x33,  0xff, 
+ 0x21,  0xcb,  0xff,  0xc4,  0x40,  0x7f,  0xff,  0x02, 
+ 0x80,  0x1e,  0x00,  0x01,  0xff,  0xff,  0xff,  0xff, 
+ 0x80,  0x00,  0x00,  0x01,  0xff,  0xff,  0xff,  0xff, 
+ 0x00,  0x02,  0x80,  0x00,  0x00,  0x02,  0x80,  0x00, 
+ 0x00,  0x07,  0xc0,  0x00,  0x00,  0x04,  0x40,  0x00, 
+ 0x00,  0x04,  0x40,  0x00,  0x00,  0x07,  0xc0,  0x00, 
+ 0x00,  0x05,  0x40,  0x00,  0x0f,  0xf9,  0x3f,  0xfc, 
+ 0x00,  0x02,  0x80,  0x00,  0x0f,  0xfc,  0x7f,  0xfc, 
+ 0x30,  0x00,  0x8f,  0xf8,  0xfc,  0x01,  0xcf,  0xfc, 
+ 0xff,  0x33,  0xef,  0xfe,  0xff,  0xff,  0xff,  0xff, 
+ 0xff,  0xff,  0xff,  0xff,  0xff,  0xff,  0xff,  0xff, 
+ 0xff,  0xff,  0xff,  0xff,  0xff,  0xff,  0xff,  0xff, 
+ 0xff,  0xff,  0xff,  0xff,  0xff,  0xff,  0xff,  0xff, 
+ 0xff,  0xff,  0xff,  0xff,  0xff,  0xff,  0xff,  0xff, 
+ 0xff,  0xff,  0xff,  0xff,  0x7f,  0xff,  0xff,  0xff, 
+ 0x1f,  0xff,  0xff,  0xff,  0x1f,  0xff,  0xff,  0xff, 
+ 0x3f,  0xff,  0xff,  0xfc,  0x7f,  0xff,  0xff,  0xfe, 
+ 0xff,  0xff,  0xff,  0xff,  0xff,  0xff,  0xff,  0xff, 
+ 0xff,  0xff,  0xff,  0xff,  0xff,  0xff,  0xff,  0xff, 
+ 0x00,  0x03,  0x80,  0x00,  0x00,  0x03,  0x80,  0x00, 
+ 0x00,  0x07,  0xc0,  0x00,  0x00,  0x07,  0xc0,  0x00, 
+ 0x00,  0x07,  0xc0,  0x00,  0x00,  0x07,  0xc0,  0x00, 
+ 0x00,  0x07,  0xc0,  0x00,  0xff,  0xff,  0xff,  0xff, 
+ 0x3f,  0xfe,  0xff,  0xff,  0xff,  0xfc,  0x7f,  0xff
+};
+
+#if defined( ultrix )
+const u_char   icon[] = {      /* declogo */
+    0x0, 0x80, 0x0, 0x0, 0x1, 0xC0, 0x0, 0x0,
+    0x3, 0xE0, 0x0, 0x0, 0x7, 0xF0, 0x0, 0x0,
+    0xF, 0xB0, 0x0, 0x0, 0x13, 0x6C, 0x0, 0x0,
+    0x2C, 0xDE, 0x0, 0x0, 0x6D, 0xBB, 0x0, 0x0,
+    0xF3, 0x7F, 0x80, 0x0, 0x7E, 0xEF, 0x40, 0x0,
+    0x3D, 0xDE, 0xE0, 0x0, 0x1B, 0xBD, 0xF0, 0x0,
+    0x7, 0x7B, 0xF8, 0x0, 0x7, 0xF7, 0xF8, 0x0,
+    0x3, 0xEC, 0xF6, 0x0, 0x1, 0xDB, 0x6F, 0x0,
+    0x0, 0xBB, 0x5D, 0x80, 0x0, 0x6C, 0xBF, 0xC0,
+    0x0, 0x2D, 0x77, 0xA0, 0x0, 0x12, 0xEF, 0x70,
+    0x0, 0xD, 0xDE, 0xF8, 0x7F, 0xFF, 0xFF, 0xFE,
+    0x20, 0x0, 0x0, 0x4, 0x1F, 0xFF, 0xFF, 0xF8,
+    0x0, 0x4, 0x40, 0x0, 0x0, 0x4, 0x40, 0x0,
+    0x0, 0x3, 0x80, 0x0, 0x0, 0x4, 0x40, 0x0,
+    0xAF, 0xF9, 0x3F, 0xF5, 0x0, 0x2, 0x80, 0x0,
+    0xAF, 0xFC, 0x7F, 0xF5, 0x0, 0x0, 0x0, 0x0,
+    0x0, 0x80, 0x0, 0x0, 0x1, 0xC0, 0x0, 0x0,
+    0x3, 0xE0, 0x0, 0x0, 0x7, 0xF0, 0x0, 0x0,
+    0xF, 0xF8, 0x0, 0x0, 0x1F, 0xFC, 0x0, 0x0,
+    0x3F, 0xFE, 0x0, 0x0, 0x7F, 0xFF, 0x0, 0x0,
+    0xFF, 0xFF, 0x80, 0x0, 0x7F, 0xFF, 0xC0, 0x0,
+    0x3F, 0xFF, 0xE0, 0x0, 0x1F, 0xFF, 0xF0, 0x0,
+    0xF, 0xFF, 0xF8, 0x0, 0x7, 0xFF, 0xFC, 0x0,
+    0x3, 0xFF, 0xFE, 0x0, 0x1, 0xFF, 0xFF, 0x0,
+    0x0, 0xFF, 0xFF, 0x80, 0x0, 0x7F, 0xFF, 0xC0,
+    0x0, 0x3F, 0xFF, 0xE0, 0x0, 0x1F, 0xFF, 0xF0,
+    0x0, 0xF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xFE,
+    0x3F, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xF8,
+    0x0, 0x7, 0xC0, 0x0, 0x0, 0x7, 0xC0, 0x0,
+    0x0, 0x3, 0x80, 0x0, 0x0, 0x7, 0xC0, 0x0,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF,
+    0xFF, 0xFC, 0x7F, 0xFF, 0x0, 0x0, 0x0, 0x0
+};
+
+#else
+#if defined( vax )
+const u_char   icon[] = {      /* daemon */
+    0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x2, 0x0,
+    0x1, 0x80, 0x3, 0x0, 0x2, 0x80, 0x2, 0x80,
+    0x2, 0x80, 0x2, 0x80, 0x4, 0x80, 0x2, 0x40,
+    0x4, 0x87, 0xC2, 0x40, 0x4, 0x58, 0x34, 0x40,
+    0x4, 0x20, 0x8, 0x40, 0x2, 0x16, 0xD0, 0x80,
+    0x1, 0x1, 0x1, 0x0, 0x2, 0x80, 0x2, 0x80,
+    0x2, 0x9C, 0x72, 0x80, 0x4, 0x22, 0x88, 0x40,
+    0x4, 0x41, 0x4, 0x40, 0x4, 0x41, 0x4, 0x40,
+    0x4, 0x41, 0x4, 0x40, 0x4, 0x49, 0x24, 0x40,
+    0xE, 0x55, 0x54, 0xE0, 0x10, 0x5D, 0x74, 0x10,
+    0x10, 0x3E, 0xF8, 0x10, 0x7F, 0xFC, 0x7F, 0xFE,
+    0x20, 0x4, 0x40, 0x4, 0x1F, 0xFC, 0x7F, 0xF8,
+    0x0, 0x7, 0xC0, 0x0, 0x0, 0x4, 0x40, 0x0,
+    0x0, 0x3, 0x80, 0x0, 0x0, 0x4, 0x40, 0x0,
+    0xAF, 0xF9, 0x3F, 0xF5, 0x0, 0x2, 0x80, 0x0,
+    0xAF, 0xFC, 0x7F, 0xF5, 0x0, 0x0, 0x0, 0x0,
+    0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x2, 0x0,
+    0x1, 0x80, 0x3, 0x0, 0x3, 0x80, 0x3, 0x80,
+    0x3, 0x80, 0x3, 0x80, 0x7, 0x80, 0x3, 0xC0,
+    0x7, 0x87, 0xC3, 0xC0, 0x7, 0xDF, 0xF7, 0xC0,
+    0x7, 0xFF, 0xFF, 0xC0, 0x3, 0xFF, 0xFF, 0x80,
+    0x1, 0xFF, 0xFF, 0x0, 0x3, 0xFF, 0xFF, 0x80,
+    0x3, 0xFF, 0xFF, 0x80, 0x7, 0xFF, 0xFF, 0xC0,
+    0x7, 0xFF, 0xFF, 0xC0, 0x7, 0xFF, 0xFF, 0xC0,
+    0x7, 0xFF, 0xFF, 0xC0, 0x7, 0xFF, 0xFF, 0xC0,
+    0xF, 0xFF, 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, 0xF0,
+    0x1F, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, 0xFE,
+    0x3F, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xF8,
+    0x0, 0x7, 0xC0, 0x0, 0x0, 0x7, 0xC0, 0x0,
+    0x0, 0x3, 0x80, 0x0, 0x0, 0x4, 0x40, 0x0,
+    0xAF, 0xF9, 0x3F, 0xF5, 0x0, 0x2, 0x80, 0x0,
+    0xAF, 0xFC, 0x7F, 0xF5, 0x0, 0x0, 0x0, 0x0
+};
+
+#else
+#if defined( sun )
+const u_char   icon[] = {      /* sunlogo */
+    0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x80, 0x0,
+    0x0, 0x2, 0x40, 0x0, 0x0, 0x2, 0x20, 0x0,
+    0x0, 0x9, 0x10, 0x0, 0x0, 0x4, 0x88, 0x0,
+    0x0, 0x22, 0x44, 0x0, 0x0, 0x11, 0x20, 0x0,
+    0x0, 0x88, 0x91, 0x0, 0x1, 0x4, 0x42, 0x0,
+    0x2, 0x22, 0x44, 0x40, 0x4, 0x41, 0x88, 0x80,
+    0x8, 0x98, 0x11, 0x30, 0x11, 0x24, 0x22, 0x48,
+    0x12, 0x44, 0x24, 0x88, 0xC, 0x88, 0x19, 0x10,
+    0x1, 0x11, 0x82, 0x20, 0x2, 0x22, 0x44, 0x40,
+    0x0, 0x42, 0x20, 0x80, 0x0, 0x89, 0x11, 0x0,
+    0x0, 0x4, 0x88, 0x0, 0x7F, 0xFF, 0xFF, 0xFE,
+    0x20, 0x0, 0x0, 0x4, 0x1F, 0xFF, 0xFF, 0xF8,
+    0x0, 0x4, 0x40, 0x0, 0x0, 0x4, 0x40, 0x0,
+    0x0, 0x3, 0x80, 0x0, 0x0, 0x4, 0x40, 0x0,
+    0xAF, 0xF9, 0x3F, 0xF5, 0x0, 0x2, 0x80, 0x0,
+    0xAF, 0xFC, 0x7F, 0xF5, 0x0, 0x0, 0x0, 0x0,
+    0x0, 0x1, 0x80, 0x0, 0x0, 0x3, 0xC0, 0x0,
+    0x0, 0x7, 0xE0, 0x0, 0x0, 0xF, 0xF0, 0x0,
+    0x0, 0x1F, 0xF8, 0x0, 0x0, 0x3F, 0xFC, 0x0,
+    0x0, 0x7F, 0xFE, 0x0, 0x0, 0xFF, 0xFF, 0x0,
+    0x1, 0xFF, 0xFF, 0x80, 0x3, 0xFF, 0xFF, 0xC0,
+    0x7, 0xFF, 0xFF, 0xE0, 0xF, 0xFF, 0xFF, 0xF0,
+    0x1F, 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, 0xFC,
+    0x3F, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xF8,
+    0xF, 0xFF, 0xFF, 0xF0, 0x7, 0xFF, 0xFF, 0xE0,
+    0x3, 0xFF, 0xFF, 0xC0, 0x1, 0xFF, 0xFF, 0x80,
+    0x0, 0xFF, 0xFF, 0x0, 0x7F, 0xFF, 0xFF, 0xFE,
+    0x3F, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xF8,
+    0x0, 0x7, 0xC0, 0x0, 0x0, 0x7, 0xC0, 0x0,
+    0x0, 0x3, 0x80, 0x0, 0x0, 0x7, 0xC0, 0x0,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFC, 0x7F, 0xFF, 0x0, 0x0, 0x0, 0x0
+};
+
+#else
+#if defined( _IBMR2 )
+const u_char   icon[] = {      /* hagar */
+    0x0, 0x0, 0x0, 0x0, 0x18, 0x0, 0x0, 0x18,
+    0x24, 0x0, 0x0, 0x24, 0x44, 0x3, 0xC0, 0x22,
+    0x44, 0xC, 0x30, 0x22, 0x42, 0x30, 0xC, 0x42,
+    0x41, 0x40, 0x2, 0x82, 0x41, 0x80, 0x1, 0x82,
+    0x21, 0x0, 0x0, 0x84, 0x11, 0x41, 0x2, 0x88,
+    0xE, 0xA2, 0x85, 0x70, 0x4, 0x41, 0x2, 0x20,
+    0x4, 0x0, 0x0, 0x20, 0x3, 0xFF, 0xFF, 0xC0,
+    0x3, 0xC0, 0x3, 0xC0, 0x3, 0x82, 0x41, 0xC0,
+    0x7, 0x81, 0x81, 0xE0, 0x7, 0xC2, 0x43, 0xE0,
+    0x7, 0xFC, 0x3F, 0xE0, 0x7, 0xFC, 0x3F, 0xE0,
+    0x7F, 0xFC, 0x3F, 0xFE, 0x20, 0x3, 0xC0, 0x4,
+    0x1F, 0xFF, 0xFF, 0xF8, 0x0, 0x2, 0x40, 0x0,
+    0x0, 0x2, 0x40, 0x0, 0x0, 0x3, 0xC0, 0x0,
+    0x0, 0x4, 0x20, 0x0, 0xAF, 0xF9, 0x9F, 0xF5,
+    0x0, 0x2, 0x40, 0x0, 0xAF, 0xFC, 0x3F, 0xF5,
+    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    0x0, 0x0, 0x0, 0x0, 0x18, 0x0, 0x0, 0x18,
+    0x3C, 0x0, 0x0, 0x3C, 0x7C, 0x3, 0xC0, 0x3E,
+    0x7C, 0xF, 0xF0, 0x3E, 0x7E, 0x3F, 0xFC, 0x7E,
+    0x7F, 0x7F, 0xFE, 0xFE, 0x7F, 0xFF, 0xFF, 0xFE,
+    0x3F, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xF8,
+    0xF, 0xFF, 0xFF, 0xF0, 0x7, 0xFF, 0xFF, 0xE0,
+    0x7, 0xFF, 0xFF, 0xE0, 0x3, 0xFF, 0xFF, 0xC0,
+    0x3, 0xFF, 0xFF, 0xC0, 0x3, 0xFF, 0xFF, 0xC0,
+    0x7, 0xFF, 0xFF, 0xE0, 0x7, 0xFF, 0xFF, 0xE0,
+    0x7, 0xFF, 0xFF, 0xE0, 0x7, 0xFF, 0xFF, 0xE0,
+    0x7F, 0xFF, 0xFF, 0xFE, 0x3F, 0xFF, 0xFF, 0xFC,
+    0x1F, 0xFF, 0xFF, 0xF8, 0x0, 0x3, 0xC0, 0x0,
+    0x0, 0x3, 0xC0, 0x0, 0x0, 0x3, 0xC0, 0x0,
+    0x0, 0x7, 0xE0, 0x0, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF,
+    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
+};
+
+#else
+const u_char   icon[] = {      /* globe */
+    0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0xF0, 0x0,
+    0x0, 0x30, 0xC, 0x0, 0x0, 0xC0, 0x3, 0x0,
+    0x1, 0x80, 0x3, 0x80, 0x3, 0xE2, 0x1F, 0xC0,
+    0x7, 0xC0, 0x1D, 0xE0, 0xF, 0x80, 0x87, 0xF0,
+    0xF, 0x1, 0x8F, 0xF0, 0xE, 0x0, 0x7F, 0x30,
+    0x1E, 0x0, 0xFD, 0x78, 0x12, 0x0, 0xE4, 0xF8,
+    0x10, 0x0, 0x40, 0xF8, 0x10, 0x0, 0x3E, 0x78,
+    0x1F, 0x0, 0x7F, 0xF8, 0x1F, 0x80, 0x7F, 0xF8,
+    0xF, 0x80, 0x3F, 0x30, 0xF, 0x80, 0xF, 0x30,
+    0xF, 0x80, 0xE, 0x90, 0x7, 0x80, 0xE, 0xA0,
+    0x3, 0xC0, 0xE, 0x40, 0x7F, 0xFF, 0xFF, 0xFE,
+    0x20, 0x0, 0x0, 0x4, 0x1F, 0xFF, 0xFF, 0xF8,
+    0x0, 0x4, 0x40, 0x0, 0x0, 0x4, 0x40, 0x0,
+    0x0, 0x3, 0x80, 0x0, 0x0, 0x4, 0x40, 0x0,
+    0xAF, 0xF9, 0x3F, 0xF5, 0x0, 0x2, 0x80, 0x0,
+    0xAF, 0xFC, 0x7F, 0xF5, 0x0, 0x0, 0x0, 0x0,
+    0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0xF0, 0x0,
+    0x0, 0x3F, 0xFC, 0x0, 0x0, 0xFF, 0xFF, 0x0,
+    0x1, 0xFF, 0xFF, 0x80, 0x3, 0xFF, 0xFF, 0xC0,
+    0x7, 0xFF, 0xFF, 0xE0, 0xF, 0xFF, 0xFF, 0xF0,
+    0xF, 0xFF, 0xFF, 0xF0, 0xF, 0xFF, 0xFF, 0xF0,
+    0x1F, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xF8,
+    0x1F, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xF8,
+    0x1F, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xF8,
+    0xF, 0xFF, 0xFF, 0xF0, 0xF, 0xFF, 0xFF, 0xF0,
+    0xF, 0xFF, 0xFF, 0xF0, 0x7, 0xFF, 0xFF, 0xE0,
+    0x3, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xFE,
+    0x3F, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xF8,
+    0x0, 0x7, 0xC0, 0x0, 0x0, 0x7, 0xC0, 0x0,
+    0x0, 0x3, 0x80, 0x0, 0x0, 0x7, 0xC0, 0x0,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF,
+    0xFF, 0xFC, 0x7F, 0xFF, 0x0, 0x0, 0x0, 0x0
+};
+#endif /*_IBMR2*/
+#endif /*sun*/
+#endif /*vax*/
+#endif /*ultrix*/
+#endif
diff --git a/etc/afpd/main.c b/etc/afpd/main.c
new file mode 100644 (file)
index 0000000..512091a
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/uio.h>
+#include <sys/syslog.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <errno.h>
+
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <atalk/compat.h>
+#include <atalk/dsi.h>
+#include <atalk/atp.h>
+#include <atalk/asp.h>
+#include <atalk/afp.h>
+#include <atalk/adouble.h>
+#include <atalk/paths.h>
+#include <atalk/util.h>
+#include <atalk/server_child.h>
+
+#include "globals.h"
+#include "config.h"
+#include "status.h"
+#include "fork.h"
+#include "uam_auth.h"
+
+unsigned char  nologin = 0;
+
+static struct afp_options default_options;
+static AFPConfig *configs;
+static server_child *server_children;
+static fd_set save_rfds;
+
+static void afp_exit(const int i)
+{
+  server_unlock(default_options.pidfile);
+  exit(i);
+}
+
+static void afp_goaway(int sig)
+{
+#ifndef NO_DDP
+    asp_kill(sig);
+#endif
+    dsi_kill(sig);
+    switch( sig ) {
+    case SIGTERM :
+      syslog( LOG_INFO, "shutting down on signal %d", sig );
+      break;
+    case SIGHUP :
+      /* w/ a configuration file, we can force a re-read if we want */
+      nologin++;
+      if ((nologin + 1) & 1) {
+       AFPConfig *config;
+
+       syslog(LOG_INFO, "re-reading configuration file");
+       for (config = configs; config; config = config->next)
+         if (config->server_cleanup)
+           config->server_cleanup(config);
+
+       configfree(configs, NULL);
+       if (!(configs = configinit(&default_options))) {
+         syslog(LOG_ERR, "config re-read: no servers configured");
+         afp_exit(1);
+       }
+       FD_ZERO(&save_rfds);
+       for (config = configs; config; config = config->next) {
+         if (config->fd < 0)
+           continue;
+         FD_SET(config->fd, &save_rfds);
+       }
+      } else {
+       syslog(LOG_INFO, "disallowing logins");
+       auth_unload();
+      }
+      break;
+    default :
+       syslog( LOG_ERR, "afp_goaway: bad signal" );
+    }
+    if ( sig == SIGTERM ) {
+      AFPConfig *config;
+      
+      for (config = configs; config; config = config->next) 
+       if (config->server_cleanup) 
+         config->server_cleanup(config);
+
+      afp_exit(0);
+    }
+    return;
+}
+
+static void child_handler()
+{
+  server_child_handler(server_children);
+}
+
+int main( ac, av )
+    int                ac;
+    char       **av;
+{
+    AFPConfig           *config;
+    fd_set              rfds;
+    struct sigaction   sv;
+    sigset_t            sigs;
+
+    umask( 0 );                /* so inherited file permissions work right */
+
+    afp_options_init(&default_options);
+    if (!afp_options_parse(ac, av, &default_options))
+      exit(1);
+    
+    switch(server_lock("afpd", default_options.pidfile, 
+                      default_options.flags & OPTION_DEBUG)) {
+    case -1: /* error */
+      exit(1);
+    case 0: /* child */
+      break;
+    default: /* server */
+      exit(0);
+    }
+
+    /* install child handler for asp and dsi. we do this before afp_goaway
+     * as afp_goaway references stuff from here. 
+     * XXX: this should really be setup after the initial connections. */
+    if (!(server_children = server_child_alloc(default_options.connections,
+                                              CHILD_NFORKS))) {
+      syslog(LOG_ERR, "main: server_child alloc: %m");
+      afp_exit(1);
+    }
+      
+    memset(&sv, 0, sizeof(sv));
+    sv.sa_handler = child_handler;
+    sigemptyset( &sv.sa_mask );
+    sv.sa_flags = SA_RESTART;
+    if ( sigaction( SIGCHLD, &sv, 0 ) < 0 ) {
+       syslog( LOG_ERR, "main: sigaction: %m" );
+       afp_exit(1);
+    }
+
+    sv.sa_handler = afp_goaway;
+    sigemptyset( &sv.sa_mask );
+    sigaddset(&sv.sa_mask, SIGHUP);
+    sigaddset(&sv.sa_mask, SIGTERM);
+    sv.sa_flags = SA_RESTART;
+    if ( sigaction( SIGHUP, &sv, 0 ) < 0 ) {
+       syslog( LOG_ERR, "main: sigaction: %m" );
+       afp_exit(1);
+    }
+    if ( sigaction( SIGTERM, &sv, 0 ) < 0 ) {
+       syslog( LOG_ERR, "main: sigaction: %m" );
+       afp_exit(1);
+    }
+    
+    /* afpd.conf: not in config file: lockfile, connections, configfile
+     *            preference: command-line provides defaults.
+     *                        config file over-writes defaults.
+     *
+     * we also need to make sure that killing afpd during startup
+     * won't leave any lingering registered names around.
+     */
+
+    sigemptyset(&sigs);
+    sigaddset(&sigs, SIGHUP);
+    sigaddset(&sigs, SIGTERM);
+    sigprocmask(SIG_BLOCK, &sigs, NULL);
+    if (!(configs = configinit(&default_options))) {
+      syslog(LOG_ERR, "main: no servers configured: %m\n");
+      afp_exit(1);
+    }
+    sigprocmask(SIG_UNBLOCK, &sigs, NULL);
+
+    /* watch atp and dsi sockets. */
+    FD_ZERO(&save_rfds);
+    for (config = configs; config; config = config->next) {
+      if (config->fd < 0) /* for proxies */
+       continue;
+      FD_SET(config->fd, &save_rfds);
+    }
+    
+    /* wait for an appleshare connection. parent remains in the loop
+     * while the children get handled by afp_over_{asp,dsi}.  this is
+     * currently vulnerable to a denial-of-service attack if a
+     * connection is made without an actual login attempt being made
+     * afterwards. establishing timeouts for logins is a possible 
+     * solution. */
+    while (1) {
+      rfds = save_rfds;
+      if (select(FD_SETSIZE, &rfds, NULL, NULL, NULL) < 0) {
+       if (errno == EINTR)
+         continue;
+       syslog(LOG_ERR, "main: can't wait for input: %m");
+       break;
+      }
+      
+      for (config = configs; config; config = config->next) {
+       if (config->fd < 0)
+         continue;
+       if (FD_ISSET(config->fd, &rfds)) 
+         config->server_start(config, configs, server_children); 
+      }
+    }
+
+    return 0;
+}
diff --git a/etc/afpd/messages.c b/etc/afpd/messages.c
new file mode 100644 (file)
index 0000000..982a706
--- /dev/null
@@ -0,0 +1,63 @@
+/* 
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <atalk/afp.h>
+#include "globals.h"
+#include "misc.h"
+
+#define MAXMESGSIZE 199
+
+/* this is only used by afpd children, so it's okay. */
+static char servermesg[MAXMESGSIZE] = "";
+
+void setmessage(const char *message)
+{
+  strncpy(servermesg, message, MAXMESGSIZE);
+}
+
+int afp_getsrvrmesg(obj, ibuf, ibuflen, rbuf, rbuflen)
+     AFPObj *obj;
+     char *ibuf, *rbuf;
+     int ibuflen, *rbuflen;
+{
+  char *message;
+  u_int16_t type, bitmap;
+
+  memcpy(&type, ibuf + 2, sizeof(type));
+  memcpy(&bitmap, ibuf + 4, sizeof(bitmap));
+
+  switch (ntohs(type)) {
+  case AFPMESG_LOGIN: /* login */
+    message = obj->options.loginmesg;
+    break;
+  case AFPMESG_SERVER: /* server */
+    message = servermesg;
+    break;
+  default:
+    *rbuflen = 0;
+    return AFPERR_BITMAP;
+  }
+
+  /* output format:
+   * message type:   2 bytes
+   * bitmap:         2 bytes
+   * message length: 1 byte
+   * message:        up to 199 bytes
+   */
+  memcpy(rbuf, &type, sizeof(type));
+  rbuf += sizeof(type);
+  memcpy(rbuf, &bitmap, sizeof(bitmap));
+  rbuf += sizeof(bitmap);
+  *rbuflen = strlen(message);
+  if (*rbuflen > MAXMESGSIZE)
+    *rbuflen = MAXMESGSIZE;
+  *rbuf++ = *rbuflen;
+  memcpy(rbuf, message, *rbuflen);
+
+  *rbuflen += 5;
+
+  return AFP_OK;
+}
diff --git a/etc/afpd/misc.h b/etc/afpd/misc.h
new file mode 100644 (file)
index 0000000..16b7c33
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef AFPD_MISC_H
+#define AFPD_MISC_H 1
+
+#include <sys/cdefs.h>
+#include "globals.h"
+
+/* FP functions */
+/* messages.c */
+extern int     afp_getsrvrmesg __P((AFPObj *, char *, int, char *, int *));
+
+/* afs.c */
+# ifdef AFS
+extern int     afp_getdiracl __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_setdiracl __P((AFPObj *, char *, int, char *, int *));
+# else AFS
+#define afp_getdiracl  NULL
+#define afp_setdiracl  NULL
+# endif AFS
+
+# if defined( AFS ) && defined( UAM_AFSKRB )
+extern int     afp_afschangepw __P((AFPObj *, char *, int, char *, int *));
+# else AFS UAM_AFSKRB
+#define afp_afschangepw        NULL
+# endif AFS UAM_AFSKRB
+
+#endif
diff --git a/etc/afpd/nfsquota.c b/etc/afpd/nfsquota.c
new file mode 100644 (file)
index 0000000..03754de
--- /dev/null
@@ -0,0 +1,163 @@
+/* parts of this are lifted from the bsd quota program and are
+ * therefore under the following copyright:
+ *
+ * Copyright (c) 1980, 1990, 1993
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Elz at The University of Melbourne.
+ *
+ * Ported for AIX (jfs) by Joerg Schumacher (J.Schumacher@tu-bs.de) at the
+ * Technische Universitaet Braunschweig, FRG
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/param.h> /* for DEV_BSIZE */
+#include <sys/time.h>  /* <rpc/rpc.h> on ultrix doesn't include this */
+#include <syslog.h>
+
+#include <netdb.h>
+#include <netinet/in.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+#include <rpcsvc/rquota.h>
+
+#include <atalk/afp.h>
+
+#include "unix.h"
+
+#ifndef NO_QUOTA_SUPPORT
+/* lifted (with modifications) from the bsd quota program */
+static int 
+callaurpc(vol, prognum, versnum, procnum, inproc, in, outproc, out)
+       struct vol *vol;
+       u_long prognum, versnum, procnum;
+       xdrproc_t inproc, outproc;
+       char *in, *out;
+{
+       enum clnt_stat clnt_stat;
+       struct timeval tottimeout;
+       if (!vol->v_nfsclient) {
+         struct hostent *hp;
+         struct sockaddr_in server_addr;
+         struct timeval timeout;
+         int socket = RPC_ANYSOCK;
+         
+         if ((hp = gethostbyname(vol->v_gvs)) == NULL)
+           return ((int) RPC_UNKNOWNHOST);
+         timeout.tv_usec = 0;
+         timeout.tv_sec = 6;
+         memcpy(&server_addr.sin_addr, hp->h_addr, hp->h_length);
+         server_addr.sin_family = AF_INET;
+         server_addr.sin_port =  0;
+         
+         if ((vol->v_nfsclient = (void *) 
+              clntudp_create(&server_addr, prognum, versnum, 
+                             timeout, &socket)) == NULL)
+           return ((int) rpc_createerr.cf_stat);
+         
+         ((CLIENT *) vol->v_nfsclient)->cl_auth = authunix_create_default();
+       }
+
+       tottimeout.tv_sec = 10;
+       tottimeout.tv_usec = 0;
+       clnt_stat = clnt_call((CLIENT *) vol->v_nfsclient, procnum,
+                             inproc, in, outproc, out, tottimeout);
+       return ((int) clnt_stat);
+}
+
+
+/* sunos 4 machines structure things a little differently. */
+#ifdef USE_OLD_RQUOTA
+#define GQR_STATUS gqr_status
+#define GQR_RQUOTA gqr_rquota
+#else
+#define GQR_STATUS status
+#define GQR_RQUOTA getquota_rslt_u.gqr_rquota
+#endif
+
+int getnfsquota(const struct vol *vol, const int uid, const u_int32_t bsize,
+               struct dqblk *dqp)
+{
+
+  struct getquota_args gq_args;
+  struct getquota_rslt gq_rslt;
+  struct timeval tv;
+  char *hostpath;
+  /* figure out the host and path */
+  if ((hostpath = strchr(vol->v_gvs, ':')) == NULL) {
+    syslog(LOG_ERR, "can't find hostname for %s", vol->v_gvs);
+    return AFPERR_PARAM;
+  }
+  if (*(hostpath + 1) != '/') 
+    return AFPERR_PARAM;
+
+  /* separate host from hostpath */
+  *hostpath = '\0';
+  
+  gq_args.gqa_pathp = hostpath + 1;
+  gq_args.gqa_uid = uid;
+
+  if(callaurpc(vol, RQUOTAPROG, RQUOTAVERS, RQUOTAPROC_GETQUOTA, 
+              (xdrproc_t) xdr_getquota_args, (char *) &gq_args,
+              (xdrproc_t) xdr_getquota_rslt, (char *) &gq_rslt) != 0) {
+    syslog(LOG_INFO, "nfsquota: can't retrieve nfs quota information. \
+make sure that rpc.rquotad is running on %s.", vol->v_gvs);
+    *hostpath = ':';
+    return AFPERR_PARAM;
+  }
+  switch (gq_rslt.GQR_STATUS) {
+  case Q_NOQUOTA:
+    break;
+
+  case Q_EPERM:
+    syslog(LOG_ERR, "nfsquota: quota permission error, host: %s\n",
+          vol->v_gvs);
+    break;
+
+  case Q_OK: /* we only copy the bits that we need. */
+    gettimeofday(&tv, NULL);
+
+#ifdef __svr4__
+    /* why doesn't using bsize work? */
+#define NFS_BSIZE (gq_rslt.GQR_RQUOTA.rq_bsize / DEV_BSIZE)
+#else
+    /* NOTE: linux' rquotad program doesn't currently report the
+     * correct rq_bsize. */
+#define NFS_BSIZE (gq_rslt.GQR_RQUOTA.rq_bsize / bsize)
+#endif
+
+    dqp->dqb_bhardlimit =
+      gq_rslt.GQR_RQUOTA.rq_bhardlimit*NFS_BSIZE;
+    dqp->dqb_bsoftlimit =
+      gq_rslt.GQR_RQUOTA.rq_bsoftlimit*NFS_BSIZE;
+    dqp->dqb_curblocks =
+      gq_rslt.GQR_RQUOTA.rq_curblocks*NFS_BSIZE;
+
+#ifdef ultrix
+    dqp->dqb_bwarn = gq_rslt.GQR_RQUOTA.rq_btimeleft;
+#else
+    dqp->dqb_btimelimit =
+      tv.tv_sec + gq_rslt.GQR_RQUOTA.rq_btimeleft;
+#endif
+
+    *hostpath = ':';
+    return AFP_OK;
+    break;
+
+  default:
+    syslog(LOG_INFO, "bad rpc result, host: %s\n", vol->v_gvs);
+    break;
+  }
+
+  *hostpath = ':';
+  return AFPERR_PARAM;
+}
+#endif
diff --git a/etc/afpd/nls/Makefile b/etc/afpd/nls/Makefile
new file mode 100644 (file)
index 0000000..1d375eb
--- /dev/null
@@ -0,0 +1,25 @@
+SRC = makecode.c parsecode.c 
+OBJ = makecode.o parsecode.o
+
+INCPATH= -I../../../include 
+CFLAGS=        ${DEFS} ${OPTOPTS} ${INCPATH}
+
+all:: codepage.h parsecode makecode
+
+codepage.h:
+       -ln -s ../codepage.h .
+
+parsecode: parsecode.o
+       ${CC} ${CFLAGS} ${LDFLAGS} parsecode.o -o parsecode
+
+makecode: makecode.o
+       ${CC} ${CFLAGS} ${LDFLAGS} makecode.o -o makecode
+
+install: 
+       -mkdir ${RESDIR}/nls
+       -./makecode
+       -${INSTALL} -m 644 maccode.* ${RESDIR}/nls
+
+clean:
+       rm -f *.o
+       rm -f makecode parsecode maccode.*
diff --git a/etc/afpd/nls/makecode.c b/etc/afpd/nls/makecode.c
new file mode 100644 (file)
index 0000000..da7b93f
--- /dev/null
@@ -0,0 +1,136 @@
+/* quick-and-dirty way of creating code pages */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <netatalk/endian.h>
+#include "codepage.h"
+
+/* map of cp10000 (mac roman) to iso-latin-1 */
+static const unsigned char mac_to_isolatin1_map[] = {
+0xC4, 0xC5, 0xC7, 0xC9, 0xD1, 0xD6, 0xDC, 0xE1,
+0xE0, 0xE2, 0xE4, 0xE3, 0xE5, 0xE7, 0xE9, 0xE8,
+0xEA, 0xEB, 0xED, 0xEC, 0xEE, 0xEF, 0xF1, 0xF3,
+0xF2, 0xF4, 0xF6, 0xF5, 0xFA, 0xF9, 0xFB, 0xFC,
+0x00, 0xB0, 0xA2, 0xA3, 0xA7, 0xB7, 0xB6, 0xDF,
+0xAE, 0xA9, 0x00, 0xB4, 0xA8, 0x00, 0xC6, 0xD8,
+0x00, 0xB1, 0x00, 0x00, 0xA5, 0xB5, 0xF0, 0x00,
+0x00, 0x00, 0x00, 0xAA, 0xBA, 0x00, 0xE6, 0xF8,
+0xBF, 0xA1, 0xAC, 0x00, 0x00, 0x00, 0x00, 0xAB,
+0xBB, 0x00, 0xA0, 0xC0, 0xC3, 0xD5, 0x00, 0x00,
+0xAD, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF7, 0x00,
+0xFF, 0x00, 0x00, 0xA4, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xB8, 0x00, 0x00, 0xC2, 0xCA, 0xC1,
+0xCB, 0xC8, 0xCD, 0xCE, 0xCF, 0xCC, 0xD3, 0xD4,
+0x00, 0xD2, 0xDA, 0xDB, 0xD9 
+/*, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00 */
+};
+
+/* Map of CP10000 (Mac Roman) to MSDOS CP 437 */
+static const unsigned char mac_to_cp437_map[] = {
+0x8e, 0x8f, 0x80, 0x90, 0xa5, 0x99, 0x9a, 0xa0, 
+0x85, 0x83, 0x84, 0xe0, 0x86, 0x87, 0x82, 0x8a,
+0x88, 0x89, 0xa1, 0x8d, 0x8c, 0x8b, 0xa4, 0xa2, 
+0x95, 0x93, 0x94, 0xe5, 0xa3, 0x97, 0x96, 0x81,
+0xc5, 0xf8, 0x9b, 0x9c, 0xb6, 0xf9, 0xb8, 0xe1, 
+0xc9, 0xe9, 0xcb, 0xd9, 0xc0, 0xd8, 0x92, 0xe8,
+0xec, 0xf1, 0xf3, 0xf2, 0x9d, 0xe6, 0xeb, 0xe4, 
+0xef, 0xe3, 0xf4, 0xa6, 0xa7, 0xea, 0x91, 0xed,
+0xa8, 0xad, 0xaa, 0xfb, 0x9f, 0xf7, 0xdd, 0xae, 
+0xaf, 0xb0, 0xff, 0xd6, 0xd2, 0xb1, 0xb2, 0xb7,
+0xb4, 0xc4, 0xb5, 0xb9, 0xba, 0xbb, 0xf6, 0xbc, 
+0x98, 0xbd, 0xf5, 0xfe, 0xbe, 0xbf, 0xc1, 0xc2,
+0xce, 0xfa, 0xc3, 0xc6, 0xc7, 0xc8, 0xee, 0xca, 
+0xcc, 0xcd, 0xb3, 0xd7, 0xcf, 0xd0, 0xd1, 0xd3,
+0xdb, 0xda, 0xa9, 0xab, 0xac, 0xd4, 0xd5, 0xdc, 
+0xde, 0xdf, 0xfc, 0xfd, 0xe7, 0xe2, 0xf0, 0x9e
+};
+
+/* Map of CP10000 (Mac Roman) to MSDOS CP 850 */
+static const unsigned char mac_to_cp850_map[] = {
+0x8e, 0x8f, 0x80, 0x90, 0xa5, 0x99, 0x9a, 0xa0,
+0x85, 0x83, 0x84, 0xc6, 0x86, 0x87, 0x82, 0x8a,
+0x88, 0x89, 0xa1, 0x8d, 0x8c, 0x8b, 0xa4, 0xa2,
+0x95, 0x93, 0x94, 0xe4, 0xa3, 0x97, 0x96, 0x81,
+0xc5, 0xf8, 0xbd, 0x9c, 0xf5, 0xb0, 0xf4, 0xe1,
+0xa9, 0xb8, 0xb1, 0xef, 0xf9, 0xca, 0x92, 0x9d,
+0xb2, 0xf1, 0xb3, 0xb4, 0xbe, 0xe6, 0xd0, 0xba,
+0xbb, 0xcb, 0xdd, 0xa6, 0xa7, 0xbc, 0x91, 0x9b,
+0xa8, 0xad, 0xaa, 0xd9, 0x9f, 0xf2, 0xfe, 0xae,
+0xaf, 0xdc, 0xff, 0xb7, 0xc7, 0xe5, 0xc0, 0xc1,
+0xc2, 0xc4, 0xc3, 0x9e, 0xab, 0xac, 0xf6, 0xbf,
+0x98, 0xed, 0xc8, 0xcf, 0xc9, 0xcc, 0xcd, 0xce,
+0xd1, 0xfa, 0xd5, 0xda, 0xdb, 0xb6, 0xd2, 0xb5,
+0xd3, 0xd4, 0xd6, 0xd7, 0xd8, 0xde, 0xe0, 0xe2,
+0xf0, 0xe3, 0xe9, 0xea, 0xeb, 0xfb, 0xdf, 0xe7,
+0xee, 0xe8, 0xec, 0xf3, 0xf7, 0xfc, 0xfd, 0xb9
+};
+
+struct mac_code {
+  char *m_name;
+  const unsigned char *m_map; 
+  u_int16_t m_len;
+  const char *m_id;
+};
+
+static struct mac_code names[] = {
+  {"maccode.437", mac_to_cp437_map, sizeof(mac_to_cp437_map), "cp437"},
+  {"maccode.850", mac_to_cp850_map, sizeof(mac_to_cp850_map), "cp850"},
+  {"maccode.iso8859-1", mac_to_isolatin1_map, 
+   sizeof(mac_to_isolatin1_map), "iso8859-1"},
+  {NULL, NULL, 0, NULL}
+};
+
+int main(int argc, char **argv)
+{
+  unsigned char buf[CODEPAGE_FILE_HEADER_SIZE];
+  u_int16_t id;
+  int i, j;
+  FILE *fp;
+  
+  for (i = 0; names[i].m_name; i++) {
+    if ((fp = fopen(names[i].m_name, "w")) == NULL) {
+      fprintf(stderr, "can't open %s\n", names[i].m_name);
+      continue;
+    }
+
+    id = htons(CODEPAGE_FILE_ID); /* file id */
+    memcpy(buf, &id, sizeof(id));
+    *(buf + 2) = CODEPAGE_FILE_VERSION; /* version */
+    if ((j = strlen(names[i].m_id)) & 1) /* pad to even boundary */
+      j++;
+    *(buf + 3) = j; /* length of name */
+
+    *(buf + 4) = 1; /* default quantum. this should be modified to 
+                    * deal with multibyte characters */
+
+    /* rules */
+    *(buf + 5) = CODEPAGE_RULE_MTOU | CODEPAGE_RULE_UTOM;
+
+    /* offset to data */
+    id = htons(CODEPAGE_FILE_HEADER_SIZE + j); 
+    memcpy(buf + 6, &id, sizeof(id));
+
+    /* size of data */
+    id = htons(names[i].m_len); 
+    memcpy(buf + 7, &id, sizeof(id));
+
+    /* write it out */
+    fwrite(buf, CODEPAGE_FILE_HEADER_SIZE, 1, fp);
+
+    /* we either keep or drop the null byte to keep the name on an
+       even boundary */
+    fwrite(names[i].m_id, j, 1, fp); 
+    
+    /* we typically only map characters > 0x7F */
+    for (j = 0; j < names[i].m_len; j++) {
+      buf[0] = CODEPAGE_RULE_MTOU | CODEPAGE_RULE_UTOM;
+      buf[1] = j + 0x80;
+      buf[2] = names[i].m_map[j];
+      fwrite(buf, 3, 1, fp);
+    }
+    fclose(fp);
+  }
+}
diff --git a/etc/afpd/nls/parsecode.c b/etc/afpd/nls/parsecode.c
new file mode 100644 (file)
index 0000000..025c0e0
--- /dev/null
@@ -0,0 +1,77 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <netatalk/endian.h>
+#include "codepage.h"
+
+int main(int argc, char **argv)
+{
+  unsigned char name[255 + 1], buf[CODEPAGE_FILE_HEADER_SIZE];
+  unsigned char quantum, rules;
+  u_int16_t id;
+  FILE *fp;
+
+  if (argc != 2) {
+    fprintf(stderr, "%s <codepage>\n", *argv);
+    return -1;
+  }
+    
+  if ((fp = fopen(argv[1], "r")) == NULL) {
+    fprintf(stderr, "%s: can't open file.\n", *argv);
+    return -1;
+  }
+
+  if (fread(buf, CODEPAGE_FILE_HEADER_SIZE, 1, fp) != 1) {
+    fprintf(stderr, "%s: can't get header.\n", *argv);
+    goto fail_end;
+  }
+
+  /* id */
+  memcpy(&id, buf, sizeof(id));
+  id = ntohs(id);
+  if (id != CODEPAGE_FILE_ID) {
+    fprintf(stderr, "%s: wrong file type.\n", *argv);
+    goto fail_end;
+  }
+
+  /* version */
+  if (*(buf + 2) != CODEPAGE_FILE_VERSION) {
+    fprintf(stderr, "%s: wrong file version.\n", *argv);
+    goto fail_end;
+  }
+    
+  /* size of name */
+  id = *(buf + 3);
+
+  /* quantum and rules */
+  quantum = *(buf + 4);
+  rules = *(buf + 5);
+
+  fread(name, id, 1, fp);
+  if (name[id - 1] != '\0') /* name isn't null-padded */
+    name[id] = '\0';
+  printf("codepage: %s [", name);
+
+  /* move to the data */
+  memcpy(&id, buf + 6, sizeof(id));
+  id = ntohs(id);
+  fseek(fp, id, SEEK_SET);
+
+  /* size of data */
+  memcpy(&id, buf + 8, sizeof(id));
+  id = ntohs(id);
+  printf("size=%d]\n", id);
+  printf("---------\n");
+
+  while (fread(buf, 1 + 2*quantum, 1, fp) == 1) {
+    printf("0x%02x: 0x%02X 0x%02X\n", buf[0], buf[1], buf[1 + quantum]);
+  }
+  fclose(fp);
+  return 0;
+
+fail_end:
+  fclose(fp);
+  return -1;
+}
diff --git a/etc/afpd/ofork.c b/etc/afpd/ofork.c
new file mode 100644 (file)
index 0000000..71eb514
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 1996 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h> /* works around a bug */
+#include <sys/param.h>
+#include <syslog.h>
+
+#include <atalk/adouble.h>
+
+#include "globals.h"
+#include "volume.h"
+#include "directory.h"
+#include "fork.h"
+
+/* we need to have a hashed list of oforks (by name). just hash
+ * by first letter. */
+#define OFORK_HASHSIZE  64
+static struct ofork     *ofork_table[OFORK_HASHSIZE];
+
+static struct ofork    **oforks = NULL;
+static int             nforks = 0;
+static u_short         lastrefnum = 0;
+
+
+/* OR some of each character for the hash*/
+static __inline__ unsigned long hashfn(const char *name)
+{
+  unsigned long i = 0;
+
+  while (*name) {
+    i = ((i << 4) | (8*sizeof(i) - 4)) ^ *name++;
+  }
+  return i & (OFORK_HASHSIZE - 1);
+}
+
+static __inline__ void of_hash(struct ofork *of)
+{
+  struct ofork **table;
+
+  table = &ofork_table[hashfn(of->of_name)];
+  if ((of->next = *table) != NULL)
+    (*table)->prevp = &of->next;
+  *table = of;
+  of->prevp = table;
+}
+
+static __inline__ void of_unhash(struct ofork *of)
+{
+  if (of->prevp) {
+    if (of->next)
+      of->next->prevp = of->prevp;
+    *(of->prevp) = of->next;
+  }
+}
+
+void of_pforkdesc( f )
+    FILE       *f;
+{
+    u_short    ofrefnum;
+
+    if (!oforks)
+      return;
+
+    for ( ofrefnum = 0; ofrefnum < nforks; ofrefnum++ ) {
+       if ( oforks[ ofrefnum ] != NULL ) {
+           fprintf( f, "%hu <%s>\n", ofrefnum, oforks[ ofrefnum ]->of_name);
+       }
+    }
+}
+
+int of_flush(const struct vol *vol)
+{
+    u_int16_t  refnum;
+
+    if (!oforks)
+      return 0;
+
+    for ( refnum = 0; refnum < nforks; refnum++ ) {
+       if (oforks[ refnum ] != NULL && (oforks[refnum]->of_vol == vol) &&
+            flushfork( oforks[ refnum ] ) < 0 ) {
+           syslog( LOG_ERR, "of_flush: %m" );
+       }
+    }
+    return( 0 );
+}
+
+
+int of_rename(vol, olddir, oldpath, newdir, newpath)
+     const struct vol *vol;
+     struct dir *olddir, *newdir;
+     const char *oldpath, *newpath;
+{
+  struct ofork *of, *next, *d_ofork;
+  
+  next = ofork_table[hashfn(oldpath)];
+  while ((of = next)) {
+    next = next->next; /* so we can unhash and still be all right. */
+
+    if ((vol == of->of_vol) && (olddir == of->of_dir) && 
+       (strcmp(of->of_name, oldpath) == 0)) {
+      of_unhash(of);
+      strncpy( of->of_name, newpath, of->of_namelen);
+      of->of_d_prev->of_d_next = of->of_d_prev;
+      of->of_d_next->of_d_prev = of->of_d_next;
+      of->of_dir = newdir;
+      if (!(d_ofork = newdir->d_ofork)) {
+       newdir->d_ofork = of;
+       of->of_d_next = of->of_d_prev = of;
+      } else {
+       of->of_d_next = d_ofork;
+       of->of_d_prev = d_ofork->of_d_prev;
+       of->of_d_prev->of_d_next = of;
+       d_ofork->of_d_prev = of;
+      }
+      of_hash(of); 
+    }
+  }
+
+  return AFP_OK;
+}
+
+    struct ofork *
+of_alloc(vol, dir, path, ofrefnum, eid, ad)
+    struct vol          *vol;
+    struct dir         *dir;
+    char               *path;
+    u_int16_t          *ofrefnum;
+    const int           eid;
+    struct adouble      *ad;
+{
+    struct ofork        *of, *d_ofork;
+    u_int16_t          refnum, of_refnum;
+
+    int                        i;
+
+    if (!oforks) {
+      nforks = (getdtablesize() - 10) / 2;
+      oforks = (struct ofork **) calloc(nforks, sizeof(struct ofork *));
+      if (!oforks)
+       return NULL;
+    }
+
+    for ( refnum = lastrefnum++, i = 0; i < nforks; i++, refnum++ ) {
+       if ( oforks[ refnum % nforks ] == NULL ) {
+           break;
+       }
+    }
+    if ( i == nforks ) {
+        syslog(LOG_ERR, "of_alloc: maximum number of forks exceeded.");
+       return( NULL );
+    }
+
+    of_refnum = refnum % nforks;
+    if (( oforks[ of_refnum ] =
+           (struct ofork *)malloc( sizeof( struct ofork ))) == NULL ) {
+       syslog( LOG_ERR, "of_alloc: malloc: %m" );
+       return NULL;
+    }
+    of = oforks[of_refnum];
+
+    /* see if we need to allocate space for the adouble struct */
+    if ((of->of_ad = ad ? ad : 
+        calloc(1, sizeof(struct adouble))) == NULL) {
+       syslog( LOG_ERR, "of_alloc: malloc: %m" );
+       return NULL;
+    }
+
+    of->of_vol = vol;
+    of->of_dir = dir;
+
+    if (!(d_ofork = dir->d_ofork)) {
+      dir->d_ofork = of;
+      of->of_d_next = of->of_d_prev = of;
+    } else {
+      of->of_d_next = d_ofork;
+      of->of_d_prev = d_ofork->of_d_prev;
+      d_ofork->of_d_prev->of_d_next = of;
+      d_ofork->of_d_prev = of;
+    }
+
+    /* here's the deal: we allocate enough for the standard mac file length. 
+     * in the future, we'll reallocate in fairly large jumps in case
+     * of long unicode names */
+    if (( of->of_name =(char *)malloc(MACFILELEN + 1)) ==
+       NULL ) {
+       syslog( LOG_ERR, "of_alloc: malloc: %m" );
+       if (!ad)
+         free(of->of_ad);
+       free(of);
+       oforks[ of_refnum ] = NULL;
+       return NULL;
+    }
+    strncpy( of->of_name, path, of->of_namelen = MACFILELEN + 1);
+    *ofrefnum = refnum;
+    of->of_refnum = of_refnum;
+    of_hash(of);
+
+    if (eid == ADEID_DFORK)
+      of->of_flags = AFPFORK_DATA;
+    else
+      of->of_flags = AFPFORK_RSRC;
+
+    return( of );
+}
+
+struct ofork *of_find(const u_int16_t ofrefnum )
+{
+   if (!oforks)
+     return NULL;
+
+    return( oforks[ ofrefnum % nforks ] );
+}
+
+struct ofork *
+of_findname(const struct vol *vol, const struct dir *dir, const char *name)
+{
+    struct ofork *of;
+
+    for (of = ofork_table[hashfn(name)]; of; of = of->next) {
+      if ((vol == of->of_vol) && (dir == of->of_dir) && 
+         (strcmp(of->of_name, name) == 0))
+       return of;
+    }
+
+    return NULL;
+}
+
+
+void of_dealloc( of )
+    struct ofork       *of;
+{
+    if (!oforks)
+      return;
+
+    of_unhash(of);
+
+    /* detach ofork */
+    of->of_d_prev->of_d_next = of->of_d_next;
+    of->of_d_next->of_d_prev = of->of_d_prev;
+    if (of->of_dir->d_ofork == of) {
+      of->of_dir->d_ofork = (of == of->of_d_next) ? NULL : of->of_d_next;
+    }
+
+    oforks[ of->of_refnum ] = NULL;
+    free( of->of_name );
+    /* free of_ad */
+    if ((of->of_ad->ad_hf.adf_fd == -1) && 
+       (of->of_ad->ad_df.adf_fd == -1)) {
+      free( of->of_ad);
+    } else {/* someone's still using it. just free this user's locks */
+      ad_unlock(of->of_ad, of->of_refnum);
+    }
+
+    free( of );
+}
diff --git a/etc/afpd/passwd.c b/etc/afpd/passwd.c
new file mode 100644 (file)
index 0000000..99a2af6
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#include <itc.h>
+#include <stdio.h>
+#include <string.h>
+#include <r/xdr.h>
+#include <r/r.h>
+#include <afs/comauth.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <afs/cellconfig.h>
+
+#define        DB_PASSWD   0
+
+extern int r_errno;
+
+afs_changepw( ibuf, ibuflen, rbuf, rbuflen )
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    char       cell[ MAXCELLCHARS ], name[ 20 ], oldpw[ 10 ], newpw[ 10 ];
+    char       *p;
+    int                len;
+    u_int16_t  clen;
+
+    len = (unsigned char )*ibuf++;
+    ibuf[ len ] = '\0';
+    if (( p = strchr( ibuf, '@' )) != NULL ) {
+       *p++ = '\0';
+       strcpy( cell, p );
+       ucase( cell );
+    } else {
+       if ( GetLocalCellName() != CCONF_SUCCESS ) {
+           *rbuflen = 0;
+           return( AFPERR_BADUAM );
+       }
+       strcpy( cell, LclCellName );
+    }
+
+    if ( strlen( ibuf ) > 20 ) {
+       *rbuflen = 0;
+       return( AFPERR_PARAM );
+    }
+    strcpy( name, ibuf );
+    ibuf += len;
+
+
+    if (U_InitRPC() != 0) {
+       *rbuflen = 0;
+       return( AFPERR_BADUAM );
+    }
+
+    memcpy( &clen, ibuf, sizeof( clen ));
+    ibuf += sizeof( short );
+    pcbc_encrypt((C_Block *)ibuf, (C_Block *)ibuf,
+           clen, seskeysched, seskey, 0 );
+
+    len = (unsigned char) *ibuf++;
+    if ( len > 9 ) {
+       *rbuflen = 0;
+       return( AFPERR_PARAM );
+    }
+    memcpy( oldpw, ibuf, len );
+    oldpw[ len ] = '\0';
+
+    len = (unsigned char) *ibuf++;
+    if ( len > 9 ) {
+       *rbuflen = 0;
+       return( AFPERR_PARAM );
+    }
+    memcpy( newpw, ibuf, len );
+    newpw[ len ] = '\0';
+
+    rc = U_CellChangePassword( name, newpw, name, oldpw, cell ) != 0 ) {
+
+    if ( rc != 0 ) {
+       *rbuflen = 0;
+       if ( rc < 0 && r_errno = R_ERROR ) {
+           return( AFPERR_NOTAUTH );
+       } else {
+           return( AFPERR_BADUAM );
+       }
+    }
+
+    return( AFP_OK );
+}
diff --git a/etc/afpd/quota.c b/etc/afpd/quota.c
new file mode 100644 (file)
index 0000000..25b7114
--- /dev/null
@@ -0,0 +1,365 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <syslog.h>
+
+#include <atalk/afp.h>
+
+#include "auth.h"
+#include "volume.h"
+#include "unix.h"
+
+#ifndef NO_QUOTA_SUPPORT
+#ifdef NEED_QUOTACTL_WRAPPER
+int quotactl(int cmd, const char *special, int id, caddr_t addr)
+{
+  return syscall(__NR_quotactl, cmd, special, id, addr);
+}
+#endif
+
+
+#if defined(USE_MNTTAB_H) || defined(__svr4__)
+/*
+ * Return the mount point associated with the filesystem
+ * on which "file" resides.  Returns NULL on failure.
+ */
+static char *
+mountp( file, nfs)
+    char       *file;
+    int         *nfs;
+{
+    struct stat                        sb;
+    FILE                       *mtab;
+    dev_t                      devno;
+    static struct mnttab       mnt;
+
+    if ( stat( file, &sb ) < 0 ) {
+       return( NULL );
+    }
+    devno = sb.st_dev;
+
+    if (( mtab = fopen( "/etc/mnttab", "r" )) == NULL ) {
+       return( NULL );
+    }
+
+    while ( getmntent( mtab, &mnt ) == 0 ) {
+      /* local fs */
+      if ( (stat( mnt.mnt_special, &sb ) == 0) && (devno == sb.st_rdev)) {
+       fclose( mtab );
+       return mnt.mnt_mountp;
+      }
+         
+      /* check for nfs. we probably should use 
+       * strcmp(mnt.mnt_fstype, MNTTYPE_NFS), but that's not as fast. */
+      if ((stat(mnt.mnt_mountp, &sb) == 0) && (devno == sb.st_dev) &&
+         strchr(mnt.mnt_special, ':')) {
+       *nfs = 1;
+       fclose( mtab );
+       return mnt.mnt_special;
+      }
+    }
+
+    fclose( mtab );
+    return( NULL );
+}
+
+#else /* __svr4__ */
+#ifdef ultrix
+/*
+ * Return the block-special device name associated with the filesystem
+ * on which "file" resides.  Returns NULL on failure.
+ */
+
+static char *
+special( file, nfs )
+    char *file;
+    int  *nfs;
+{
+    static struct fs_data      fsd;
+
+    if ( getmnt(0, &fsd, 0, STAT_ONE, file ) < 0 ) {
+       syslog(LOG_INFO, "special: getmnt %s: %m", file );
+       return( NULL );
+    }
+
+    /* XXX: does this really detect an nfs mounted fs? */
+    if (strchr(fsd.fd_req.devname, ':'))
+      *nfs = 1;
+    return( fsd.fd_req.devname );
+}
+
+#else /* ultrix */
+#if defined(USE_MOUNT_H) || defined(BSD4_4) || defined(_IBMR2)
+
+static char *
+special( file, nfs )
+    char       *file;
+    int         *nfs;
+{
+    static struct statfs       sfs;
+
+    if ( statfs( file, &sfs ) < 0 ) {
+       return( NULL );
+    }
+
+    /* XXX: make sure this really detects an nfs mounted fs */
+    if (strchr(sfs.f_mntfromname, ':'))
+      *nfs = 1;
+    return( sfs.f_mntfromname );
+}
+
+#else /* BSD4_4 */
+
+static char *
+special( file, nfs )
+    char *file;
+    int *nfs;
+{
+    struct stat                sb;
+    FILE               *mtab;
+    dev_t              devno;
+    struct mntent      *mnt;
+
+    if ( stat( file, &sb ) < 0 ) {
+       return( NULL );
+    }
+    devno = sb.st_dev;
+
+    if (( mtab = setmntent( "/etc/mtab", "r" )) == NULL ) {
+       return( NULL );
+    }
+
+    while (( mnt = getmntent( mtab )) != NULL ) {
+      /* check for local fs */
+      if ( (stat( mnt->mnt_fsname, &sb ) == 0) && devno == sb.st_rdev) {
+       endmntent( mtab );
+       return( mnt->mnt_fsname );
+      }
+      
+      /* check for an nfs mount entry. the alternative is to use
+       * strcmp(mnt->mnt_type, MNTTYPE_NFS) instead of the strchr. */
+      if ((stat(mnt->mnt_dir, &sb) == 0) && (devno == sb.st_dev) &&
+         strchr(mnt->mnt_fsname, ':')) {
+       *nfs = 1;
+       endmntent( mtab );
+       return( mnt->mnt_fsname );
+      }
+    }
+
+    endmntent( mtab );
+    return( NULL );
+}
+
+#endif /* BSD4_4 */
+#endif /* ultrix */
+#endif /* __svr4__ */
+
+
+static int getfsquota(vol, uid, dq)
+    struct vol          *vol;
+    const int           uid;
+    struct dqblk        *dq;
+{
+#ifdef __svr4__
+    struct quotctl     qc;
+
+    qc.op = Q_GETQUOTA;
+    qc.uid = uid;
+    qc.addr = (caddr_t)dq;
+    if ( ioctl( vol->v_qfd, Q_QUOTACTL, &qc ) < 0 ) {
+       return( AFPERR_PARAM );
+    }
+
+#else /* __svr4__ */
+#ifdef ultrix
+    if ( quota( Q_GETDLIM, uid, vol->v_gvs, dq ) != 0 ) {
+       return( AFPERR_PARAM );
+    }
+#else ultrix
+
+#ifndef USRQUOTA
+#define USRQUOTA   0
+#endif
+
+#ifndef QCMD
+#define QCMD(a,b)  (a)
+#endif
+
+    /* for group quotas. we only use these if the user belongs
+     * to one group. */
+    struct dqblk        dqg;
+
+    memset(&dqg, 0, sizeof(dqg));
+
+#ifdef BSD4_4
+    if ( quotactl( vol->v_gvs, QCMD(Q_GETQUOTA,USRQUOTA), 
+                  uid, (char *)dq ) != 0 ) {
+       return( AFPERR_PARAM );
+    }
+
+    if (ngroups == 1) 
+      quotactl(vol->v_gvs, QCMD(Q_GETQUOTA, GRPQUOTA),
+                  groups[0], (char *) &dqg);
+      
+#else /* BSD4_4 */
+    if ( quotactl(QCMD(Q_GETQUOTA, USRQUOTA), vol->v_gvs, uid,
+                 (caddr_t) dq ) != 0 ) {
+       return( AFPERR_PARAM );
+    }
+
+    if (ngroups == 1) 
+      quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), vol->v_gvs, 
+              groups[0], (char *) &dqg);
+#endif  /* BSD4_4 */
+
+    /* set stuff up for group quotas if necessary */
+
+    /* max(user blocks, group blocks) */
+    if (dqg.dqb_curblocks && (dq->dqb_curblocks < dqg.dqb_curblocks))
+      dq->dqb_curblocks = dqg.dqb_curblocks;
+    
+    /* min(user limit, group limit) */
+    if (dqg.dqb_bhardlimit && (!dq->dqb_bhardlimit ||
+        (dq->dqb_bhardlimit > dqg.dqb_bhardlimit)))
+      dq->dqb_bhardlimit = dqg.dqb_bhardlimit;
+
+    /* ditto */
+    if (dqg.dqb_bsoftlimit && (!dq->dqb_bsoftlimit ||
+        (dq->dqb_bsoftlimit > dqg.dqb_bsoftlimit)))
+      dq->dqb_bsoftlimit = dqg.dqb_bsoftlimit;
+
+    /* ditto */
+    if (dqg.dqb_btimelimit && (!dq->dqb_btimelimit ||
+        (dq->dqb_btimelimit > dqg.dqb_btimelimit)))
+      dq->dqb_btimelimit = dqg.dqb_btimelimit;
+
+#endif /* ultrix */
+#endif /* __svr4__ */
+
+    return AFP_OK;
+}
+
+
+static int getquota( vol, dq, bsize)
+    struct vol         *vol;
+    struct dqblk       *dq;
+    const u_int32_t     bsize;
+{
+    char *p;
+
+#ifdef __svr4__
+    char               buf[ MAXPATHLEN + 1];
+
+    if ( vol->v_qfd == -1 && vol->v_gvs == NULL) {
+       if (( p = mountp( vol->v_path, &vol->v_nfs)) == NULL ) {
+           syslog( LOG_INFO, "getquota: mountp %s fails", vol->v_path );
+           return( AFPERR_PARAM );
+       }
+
+       if (vol->v_nfs) {
+         if (( vol->v_gvs = (char *)malloc( strlen( p ) + 1 )) == NULL ) {
+           syslog( LOG_ERR, "getquota: malloc: %m" );
+           return AFPERR_MISC;
+         }
+         strcpy( vol->v_gvs, p );
+
+       } else {
+         sprintf( buf, "%s/quotas", p );
+         if (( vol->v_qfd = open( buf, O_RDONLY, 0 )) < 0 ) {
+           syslog( LOG_INFO, "open %s: %m", buf );
+           return( AFPERR_PARAM );
+         }
+       }
+         
+    }
+#else
+    if ( vol->v_gvs == NULL ) {
+       if (( p = special( vol->v_path, &vol->v_nfs )) == NULL ) {
+           syslog( LOG_INFO, "getquota: special %s fails", vol->v_path );
+           return( AFPERR_PARAM );
+       }
+
+       if (( vol->v_gvs = (char *)malloc( strlen( p ) + 1 )) == NULL ) {
+           syslog( LOG_ERR, "getquota: malloc: %m" );
+           return AFPERR_MISC;
+       }
+       strcpy( vol->v_gvs, p );
+    }
+#endif
+
+    return vol->v_nfs ? getnfsquota(vol, uuid, bsize, dq) : 
+      getfsquota(vol, uuid, dq);
+}
+
+static int overquota( dqblk )
+    struct dqblk       *dqblk;
+{
+    struct timeval     tv;
+
+    if ( dqblk->dqb_curblocks < dqblk->dqb_bsoftlimit ) {
+       return( 0 );
+    }
+#ifdef ultrix
+    if ( dqblk->dqb_bwarn ) {
+       return( 0 );
+    }
+#else /* ultrix */
+    if ( gettimeofday( &tv, 0 ) < 0 ) {
+       syslog( LOG_ERR, "overquota: gettimeofday: %m" );
+       return( AFPERR_PARAM );
+    }
+    if ( !dqblk->dqb_btimelimit || dqblk->dqb_btimelimit > tv.tv_sec ) {
+       return( 0 );
+    }
+#endif /* ultrix */
+    return( 1 );
+}
+
+
+#ifndef dbtob
+#define dbtob(a)       ((a) << 10)
+#endif
+
+/* i do the cast to VolSpace here to make sure that 64-bit shifts 
+   work */
+#ifdef HAVE_2ARG_DBTOB
+#define tobytes(a, b)  dbtob((VolSpace) (a), (VolSpace) (b))
+#else 
+#define tobytes(a, b)  dbtob((VolSpace) (a))
+#endif
+int uquota_getvolspace( vol, bfree, btotal, bsize)
+    const struct vol   *vol;
+    VolSpace   *bfree, *btotal;
+    const u_int32_t bsize;
+{
+    struct dqblk       dqblk;
+
+    if (getquota( vol, &dqblk, bsize) != 0 ) {
+      return( AFPERR_PARAM );
+    }
+
+    /* no limit set for this user. it might be set in the future. */
+    if (dqblk.dqb_bsoftlimit == 0 && dqblk.dqb_bhardlimit == 0) {
+       *btotal = *bfree = ~((VolSpace) 0);
+    } else if ( overquota( &dqblk )) {
+       *btotal = tobytes( dqblk.dqb_bhardlimit, bsize );
+       *bfree = tobytes( dqblk.dqb_bhardlimit, bsize ) - 
+         tobytes( dqblk.dqb_curblocks, bsize );
+    } else {
+       *btotal = tobytes( dqblk.dqb_bsoftlimit, bsize );
+       *bfree = tobytes( dqblk.dqb_bsoftlimit, bsize  ) - 
+         tobytes( dqblk.dqb_curblocks, bsize );
+    }
+
+    return( AFP_OK );
+}
+#endif
diff --git a/etc/afpd/status.c b/etc/afpd/status.c
new file mode 100644 (file)
index 0000000..a770c1a
--- /dev/null
@@ -0,0 +1,368 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+
+#ifdef BSD4_4
+#include <sys/param.h>
+#ifndef USE_GETHOSTID
+#include <sys/sysctl.h>
+#endif
+#endif
+
+#include <netatalk/at.h>
+#include <netatalk/endian.h>
+#include <atalk/dsi.h>
+#include <atalk/atp.h>
+#include <atalk/asp.h>
+#include <atalk/nbp.h>
+
+#include "globals.h"  /* includes <netdb.h> */
+#include "status.h"
+#include "config.h"
+#include "icon.h"
+
+static void status_flags(char *data, const int ipok,
+                        const unsigned char passwdbits)
+{
+    u_int16_t           status;
+
+    status = AFPSRVRINFO_COPY;
+    if (passwdbits & PASSWD_SET) /* some uams may not allow this. */
+      status |= AFPSRVRINFO_PASSWD;
+    if (passwdbits & PASSWD_NOSAVE)
+      status |= AFPSRVRINFO_NOSAVEPASSWD;
+    status |= AFPSRVRINFO_SRVSIGNATURE;
+    /* only advertise tcp/ip if we have a valid address */
+    if (ipok)
+      status |= AFPSRVRINFO_TCPIP;
+    status |= AFPSRVRINFO_SRVMSGS; 
+    status |= AFPSRVRINFO_SRVNOTIFY;
+    status |= AFPSRVRINFO_FASTBOZO;
+    status = htons(status);
+    memcpy(data + AFPSTATUS_FLAGOFF, &status, sizeof(status));
+}
+
+static int status_server(char *data, const char *server)
+{
+    char                *start = data;
+    char                *Obj, *Type, *Zone;
+    u_int16_t           status;
+    int                        len;
+
+    /* make room for all offsets before server name */
+    data += AFPSTATUS_PRELEN;
+
+    /* extract the obj part of the server */
+    Obj = (char *) server;
+    nbp_name(server, &Obj, &Type, &Zone);
+    len = strlen(Obj);
+    *data++ = len;
+    memcpy( data, Obj, len );
+    if ((len + 1) & 1) /* pad server name and length byte to even boundary */
+      len++;
+    data += len;
+
+    /* make room for signature and net address offset. save location of 
+     * signature offset. 
+     *
+     * NOTE: technically, we don't need to reserve space for the
+     * signature and net address offsets if they're not going to be
+     * used. as there are no offsets after them, it doesn't hurt to
+     * have them specified though. so, we just do that to simplify
+     * things.  
+     */
+    len = data - start;
+    status = htons(len + AFPSTATUS_POSTLEN);
+    memcpy(start + AFPSTATUS_MACHOFF, &status, sizeof(status));
+    return len; /* return the offset to beginning of signature offset */
+}
+
+static void status_machine(char *data)
+{
+    char                *start = data;
+    u_int16_t           status;
+    int                        len;
+#ifdef AFS
+    const char         *machine = "afs";
+#else !AFS
+    const char         *machine = "unix";
+#endif
+
+    memcpy(&status, start + AFPSTATUS_MACHOFF, sizeof(status));
+    data += ntohs( status );
+    len = strlen( machine );
+    *data++ = len;
+    memcpy( data, machine, len );
+    data += len;
+    status = htons(data - start);
+    memcpy(start + AFPSTATUS_VERSOFF, &status, sizeof(status));
+}
+
+
+/* server signature is a 16-byte quantity */
+static u_int16_t status_signature(char *data, int *servoffset, DSI *dsi, 
+                                 const char *hostname)
+{
+  char                 *status;
+  int                  i;
+  u_int16_t            offset, sigoff;
+  long                 hostid;
+  static int           id = 0;
+#ifdef BSD4_4
+  int                  mib[2];
+  size_t               len;
+#endif
+
+  status = data;
+
+  /* get server signature offset */
+  memcpy(&offset, data + *servoffset, sizeof(offset));
+  sigoff = offset = ntohs(offset);
+
+  /* jump to server signature offset */
+  data += offset;
+
+  /* 16-byte signature consists of copies of the hostid */
+#if defined(BSD4_4) && defined(USE_GETHOSTID)
+  mib[0] = CTL_KERN;
+  mib[1] = KERN_HOSTID;
+  len = sizeof(hostid);
+  sysctl(mib, 2, &hostid, &len, NULL, 0);
+#else
+  hostid = gethostid();
+#endif
+  if (!hostid) {
+    if (dsi)
+      hostid = dsi->server.sin_addr.s_addr;
+    else {
+      struct hostent *host;
+      
+      if ((host = gethostbyname(hostname)))
+       hostid = ((struct in_addr *) host->h_addr)->s_addr;
+    }
+  }
+
+  /* it turns out that a server signature screws up separate 
+   * servers running on the same machine. to work around that, 
+   * i add in an increment */
+  hostid += id;
+  id++;
+  for (i = 0; i < 16; i += sizeof(hostid)) {
+    memcpy(data, &hostid, sizeof(hostid));
+    data += sizeof(hostid);
+  }
+
+  /* calculate net address offset */
+  *servoffset += sizeof(offset);
+  offset = htons(data - status);
+  memcpy(status + *servoffset, &offset, sizeof(offset));
+  return sigoff;
+}
+
+static int status_netaddress(char *data, const int servoffset, 
+                            const ASP asp, const DSI *dsi, 
+                            const char *fqdn)
+{
+    char               *begin;
+    u_int16_t          offset;
+    
+    begin = data;
+
+    /* get net address offset */
+    memcpy(&offset, data + servoffset, sizeof(offset));
+    data += ntohs(offset);
+
+    /* format: 
+       Address count (byte) 
+       len (byte = sizeof(length + address type + address) 
+       address type (byte, ip address = 0x01, ip + port = 0x02,
+                           ddp address = 0x03, fqdn = 0x04) 
+       address (up to 254 bytes, ip = 4, ip + port = 6, ddp = 4)
+       */
+
+    /* number of addresses. this currently screws up if we have a dsi
+       connection, but we don't have the ip address. to get around this,
+       we turn off the status flag for tcp/ip. */
+    *data++ = (fqdn ? 1 : 0) + (dsi ? 1 : 0) + (asp ? 1 : 0);
+
+    /* handle DNS names */
+    if (fqdn) {
+      int len = strlen(fqdn) + 2;
+      *data++ = len;
+      *data++ = 0x04;
+      memcpy(data, fqdn, len);
+      data += len;
+    }
+
+    /* ip address */
+    if (dsi) {
+      const struct sockaddr_in *inaddr = &dsi->server;
+
+      if (inaddr->sin_port == htons(DSI_AFPOVERTCP_PORT)) {
+       *data++ = 6; /* length */
+       *data++ = 0x01; /* basic ip address */
+       memcpy(data, &inaddr->sin_addr.s_addr,
+              sizeof(inaddr->sin_addr.s_addr));
+       data += sizeof(inaddr->sin_addr.s_addr);
+      } else {
+       /* ip address + port */
+       *data++ = 8; 
+       *data++ = 0x02; /* ip address with port */ 
+       memcpy(data, &inaddr->sin_addr.s_addr,
+              sizeof(inaddr->sin_addr.s_addr));
+       data += sizeof(inaddr->sin_addr.s_addr);
+       memcpy(data, &inaddr->sin_port, sizeof(inaddr->sin_port));
+       data += sizeof(inaddr->sin_port);
+      }
+    }
+
+#ifndef NO_DDP
+    if (asp) {
+      const struct sockaddr_at *ddpaddr = atp_sockaddr(asp->asp_atp);
+      
+      /* ddp address */
+      *data++ = 6;
+      *data++ = 0x03; /* ddp address */
+      memcpy(data, &ddpaddr->sat_addr.s_net, sizeof(ddpaddr->sat_addr.s_net));
+      data += sizeof(ddpaddr->sat_addr.s_net);
+      memcpy(data, &ddpaddr->sat_addr.s_node, 
+            sizeof(ddpaddr->sat_addr.s_node));
+      data += sizeof(ddpaddr->sat_addr.s_node);
+      memcpy(data, &ddpaddr->sat_port, sizeof(ddpaddr->sat_port));
+      data += sizeof(ddpaddr->sat_port);
+    }
+#endif
+
+    /* return length of buffer */
+    return (data - begin);
+}
+
+/* returns actual offset to signature */
+static void status_icon(char *data, const char *icondata, 
+                       const int iconlen, const int sigoffset)
+{
+    char                *start = data;
+    char                *sigdata = data + sigoffset;
+    u_int16_t          ret, status;
+
+    memcpy(&status, start + AFPSTATUS_ICONOFF, sizeof(status));
+    if ( icondata == NULL ) {
+       ret = status;
+       memset(start + AFPSTATUS_ICONOFF, 0, sizeof(status));
+    } else {
+       data += ntohs( status );
+       memcpy( data, icondata, iconlen);
+       data += iconlen;
+       ret = htons(data - start);
+    }
+    
+    /* put in signature offset */
+    if (sigoffset)
+      memcpy(sigdata, &ret, sizeof(ret));
+}
+
+const void status_init(AFPConfig *aspconfig, AFPConfig *dsiconfig,
+                       const struct afp_options *options)
+{
+  ASP asp;
+  DSI *dsi;
+  char *status;
+  int c, sigoff;
+
+  if (!(aspconfig || dsiconfig) || !options)
+    return;
+
+  if (aspconfig) {
+    asp = aspconfig->obj.handle;
+    status = aspconfig->status;
+  } else 
+    asp = NULL;
+
+  if (dsiconfig) {
+    dsi = dsiconfig->obj.handle;
+    if (!aspconfig)
+      status = dsiconfig->status;
+  } else 
+    dsi = NULL;
+
+  /*
+   * These routines must be called in order -- earlier calls
+   * set the offsets for later calls.
+   *
+   * using structs is a bad idea, but that's what the original code
+   * does. solaris, in fact, will segfault with them. so, now
+   * we just use the powers of #defines and memcpy.
+   *
+   * reply block layout (offsets are 16-bit quantities):
+   * machine type offset -> AFP version count offset ->
+   * UAM count offset -> vol icon/mask offset -> flags ->
+   *
+   * server name [padded to even boundary] -> signature offset ->
+   * network address offset ->
+   *
+   * at the appropriate offsets:
+   * machine type, afp versions, uams, server signature
+   * (16-bytes), network addresses, volume icon/mask 
+   */
+
+  status_flags(status, options->fqdn || 
+              (dsiconfig && dsi->server.sin_addr.s_addr),
+              options->passwdbits);
+  /* returns offset to signature offset */
+  c = status_server(status, options->server ? options->server :
+                            options->hostname); 
+  status_machine(status);
+  status_versions(status);
+  status_uams(status, options->uamlist);
+  if (options->flags & OPTION_CUSTOMICON) 
+    status_icon(status, icon, sizeof(icon), c); 
+  else 
+    status_icon(status, apple_atalk_icon, sizeof(apple_atalk_icon), c);
+
+  sigoff = status_signature(status, &c, dsi, options->hostname); 
+  
+  /* returns length */
+  c = status_netaddress(status, c, asp, dsi, options->fqdn); 
+
+
+#ifndef NO_DDP
+  if (aspconfig) {
+    asp_setstatus(asp, status, c);
+    aspconfig->signature = status + sigoff;
+    aspconfig->statuslen = c;
+  }
+#endif
+
+  if (dsiconfig) {
+    if (aspconfig) { /* copy to dsiconfig */
+      memcpy(dsiconfig->status, status, ATP_MAXDATA);
+      status = dsiconfig->status;
+    }
+      
+    if ((options->flags & OPTION_CUSTOMICON) == 0) {
+      status_icon(status, apple_tcp_icon, sizeof(apple_tcp_icon), 0);
+    } 
+    dsi_setstatus(dsi, status, c);
+    dsiconfig->signature = status + sigoff;
+    dsiconfig->statuslen = c;
+  }
+}
+
+/* this is the same as asp/dsi_getstatus */
+int afp_getsrvrinfo(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    AFPConfig *config = obj->config;
+
+    memcpy(rbuf, config->status, config->statuslen);
+    *rbuflen = config->statuslen;
+    return AFP_OK;
+}
diff --git a/etc/afpd/status.h b/etc/afpd/status.h
new file mode 100644 (file)
index 0000000..a59280a
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef AFPD_STATUS_H
+#define AFPD_STATUS_H 1
+
+#include <sys/cdefs.h>
+#include <atalk/dsi.h>
+#include <atalk/asp.h>
+#include "globals.h"
+#include "config.h"
+
+/* we use these to prevent whacky alignment problems */
+#define AFPSTATUS_MACHOFF     0
+#define AFPSTATUS_VERSOFF     2
+#define AFPSTATUS_UAMSOFF     4
+#define AFPSTATUS_ICONOFF     6
+#define AFPSTATUS_FLAGOFF     8
+#define AFPSTATUS_PRELEN     10
+#define AFPSTATUS_POSTLEN     4
+#define AFPSTATUS_LEN        (AFPSTATUS_PRELEN + AFPSTATUS_POSTLEN)
+
+
+#define PASSWD_NONE     0
+#define PASSWD_SET     (1 << 0)
+#define PASSWD_NOSAVE  (1 << 1)
+#define PASSWD_ALL     (PASSWD_SET | PASSWD_NOSAVE)
+
+extern void status_versions __P((char * /*status*/));
+extern void status_uams __P((char * /*status*/, const char * /*authlist*/));
+extern const void status_init __P((AFPConfig *, AFPConfig *,
+                                  const struct afp_options *));
+extern int      afp_getsrvrinfo __P((AFPObj *, char *, int, char *, int *));
+
+#endif
diff --git a/etc/afpd/switch.c b/etc/afpd/switch.c
new file mode 100644 (file)
index 0000000..854510b
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#include <stdio.h>  /* to pick up NULL */
+#include <sys/stat.h> /* works around a bug */
+#include <syslog.h>
+
+#include <atalk/afp.h>
+#include <atalk/uam.h>
+
+#include "globals.h"
+
+/* grab the FP functions */
+#include "auth.h" 
+#include "desktop.h"
+#include "switch.h"
+#include "fork.h"
+#include "file.h"
+#include "directory.h"
+#include "filedir.h"
+#include "status.h"
+#include "misc.h"
+
+static int afp_null(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    syslog( LOG_INFO, "afp_null handle %d", *ibuf );
+    *rbuflen = 0;
+    return( AFPERR_NOOP );
+}
+
+/*
+ * Routines marked "NULL" are not AFP functions.
+ * Routines marked "afp_null" are AFP functions
+ * which are not yet implemented. A fine line...
+ */
+int    (*preauth_switch[])() = {
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /*   0 -   7 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /*   8 -  15 */
+    NULL, NULL, afp_login, afp_logincont,
+    afp_logout, NULL, NULL, NULL,                              /*  16 -  23 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /*  24 -  31 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                            /*  32 -  39 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /*  40 -  47 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /*  48 -  55 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /*  56 -  63 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /*  64 -  71 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /*  72 -  79 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /*  80 -  87 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /*  88 -  95 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /*  96 - 103 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 104 - 111 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 112 - 119 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 120 - 127 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 128 - 135 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 136 - 143 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 144 - 151 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 152 - 159 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 160 - 167 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 168 - 175 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 176 - 183 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 184 - 191 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 192 - 199 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 200 - 207 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 208 - 215 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 216 - 223 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 224 - 231 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 232 - 239 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 240 - 247 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 248 - 255 */
+};
+
+int    (**afp_switch)() = preauth_switch;
+
+int    (*postauth_switch[])() = {
+    NULL, afp_bytelock, afp_closevol, afp_closedir,
+    afp_closefork, afp_copyfile, afp_createdir, afp_createfile,        /*   0 -   7 */
+    afp_delete, afp_enumerate, afp_flush, afp_flushfork,
+    afp_null, afp_null, afp_getforkparams, afp_getsrvrinfo,    /*   8 -  15 */
+    afp_getsrvrparms, afp_getvolparams, afp_login, afp_logincont,
+    afp_logout, afp_mapid, afp_mapname, afp_moveandrename,     /*  16 -  23 */
+    afp_openvol, afp_opendir, afp_openfork, afp_read,
+    afp_rename, afp_setdirparams, afp_setfilparams, afp_setforkparams,
+                                                               /*  24 -  31 */
+    afp_setvolparams, afp_write, afp_getfildirparams, afp_setfildirparams,
+    afp_changepw, afp_getuserinfo, afp_getsrvrmesg, afp_createid, /*  32 -  39 */
+    afp_deleteid, afp_resolveid, afp_exchangefiles, afp_null /*afp_catsearch*/,
+    afp_null, afp_null, afp_null, afp_null,                    /*  40 -  47 */
+    afp_opendt, afp_closedt, afp_null, afp_geticon,
+    afp_geticoninfo, afp_addappl, afp_rmvappl, afp_getappl,    /*  48 -  55 */
+    afp_addcomment, afp_rmvcomment, afp_getcomment, NULL,
+    NULL, NULL, NULL, NULL,                                    /*  56 -  63 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /*  64 -  71 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /*  72 -  79 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /*  80 -  87 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /*  88 -  95 */
+    NULL, NULL, NULL, NULL,
+    afp_getdiracl, afp_setdiracl, afp_afschangepw, NULL,       /*  96 - 103 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 104 - 111 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 112 - 119 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 120 - 127 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 128 - 135 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 136 - 143 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 144 - 151 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 152 - 159 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 160 - 167 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 168 - 175 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 176 - 183 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 184 - 191 */
+    afp_addicon, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 192 - 199 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 200 - 207 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 208 - 215 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 216 - 223 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 224 - 231 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 232 - 239 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 240 - 247 */
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,                                    /* 248 - 255 */
+};
+
+
+/* add a new function if it's specified. return the old function in
+ * "old" if there's a pointer there. */
+int uam_afpserver_action(const int id, const int which,
+                       int (**new)(), int (**old)())
+{
+  switch (which) {
+  case UAM_AFPSERVER_PREAUTH:
+    if (old)
+      *old = preauth_switch[id];
+    if (new)
+      preauth_switch[id] = *new;
+    break;
+  case UAM_AFPSERVER_POSTAUTH:
+    if (old)
+      *old = postauth_switch[id];
+    if (new)
+      postauth_switch[id] = *new;
+    break;
+  default:
+    syslog(LOG_DEBUG, "uam_afpserver_action: unknown switch %d[%d]", 
+          which, id);
+    return -1;
+  }
+
+  return 0;
+}
diff --git a/etc/afpd/switch.h b/etc/afpd/switch.h
new file mode 100644 (file)
index 0000000..38eaf67
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#ifndef AFPD_SWITCH_H
+#define AFPD_SWITCH_H 1
+
+extern int     (**afp_switch)();
+extern int     (*postauth_switch[])();
+#endif
diff --git a/etc/afpd/uam.c b/etc/afpd/uam.c
new file mode 100644 (file)
index 0000000..af39f35
--- /dev/null
@@ -0,0 +1,362 @@
+/* Copyright (c) 1999 Adrian Sun (asun@zoology.washington.edu)
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <syslog.h>
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <netatalk/endian.h>
+#include <atalk/asp.h>
+#include <atalk/dsi.h>
+#include <atalk/afp.h>
+#include <atalk/util.h>
+
+#include "globals.h"
+#include "config.h"
+#include "auth.h"
+#include "uam_auth.h"
+
+/* --- server uam functions -- */
+
+/* uam_load. uams must have a uam_setup function. */
+struct uam_mod *uam_load(const char *path, const char *name)
+{
+  char buf[MAXPATHLEN + 1], *p;
+  struct uam_mod *mod;
+  void *module;
+
+  if ((module = mod_open(path)) == NULL) {
+    syslog(LOG_ERR, "uam_load(%s): failed to load: %s", name, mod_error());
+    return NULL;
+  }
+
+  if ((mod = (struct uam_mod *) malloc(sizeof(struct uam_mod))) == NULL) {
+    syslog(LOG_ERR, "uam_load(%s): malloc failed", name);
+    goto uam_load_fail;
+  }
+
+  strncpy(buf, name, sizeof(buf));
+  if ((p = strchr(buf, '.')))
+    *p = '\0';
+  if ((mod->uam_fcn = mod_symbol(module, buf)) == NULL) {
+    goto uam_load_err;
+  }
+
+  if (mod->uam_fcn->uam_type != UAM_MODULE_SERVER) {
+    syslog(LOG_ERR, "uam_load(%s): attempted to load a non-server module",
+          name);
+    goto uam_load_err;
+  }
+
+  /* version check would go here */
+
+  if (!mod->uam_fcn->uam_setup || 
+      ((*mod->uam_fcn->uam_setup)(name) < 0)) {
+    syslog(LOG_ERR, "uam_load(%s): uam_setup failed", name);
+    goto uam_load_err;
+  }
+
+  mod->uam_module = module;
+  return mod;
+
+uam_load_err:
+  free(mod);
+uam_load_fail:
+  mod_close(module);
+  return NULL;
+}
+
+/* unload the module. we check for a cleanup function, but we don't
+ * die if one doesn't exist. however, things are likely to leak without one.
+ */
+void uam_unload(struct uam_mod *mod)
+{
+  if (mod->uam_fcn->uam_cleanup)
+    (*mod->uam_fcn->uam_cleanup)();
+  mod_close(mod->uam_module);
+  free(mod);
+}
+
+/* -- client-side uam functions -- */
+
+/* set up stuff for this uam. */
+int uam_register(const int type, const char *path, const char *name, ...)
+{
+  va_list ap;
+  struct uam_obj *uam;
+
+  if (!name)
+    return -1;
+
+  /* see if it already exists. */
+  if ((uam = auth_uamfind(type, name, strlen(name)))) {
+    if (strcmp(uam->uam_path, path)) {
+      /* it exists, but it's not the same module. */
+      syslog(LOG_ERR, "uam_register: \"%s\" already loaded by %s",
+            name, path);
+      return -1;
+    }
+    uam->uam_count++;
+    return 0;
+  }
+  
+  /* allocate space for uam */
+  if ((uam = calloc(1, sizeof(struct uam_obj))) == NULL)
+    return -1;
+
+  uam->uam_name = name;
+  uam->uam_path = strdup(path);
+  uam->uam_count++;
+
+  va_start(ap, name);
+  switch (type) {
+  case UAM_SERVER_LOGIN: /* expect three arguments */
+    uam->u.uam_login.login = va_arg(ap, void *);
+    uam->u.uam_login.logincont = va_arg(ap, void *);
+    uam->u.uam_login.logout = va_arg(ap, void *);
+    break;
+  case UAM_SERVER_CHANGEPW: /* one argument */
+    uam->u.uam_changepw = va_arg(ap, void *);
+    break;
+  case UAM_SERVER_PRINTAUTH: /* x arguments */
+  default:
+    break;
+  }
+  va_end(ap);
+
+  /* attach to other uams */
+  if (auth_register(type, uam) < 0) {
+    free(uam->uam_path);
+    free(uam);
+    return -1;
+  }
+
+  return 0;
+}
+
+void uam_unregister(const int type, const char *name)
+{
+  struct uam_obj *uam;
+
+  if (!name)
+    return;
+
+  uam = auth_uamfind(type, name, strlen(name));
+  if (!uam || --uam->uam_count > 0)
+    return;
+
+  auth_unregister(uam);
+  free(uam->uam_path);
+  free(uam);
+}
+
+/* --- helper functions for plugin uams --- */
+
+struct passwd *uam_getname(char *name, const int len)
+{
+  struct passwd *pwent;
+  char *user;
+  int i;
+
+  if ((pwent = getpwnam(name)))
+    return pwent;
+
+#ifndef NO_REAL_USER_NAME
+  for (i = 0; i < len; i++)
+    name[i] = tolower(name[i]);
+
+  setpwent();
+  while ((pwent = getpwent())) {
+    if (user = strchr(pwent->pw_gecos, ','))
+      *user = '\0';
+    user = pwent->pw_gecos;
+
+    /* check against both the gecos and the name fields. the user
+     * might have just used a different capitalization. */
+    if ((strncasecmp(user, name, len) == 0) ||
+       (strncasecmp(pwent->pw_name, name, len) == 0)) {
+      strncpy(name, pwent->pw_name, len);
+      break;
+    }
+  }
+  endpwent();
+#endif
+
+  /* os x server doesn't keep anything useful if we do getpwent */
+  return pwent ? getpwnam(name) : NULL;
+}
+
+int uam_checkuser(const struct passwd *pwd)
+{
+  char *p;
+
+  if (!pwd || !pwd->pw_shell || (*pwd->pw_shell == '\0')) 
+    return -1;
+
+  while ((p = getusershell())) {
+    if ( strcmp( p, pwd->pw_shell ) == 0 ) 
+      break;
+  }
+  endusershell();
+
+  if (!p) {
+    syslog( LOG_INFO, "illegal shell %s for %s", pwd->pw_shell, pwd->pw_name);
+    return -1;
+  }
+
+  return 0;
+}
+
+/* afp-specific functions */
+int uam_afpserver_option(void *private, const int what, void *option,
+                        int *len)
+{
+  AFPObj *obj = private;
+  char **buf = (char **) option; /* most of the options are this */
+  int32_t result;
+  int fd;
+
+  if (!obj || !option)
+    return -1;
+
+  switch (what) {
+  case UAM_OPTION_USERNAME:
+    *buf = (void *) obj->username;
+    if (len) 
+      *len = sizeof(obj->username) - 1;
+    break;
+
+  case UAM_OPTION_GUEST:
+    *buf = (void *) obj->options.guest;
+    if (len) 
+      *len = strlen(obj->options.guest);
+    break;
+
+  case UAM_OPTION_PASSWDOPT:
+    if (!len)
+      return -1;
+
+    switch (*len) {
+    case UAM_PASSWD_FILENAME:
+      *buf = (void *) obj->options.passwdfile;
+      *len = strlen(obj->options.passwdfile);
+      break;
+
+    case UAM_PASSWD_MINLENGTH:
+      *((int *) option) = obj->options.passwdminlen;
+      *len = sizeof(obj->options.passwdminlen);
+      break;
+
+    case UAM_PASSWD_MAXFAIL: 
+      *((int *) option) = obj->options.loginmaxfail;
+      *len = sizeof(obj->options.loginmaxfail);
+      break;
+      
+    case UAM_PASSWD_EXPIRETIME: /* not implemented */
+    default:
+      return -1;
+    break;
+    }
+    break;
+
+  case UAM_OPTION_SIGNATURE:
+    *buf = (void *) (((AFPConfig *)obj->config)->signature);
+    if (len)
+      *len = 16;
+    break;
+
+  case UAM_OPTION_RANDNUM: /* returns a random number in 4-byte units. */
+    if (!len || (*len < 0) || (*len % sizeof(result)))
+      return -1;
+
+    /* construct a random number */
+    if ((fd = open("/dev/urandom", O_RDONLY)) < 0) {
+      struct timeval tv;
+      struct timezone tz;
+      char *randnum = (char *) option;
+      int i;
+    
+      if (gettimeofday(&tv, &tz) < 0)
+       return -1;
+      srandom(tv.tv_sec + (unsigned long) obj + (unsigned long) obj->handle);
+      for (i = 0; i < *len; i += sizeof(result)) {
+       result = random();
+       memcpy(randnum + i, &result, sizeof(result));
+      }
+    } else {
+      result = read(fd, option, *len);
+      close(fd);
+      if (result < 0)
+       return -1;
+    }
+    break;
+
+  case UAM_OPTION_HOSTNAME:
+    *buf = (void *) obj->options.hostname;
+    if (len) 
+      *len = strlen(obj->options.hostname);
+    break;
+
+  case UAM_OPTION_COOKIE: 
+    /* it's up to the uam to actually store something useful here.
+     * this just passes back a handle to the cookie. the uam side
+     * needs to do something like **buf = (void *) cookie to store
+     * the cookie. */
+    *buf = (void *) &obj->uam_cookie;
+    break;
+
+  default:
+    return -1;
+    break;
+  }
+
+  return 0;
+}
+
+/* if we need to maintain a connection, this is how we do it. 
+ * because an action pointer gets passed in, we can stream 
+ * DSI connections */
+int uam_afp_read(void *handle, char *buf, int *buflen, 
+                int (*action)(void *, void *, const int))
+{
+  AFPObj *obj = handle;
+  int len;
+
+  if (!obj)
+    return AFPERR_PARAM;
+
+  switch (obj->proto) {
+    case AFPPROTO_ASP:
+      if ((len = asp_wrtcont(obj->handle, buf, buflen )) < 0) 
+       goto uam_afp_read_err;
+      return action(handle, buf, *buflen);
+      break;
+
+    case AFPPROTO_DSI:
+      len = dsi_writeinit(obj->handle, buf, *buflen);
+      if (!len || ((len = action(handle, buf, len)) < 0)) {
+       dsi_writeflush(obj->handle);
+       goto uam_afp_read_err;
+      }
+       
+      while (len = (dsi_write(obj->handle, buf, *buflen))) {
+       if ((len = action(handle, buf, len)) < 0) {
+         dsi_writeflush(obj->handle);
+         goto uam_afp_read_err;
+       }
+      }
+      break;
+  }
+  return 0;
+
+uam_afp_read_err:
+  *buflen = 0;
+  return len;
+}
diff --git a/etc/afpd/uam_auth.h b/etc/afpd/uam_auth.h
new file mode 100644 (file)
index 0000000..eedc600
--- /dev/null
@@ -0,0 +1,63 @@
+/* Copyright (c) 1999 Adrian Sun (asun@zoology.washington.edu)
+ * All Rights Reserved.  See COPYRIGHT.
+ *
+ * interface between uam.c and auth.c
+ */
+
+#ifndef AFPD_UAM_AUTH_H
+#define AFPD_UAM_AUTH_H 1
+
+#include <sys/cdefs.h>
+#include <pwd.h>
+
+#include <atalk/uam.h>
+#include "globals.h"
+
+struct uam_mod {
+  void *uam_module;
+  struct uam_export *uam_fcn;
+  struct uam_mod *uam_prev, *uam_next;
+};
+
+struct uam_obj {
+  const char *uam_name; /* authentication method */
+  char *uam_path; /* where it's located */
+  int uam_count;
+  union {
+    struct {
+      int (*login) __P((void *, struct passwd **, 
+                       char *, int, char *, int *));
+      int (*logincont) __P((void *, struct passwd **, char *,
+                           int, char *, int *));
+      void (*logout) __P((void));
+    } uam_login;
+    int (*uam_changepw) __P((void *, char *, struct passwd *, char *,
+                            int, char *, int *));
+  } u;
+  struct uam_obj *uam_prev, *uam_next;
+};
+
+#define uam_attach(a, b) do { \
+    (a)->uam_prev->uam_next = (b); \
+    (b)->uam_prev = (a)->uam_prev; \
+    (b)->uam_next = (a); \
+    (a)->uam_prev = (b); \
+} while (0)                                 
+
+#define uam_detach(a) do { \
+    (a)->uam_prev->uam_next = (a)->uam_next; \
+    (a)->uam_next->uam_prev = (a)->uam_prev; \
+} while (0)
+
+
+extern struct uam_mod *uam_load __P((const char *, const char *));
+extern void uam_unload __P((struct uam_mod *));
+
+/* auth.c */
+int auth_load __P((const char *, const char *));
+int auth_register __P((const int, struct uam_obj *));
+#define auth_unregister(a) uam_detach(a)
+struct uam_obj *auth_uamfind __P((const int, const char *, const int));
+void auth_unload __P((void));
+
+#endif /* uam_auth.h */
diff --git a/etc/afpd/unix.c b/etc/afpd/unix.c
new file mode 100644 (file)
index 0000000..4dfde80
--- /dev/null
@@ -0,0 +1,450 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/syslog.h>
+#include <netatalk/endian.h>
+#include <dirent.h>
+#include <limits.h>
+#include <atalk/afp.h>
+#include <string.h>
+#include <fcntl.h>
+#include "auth.h"
+#include "directory.h"
+#include "volume.h"
+#include "unix.h"
+
+/*
+ * Get the free space on a partition.
+ */
+int ustatfs_getvolspace( vol, bfree, btotal, bsize )
+    const struct vol   *vol;
+    VolSpace    *bfree, *btotal;
+    u_int32_t   *bsize;
+{
+#ifdef ultrix
+    struct fs_data     sfs;
+#else /*ultrix*/
+    struct statfs      sfs;
+#endif /*ultrix*/
+
+    if ( statfs( vol->v_path, &sfs ) < 0 ) {
+       return( AFPERR_PARAM );
+    }
+
+#ifdef ultrix
+    *bfree = (VolSpace) sfs.fd_req.bfreen * 1024;
+    *bsize = 1024;
+#else
+    *bfree = (VolSpace) sfs.f_bavail * sfs.f_frsize;
+    *bsize = sfs.f_frsize;
+#endif ultrix
+
+#ifdef ultrix
+    *btotal = (VolSpace) 
+      ( sfs.fd_req.btot - ( sfs.fd_req.bfree - sfs.fd_req.bfreen )) * 1024;
+#else ultrix
+    *btotal = (VolSpace) 
+      ( sfs.f_blocks - ( sfs.f_bfree - sfs.f_bavail )) * sfs.f_frsize;
+#endif ultrix
+    return( AFP_OK );
+}
+
+static __inline__ int utombits( bits )
+    mode_t     bits;
+{
+    int                mbits;
+
+    mbits = 0;
+
+    mbits |= ( bits & ( S_IREAD >> 6 )) ? AR_UREAD : 0;
+    mbits |= ( bits & ( S_IWRITE >> 6 )) ? AR_UWRITE : 0;
+    mbits |= ( bits & ( S_IEXEC >> 6) ) ? AR_USEARCH : 0;
+
+    return( mbits );
+}
+
+void utommode( stat, ma )
+    struct stat                *stat;
+    struct maccess     *ma;
+{
+    mode_t             mode;
+
+    mode = stat->st_mode;
+
+    ma->ma_world = utombits( mode );
+    mode = mode >> 3;
+
+    ma->ma_group = utombits( mode );
+    mode = mode >> 3;
+
+    ma->ma_owner = utombits( mode );
+
+    if ( uuid == stat->st_uid ) {
+       ma->ma_user = ma->ma_owner | AR_UOWN;
+    } else if ( gmem( stat->st_gid )) {
+       ma->ma_user = ma->ma_group;
+    } else {
+       ma->ma_user = ma->ma_world;
+    }
+
+    /*
+     * There are certain things the mac won't try if you don't have
+     * the "owner" bit set, even tho you can do these things on unix wiht
+     * only write permission.  What were the things?
+     */
+    if ( ma->ma_user & AR_UWRITE ) {
+       ma->ma_user |= AR_UOWN;
+    }
+}
+
+
+int gmem( gid )
+    const gid_t        gid;
+{
+    int                i;
+
+    for ( i = 0; i < ngroups; i++ ) {
+       if ( groups[ i ] == gid ) {
+           return( 1 );
+       }
+    }
+    return( 0 );
+}
+
+static __inline__ mode_t mtoubits( bits )
+    u_char     bits;
+{
+    mode_t     mode;
+
+    mode = 0;
+
+    mode |= ( bits & AR_UREAD ) ? ( S_IREAD >> 6 ) : 0;
+    mode |= ( bits & AR_UWRITE ) ? ( S_IWRITE >> 6 ) : 0;
+    mode |= ( bits & AR_USEARCH ) ? ( S_IEXEC >> 6 ) : 0;
+
+    return( mode );
+}
+
+mode_t mtoumode( ma )
+    struct maccess     *ma;
+{
+    mode_t             mode;
+
+    mode = 0;
+    mode |= mtoubits( ma->ma_owner );
+    mode = mode << 3;
+
+    mode |= mtoubits( ma->ma_group );
+    mode = mode << 3;
+
+    mode |= mtoubits( ma->ma_world );
+
+    return( mode );
+}
+
+
+int setdeskmode( mode )
+    const mode_t       mode;
+{
+    char               wd[ MAXPATHLEN + 1];
+    struct stat         st;
+    char               modbuf[ 12 + 1], *m;
+    struct dirent      *deskp, *subp;
+    DIR                        *desk, *sub;
+
+    if ( getcwd( wd , MAXPATHLEN) == NULL ) {
+       return( -1 );
+    }
+    if ( chdir( ".AppleDesktop" ) < 0 ) {
+       return( -1 );
+    }
+    if (( desk = opendir( "." )) == NULL ) {
+       if ( chdir( wd ) < 0 ) {
+           syslog( LOG_ERR, "setdeskmode: chdir %s: %m", wd );
+       }
+       return( -1 );
+    }
+    for ( deskp = readdir( desk ); deskp != NULL; deskp = readdir( desk )) {
+       if ( strcmp( deskp->d_name, "." ) == 0 ||
+               strcmp( deskp->d_name, ".." ) == 0 || strlen( deskp->d_name ) > 2 ) {
+           continue;
+       }
+       strcpy( modbuf, deskp->d_name );
+       strcat( modbuf, "/" );
+       m = strchr( modbuf, '\0' );
+       if (( sub = opendir( deskp->d_name )) == NULL ) {
+           continue;
+       }
+       for ( subp = readdir( sub ); subp != NULL; subp = readdir( sub )) {
+           if ( strcmp( subp->d_name, "." ) == 0 ||
+                   strcmp( subp->d_name, ".." ) == 0 ) {
+               continue;
+           }
+           *m = '\0';
+           strcat( modbuf, subp->d_name );
+           /* XXX: need to preserve special modes */
+           if (stat(modbuf, &st) < 0) {
+               syslog( LOG_DEBUG, "setdeskmode: stat %s: %m", modbuf );
+               continue;
+           }       
+
+           if (S_ISDIR(st.st_mode)) {
+             if ( chmod( modbuf,  DIRBITS | mode ) < 0 ) {
+               syslog( LOG_DEBUG, "setdeskmode: chmod %s: %m", modbuf );
+             }
+           } else if ( chmod( modbuf,  mode ) < 0 ) {
+               syslog( LOG_DEBUG, "setdeskmode: chmod %s: %m", modbuf );
+           }
+
+       }
+       closedir( sub );
+       /* XXX: need to preserve special modes */
+       if ( chmod( deskp->d_name,  DIRBITS | mode ) < 0 ) {
+           syslog( LOG_DEBUG, "setdeskmode: chmod %s: %m", deskp->d_name );
+       }
+    }
+    closedir( desk );
+    if ( chdir( wd ) < 0 ) {
+       syslog( LOG_ERR, "setdeskmode: chdir %s: %m", wd );
+       return -1;
+    }
+    /* XXX: need to preserve special modes */
+    if ( chmod( ".AppleDesktop",  DIRBITS | mode ) < 0 ) {
+       syslog( LOG_DEBUG, "setdeskmode: chmod .AppleDesktop: %m" );
+    }
+    return( 0 );
+}
+
+int setdirmode( mode, noadouble )
+    const mode_t mode;
+    const int noadouble;
+{
+    char               buf[ MAXPATHLEN + 1];
+    struct stat                st;
+    char               *m;
+    struct dirent      *dirp;
+    DIR                        *dir;
+
+    if (( dir = opendir( "." )) == NULL ) {
+       syslog( LOG_ERR, "setdirmode: opendir .: %m" );
+       return( -1 );
+    }
+
+    for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) {
+       if ( *dirp->d_name == '.' ) {
+           continue;
+       }
+       if ( stat( dirp->d_name, &st ) < 0 ) {
+           syslog( LOG_DEBUG, "setdirmode: stat %s: %m", dirp->d_name );
+           continue;
+       }
+
+       if (S_ISREG(st.st_mode)) {
+           /* XXX: need to preserve special modes */
+           if (S_ISDIR(st.st_mode)) {
+             if ( chmod( dirp->d_name, DIRBITS | mode ) < 0 ) {
+               syslog( LOG_DEBUG, "setdirmode: chmod %s: %m", dirp->d_name );
+             }
+           } else if ( chmod( dirp->d_name, mode ) < 0 ) {
+               syslog( LOG_DEBUG, "setdirmode: chmod %s: %m", dirp->d_name );
+           }
+       }
+    }
+    closedir( dir );
+    if (( dir = opendir( ".AppleDouble" )) == NULL ) {
+        if (noadouble)
+         goto setdirmode_noadouble;
+       syslog( LOG_ERR, "setdirmode: opendir .AppleDouble: %m" );
+       return( -1 );
+    }
+    strcpy( buf, ".AppleDouble" );
+    strcat( buf, "/" );
+    m = strchr( buf, '\0' );
+    for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) {
+       if ( strcmp( dirp->d_name, "." ) == 0 ||
+               strcmp( dirp->d_name, ".." ) == 0 ) {
+           continue;
+       }
+       *m = '\0';
+       strcat( buf, dirp->d_name );
+
+       if ( stat( buf, &st ) < 0 ) {
+           syslog( LOG_DEBUG, "setdirmode: stat %s: %m", buf );
+           continue;
+       }
+
+       if (S_ISDIR(st.st_mode)) {
+         if ( chmod(buf,  DIRBITS | mode) < 0 ) {
+           syslog( LOG_DEBUG, "setdirmode: chmod %s: %m", buf );
+         }
+       } else if ( chmod(buf, mode) < 0 ) {
+           syslog( LOG_DEBUG, "setdirmode: chmod %s: %m", buf );
+       }
+    }
+    closedir( dir );
+
+    /* XXX: use special bits to tag directory permissions */
+      
+    /* XXX: need to preserve special modes */
+    if ( chmod( ".AppleDouble",  DIRBITS | mode ) < 0 ) {
+       syslog( LOG_ERR, "setdirmode: chmod .AppleDouble: %m" );
+       return( -1 );
+    }
+
+setdirmode_noadouble:
+    /* XXX: need to preserve special modes */
+    if ( chmod( ".",  DIRBITS | mode ) < 0 ) {
+       syslog( LOG_ERR, "setdirmode: chmod .: %m" );
+       return( -1 );
+    }
+    return( 0 );
+}
+
+int setdeskowner( uid, gid )
+    const uid_t        uid;
+    const gid_t        gid;
+{
+    char               wd[ MAXPATHLEN + 1];
+    char               modbuf[12 + 1], *m;
+    struct dirent      *deskp, *subp;
+    DIR                        *desk, *sub;
+
+    if ( getcwd( wd, MAXPATHLEN ) == NULL ) {
+       return( -1 );
+    }
+    if ( chdir( ".AppleDesktop" ) < 0 ) {
+       return( -1 );
+    }
+    if (( desk = opendir( "." )) == NULL ) {
+       if ( chdir( wd ) < 0 ) {
+           syslog( LOG_ERR, "setdeskowner: chdir %s: %m", wd );
+       }
+       return( -1 );
+    }
+    for ( deskp = readdir( desk ); deskp != NULL; deskp = readdir( desk )) {
+       if ( strcmp( deskp->d_name, "." ) == 0 ||
+            strcmp( deskp->d_name, ".." ) == 0 || 
+            strlen( deskp->d_name ) > 2 ) {
+           continue;
+       }
+       strcpy( modbuf, deskp->d_name );
+       strcat( modbuf, "/" );
+       m = strchr( modbuf, '\0' );
+       if (( sub = opendir( deskp->d_name )) == NULL ) {
+           continue;
+       }
+       for ( subp = readdir( sub ); subp != NULL; subp = readdir( sub )) {
+           if ( strcmp( subp->d_name, "." ) == 0 ||
+                strcmp( subp->d_name, ".." ) == 0 ) {
+               continue;
+           }
+           *m = '\0';
+           strcat( modbuf, subp->d_name );
+           /* XXX: add special any uid, ignore group bits */
+           if ( chown( modbuf, uid, gid ) < 0 ) {
+               syslog( LOG_DEBUG, "setdeskown: chown %s: %m", modbuf );
+           }
+       }
+       closedir( sub );
+       /* XXX: add special any uid, ignore group bits */
+       if ( chown( deskp->d_name, uid, gid ) < 0 ) {
+           syslog( LOG_DEBUG, "setdeskowner: chown %s: %m", deskp->d_name );
+       }
+    }
+    closedir( desk );
+    if ( chdir( wd ) < 0 ) {
+       syslog( LOG_ERR, "setdeskowner: chdir %s: %m", wd );
+       return -1;
+    }
+    if ( chown( ".AppleDesktop", uid, gid ) < 0 ) {
+       syslog( LOG_ERR, "setdeskowner: chown .AppleDesktop: %m" );
+    }
+    return( 0 );
+}
+
+
+/* uid/gid == 0 need to be handled as special cases. they really mean
+ * that user/group should inherit from other, but that doesn't fit
+ * into the unix permission scheme. we can get around this by
+ * co-opting some bits. */
+int setdirowner( uid, gid, noadouble )
+    const uid_t        uid;
+    const gid_t        gid;
+    const int   noadouble;
+{
+    char               buf[ MAXPATHLEN + 1];
+    struct stat                st;
+    char               *m;
+    struct dirent      *dirp;
+    DIR                        *dir;
+
+    if (( dir = opendir( "." )) == NULL ) {
+       return( -1 );
+    }
+    for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) {
+       if ( *dirp->d_name == '.' ) {
+           continue;
+       };
+       if ( stat( dirp->d_name, &st ) < 0 ) {
+           syslog( LOG_DEBUG, "setdirowner: stat %s: %m", dirp->d_name );
+           continue;
+       }
+       if (( st.st_mode & S_IFMT ) == S_IFREG ) {
+           if ( chown( dirp->d_name, uid, gid ) < 0 ) {
+               syslog( LOG_DEBUG, "setdirowner: chown %s: %m", dirp->d_name );
+           }
+       }
+    }
+    closedir( dir );
+    if (( dir = opendir( ".AppleDouble" )) == NULL ) {
+      if (noadouble)
+       goto setdirowner_noadouble;
+      return( -1 );
+    }
+    strcpy( buf, ".AppleDouble" );
+    strcat( buf, "/" );
+    m = strchr( buf, '\0' );
+    for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) {
+       if ( strcmp( dirp->d_name, "." ) == 0 ||
+               strcmp( dirp->d_name, ".." ) == 0 ) {
+           continue;
+       }
+       *m = '\0';
+       strcat( buf, dirp->d_name );
+       if ( chown( buf, uid, gid ) < 0 ) {
+           syslog( LOG_DEBUG, "setdirowner: chown %d/%d %s: %m",
+                   uid, gid, buf );
+       }
+    }
+    closedir( dir );
+
+    /*
+     * We cheat: we know that chown doesn't do anything.
+     */
+    if ( stat( ".AppleDouble", &st ) < 0 ) {
+       syslog( LOG_ERR, "setdirowner: stat .AppleDouble: %m" );
+       return( -1 );
+    }
+    if ( gid && gid != st.st_gid && chown( ".AppleDouble", uid, gid ) < 0 ) {
+       syslog( LOG_DEBUG, "setdirowner: chown %d/%d .AppleDouble: %m",
+               uid, gid);
+    }
+
+setdirowner_noadouble:
+    if ( stat( ".", &st ) < 0 ) {
+       return( -1 );
+    }
+    if ( gid && gid != st.st_gid && chown( ".", uid, gid ) < 0 ) {
+        syslog( LOG_DEBUG, "setdirowner: chown %d/%d .: %m",
+               uid, gid);
+    }
+
+    return( 0 );
+}
diff --git a/etc/afpd/unix.h b/etc/afpd/unix.h
new file mode 100644 (file)
index 0000000..79ce68f
--- /dev/null
@@ -0,0 +1,100 @@
+#ifndef AFPD_UNIX_H
+#define AFPD_UNIX_H
+
+#include <sys/cdefs.h>
+#include <netatalk/endian.h>
+#include "volume.h"
+
+#if defined( sun ) && !defined( __svr4__ )
+#ifdef i386
+typedef int    mode_t;
+#endif /*i386*/
+#endif /*sun __svr4__*/
+
+
+/* some GLIBC/old-libc-isms */
+#if defined(__GNU_LIBRARY__) 
+#if __GNU_LIBRARY__ < 6
+#define USE_VFS_H
+#else
+#define USE_STATFS_H
+#endif
+#endif
+
+#if defined(USE_VFS_H) || defined( sun ) || defined( ibm032 ) 
+#include <sys/vfs.h>
+#endif
+
+#if defined(_IBMR2) || defined(USE_STATFS_H) 
+#include <sys/statfs.h>
+/* this might not be right. */
+#define f_mntfromname f_fname
+#endif
+
+#if defined(USE_STATVFS_H) || defined(__svr4__)
+#include <sys/statvfs.h>
+#define statfs statvfs
+#else
+#define        f_frsize f_bsize
+#endif
+
+#if defined(__svr4__) || defined(USE_MNTTAB_H)
+#include <sys/mnttab.h>
+#endif
+
+#if defined(USE_MOUNT_H) || defined(BSD4_4) || \
+    defined(linux) || defined(ultrix)
+#include <sys/mount.h>
+#endif
+
+#if defined(linux) || defined(USE_MNTENT_H)
+#include <mntent.h>
+#endif
+
+
+#ifndef NO_QUOTA_SUPPORT
+
+#if !(defined(__svr4__) || defined(HAVE_DQB_BTIMELIMIT))
+#define dqb_btimelimit  dqb_btime
+#endif
+
+#if defined(linux) || defined(ultrix) || defined(USE_QUOTA_H)
+#ifndef NEED_QUOTACTL_WRAPPER
+#include <sys/quota.h>
+#else
+#include <asm/types.h>
+#include <asm/unistd.h>
+#include <linux/quota.h>
+#endif
+#endif
+
+#ifdef __svr4__ 
+#include <sys/fs/ufs_quota.h>
+#endif
+
+#ifdef BSD4_4
+#include <ufs/ufs/quota.h>
+#endif
+
+#ifdef USE_UFS_QUOTA_H
+#include <ufs/quota.h>
+#endif
+
+#ifdef _IBMR2
+#include <jfs/quota.h>
+#endif
+
+extern int getnfsquota __P((const struct vol *, const int, const u_int32_t,
+                           struct dqblk *));
+
+extern int uquota_getvolspace __P((const struct vol *, VolSpace *, VolSpace *,
+                                  const u_int32_t));
+#endif /* NO_QUOTA_SUPPORT */
+
+extern int gmem         __P((const gid_t));
+extern int setdeskmode  __P((const mode_t));
+extern int setdirmode   __P((const mode_t, const int));
+extern int setdeskowner __P((const uid_t, const gid_t));
+extern int setdirowner  __P((const uid_t, const gid_t, const int));
+
+#endif /* UNIX_H */
diff --git a/etc/afpd/volume.c b/etc/afpd/volume.c
new file mode 100644 (file)
index 0000000..8b73be5
--- /dev/null
@@ -0,0 +1,1293 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#include <sys/time.h>
+#include <sys/syslog.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <netatalk/endian.h>
+#include <atalk/asp.h>
+#include <atalk/dsi.h>
+#include <atalk/adouble.h>
+#include <atalk/afp.h>
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <pwd.h>
+#include <grp.h>
+#include <utime.h>
+#include <errno.h>
+
+#include "directory.h"
+#include "file.h"
+#include "volume.h"
+#include "globals.h"
+#include "unix.h"
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef NO_LARGE_VOL_SUPPORT
+#if BYTE_ORDER == BIG_ENDIAN
+#define hton64(x)       (x)
+#define ntoh64(x)       (x)
+#else
+#define hton64(x)       ((u_int64_t) (htonl(((x) >> 32) & 0xffffffffLL)) | \
+                         (u_int64_t) ((htonl(x) & 0xffffffffLL) << 32))
+#define ntoh64(x)       (hton64(x))
+#endif
+#endif
+
+static struct vol *volumes = NULL;
+static int             lastvid = 0;
+#if AD_VERSION == AD_VERSION1
+static char            *Trash = "\02\024Network Trash Folder";
+#endif
+static struct extmap   *extmap = NULL, *defextmap = NULL;
+
+#define VOLOPT_ALLOW      0  /* user allow list */
+#define VOLOPT_DENY       1  /* user deny list */
+#define VOLOPT_RWLIST     2  /* user rw list */
+#define VOLOPT_ROLIST     3  /* user ro list */
+#define VOLOPT_CODEPAGE   4  /* codepage */
+#define VOLOPT_PASSWORD   5  /* volume password */
+#define VOLOPT_CASEFOLD   6  /* character case mangling */
+#define VOLOPT_FLAGS      7  /* various flags */
+#define VOLOPT_DBPATH     8  /* path to database */
+#define VOLOPT_MAPCHARS   9  /* does mtou and utom mappings. syntax:
+                               m and u can be double-byte hex
+                               strings if necessary.
+                               m=u -> map both ways
+                               m>u -> map m to u
+                               m<u -> map u to m
+                               !u  -> make u illegal always
+                               ~u  -> make u illegal only as the first
+                                      part of a double-byte character.
+                            */
+#define VOLOPT_MAX        9
+#define VOLOPT_NUM        (VOLOPT_MAX + 1)
+
+#define VOLPASSLEN  8
+#define VOLOPT_DEFAULT     ":DEFAULT:"
+#define VOLOPT_DEFAULT_LEN 9
+struct vol_option {
+  char *c_value;
+  int i_value; 
+};
+
+static __inline__ void volfree(struct vol_option *options,
+                              const struct vol_option *save)
+{
+  int i;
+
+  if (save) {
+    for (i = 0; i < VOLOPT_MAX; i++) {
+      if (options[i].c_value && (options[i].c_value != save[i].c_value))
+       free(options[i].c_value);
+    }
+  } else {
+    for (i = 0; i < VOLOPT_MAX; i++) {
+      if (options[i].c_value)
+       free(options[i].c_value);
+    }
+  }
+}
+
+
+/* handle variable substitutions. here's what we understand:
+ * $c   -> client ip/appletalk address
+ * $f   -> full name (whatever's in the gecos field)
+ * $g   -> group
+ * $h   -> hostname 
+ * $s   -> server name (hostname if it doesn't exist)
+ * $u   -> username (guest is usually nobody)
+ * $v   -> volume name (ADEID_NAME or basename)
+ * $z   -> zone (may not exist)
+ * $$   -> $
+ */
+#define is_var(a, b) (strncmp((a), (b), 2) == 0)
+static void volxlate(AFPObj *obj, char *dest, int destlen, 
+                    char *src, struct passwd *pwd, char *path)
+{
+  char *p, *q;
+  int len;
+
+  strncpy(dest, src, destlen);
+  if ((p = strchr(src, '$')) == NULL) /* nothing to do */
+    return; 
+
+  /* first part of the path. just forward to the next variable. */
+  len = MIN(p - src, destlen);
+  if (len > 0) {
+    destlen -= len;
+    dest += len;
+  }
+
+  while (p && destlen > 0) {
+    /* now figure out what the variable is */
+    q = NULL;
+    if (is_var(p, "$c")) {
+      if (obj->proto == AFPPROTO_ASP) {
+       ASP asp = obj->handle;
+
+       len = sprintf(dest, "%u.%u", ntohs(asp->asp_sat.sat_addr.s_net),
+                     asp->asp_sat.sat_addr.s_node);
+       dest += len;
+       destlen -= len;
+
+      } else if (obj->proto == AFPPROTO_DSI) {
+       DSI *dsi = obj->handle;
+
+       len = sprintf(dest, "%s:%u", inet_ntoa(dsi->client.sin_addr),
+                     ntohs(dsi->client.sin_port));
+       dest += len;
+       destlen -= len;
+      }
+    } else if (is_var(p, "$f")) {
+      if (q = strchr(pwd->pw_gecos, ','))
+       *q = '\0';
+      q = pwd->pw_gecos;
+    } else if (is_var(p, "$g")) {
+      struct group *grp = getgrgid(pwd->pw_gid);
+      if (grp)
+       q = grp->gr_name;
+    } else if (is_var(p, "$h")) {
+      q = obj->options.hostname;
+    } else if (is_var(p, "$s")) {
+      if (obj->Obj)
+       q = obj->Obj;
+      else if (obj->options.server) {
+       q = obj->options.server;
+      } else
+       q = obj->options.hostname;
+    } else if (is_var(p, "$u")) {
+      q = obj->username;
+    } else if (is_var(p, "$v")) {
+      if (path) {
+       struct adouble ad;
+
+       memset(&ad, 0, sizeof(ad));
+       if (ad_open(path, ADFLAGS_HF, O_RDONLY, 0, &ad) < 0)
+         goto no_volname;
+
+       if ((len = MIN(ad_getentrylen(&ad, ADEID_NAME), destlen)) > 0) {
+         memcpy(dest, ad_entry(&ad, ADEID_NAME), len);
+         ad_close(&ad, ADFLAGS_HF);
+         dest += len;
+         destlen -= len;
+       } else {
+         ad_close(&ad, ADFLAGS_HF);
+no_volname: /* simple basename */
+         if ((q = strrchr(path, '/')) == NULL)
+           q = path;
+         else if (*(q + 1) != '\0')
+           q++;
+       }
+      }
+    } else if (is_var(p, "$z")) {
+      q = obj->Zone;
+    } else if (is_var(p, "$$")) {
+      q = "$";
+    } else
+      q = p;
+    
+    /* copy the stuff over. if we don't understand something that we
+     * should, just skip it over. */
+    if (q) {
+      len = MIN(p == q ? 2 : strlen(q), destlen);
+      strncpy(dest, q, len);
+      dest += len;
+      destlen -= len;
+    }
+
+    /* stuff up to next $ */
+    src = p + 2;
+    p = strchr(src, '$');
+    len = p ? MIN(p - src, destlen) : destlen;
+    if (len > 0) {
+      strncpy(dest, src, len);
+      dest += len;
+      destlen -= len;
+    }
+  }
+}
+
+/* to make sure that val is valid, make sure to select an opt that
+   includes val */
+#define optionok(buf,opt,val) (strstr((buf),(opt)) && ((val)[1] != '\0'))
+
+static __inline__ char *get_codepage_path(const char *path, const char *name)
+{
+  char *page;
+  int len;
+
+  if (path) {
+    page = (char *) malloc((len = strlen(path)) + strlen(name) + 2);
+    if (page) {
+      strcpy(page, path);
+      if (path[len - 1] != '/') /* add a / */
+       strcat(page, "/");
+      strcat(page, name);
+    }
+  } else {
+    page = strdup(name);
+  }
+
+  return page;
+}
+
+/* handle all the options. tmp can't be NULL. */
+static void volset(struct vol_option *options, char *volname, int vlen, 
+                  const char *nlspath, const char *tmp)
+{
+  char *val;
+
+  val = strchr(tmp, ':');
+  if (optionok(tmp, "allow:", val)) {
+    if (options[VOLOPT_ALLOW].c_value)
+      free(options[VOLOPT_ALLOW].c_value);
+    options[VOLOPT_ALLOW].c_value = strdup(val + 1);
+
+  } else if (optionok(tmp, "deny:", val)) {
+    if (options[VOLOPT_DENY].c_value)
+      free(options[VOLOPT_DENY].c_value);
+    options[VOLOPT_DENY].c_value = strdup(val + 1);
+
+  } else if (optionok(tmp, "rwlist:", val)) {
+    if (options[VOLOPT_RWLIST].c_value)
+      free(options[VOLOPT_RWLIST].c_value);
+    options[VOLOPT_RWLIST].c_value = strdup(val + 1);
+
+  } else if (optionok(tmp, "rolist:", val)) {
+    if (options[VOLOPT_ROLIST].c_value)
+      free(options[VOLOPT_ROLIST].c_value);
+    options[VOLOPT_ROLIST].c_value = strdup(val + 1);
+
+  } else if (optionok(tmp, "codepage:", val)) {
+    if (options[VOLOPT_CODEPAGE].c_value)
+      free(options[VOLOPT_CODEPAGE].c_value);
+    options[VOLOPT_CODEPAGE].c_value = get_codepage_path(nlspath, val + 1);
+
+  } else if (optionok(tmp, "casefold:", val)) {
+    if (strcasecmp(val + 1, "tolower") == 0)
+      options[VOLOPT_CASEFOLD].i_value = AFPVOL_UMLOWER;
+    else if (strcasecmp(val + 1, "toupper") == 0)
+      options[VOLOPT_CASEFOLD].i_value = AFPVOL_UMUPPER;
+    else if (strcasecmp(val + 1, "xlatelower") == 0)
+      options[VOLOPT_CASEFOLD].i_value = AFPVOL_UUPPERMLOWER;
+    else if (strcasecmp(val + 1, "xlateupper") == 0)
+      options[VOLOPT_CASEFOLD].i_value = AFPVOL_ULOWERMUPPER;
+
+  } else if (optionok(tmp, "options:", val)) {
+    char *p;
+    
+    if ((p = strtok(val + 1, ",")) == NULL) /* nothing */
+      return;
+    
+    while (p) {
+      if (strcasecmp(p, "prodos") == 0)
+       options[VOLOPT_FLAGS].i_value |= AFPVOL_A2VOL;
+      else if (strcasecmp(p, "mswindows") == 0) {
+       options[VOLOPT_FLAGS].i_value |= AFPVOL_MSWINDOWS;
+       if (!options[VOLOPT_CODEPAGE].c_value)
+         options[VOLOPT_CODEPAGE].c_value = 
+           get_codepage_path(nlspath, MSWINDOWS_CODEPAGE);
+
+      } else if (strcasecmp(p, "crlf") == 0)
+       options[VOLOPT_FLAGS].i_value |= AFPVOL_CRLF;
+      else if (strcasecmp(p, "noadouble") == 0)
+       options[VOLOPT_FLAGS].i_value |= AFPVOL_NOADOUBLE;
+      else if (strcasecmp(p, "ro") == 0)
+       options[VOLOPT_FLAGS].i_value |= AFPVOL_RO;
+      else if (strcasecmp(p, "nohex") == 0)
+       options[VOLOPT_FLAGS].i_value |= AFPVOL_NOHEX;
+      else if (strcasecmp(p, "usedots") == 0)
+       options[VOLOPT_FLAGS].i_value |= AFPVOL_USEDOTS;
+      else if (strcasecmp(p, "limitsize") == 0)
+       options[VOLOPT_FLAGS].i_value |= AFPVOL_LIMITSIZE;
+
+      p = strtok(NULL, ",");
+    }
+
+#if AD_VERSION > AD_VERSION1
+  } else if (optionok(tmp, "dbpath:", val)) {
+    if (options[VOLOPT_DBPATH].c_value)
+      free(options[VOLOPT_DBPATH].c_value);
+    
+    options[VOLOPT_DBPATH].c_value = strdup(val + 1);
+#endif
+  } else if (optionok(tmp, "mapchars:",val)) {
+    if (options[VOLOPT_MAPCHARS].c_value)
+      free(options[VOLOPT_MAPCHARS].c_value);
+    options[VOLOPT_MAPCHARS].c_value = strdup(val + 1);
+
+  } else if (optionok(tmp, "password:", val)) {
+    if (options[VOLOPT_PASSWORD].c_value)
+      free(options[VOLOPT_PASSWORD].c_value);
+    options[VOLOPT_PASSWORD].c_value = strdup(val + 1);
+  } else if (val) {
+    /* ignore unknown options */
+    syslog(LOG_DEBUG, "ignoring unknown volume option: %s", tmp);
+
+  } else {
+    /* we'll assume it's a volume name. */
+    strncpy(volname, tmp, vlen); 
+  }
+}
+
+static int creatvol(const char *path, char *name, struct vol_option *options)
+{
+    struct vol *volume;
+    int                vlen;
+
+    if ( name == NULL || *name == '\0' ) {
+        if ((name = strrchr( path, '/' )) == NULL) {
+           return -1;  /* Obviously not a fully qualified path */
+       }
+
+       /* if you wish to share /, you need to specify a name. */
+       if (*++name == '\0')   
+         return -1; 
+    }
+
+    for ( volume = volumes; volume; volume = volume->v_next ) {
+       if ( strcasecmp( volume->v_name, name ) == 0 ) {
+           return -1;  /* Won't be able to access it, anyway... */
+       }
+    }
+
+    vlen = strlen( name );
+    if ( vlen > AFPVOL_NAMELEN ) {
+       vlen = AFPVOL_NAMELEN;
+       name[AFPVOL_NAMELEN] = '\0';
+    }
+
+    if (( volume =
+           (struct vol *)calloc(1, sizeof( struct vol ))) == NULL ) {
+       syslog( LOG_ERR, "creatvol: malloc: %m" );
+       return -1;
+    }
+    if (( volume->v_name =
+           (char *)malloc( vlen + 1 )) == NULL ) {
+       syslog( LOG_ERR, "creatvol: malloc: %m" );
+       free(volume);
+       return -1;
+    }
+    if (( volume->v_path =
+           (char *)malloc( strlen( path ) + 1 )) == NULL ) {
+       syslog( LOG_ERR, "creatvol: malloc: %m" );
+       free(volume->v_name);
+       free(volume);
+       return -1;
+    }
+
+    strcpy( volume->v_name, name);
+    strcpy( volume->v_path, path );
+
+#ifdef __svr4__
+    volume->v_qfd = -1;
+#endif
+    volume->v_vid = lastvid++;
+    volume->v_lastdid = 3;
+
+    /* handle options */
+    if (options) {
+      /* should we casefold? */
+      volume->v_casefold = options[VOLOPT_CASEFOLD].i_value;
+
+      /* shift in some flags */
+      volume->v_flags = options[VOLOPT_FLAGS].i_value;
+
+      /* read in the code pages */
+      if (options[VOLOPT_CODEPAGE].c_value)
+       codepage_read(volume, options[VOLOPT_CODEPAGE].c_value);
+
+      if (options[VOLOPT_PASSWORD].c_value) 
+       volume->v_password = strdup(options[VOLOPT_PASSWORD].c_value);
+
+#if AD_VERSION > AD_VERSION1
+      if (options[VOLOPT_DBPATH].c_value)
+       volume->v_dbpath = strdup(options[VOLOPT_DBPATH].c_value);
+#endif
+    }
+
+    volume->v_next = volumes;
+    volumes = volume;
+    return 0;
+}
+
+static char *myfgets( buf, size, fp )
+    char       *buf;
+    int                size;
+    FILE       *fp;
+{
+    char       *p;
+    int                c;
+
+    p = buf;
+    while ((( c = getc( fp )) != EOF ) && ( size > 0 )) {
+       if ( c == '\n' || c == '\r' ) {
+           *p++ = '\n';
+           break;
+       } else {
+           *p++ = c;
+       }
+       size--;
+    }
+
+    if ( p == buf ) {
+       return( NULL );
+    } else {
+       *p = '\0';
+       return( buf );
+    }
+}
+
+
+/* check access list. this function wants something of the following
+ * form:
+ *        @group,name,name2,@group2,name3
+ *
+ * a NULL argument allows everybody to have access.
+ * we return three things:
+ *     -1: no list
+ *      0: list exists, but name isn't in it
+ *      1: in list
+ */
+static int accessvol(args, name)
+    const char *args;
+    const char *name;
+{
+    char buf[MAXPATHLEN + 1], *p;
+    struct group *gr;
+
+    if (!args)
+      return -1;
+
+    strncpy(buf, args, sizeof(buf));
+    if ((p = strtok(buf, ",")) == NULL) /* nothing, return okay */
+      return -1;
+
+    while (p) {
+      if (*p == '@') { /* it's a group */
+       if ((gr = getgrnam(p + 1)) && gmem(gr->gr_gid))
+           return 1;
+      } else if (strcmp(p, name) == 0) /* it's a user name */
+       return 1;
+      p = strtok(NULL, ",");
+    }
+
+    return 0;
+}
+
+static void setextmap( ext, type, creator, user)
+    char               *ext, *type, *creator;
+    int                        user;
+{
+    struct extmap      *em;
+
+    for ( em = extmap; em; em = em->em_next ) {
+       if ( strdiacasecmp( em->em_ext, ext ) == 0 ) {
+           break;
+       }
+    }
+
+    if ( em == NULL ) {
+       if (( em =
+               (struct extmap *)malloc( sizeof( struct extmap ))) == NULL ) {
+           syslog( LOG_ERR, "setextmap: malloc: %m" );
+           return;
+       }
+       em->em_next = extmap;
+       extmap = em;
+    } else if ( !user ) {
+       return;
+    }
+
+    strcpy( em->em_ext, ext );
+
+    if ( *type == '\0' ) {
+       memcpy(em->em_type, "????", sizeof( em->em_type ));
+    } else {
+       memcpy(em->em_type, type, sizeof( em->em_type ));
+    }
+    if ( *creator == '\0' ) {
+       memcpy(em->em_creator, "UNIX", sizeof( em->em_creator ));
+    } else {
+       memcpy(em->em_creator, creator, sizeof( em->em_creator ));
+    }
+
+    if ( strcmp( ext, "." ) == 0 ) {
+       defextmap = em;
+    }
+}
+
+/*
+ * Read a volume configuration file and add the volumes contained within to
+ * the global volume list.  If p2 is non-NULL, the file that is opened is
+ * p1/p2
+ *
+ * Lines that begin with # and blank lines are ignored.
+ * Volume lines are of the form:
+ *             <unix path> [<volume name>] [allow:<user>,<@group>,...] \
+ *                           [codepage:<file>] [casefold:<num>]
+ *             <extension> TYPE [CREATOR]
+ */
+static int readvolfile(obj, p1, p2, user, pwent)
+    AFPObj      *obj;
+    char       *p1, *p2;
+    int                user;
+    struct passwd *pwent;
+{
+    FILE               *fp;
+    char               path[ MAXPATHLEN + 1], tmp[ MAXPATHLEN + 1],
+                       volname[ AFPVOL_NAMELEN + 1 ], buf[ BUFSIZ ],
+                       type[ 5 ], creator[ 5 ];
+    char               *u, *p;
+    struct passwd      *pw;
+    struct vol_option   options[VOLOPT_NUM], save_options[VOLOPT_NUM];
+    int                 i;
+
+    if (!p1)
+        return -1;
+
+    strcpy( path, p1 );
+    if ( p2 != NULL ) {
+       strcat( path, "/" );
+       strcat( path, p2 );
+    }
+
+    if (( fp = fopen( path, "r" )) == NULL ) {
+       return( -1 );
+    }
+    
+    memset(save_options, 0, sizeof(save_options));
+    while ( myfgets( buf, sizeof( buf ), fp ) != NULL ) {
+       initline( strlen( buf ), buf );
+       parseline( sizeof( path ) - 1, path );
+       switch ( *path ) {
+       case '\0' :
+       case '#' :
+           continue;
+
+       case ':':
+         /* change the default options for this file */
+         if (strncmp(path, VOLOPT_DEFAULT, VOLOPT_DEFAULT_LEN) == 0) {
+           *tmp = '\0';
+           for (i = 0; i < VOLOPT_NUM; i++) {
+             if (parseline( sizeof( path ) - VOLOPT_DEFAULT_LEN - 1, 
+                            path + VOLOPT_DEFAULT_LEN) < 0)
+               break;
+             volset(save_options, tmp, sizeof(tmp) - 1, 
+                    obj->options.nlspath, path + VOLOPT_DEFAULT_LEN);
+           }
+         }
+         break;
+
+       case '~' :
+           if (( p = strchr( path, '/' )) != NULL ) {
+               *p++ = '\0';
+           }
+           u = path;
+           u++;
+           if ( *u == '\0' ) {
+               u = obj->username;
+           }
+           if ( u == NULL || *u == '\0' || ( pw = getpwnam( u )) == NULL ) {
+               continue;
+           }
+           strcpy( tmp, pw->pw_dir );
+           if ( p != NULL && *p != '\0' ) {
+               strcat( tmp, "/" );
+               strcat( tmp, p );
+           }
+           /* fall through */
+
+       case '/' :
+           /* send path through variable substitution */
+           if (*path != '~') /* need to copy path to tmp */
+             strcpy(tmp, path);
+           if (!pwent)
+             pwent = getpwnam(obj->username);
+           volxlate(obj, path, sizeof(path) - 1, tmp, pwent, NULL);
+
+           /* this is sort of braindead. basically, i want to be
+            * able to specify things in any order, but i don't want to 
+            * re-write everything. 
+            *
+            * currently we have 11 options: 
+            *   volname
+            *   codepage:x
+            *   casefold:x
+            *   allow:x,y,@z
+            *   deny:x,y,@z
+            *   rwlist:x,y,@z
+            *   rolist:x,y,@z
+            *   options:prodos,crlf,noadouble,ro
+            *   dbpath:x
+            *   password:x
+            *   namemask:x,y,!z  (not implemented yet)
+            */
+           memcpy(options, save_options, sizeof(options));
+           *volname = '\0';
+
+           /* read in up to 11 possible options */
+           for (i = 0; i < VOLOPT_NUM; i++) {
+             if (parseline( sizeof( tmp ) - 1, tmp ) < 0)
+               break;
+
+             volset(options, volname, sizeof(volname) - 1, 
+                    obj->options.nlspath, tmp);
+           }
+
+           /* check allow/deny lists: 
+              allow -> either no list (-1), or in list (1)
+              deny -> either no list (-1), or not in list (0) */
+           if (accessvol(options[VOLOPT_ALLOW].c_value, obj->username) &&
+               (accessvol(options[VOLOPT_DENY].c_value, obj->username) < 1)) {
+
+             /* handle read-only behaviour. semantics: 
+              * 1) neither the rolist nor the rwlist exist -> rw
+              * 2) rolist exists -> ro if user is in it.
+              * 3) rwlist exists -> ro unless user is in it. */
+             if (((options[VOLOPT_FLAGS].i_value & AFPVOL_RO) == 0) && 
+                 ((accessvol(options[VOLOPT_ROLIST].c_value, 
+                             obj->username) == 1) ||
+                  !accessvol(options[VOLOPT_RWLIST].c_value, 
+                             obj->username))) 
+               options[VOLOPT_FLAGS].i_value |= AFPVOL_RO;
+
+             /* do variable substitution */
+             volxlate(obj, tmp, sizeof(tmp) - 1, volname, pwent, path);
+             creatvol(path, tmp, options);
+           }
+           volfree(options, save_options);
+           break;
+
+       case '.' :
+           parseline( sizeof( type ) - 1, type );
+           parseline( sizeof( creator ) - 1, creator );
+           setextmap( path, type, creator, user);
+           break;
+
+       default :
+           break;
+       }
+    }
+    volfree(save_options, NULL);
+    if ( fclose( fp ) != 0 ) {
+       syslog( LOG_ERR, "readvolfile: fclose: %m" );
+    }
+    return( 0 );
+}
+
+
+static void load_volumes(AFPObj *obj)
+{
+  struct passwd        *pwent = getpwnam(obj->username);
+
+  if ( (obj->options.flags & OPTION_USERVOLFIRST) == 0 ) {
+    readvolfile(obj, obj->options.systemvol, 0, pwent);
+  }
+
+  if ((*obj->username == '\0') || (obj->options.flags & OPTION_NOUSERVOL)) {
+    readvolfile(obj, obj->options.defaultvol, NULL, 1, pwent);
+  } else if (pwent) {
+        /*
+        * Read user's AppleVolumes or .AppleVolumes file
+        * If neither are readable, read the default volumes file. if 
+        * that doesn't work, create a user share.
+        */
+    if ( readvolfile(obj, pwent->pw_dir, "AppleVolumes", 1, pwent) < 0 &&
+        readvolfile(obj, pwent->pw_dir, ".AppleVolumes", 1, pwent) < 0 &&
+        readvolfile(obj, pwent->pw_dir, "applevolumes", 1, pwent) < 0 &&
+        readvolfile(obj, pwent->pw_dir, ".applevolumes", 1, pwent) < 0 &&
+        obj->options.defaultvol != NULL ) {
+      if (readvolfile(obj, obj->options.defaultvol, NULL, 1, pwent) < 0)
+       creatvol(pwent->pw_dir, NULL, NULL);
+    }
+  }
+  if ( obj->options.flags & OPTION_USERVOLFIRST ) {
+    readvolfile(obj, obj->options.systemvol, NULL, 0, pwent );
+  }
+}
+
+static int getvolspace( vol, bfree, btotal, xbfree, xbtotal, bsize )
+    struct vol *vol;
+    u_int32_t  *bfree, *btotal, *bsize;
+    VolSpace    *xbfree, *xbtotal;
+{
+    int                spaceflag, rc;
+    u_int32_t   maxsize;
+#ifndef NO_QUOTA_SUPPORT
+    VolSpace   qfree, qtotal;
+#endif
+
+    spaceflag = AFPVOL_GVSMASK & vol->v_flags;
+    /* report up to 2GB if afp version is < 2.2 (4GB if not) */
+    maxsize = (vol->v_flags & AFPVOL_A2VOL) ? 0x01fffe00 :
+      (((afp_version < 22) || (vol->v_flags & AFPVOL_LIMITSIZE))
+       ? 0x7fffffffL : 0xffffffffL);
+
+#ifdef AFS
+    if ( spaceflag == AFPVOL_NONE || spaceflag == AFPVOL_AFSGVS ) {
+       if ( afs_getvolspace( vol, xbfree, xbtotal, bsize ) == AFP_OK ) {
+           vol->v_flags = ( ~AFPVOL_GVSMASK & vol->v_flags ) | AFPVOL_AFSGVS;
+           goto getvolspace_done;
+       }
+    }
+#endif AFS
+
+    if (( rc = ustatfs_getvolspace( vol, xbfree, xbtotal,
+                                   bsize)) != AFP_OK ) {
+       return( rc );
+    }
+
+#define min(a,b)       ((a)<(b)?(a):(b))
+#ifndef NO_QUOTA_SUPPORT
+    if ( spaceflag == AFPVOL_NONE || spaceflag == AFPVOL_UQUOTA ) {
+       if ( uquota_getvolspace( vol, &qfree, &qtotal, *bsize ) == AFP_OK ) {
+           vol->v_flags = ( ~AFPVOL_GVSMASK & vol->v_flags ) | AFPVOL_UQUOTA;
+           *xbfree = min(*xbfree, qfree);
+           *xbtotal = min( *xbtotal, qtotal);
+           goto getvolspace_done;
+       }
+    }
+#endif
+    vol->v_flags = ( ~AFPVOL_GVSMASK & vol->v_flags ) | AFPVOL_USTATFS;
+
+getvolspace_done:
+    *bfree = min( *xbfree, maxsize);
+    *btotal = min( *xbtotal, maxsize);
+    return( AFP_OK );
+}
+
+static int getvolparams( bitmap, vol, st, buf, buflen )
+    u_int16_t  bitmap;
+    struct vol *vol;
+    struct stat        *st;
+    char       *buf;
+    int                *buflen;
+{
+    struct adouble     ad;
+    int                        bit = 0, aint, isad = 1;
+    u_short            ashort;
+    u_int32_t          bfree, btotal, bsize;
+    VolSpace            xbfree, xbtotal; /* extended bytes */
+    char               *data, *nameoff = NULL;
+    char                *slash;
+
+    /* courtesy of jallison@whistle.com:
+     * For MacOS8.x support we need to create the
+     * .Parent file here if it doesn't exist. */
+    
+    memset(&ad, 0, sizeof(ad));
+    if ( ad_open( vol->v_path, vol_noadouble(vol) | 
+                 ADFLAGS_HF|ADFLAGS_DIR, O_RDWR | O_CREAT, 
+                 0666, &ad) < 0 ) {
+         isad = 0;
+
+    } else if (ad_getoflags( &ad, ADFLAGS_HF ) & O_CREAT) {
+          slash = strrchr( vol->v_path, '/' );
+          if(slash)
+              slash++;
+          else
+              slash = vol->v_path;
+
+         ad_setentrylen( &ad, ADEID_NAME, strlen( slash ));
+         memcpy(ad_entry( &ad, ADEID_NAME ), slash, 
+                ad_getentrylen( &ad, ADEID_NAME ));
+         ad_flush(&ad, ADFLAGS_HF);
+    }
+
+    if (( bitmap & ( (1<<VOLPBIT_BFREE)|(1<<VOLPBIT_BTOTAL) |
+                    (1<<VOLPBIT_XBFREE)|(1<<VOLPBIT_XBTOTAL) |
+                    (1<<VOLPBIT_BSIZE)) ) != 0 ) {
+       if ( getvolspace( vol, &bfree, &btotal, &xbfree, &xbtotal,
+                         &bsize) < 0 ) {
+           if ( isad ) {
+               ad_close( &ad, ADFLAGS_HF );
+           }
+           return( AFPERR_PARAM );
+       }
+    }
+
+    data = buf;
+    while ( bitmap != 0 ) {
+       while (( bitmap & 1 ) == 0 ) {
+           bitmap = bitmap>>1;
+           bit++;
+       }
+
+       switch ( bit ) {
+       case VOLPBIT_ATTR :
+#if AD_VERSION > AD_VERSION1
+           ashort = VOLPBIT_ATTR_FILEID;
+#else
+           ashort = 0;
+#endif
+           /* check for read-only.
+            * NOTE: we don't actually set the read-only flag unless
+            *       it's passed in that way as it's possible to mount
+            *       a read-write filesystem under a read-only one. */
+           if ((vol->v_flags & AFPVOL_RO) ||
+               ((utime(vol->v_path, NULL) < 0) && (errno == EROFS)))
+             ashort |= VOLPBIT_ATTR_RO;
+           ashort = htons(ashort);
+           memcpy(data, &ashort, sizeof( ashort ));
+           data += sizeof( ashort );
+           break;
+
+       case VOLPBIT_SIG :
+           ashort = htons( AFPVOLSIG_DEFAULT );
+           memcpy(data, &ashort, sizeof( ashort ));
+           data += sizeof( ashort );
+           break;
+
+       case VOLPBIT_CDATE :
+           if (!isad || (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0))
+               aint = AD_DATE_FROM_UNIX(st->st_mtime);
+           memcpy(data, &aint, sizeof( aint ));
+           data += sizeof( aint );
+           break;
+
+       case VOLPBIT_MDATE :
+           if ( st->st_mtime > vol->v_time ) {
+               vol->v_time = st->st_mtime;
+               aint = AD_DATE_FROM_UNIX(st->st_mtime);
+           } else {
+               aint = AD_DATE_FROM_UNIX(vol->v_time);
+           }
+           memcpy(data, &aint, sizeof( aint ));
+           data += sizeof( aint );
+           break;
+
+       case VOLPBIT_BDATE :
+           if (!isad ||  (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0))
+               aint = AD_DATE_START;
+           memcpy(data, &aint, sizeof( aint ));
+           data += sizeof( aint );
+           break;
+
+       case VOLPBIT_VID :
+           memcpy(data, &vol->v_vid, sizeof( vol->v_vid ));
+           data += sizeof( vol->v_vid );
+           break;
+
+       case VOLPBIT_BFREE :
+           bfree = htonl( bfree );
+           memcpy(data, &bfree, sizeof( bfree ));
+           data += sizeof( bfree );
+           break;
+
+       case VOLPBIT_BTOTAL :
+           btotal = htonl( btotal );
+           memcpy(data, &btotal, sizeof( btotal ));
+           data += sizeof( btotal );
+           break;
+
+#ifndef NO_LARGE_VOL_SUPPORT
+       case VOLPBIT_XBFREE :
+           xbfree = hton64( xbfree );
+#if defined(__GNUC__) && defined(HAVE_GCC_MEMCPY_BUG)
+           bcopy(&xbfree, data, sizeof(xbfree));
+#else
+           memcpy(data, &xbfree, sizeof( xbfree ));
+#endif
+           data += sizeof( xbfree );
+           break;
+
+       case VOLPBIT_XBTOTAL :
+           xbtotal = hton64( xbtotal );
+#if defined(__GNUC__) && defined(HAVE_GCC_MEMCPY_BUG)
+           bcopy(&xbtotal, data, sizeof(xbtotal));
+#else
+           memcpy(data, &xbtotal, sizeof( xbtotal ));
+#endif
+           data += sizeof( xbfree );
+           break;
+#endif
+
+       case VOLPBIT_NAME :
+           nameoff = data;
+           data += sizeof( u_int16_t );
+           break;
+
+       case VOLPBIT_BSIZE:  /* block size */
+           bsize = htonl(bsize);
+           memcpy(data, &bsize, sizeof(bsize));
+           data += sizeof(bsize);
+           break;
+
+       default :
+           if ( isad ) {
+               ad_close( &ad, ADFLAGS_HF );
+           }
+           return( AFPERR_BITMAP );
+       }
+       bitmap = bitmap>>1;
+       bit++;
+    }
+    if ( nameoff ) {
+       ashort = htons( data - buf );
+       memcpy(nameoff, &ashort, sizeof( ashort ));
+       aint = strlen( vol->v_name );
+       *data++ = aint;
+       memcpy(data, vol->v_name, aint );
+       data += aint;
+    }
+    if ( isad ) {
+        ad_close( &ad, ADFLAGS_HF );
+    }
+    *buflen = data - buf;
+    return( AFP_OK );
+}
+
+
+
+int afp_getsrvrparms(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int        ibuflen, *rbuflen;
+{
+    struct timeval     tv;
+    struct stat                st;
+    struct vol         *volume;
+    char       *data;
+    int                        vcnt, len;
+
+  
+    if (!volumes)
+      load_volumes(obj);
+
+    data = rbuf + 5;
+    for ( vcnt = 0, volume = volumes; volume; volume = volume->v_next ) {
+       if ( stat( volume->v_path, &st ) < 0 ) {
+           syslog( LOG_INFO, "afp_getsrvrparms: stat %s: %m", 
+                   volume->v_path );
+           continue;           /* can't access directory */
+       }
+       if (!S_ISDIR(st.st_mode)) {
+           continue;           /* not a dir */
+       }
+
+       /* set password bit if there's a volume password */
+       *data = (volume->v_password) ? AFPSRVR_PASSWD : 0;
+
+       /* Apple 2 clients running ProDOS-8 expect one volume to have
+          bit 0 of this byte set.  They will not recognize anything
+          on the server unless this is the case.  I have not
+          completely worked this out, but it's related to booting
+          from the server.  Support for that function is a ways
+          off.. <shirsch@ibm.net> */
+       *data++ |= (volume->v_flags & AFPVOL_A2VOL) ? AFPSRVR_CONFIGINFO : 0;
+       len = strlen( volume->v_name );
+       *data++ = len;
+       memcpy(data, volume->v_name, len );
+       data += len;
+       vcnt++;
+    }
+
+    *rbuflen = data - rbuf;
+    data = rbuf;
+    if ( gettimeofday( &tv, 0 ) < 0 ) {
+       syslog( LOG_ERR, "afp_getsrvrparms: gettimeofday: %m" );
+       *rbuflen = 0;
+       return AFPERR_PARAM;
+    }
+    tv.tv_sec = AD_DATE_FROM_UNIX(tv.tv_sec);
+    memcpy(data, &tv.tv_sec, sizeof( u_int32_t));
+    data += sizeof( u_int32_t);
+    *data = vcnt;
+    return( AFP_OK );
+}
+
+int afp_openvol(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct stat        st;
+    char       *volname;
+#if AD_VERSION == AD_VERSION1
+    char *p;
+#endif
+    struct vol *volume;
+    struct dir *dir;
+    int                len, ret, buflen;
+    u_int16_t  bitmap;
+
+    ibuf += 2;
+    memcpy(&bitmap, ibuf, sizeof( bitmap ));
+    bitmap = ntohs( bitmap );
+    ibuf += sizeof( bitmap );
+    if (( bitmap & (1<<VOLPBIT_VID)) == 0 ) {
+        ret = AFPERR_BITMAP;
+       goto openvol_err;
+    }
+
+    len = (unsigned char)*ibuf++;
+    volname = obj->oldtmp;
+    memcpy(volname, ibuf, len );
+    *(volname +  len) = '\0';
+    ibuf += len;
+    if ((len + 1) & 1) /* pad to an even boundary */
+      ibuf++;
+
+    if (!volumes)
+      load_volumes(obj);
+
+    for ( volume = volumes; volume; volume = volume->v_next ) {
+       if ( strcasecmp( volname, volume->v_name ) == 0 ) {
+           break;
+       }
+    }
+
+    if ( volume == NULL ) {
+       ret = AFPERR_PARAM;
+       goto openvol_err;
+    }
+
+    /* check for a volume password */
+    if (volume->v_password && 
+       strncmp(ibuf, volume->v_password, VOLPASSLEN)) {
+        ret = AFPERR_ACCESS;
+       goto openvol_err;
+    }
+
+    if (( volume->v_flags & AFPVOL_OPEN  ) == 0 ) {
+        if ((dir = dirnew(strlen(volume->v_name) + 1)) == NULL) {
+           syslog( LOG_ERR, "afp_openvol: malloc: %m" );
+           ret = AFPERR_MISC;
+           goto openvol_err;
+       }
+       dir->d_did = DIRDID_ROOT;
+       dir->d_color = DIRTREE_COLOR_BLACK; /* root node is black */
+       strcpy( dir->d_name, volume->v_name );
+       volume->v_dir = volume->v_root = dir;
+       volume->v_flags |= AFPVOL_OPEN;
+    }
+
+    if ( stat( volume->v_path, &st ) < 0 ) {
+       ret = AFPERR_PARAM;
+       goto openvol_err;
+    }
+
+    buflen = *rbuflen - sizeof( bitmap );
+    if (( ret = getvolparams( bitmap, volume, &st,
+           rbuf + sizeof(bitmap), &buflen )) != AFP_OK ) {
+        goto openvol_err;
+    }
+    *rbuflen = buflen + sizeof( bitmap );
+    bitmap = htons( bitmap );
+    memcpy(rbuf, &bitmap, sizeof( bitmap ));
+
+    curdir = volume->v_dir;
+    if ( chdir( volume->v_path ) < 0 ) {
+        ret = AFPERR_PARAM;
+       goto openvol_err;
+    }
+#if AD_VERSION == AD_VERSION1
+    /*
+     * If you mount a volume twice, the second time the trash appears on
+     * the desk-top.  That's because the Mac remembers the DID for the
+     * trash (even for volumes in different zones, on different servers).
+     * Just so this works better, we prime the DID cache with the trash,
+     * fixing the trash at DID 3.
+     */
+    p = Trash;
+    cname( volume, volume->v_dir, &p );
+#endif
+
+    return( AFP_OK );
+
+openvol_err:
+    *rbuflen = 0;
+    return ret;
+}
+
+int afp_closevol(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct vol *vol, *ovol;
+    u_int16_t  vid;
+
+    *rbuflen = 0;
+    ibuf += 2;
+    memcpy(&vid, ibuf, sizeof( vid ));
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    vol->v_flags &= ~AFPVOL_OPEN;
+    for ( ovol = volumes; ovol; ovol = ovol->v_next ) {
+       if ( ovol->v_flags & AFPVOL_OPEN ) {
+           break;
+       }
+    }
+    if ( ovol != NULL ) {
+       curdir = ovol->v_dir;
+       if ( chdir( ovol->v_path ) < 0 ) {
+           return( AFPERR_PARAM );
+       }
+    }
+
+    dirfree( vol->v_root );
+    vol->v_dir = NULL;
+#if AD_VERSION > AD_VERSION1
+    cnid_close(vol->v_db);
+    vol->v_db = NULL;
+#endif
+    return( AFP_OK );
+}
+
+struct vol *getvolbyvid(const u_int16_t vid )
+{
+    struct vol *vol;
+
+    for ( vol = volumes; vol; vol = vol->v_next ) {
+       if ( vid == vol->v_vid ) {
+           break;
+       }
+    }
+    if ( vol == NULL || ( vol->v_flags & AFPVOL_OPEN ) == 0 ) {
+       return( NULL );
+    }
+
+    return( vol );
+}
+
+struct extmap *getextmap(const char *path)
+{
+    char       *p;
+    struct extmap      *em;
+
+    if (( p = strrchr( path, '.' )) == NULL ) {
+       return( defextmap );
+    }
+
+    for ( em = extmap; em; em = em->em_next ) {
+       if ( strdiacasecmp( em->em_ext, p ) == 0 ) {
+           break;
+       }
+    }
+    if ( em == NULL ) {
+       return( defextmap );
+    } else {
+       return( em );
+    }
+}
+
+void setvoltime(obj, vol )
+    AFPObj *obj;
+    struct vol *vol;
+{
+    struct timeval     tv;
+
+    /* fail w/out dying */
+    if ( gettimeofday( &tv, 0 ) < 0 ) {
+       syslog( LOG_ERR, "setvoltime: gettimeofday: %m" );
+       return;
+    }
+    
+    /* a little granularity */
+    if (vol->v_time < tv.tv_sec) {
+      vol->v_time = tv.tv_sec;
+      obj->attention(obj->handle, AFPATTN_NOTIFY | AFPATTN_VOLCHANGED);
+    }
+}
+
+int afp_getvolparams(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct stat        st;
+    struct vol *vol;
+    int                buflen, ret;
+    u_int16_t  vid, bitmap;
+
+    ibuf += 2;
+    memcpy(&vid, ibuf, sizeof( vid ));
+    ibuf += sizeof( vid );
+    memcpy(&bitmap, ibuf, sizeof( bitmap ));
+    bitmap = ntohs( bitmap );
+
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       *rbuflen = 0;
+       return( AFPERR_PARAM );
+    }
+
+    if ( stat( vol->v_path, &st ) < 0 ) {
+       *rbuflen = 0;
+       return( AFPERR_PARAM );
+    }
+
+    buflen = *rbuflen - sizeof( bitmap );
+    if (( ret = getvolparams( bitmap, vol, &st,
+           rbuf + sizeof( bitmap ), &buflen )) != AFP_OK ) {
+       *rbuflen = 0;
+       return( ret );
+    }
+    *rbuflen = buflen + sizeof( bitmap );
+    bitmap = htons( bitmap );
+    memcpy(rbuf, &bitmap, sizeof( bitmap ));
+    return( AFP_OK );
+}
+
+int afp_setvolparams(obj, ibuf, ibuflen, rbuf, rbuflen )
+    AFPObj      *obj;
+    char       *ibuf, *rbuf;
+    int                ibuflen, *rbuflen;
+{
+    struct adouble ad;
+    struct vol *vol;
+    u_int16_t  vid, bitmap;
+    u_int32_t   aint;
+
+    ibuf += 2;
+    *rbuflen = 0;
+
+    memcpy(&vid, ibuf, sizeof( vid ));
+    ibuf += sizeof( vid );
+    memcpy(&bitmap, ibuf, sizeof( bitmap ));
+    bitmap = ntohs( bitmap );
+    ibuf += sizeof(bitmap);
+
+    if (( vol = getvolbyvid( vid )) == NULL ) {
+       return( AFPERR_PARAM );
+    }
+
+    if (vol->v_flags & AFPVOL_RO)
+        return AFPERR_VLOCK;
+
+    /* we can only set the backup date. */
+    if (bitmap != VOLPBIT_BDATE)
+      return AFPERR_BITMAP;
+
+    memset(&ad, 0, sizeof(ad));
+    if ( ad_open( vol->v_path, ADFLAGS_HF|ADFLAGS_DIR, O_RDWR, 
+                 0666, &ad) < 0 ) {
+      if (errno == EROFS)
+       return AFPERR_VLOCK;
+
+      return AFPERR_ACCESS;
+    }
+
+    memcpy(&aint, ibuf, sizeof(aint));
+    ad_setdate(&ad, AD_DATE_BACKUP, aint);
+    ad_flush(&ad, ADFLAGS_HF);
+    ad_close(&ad, ADFLAGS_HF);
+    return( AFP_OK );
+}
diff --git a/etc/afpd/volume.h b/etc/afpd/volume.h
new file mode 100644 (file)
index 0000000..86274f3
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#ifndef AFPD_VOLUME_H
+#define AFPD_VOLUME_H 1
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <netatalk/endian.h>
+#include "globals.h"
+
+#define AFPVOL_NAMELEN   27
+
+struct codepage_hash {
+  unsigned char *from, *to;
+  struct codepage_hash *next, *prev;
+};
+
+union codepage_val {
+  struct codepage_hash hash; /* hash for multibyte values */
+  unsigned char value; /* single byte value/rule */
+};
+
+struct codepage {
+  union codepage_val *map;
+  int quantum;
+};
+
+#define CP_HASH(a)    (*(a))
+
+struct vol {
+    struct vol         *v_next;
+    char               *v_name;
+    char               *v_path;
+    struct dir         *v_dir, *v_root;
+    int                        v_flags;
+#ifdef __svr4__
+    int                        v_qfd;
+#endif /*__svr4__*/
+    void               *v_gvs;
+    u_int32_t          v_time;
+    int                        v_lastdid;
+    u_int16_t          v_vid;
+    void                *v_nfsclient;
+    int                 v_nfs, v_casefold;
+    struct codepage     *v_mtoupage, *v_utompage, *v_badumap;
+    char                *v_password;
+#if AD_VERSION > AD_VERSION1
+    void                *v_db;
+    char                *v_dbpath;
+#endif
+};
+
+#ifdef NO_LARGE_VOL_SUPPORT
+typedef u_int32_t VolSpace;
+#else
+typedef u_int64_t VolSpace;
+#endif
+
+#define AFPVOL_OPEN    (1<<0)
+#define AFPVOL_DT      (1<<1)
+
+#define AFPVOL_GVSMASK (7<<2)
+#define AFPVOL_NONE    (0<<2)
+#define AFPVOL_AFSGVS  (1<<2)
+#define AFPVOL_USTATFS (2<<2)
+#define AFPVOL_UQUOTA  (4<<2)
+
+/* flags that alter volume behaviour. */
+#define AFPVOL_A2VOL     (1 << 5)   /* prodos volume */
+#define AFPVOL_CRLF      (1 << 6)   /* cr/lf translation */
+#define AFPVOL_NOADOUBLE (1 << 7)   /* don't create .AppleDouble by default */
+#define AFPVOL_RO        (1 << 8)   /* read-only volume */
+#define AFPVOL_MSWINDOWS (1 << 9)   /* deal with ms-windows yuckiness.
+                                      this is going away. */
+#define AFPVOL_NOHEX     (1 << 10)  /* don't do :hex translation */
+#define AFPVOL_USEDOTS   (1 << 11)  /* use real dots */
+#define AFPVOL_LIMITSIZE (1 << 12)  /* limit size for older macs */
+#define AFPVOL_MAPASCII  (1 << 13)  /* map the ascii range as well */
+
+/* FPGetSrvrParms options */
+#define AFPSRVR_CONFIGINFO     (1 << 0)
+#define AFPSRVR_PASSWD         (1 << 7)
+
+/* handle casefolding */
+#define AFPVOL_MTOUUPPER       (1 << 0) 
+#define AFPVOL_MTOULOWER       (1 << 1) 
+#define AFPVOL_UTOMUPPER       (1 << 2) 
+#define AFPVOL_UTOMLOWER       (1 << 3) 
+#define AFPVOL_UMLOWER         (AFPVOL_MTOULOWER | AFPVOL_UTOMLOWER)
+#define AFPVOL_UMUPPER         (AFPVOL_MTOUUPPER | AFPVOL_UTOMUPPER)
+#define AFPVOL_UUPPERMLOWER    (AFPVOL_MTOUUPPER | AFPVOL_UTOMLOWER)
+#define AFPVOL_ULOWERMUPPER    (AFPVOL_MTOULOWER | AFPVOL_UTOMUPPER)
+#define MSWINDOWS_BADCHARS "\\/<>*?|\""
+#define MSWINDOWS_CODEPAGE "maccode.iso8859-1"
+
+#define AFPVOLSIG_FLAT          0x0001 /* flat fs */
+#define AFPVOLSIG_FIX          0x0002 /* fixed ids */
+#define AFPVOLSIG_VAR           0x0003 /* variable ids */
+#define AFPVOLSIG_DEFAULT       AFPVOLSIG_FIX
+
+/* volume attributes */
+#define VOLPBIT_ATTR_RO           (1 << 0)
+#define VOLPBIT_ATTR_PASSWD       (1 << 1)
+#define VOLPBIT_ATTR_FILEID       (1 << 2)
+#define VOLPBIT_ATTR_CATSEARCH    (1 << 3)
+#define VOLPBIT_ATTR_BLANKACCESS  (1 << 4)
+
+#define VOLPBIT_ATTR   0
+#define VOLPBIT_SIG    1
+#define VOLPBIT_CDATE  2
+#define VOLPBIT_MDATE  3
+#define VOLPBIT_BDATE  4
+#define VOLPBIT_VID    5
+#define VOLPBIT_BFREE  6
+#define VOLPBIT_BTOTAL 7
+#define VOLPBIT_NAME   8
+/* handle > 4GB volumes */
+#define VOLPBIT_XBFREE  9
+#define VOLPBIT_XBTOTAL 10
+#define VOLPBIT_BSIZE   11        /* block size */
+
+
+#define vol_noadouble(vol) (((vol)->v_flags & AFPVOL_NOADOUBLE) ? \
+                           ADFLAGS_NOADOUBLE : 0)
+
+extern struct vol      *getvolbyvid __P((const u_int16_t));
+extern int              ustatfs_getvolspace __P((const struct vol *,
+                                                VolSpace *, VolSpace *,
+                                                u_int32_t *));
+extern int              codepage_init __P((struct vol *, const int, 
+                                          const int));
+extern int              codepage_read __P((struct vol *, const char *));
+extern union codepage_val codepage_find __P(());
+extern void             setvoltime __P((AFPObj *, struct vol *));
+
+/* FP functions */
+extern int     afp_openvol      __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_getvolparams __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_setvolparams __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_getsrvrparms __P((AFPObj *, char *, int, char *, int *));
+extern int     afp_closevol     __P((AFPObj *, char *, int, char *, int *));
+#endif
diff --git a/etc/atalkd/Makefile b/etc/atalkd/Makefile
new file mode 100644 (file)
index 0000000..65cedac
--- /dev/null
@@ -0,0 +1,56 @@
+SRC = main.c config.c zip.c nbp.c aep.c rtmp.c route.c multicast.c 
+OBJ = main.o config.o zip.o nbp.o aep.o rtmp.o route.o multicast.o
+
+INCPATH= -I../../include
+CFLAGS=        ${DEFS} ${OPTOPTS} ${INCPATH} -DPHASE1NET
+LIBS=  -latalk ${ADDLIBS}
+TAGSFILE=      tags
+CC=    cc
+INSTALL=       install
+LIBDIRS=       -L../../libatalk
+
+all : atalkd
+
+atalkd : ${OBJ} ../../libatalk/libatalk.a
+       ${CC} ${CFLAGS} -o atalkd ${OBJ} ${LIBDIRS} ${LIBS}
+
+main.o : main.c
+       ${CC} ${CFLAGS} -DVERSION=\"`cat ../../VERSION`\" \
+           ${CPPFLAGS} -c main.c
+
+config.o : config.c
+       ${CC} ${CFLAGS} -D_PATH_ATALKDCONF=\"${ETCDIR}/atalkd.conf\" \
+           ${CPPFLAGS} -c config.c
+
+install : all
+       ${INSTALL} -c atalkd ${SBINDIR}
+
+clean :
+       rm -f a.out core* *.o *.bak *[Ee]rrs tags
+       rm -f atalkd
+
+tags : ${SRC}
+       cwd=`pwd`; \
+       for i in ${SRC}; do \
+           ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+       done
+
+depend :
+       for i in ${SRC} ; do \
+           ${CC} -M ${DEFS} ${INCPATH} $$i | \
+           awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+               else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+               else rec = rec " " $$2 } } \
+               END { print rec } ' >> makedep; done
+       sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+       cat makedep >> Makefile.tmp
+       rm makedep
+       echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+       echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+       echo '# see make depend above' >> Makefile.tmp
+       rm -f Makefile.bak
+       cp Makefile Makefile.bak
+       mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
diff --git a/etc/atalkd/aep.c b/etc/atalkd/aep.c
new file mode 100644 (file)
index 0000000..83a5323
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <sys/syslog.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netatalk/at.h>
+#include <atalk/aep.h>
+#include <atalk/ddp.h>
+
+#include "atserv.h"
+
+aep_packet( ap, from, data, len )
+    struct atport      *ap;
+    struct sockaddr_at *from;
+    char               *data;
+    int                        len;
+{
+    char               *end;
+
+    end = data + len;
+    if ( data + 2 > end || *data != DDPTYPE_AEP ||
+           *( data + 1 ) != AEPOP_REQUEST ) {
+       syslog( LOG_INFO, "aep_packet malformed packet" );
+       return 1;
+    }
+
+    *( data + 1 ) = AEPOP_REPLY;
+    if ( sendto( ap->ap_fd, data, len, 0, (struct sockaddr *)from,
+           sizeof( struct sockaddr_at )) < 0 ) {
+       syslog( LOG_ERR, "aep sendto: %m" );
+       return 1;
+    }
+
+    return 0;
+}
diff --git a/etc/atalkd/atserv.h b/etc/atalkd/atserv.h
new file mode 100644 (file)
index 0000000..6447420
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 1990,1992 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+struct atserv {
+    char       *as_name;
+    u_char     as_port;                /* Used as a fall back */
+    int                (*as_packet)();
+};
+
+struct atport {
+    int                        ap_fd;
+    struct atport      *ap_next;
+    struct interface   *ap_iface;
+    u_char             ap_port;
+    int                        (*ap_packet)();
+};
+
+extern struct atserv   atserv[];
+extern int             atservNATSERV;
diff --git a/etc/atalkd/config.c b/etc/atalkd/config.c
new file mode 100644 (file)
index 0000000..1590d82
--- /dev/null
@@ -0,0 +1,753 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <syslog.h>
+#include <sys/param.h>
+#include <net/if.h>
+#include <netatalk/at.h>
+#include <netatalk/endian.h>
+#include <atalk/paths.h>
+#include <atalk/util.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#ifdef __svr4__
+#include <sys/sockio.h>
+#include <sys/stropts.h>
+#endif __svr4__
+
+#include "interface.h"
+#include "multicast.h"
+#include "rtmp.h"
+#include "zip.h"
+#include "list.h"
+
+#ifndef IFF_SLAVE /* a little backward compatibility */
+#define IFF_SLAVE 0
+#endif
+
+int    router(), dontroute(), seed(), phase(), net(), addr(), zone();
+
+static struct param {
+    char       *p_name;
+    int                (*p_func)();
+} params[] = {
+    { "router", router },
+    { "dontroute", dontroute },
+    { "seed",  seed },
+    { "phase", phase },
+    { "net",   net },
+    { "addr",  addr },
+    { "zone",  zone },
+};
+
+static char **
+parseline( line )
+    char       *line;
+{
+    char       *p;
+    int                argc = 0;
+    static char        *argv[ 128 ];
+    static char        buf[ 1024 ];
+
+    if ( *line == '#' ) {
+       return( NULL );
+    }
+    argc = 0;
+
+    memset(argv, 0, sizeof(argv));
+    strcpy( buf, line );
+    p = buf;
+
+    /*
+     * This parser should be made more powerful -- it should
+     * handle various escapes, e.g. \" and \031.
+     */
+    while ( *p != '\0' ) {
+       while ( *p != '\0' && *p != '"' && isspace( *p )) {
+           p++;
+       }
+       if ( *p == '\0' ) {
+           argv[ argc ] = 0;
+           break;
+       }
+       if ( *p == '"' ) {
+           argv[ argc ] = ++p;
+           while ( *p != '\0' && *p != '"' ) {
+               p++;
+           }
+       } else {
+           argv[ argc ] = p;
+           while ( *p != '\0' && !isspace( *p )) {
+               p++;
+           }
+       }
+       *p++ = '\0';
+       argc++;
+    }
+    if ( argv[ 0 ] == '\0' || *argv[ 0 ] == '#' ) {
+       return( NULL );
+    }
+    return( argv );
+}
+
+writeconf( cf )
+    char       *cf;
+{
+    struct stat                st;
+    char               *path, *p, newpath[ MAXPATHLEN ], line[ 1024 ];
+    char               **argv;
+    FILE               *conf, *newconf;
+    struct interface   *iface;
+    struct list                *l;
+    int                        mode = 0644, fd;
+
+    if ( cf == NULL ) {
+       path = _PATH_ATALKDCONF;
+    } else {
+       path = cf;
+    }
+
+    /* check if old conf is writable */
+    if ( stat( path, &st ) == 0 ) {
+       if (( st.st_mode & S_IWUSR ) == 0 ) {
+           syslog( LOG_INFO, "%s not writable, won't rewrite", path );
+           return( -1 );
+       }
+        mode = st.st_mode;
+    }
+
+    if (( p = strrchr( path, '/' )) == NULL ) {
+       strcpy( newpath, _PATH_ATALKDTMP );
+    } else {
+       sprintf( newpath, "%.*s/%s", p - path, path, _PATH_ATALKDTMP );
+    }
+    if (( fd = open( newpath, O_WRONLY|O_CREAT|O_TRUNC, mode )) < 0 ) {
+       syslog( LOG_ERR, "%s: %m", newpath );
+       return( -1 );
+    }
+    if (( newconf = fdopen( fd, "w" )) == NULL ) {
+       syslog( LOG_ERR, "fdreopen %s: %m", newpath );
+       return( -1 );
+    }
+
+    if (( conf = fopen( path, "r" )) == NULL && cf ) {
+       syslog( LOG_ERR, "%s: %m", path );
+       return( -1 );
+    }
+
+    iface = interfaces->i_next;
+
+    while ( conf == NULL || fgets( line, sizeof( line ), conf ) != NULL ) {
+       if ( conf != NULL && ( argv = parseline( line )) == NULL ) {
+           if ( fputs( line, newconf ) == EOF ) {
+               syslog( LOG_ERR, "fputs: %m" );
+               return( -1 );
+           }
+           continue;
+       }
+
+       /* write real lines */
+       if ( iface ) {
+           fprintf( newconf, "%s", iface->i_name );
+           if ( iface->i_flags & IFACE_RSEED ) {
+               fprintf( newconf, " -router" );
+           } else if ( iface->i_flags & IFACE_SEED ) {
+               fprintf( newconf, " -seed" );
+           }
+           if ( iface->i_flags & IFACE_DONTROUTE) {
+               fprintf( newconf, " -dontroute");
+           }
+
+           fprintf( newconf, " -phase %d",
+                   ( iface->i_flags & IFACE_PHASE1 ) ? 1 : 2 );
+           fprintf( newconf, " -net %d", ntohs( iface->i_rt->rt_firstnet ));
+           if ( iface->i_rt->rt_lastnet != iface->i_rt->rt_firstnet ) {
+               fprintf( newconf, "-%d", ntohs( iface->i_rt->rt_lastnet ));
+           }
+           fprintf( newconf, " -addr %u.%u",
+                   ntohs( iface->i_addr.sat_addr.s_net ),
+                   iface->i_addr.sat_addr.s_node );
+           for ( l = iface->i_rt->rt_zt; l; l = l->l_next ) {
+               fprintf( newconf, " -zone \"%.*s\"",
+                       ((struct ziptab *)l->l_data)->zt_len,
+                       ((struct ziptab *)l->l_data)->zt_name );
+           }
+           fprintf( newconf, "\n" );
+
+           iface = iface->i_next;
+           if ( conf == NULL && iface == NULL ) {
+               break;
+           }
+       }
+    }
+    if ( conf != NULL ) {
+       fclose( conf );
+    }
+    fclose( newconf );
+
+    if ( rename( newpath, path ) < 0 ) {
+       syslog( LOG_ERR, "rename %s to %s: %m", newpath, path );
+       return( -1 );
+    }
+    return( 0 );
+}
+
+/*
+ * Read our config file. If it's not there, return -1. If it is there,
+ * but has syntax errors, exit. Format of the file is as follows:
+ *
+ *     interface [ -seed ] [ -phase number ] [ -net net-range ]
+ *     [ -addr net.node ] [ -zone zonename ]...
+ * e.g.
+ *     le0 -phase 1 -net 7938 -zone Argus
+ * or
+ *     le0 -phase 2 -net 8043-8044 -zone Argus -zone "Research Systems"
+ *     le0 -phase 1 -net 7938 -zone Argus
+ *
+ * Pretty much everything is optional. Anything that is unspecified is
+ * searched for on the network.  If -seed is not specified, the
+ * configuration is assumed to be soft, i.e. it can be overridden by
+ * another router. If -seed is specified, atalkd will exit if another
+ * router disagrees.  If the phase is unspecified, it defaults to phase
+ * 2 (the default can be overridden on the command line).  -addr can
+ * replace -net, if the network in question isn't a range.  The default
+ * zone for an interface is the first zone encountered for that
+ * interface.
+ */
+readconf( cf )
+    char               *cf;
+{
+    struct ifreq       ifr;
+    struct interface   *iface, *niface;
+    char               line[ 1024 ], **argv, *p;
+    int                        i, j, s, cc;
+    FILE               *conf;
+
+    if ( cf == NULL ) {
+       p = _PATH_ATALKDCONF;
+    } else {
+       p = cf;
+    }
+    if (( conf = fopen( p, "r" )) == NULL ) {
+        return( -1 );
+    }
+
+#ifndef __svr4__
+    if (( s = socket( AF_APPLETALK, SOCK_DGRAM, 0 )) < 0 ) {
+       perror( "socket" );
+       fclose(conf);
+       return -1;
+    }
+#endif __svr4__
+
+    while ( fgets( line, sizeof( line ), conf ) != NULL ) {
+       if (( argv = parseline( line )) == NULL ) {
+           continue;
+       }
+
+#ifndef __svr4__
+       /*
+        * Check that av[ 0 ] is a valid interface.
+        * Not possible under sysV.
+        */
+       strcpy( ifr.ifr_name, argv[ 0 ] );
+
+       /* for devices that don't support appletalk */
+       if ((ioctl(s, SIOCGIFADDR, &ifr) < 0) && (errno == ENODEV)) {
+         perror(argv[0]);
+         goto read_conf_err;
+       }
+
+       if ( ioctl( s, SIOCGIFFLAGS, &ifr ) < 0 ) {
+           perror( argv[ 0 ] );
+           goto read_conf_err;
+       }
+
+       if (ifr.ifr_flags & (IFF_LOOPBACK | IFF_POINTOPOINT |IFF_SLAVE)) {
+           fprintf( stderr, "%s: can't configure.\n", ifr.ifr_name );
+           goto read_conf_err;
+       }
+
+#ifdef IFF_MULTICAST
+       if ((ifr.ifr_flags & IFF_MULTICAST) == 0)
+           fprintf(stderr, "%s: multicast may not work properly.\n",
+                   ifr.ifr_name);
+#endif
+
+       /* configure hw multicast for this interface. */
+       if (addmulti(ifr.ifr_name, NULL) < 0) {
+         perror(ifr.ifr_name);
+         fprintf(stderr, "Can't configure multicast.\n");
+         goto read_conf_err;
+       }
+#endif __svr4__
+
+       if (( niface = newiface( argv[ 0 ] )) == NULL ) {
+           perror( "newiface" );
+           goto read_conf_err;
+       }
+
+       for ( i = 1; argv[ i ]; i += cc ) {
+           if ( argv[ i ][ 0 ] == '-' ) {
+               argv[ i ]++;
+           }
+           for ( j = 0; j < sizeof( params ) / sizeof( params[ 0 ] ); j++ ) {
+               if ( strcmp( argv[ i ], params[ j ].p_name ) == 0 ) {
+                   if ( params[ j ].p_func != NULL ) {
+                       cc = (*params[ j ].p_func)( niface, &argv[ i + 1 ] );
+                       if (cc < 0) 
+                         goto read_conf_err;
+                       break;
+                   }
+               }
+           }
+           if ( j >= sizeof( params ) / sizeof( params[ 0 ] )) {
+               fprintf( stderr, "%s: attribute not found.\n", argv[ i ] );
+               goto read_conf_err;
+           }
+       }
+
+       for ( iface = interfaces; iface; iface = iface->i_next ) {
+           if ( strcmp( niface->i_name, iface->i_name ) == 0 &&
+                   ((( niface->i_flags & iface->i_flags &
+                   ( IFACE_PHASE1|IFACE_PHASE2 )) != 0 ) ||
+                   niface->i_flags == 0 || iface->i_flags == 0 )) {
+               break;
+           }
+       }
+       if ( iface ) {  /* Already have this interface and phase */
+           fprintf( stderr, "%s already configured!\n", niface->i_name );
+           goto read_conf_err;
+       }
+
+       if ( interfaces == NULL ) {
+           interfaces = niface;
+       } else {
+           for ( iface = interfaces; iface->i_next; iface = iface->i_next )
+               ;
+           iface->i_next = niface;
+       }
+       niface->i_next = NULL;
+    }
+
+#ifndef __svr4__
+    close( s );
+#endif
+
+    fclose( conf );
+
+    /*
+     * Note: we've added lo0 to the interface list previously, so we must
+     * have configured more than one interface...
+     */
+    for ( iface = interfaces, cc = 0; iface; iface = iface->i_next, cc++ )
+       ;
+    if ( cc >= IFBASE ) {
+       return( 0 );
+    } else {
+       return( -1 );
+    }
+
+read_conf_err:
+#ifndef __svr4__
+    close(s);
+#endif
+    fclose(conf);
+    return -1;
+}
+
+/*ARGSUSED*/
+router( iface, av )
+    struct interface   *iface;
+    char               **av;
+{
+    /* make sure "-router" and "-dontroute" aren't both on the same line. */
+    if (iface->i_flags & IFACE_DONTROUTE) {
+       fprintf( stderr, "Can't specify both -router and -dontroute.\n");
+       return -1;
+    }
+
+    /*
+     * Check to be sure "-router" is before "-zone".
+     */
+    if ( iface->i_czt ) {
+       fprintf( stderr, "Must specify -router before -zone.\n");
+       return -1;
+    }
+
+    /* -router also implies -seed */
+    iface->i_flags |= IFACE_RSEED | IFACE_SEED | IFACE_ISROUTER;
+    return( 1 );
+}
+
+/*ARGSUSED*/
+dontroute( iface, av )
+    struct interface   *iface;
+    char               **av;
+{
+    /* make sure "-router" and "-dontroute" aren't both on the same line. */
+    if (iface->i_flags & IFACE_RSEED) {
+       fprintf( stderr, "Can't specify both -router and -dontroute.\n");
+       return -1;
+    }
+
+    iface->i_flags |= IFACE_DONTROUTE;
+    return( 1 );
+}
+
+/*ARGSUSED*/
+seed( iface, av )
+    struct interface   *iface;
+    char               **av;
+{
+    /*
+     * Check to be sure "-seed" is before "-zone". we keep the old
+     * semantics of just ignoring this in a routerless world.
+     */
+    if ( iface->i_czt ) {
+       fprintf( stderr, "Must specify -seed before -zone(%s).\n",
+                iface->i_czt->zt_name);
+       return -1;
+    }
+
+    iface->i_flags |= IFACE_SEED;
+    return( 1 );
+}
+
+phase( iface, av )
+    struct interface   *iface;
+    char               **av;
+{
+    int                        n;
+    char               *pnum;
+
+    if (( pnum = av[ 0 ] ) == NULL ) {
+       fprintf( stderr, "No phase.\n" );
+       return -1;
+    }
+
+    switch ( n = atoi( pnum )) {
+    case 1 :
+       iface->i_flags |= IFACE_PHASE1;
+       break;
+
+    case 2 :
+       iface->i_flags |= IFACE_PHASE2;
+       break;
+
+    default :
+       fprintf( stderr, "No phase %d.\n", n );
+       return -1;
+    }
+    return( 2 );
+}
+
+net( iface, av )
+    struct interface   *iface;
+    char               **av;
+{
+    char               *nrange;
+    char               *stop;
+    int                        net;
+
+    if (( nrange = av[ 0 ] ) == NULL ) {
+       fprintf( stderr, "No network.\n" );
+       return -1;
+    }
+
+    if (( stop = strchr( nrange, '-' )) != 0 ) {
+       stop++;
+    }
+    net = atoi( nrange );
+    if ( net < 0 || net >= 0xffff ) {
+       fprintf( stderr, "Bad network: %d\n" );
+       return -1;
+    }
+
+    if ( iface->i_rt == NULL && ( iface->i_rt = newrt(iface)) == NULL ) {
+       perror( "newrt" );
+       return -1;
+    }
+
+    if ( iface->i_flags & IFACE_PHASE1 ) {
+       if ( stop != 0 ) {
+           fprintf( stderr, "Phase 1 doesn't use an address range.\n" );
+           return -1;
+       }
+       if ( iface->i_caddr.sat_addr.s_net != ATADDR_ANYNET &&
+               ntohs( iface->i_caddr.sat_addr.s_net ) != net ) {
+           fprintf( stderr, "Net-range (%u) doesn't match net %u.\n",
+                   net, ntohs( iface->i_caddr.sat_addr.s_net ));
+           return -1;
+       }
+       iface->i_rt->rt_firstnet = iface->i_rt->rt_lastnet = htons( net );
+    } else if ( iface->i_flags & IFACE_PHASE2 ) {
+       iface->i_rt->rt_firstnet = htons( net );
+       if ( stop != 0 ) {
+           net = atoi( stop );
+           if ( net < 0 || net >= 0xffff ) {
+               fprintf( stderr, "Bad network: %d\n" );
+               return -1;
+           }
+       }
+       iface->i_rt->rt_lastnet = htons( net );
+       if ( iface->i_caddr.sat_addr.s_net != ATADDR_ANYNET &&
+               ( ntohs( iface->i_rt->rt_firstnet ) >
+               ntohs( iface->i_caddr.sat_addr.s_net ) ||
+               ntohs( iface->i_rt->rt_lastnet ) <
+               ntohs( iface->i_caddr.sat_addr.s_net ))) {
+           fprintf( stderr, "Net-range (%u-%u) doesn't contain net (%u).\n",
+                   ntohs( iface->i_rt->rt_firstnet ),
+                   ntohs( iface->i_rt->rt_lastnet ),
+                   ntohs( iface->i_caddr.sat_addr.s_net ));
+           return -1;
+       }
+       if ( iface->i_rt->rt_firstnet != iface->i_rt->rt_lastnet ) {
+           iface->i_rt->rt_flags |= RTMPTAB_EXTENDED;
+       }
+    } else {
+       fprintf( stderr, "Must specify phase before networks.\n" );
+       return -1;
+    }
+    return( 2 );
+}
+
+addr( iface, av )
+    struct interface   *iface;
+    char               **av;
+{
+    if ( av[ 0 ] == NULL ) {
+       fprintf( stderr, "No address.\n" );
+       return -1;
+    }
+    if ( atalk_aton( av[ 0 ], &iface->i_caddr.sat_addr ) == 0 ) {
+       fprintf( stderr, "Bad address, %s\n", av[ 0 ] );
+       return -1;
+    }
+
+    if ( iface->i_rt ) {
+       if ( ntohs( iface->i_rt->rt_firstnet ) >
+               ntohs( iface->i_caddr.sat_addr.s_net ) ||
+               ntohs( iface->i_rt->rt_lastnet ) <
+               ntohs( iface->i_caddr.sat_addr.s_net )) {
+           fprintf( stderr, "Net (%u) not in net-range (%u-%u).\n",
+                   ntohs( iface->i_caddr.sat_addr.s_net ),
+                   ntohs( iface->i_rt->rt_firstnet ),
+                   ntohs( iface->i_rt->rt_lastnet ));
+           return -1;
+       }
+    } else {
+       if (( iface->i_rt = newrt(iface)) == NULL ) {
+           perror( "newrt" );
+           return -1;
+       }
+       iface->i_rt->rt_firstnet = iface->i_rt->rt_lastnet =
+               iface->i_caddr.sat_addr.s_net;
+    }
+
+    return( 2 );
+}
+
+zone( iface, av )
+    struct interface   *iface;
+    char               **av;
+{
+    struct ziptab      *zt;
+    char               *zname;
+
+    if (( zname = av[ 0 ] ) == NULL ) {
+       fprintf( stderr, "No zone.\n" );
+       return -1;
+    }
+
+    /*
+     * Only process "-zone" if this interface has "-seed".  We keep our
+     * list of configured zones in the interface structure.  Then we can
+     * check that the network has given us good zones.
+     */
+    if ( iface->i_flags & IFACE_SEED ) {
+        if ( iface->i_rt == NULL ) {
+           fprintf( stderr, "Must specify net-range before zones.\n" );
+           return -1;
+        }
+       if (( zt = newzt( strlen( zname ), zname )) == NULL ) {
+           perror( "newzt" );
+           return -1;
+       }
+       if ( iface->i_czt == NULL ) {
+           iface->i_czt = zt;
+       } else {
+           zt->zt_next = iface->i_czt->zt_next;
+           iface->i_czt->zt_next = zt;
+       }
+    }
+    return( 2 );
+}
+
+/*
+ * Get the configuration from the kernel. Only called if there's no
+ * configuration.
+ */
+getifconf()
+{
+    struct interface   *iface, *niface;
+    struct ifreq        ifr;
+    char                **start, **list;
+    int                        s;
+
+    if (( s = socket( AF_APPLETALK, SOCK_DGRAM, 0 )) < 0 ) {
+       perror( "socket" );
+       return -1;
+    }
+
+    start = list = getifacelist();
+    while (list && *list) {
+        strncpy(ifr.ifr_name, *list, sizeof(ifr.ifr_name));
+       list++;
+
+       if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0)
+         continue;
+
+       if (ifr.ifr_flags & (IFF_LOOPBACK | IFF_POINTOPOINT | IFF_SLAVE))
+         continue;
+
+       if ((ifr.ifr_flags & IFF_UP) == 0)
+         continue;
+
+       /* for devices that don't support appletalk */
+       if (ioctl(s, SIOCGIFADDR, &ifr) < 0 && (errno == ENODEV))
+         continue;
+
+       for ( iface = interfaces; iface; iface = iface->i_next ) {
+           if ( strcmp( iface->i_name, ifr.ifr_name ) == 0 ) {
+               break;
+           }
+       }
+       if ( iface ) {  /* Already have this interface name */
+           continue;
+       }
+
+
+#ifdef IFF_MULTICAST
+       if ((ifr.ifr_flags & IFF_MULTICAST) == 0)
+         fprintf(stderr, "%s: multicast may not work correctly.\n",
+                 ifr.ifr_name);
+#endif
+
+       if (addmulti(ifr.ifr_name, NULL) < 0) {
+         fprintf(stderr, "%s: disabled.\n", ifr.ifr_name);
+         continue;
+       }
+       
+       if (( niface = newiface( ifr.ifr_name )) == NULL ) {
+           perror( "newiface" );
+           close(s);
+           freeifacelist(start);
+           return -1;
+       }
+       /*
+        * Could try to get the address from the kernel...
+        */
+
+       if ( interfaces == NULL ) {
+           interfaces = niface;
+       } else {
+           for ( iface = interfaces; iface->i_next; iface = iface->i_next )
+               ;
+           iface->i_next = niface;
+       }
+       niface->i_next = NULL;
+    }
+    freeifacelist(start);
+    (void)close( s );
+    return( 0 );
+}
+
+/*
+ * Allocate a new interface structure.  Centralized here so we can change
+ * the interface structure and have it updated nicely.
+ */
+    struct interface *
+newiface( name )
+    const char         *name;
+{
+    struct interface   *niface;
+
+    if (( niface = (struct interface *)calloc(1, sizeof( struct interface )))
+           == NULL ) {
+       return( NULL );
+    }
+    strncpy( niface->i_name, name, sizeof(niface->i_name));
+#ifdef BSD4_4
+    niface->i_addr.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+    niface->i_addr.sat_family = AF_APPLETALK;
+#ifdef BSD4_4
+    niface->i_caddr.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+    niface->i_caddr.sat_family = AF_APPLETALK;
+    return( niface );
+}
+
+#ifdef __svr4__
+    int
+plumb()
+{
+    struct interface   *iface;
+    char               device[ MAXPATHLEN + 1], *p;
+    int                        fd, ppa;
+
+    for ( iface = interfaces; iface != NULL; iface = iface->i_next ) {
+       if ( strcmp( iface->i_name, LOOPIFACE ) == 0 ) {
+           continue;
+       }
+
+       strcpy( device, "/dev/" );
+       strcat( device, iface->i_name );
+       if (( p = strpbrk( device, "0123456789" )) == NULL ) {
+           syslog( LOG_ERR, "plumb: invalid device: %s", device );
+           return -1;
+       }
+       ppa = atoi( p );
+       *p = '\0';
+
+       if (( fd = open( device, O_RDWR, 0 )) < 0 ) {
+           syslog( LOG_ERR, "%s: %m", device );
+           return -1;
+       }
+       if ( ioctl( fd, I_PUSH, "ddp" ) < 0 ) {
+           syslog( LOG_ERR, "I_PUSH: %m" );
+           close(fd);
+           return -1;
+       }
+       if ( ioctl( fd, IF_UNITSEL, ppa ) < 0 ) {
+           syslog( LOG_ERR, "IF_UNITSEL: %m" );
+           close(fd);
+           return -1;
+       }
+
+       /* configure multicast. */
+       if (addmulti(iface->i_name, NULL) < 0) {
+         perror(iface->i_name);
+         fprintf(stderr,"Can't configure multicast.\n");
+         close(fd);
+         return -1;
+       }
+
+       syslog( LOG_INFO, "plumbed %s%d", device, ppa );
+    }
+
+    return( 0 );
+}
+#endif __svr4__
diff --git a/etc/atalkd/gate.h b/etc/atalkd/gate.h
new file mode 100644 (file)
index 0000000..ca09fb7
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+struct gate {
+    struct gate                *g_next,
+                       *g_prev;
+    int                        g_state;
+    struct interface   *g_iface;
+    struct rtmptab     *g_rt;
+    struct sockaddr_at g_sat;
+};
diff --git a/etc/atalkd/interface.h b/etc/atalkd/interface.h
new file mode 100644 (file)
index 0000000..3c68208
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1990,1992 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#ifndef ATALKD_INTERFACE_H
+#define ATALKD_INTERFACE_H 1
+
+#include <sys/cdefs.h>
+
+struct interface {
+    struct interface   *i_next;
+    char               i_name[ IFNAMSIZ ];
+    int                        i_flags;
+    int                        i_time;
+    int                 i_group; /* for isolated appletalk domains */
+    struct sockaddr_at i_addr;
+    struct sockaddr_at i_caddr;
+    struct ziptab      *i_czt;
+    struct rtmptab     *i_rt;
+    struct gate                *i_gate;
+    struct atport      *i_ports;
+};
+
+#define IFACE_PHASE1   0x001
+#define IFACE_PHASE2   0x002
+#define IFACE_LOOPBACK 0x004           /* is the loopback interface */
+#define IFACE_SEED     0x008           /* act as seed */
+#define IFACE_ADDR     0x010           /* has an address set */
+#define IFACE_CONFIG   0x020           /* has been configured */
+#define IFACE_NOROUTER 0x040           /* no router on interface */
+#define IFACE_LOOP     0x080           /* has a loopback route */
+#define IFACE_RSEED     0x100           /* almost the same as seed. RSEED
+                                          says that we should try to 
+                                          do routing. */
+#define IFACE_DONTROUTE 0x200           /* don't route this interface */
+#define IFACE_ISROUTER  0x400           /* act as a router. */
+
+#define UNSTABLE       2
+#define STABLE         0
+#define STABLEANYWAY   -2
+
+#define IFBASE         2       /* base number of interfaces */
+
+#ifdef linux
+#define LOOPIFACE      "lo"
+#else linux
+#define LOOPIFACE      "lo0"
+#endif linux
+
+extern struct interface        *interfaces;
+extern struct interface        *ciface;
+struct interface       *newiface __P((const char *));
+
+#endif
diff --git a/etc/atalkd/list.h b/etc/atalkd/list.h
new file mode 100644 (file)
index 0000000..8d05b34
--- /dev/null
@@ -0,0 +1,10 @@
+/*
+ * Copyright (c) 1990,1992 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+struct list {
+    void       *l_data;
+    struct list        *l_next,
+               *l_prev;
+};
diff --git a/etc/atalkd/main.c b/etc/atalkd/main.c
new file mode 100644 (file)
index 0000000..a05e908
--- /dev/null
@@ -0,0 +1,1493 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#if defined( sun ) && defined( __svr4__ )
+#include </usr/ucbinclude/sys/file.h>
+#else sun __svr4__
+#include <sys/file.h>
+#endif sun __svr4__
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <net/if.h>
+#include <net/route.h>
+
+#include <signal.h>
+#include <syslog.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <atalk/compat.h>
+#include <atalk/zip.h>
+#include <atalk/rtmp.h>
+#include <atalk/ddp.h>
+#include <atalk/atp.h>
+#include <atalk/paths.h>
+#include <atalk/util.h>
+
+#ifdef __svr4__
+#include <sys/sockio.h>
+#include <termios.h>
+#endif __svr4__
+
+#include "interface.h"
+#include "gate.h"
+#include "list.h"
+#include "rtmp.h"
+#include "zip.h"
+#include "atserv.h"
+
+/* FIXME/SOCKLEN_T: socklen_t is a unix98 feature */
+#ifndef SOCKLEN_T
+#define SOCKLEN_T unsigned int
+#endif
+
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(x) ((x).w_retcode)
+#endif WEXITSTATUS
+
+/* linux has a special ioctl for appletalk device destruction.  as of
+ * 2.1.57, SIOCDIFADDR works w/ linux. okay, we need to deal with the
+ * fact that SIOCDIFADDR may be defined on linux despite the fact that
+ * it doesn't work. */
+#if !defined(SIOCDIFADDR) && defined(SIOCATALKDIFADDR)
+#define SIOCDIFADDR SIOCATALKDIFADDR
+#endif
+
+#define elements(a)    (sizeof(a)/sizeof((a)[0]))
+
+#define PKTSZ  1024
+
+extern int     rtmp_packet();
+extern int     nbp_packet();
+extern int     aep_packet();
+extern int     zip_packet();
+
+int            rtfd;
+
+struct atserv  atserv[] = {
+    { "rtmp",          1,      rtmp_packet },          /* 0 */
+    { "nbp",           2,      nbp_packet },           /* 1 */
+    { "echo",          4,      aep_packet },           /* 2 */
+    { "zip",           6,      zip_packet },           /* 3 */
+};
+int            atservNATSERV = elements( atserv );
+
+struct interface       *interfaces = NULL, *ciface = NULL;
+
+int            debug = 0, quiet = 0, chatty = 0;
+char           *configfile = NULL;
+int            ziptimeout = 0, transition = 0;
+int            stabletimer, stable = 0, newrtmpdata = 0, noparent = 0;
+static int     ninterfaces;
+int            defphase = IFACE_PHASE2;
+int            nfds = 0;
+fd_set         fds;
+char           Packet[ PKTSZ ];
+char           *version = VERSION;
+static char     *pidfile = _PATH_ATALKDLOCK;
+
+
+/* this is the messiest of the bunch as atalkd can exit pretty much
+ * everywhere. we delete interfaces here instead of in as_down. */
+static void atalkd_exit(const int i)
+{
+#ifdef SIOCDIFADDR
+  struct interface *iface;
+
+  for (iface = interfaces; iface; iface = iface->i_next) {
+    if (ifconfig( iface->i_name, SIOCDIFADDR, &iface->i_addr)) {
+#ifdef SIOCATALKDIFADDR
+#if (SIOCDIFADDR != SIOCATALKDIFADDR)
+      if (!ifconfig(iface->i_name, SIOCATALKDIFADDR, &iface->i_addr)) 
+       continue;
+#endif
+#endif
+      syslog( LOG_ERR, "difaddr(%u.%u): %m", 
+             ntohs(iface->i_addr.sat_addr.s_net), 
+             iface->i_addr.sat_addr.s_node);
+    }
+  }
+#endif 
+
+  server_unlock(pidfile);
+  exit(i);
+}
+
+
+#if !defined( ibm032 ) && !defined( _IBMR2 )
+    void
+#endif ibm032 _IBMR2
+as_timer()
+{
+    struct sockaddr_at sat;
+    struct ziphdr      zh;
+    struct rtmp_head   rh;
+    struct rtmp_tuple  rt;
+    struct atport      *ap, *zap, *rap;
+    struct interface   *iface, *iface2;
+    struct gate                *gate, *fgate = NULL;
+    struct rtmptab     *rtmp, *frtmp;
+    struct ziptab      *zt;
+    char               *data, *end, packet[ ATP_BUFSIZ ];
+    int                        sentzipq = 0;
+    int                        n, cc;
+
+    memset(&sat, 0, sizeof( struct sockaddr_at ));
+    for ( iface = interfaces; iface; iface = iface->i_next ) {
+       if ( iface->i_flags & IFACE_LOOPBACK ) {
+           continue;
+       }
+       for ( ap = iface->i_ports; ap; ap = ap->ap_next ) {
+           if ( ap->ap_packet == zip_packet ) {
+               zap = ap;
+           }
+           if ( ap->ap_packet == rtmp_packet ) {
+               rap = ap;
+           }
+       }
+
+       if (( iface->i_flags & ( IFACE_ADDR|IFACE_CONFIG|IFACE_NOROUTER )) ==
+               IFACE_ADDR ) {
+           if ( iface->i_time < 3 ) {
+               if ( iface->i_flags & IFACE_PHASE1 ) {
+                 if (rtmp_request( iface ) < 0) {
+                     syslog(LOG_ERR, "rtmp_request: %m");
+                     atalkd_exit(1);
+                 }
+                   newrtmpdata = 1;
+               } else {
+                 if (zip_getnetinfo( iface ) < 0) {
+                   syslog(LOG_ERR, "zip_getnetinfo: %m");
+                   atalkd_exit(1);
+                 }
+                 sentzipq = 1;
+               }
+               iface->i_time++;
+           } else {
+               iface->i_flags |= IFACE_NOROUTER;
+               if ((iface->i_flags & IFACE_ISROUTER)) {
+                   if (( iface->i_flags & IFACE_SEED ) == 0 ) {
+                       /*
+                        * No seed info, and we've got multiple interfaces.
+                        * Wait forever.
+                        */
+                       syslog( LOG_INFO,
+                               "as_timer multiple interfaces, no seed" );
+                       syslog( LOG_INFO, "as_timer can't configure %s",
+                               iface->i_name );
+                       syslog( LOG_INFO, "as_timer waiting for router" );
+                       iface->i_time = 0;
+                       continue;
+                   } else {
+                       /*
+                        * Complete configuration for iface, and boot next
+                        * interface.
+                        */
+                       iface->i_flags |= IFACE_CONFIG;
+                       for ( zt = iface->i_czt; zt; zt = zt->zt_next ) {
+                           if (addzone( iface->i_rt, zt->zt_len, 
+                                        zt->zt_name) < 0) {
+                             syslog(LOG_ERR, "addzone: %m");
+                             atalkd_exit(1);
+                           }
+                       }
+                       if ( iface->i_rt->rt_zt ) {
+                           iface->i_rt->rt_flags &= ~RTMPTAB_ZIPQUERY;
+                           iface->i_rt->rt_flags |= RTMPTAB_HASZONES;
+                       }
+                       if ( iface->i_flags & IFACE_PHASE1 ) {
+                           syslog( LOG_INFO,
+                                   "as_timer configured %s phase 1 from seed",
+                                   iface->i_name );
+                           setaddr( iface, IFACE_PHASE1,
+                                   iface->i_caddr.sat_addr.s_net,
+                                   iface->i_addr.sat_addr.s_node,
+                                   iface->i_caddr.sat_addr.s_net,
+                                   iface->i_caddr.sat_addr.s_net );
+                       } else {
+                           syslog( LOG_INFO,
+                                   "as_timer configured %s phase 2 from seed",
+                                   iface->i_name );
+                       }
+
+                       if ( looproute( iface, RTMP_ADD )) { /* -1 or 1 */
+                           syslog( LOG_ERR,
+                                   "as_timer: can't route %u.%u to loop: %m",
+                                   ntohs( iface->i_addr.sat_addr.s_net ),
+                                   iface->i_addr.sat_addr.s_node );
+                           atalkd_exit( 1 );
+                       }
+                       if ( iface == ciface ) {
+                           ciface = ciface->i_next;
+                           bootaddr( ciface );
+                       }
+                   }
+               } else {
+                   /*
+                    * Configure for no router operation.  Wait for a route
+                    * to become available in rtmp_packet().
+                    */
+                   syslog( LOG_INFO, "config for no router" );
+                     
+                   if ( iface->i_flags & IFACE_PHASE2 ) {
+                       iface->i_rt->rt_firstnet = 0;
+                       iface->i_rt->rt_lastnet = htons( STARTUP_LASTNET );
+                       setaddr( iface, IFACE_PHASE2,
+                               iface->i_addr.sat_addr.s_net,
+                               iface->i_addr.sat_addr.s_node,
+                               0, htons( STARTUP_LASTNET ));
+                   }
+                   if ( looproute( iface, RTMP_ADD ) ) { /* -1 or 1 */
+                       syslog( LOG_ERR,
+                               "as_timer: can't route %u.%u to loopback: %m",
+                               ntohs( iface->i_addr.sat_addr.s_net ),
+                               iface->i_addr.sat_addr.s_node );
+                       atalkd_exit( 1 );
+                   }
+
+                   if ( iface == ciface ) {
+                     ciface = ciface->i_next;
+                     bootaddr( ciface );
+                   }
+               }
+           }
+       }
+
+       for ( gate = iface->i_gate; gate; gate = gate->g_next ) {
+           if ( fgate ) {
+               free( (caddr_t)fgate );
+               fgate = NULL;
+           }
+
+           n = 0;
+           data = packet + 1 + sizeof( struct ziphdr );
+           end = packet + sizeof( packet );
+
+           sat = gate->g_sat;
+           sat.sat_port = zap->ap_port;
+
+           /*
+            * Perform timeouts on routers.  If we've only got one
+            * interface, we'll use these timeouts to decide that
+            * our zone has gone away.
+            */
+           if ( ++gate->g_state >= RTMPTAB_BAD ) {
+               syslog( LOG_INFO, "as_timer gateway %u.%u down",
+                       ntohs( gate->g_sat.sat_addr.s_net ),
+                       gate->g_sat.sat_addr.s_node );
+               rtmp = gate->g_rt;
+               while ( rtmp ) {
+                   frtmp = rtmp->rt_next;
+                   if ( rtmp->rt_hops == RTMPHOPS_POISON ||
+                           rtmp->rt_iprev == 0 ) {
+                       rtmp_free( rtmp );
+                   } else {
+                       rtmp->rt_hops = RTMPHOPS_POISON;
+                       if ((cc = rtmp_replace( rtmp )) < 0) {
+                         syslog(LOG_ERR, "rtmp_replace: %m");
+                         atalkd_exit(1);
+                       }
+                       if (cc) {
+                           gate->g_state = rtmp->rt_state = RTMPTAB_GOOD;
+                       }
+                   }
+                   rtmp = frtmp;
+               }
+               if ( gate->g_rt == 0 ) {
+                   if ( gate->g_prev == 0 ) {
+                       gate->g_iface->i_gate = gate->g_next;
+                   } else {
+                       gate->g_prev->g_next = gate->g_next;
+                   }
+                   if ( gate->g_next != 0 ) {
+                       gate->g_next->g_prev = gate->g_prev;
+                   }
+                   fgate = gate;       /* can't free here, just mark it */
+               }
+               /*
+                * If this is the last router on the only interface,
+                * reconfigure our netrange.  By marking the interface
+                * as having no router, we will notice when a router
+                * comes back up.
+                *
+                * XXX: actually, we always reconfigure an interface
+                * if we're not a seed router.
+                */
+
+               if ( gate->g_iface->i_gate == 0 && 
+                    ((iface->i_flags & IFACE_SEED) == 0)) {
+                   gate->g_iface->i_flags |= IFACE_NOROUTER;
+                   gate->g_iface->i_flags &= ~IFACE_CONFIG;
+
+                   /* get rid of any zones associated with this iface */
+                   if (gate->g_iface->i_rt->rt_zt) {
+                     rtmp_delzonemap(gate->g_iface->i_rt);
+                     gate->g_iface->i_rt->rt_flags &= ~RTMPTAB_HASZONES;
+                   }
+
+                   syslog( LOG_INFO, "as_timer last gateway down" );
+
+                   /* Set netrange to 0-fffe.  */
+                   if ( gate->g_iface->i_flags & IFACE_PHASE2 ) {
+                       gate->g_iface->i_rt->rt_firstnet = 0;
+                       gate->g_iface->i_rt->rt_lastnet =
+                               htons( STARTUP_LASTNET );
+                       setaddr( iface, IFACE_PHASE2,
+                               iface->i_addr.sat_addr.s_net,
+                               iface->i_addr.sat_addr.s_node,
+                               0, htons( STARTUP_LASTNET ));
+                   }
+               }
+               continue;
+           }
+
+           /*
+            * If we don't have a zone for our interface yet, ask for
+            * it from any router (all routers) on the interface.
+            */
+           if (( iface->i_rt->rt_flags & RTMPTAB_HASZONES ) == 0 ) {
+               iface->i_rt->rt_flags |= RTMPTAB_ZIPQUERY;
+               memcpy( data, &iface->i_rt->rt_firstnet, sizeof( u_short ));
+               data += sizeof( u_short );
+               n++;
+           }
+
+           rtmp = gate->g_rt;
+           while ( rtmp ) {
+               /*
+                * Delete old routing tuples.
+                */
+               if ( rtmp->rt_state != RTMPTAB_PERM ) {
+                   rtmp->rt_state++;
+               }
+
+               /*
+                * We've not been updated for this route in a while.  If
+                * it's not in use, go ahead and remove it.  If it is in
+                * use, mark the route as down (POISON), and look for a
+                * better route.  If one is found, delete this route and use
+                * the new one.  If it's not found, mark the route as GOOD
+                * (so we'll propogate our poison) and delete it the next
+                * time it becomes BAD.
+                */
+               if ( rtmp->rt_state >= RTMPTAB_BAD ) {
+                   frtmp = rtmp->rt_next;
+                   if ( rtmp->rt_iprev == 0 ) {        /* not in use */
+                       rtmp_free( rtmp );
+                   } else {                            /* in use */
+                       if ( rtmp->rt_hops == RTMPHOPS_POISON ) {
+                           rtmp_free( rtmp );
+                       } else {
+                           rtmp->rt_hops = RTMPHOPS_POISON;
+                           if ((cc = rtmp_replace( rtmp )) < 0) {
+                               syslog(LOG_ERR, "rtmp_replace: %m");
+                               atalkd_exit(1);
+                           }
+                           if (cc)
+                               rtmp->rt_state = RTMPTAB_GOOD;
+                       }
+                   }
+                   rtmp = frtmp;
+                   continue;
+               }
+
+               /*
+                * Do ZIP lookups.
+                */
+               if ( rtmp->rt_iprev &&
+                       ( rtmp->rt_flags & RTMPTAB_HASZONES ) == 0 ) {
+                   if ( data + sizeof( u_short ) > end || n == 255 ) {
+                       /* send what we've got */
+                       zh.zh_op = ZIPOP_QUERY;
+                       zh.zh_count = n;
+                       cc = data - packet;
+                       data = packet;
+                       *data++ = DDPTYPE_ZIP;
+                       memcpy( data, &zh, sizeof( struct ziphdr ));
+
+                       if ( sendto( zap->ap_fd, packet, cc, 0,
+                               (struct sockaddr *)&sat,
+                               sizeof( struct sockaddr_at )) < 0 ) {
+                           syslog( LOG_ERR, "as_timer sendto: %m" );
+                       }
+                       sentzipq = 1;
+
+                       n = 0;
+                       data = packet + 1 + sizeof( struct ziphdr );
+                       end = packet + sizeof( packet );
+                   }
+
+                   /*
+                    * rt_nzq is number of ZIP Queries we've issued for a
+                    * given netrange.  If we've got ziptimeout on, we
+                    * will only ask 3 times for any given netrange.
+                    * Interestingly enough, since rt_nzq is a u_char,
+                    * it will overflow after a while.  This means we will
+                    * periodically ask for nets that we've decided not to
+                    * ask about, and warn that we can't get it's zone.
+                    */
+                   if ( rtmp->rt_nzq++ == 3 ) {
+                       syslog( LOG_INFO, "as_timer can't get zone for %u",
+                               ntohs( rtmp->rt_firstnet ));
+                   }
+                   if ( rtmp->rt_nzq > 3 ) {
+                       if ( ziptimeout ) {
+                           rtmp = rtmp->rt_next;
+                           continue;
+                       }
+                   } else {
+                       sentzipq = 1;
+                   }
+                   rtmp->rt_flags |= RTMPTAB_ZIPQUERY;
+                   memcpy( data, &rtmp->rt_firstnet, sizeof( u_short ));
+                   data += sizeof( u_short );
+                   n++;
+               }
+               rtmp = rtmp->rt_next;
+           }
+
+           /* send what we've got */
+           if ( n > 0 ) {
+               zh.zh_op = ZIPOP_QUERY;
+               zh.zh_count = n;
+               cc = data - packet;
+               data = packet;
+               *data++ = DDPTYPE_ZIP;
+               memcpy( data, &zh, sizeof( struct ziphdr ));
+
+               if ( sendto( zap->ap_fd, packet, cc, 0, (struct sockaddr *)&sat,
+                       sizeof( struct sockaddr_at )) < 0 ) {
+                   syslog( LOG_ERR, "as_timer sendto: %m" );
+               }
+           }
+       }
+       if ( fgate ) {
+           free( (caddr_t)fgate );
+           fgate = NULL;
+       }
+
+       /*
+        * Send RTMP broadcasts if we have multiple interfaces or our 
+        * interface is configured as a router.  
+        */
+       if ((iface->i_flags & IFACE_ISROUTER)) {
+#ifdef BSD4_4
+           sat.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+           sat.sat_family = AF_APPLETALK;
+           sat.sat_addr.s_net = ATADDR_ANYNET;
+           sat.sat_addr.s_node = ATADDR_BCAST;
+           sat.sat_port = rap->ap_port;
+
+           data = packet;
+           end = data + sizeof( packet );
+           *data++ = DDPTYPE_RTMPRD;
+           rh.rh_net = iface->i_addr.sat_addr.s_net;
+           rh.rh_nodelen = 8;
+           rh.rh_node = iface->i_addr.sat_addr.s_node;
+           memcpy( data, &rh, sizeof( struct rtmp_head ));
+           data += sizeof( struct rtmp_head );
+           n = 0;
+
+
+           if ( iface->i_flags & IFACE_PHASE1 ) {
+               rt.rt_net = 0;
+               rt.rt_dist = 0x82;
+               memcpy( data, &rt, SZ_RTMPTUPLE );
+               data += SZ_RTMPTUPLE;
+           } else {
+               rt.rt_net = iface->i_rt->rt_firstnet;
+               rt.rt_dist = 0x80;
+               memcpy( data, &rt, SZ_RTMPTUPLE );
+               data += SZ_RTMPTUPLE;
+
+               rt.rt_net = iface->i_rt->rt_lastnet;
+               rt.rt_dist = 0x82;
+               memcpy( data, &rt, SZ_RTMPTUPLE );
+               data += SZ_RTMPTUPLE;
+           }
+
+           for ( iface2 = interfaces; iface2; iface2 = iface2->i_next ) {
+
+             /* XXX: there used to be a bit checking against iface ==
+                iface2. also, we don't want to send an rtmp broadcast
+                to an interface that doesn't want it.  */
+               if ((( iface2->i_flags & IFACE_CONFIG ) == 0) ||
+                   ((iface2->i_flags & IFACE_ISROUTER) == 0)) {
+                   continue;
+               }
+               /*
+                * Fill in tuples.  Always send the same thing, regardless
+                * of the phase of the destination.  Routers who don't
+                * understand extended rtmp packets will toss extended
+                * tuples because their distance will have the high bit set.
+                */
+               for ( rtmp = iface2->i_rt; rtmp; rtmp = rtmp->rt_inext ) {
+                   /* don't broadcast routes we have no zone for */
+                   if ( rtmp->rt_zt == 0 ||
+                           ( rtmp->rt_flags & RTMPTAB_ZIPQUERY ) ||
+                           ( rtmp->rt_flags & RTMPTAB_HASZONES ) == 0 ) {
+                       continue;
+                   }
+
+                   if ((( rtmp->rt_flags & RTMPTAB_EXTENDED ) &&
+                           data + 2 * SZ_RTMPTUPLE > end ) ||
+                           data + SZ_RTMPTUPLE > end ) {
+                       if ( sendto( rap->ap_fd, packet, data - packet, 0,
+                               (struct sockaddr *)&sat,
+                               sizeof( struct sockaddr_at )) < 0 ) {
+                           syslog( LOG_ERR, "as_timer sendto %u.%u (%u): %m",
+                                   ntohs( sat.sat_addr.s_net ),
+                                   sat.sat_addr.s_node,
+                                   ntohs( iface->i_rt->rt_firstnet ));
+                       }
+
+                       if ( iface->i_flags & IFACE_PHASE2 ) {
+                           data = packet + 1 + sizeof( struct rtmp_head ) +
+                                   2 * SZ_RTMPTUPLE;
+                       } else {
+                           data = packet + 1 + sizeof( struct rtmp_head ) +
+                                   SZ_RTMPTUPLE;
+                       }
+                       n = 0;
+                   }
+
+                   rt.rt_net = rtmp->rt_firstnet;
+                   rt.rt_dist = rtmp->rt_hops;
+                   if ( rtmp->rt_flags & RTMPTAB_EXTENDED ) {
+                       rt.rt_dist |= 0x80;
+                   }
+                   memcpy( data, &rt, SZ_RTMPTUPLE );
+                   data += SZ_RTMPTUPLE;
+
+                   if ( rtmp->rt_flags & RTMPTAB_EXTENDED ) {
+                       rt.rt_net = rtmp->rt_lastnet;
+                       rt.rt_dist = 0x82;
+                       memcpy( data, &rt, SZ_RTMPTUPLE );
+                       data += SZ_RTMPTUPLE;
+                   }
+                   n++;
+               }
+           }
+
+           /* send rest */
+           if ( n ) {
+               if ( sendto( rap->ap_fd, packet, data - packet, 0,
+                       (struct sockaddr *)&sat,
+                       sizeof( struct sockaddr_at )) < 0 ) {
+                   syslog( LOG_ERR, "as_timer sendto %u.%u (%u): %m",
+                           ntohs( sat.sat_addr.s_net ),
+                           sat.sat_addr.s_node,
+                           ntohs( iface->i_rt->rt_firstnet ));
+               }
+           }
+       }
+    }
+
+    /*
+     * Check if we're stable.  Each time we configure an interface, we
+     * sent stabletimer to UNSTABLE.  If stabletimer ever gets to
+     * STABLEANYWAY, we give up and decide to "be" stable anyway.
+     * Normally, we wait for stabletimer get <= STABLE with no new rtmp
+     * data and all zip data complete.
+     */
+    if ( !stable ) {
+       if ( stabletimer <= STABLE && !newrtmpdata && !sentzipq ) {
+           /* write out config file */
+           stable = 1;
+           writeconf( configfile );
+       } else {
+           if ( stabletimer-- <= STABLEANYWAY ) {
+               stable = 1;
+           }
+       }
+       newrtmpdata = 0;
+
+       if ( stable && !noparent ) {
+           noparent = 1;
+           syslog( LOG_INFO, "ready %d/%d/%d", stabletimer, newrtmpdata,
+                   sentzipq );
+           if ( !debug ) {
+               /*
+                * Seems like we could get here more than once...
+                */
+               if ( kill( getpid(), SIGSTOP ) < 0 ) {
+                   syslog( LOG_ERR, "as_timer: kill-self failed!" );
+                   atalkd_exit( 1 );
+               }
+           }
+       }
+    }
+
+#ifdef DEBUG
+    consistency();
+#endif DEBUG
+}
+
+#ifdef DEBUG
+/*
+* Consistency check...
+*/
+consistency()
+{
+    struct rtmptab     *rtmp;
+    struct list                *lr, *lz;
+    struct ziptab      *zt;
+
+    for ( zt = ziptab; zt; zt = zt->zt_next ) {
+       for ( lr = zt->zt_rt; lr; lr = lr->l_next ) {
+           rtmp = (struct rtmptab *)lr->l_data;
+           if ( rtmp->rt_iprev == 0 && rtmp->rt_gate != 0 ) {
+               syslog( LOG_ERR, "%.*s has %u-%u (unused)\n",
+                       zt->zt_len, zt->zt_name, ntohs( rtmp->rt_firstnet ),
+                       ntohs( rtmp->rt_lastnet ));
+               atalkd_exit(1);
+           }
+           for ( lz = rtmp->rt_zt; lz; lz = lz->l_next ) {
+               if ( zt == (struct ziptab *)lz->l_data ) {
+                   break;
+               }
+           }
+           if ( lz == 0 ) {
+               syslog( LOG_ERR, "no map from %u-%u to %.*s\n", 
+                       ntohs( rtmp->rt_firstnet ),
+                       ntohs( rtmp->rt_lastnet ),
+                       zt->zt_len, zt->zt_name );
+               atalkd_exit(1);
+           }
+       }
+    }
+}
+#endif DEBUG
+
+#if !defined( ibm032 ) && !defined( _IBMR2 )
+    void
+#endif ibm032 _IBMR2
+as_debug()
+{
+    struct interface   *iface;
+    struct list                *l;
+    struct ziptab      *zt;
+    struct gate                *gate;
+    struct rtmptab     *rt;
+    FILE               *rtmpdebug;
+
+    if (( rtmpdebug = fopen( _PATH_ATALKDEBUG, "w" )) == NULL ) {
+       syslog( LOG_ERR, "rtmp: %m" );
+    }
+
+    for ( iface = interfaces; iface; iface = iface->i_next ) {
+       fprintf( rtmpdebug, "interface %s %u.%u ", iface->i_name,
+               ntohs( iface->i_addr.sat_addr.s_net ),
+               iface->i_addr.sat_addr.s_node );
+       if ( iface->i_flags & IFACE_PHASE1 ) {
+           putc( '1', rtmpdebug );
+       }
+       if ( iface->i_flags & IFACE_PHASE2 ) {
+           putc( '2', rtmpdebug );
+       }
+       if ( iface->i_flags & IFACE_RSEED ) {
+           putc( 'R', rtmpdebug );
+       }
+       if ( iface->i_flags & IFACE_SEED ) {
+           putc( 'S', rtmpdebug );
+       }
+       if ( iface->i_flags & IFACE_DONTROUTE ) {
+           putc( 'D', rtmpdebug );
+       }
+       if ( iface->i_flags & IFACE_ADDR ) {
+           putc( 'A', rtmpdebug );
+       }
+       if ( iface->i_flags & IFACE_CONFIG ) {
+           putc( 'C', rtmpdebug );
+       }
+       if ( iface->i_flags & IFACE_NOROUTER ) {
+           putc( 'N', rtmpdebug );
+       }
+       if ( iface->i_flags & IFACE_LOOP ) {
+           putc( 'L', rtmpdebug );
+       }
+       putc( '\n', rtmpdebug );
+
+       if ( iface->i_rt ) {
+           fprintf( rtmpdebug, "\t%u-%u ",
+                   ntohs( iface->i_rt->rt_firstnet ),
+                   ntohs( iface->i_rt->rt_lastnet ));
+           if ( iface->i_rt->rt_flags & RTMPTAB_ZIPQUERY ) {
+               putc( 'q', rtmpdebug );
+           }
+           if ( iface->i_rt->rt_flags & RTMPTAB_HASZONES ) {
+               putc( 'z', rtmpdebug );
+           }
+           if ( iface->i_rt->rt_flags & RTMPTAB_EXTENDED ) {
+               putc( 'x', rtmpdebug );
+           }
+           putc( 'i', rtmpdebug );
+           for ( l = iface->i_rt->rt_zt; l; l = l->l_next ) {
+               zt = (struct ziptab *)l->l_data;
+               fprintf( rtmpdebug, " '%.*s'", zt->zt_len, zt->zt_name );
+           }
+           fprintf( rtmpdebug, "\n" );
+       }
+
+       for ( gate = iface->i_gate; gate; gate = gate->g_next ) {
+           fprintf( rtmpdebug, "gate %u.%u %X\n",
+                   ntohs( gate->g_sat.sat_addr.s_net ),
+                   gate->g_sat.sat_addr.s_node, gate->g_state );
+           for ( rt = gate->g_rt; rt; rt = rt->rt_next ) {
+               fprintf( rtmpdebug, "\t%u-%u ", ntohs( rt->rt_firstnet ),
+                       ntohs( rt->rt_lastnet ));
+               if ( rt->rt_flags & RTMPTAB_ZIPQUERY ) {
+                   putc( 'q', rtmpdebug );
+               }
+               if ( rt->rt_flags & RTMPTAB_HASZONES ) {
+                   putc( 'z', rtmpdebug );
+               }
+               if ( rt->rt_flags & RTMPTAB_EXTENDED ) {
+                   putc( 'x', rtmpdebug );
+               }
+               if ( rt->rt_iprev ) {
+                   putc( 'i', rtmpdebug );
+               }
+               for ( l = rt->rt_zt; l; l = l->l_next ) {
+                   zt = (struct ziptab *)l->l_data;
+                   fprintf( rtmpdebug, " '%.*s'", zt->zt_len, zt->zt_name );
+               }
+               fprintf( rtmpdebug, "\n" );
+           }
+       }
+    }
+
+    fclose( rtmpdebug );
+}
+
+/*
+ * Called when SIGTERM is recieved.  Remove all routes and then exit.
+ */
+#if !defined( ibm032 ) && !defined( _IBMR2 )
+    void
+#endif ibm032 _IBMR2
+as_down()
+{
+    struct interface   *iface;
+    struct gate                *gate;
+    struct rtmptab     *rt;
+
+    for ( iface = interfaces; iface; iface = iface->i_next ) {
+       for ( gate = iface->i_gate; gate; gate = gate->g_next ) {
+           for ( rt = gate->g_rt; rt; rt = rt->rt_next ) {
+               if ( rt->rt_iprev ) {
+                   if ( gateroute( RTMP_DEL, rt ) < 0 ) {
+                       syslog( LOG_ERR, "as_down remove %u-%u failed: %m",
+                               ntohs( rt->rt_firstnet ),
+                               ntohs( rt->rt_lastnet ));
+                   }
+               }
+           }
+       }
+       if ( iface->i_flags & IFACE_LOOP ) {
+         if (looproute( iface, RTMP_DEL )) {
+           syslog( LOG_ERR, "as_down remove %s %u.%u failed: %m",
+                   iface->i_name, ntohs( iface->i_addr.sat_addr.s_net ),
+                   iface->i_addr.sat_addr.s_node );
+         }
+       }
+    }
+
+    syslog( LOG_INFO, "done" );
+    atalkd_exit( 0 );
+}
+
+main( ac, av )
+    int                ac;
+    char       **av;
+{
+    extern char         *optarg;
+    extern int          optind;
+
+    struct sockaddr_at sat;
+    struct sigaction   sv;
+    struct itimerval   it;
+    struct interface   *iface;
+    int                        status;
+    struct atport      *ap;
+    fd_set             readfds;
+    int                        i, mask, c;
+    SOCKLEN_T          fromlen;
+    char               *prog;
+;
+
+    while (( c = getopt( ac, av, "12qsdtf:P:" )) != EOF ) {
+       switch ( c ) {
+       case '1' :
+           defphase = IFACE_PHASE1;
+           break;
+
+       case '2' :
+           defphase = IFACE_PHASE2;
+           break;
+
+       case 'd' :
+           debug++;
+           break;
+
+       case 'f' :
+           configfile = optarg;
+           break;
+
+       case 'q' :      /* don't seed */
+           quiet++;
+           break;
+
+       case 's' :      /* seed */
+           chatty++;
+           break;
+
+       case 't' :      /* transition */
+           transition++;
+           break;
+
+       case 'P' :      /* pid file */
+           pidfile = optarg;
+           break;
+
+       default :
+           fprintf( stderr, "Unknown option -- '%c'\n", c );
+           exit( 1 );
+       }
+    }
+    if ( optind != ac ) {
+       fprintf( stderr, "Too many arguments.\n" );
+       exit( 1 );
+    }
+
+    if (( prog = strrchr( av[ 0 ], '/' )) == NULL ) {
+       prog = av[ 0 ];
+    } else {
+       prog++;
+    }
+
+    /*
+     * Configure loop back address first, so appearances of "lo0" in
+     * the config file fail.  Also insures that lo0 gets configured,
+     * even if there's some hangup during configuration of some
+     * other interface.
+     */
+    if (( interfaces = newiface( LOOPIFACE )) == NULL ) {
+       perror( "newiface" );
+       exit( 1 );
+    }
+    interfaces->i_flags |= IFACE_PHASE2 | IFACE_LOOPBACK;
+
+    /*
+     * Check our initial configuration before we fork. This way we can
+     * complain about syntax errors on stdout.
+     *
+     * Basically, if we're going to read our config file, we should read
+     * it and initialize our data structures. If we're not going to read
+     * our config file, use GIFCONF to initialize our data structures.
+     */
+    if ( readconf( configfile ) < 0 && getifconf() < 0 ) {
+       fprintf( stderr, "%s: can't get interfaces, exiting.\n", prog );
+       exit( 1 );
+    }
+
+    /* we need to count up our interfaces so that we can simplify things
+     * later. we also need to figure out if we have more than one interface
+     * that is routing. */
+    for (i = 0, ninterfaces = 0, iface = interfaces; iface;
+        iface=iface->i_next) {
+      if (iface->i_flags & IFACE_DONTROUTE)
+       i++;
+      ninterfaces++;
+    }
+    i = ninterfaces - i; /* number of routable interfaces */
+
+    /*
+     * At this point, we have (at least partially) initialized data
+     * structures. Fill in what we can and verify that nothing is obviously
+     * broken.
+     */
+    for (iface = interfaces; iface; iface = iface->i_next) {
+       /* Apply the default phase */
+       if (( iface->i_flags & IFACE_PHASE1 ) == 0 &&
+               ( iface->i_flags & IFACE_PHASE2 ) == 0 ) {
+           iface->i_flags |= defphase;
+       }
+
+        /* set up router flag information. if we have multiple interfaces
+        * and DONTROUTE isn't set, set up ROUTER. i is the number of 
+        * interfaces that don't have the DONTROUTE flag set. */
+       if ((i > IFBASE) && ((iface->i_flags & IFACE_DONTROUTE) == 0)) {
+         iface->i_flags |= IFACE_ISROUTER;
+       }
+
+       /* Set default addresses */
+       if ( iface->i_rt == NULL ) {
+           if (( iface->i_rt = newrt(iface)) == NULL ) {
+               perror( "newrt" );
+               exit( 1 );
+           }
+
+           if ( iface->i_flags & IFACE_PHASE1 ) {
+               iface->i_rt->rt_firstnet = iface->i_rt->rt_lastnet =
+                       iface->i_caddr.sat_addr.s_net;
+           } else {
+               if ( iface->i_caddr.sat_addr.s_net != ATADDR_ANYNET ||
+                       ( iface->i_flags & IFACE_LOOPBACK )) {
+                   iface->i_rt->rt_firstnet = iface->i_rt->rt_lastnet =
+                           iface->i_caddr.sat_addr.s_net;
+               } else {
+                   iface->i_rt->rt_firstnet = htons( STARTUP_FIRSTNET );
+                   iface->i_rt->rt_lastnet = htons( STARTUP_LASTNET );
+               }
+           }
+       }
+       
+       if (( iface->i_flags & IFACE_PHASE1 ) == 0 ) {
+           iface->i_rt->rt_flags |= RTMPTAB_EXTENDED;
+       }
+
+       if ( iface->i_caddr.sat_addr.s_net == ATADDR_ANYNET ) {
+           iface->i_caddr.sat_addr.s_net = iface->i_rt->rt_firstnet;
+       }
+
+       if ( debug ) {
+           dumpconfig( iface );        /* probably needs args */
+       }
+    }
+
+    /*
+     * A little consistency check...
+     */
+    if ( ninterfaces < IFBASE ) {
+       fprintf( stderr, "%s: zero interfaces, exiting.\n", prog );
+       exit( 1 );
+    }
+
+    /* do this here so that we can use ifconfig */
+#ifdef __svr4__
+    if ( plumb() < 0 ) {
+       fprintf(stderr, "can't establish STREAMS plumbing, exiting.\n" );
+       atalkd_exit( 1 );
+    }
+#endif __svr4__
+
+    /* delete pre-existing interface addresses. */
+#ifdef SIOCDIFADDR
+    for (iface = interfaces; iface; iface = iface->i_next) {
+      if (ifconfig(iface->i_name, SIOCDIFADDR, &iface->i_addr)) {
+#ifdef SIOCATALKDIFADDR
+#if (SIOCDIFADDR != SIOCATALKDIFADDR)
+       ifconfig(iface->i_name, SIOCATALKDIFADDR, &iface->i_addr);
+#endif
+#endif
+      }
+    }
+#endif
+
+    /*
+     * Disassociate. The child will send itself a signal when it is
+     * stable. This indicates that other processes may begin using
+     * AppleTalk.
+     */
+    switch (i = server_lock("atalkd", pidfile, debug)) {
+    case -1:
+      exit(1);
+    case 0: /* child */
+      break;
+    default: /* parent */
+      /*
+       * Wait for the child to send itself a SIGSTOP, after which
+       * we send it a SIGCONT and exit ourself.
+       */
+      if ( wait3( &status, WUNTRACED, (struct rusage *)0 ) != i) {
+       perror( "wait3" );      /* Child died? */
+       atalkd_exit( 1 );
+      }
+      if ( !WIFSTOPPED( status )) {
+       fprintf( stderr, "AppleTalk not up! Check your syslog for the reason." );
+       if ( WIFEXITED( status )) {
+         fprintf( stderr, " Child exited with %d.\n",
+                  WEXITSTATUS( status ));
+       } else {
+         fprintf( stderr, " Child died.\n" );
+       }
+       atalkd_exit( 1 );
+      }
+      if ( kill(i, SIGCONT ) < 0 ) {
+       perror( "kill" );
+       atalkd_exit( 1 );
+      }
+      exit( 0 );
+    }
+
+#ifdef ultrix
+    openlog( prog, LOG_PID );
+#else ultrix
+    openlog( prog, LOG_PID, LOG_DAEMON );
+#endif ultrix
+
+    syslog( LOG_INFO, "restart (%s)", version );
+
+    /*
+     * Socket for use in routing ioctl()s. Can't add routes to our
+     * interfaces until we have our routing socket.
+     */
+#ifdef BSD4_4
+    if (( rtfd = socket( PF_ROUTE, SOCK_RAW, AF_APPLETALK )) < 0 ) {
+       syslog( LOG_ERR, "route socket: %m" );
+       atalkd_exit( 1 );
+    }
+    if ( shutdown( rtfd, 0 ) < 0 ) {
+       syslog( LOG_ERR, "route shutdown: %m" );
+       atalkd_exit( 1 );
+    }
+#else BSD4_4
+    if (( rtfd = socket( AF_APPLETALK, SOCK_DGRAM, 0 )) < 0 ) {
+       syslog( LOG_ERR, "route socket: %m" );
+       atalkd_exit( 1 );
+    }
+#endif BSD4_4
+
+    memset(&sv, 0, sizeof(sv));
+    sv.sa_handler = as_down;
+    sigemptyset( &sv.sa_mask );
+    sigaddset( &sv.sa_mask, SIGUSR1 );
+    sigaddset( &sv.sa_mask, SIGALRM );
+    sigaddset( &sv.sa_mask, SIGTERM );
+    sv.sa_flags = SA_RESTART;
+    if ( sigaction( SIGTERM, &sv, NULL) < 0 ) {
+       syslog( LOG_ERR, "sigterm: %m" );
+       atalkd_exit( 1 );
+    }
+
+    sv.sa_handler = as_debug;
+    sigemptyset( &sv.sa_mask );
+    sigaddset( &sv.sa_mask, SIGUSR1 );
+    sigaddset( &sv.sa_mask, SIGALRM );
+    sigaddset( &sv.sa_mask, SIGTERM );
+    sv.sa_flags = SA_RESTART;
+    if ( sigaction( SIGUSR1, &sv, NULL) < 0 ) {
+       syslog( LOG_ERR, "sigusr1: %m" );
+       atalkd_exit( 1 );
+    }
+
+    sv.sa_handler = as_timer;
+    sigemptyset( &sv.sa_mask );
+    sigaddset( &sv.sa_mask, SIGUSR1 );
+    sigaddset( &sv.sa_mask, SIGALRM );
+    sigaddset( &sv.sa_mask, SIGTERM );
+    sv.sa_flags = SA_RESTART;
+    if ( sigaction( SIGALRM, &sv, NULL) < 0 ) {
+       syslog( LOG_ERR, "sigalrm: %m" );
+       atalkd_exit( 1 );
+    }
+
+    it.it_interval.tv_sec = 10L;
+    it.it_interval.tv_usec = 0L;
+    it.it_value.tv_sec = 10L;
+    it.it_value.tv_usec = 0L;
+    if ( setitimer( ITIMER_REAL, &it, NULL) < 0 ) {
+       syslog( LOG_ERR, "setitimer: %m" );
+       atalkd_exit( 1 );
+    }
+
+    ciface = interfaces;
+    bootaddr( ciface );
+    for (;;) {
+       readfds = fds;
+       if ( select( nfds, &readfds, NULL, NULL, NULL) < 0 ) {
+           if ( errno == EINTR ) {
+               errno = 0;
+               continue;
+           } else {
+               syslog( LOG_ERR, "select: %m" );
+               atalkd_exit( 1 );
+           }
+       }
+
+       for ( iface = interfaces; iface; iface = iface->i_next ) {
+           for ( ap = iface->i_ports; ap; ap = ap->ap_next ) {
+               if ( FD_ISSET( ap->ap_fd, &readfds )) {
+                   if ( ap->ap_packet ) {
+                       fromlen = sizeof( struct sockaddr_at );
+                       if (( c = recvfrom( ap->ap_fd, Packet, sizeof( Packet ),
+                               0, (struct sockaddr *)&sat, &fromlen )) < 0 ) {
+                           syslog( LOG_ERR, "recvfrom: %m" );
+                           continue;
+                       }
+#ifdef DEBUG
+                       if ( debug ) {
+                           printf( "packet from %u.%u on %s (%x) %d (%d)\n",
+                                   ntohs( sat.sat_addr.s_net ),
+                                   sat.sat_addr.s_node, iface->i_name,
+                                   iface->i_flags, ap->ap_port, ap->ap_fd );
+                           bprint( Packet, c );
+                       }
+#endif DEBUG
+#ifdef __svr4__
+                       if ( sighold( SIGALRM ) || sighold( SIGUSR1 )) {
+                           syslog( LOG_ERR, "sighold: %m" );
+                           atalkd_exit( 1 );
+                       }
+#else __svr4__
+                       mask = sigsetmask( sigmask( SIGALRM ) |
+                               sigmask( SIGUSR1 ));
+#endif __svr4__
+                       if (( *ap->ap_packet )( ap, &sat, Packet, c ) < 0) {
+                         syslog(LOG_ERR, "ap->ap_packet: %m");
+                         atalkd_exit(1);
+                       }
+
+#ifdef DEBUG
+                       consistency();
+#endif DEBUG
+#ifdef __svr4__
+                       if ( sigrelse( SIGUSR1 ) || sigrelse( SIGALRM )) {
+                           syslog( LOG_ERR, "sigrelse: %m" );
+                           atalkd_exit( 1 );
+                       }
+#else __svr4__
+                       sigsetmask( mask );
+#endif __svr4__
+                   }
+               }
+           }
+       }
+    }
+}
+
+/*
+ * This code is called (from main(), as_timer(), zip_packet(),
+ * and rtmp_packet()) to set the initial "bootstrapping" address
+ * on an interface.
+ */
+bootaddr( iface )
+    struct interface   *iface;
+{
+    if ( iface == 0 ) {
+       return;
+    }
+
+    /* consistency */
+    if ( iface->i_flags & IFACE_ADDR ) {
+       syslog( LOG_ERR, "bootaddr OOPS!" );
+       atalkd_exit(1);
+    }
+
+    if ( iface->i_flags & IFACE_PHASE1 ) {
+       setaddr( iface, IFACE_PHASE1, 0,
+               iface->i_caddr.sat_addr.s_node, 0, 0 );
+
+       if ( iface->i_flags & IFACE_LOOPBACK ) {
+           iface->i_flags |= IFACE_CONFIG | IFACE_ADDR;
+           if ( ciface == iface ) {
+               ciface = ciface->i_next;
+               bootaddr( ciface );
+           }
+
+       } else if (rtmp_request( iface ) < 0) {
+         syslog(LOG_ERR, "bootaddr (rtmp_request): %m");
+         atalkd_exit(1);
+       }
+
+    } else {
+       setaddr( iface, IFACE_PHASE2, iface->i_caddr.sat_addr.s_net,
+               iface->i_caddr.sat_addr.s_node,
+               iface->i_rt->rt_firstnet, iface->i_rt->rt_lastnet );
+
+       if ( iface->i_flags & IFACE_LOOPBACK ) {
+           iface->i_flags |= IFACE_CONFIG | IFACE_ADDR;
+           if ( ciface == iface ) {
+               ciface = ciface->i_next;
+               bootaddr( ciface );
+           }
+           
+       } else if (zip_getnetinfo( iface ) < 0) {
+         syslog(LOG_ERR, "bootaddr (zip_getnetinfo): %m");
+         atalkd_exit(1);
+       }
+    }
+    ++iface->i_time;
+    iface->i_flags |= IFACE_ADDR;
+    stabletimer = UNSTABLE;
+}
+
+
+/*
+ * Change setaddr()
+ * to manage the i_ports field and the fds for select().
+ */
+setaddr( iface, phase, net, node, first, last )
+    struct interface   *iface;
+    u_int8_t           phase;
+    u_int16_t          net;
+    u_int8_t           node;
+    u_int16_t          first, last;
+{
+    int                        i;
+    struct atserv      *as;
+    struct atport      *ap;
+    struct servent     *se;
+    struct sockaddr_at sat;
+    struct netrange    nr;
+
+    if ( iface->i_ports == NULL ) {    /* allocate port structures */
+       for ( i = 0, as = atserv; i < atservNATSERV; i++, as++ ) {
+           if (( se = getservbyname( as->as_name, "ddp" )) == NULL ) {
+               syslog( LOG_INFO, "%s: service unknown", as->as_name );
+           } else {
+               as->as_port = ntohs( se->s_port );
+           }
+           if (( ap = (struct atport *)malloc( sizeof( struct atport ))) ==
+                   NULL ) {
+               syslog( LOG_ERR, "malloc: %m" );
+               atalkd_exit( 1 );
+           }
+           ap->ap_fd = 0;
+           ap->ap_next = iface->i_ports;
+           ap->ap_iface = iface;
+           ap->ap_port = as->as_port;
+           ap->ap_packet = as->as_packet;
+
+           iface->i_ports = ap;
+       }
+    } else {                           /* close ports */
+       for ( ap = iface->i_ports; ap; ap = ap->ap_next ) {
+           (void)close( ap->ap_fd );
+       }
+    }
+
+#ifdef BSD4_4
+    iface->i_addr.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+    iface->i_addr.sat_family = AF_APPLETALK;
+    iface->i_addr.sat_addr.s_net = net;
+    iface->i_addr.sat_addr.s_node = node;
+
+    nr.nr_phase = phase;
+    nr.nr_firstnet = first;
+    nr.nr_lastnet = last;
+    memcpy( iface->i_addr.sat_zero, &nr, sizeof( struct netrange ));
+
+    if ( ifconfig( iface->i_name, SIOCSIFADDR, &iface->i_addr )) {
+      syslog( LOG_ERR, "setifaddr: %s (%u-%u): %m. try specifying a \
+smaller net range.", iface->i_name, ntohs(first), ntohs(last));
+       atalkd_exit( 1 );
+    }
+    if ( ifconfig( iface->i_name, SIOCGIFADDR, &iface->i_addr )) {
+       syslog( LOG_ERR, "getifaddr: %s: %m", iface->i_name );
+       atalkd_exit( 1 );
+    }
+
+    /* open ports */
+    i = 1; /* enable broadcasts */
+#if defined(__svr4__) 
+    syslog(LOG_INFO, "setsockopt incompatible w/ Solaris STREAMS module.");
+#endif
+    for ( ap = iface->i_ports; ap; ap = ap->ap_next ) {
+       if (( ap->ap_fd = socket( AF_APPLETALK, SOCK_DGRAM, 0 )) < 0 ) {
+           syslog( LOG_ERR, "socket: %m" );
+           atalkd_exit( 1 );
+       }
+#if !defined(__svr4__)
+       setsockopt(ap->ap_fd, SOL_SOCKET, SO_BROADCAST, &i, sizeof(i));
+#endif
+
+       memset( &sat, 0, sizeof( struct sockaddr_at ));
+#ifdef BSD4_4
+       sat.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+       sat.sat_family = AF_APPLETALK;
+       sat.sat_addr.s_net = iface->i_addr.sat_addr.s_net;
+       sat.sat_addr.s_node = iface->i_addr.sat_addr.s_node;
+       sat.sat_port = ap->ap_port;
+
+       if ( bind( ap->ap_fd, (struct sockaddr *)&sat,
+               sizeof( struct sockaddr_at )) < 0 ) {
+           syslog( LOG_ERR, "bind %u.%u:%u: %m",
+                   ntohs( sat.sat_addr.s_net ),
+                   sat.sat_addr.s_node, sat.sat_port );
+#ifdef SIOCDIFADDR
+           /* remove all interfaces if we have a problem with bind */
+           for (iface = interfaces; iface; iface = iface->i_next) {
+             if (ifconfig( iface->i_name, SIOCDIFADDR, &iface->i_addr )) {
+#ifdef SIOCATALKDIFADDR
+#if (SIOCDIFADDR != SIOCATALKDIFADDR)
+               ifconfig( iface->i_name, SIOCATALKDIFADDR, &iface->i_addr );
+#endif         
+#endif
+             }
+           }
+#endif 
+           atalkd_exit( 1 );
+       }
+    }
+
+    /* recalculate nfds and fds */
+    FD_ZERO( &fds );
+    for ( nfds = 0, iface = interfaces; iface; iface = iface->i_next ) {
+       for ( ap = iface->i_ports; ap; ap = ap->ap_next ) {
+           FD_SET( ap->ap_fd, &fds );
+           if ( ap->ap_fd > nfds ) {
+               nfds = ap->ap_fd;
+           }
+       }
+    }
+    nfds++;
+}
+
+ifconfig( iname, cmd, sa )
+    char               *iname;
+    unsigned long      cmd;
+    struct sockaddr_at *sa;
+{
+    struct ifreq       ifr;
+    int                        s;
+
+    memset(&ifr, 0, sizeof(ifr));
+    strcpy( ifr.ifr_name, iname );
+    ifr.ifr_addr = *(struct sockaddr *)sa;
+
+    if (( s = socket( AF_APPLETALK, SOCK_DGRAM, 0 )) < 0 ) {
+       return( 1 );
+    }
+    if ( ioctl( s, cmd, &ifr ) < 0 ) {
+       close(s);
+       return( 1 );
+    }
+    close( s );
+    if ( cmd == SIOCGIFADDR ) {
+       *(struct sockaddr *)sa = ifr.ifr_addr;
+    }
+    return( 0 );
+}
+
+dumpconfig( iface )
+    struct interface   *iface;
+{
+    struct list                *l;
+
+    printf( "%s", iface->i_name );
+    if ( iface->i_flags & IFACE_RSEED ) {
+       printf( " -router" );
+    } else if ( iface->i_flags & IFACE_SEED ) {
+       printf( " -seed" );
+    }
+
+    if ( iface->i_flags & IFACE_DONTROUTE) 
+        printf( " -dontroute");
+
+    printf( " -phase" );
+    if ( iface->i_flags & IFACE_PHASE1 ) {
+       printf( " 1" );
+    } else {
+       printf( " 2" );
+    }
+    printf( " -net %d", ntohs( iface->i_rt->rt_firstnet ));
+    if ( iface->i_rt->rt_lastnet != iface->i_rt->rt_firstnet ) {
+       printf( "-%d", ntohs( iface->i_rt->rt_lastnet ));
+    }
+    printf( " -addr %u.%u", ntohs( iface->i_addr.sat_addr.s_net ),
+           iface->i_addr.sat_addr.s_node );
+    printf( " -caddr %u.%u", ntohs( iface->i_caddr.sat_addr.s_net ),
+           iface->i_caddr.sat_addr.s_node );
+    for ( l = iface->i_rt->rt_zt; l; l = l->l_next ) {
+       printf( " -zone %.*s", ((struct ziptab *)l->l_data)->zt_len,
+               ((struct ziptab *)l->l_data)->zt_name );
+    }
+    printf( "\n" );
+}
+
+#ifdef DEBUG
+dumproutes()
+{
+    struct interface   *iface;
+    struct rtmptab     *rtmp;
+    struct list                *l;
+    struct ziptab      *zt;
+
+    for ( iface = interfaces; iface; iface = iface->i_next ) {
+       for ( rtmp = iface->i_rt; rtmp; rtmp = rtmp->rt_inext ) {
+           if ( rtmp->rt_gate == 0 ) {
+               if ( rtmp->rt_flags & RTMPTAB_EXTENDED ) {
+                   printf( "%u-%u", ntohs( rtmp->rt_firstnet ),
+                           ntohs( rtmp->rt_lastnet ));
+               } else {
+                   printf( "%u", ntohs( rtmp->rt_firstnet ));
+               }
+           } else {
+               if ( rtmp->rt_flags & RTMPTAB_EXTENDED ) {
+                   printf( "%u.%u for %u-%u",
+                           ntohs( rtmp->rt_gate->g_sat.sat_addr.s_net ),
+                           rtmp->rt_gate->g_sat.sat_addr.s_node,
+                           ntohs( rtmp->rt_firstnet ),
+                           ntohs( rtmp->rt_lastnet ));
+               } else {
+                   printf( "%u.%u for %u",
+                           ntohs( rtmp->rt_gate->g_sat.sat_addr.s_net ),
+                           rtmp->rt_gate->g_sat.sat_addr.s_node,
+                           ntohs( rtmp->rt_firstnet ));
+               }
+           }
+
+           if ( rtmp->rt_iprev == 0 && rtmp != iface->i_rt ) {
+               printf( " *" );
+           }
+
+           for ( l = rtmp->rt_zt; l; l = l->l_next ) {
+               zt = (struct ziptab *)l->l_data;
+               printf( " %.*s", zt->zt_len, zt->zt_name );
+           }
+
+           printf( "\n" );
+       }
+    }
+
+    printf( "\n" );
+    fflush( stdout );
+}
+
+dumpzones()
+{
+    struct interface   *iface;
+    struct rtmptab     *rtmp;
+    struct list                *l;
+    struct ziptab      *zt;
+
+    for ( zt = ziptab; zt; zt = zt->zt_next ) {
+       printf( "%.*s", zt->zt_len, zt->zt_name );
+       for ( l = zt->zt_rt; l; l = l->l_next ) {
+           rtmp = (struct rtmptab *)l->l_data;
+           if ( rtmp->rt_flags & RTMPTAB_EXTENDED ) {
+               printf( " %u-%u", ntohs( rtmp->rt_firstnet ),
+                       ntohs( rtmp->rt_lastnet ));
+           } else {
+               printf( " %u", ntohs( rtmp->rt_firstnet ));
+           }
+           if ( rtmp->rt_iprev == 0 && rtmp->rt_gate != 0 ) {
+               printf( "*" );
+           }
+       }
+       printf( "\n" );
+    }
+
+    printf( "\n" );
+    fflush( stdout );
+}
+#endif DEBUG
diff --git a/etc/atalkd/multicast.c b/etc/atalkd/multicast.c
new file mode 100644 (file)
index 0000000..365bcec
--- /dev/null
@@ -0,0 +1,395 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <syslog.h>
+
+/* work around for FreeBSD */
+#if defined(__FreeBSD__) && (__FreeBSD__ >= 2)
+#include <osreldate.h>
+#if __FreeBSD_version >= 300000
+#include <net/if_dl.h>
+#define NO_DATA_LINK_PASSTHROUGH
+#endif
+#endif
+
+#ifdef __svr4__
+#include <sys/sockio.h>
+#endif __svr4__
+
+#include <atalk/util.h>
+#include <netatalk/endian.h>
+#include "zip.h"
+
+static const unsigned char     ethermulti[ 6 ] = {
+    0x09, 0x00, 0x07, 0xff, 0xff, 0xff,
+};
+
+static const unsigned char     ethermultitab[ 253 ][ 6 ] = {
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x00, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x01, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x02, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x03, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x04, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x05, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x06, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x07, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x08, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x09, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x0a, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x0b, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x0c, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x0d, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x0e, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x0f, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x10, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x11, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x12, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x13, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x14, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x15, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x16, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x17, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x18, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x19, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x1a, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x1b, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x1c, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x1d, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x1e, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x1f, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x20, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x21, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x22, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x23, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x24, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x25, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x26, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x27, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x28, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x29, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x2a, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x2b, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x2c, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x2d, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x2e, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x2f, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x30, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x31, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x32, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x33, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x34, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x35, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x36, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x37, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x38, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x39, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x3a, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x3b, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x3c, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x3d, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x3e, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x3f, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x40, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x41, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x42, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x43, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x44, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x45, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x46, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x47, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x48, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x49, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x4a, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x4b, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x4c, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x4d, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x4e, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x4f, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x50, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x51, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x52, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x53, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x54, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x55, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x56, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x57, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x58, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x59, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x5a, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x5b, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x5c, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x5d, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x5e, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x5f, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x60, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x61, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x62, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x63, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x64, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x65, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x66, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x67, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x68, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x69, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x6a, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x6b, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x6c, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x6d, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x6e, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x6f, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x70, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x71, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x72, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x73, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x74, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x75, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x76, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x77, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x78, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x79, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x7a, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x7b, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x7c, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x7d, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x7e, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x7f, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x80, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x81, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x82, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x83, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x84, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x85, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x86, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x87, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x88, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x89, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x8a, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x8b, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x8c, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x8d, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x8e, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x8f, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x90, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x91, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x92, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x93, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x94, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x95, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x96, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x97, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x98, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x99, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x9a, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x9b, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x9c, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x9d, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x9e, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0x9f, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xa0, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xa1, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xa2, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xa3, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xa4, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xa5, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xa6, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xa7, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xa8, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xa9, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xaa, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xab, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xac, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xad, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xae, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xaf, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xb0, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xb1, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xb2, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xb3, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xb4, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xb5, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xb6, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xb7, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xb8, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xb9, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xba, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xbb, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xbc, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xbd, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xbe, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xbf, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xc0, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xc1, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xc2, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xc3, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xc4, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xc5, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xc6, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xc7, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xc8, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xc9, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xca, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xcb, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xcc, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xcd, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xce, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xcf, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xd0, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xd1, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xd2, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xd3, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xd4, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xd5, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xd6, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xd7, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xd8, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xd9, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xda, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xdb, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xdc, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xdd, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xde, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xdf, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xe0, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xe1, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xe2, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xe3, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xe4, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xe5, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xe6, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xe7, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xe8, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xe9, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xea, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xeb, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xec, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xed, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xee, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xef, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xf0, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xf1, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xf2, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xf3, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xf4, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xf5, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xf6, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xf7, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xf8, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xf9, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xfa, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xfb, },
+    { 0x09, 0x00, 0x07, 0x00, 0x00, 0xfc, },
+};
+
+static const unsigned char     tokenmulti[ 6 ] = {
+    0xc0, 0x00, 0x40, 0x00, 0x00, 0x00,
+};
+
+static const unsigned char     tokenmultitab[ 19 ][ 6 ] = {
+    { 0xc0, 0x00, 0x00, 0x00, 0x08, 0x00, },
+    { 0xc0, 0x00, 0x00, 0x00, 0x10, 0x00, },
+    { 0xc0, 0x00, 0x00, 0x00, 0x20, 0x00, },
+    { 0xc0, 0x00, 0x00, 0x00, 0x40, 0x00, },
+    { 0xc0, 0x00, 0x00, 0x00, 0x80, 0x00, },
+    { 0xc0, 0x00, 0x00, 0x01, 0x00, 0x00, },
+    { 0xc0, 0x00, 0x00, 0x02, 0x00, 0x00, },
+    { 0xc0, 0x00, 0x00, 0x04, 0x00, 0x00, },
+    { 0xc0, 0x00, 0x00, 0x08, 0x00, 0x00, },
+    { 0xc0, 0x00, 0x00, 0x10, 0x00, 0x00, },
+    { 0xc0, 0x00, 0x00, 0x20, 0x00, 0x00, },
+    { 0xc0, 0x00, 0x00, 0x40, 0x00, 0x00, },
+    { 0xc0, 0x00, 0x00, 0x80, 0x00, 0x00, },
+    { 0xc0, 0x00, 0x01, 0x00, 0x00, 0x00, },
+    { 0xc0, 0x00, 0x02, 0x00, 0x00, 0x00, },
+    { 0xc0, 0x00, 0x04, 0x00, 0x00, 0x00, },
+    { 0xc0, 0x00, 0x08, 0x00, 0x00, 0x00, },
+    { 0xc0, 0x00, 0x10, 0x00, 0x00, 0x00, },
+    { 0xc0, 0x00, 0x20, 0x00, 0x00, 0x00, },
+};
+
+
+
+/* configure multicast for a given named interface */
+int addmulti(const char *name, const unsigned char *data)
+{
+#ifdef NO_DATA_LINK_PASSTHROUGH
+    struct sockaddr_dl sa;
+#else
+    struct sockaddr sa;
+#endif
+
+    memset(&sa, 0, sizeof(sa));
+#ifdef NO_DATA_LINK_PASSTHROUGH
+    sa.sdl_family = AF_LINK;
+    memcpy(LLADDR(&sa), data ? data : ethermulti, sizeof(ethermulti));
+    sa.sdl_alen = sizeof(ethermulti);
+    sa.sdl_len = sizeof(sa);
+#else
+    memcpy(sa.sa_data, data ? data : ethermulti, sizeof( ethermulti ));
+#endif
+    if (ifconfig(name, SIOCADDMULTI, &sa))
+      return -1;
+
+    return 0;
+}
+
+static u_int16_t
+atalk_cksum( data, len )
+    u_char     *data;
+    int                len;
+{
+    u_char     *end;
+    u_int32_t  cksum = 0;
+
+    for ( end = data + len; data < end; data++ ) {
+       cksum = ( cksum + *data ) << 1;
+       if ( cksum & 0x00010000 ) {
+           cksum++;
+       }
+       cksum &= 0x0000ffff;
+    }
+
+    if ( cksum == 0 ) {
+       cksum = 0x0000ffff;
+    }
+
+    return( (u_int16_t) cksum );
+}
+
+/*
+ * Fill in multicast for zone.  There is a general issue here:  how can
+ * we tell the type of interface we're configuring for?  E.g.  Is it
+ * ethernet, tokenring, or FDDI?  (Of course, FDDI and Ethernet look just
+ * alike.)
+ */
+int
+zone_bcast( zt )
+     struct ziptab     *zt;
+{
+    u_char             uname[ 32 ];
+    u_int16_t          cksum;
+    int                        i;
+
+    if (!zt->zt_bcast &&
+       (zt->zt_bcast = (u_char *) malloc(sizeof( ethermulti ))) == NULL) {
+       syslog( LOG_ERR, "zone_bcast malloc: %m" );
+       return -1;
+     }
+
+    for ( i = 0; i < zt->zt_len; i++ ) {
+       uname[ i ] = diatoupper(zt->zt_name[ i ]);
+    }
+    cksum = atalk_cksum( uname, zt->zt_len );
+#define elements(a)   (sizeof(a)/sizeof((a)[0]))
+    memcpy(zt->zt_bcast, ethermultitab[ cksum % elements( ethermultitab ) ],
+          sizeof( ethermulti ));
+    return 0;
+}
diff --git a/etc/atalkd/multicast.h b/etc/atalkd/multicast.h
new file mode 100644 (file)
index 0000000..a3ff56d
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 1990,1997 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#ifndef ATALKD_MULTICAST_H
+#define ATALKD_MULTICAST_H 1
+
+#include <sys/cdefs.h>
+#include "zip.h"
+
+extern unsigned char   ethermultitab[ 253 ][ 6 ];
+extern unsigned char   tokenmultitab[ 19 ][ 6 ];
+extern unsigned char   ethermulti[ 6 ];
+
+int addmulti __P((const char *, const unsigned char *));
+int zone_bcast __P((struct ziptab *));
+
+#endif /* atalkd/multicast.h */
diff --git a/etc/atalkd/nbp.c b/etc/atalkd/nbp.c
new file mode 100644 (file)
index 0000000..e94130d
--- /dev/null
@@ -0,0 +1,633 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/syslog.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <netatalk/at.h>
+#include <atalk/ddp.h>
+#include <atalk/atp.h>
+#include <atalk/nbp.h>
+#include <atalk/util.h>
+
+#ifdef __svr4__
+#include <sys/sockio.h>
+#endif __svr4__
+
+#include "atserv.h"
+#include "interface.h"
+#include "list.h"
+#include "rtmp.h"
+#include "gate.h"
+#include "zip.h"
+#include "nbp.h"
+#include "multicast.h"
+
+extern int     transition;
+
+struct nbptab  *nbptab = NULL;
+
+static 
+void nbp_ack( fd, nh_op, nh_id, to )
+    int                        fd;
+    int                        nh_op;
+    int                        nh_id;
+    struct sockaddr_at *to;
+{
+    struct nbphdr      nh;
+    char               *data, packet[ SZ_NBPHDR + 1 ];
+
+    nh.nh_op = nh_op;
+    nh.nh_cnt = 0;
+    nh.nh_id = nh_id;
+    data = packet;
+    *data++ = DDPTYPE_NBP;
+    bcopy( &nh, data, SZ_NBPHDR );
+    data += SZ_NBPHDR;
+    if ( sendto( fd, packet, data - packet, 0, (struct sockaddr *)to,
+           sizeof( struct sockaddr_at )) < 0 ) {
+       syslog( LOG_ERR, "sendto: %m" );
+    }
+}
+
+int nbp_packet( ap, from, data, len )
+    struct atport      *ap;
+    struct sockaddr_at *from;
+    char               *data;
+    int                        len;
+{
+    struct nbphdr      nh;
+    struct nbptuple    nt;
+    struct nbpnve      nn;
+    struct sockaddr_at sat;
+    struct nbptab      *ntab;
+    struct ziptab      *zt;
+    struct interface   *iface;
+    struct list                *l;
+    struct rtmptab     *rtmp;
+    char               *end, *nbpop, *zonep, packet[ ATP_BUFSIZ ];
+    int                        n, i, cc, locallkup;
+
+    end = data + len;
+    if ( data >= end ) {
+       syslog( LOG_INFO, "nbp_packet malformed packet" );
+       return 1;
+    }
+    if ( *data++ != DDPTYPE_NBP ) {
+       syslog( LOG_INFO, "nbp_packet bad ddp type" );
+       return 1;
+    }
+
+    if ( data + SZ_NBPHDR + SZ_NBPTUPLE > end ) {
+       syslog( LOG_INFO, "nbp_packet: malformed packet" );
+       return 1;
+    }
+    memcpy( &nh, data, SZ_NBPHDR );
+    nbpop = data;                      /* remember for fwd and brrq */
+    data += SZ_NBPHDR;
+    if ( nh.nh_cnt != 1 ) {
+       syslog( LOG_INFO, "nbp_packet: bad tuple count (%d/%d)", nh.nh_cnt,
+               nh.nh_op );
+       return 1;
+    }
+
+    memcpy( &nt, data, SZ_NBPTUPLE );
+    data += SZ_NBPTUPLE;
+
+    memset( &nn.nn_sat, 0, sizeof( struct sockaddr_at ));
+#ifdef BSD4_4
+    nn.nn_sat.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+    nn.nn_sat.sat_family = AF_APPLETALK;
+    nn.nn_sat.sat_addr.s_net = nt.nt_net;
+    nn.nn_sat.sat_addr.s_node = nt.nt_node;
+    nn.nn_sat.sat_port = nt.nt_port;
+
+    /* object */
+    if ( data >= end || ( *data < 0 || *data > 32 ) || data + *data > end ) {
+       syslog( LOG_INFO, "nbp_packet: malformed packet" );
+       return 1;
+    }
+    nn.nn_objlen = *data++;
+    memcpy( nn.nn_obj, data, nn.nn_objlen );
+    data += nn.nn_objlen;
+
+    /* type */
+    if ( data >= end || ( *data < 0 || *data > 32 ) || data + *data > end ) {
+       syslog( LOG_INFO, "nbp_packet: malformed packet" );
+       return 1;
+    }
+    nn.nn_typelen = *data++;
+    memcpy( nn.nn_type, data, nn.nn_typelen );
+    data += nn.nn_typelen;
+
+    /* zone */
+    if ( data >= end || ( *data < 0 || *data > 32 ) || data + *data > end ) {
+       syslog( LOG_INFO, "nbp_packet: malformed packet" );
+       return 1;
+    }
+    zonep = data;                      /* remember for fwd */
+    nn.nn_zonelen = *data++;
+    memcpy( nn.nn_zone, data, nn.nn_zonelen );
+    data += nn.nn_zonelen;
+
+    if ( data != end ) {
+       syslog( LOG_INFO, "nbp_packet: malformed packet" );
+       return 1;
+    }
+
+    locallkup = 0;
+    switch ( nh.nh_op ) {
+
+    case NBPOP_RGSTR :
+       /*
+        * Find the ziptab entry for the zone we're trying to register in.
+        */
+       if ( nn.nn_zonelen == 0 ||
+               ( nn.nn_zonelen == 1 && *nn.nn_zone == '*' )) {
+           if ( interfaces->i_next->i_rt->rt_zt ) {
+               zt = (struct ziptab *)interfaces->i_next->i_rt->rt_zt->l_data;
+           } else {
+               zt = 0;
+           }
+       } else {
+           for ( zt = ziptab; zt; zt = zt->zt_next ) {
+               if ( zt->zt_len == nn.nn_zonelen && strndiacasecmp( zt->zt_name,
+                       nn.nn_zone, zt->zt_len ) == 0 ) {
+                   break;
+               }
+           }
+           if ( zt == 0 ) {
+               nbp_ack( ap->ap_fd, NBPOP_ERROR, (int)nh.nh_id, from );
+               return 0;
+           }
+       }
+
+       /*
+        * Observe that we don't have to do any local-zone verification
+        * if the zone aleady has a multicast address set.
+        */
+       if ( zt != 0 && zt->zt_bcast == 0 ) {
+           /*
+            * Check if zone is associated with any of our local interfaces.
+            */
+           for ( iface = interfaces; iface; iface = iface->i_next ) {
+               for ( l = iface->i_rt->rt_zt; l; l = l->l_next ) {
+                   if ( zt == (struct ziptab *)l->l_data ) {
+                       break;
+                   }
+               }
+               if ( l != 0 ) {
+                   break;
+               }
+           }
+           if ( iface == 0 ) {
+               nbp_ack( ap->ap_fd, NBPOP_ERROR, (int)nh.nh_id, from );
+               return 0;
+           }
+
+           /* calculate and save multicast address */
+           if (zone_bcast(zt) < 0) {
+               syslog(LOG_ERR, "nbp_packet: zone_bcast");
+               return -1;
+           }
+
+           for ( iface = interfaces; iface; iface = iface->i_next ) {
+               if (( iface->i_flags & IFACE_PHASE2 ) == 0 ) {
+                   continue;
+               }
+               for ( l = iface->i_rt->rt_zt; l; l = l->l_next ) {
+                   if ( zt == (struct ziptab *)l->l_data ) {
+                       /* add multicast */
+                       if (addmulti(iface->i_name, zt->zt_bcast) < 0) {
+                           syslog( LOG_ERR, "nbp_packet: addmulti: %m" );
+                           return -1;
+                       }
+                   }
+               }
+           }
+       }
+
+       if (( ntab = (struct nbptab *)malloc( sizeof( struct nbptab )))
+               == 0 ) {
+           syslog( LOG_ERR, "nbp_packet: malloc: %m" );
+           return -1;
+       }
+       memcpy( &ntab->nt_nve, &nn, sizeof( struct nbpnve ));
+       ntab->nt_iface = ap->ap_iface;
+       ntab->nt_next = nbptab;
+       ntab->nt_prev = 0;
+       if ( nbptab ) {
+           nbptab->nt_prev = ntab;
+       }
+       nbptab = ntab;
+
+       nbp_ack( ap->ap_fd, NBPOP_OK, (int)nh.nh_id, from );
+       break;
+
+    case NBPOP_UNRGSTR :
+        /* deal with local zone info */
+        if (( nn.nn_zonelen == 1 && *nn.nn_zone == '*' ) ||
+           ( nn.nn_zonelen == 0 )) {
+         locallkup = 1;
+         if ( interfaces->i_next->i_rt->rt_zt ) {
+           zt = (struct ziptab *)interfaces->i_next->i_rt->rt_zt->l_data;
+         } else {
+           zt = NULL;
+         }
+       }
+
+       /* remove from our data, perhaps removing a multicast address */
+       for ( ntab = nbptab; ntab; ntab = ntab->nt_next ) {
+           if ( ntab->nt_nve.nn_objlen != nn.nn_objlen ||
+                   strndiacasecmp( ntab->nt_nve.nn_obj, nn.nn_obj,
+                   nn.nn_objlen )) {
+               continue;
+           }
+           if ( ntab->nt_nve.nn_typelen != nn.nn_typelen ||
+                   strndiacasecmp( ntab->nt_nve.nn_type, nn.nn_type,
+                   nn.nn_typelen )) {
+               continue;
+           }
+           /*
+            * I *think* we really do check the zone, here.
+            *
+            * i changed it to better handle local zone cases as well.
+            * -- asun
+            */
+           
+           /* match local zones */
+           if (locallkup) {
+             /* ntab is also local zone */
+             if (( ntab->nt_nve.nn_zonelen == 1 &&
+                   *ntab->nt_nve.nn_zone == '*' ) || 
+                 (ntab->nt_nve.nn_zonelen == 0))
+               break;
+             
+             /* ntab is default zone */
+             if (zt && (zt->zt_len == ntab->nt_nve.nn_zonelen) &&
+                 (strndiacasecmp(ntab->nt_nve.nn_zone, zt->zt_name,
+                                 zt->zt_len) == 0)) {
+               break;
+             }
+           }
+           
+           /* match particular zone */
+           if ((ntab->nt_nve.nn_zonelen == nn.nn_zonelen) &&
+               (strndiacasecmp( ntab->nt_nve.nn_zone, nn.nn_zone,
+                               nn.nn_zonelen ) == 0)) {
+               break;
+           }
+       }
+       if ( ntab == 0 ) {
+           nbp_ack( ap->ap_fd, NBPOP_ERROR, (int)nh.nh_id, from );
+           return 0;
+       }
+
+       if ( ntab->nt_next != 0 ) {
+           ntab->nt_next->nt_prev = ntab->nt_prev;
+       }
+       if ( ntab->nt_prev != 0 ) {
+           ntab->nt_prev->nt_next = ntab->nt_next;
+       }
+       if ( ntab == nbptab ) {
+           nbptab = ntab->nt_next;
+       }
+
+       /*
+        * Check for another nbptab entry with the same zone.  If
+        * there isn't one, find the ziptab entry for the zone and
+        * remove the multicast address from the appropriate interfaces.
+        * XXX
+        */
+
+       nbp_ack( ap->ap_fd, NBPOP_OK, (int)nh.nh_id, from );
+       break;
+
+    case NBPOP_BRRQ :
+       /*
+        * Couple of things:  1. Unless we have the -t flag (which is sort
+        * of a misnomer, since you need it if you're doing any phase 1
+        * work), always send NBPOP_FWD.  2. If we get a zone of '*',
+        * and we know what the sender meant by '*', we copy the real
+        * zone into the packet.
+        */
+       if ( nn.nn_zonelen == 0 ||
+               ( nn.nn_zonelen == 1 && *nn.nn_zone == '*' )) {
+           iface = ap->ap_iface;
+           if ( iface && iface->i_rt->rt_zt ) {
+               zt = (struct ziptab *)iface->i_rt->rt_zt->l_data;
+           } else if ( interfaces->i_next->i_rt->rt_zt ) {
+               zt = (struct ziptab *)interfaces->i_next->i_rt->rt_zt->l_data;
+           } else {
+               zt = NULL;
+           }
+
+           /*
+            * Copy zone into packet.  Note that we're changing len, data, and
+            * nbpop.  Later, we'll use ( data - len ) to mean the beginning
+            * of this packet.
+            */
+           if ( zt ) {
+               memcpy( packet, data - len, len );
+               nbpop = packet + ( len - ( data - nbpop ));
+               data = packet + ( len - ( data - zonep ));
+               *data++ = zt->zt_len;
+               memcpy( data, zt->zt_name, zt->zt_len );
+               data += zt->zt_len;
+               len = data - packet;
+           }
+       } else {
+           for ( zt = ziptab; zt; zt = zt->zt_next ) {
+               if ( zt->zt_len == nn.nn_zonelen && strndiacasecmp( zt->zt_name,
+                       nn.nn_zone, zt->zt_len ) == 0 ) {
+                   break;
+               }
+           }
+           if ( zt == 0 ) {
+               nbp_ack( ap->ap_fd, NBPOP_ERROR, (int)nh.nh_id, from );
+               return 0;
+           }
+       }
+
+       /*
+        * If we've got no zones, send out LKUP on the local net.
+        * Otherwise, look through the zone table.
+        */
+       if ( zt == 0 ) {
+#ifdef BSD4_4
+           sat.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+           sat.sat_family = AF_APPLETALK;
+           sat.sat_port = ap->ap_port;
+
+           nh.nh_op = NBPOP_LKUP;
+           memcpy( nbpop, &nh, SZ_NBPHDR );
+           sat.sat_addr.s_net = 0;                     /* XXX */
+           sat.sat_addr.s_node = ATADDR_BCAST;
+
+           /* Find the first non-loopback ap */
+           for ( iface = interfaces; iface; iface = iface->i_next ) {
+               if ((( iface->i_flags & IFACE_LOOPBACK ) == 0) &&
+                   (iface == ap->ap_iface || 
+                   (iface->i_flags & IFACE_ISROUTER))) {
+                   break;
+               }
+           }
+           if ( iface == 0 ) {
+               return 0;
+           }
+           for ( ap = iface->i_ports; ap; ap = ap->ap_next ) {
+               if ( ap->ap_packet == nbp_packet ) {
+                   break;
+               }
+           }
+
+           if ( sendto( ap->ap_fd, data - len, len, 0, (struct sockaddr *)&sat,
+                   sizeof( struct sockaddr_at )) < 0 ) {
+               syslog( LOG_ERR, "nbp brrq sendto: %m" );
+           }
+
+           locallkup = 1;
+       } else {
+#ifdef BSD4_4
+           sat.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+           sat.sat_family = AF_APPLETALK;
+           sat.sat_port = ap->ap_port;
+           for ( l = zt->zt_rt; l; l = l->l_next ) {
+               rtmp = (struct rtmptab *)l->l_data;
+
+               if ( rtmp->rt_gate == 0 ) {
+                   for ( iface = interfaces; iface;
+                           iface = iface->i_next ) {
+                       if ( iface->i_rt == rtmp ) {
+                           break;
+                       }
+                   }
+                   if ( !iface ) {
+                       syslog( LOG_ERR, "nbp_packet: \
+Can't find route's interface!" );
+                       return -1;
+                   }
+                   ap = iface->i_ports;
+               } else {
+                   ap = rtmp->rt_gate->g_iface->i_ports;
+               }
+               for ( ; ap; ap = ap->ap_next ) {
+                   if ( ap->ap_packet == nbp_packet ) {
+                       break;
+                   }
+               }
+               if ( !ap ) {
+                   syslog( LOG_ERR, "nbp_packet: Can't find port!" );
+                   return -1;
+               }
+
+               if ( transition &&
+                       ( rtmp->rt_flags & RTMPTAB_EXTENDED ) == 0 ) {
+                   if ( rtmp->rt_gate == 0 ) {
+                       locallkup = 1;
+                   }
+                   nh.nh_op = NBPOP_LKUP;
+                   bcopy( &nh, nbpop, SZ_NBPHDR );
+                   sat.sat_addr.s_net = rtmp->rt_firstnet;
+                   sat.sat_addr.s_node = ATADDR_BCAST;
+               } else {
+                   if ( rtmp->rt_gate == 0 ) {
+                       nh.nh_op = NBPOP_LKUP;
+                       bcopy( &nh, nbpop, SZ_NBPHDR );
+                       sat.sat_addr.s_net = 0;
+                       sat.sat_addr.s_node = ATADDR_BCAST;
+                       locallkup = 1;
+                   } else {
+                       nh.nh_op = NBPOP_FWD;
+                       bcopy( &nh, nbpop, SZ_NBPHDR );
+                       sat.sat_addr.s_net = rtmp->rt_firstnet;
+                       sat.sat_addr.s_node = 0;
+                   }
+               }
+
+               if ( sendto( ap->ap_fd, data - len, len, 0,
+                       (struct sockaddr *)&sat,
+                       sizeof( struct sockaddr_at )) < 0 ) {
+                   syslog( LOG_ERR, "nbp brrq sendto %u.%u: %m",
+                           ntohs( sat.sat_addr.s_net ), sat.sat_addr.s_node );
+                   continue;
+               }
+           }
+       }
+
+       if ( !locallkup ) {
+           break;
+       }
+       /*FALL THROUGH*/
+
+    case NBPOP_FWD :
+        /* send lkup on net. we need to make sure we're a router. */
+        if ( !locallkup && (ap->ap_iface->i_flags & IFACE_ISROUTER)) {
+           nh.nh_op = NBPOP_LKUP;
+           bcopy( &nh, nbpop, SZ_NBPHDR );
+           from->sat_addr.s_net = 0;
+           from->sat_addr.s_node = ATADDR_BCAST;
+           if ( sendto( ap->ap_fd, data - len, len, 0, (struct sockaddr *)from,
+                   sizeof( struct sockaddr_at )) < 0 ) {
+               syslog( LOG_ERR, "nbp fwd sendto %u.%u: %m",
+                       ntohs( from->sat_addr.s_net ), from->sat_addr.s_node );
+               return 0;
+           }
+       }
+       /*FALL THROUGH*/
+
+    case NBPOP_LKUP :
+       /* search our data */
+       n = i = 0;
+       data = packet + 1 + SZ_NBPHDR;
+       end = packet + sizeof( packet );
+
+       for ( ntab = nbptab; ntab; ntab = ntab->nt_next ) {
+           /* don't send out entries if we don't want to route. */
+           if ((ap->ap_iface != ntab->nt_iface) && 
+               (ntab->nt_iface->i_flags & IFACE_ISROUTER) == 0) {
+             continue;
+           }
+
+           if ( nn.nn_objlen != 1 || *nn.nn_obj != '=' ) {
+               if ( ntab->nt_nve.nn_objlen != nn.nn_objlen ||
+                       strndiacasecmp( ntab->nt_nve.nn_obj, nn.nn_obj,
+                       nn.nn_objlen )) {
+                   continue;
+               }
+           }
+
+           if ( nn.nn_typelen != 1 || *nn.nn_type != '=' ) {
+               if ( ntab->nt_nve.nn_typelen != nn.nn_typelen ||
+                       strndiacasecmp( ntab->nt_nve.nn_type, nn.nn_type,
+                       nn.nn_typelen )) {
+                   continue;
+               }
+           }
+
+           if ( nn.nn_zonelen != 0 &&
+                   ( nn.nn_zonelen != 1 || *nn.nn_zone != '*' )) {
+               if ( ntab->nt_nve.nn_zonelen == 0 ||
+                       ( ntab->nt_nve.nn_zonelen == 1 &&
+                       *ntab->nt_nve.nn_zone == '*' )) {
+                   if ( interfaces->i_next->i_rt->rt_zt ) {
+                       zt = (struct ziptab *)interfaces->i_next->i_rt->
+                               rt_zt->l_data;
+                       if ( zt->zt_len != nn.nn_zonelen ||
+                               strndiacasecmp( zt->zt_name, nn.nn_zone,
+                               zt->zt_len )) {
+                           continue;
+                       }
+                   }
+               } else {
+                   if ( ntab->nt_nve.nn_zonelen != nn.nn_zonelen ||
+                           strndiacasecmp( ntab->nt_nve.nn_zone, nn.nn_zone,
+                           nn.nn_zonelen )) {
+                       continue;
+                   }
+               }
+           }
+
+           /*
+            * Another tuple won't fit. Send what we've already
+            * got, and start the next packet.
+            */
+           if ( data + SZ_NBPTUPLE + 3 + ntab->nt_nve.nn_objlen +
+                   ntab->nt_nve.nn_typelen + ntab->nt_nve.nn_zonelen > end ) {
+               nh.nh_op = NBPOP_LKUPREPLY;
+               nh.nh_cnt = n;
+               cc = data - packet;
+               data = packet;
+               *data++ = DDPTYPE_NBP;
+               bcopy( &nh, data, SZ_NBPHDR );
+
+               if ( sendto( ap->ap_fd, packet, cc, 0,
+                       (struct sockaddr *)&nn.nn_sat,
+                       sizeof( struct sockaddr_at )) < 0 ) {
+                   syslog( LOG_ERR, "nbp lkup sendto %u.%u: %m",
+                           ntohs( nn.nn_sat.sat_addr.s_net ),
+                           nn.nn_sat.sat_addr.s_node );
+                   return 0;
+               }
+
+               n = 0;
+               data = packet + 1 + SZ_NBPHDR;
+               end = packet + sizeof( packet );
+           }
+
+           nt.nt_net = ntab->nt_nve.nn_sat.sat_addr.s_net;
+           nt.nt_node = ntab->nt_nve.nn_sat.sat_addr.s_node;
+           nt.nt_port = ntab->nt_nve.nn_sat.sat_port;
+           /*
+            * Right now, we'll just give each name a unique enum.  In
+            * the future, we might need to actually assign and save
+            * an enum, based on the associated address.  For the moment,
+            * the enums will be unique and constant, since the order
+            * is fixed.
+            */
+           nt.nt_enum = i++;
+
+           memcpy( data, &nt, SZ_NBPTUPLE );
+           data += SZ_NBPTUPLE;
+
+           *data++ = ntab->nt_nve.nn_objlen;
+           memcpy( data, ntab->nt_nve.nn_obj, ntab->nt_nve.nn_objlen );
+           data += ntab->nt_nve.nn_objlen;
+
+           *data++ = ntab->nt_nve.nn_typelen;
+           memcpy(data, ntab->nt_nve.nn_type, ntab->nt_nve.nn_typelen );
+           data += ntab->nt_nve.nn_typelen;
+
+           /*
+            * Macs won't see something with a zone of 0 length.  We
+            * will always return '*' instead.  Perhaps we should
+            * unconditionally return the real zone?
+            */
+           if ( ntab->nt_nve.nn_zonelen ) {
+               *data++ = ntab->nt_nve.nn_zonelen;
+               bcopy( ntab->nt_nve.nn_zone, data, ntab->nt_nve.nn_zonelen );
+               data += ntab->nt_nve.nn_zonelen;
+           } else {
+               *data++ = 1;
+               *data++ = '*';
+           }
+
+           n++;
+       }
+
+       if ( n != 0 ) {
+           nh.nh_op = NBPOP_LKUPREPLY;
+           nh.nh_cnt = n;
+           cc = data - packet;
+           data = packet;
+           *data++ = DDPTYPE_NBP;
+           bcopy( &nh, data, SZ_NBPHDR );
+
+           if ( sendto( ap->ap_fd, packet, cc, 0,
+                   (struct sockaddr *)&nn.nn_sat,
+                   sizeof( struct sockaddr_at )) < 0 ) {
+               syslog( LOG_ERR, "nbp lkup sendto %u.%u: %m",
+                       ntohs( nn.nn_sat.sat_addr.s_net ),
+                       nn.nn_sat.sat_addr.s_node );
+               return 0;
+           }
+       }
+       break;
+
+    default :
+       syslog( LOG_INFO, "nbp_packet: bad op (%d)", nh.nh_op );
+       return 1;
+    }
+
+    return 0;
+}
diff --git a/etc/atalkd/nbp.h b/etc/atalkd/nbp.h
new file mode 100644 (file)
index 0000000..42961f0
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+struct nbptab {
+    struct nbptab      *nt_prev, *nt_next;
+    struct nbpnve      nt_nve;
+    struct interface    *nt_iface;
+};
+
+extern struct nbptab   *nbptab;
diff --git a/etc/atalkd/route.c b/etc/atalkd/route.c
new file mode 100644 (file)
index 0000000..94ee92c
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 1990,1996 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/route.h>
+
+#include <netatalk/at.h>
+
+#include "rtmp.h"
+
+#ifndef BSD4_4
+route( message, dst, gate, flags )
+    int                        message;
+    struct sockaddr    *dst, *gate;
+    int                        flags;
+{
+    struct rtentry     rtent;
+
+    bzero( &rtent, sizeof( struct rtentry ));
+    rtent.rt_dst = *dst;
+    rtent.rt_gateway = *gate;
+    rtent.rt_flags = flags;
+    return( ioctl( rtfd, message, &rtent ));
+}
+
+#else BSD4_4
+
+struct sockaddr_m {
+    u_char     sam_len;
+    u_char     sam_family;
+    u_short    sam_pad;
+    u_short    sam_mask;
+} mask = { sizeof( struct sockaddr_m ), 0, 0, 0xffff };
+
+struct rt_msg_at {
+    struct rt_msghdr   rtma_rtm;
+    struct sockaddr_at rtma_dst;
+    struct sockaddr_at rtma_gate;
+    struct sockaddr_m  rtma_mask;
+} rtma;
+
+route( message, dst, gate, flags )
+    int                        message;
+    struct sockaddr_at *dst, *gate;
+    int                        flags;
+{
+    int                        rc;
+
+    bzero( &rtma, sizeof( struct rt_msg_at ));
+    rtma.rtma_rtm.rtm_msglen = sizeof( struct rt_msg_at );
+    rtma.rtma_rtm.rtm_version = RTM_VERSION;
+    rtma.rtma_rtm.rtm_type = message;
+    rtma.rtma_rtm.rtm_pid = getpid();
+    rtma.rtma_rtm.rtm_addrs = RTA_DST|RTA_GATEWAY;
+    if ( flags & RTF_HOST ) {
+       rtma.rtma_rtm.rtm_msglen = sizeof( struct rt_msg_at ) -
+               sizeof( struct sockaddr_m );
+    } else {
+       rtma.rtma_rtm.rtm_msglen = sizeof( struct rt_msg_at );
+       rtma.rtma_rtm.rtm_addrs |= RTA_NETMASK;
+       rtma.rtma_mask = mask;
+    }
+
+    rtma.rtma_rtm.rtm_flags = flags;
+    rtma.rtma_dst = *dst;
+    rtma.rtma_gate = *gate;
+    if (( rc = write( rtfd, &rtma, rtma.rtma_rtm.rtm_msglen )) !=
+           rtma.rtma_rtm.rtm_msglen ) {
+       return( -1 );
+    }
+    return( 0 );
+}
+#endif BSD4_4
diff --git a/etc/atalkd/rtmp.c b/etc/atalkd/rtmp.c
new file mode 100644 (file)
index 0000000..f5db9ef
--- /dev/null
@@ -0,0 +1,957 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <stdlib.h>
+#include <sys/syslog.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+
+#ifdef __svr4__
+#include <sys/sockio.h>
+#endif __svr4__
+
+#include <atalk/ddp.h>
+#include <atalk/atp.h>
+#include <atalk/rtmp.h>
+
+#include "interface.h"
+#include "gate.h"
+#include "rtmp.h"
+#include "zip.h"
+#include "list.h"
+#include "atserv.h"
+
+
+void rtmp_delzonemap(rtmp)
+    struct rtmptab *rtmp;
+{
+    struct list                *lz, *flz, *lr, *flr;
+    struct ziptab      *zt;
+
+    lz = rtmp->rt_zt;
+    while ( lz ) {                                     /* for each zone */
+       zt = (struct ziptab *)lz->l_data;
+       lr = zt->zt_rt;
+       while ( lr ) {                                  /* for each route */
+           if ( (struct rtmptab *)lr->l_data == rtmp ) {
+               if ( lr->l_prev == NULL ) {             /* head */
+                   if ( lr->l_next == NULL ) {         /* last route in zone */
+                       if ( zt->zt_prev == NULL ) {
+                           ziptab = zt->zt_next;
+                       } else {
+                           zt->zt_prev->zt_next = zt->zt_next;
+                       }
+                       if ( zt->zt_next == NULL ) {
+                           ziplast = zt->zt_prev;
+                       } else {
+                           zt->zt_next->zt_prev = zt->zt_prev;
+                       }
+                       free( zt->zt_bcast );
+                       free( zt->zt_name );
+                       free( zt );
+                   } else {
+                       zt->zt_rt = lr->l_next;
+                   }
+               } else {
+                   lr->l_prev->l_next = lr->l_next;
+               }
+               if ( lr->l_next != NULL ) {
+                   lr->l_next->l_prev = lr->l_prev;
+               }
+               flr = lr;
+               lr = lr->l_next;
+               free( flr );
+           } else {
+               lr = lr->l_next;
+           }
+       }
+       flz = lz;
+       lz = lz->l_next;
+       free( flz );
+    }
+    rtmp->rt_zt = NULL;
+}
+
+
+/*
+ * Complete configuration for phase 1 interface using RTMP information.
+ */
+static int rtmp_config( rh, iface )
+    struct rtmp_head   *rh;
+    struct interface   *iface;
+{
+    extern int         stabletimer;
+    int cc;
+
+    /*
+     * If we're configuring a phase 2 interface, don't complete
+     * configuration with RTMP.
+     */
+    if ( iface->i_flags & IFACE_PHASE2 ) {
+       syslog( LOG_INFO, "rtmp_config ignoring data" );
+       return 0;
+    }
+
+    /*
+     * Check our seed information, and reconfigure.
+     */
+    if ( rh->rh_net != iface->i_addr.sat_addr.s_net ) {
+       if (( iface->i_flags & IFACE_SEED ) &&
+               rh->rh_net != iface->i_caddr.sat_addr.s_net) {
+           syslog( LOG_ERR, "rtmp_config net mismatch %u != %u",
+                   ntohs( rh->rh_net ),
+                   ntohs( iface->i_addr.sat_addr.s_net ));
+           return 1;
+       }
+       iface->i_addr.sat_addr.s_net = rh->rh_net;
+
+       /*
+        * It is possible that we will corrupt our route database
+        * by just forcing this change.  XXX
+        */
+       iface->i_rt->rt_firstnet = iface->i_rt->rt_lastnet = rh->rh_net;
+
+       setaddr( iface, IFACE_PHASE1, iface->i_addr.sat_addr.s_net,
+               iface->i_addr.sat_addr.s_node, rh->rh_net, rh->rh_net );
+       stabletimer = UNSTABLE;
+    }
+
+    /* add addr to loopback route */
+    if ((cc = looproute( iface, RTMP_ADD )) < 0 )
+      return -1;
+
+    if (cc) {
+       syslog( LOG_ERR, "rtmp_config: can't route %u.%u to loopback: %m",
+               ntohs( iface->i_addr.sat_addr.s_net ),
+               iface->i_addr.sat_addr.s_node );
+    }
+
+    syslog( LOG_INFO, "rtmp_config configured %s", iface->i_name );
+    iface->i_flags |= IFACE_CONFIG;
+    if ( iface == ciface ) {
+       ciface = ciface->i_next;
+       bootaddr( ciface );
+    }
+
+    return 0;
+}
+
+/*
+ * Delete rtmp from the per-interface in-use table, remove all
+ * zone references, and remove the route from the kernel.
+ */
+static void rtmp_delinuse( rtmp )
+    struct rtmptab     *rtmp;
+{
+    struct rtmptab     *irt;
+
+    irt = rtmp->rt_gate->g_iface->i_rt;
+    if ( irt->rt_inext == rtmp ) {                     /* first */
+       if ( rtmp->rt_iprev == rtmp ) {                 /* only */
+           irt->rt_inext = NULL;
+       } else {
+           irt->rt_inext = rtmp->rt_inext;
+           rtmp->rt_inext->rt_iprev = rtmp->rt_iprev;
+       }
+    } else {
+       if ( rtmp->rt_inext == NULL ) {                 /* last */
+           rtmp->rt_iprev->rt_inext = NULL;
+           irt->rt_inext->rt_iprev = rtmp->rt_iprev;
+       } else {
+           rtmp->rt_iprev->rt_inext = rtmp->rt_inext;
+           rtmp->rt_inext->rt_iprev = rtmp->rt_iprev;
+       }
+    }
+    rtmp->rt_iprev = NULL;
+    rtmp->rt_inext = NULL;
+
+    /* remove zone map */
+    rtmp_delzonemap(rtmp);
+
+    /* remove old route */
+    gateroute( RTMP_DEL, rtmp );
+}
+
+/*
+ * Add rtmp to the per-interface in-use table.  No verification is done...
+ */
+static void rtmp_addinuse( rtmp )
+    struct rtmptab     *rtmp;
+{
+    struct rtmptab     *irt;
+
+    gateroute( RTMP_ADD, rtmp );
+
+    irt = rtmp->rt_gate->g_iface->i_rt;
+    if ( irt->rt_inext == NULL ) {     /* empty list */
+       rtmp->rt_inext = NULL;
+       rtmp->rt_iprev = rtmp;
+       irt->rt_inext = rtmp;
+    } else {
+       rtmp->rt_inext = irt->rt_inext;
+       rtmp->rt_iprev = irt->rt_inext->rt_iprev;
+       irt->rt_inext->rt_iprev = rtmp;
+       irt->rt_inext = rtmp;
+    }
+}
+
+
+/*
+ * Change the zone mapping to replace "from" with "to".  This code assumes
+ * the consistency of both the route -> zone map and the zone -> route map.
+ * This is probably a bad idea.  How can we insure that the data is good
+ * at this point?  What do we do if we get several copies of a route in
+ * an RTMP packet?
+ */
+static int rtmp_copyzones( to, from )
+    struct rtmptab     *to, *from;
+{
+    struct list                *lz, *lr;
+
+    to->rt_zt = from->rt_zt;
+    from->rt_zt = NULL;
+    if ( from->rt_flags & RTMPTAB_HASZONES ) {
+       to->rt_flags |= RTMPTAB_HASZONES;
+    }
+    for ( lz = to->rt_zt; lz; lz = lz->l_next ) {
+       for ( lr = ((struct ziptab *)lz->l_data)->zt_rt; lr; lr = lr->l_next ) {
+           if ( (struct rtmptab *)lr->l_data == from ) {
+               lr->l_data = (void *)to;        /* cast BS */
+               break;
+           }
+       }
+       if ( lr == NULL ) {
+           syslog( LOG_ERR, "rtmp_copyzones z -> r without r -> z, abort" );
+           return -1;
+       }
+    }
+
+    return 0;
+}
+
+
+/*
+ * Remove rtmp from the in-use table and the per-gate table.
+ * Free any associated space.
+ */
+void rtmp_free( rtmp )
+    struct rtmptab     *rtmp;
+{
+    struct gate                *gate;
+
+    syslog(LOG_INFO, "rtmp_free: %u-%u", ntohs(rtmp->rt_firstnet),
+          ntohs(rtmp->rt_lastnet));
+    if ( rtmp->rt_iprev ) {
+       rtmp_delinuse( rtmp );
+    }
+
+    /* remove from per-gate */
+    gate = rtmp->rt_gate;
+    if ( gate->g_rt == rtmp ) {                                /* first */
+       if ( rtmp->rt_prev == rtmp ) {                  /* only */
+           gate->g_rt = NULL;
+       } else {
+           gate->g_rt = rtmp->rt_next;
+           rtmp->rt_next->rt_prev = rtmp->rt_prev;
+       }
+    } else {
+       if ( rtmp->rt_next == NULL ) {                  /* last */
+           rtmp->rt_prev->rt_next = NULL;
+           gate->g_rt->rt_prev = rtmp->rt_prev;
+       } else {
+           rtmp->rt_prev->rt_next = rtmp->rt_next;
+           rtmp->rt_next->rt_prev = rtmp->rt_prev;
+       }
+    }
+
+    free( rtmp );
+}
+
+
+/*
+ * Find a replacement for "replace".  If we can't find a replacement,
+ * return 1.  If we do find a replacement, return 0. -1 on error.
+ */
+int rtmp_replace( replace )
+    struct rtmptab     *replace;
+{
+    struct interface   *iface;
+    struct gate                *gate;
+    struct rtmptab     *rtmp, *found = NULL;
+
+    syslog(LOG_INFO, "rtmp_replace %u-%u", ntohs(replace->rt_firstnet),
+          ntohs(replace->rt_lastnet));
+    for ( iface = interfaces; iface; iface = iface->i_next ) {
+        if ((replace->rt_iface != iface) && 
+           ((iface->i_flags & IFACE_ISROUTER) == 0))
+         continue;
+
+       for ( gate = iface->i_gate; gate; gate = gate->g_next ) {
+           for ( rtmp = gate->g_rt; rtmp; rtmp = rtmp->rt_next ) {
+               if ( rtmp->rt_firstnet == replace->rt_firstnet &&
+                       rtmp->rt_lastnet == replace->rt_lastnet ) {
+                   if ( found == NULL || rtmp->rt_hops < found->rt_hops ) {
+                       found = rtmp;
+                   }
+                   break;
+               }
+           }
+       }
+    }
+
+    if ( found != replace ) {
+       if (rtmp_copyzones( found, replace ) < 0)
+         return -1;
+       rtmp_delinuse( replace );
+       rtmp_addinuse( found );
+       if ( replace->rt_state == RTMPTAB_BAD ) {
+           rtmp_free( replace );
+       }
+       return( 0 );
+    } else {
+       if ( replace->rt_hops == RTMPHOPS_POISON ) {
+           gateroute( RTMP_DEL, replace );
+       }
+       return( 1 );
+    }
+}
+
+
+static int rtmp_new( rtmp )
+    struct rtmptab     *rtmp;
+{
+    struct interface   *i;
+    struct rtmptab     *r;
+    extern int         newrtmpdata;
+
+    newrtmpdata = 1;
+
+    /*
+     * Do we already have a gateway for this route?
+     */
+    for ( i = interfaces; i; i = i->i_next ) {
+        if ((rtmp->rt_iface != i) && 
+           ((i->i_flags & IFACE_ISROUTER) == 0))
+         continue;
+
+       for ( r = i->i_rt; r; r = r->rt_inext ) {
+           /* Should check RTMPTAB_EXTENDED here. XXX */
+           if (( ntohs( r->rt_firstnet ) <= ntohs( rtmp->rt_firstnet ) &&
+                   ntohs( r->rt_lastnet ) >= ntohs( rtmp->rt_firstnet )) ||
+                   ( ntohs( r->rt_firstnet ) <= ntohs( rtmp->rt_lastnet ) &&
+                   ntohs( r->rt_lastnet ) >= ntohs( rtmp->rt_lastnet ))) {
+               break;
+           }
+       }
+       if ( r ) {
+           break;
+       }
+    }
+
+    /*
+     * This part of this routine is almost never run.
+     */
+    if ( i ) { /* can we get here without r being set? */
+       if ( r->rt_firstnet != rtmp->rt_firstnet ||
+               r->rt_lastnet != rtmp->rt_lastnet ) {
+           syslog( LOG_INFO, "rtmp_new netrange mismatch %u-%u != %u-%u",
+                   ntohs( r->rt_firstnet ), ntohs( r->rt_lastnet ),
+                   ntohs( rtmp->rt_firstnet ), ntohs( rtmp->rt_lastnet ));
+           return 1;
+       }
+
+       /*
+        * Note that our whole methodology is wrong, if we want to do
+        * route "load balancing."  This entails changing our route
+        * each time we receive a tuple of equal value.  In fact, we can't
+        * do this, using our method, since we only check against in-use
+        * routes when a tuple is new from a router.
+        */
+       if ( r->rt_hops < rtmp->rt_hops ) {
+           return 1;
+       }
+
+       if (rtmp_copyzones( rtmp, r ) < 0)
+         return -1;
+       rtmp_delinuse( r );
+    }
+
+    rtmp_addinuse( rtmp );
+    return 0;
+}
+
+
+int rtmp_packet( ap, from, data, len )
+    struct atport      *ap;
+    struct sockaddr_at *from;
+    char               *data;
+    int                        len;
+{
+    struct rtmp_head   rh;
+    struct rtmp_tuple  rt, xrt;
+    struct gate                *gate;
+    struct interface   *iface;
+    struct rtmptab     *rtmp;
+    char               *end, packet[ ATP_BUFSIZ ];
+    int                 cc;
+
+    end = data + len;
+
+    if ( data >= end ) {
+       syslog( LOG_INFO, "rtmp_packet no data" );
+       return 1;
+    }
+
+    iface = ap->ap_iface;
+
+    /* ignore our own packets */
+    if ( from->sat_addr.s_net == iface->i_addr.sat_addr.s_net &&
+           from->sat_addr.s_node == iface->i_addr.sat_addr.s_node  ) {
+       return 0;
+    }
+
+    switch( *data++ ) {
+    case DDPTYPE_RTMPRD :
+       /*
+        * Response and Data.
+        */
+       if ( data + sizeof( struct rtmprdhdr ) > end ) {
+           syslog( LOG_INFO, "rtmp_packet no data header" );
+           return 1;
+       }
+       bcopy( data, &rh, sizeof( struct rtmprdhdr ));
+       data += sizeof( struct rtmprdhdr );
+
+       /* check rh address against from address */
+       if ( rh.rh_nodelen != 8 ) {
+           syslog( LOG_INFO, "rtmp_packet bad node len (%d)", rh.rh_nodelen );
+           return 1;
+       }
+       if (( from->sat_addr.s_net != 0 && 
+             from->sat_addr.s_net != rh.rh_net ) ||
+             from->sat_addr.s_node != rh.rh_node ) {
+           syslog( LOG_INFO, "rtmp_packet address mismatch" );
+           return 1;
+       }
+
+       if (( iface->i_flags & ( IFACE_ADDR|IFACE_CONFIG )) == IFACE_ADDR ) {
+           if ( iface->i_flags & IFACE_NOROUTER ) {
+               /* remove addr to loopback route */
+               if ((cc = looproute( iface, RTMP_DEL )) < 0) {
+                 syslog(LOG_ERR, "rtmp_packet: looproute");
+                 return -1;
+               } 
+
+               if (cc)
+                 syslog( LOG_ERR, "rtmp_packet: can't remove loopback: %m" );
+
+               iface->i_flags &= ~IFACE_NOROUTER;
+               iface->i_time = 0;
+               syslog( LOG_INFO, "rtmp_packet router has become available" );
+           }
+           if ( iface->i_flags & IFACE_PHASE1 ) {
+             if (rtmp_config( &rh, iface ) < 0) {
+                 syslog(LOG_ERR, "rtmp_packet: rtmp_config");
+                 return -1;
+             }
+           } else if (zip_getnetinfo( iface ) < 0) {
+             syslog(LOG_ERR, "rtmp_packet: zip_getnetinfo");
+             return -1;
+           }
+           return 0;
+       }
+
+       if (( iface->i_flags & IFACE_CONFIG ) == 0 ) {
+           return 0;
+       }
+
+       /*
+        * Parse first tuple.  For phase 2, verify that net is correct.
+        */
+       if ( data + SZ_RTMPTUPLE > end ) {
+           syslog( LOG_INFO, "rtmp_packet missing first tuple" );
+           return 1;
+       }
+       bcopy( data, &rt, SZ_RTMPTUPLE );
+       data += SZ_RTMPTUPLE;
+
+       if ( rt.rt_net == 0 ) {
+           if ( rt.rt_dist != 0x82 ) {
+               syslog( LOG_INFO, "rtmp_packet bad phase 1 version" );
+               return 1;
+           }
+
+           /*
+            * Grab the next tuple, since we don't want to pass the version
+            * number to the parsing code.  We're assuming that there are
+            * no extended tuples in this packet.
+            */
+           if ( data + SZ_RTMPTUPLE > end ) {
+               syslog( LOG_INFO, "rtmp_packet missing second tuple" );
+               return 1;
+           }
+           bcopy( data, &rt, SZ_RTMPTUPLE );
+           data += SZ_RTMPTUPLE;
+       } else if ( rt.rt_dist & 0x80 ) {
+           if ( data + SZ_RTMPTUPLE > end ) {
+               syslog( LOG_INFO, "rtmp_packet missing first range-end" );
+               return 1;
+           }
+           bcopy( data, &xrt, SZ_RTMPTUPLE );
+           data += SZ_RTMPTUPLE;
+
+           if ( xrt.rt_dist != 0x82 ) {
+               syslog( LOG_INFO, "rtmp_packet bad phase 2 version" );
+               return 1;
+           }
+
+           /*
+            * Check for net range conflict.
+            */
+           if ( rt.rt_net != iface->i_rt->rt_firstnet ||
+                   xrt.rt_net != iface->i_rt->rt_lastnet ) {
+               syslog( LOG_INFO, "rtmp_packet interface mismatch" );
+               return 1;
+           }
+       } else {
+#ifdef PHASE1NET
+           /*
+            * Gatorboxes put a net number in the first tuple, even on
+            * phase 1 nets.  This is wrong, but since we've got it, we
+            * might just as well check it.
+           if ( rt.rt_net != iface->i_rt->rt_firstnet ||
+                   rt.rt_net != iface->i_rt->rt_lastnet ) {
+               syslog( LOG_INFO, "rtmp_packet phase 1 interface mismatch" );
+               return 1;
+           }
+            */
+#else PHASE1NET
+           syslog( LOG_INFO, "rtmp_packet bad first tuple" );
+           return 1;
+#endif PHASE1NET
+       }
+
+       /*
+        * Find gateway.
+        */
+       for ( gate = iface->i_gate; gate; gate = gate->g_next ) {
+           if ( gate->g_sat.sat_addr.s_net == from->sat_addr.s_net &&
+                   gate->g_sat.sat_addr.s_node == from->sat_addr.s_node ) {
+               break;
+           }
+       }
+       if ( !gate ) {  /* new gateway */
+           if (( gate = (struct gate *)malloc( sizeof( struct gate ))) == 0 ) {
+               syslog( LOG_ERR, "rtmp_packet: malloc: %m" );
+               return -1;
+           }
+           gate->g_next = iface->i_gate;
+           gate->g_prev = 0;
+           gate->g_rt = 0;
+           gate->g_iface = iface;      /* need this? */
+           gate->g_sat = *from;
+           if ( iface->i_gate ) {
+               iface->i_gate->g_prev = gate;
+           }
+           iface->i_gate = gate;
+           syslog( LOG_INFO, "rtmp_packet gateway %u.%u up",
+                   ntohs( gate->g_sat.sat_addr.s_net ),
+                   gate->g_sat.sat_addr.s_node );
+       }
+
+       /*
+        * Reset the timeout on this gateway.  We'll remove the gateway
+        * entry, if the timeout gets to RTMPTAB_BAD.
+        */
+       gate->g_state = RTMPTAB_GOOD;
+
+       /*
+        * Parse remaining tuples.
+        */
+       for (;;) {
+           /*
+            * Is route on this gateway?
+            */
+           for ( rtmp = gate->g_rt; rtmp; rtmp = rtmp->rt_next ) {
+               if ( ntohs( rtmp->rt_firstnet ) <= ntohs( rt.rt_net ) &&
+                       ntohs( rtmp->rt_lastnet ) >= ntohs( rt.rt_net )) {
+                   break;
+               }
+               if (( rt.rt_dist & 0x80 ) &&
+                       ntohs( rtmp->rt_firstnet ) <= ntohs( xrt.rt_net ) &&
+                       ntohs( rtmp->rt_lastnet ) >= ntohs( xrt.rt_net )) {
+                   break;
+               }
+           }
+
+           if ( rtmp ) {       /* found it */
+               /*
+                * Check for range conflicts.  (This is getting a little
+                * ugly.)
+                */
+               if ( rtmp->rt_firstnet != rt.rt_net ) {
+                   syslog( LOG_INFO, "rtmp_packet firstnet mismatch %u!=%u",
+                           ntohs( rtmp->rt_firstnet ), ntohs( rt.rt_net ));
+                   return 1;
+               }
+               if ( rt.rt_dist & 0x80 ) {
+                   if (( rtmp->rt_flags & RTMPTAB_EXTENDED ) == 0 ) {
+                       syslog( LOG_INFO, "rtmp_packet extended mismatch %u",
+                               ntohs( rtmp->rt_firstnet ));
+                       return 1;
+                   }
+                   if ( rtmp->rt_lastnet != xrt.rt_net ) {
+                       syslog( LOG_INFO, "rtmp_packet lastnet mismatch %u!=%u",
+                           ntohs( rtmp->rt_lastnet ), ntohs( xrt.rt_net ));
+                       return 1;
+                   }
+               } else {
+                   if ( rtmp->rt_flags & RTMPTAB_EXTENDED ) {
+                       syslog( LOG_INFO, "rtmp_packet !extended mismatch %u",
+                               ntohs( rtmp->rt_firstnet ));
+                       return 1;
+                   }
+                   if ( rtmp->rt_lastnet != rt.rt_net ) {
+                       syslog( LOG_INFO, "rtmp_packet lastnet mismatch %u!=%u",
+                           ntohs( rtmp->rt_lastnet ), ntohs( rt.rt_net ));
+                       return 1;
+                   }
+               }
+
+               rtmp->rt_state = RTMPTAB_GOOD;
+
+               /*
+                * Check hop count.  If the count has changed, update
+                * the routing database.
+                */
+               if (( rtmp->rt_hops != ( rt.rt_dist & 0x7f ) + 1 ) &&
+                       ( rtmp->rt_hops != RTMPHOPS_POISON ||
+                       ( rt.rt_dist & 0x7f ) + 1 <= RTMPHOPS_MAX )) {
+                   if ( rtmp->rt_iprev ) {     /* route is in use */
+                       if ( rtmp->rt_hops > ( rt.rt_dist & 0x7f ) + 1 ) {
+                           /*
+                            * If this was POISON, we've deleted it from
+                            * the kernel.  Add it back in.
+                            */
+                           if ( rtmp->rt_hops == RTMPHOPS_POISON ) {
+                               gateroute( RTMP_ADD, rtmp );
+                           }
+                           rtmp->rt_hops = ( rt.rt_dist & 0x7f ) + 1;
+                       } else {
+                           /*
+                            * Hop count has gone up for this route.
+                            * Search for a new best route.  If we can't
+                            * find one, just keep this route.  "poison"
+                            * route are deleted in as_timer().
+                            */
+                           if (( rt.rt_dist & 0x7f ) + 1 > RTMPHOPS_MAX ) {
+                               rtmp->rt_hops = RTMPHOPS_POISON;
+                           } else {
+                               rtmp->rt_hops = ( rt.rt_dist & 0x7f ) + 1;
+                           }
+                           if (rtmp_replace( rtmp ) < 0) {
+                             syslog(LOG_ERR, "rtmp_packet: rtmp_replace");
+                             return -1;
+                           }
+                       }
+                   } else {                    /* route not in use */
+                       rtmp->rt_hops = ( rt.rt_dist & 0x7f ) + 1;
+                       if ( rtmp->rt_hops > ( rt.rt_dist & 0x7f ) + 1 ) {
+                         if (rtmp_new( rtmp ) < 0) {
+                             syslog(LOG_ERR, "rtmp_packet: rtmp_new");
+                             return -1;
+                         }
+                       }
+                   }
+               }
+
+               /*
+                * Make the *next* node the head, since
+                * we're not likely to be asked for the same tuple twice
+                * in a row.
+                */
+               if ( rtmp->rt_next != 0 ) {
+                   gate->g_rt->rt_prev->rt_next = gate->g_rt;
+                   gate->g_rt = rtmp->rt_next;
+                   rtmp->rt_next = 0;
+               }
+           } else if (( rt.rt_dist & 0x7f ) + 1 > RTMPHOPS_MAX ) {
+               syslog( LOG_INFO, "rtmp_packet bad hop count from %u.%u for %u",
+                       ntohs( from->sat_addr.s_net ), from->sat_addr.s_node,
+                       ntohs( rt.rt_net ));
+           } else {            /* new for router */
+               if (( rtmp = newrt(iface)) == NULL ) {
+                   syslog( LOG_ERR, "rtmp_packet: newrt: %m" );
+                   return -1;
+               }
+               rtmp->rt_firstnet = rt.rt_net;
+               if ( rt.rt_dist & 0x80 ) {
+                   rtmp->rt_lastnet = xrt.rt_net;
+                   rtmp->rt_flags = RTMPTAB_EXTENDED;
+               } else {
+                   rtmp->rt_lastnet = rt.rt_net;
+               }
+               rtmp->rt_hops = ( rt.rt_dist & 0x7f ) + 1;
+               rtmp->rt_state = RTMPTAB_GOOD;
+               rtmp->rt_gate = gate;
+
+               /*
+                * Add rtmptab entry to end of list (leave head alone).
+                */
+               if ( gate->g_rt == 0 ) {
+                   rtmp->rt_prev = rtmp;
+                   gate->g_rt = rtmp;
+               } else {
+                   rtmp->rt_prev = gate->g_rt->rt_prev;
+                   gate->g_rt->rt_prev->rt_next = rtmp;
+                   gate->g_rt->rt_prev = rtmp;
+               }
+               
+               if (rtmp_new( rtmp ) < 0) {
+                   syslog(LOG_ERR, "rtmp_packet: rtmp_new");
+                   return -1;
+               }
+           }
+
+           if ( data + SZ_RTMPTUPLE > end ) {
+               break;
+           }
+           bcopy( data, &rt, SZ_RTMPTUPLE );
+           data += SZ_RTMPTUPLE;
+           if ( rt.rt_dist & 0x80 ) {
+               if ( data + SZ_RTMPTUPLE > end ) {
+                   syslog( LOG_INFO, "rtmp_packet missing range-end" );
+                   return 1;
+               }
+               bcopy( data, &xrt, SZ_RTMPTUPLE );
+               data += SZ_RTMPTUPLE;
+           }
+       }
+
+       /*
+        * Make sure we've processed the whole packet.
+        */
+       if ( data != end ) {
+           syslog( LOG_INFO, "rtmp_packet length and count mismatch" );
+       }
+       break;
+
+    case DDPTYPE_RTMPR :
+       /*
+        * Request and RDR.
+        */
+        if (((iface->i_flags & IFACE_ISROUTER) == 0) ||
+           iface->i_rt->rt_zt == 0 ||
+           ( iface->i_flags & IFACE_CONFIG ) == 0 ) {
+           return 0;
+       }
+       if ( *data == 1 ) {
+           data = packet;
+           *data++ = DDPTYPE_RTMPRD;
+           rh.rh_net = iface->i_addr.sat_addr.s_net;
+           rh.rh_nodelen = 8;
+           rh.rh_node = iface->i_addr.sat_addr.s_node;
+           bcopy( &rh, data, sizeof( struct rtmp_head ));
+           data += sizeof( struct rtmp_head );
+
+           if ( iface->i_flags & IFACE_PHASE2 ) {
+               rt.rt_net = iface->i_rt->rt_firstnet;
+               rt.rt_dist = 0x80;
+               bcopy( &rt, data, SZ_RTMPTUPLE );
+               data += SZ_RTMPTUPLE;
+
+               rt.rt_net = iface->i_rt->rt_lastnet;
+               rt.rt_dist = 0x82;
+               bcopy( &rt, data, SZ_RTMPTUPLE );
+               data += SZ_RTMPTUPLE;
+           }
+           if ( sendto( ap->ap_fd, packet, data - packet, 0,
+                   (struct sockaddr *)from,
+                   sizeof( struct sockaddr_at )) < 0 ) {
+               syslog( LOG_ERR, "as_timer sendto: %m" );
+           }
+       } else if ( *data == 2 || *data == 3 ) {
+#ifdef DEBUG
+           printf( "rtmp_packet rdr (%d) from %u.%u\n",
+                   *data, ntohs( from->sat_addr.s_net ),
+                   from->sat_addr.s_node );
+#endif DEBUG
+       } else {
+           syslog( LOG_INFO, "rtmp_packet unknown request from %u.%u\n",
+                   ntohs( from->sat_addr.s_net ), from->sat_addr.s_node );
+       }
+       break;
+
+    default :
+       syslog( LOG_INFO, "rtmp_packet bad ddp type from %u.%u",
+                   ntohs( from->sat_addr.s_net ), from->sat_addr.s_node );
+       return 0;
+    }
+
+    return 0;
+}
+
+int rtmp_request( iface )
+    struct interface   *iface;
+{
+    struct sockaddr_at sat;
+    struct atport      *ap;
+    char               *data, packet[ 2 ];
+
+    syslog( LOG_INFO, "rtmp_request for %s", iface->i_name );
+
+    for ( ap = iface->i_ports; ap; ap = ap->ap_next ) {
+       if ( ap->ap_packet == rtmp_packet ) {
+           break;
+       }
+    }
+    if ( ap == 0 ) {
+       syslog( LOG_ERR, "rtmp_request can't find rtmp socket!" );
+       return -1;
+    }
+
+    data = packet;
+    *data++ = DDPTYPE_RTMPR;
+    *data++ = RTMPROP_REQUEST;
+
+    /*
+     * There is a problem with the net zero "hint" hack.
+     */
+    bzero( &sat, sizeof( struct sockaddr_at ));
+#ifdef BSD4_4
+    sat.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+    sat.sat_family = AF_APPLETALK;
+    sat.sat_addr.s_net = iface->i_addr.sat_addr.s_net;
+    sat.sat_addr.s_node = ATADDR_BCAST;
+    sat.sat_port = ap->ap_port;
+    if ( sendto( ap->ap_fd, packet, data - packet, 0, (struct sockaddr *)&sat,
+           sizeof( struct sockaddr_at )) < 0 ) {
+       syslog( LOG_ERR, "rtmp_request sendto: %m" );
+       return -1;
+    }
+    return 0;
+}
+
+
+int looproute( iface, cmd )
+    struct interface   *iface;
+    int                        cmd;
+{
+    struct sockaddr_at dst, loop;
+
+    if ( cmd == RTMP_DEL && ( iface->i_flags & IFACE_LOOP ) == 0 ) {
+       syslog( LOG_ERR, "looproute panic no route" );
+       return -1;
+    }
+
+    if ( cmd == RTMP_ADD && ( iface->i_flags & IFACE_LOOP )) {
+       syslog( LOG_ERR, "looproute panic two routes" );
+       return -1;
+    }
+
+    bzero( &dst, sizeof( struct sockaddr_at ));
+#ifdef BSD4_4
+    dst.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+    dst.sat_family = AF_APPLETALK;
+    dst.sat_addr.s_net = iface->i_addr.sat_addr.s_net;
+    dst.sat_addr.s_node = iface->i_addr.sat_addr.s_node;
+    bzero( &loop, sizeof( struct sockaddr_at ));
+#ifdef BSD4_4
+    loop.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+    loop.sat_family = AF_APPLETALK;
+    loop.sat_addr.s_net = htons( ATADDR_ANYNET );
+    loop.sat_addr.s_node = ATADDR_ANYNODE;
+
+    if ( route( cmd, &dst, &loop, RTF_UP | RTF_HOST )) {
+       return( 1 );
+    }
+    if ( cmd == RTMP_ADD ) {
+       iface->i_flags |= IFACE_LOOP;
+    }
+    if ( cmd == RTMP_DEL ) {
+       iface->i_flags &= ~IFACE_LOOP;
+    }
+    return( 0 );
+}
+
+int gateroute( command, rtmp )
+    int                        command;
+    struct rtmptab     *rtmp;
+{
+    struct sockaddr_at dst, gate;
+    unsigned short     net;
+
+    if ( command == RTMP_DEL && ( rtmp->rt_flags & RTMPTAB_ROUTE ) == 0 ) {
+       return( -1 );
+    }
+    if ( command == RTMP_ADD && ( rtmp->rt_flags & RTMPTAB_ROUTE )) {
+       return( -1 );
+    }
+
+    net = ntohs( rtmp->rt_firstnet );
+    /*
+     * Since we will accept routes from gateways who advertise their
+     * address as 0.YY, we must munge the gateway address we give to
+     * the kernel.  Otherwise, we'll get a bunch of routes to the loop
+     * back interface, and who wants that?
+     */
+    bzero( &gate, sizeof( struct sockaddr_at ));
+#ifdef BSD4_4
+    gate.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+    gate.sat_family = AF_APPLETALK;
+    gate.sat_addr.s_net = rtmp->rt_gate->g_sat.sat_addr.s_net;
+    gate.sat_addr.s_node = rtmp->rt_gate->g_sat.sat_addr.s_node;
+    if ( gate.sat_addr.s_net == 0 ) {
+       gate.sat_addr.s_net = net;
+    }
+
+    bzero( &dst, sizeof( struct sockaddr_at ));
+#ifdef BSD4_4
+    dst.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+    dst.sat_family = AF_APPLETALK;
+    dst.sat_addr.s_node = ATADDR_ANYNODE;
+
+    do {
+       dst.sat_addr.s_net = htons( net );
+       if ( route( command, &dst, &gate, RTF_UP | RTF_GATEWAY )) {
+           syslog( LOG_ERR, "route: %u -> %u.%u: %m", net,
+                   ntohs( gate.sat_addr.s_net ), gate.sat_addr.s_node );
+           continue;
+       }
+    } while ( net++ < ntohs( rtmp->rt_lastnet ));
+
+    if ( command == RTMP_ADD ) {
+       rtmp->rt_flags |= RTMPTAB_ROUTE;
+    }
+    if ( command == RTMP_DEL ) {
+       rtmp->rt_flags &= ~RTMPTAB_ROUTE;
+    }
+
+    return( 0 );
+}
+
+    struct rtmptab *
+newrt(const struct interface *iface)
+{
+    struct rtmptab     *rtmp;
+
+    if (( rtmp = (struct rtmptab *)calloc(1, sizeof(struct rtmptab))) == 0 ) {
+       return( 0 );
+    }
+
+    rtmp->rt_iface = iface;
+    return( rtmp );
+}
diff --git a/etc/atalkd/rtmp.h b/etc/atalkd/rtmp.h
new file mode 100644 (file)
index 0000000..edc3e74
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+/*
+ * We have an rtmptab circular linked list for each gateway.  Entries
+ * are inserted in the order we get them.  The expectation is that
+ * we will get a complexity of N for the stable case.  If we have N
+ * existing entries, and M new entries, we'll have on the order of
+ * N + ( M * N ) complexity (really it will be something more than
+ * that, maybe N + ( M * ( N + 1/2 M )).  Note that having a list to
+ * search is superior to a hash table if you are expecting bad data:
+ * you have the opportunity to range-check the incoming data.
+ *
+ * We keep several ZIP related flags and counters here.  For ZIP Extended
+ * Replies, we must keep a flag indicating that the zone is up or down.
+ * This flag is necessary for ZIP Extended Replies which cross packet
+ * boundaries: even tho the rtmptab entry has data, it is not yet
+ * complete.  For ZIP in general, we keep a flag indicating that we've
+ * asked for a ZIP (E)Reply.  If this flag is not set, we won't process
+ * ZIP Reply data for given rtmptab entries.  Lastly, we keep a count of
+ * the number of times we've asked for ZIP Reply data.  When this value
+ * reaches some value (3?), we can optionally stop asking.
+ */
+
+#ifndef ATALKD_RTMP_H
+#define ATALKD_RTMP_H 1
+
+#include <sys/cdefs.h>
+
+struct rtmptab {
+    struct rtmptab     *rt_next,
+                       *rt_prev;
+    struct rtmptab     *rt_inext,
+                       *rt_iprev;
+    u_short            rt_firstnet, rt_lastnet;
+    u_char             rt_hops;
+    u_char             rt_state;
+    u_char             rt_flags;
+    u_char             rt_nzq;         /* number of zip queries issued */
+    struct gate                *rt_gate;       /* gate is NULL for interfaces */
+    struct list                *rt_zt;
+    const struct interface    *rt_iface;
+};
+
+struct rtmp_head {
+    u_short    rh_net;
+    u_char     rh_nodelen;
+    u_char     rh_node;
+};
+
+struct rtmp_tuple {
+    u_short    rt_net;
+    u_char     rt_dist;
+};
+#define SZ_RTMPTUPLE   3
+
+#define RTMPTAB_PERM   0
+#define RTMPTAB_GOOD   1
+#define RTMPTAB_SUSP1  2
+#define RTMPTAB_SUSP2  3
+#define RTMPTAB_BAD    4
+
+#define RTMPTAB_ZIPQUERY       0x01
+#define RTMPTAB_HASZONES       0x02
+#define RTMPTAB_EXTENDED       0x04
+#define RTMPTAB_ROUTE          0x08
+
+#ifndef BSD4_4
+#define RTMP_ADD       SIOCADDRT
+#define RTMP_DEL       SIOCDELRT
+#else BSD4_4
+#define RTMP_ADD       RTM_ADD
+#define RTMP_DEL       RTM_DELETE
+#endif BSD4_4
+
+#define STARTUP_FIRSTNET       0xff00
+#define STARTUP_LASTNET                0xfffe
+
+extern int     rtfd;
+struct rtmptab *newrt __P((const struct interface *));
+void rtmp_delzonemap  __P((struct rtmptab *));
+
+#endif /* atalkd/rtmp.h */
diff --git a/etc/atalkd/zip.c b/etc/atalkd/zip.c
new file mode 100644 (file)
index 0000000..246fcb5
--- /dev/null
@@ -0,0 +1,1049 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/syslog.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+
+#ifdef __svr4__
+#include <sys/sockio.h>
+#endif __svr4__
+
+#include <atalk/ddp.h>
+#include <atalk/zip.h>
+#include <atalk/atp.h>
+#include <atalk/util.h>
+
+#include "atserv.h"
+#include "interface.h"
+#include "gate.h"
+#include "zip.h"
+#include "rtmp.h"
+#include "list.h"
+#include "multicast.h"
+
+struct ziptab  *ziptab = NULL, *ziplast = NULL;
+
+static int zonecheck( rtmp, iface )
+    struct rtmptab     *rtmp;
+    struct interface   *iface;
+{
+    struct list                *l;
+    struct ziptab      *czt, *zt;
+    int                        cztcnt, ztcnt;
+
+    if (( iface->i_flags & IFACE_SEED ) == 0 ) {
+       return( 0 );
+    }
+
+    for ( cztcnt = 0, czt = iface->i_czt; czt; czt = czt->zt_next, cztcnt++ ) {
+       for ( l = rtmp->rt_zt; l; l = l->l_next ) {
+           zt = (struct ziptab *)l->l_data;
+           if ( czt->zt_len == zt->zt_len &&
+                   !strndiacasecmp( czt->zt_name, zt->zt_name, czt->zt_len )) {
+               break;
+           }
+       }
+       if ( l == 0 ) {
+           syslog( LOG_ERR, "zonecheck: %.*s not in zone list", czt->zt_len,
+                   czt->zt_name );
+           return( -1 );       /* configured zone not found in net zones */
+       }
+    }
+
+    for ( ztcnt = 0, l = rtmp->rt_zt; l; l = l->l_next, ztcnt++ )
+       ;
+
+    if ( cztcnt != ztcnt ) {
+       syslog( LOG_ERR, "zonecheck: %d configured zones, %d zones found",
+               cztcnt, ztcnt );
+       return( -1 );           /* more net zones than configured zones */
+    }
+
+    return( 0 );
+}
+
+
+int zip_packet( ap, from, data, len )
+    struct atport      *ap;
+    struct sockaddr_at *from;
+    char               *data;
+    int                        len;
+{
+    struct ziphdr      zh;
+    struct atphdr      ah;
+    struct interface   *iface;
+    struct gate                *gate;
+    struct rtmptab     *rtmp;
+    struct list                *l;
+    struct ziptab      *zt;
+    u_short            firstnet, lastnet, index, nz;
+    char               *end, zname[ 32 ], packet[ ATP_BUFSIZ ], *nzones, *lastflag;
+    char               *reply, *rend, *ziphdr;
+    int                        zlen, n, zipop, rcnt, qcnt, zcnt, zsz;
+    extern int         debug, stabletimer;
+
+    end = data + len;
+
+    if ( data >= end ) {
+       syslog( LOG_INFO, "zip_packet malformed packet" );
+       return 1;
+    }
+
+    /* get interface */
+    iface = ap->ap_iface;
+
+    switch( *data++ ) {
+    case DDPTYPE_ZIP :
+       if ( data + sizeof( struct ziphdr ) > end ) {
+           syslog( LOG_INFO, "zip_packet malformed packet" );
+           return 1;
+       }
+       memcpy( &zh, data, sizeof( struct ziphdr ));
+       data += sizeof( struct ziphdr );
+
+       switch ( zh.zh_op ) {
+       case ZIPOP_QUERY :
+           /* set up reply */
+           reply = packet;
+           rend = packet + sizeof( packet );
+           *reply++ = DDPTYPE_ZIP;
+           ziphdr = reply;
+           reply += 2;
+           rcnt = 0;
+
+           qcnt = zh.zh_count;
+
+           while ( data + sizeof( u_short ) <= end && qcnt-- > 0 ) {
+               memcpy( &firstnet, data, sizeof( u_short ));
+               data += sizeof( u_short );
+
+               /*
+                * Look for the given network number (firstnet).
+                * Perhaps we could do better than brute force?
+                */
+               for ( iface = interfaces; iface; iface = iface->i_next ) {
+                   for ( rtmp = iface->i_rt; rtmp; rtmp = rtmp->rt_inext ) {
+                       if ( firstnet == rtmp->rt_firstnet ) {
+                           break;
+                       }
+                   }
+                   if ( rtmp ) {
+                       break;
+                   }
+               }
+               if ( rtmp == 0 ) {
+                   continue;
+               }
+
+               /*
+                * Count the number of zones in this list, and the
+                * number of byte it will consume in a reply.
+                */
+               for ( zsz = 0, zcnt = 0, l = rtmp->rt_zt; l; l = l->l_next ) {
+                   zcnt++;
+                   zt = (struct ziptab *)l->l_data;
+                   zsz += sizeof( u_short ) + 1 + zt->zt_len;
+               }
+
+               /*
+                * We might send this list in the current reply, as the
+                * first thing in the next reply, or as an extended packet.
+                */
+               if ( reply + zsz > rend ) {
+                   if ( rcnt > 0 ) {
+                       zh.zh_op = ZIPOP_REPLY;
+                       zh.zh_cnt = rcnt;
+                       memcpy( ziphdr, &zh, sizeof( struct ziphdr ));
+                       if ( sendto( ap->ap_fd, packet, reply - packet, 0,
+                               (struct sockaddr *)from,
+                               sizeof( struct sockaddr_at )) < 0 ) {
+                           syslog( LOG_ERR, "zip reply sendto: %m" );
+                       }
+
+                       reply = packet + 3;
+                       rcnt = 0;
+                   }
+
+                   if ( reply + zsz > rend ) {
+                       /* ereply */
+                       for ( l = rtmp->rt_zt; l; l = l->l_next, rcnt++ ) {
+                           zt = (struct ziptab *)l->l_data;
+                           if ( reply + sizeof( u_short ) + 1 + zt->zt_len >
+                                   rend ) {
+                               zh.zh_op = ZIPOP_EREPLY;
+                               zh.zh_cnt = zcnt;
+                               memcpy( ziphdr, &zh, sizeof( struct ziphdr ));
+                               if ( sendto( ap->ap_fd, packet, reply - packet,
+                                       0, (struct sockaddr *)from,
+                                       sizeof( struct sockaddr_at )) < 0 ) {
+                                   syslog( LOG_ERR, "zip reply sendto: %m" );
+                               }
+
+                               reply = packet + 3;
+                               rcnt = 0;
+                           }
+
+                           memcpy( reply, &firstnet, sizeof( u_short ));
+                           reply += sizeof( u_short );
+                           *reply++ = zt->zt_len;
+                           memcpy( reply, zt->zt_name, zt->zt_len );
+                           reply += zt->zt_len;
+                       }
+
+                       if ( rcnt > 0 ) {
+                           zh.zh_op = ZIPOP_EREPLY;
+                           zh.zh_cnt = zcnt;
+                           memcpy( ziphdr, &zh, sizeof( struct ziphdr ));
+                           if ( sendto( ap->ap_fd, packet, reply - packet, 0,
+                                   (struct sockaddr *)from,
+                                   sizeof( struct sockaddr_at )) < 0 ) {
+                               syslog( LOG_ERR, "zip reply sendto: %m" );
+                           }
+
+                           reply = packet + 3;
+                           rcnt = 0;
+                       }
+                       continue;
+                   }
+               }
+
+               for ( l = rtmp->rt_zt; l; l = l->l_next, rcnt++ ) {
+                   zt = (struct ziptab *)l->l_data;
+                   memcpy( reply, &firstnet, sizeof( u_short ));
+                   reply += sizeof( u_short );
+                   *reply++ = zt->zt_len;
+                   memcpy( reply, zt->zt_name, zt->zt_len );
+                   reply += zt->zt_len;
+               }
+           }
+
+           if ( rcnt > 0 ) {
+               zh.zh_op = ZIPOP_REPLY;
+               zh.zh_cnt = rcnt;
+               memcpy( ziphdr, &zh, sizeof( struct ziphdr ));
+               if ( sendto( ap->ap_fd, packet, reply - packet, 0,
+                       (struct sockaddr *)from,
+                       sizeof( struct sockaddr_at )) < 0 ) {
+                   syslog( LOG_ERR, "zip reply sendto: %m" );
+               }
+           }
+           break;
+
+       case ZIPOP_REPLY :
+           for ( gate = iface->i_gate; gate; gate = gate->g_next ) {
+               if (( from->sat_addr.s_net == 0 ||
+                       gate->g_sat.sat_addr.s_net == from->sat_addr.s_net ) &&
+                       gate->g_sat.sat_addr.s_node == from->sat_addr.s_node ) {
+                   break;
+               }
+           }
+           if ( gate == NULL ) {
+               syslog( LOG_INFO, "zip reply from non-gateway %u.%u", 
+                   ntohs( from->sat_addr.s_net ), from->sat_addr.s_node );
+               return 1;
+           }
+
+           rtmp = NULL;
+
+           do {
+               if ( data + sizeof( u_short ) + 1 > end ) {     /* + strlen */
+                   syslog( LOG_INFO, "zip reply short (%d)", len );
+                   return 1;
+               }
+               memcpy( &firstnet, data, sizeof( u_short ));
+               data += sizeof( u_short );
+
+               if ( rtmp && rtmp->rt_firstnet != firstnet ) {
+                   /* XXX */
+                   if ( rtmp->rt_gate == NULL &&
+                           zonecheck( rtmp, gate->g_iface ) != 0 ) {
+                       syslog( LOG_ERR, "zip_packet seed zonelist mismatch" );
+                       return -1;
+                   }
+                   rtmp->rt_flags &= ~RTMPTAB_ZIPQUERY;
+               }
+
+               /* Check if this is the interface's route. */
+               if ( firstnet == gate->g_iface->i_rt->rt_firstnet ) {
+                   rtmp = gate->g_iface->i_rt;
+               } else {
+                   for ( rtmp = gate->g_rt; rtmp; rtmp = rtmp->rt_next ) {
+                       if ( rtmp->rt_firstnet == firstnet ) {
+                           break;
+                       }
+                   }
+
+                   /*
+                    * Update head to this rtmp entry.
+                    */
+                   if ( rtmp != 0 && gate->g_rt != rtmp ) {
+                       gate->g_rt->rt_prev->rt_next = gate->g_rt;
+                       gate->g_rt = rtmp;
+                       rtmp->rt_prev->rt_next = 0;
+                   }
+               }
+
+               zlen = *data++;
+               if ( zlen > 32 || zlen <= 0 ) {
+                   syslog( LOG_INFO, "zip reply bad packet" );
+                   return 1;
+               }
+               if ( data + zlen > end ) {
+                   syslog( LOG_INFO, "zip reply short (%d)", len );
+                   return 1;
+               }
+               memcpy( zname, data, zlen );
+               data += zlen;
+
+               /*
+                * We won't find any rtmp entry if the gateway is no longer
+                * telling us about the entry.
+                */
+               if ( rtmp == 0 ) {
+                   syslog( LOG_INFO, "zip skip reply %u from %u.%u (no rtmp)",
+                           ntohs( firstnet ), ntohs( from->sat_addr.s_net ),
+                           from->sat_addr.s_node );
+               /*
+                * Check if the route is still in use (the iprev check is
+                * no good if rtmp is the interface's route).
+                */
+               } else if ( rtmp->rt_iprev == NULL && rtmp->rt_prev != NULL ) {
+                   syslog( LOG_INFO,
+                           "zip skip reply %u-%u from %u.%u (rtmp not in use)",
+                           ntohs( rtmp->rt_firstnet ),
+                           ntohs( rtmp->rt_lastnet ),
+                           ntohs( from->sat_addr.s_net ),
+                           from->sat_addr.s_node );
+               /*
+                * Check if we've got an outstanding query for this route.
+                * We will often get this, since we ask every router on a
+                * net to verify our interface's zone(s).
+                */
+               } else if (( rtmp->rt_flags & RTMPTAB_ZIPQUERY ) == 0 ) {
+                   syslog( LOG_INFO,
+                           "zip skip reply %u-%u from %u.%u (no query)",
+                           ntohs( rtmp->rt_firstnet ),
+                           ntohs( rtmp->rt_lastnet ),
+                           ntohs( from->sat_addr.s_net ),
+                           from->sat_addr.s_node );
+               } else {
+                   if (addzone( rtmp, zlen, zname ) < 0) {
+                       syslog(LOG_ERR, "zip_packet: addzone");
+                       return -1;
+                   }
+                   rtmp->rt_flags |= RTMPTAB_HASZONES;
+               }
+           } while ( data < end );
+
+           if ( rtmp && rtmp->rt_flags & RTMPTAB_HASZONES ) {
+               /* XXX */
+               if ( rtmp->rt_gate == 0 &&
+                       zonecheck( rtmp, gate->g_iface ) != 0 ) {
+                   syslog( LOG_ERR, "zip_packet seed zonelist mismatch" );
+                   return -1;
+               }
+               rtmp->rt_flags &= ~RTMPTAB_ZIPQUERY;
+           }
+           break;
+
+       case ZIPOP_EREPLY :
+           for ( gate = iface->i_gate; gate; gate = gate->g_next ) {
+               if (( from->sat_addr.s_net == 0 ||
+                       gate->g_sat.sat_addr.s_net == from->sat_addr.s_net ) &&
+                       gate->g_sat.sat_addr.s_node == from->sat_addr.s_node ) {
+                   break;
+               }
+           }
+           if ( gate == NULL ) {
+               syslog( LOG_INFO, "zip ereply from non-gateway %u.%u", 
+                   ntohs( from->sat_addr.s_net ), from->sat_addr.s_node );
+               return 1;
+           }
+
+           /*
+            * Note that we're not advancing "data" here.  We do that
+            * at the top of the do-while loop, below.
+            */
+           if ( data + sizeof( u_short ) + 1 > end ) { /* + strlen */
+               syslog( LOG_INFO, "zip ereply short (%d)", len );
+               return 1;
+           }
+           memcpy( &firstnet, data, sizeof( u_short ));
+
+           /* Check if this is the interface's route. */
+           if ( firstnet == gate->g_iface->i_rt->rt_firstnet ) {
+               rtmp = gate->g_iface->i_rt;
+           } else {
+               for ( rtmp = gate->g_rt; rtmp; rtmp = rtmp->rt_next ) {
+                   if ( rtmp->rt_firstnet == firstnet ) {
+                       break;
+                   }
+               }
+               if ( rtmp == NULL ) {
+                   syslog( LOG_INFO, "zip ereply %u from %u.%u (no rtmp)",
+                           ntohs( firstnet ), ntohs( from->sat_addr.s_net ),
+                           from->sat_addr.s_node );
+                   return 1;
+               }
+               if ( rtmp->rt_iprev == 0 ) {
+                   syslog( LOG_INFO,
+                           "zip ereply %u-%u from %u.%u (rtmp not in use)",
+                           ntohs( rtmp->rt_firstnet ),
+                           ntohs( rtmp->rt_lastnet ),
+                           ntohs( from->sat_addr.s_net ),
+                           from->sat_addr.s_node );
+               }
+
+               /* update head to *next* rtmp entry */
+               if ( rtmp->rt_next != 0 ) {
+                   gate->g_rt->rt_prev->rt_next = gate->g_rt;
+                   gate->g_rt = rtmp->rt_next;
+                   rtmp->rt_next = 0;
+               }
+           }
+
+           if (( rtmp->rt_flags & RTMPTAB_ZIPQUERY ) == 0 ) {
+               syslog( LOG_INFO, "zip ereply %u-%u from %u.%u (no query)",
+                       ntohs( rtmp->rt_firstnet ),
+                       ntohs( rtmp->rt_lastnet ),
+                       ntohs( from->sat_addr.s_net ),
+                       from->sat_addr.s_node );
+               return 0;
+           }
+
+           do {
+               /*
+                * We copy out firstnet, twice (see above).  Not
+                * a big deal, and it makes the end condition cleaner.
+                */
+               if ( data + sizeof( u_short ) + 1 > end ) {     /* + strlen */
+                   syslog( LOG_INFO, "zip ereply short (%d)", len );
+                   return 1;
+               }
+               memcpy( &firstnet, data, sizeof( u_short ));
+               data += sizeof( u_short );
+
+               /* check route */
+               if ( firstnet != rtmp->rt_firstnet ) {
+                   syslog( LOG_INFO, "zip ereply with multiple nets" );
+                   return 1;
+               }
+
+               zlen = *data++;
+               if ( zlen > 32 || zlen <= 0 ) {
+                   syslog( LOG_INFO, "zip ereply bad zone length (%d)", zlen );
+                   return 1;
+               }
+               if ( data + zlen > end ) {
+                   syslog( LOG_INFO, "zip ereply short (%d)", len );
+                   return 1;
+               }
+               memcpy( zname, data, zlen );
+               data += zlen;
+               if (addzone( rtmp, zlen, zname ) < 0) {
+                   syslog(LOG_ERR, "zip_packet: addzone");
+                   return -1;
+               }
+           } while ( data < end );
+
+           if ( rtmp ) {
+               /*
+                * Count zones for rtmptab entry.
+                */
+               for ( n = 0, l = rtmp->rt_zt; l; l = l->l_next, n++ )
+                   ;
+               if ( n == zh.zh_count ) {
+                   rtmp->rt_flags |= RTMPTAB_HASZONES;
+                   /* XXX */
+                   if ( rtmp->rt_gate == 0 &&
+                           zonecheck( rtmp, gate->g_iface ) != 0 ) {
+                       syslog( LOG_ERR, "zip_packet seed zonelist mismatch" );
+                       return -1;
+                   }
+                   rtmp->rt_flags &= ~RTMPTAB_ZIPQUERY;
+               }
+           }
+           break;
+
+       case ZIPOP_GNI :
+           /*
+            * Don't answer with bogus information.
+            */
+           if (((iface->i_flags & IFACE_ISROUTER) == 0) ||
+               iface->i_rt->rt_zt == 0 ||
+               ( iface->i_flags & IFACE_CONFIG ) == 0 ) {
+               return 0;
+           }
+
+           if ( zh.zh_zero != 0 || data + 2 * sizeof( u_short ) > end ) {
+               syslog( LOG_INFO, "zip_packet malformed packet" );
+               return 1;
+           }
+
+           memcpy( &firstnet, data, sizeof( u_short ));
+           data += sizeof( u_short );
+           memcpy( &lastnet, data, sizeof( u_short ));
+           data += sizeof( u_short );
+           if ( firstnet != 0 || lastnet != 0 || data >= end ) {
+               syslog( LOG_INFO, "zip_packet malformed packet" );
+               return 1;
+           }
+
+           zlen = *data++;
+           if ( zlen < 0 || zlen > 32 ) {
+               syslog( LOG_INFO, "zip_packet malformed packet" );
+               return 1;
+           }
+           memcpy( zname, data, zlen );
+
+           data = packet;
+           end = data + sizeof( packet );
+           zh.zh_op = ZIPOP_GNIREPLY;
+           zh.zh_flags = 0;
+
+           /*
+            * Skip to the nets.  Fill in header when we're done.
+            */
+           data += 1 + sizeof( struct ziphdr );
+           memcpy( data, &iface->i_rt->rt_firstnet, sizeof( u_short ));
+           data += sizeof( u_short );
+           memcpy( data, &iface->i_rt->rt_lastnet, sizeof( u_short ));
+           data += sizeof( u_short );
+
+           *data++ = zlen;
+           memcpy( data, zname, zlen );
+           data += zlen;
+
+           /*
+            * Check if the given zone is valid.  If it's valid, just fill in
+            * the multicast address.  If it's not, fill the multicast address
+            * in with the default zone and return the default zone.
+            */
+           for ( l = iface->i_rt->rt_zt; l; l = l->l_next ) {
+               zt = (struct ziptab *)l->l_data;
+               if ( zt->zt_len == zlen &&
+                       strndiacasecmp( zname, zt->zt_name, zlen ) == 0 ) {
+                   break;
+               }
+           }
+           if ( l == 0 ) {
+               zt = (struct ziptab *)iface->i_rt->rt_zt->l_data;
+               zh.zh_flags |= ZIPGNI_INVALID;
+           }
+
+           for ( n = 0, l = iface->i_rt->rt_zt; l; l = l->l_next, n++ )
+               ;
+           if ( n == 1 ) {
+               zh.zh_flags |= ZIPGNI_ONEZONE;
+           }
+
+           /* multicast */
+           *data++ = 6;        /* sizeof ??? */
+           if (zone_bcast(zt) < 0) {
+             syslog(LOG_ERR, "zip_packet: zone_bcast");
+             return -1;
+           }
+           memcpy(data, zt->zt_bcast, 6);
+           data += 6;
+
+           /*
+            * Add default zone.
+            */
+           if ( zh.zh_flags & ZIPGNI_INVALID ) {
+               *data++ = zt->zt_len;
+               memcpy( data, zt->zt_name, zt->zt_len );
+               data += zt->zt_len;
+           }
+
+           /* fill in header */
+           *packet = DDPTYPE_ZIP;
+           memcpy( packet + 1, &zh, sizeof( struct ziphdr ));
+
+           /*
+            * If the address we received this request from isn't correct
+            * for the net we received it on, send a broadcast.
+            */
+           if ( ntohs( from->sat_addr.s_net ) <
+                   ntohs( iface->i_rt->rt_firstnet ) ||
+                   ntohs( from->sat_addr.s_net ) >
+                   ntohs( iface->i_rt->rt_lastnet )) {
+               from->sat_addr.s_net = 0;
+               from->sat_addr.s_node = ATADDR_BCAST;
+           }
+
+           if ( sendto( ap->ap_fd, packet, data - packet, 0,
+                   (struct sockaddr *)from,
+                   sizeof( struct sockaddr_at )) < 0 ) {
+               syslog( LOG_ERR, "zip gni sendto %u.%u: %m",
+                       ntohs( from->sat_addr.s_net ), from->sat_addr.s_node );
+               return 1;
+           }
+           break;
+
+       case ZIPOP_GNIREPLY :
+           /*
+            * Ignore ZIP GNIReplys which are either late or unsolicited.
+            */
+           syslog( LOG_INFO, "zip gnireply from %u.%u (%s %x)",
+                   ntohs( from->sat_addr.s_net ), from->sat_addr.s_node,
+                   iface->i_name, iface->i_flags );
+
+           if (( iface->i_flags & ( IFACE_CONFIG|IFACE_PHASE1 )) ||
+                   ( iface->i_flags & IFACE_ADDR ) == 0 ) {
+               syslog( LOG_INFO, "zip ignoring gnireply" );
+               return 1;
+           }
+
+           if ( data + 2 * sizeof( u_short ) > end ) {
+               syslog( LOG_INFO, "zip_packet malformed packet" );
+               return 1;
+           }
+           memcpy( &firstnet, data, sizeof( u_short ));
+           data += sizeof( u_short );
+           memcpy( &lastnet, data, sizeof( u_short ));
+           data += sizeof( u_short );
+
+           /*
+            * We never ask for a zone, so we can get back what the
+            * default zone is.
+            */
+           if ( data >= end || data + *data > end ) {
+               syslog( LOG_INFO, "zip_packet malformed packet" );
+               return 1;
+           }
+           if ( *data++ != 0 ) {
+               syslog( LOG_INFO, "zip_packet unsolicited zone" );
+               return 1;
+           }
+
+           /* skip multicast (should really check it) */
+           if ( data >= end || data + *data > end ) {
+               syslog( LOG_INFO, "zip_packet malformed packet" );
+               return 1;
+           }
+           data += *data + 1;
+
+           if ( data >= end || data + *data > end ) {
+               syslog( LOG_INFO, "zip_packet malformed packet" );
+               return 1;
+           }
+
+           /*
+            * First, if we're not seed, we always get our zone information
+            * from the net -- we don't even save what was in the file.
+            * Second, if we are seed, we keep our zone list in the
+            * interface structure, not in the zone table.  This allows us
+            * to check that the net is giving us good zones.
+            */
+           if ( iface->i_flags & IFACE_SEED ) {
+               if ( iface->i_czt->zt_len != *data ||
+                       strndiacasecmp( iface->i_czt->zt_name,
+                       data + 1, *data ) != 0 ) {
+                   syslog( LOG_ERR, "default zone mismatch on %s",
+                           iface->i_name );
+                   syslog( LOG_ERR, "%.*s != %.*s",
+                           iface->i_czt->zt_len, iface->i_czt->zt_name,
+                           *data, data + 1 );
+                   syslog( LOG_ERR, "Seed error! Exiting!" );
+                   return -1;
+               }
+           }
+
+           if (addzone( iface->i_rt, *data, data + 1 ) < 0) {
+               syslog(LOG_ERR, "zip_packet: addzone");
+               return -1;
+           }
+           
+           /*
+            * The netrange we received from the router doesn't match the
+            * range we have locally. This is not a problem, unless we
+            * have seed information.
+            */
+           if ( firstnet != iface->i_rt->rt_firstnet ||
+                   lastnet != iface->i_rt->rt_lastnet ) {
+               if ( iface->i_flags & IFACE_SEED ) {
+                   syslog( LOG_ERR, "netrange mismatch on %s",
+                           iface->i_name );
+                   syslog( LOG_ERR, "%u-%u != %u-%u",
+                           ntohs( firstnet ), ntohs( lastnet ),
+                           ntohs( iface->i_rt->rt_firstnet ),
+                           ntohs( iface->i_rt->rt_lastnet ));
+                   syslog( LOG_ERR, "Seed error! Exiting!" );
+                   return -1;
+               }
+
+
+               /*
+                * It is possible that we will corrupt our route database
+                * by just forcing this change.  A better solution would
+                * be to search all of our current routes, looking for
+                * this new route, and delete any old versions.  Also, we
+                * would call rtmp_delete() on the old net range, in case
+                * there is some other net which actually had that range.  XXX
+                */
+               iface->i_rt->rt_firstnet = firstnet;
+               iface->i_rt->rt_lastnet = lastnet;
+
+               if ( ntohs( iface->i_addr.sat_addr.s_net ) <
+                       ntohs( firstnet ) ||
+                       ntohs( iface->i_addr.sat_addr.s_net ) >
+                       ntohs( lastnet )) {
+                   iface->i_addr.sat_addr.s_net = 0;   /* ATADDR_ANYNET? */
+               }
+               setaddr( iface, IFACE_PHASE2, iface->i_addr.sat_addr.s_net,
+                       iface->i_addr.sat_addr.s_node, firstnet, lastnet );
+               stabletimer = UNSTABLE;
+           }
+
+           /* add addr to loopback route */
+           if ( looproute( iface, RTMP_ADD )) { /* -1 or 1 */
+               syslog( LOG_ERR,
+                       "zip_packet: can't route %u.%u to loopback: %m",
+                       ntohs( iface->i_addr.sat_addr.s_net ),
+                       iface->i_addr.sat_addr.s_node );
+               return -1;
+           }
+
+           syslog( LOG_INFO, "zip_packet configured %s from %u.%u",
+                   iface->i_name, ntohs( from->sat_addr.s_net ),
+                   from->sat_addr.s_node );
+           iface->i_flags |= IFACE_CONFIG;
+           if ( iface == ciface ) {
+               ciface = ciface->i_next;
+               bootaddr( ciface );
+           }
+           break;
+
+       case ZIPOP_NOTIFY :
+#ifdef DEBUG
+           printf( "zip notify from %u.%u\n", ntohs( from->sat_addr.s_net ),
+                   from->sat_addr.s_node );
+#endif DEBUG
+           break;
+
+       default :
+           syslog( LOG_INFO, "zip_packet bad zip op from %u.%u\n",
+                   ntohs( from->sat_addr.s_net ), from->sat_addr.s_node );
+       }
+       break;
+
+    case DDPTYPE_ATP :
+       if ( data + sizeof( struct atphdr ) > end ) {
+           syslog( LOG_INFO, "zip atp malformed packet" );
+           return 1;
+       }
+       memcpy( &ah, data, sizeof( struct atphdr ));
+       data += sizeof( struct atphdr );
+       if ( ah.atphd_ctrlinfo != ATP_TREQ ) {
+           syslog( LOG_INFO, "zip atp bad control" );
+           return 1;
+       }
+       ah.atphd_ctrlinfo = ATP_TRESP | ATP_EOM;
+       if ( ah.atphd_bitmap != 1 ) {
+           syslog( LOG_ERR, "zip atp bad bitmap" );
+           return 1;
+       }
+       ah.atphd_bitmap = 0;
+
+       zipop = *data++;
+       data++;
+       memcpy( &index, data, sizeof( u_short ));
+       data += sizeof( u_short );
+       index = ntohs( index );
+       if ( data != end ) {
+           syslog( LOG_INFO, "zip atp malformed packet" );
+           return 1;
+       }
+
+       data = packet;
+       end = data + sizeof( packet );
+       *data++ = DDPTYPE_ATP;
+       memcpy( data, &ah, sizeof( struct atphdr ));
+       data += sizeof( struct atphdr );
+       lastflag = data++;              /* mark and space for last flag */
+       *data++ = 0;
+       nzones = data;                  /* mark and space for zone count */
+       data += sizeof( u_short );
+
+       switch ( zipop ) {
+       case ZIPOP_GETMYZONE :
+           if ( index != 0 ) {
+               syslog( LOG_INFO, "zip atp gmz bad index" );
+               return 1;
+           }
+
+           if ( iface->i_flags & IFACE_LOOPBACK ) {
+               iface = interfaces->i_next;     /* first interface */
+           } else if ( ntohs( iface->i_rt->rt_firstnet ) >
+                   ntohs( from->sat_addr.s_net ) ||
+                   ntohs( iface->i_rt->rt_lastnet ) <
+                   ntohs( from->sat_addr.s_net )) {
+               return 0;
+           }
+
+           if ( iface->i_rt->rt_zt == 0 ) {
+               return 0;
+           }
+           zt = (struct ziptab *)iface->i_rt->rt_zt->l_data;
+           if ( data + 1 + zt->zt_len > end ) {
+               syslog( LOG_INFO, "zip atp gmz reply too long" );
+               return 1;
+           }
+           *data++ = zt->zt_len;
+           memcpy( data, zt->zt_name, zt->zt_len );
+           data += zt->zt_len;
+
+           *lastflag = 0;
+           nz = 1;
+           break;
+
+       case ZIPOP_GETZONELIST :
+           for ( zt = ziptab; zt && ( index > 1 ); zt = zt->zt_next, index-- )
+               ;
+           for ( nz = 0; zt; zt = zt->zt_next, nz++ ) {
+               if ( data + 1 + zt->zt_len > end ) {
+                   break;
+               }
+               *data++ = zt->zt_len;
+               memcpy( data, zt->zt_name, zt->zt_len );
+               data += zt->zt_len;
+           }
+
+           *lastflag = ( zt == 0 );            /* Too clever? */
+           break;
+
+       case ZIPOP_GETLOCALZONES :
+           if ( iface->i_flags & IFACE_LOOPBACK ) {
+               iface = interfaces->i_next;     /* first interface */
+           } else if ( ntohs( iface->i_rt->rt_firstnet ) >
+                   ntohs( from->sat_addr.s_net ) ||
+                   ntohs( iface->i_rt->rt_lastnet ) <
+                   ntohs( from->sat_addr.s_net )) {
+               return 0;
+           }
+
+           for ( l = iface->i_rt->rt_zt; l && ( index > 1 );
+                   l = l->l_next, index-- )
+               ;
+           for ( nz = 0; l; l = l->l_next, nz++ ) {
+               zt = (struct ziptab *)l->l_data;
+               if ( data + 1 + zt->zt_len > end ) {
+                   break;
+               }
+               *data++ = zt->zt_len;
+               memcpy( data, zt->zt_name, zt->zt_len );
+               data += zt->zt_len;
+           }
+
+           *lastflag = ( l == 0 );
+           break;
+
+       default :
+           syslog( LOG_INFO, "zip atp bad option" );
+           return 1;
+       }
+
+       /* send reply */
+       if ( nz > 0 ) {
+           nz = htons( nz );
+           memcpy( nzones, &nz, sizeof( u_short ));
+           if ( sendto( ap->ap_fd, packet, data - packet, 0,
+                   (struct sockaddr *)from,
+                   sizeof( struct sockaddr_at )) < 0 ) {
+               syslog( LOG_ERR, "zip atp sendto %u.%u: %m",
+                       ntohs( from->sat_addr.s_net ), from->sat_addr.s_node );
+               return 1;
+           }
+       }
+       break;
+
+    default :
+       syslog( LOG_INFO, "zip_packet bad ddp type from %u.%u\n",
+               ntohs( from->sat_addr.s_net ), from->sat_addr.s_node );
+       return 1;
+    }
+
+    return 0;
+}
+
+int zip_getnetinfo( iface )
+    struct interface   *iface;
+{
+    struct atport      *ap;
+    struct ziphdr      zh;
+    struct sockaddr_at sat;
+    char               *data, packet[ 40 ];
+    u_short            net;
+
+    syslog( LOG_INFO, "zip_getnetinfo for %s", iface->i_name );
+
+    for ( ap = iface->i_ports; ap; ap = ap->ap_next ) {
+       if ( ap->ap_packet == zip_packet ) {
+           break;
+       }
+    }
+    if ( ap == 0 ) {
+       syslog( LOG_ERR, "zip_getnetinfo can't find zip socket!" );
+       return -1;
+    }
+
+    data = packet;
+
+    *data++ = DDPTYPE_ZIP;
+
+    zh.zh_op = ZIPOP_GNI;
+    zh.zh_zero = 0;
+    memcpy( data, &zh, sizeof( struct ziphdr ));
+    data += sizeof( struct ziphdr );
+    net = 0;
+    memcpy( data, &net, sizeof( u_short ));
+    data += sizeof( u_short );
+    memcpy( data, &net, sizeof( u_short ));
+    data += sizeof( u_short );
+
+    /*
+     * Set our requesting zone to NULL, so the response will contain
+     * the default zone.
+     */
+    *data++ = 0;
+
+#ifdef BSD4_4
+    sat.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+    sat.sat_family = AF_APPLETALK;
+    sat.sat_addr.s_net = 0;
+    sat.sat_addr.s_node = ATADDR_BCAST;
+    sat.sat_port = ap->ap_port;
+
+    if ( sendto( ap->ap_fd, packet, data - packet, 0, (struct sockaddr *)&sat,
+           sizeof( struct sockaddr_at )) < 0 ) {
+       syslog( LOG_ERR, "zip_getnetinfo sendto: %m" );
+       return -1;
+    }
+    return 0;
+}
+
+    struct ziptab *
+newzt( len, name )
+    const int          len;
+    const char *name;
+{
+    struct ziptab      *zt;
+
+    if (( zt = (struct ziptab *)calloc(1, sizeof( struct ziptab ))) == NULL ) {
+       return( NULL );
+    }
+
+    zt->zt_len = len;
+    if (( zt->zt_name = (char *)malloc( len )) == NULL ) {
+       free(zt);
+       return( NULL );
+    }
+
+    memcpy( zt->zt_name, name, len );
+    return( zt );
+}
+
+
+/*
+ * Insert at the end.  Return 1 if a mapping already exists, 0 otherwise.
+ * -1 on error.
+ */
+static int add_list( head, data )
+    struct list        **head;
+    void       *data;
+{
+    struct list        *l, *l2;
+
+    for ( l = *head; l; l = l->l_next ) {
+       if ( l->l_data == data ) {
+           return( 1 );
+       }
+    }
+    if (( l = (struct list *)malloc( sizeof( struct list ))) == NULL ) {
+       syslog( LOG_ERR, "add_list malloc: %m" );
+       return -1;
+    }
+
+    l->l_data = data;
+    l->l_next = NULL;
+    if ( *head == NULL ) {
+       l->l_prev = NULL;
+       *head = l;
+    } else {
+       /* find end of list */
+       for ( l2 = *head; l2->l_next; l2 = l2->l_next )
+           ;
+       l->l_prev = l2;
+       l2->l_next = l;
+    }
+    return( 0 );
+}
+
+int addzone( rt, len, zone )
+    struct rtmptab     *rt;
+    int                        len;
+    char               *zone;
+{
+    struct ziptab      *zt;
+    int                        cc, exists = 0;
+
+    for ( zt = ziptab; zt; zt = zt->zt_next ) {
+       if ( zt->zt_len == len &&
+               strndiacasecmp( zt->zt_name, zone, len ) == 0 ) {
+           break;
+       }
+    }
+    if ( zt == NULL ) {
+       if (( zt = newzt( len, zone )) == NULL ) {
+           syslog( LOG_ERR, "addzone newzt: %m" );
+           return -1;
+       }
+       if ( ziptab == NULL ) {
+           zt->zt_prev = NULL;
+           ziptab = zt;
+       } else {
+           zt->zt_prev = ziplast;
+           ziplast->zt_next = zt;
+       }
+       ziplast = zt;
+    }
+
+    if ((cc = add_list( &zt->zt_rt, rt )) < 0) 
+      return -1;
+
+    if (cc)
+      exists++;
+
+    if ((cc = add_list( &rt->rt_zt, zt )) < 0 )
+      return -1;
+
+    if (cc) {
+        if ( !exists ) {
+           syslog( LOG_ERR, "addzone corrupted route/zone mapping" );
+           return -1;
+       }
+       /*
+        * We get the repeat for local nets which have zone information
+        * already: we ask anyway, just to make sure.
+        */
+       
+       return 0;
+    }
+    if ( exists ) {
+       syslog( LOG_ERR, "addzone corrupted zone/route mapping" );
+       return -1;
+    }
+}
diff --git a/etc/atalkd/zip.h b/etc/atalkd/zip.h
new file mode 100644 (file)
index 0000000..dacba24
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#ifndef ATALKD_ZIP_H
+#define ATALKD_ZIP_H 1
+
+#include <sys/cdefs.h>
+
+struct ziptab {
+    struct ziptab      *zt_next,
+                       *zt_prev;
+    u_char             zt_len;
+    char               *zt_name;
+    u_char             *zt_bcast;
+    struct list                *zt_rt;
+};
+
+extern struct ziptab   *ziptab, *ziplast;
+struct ziptab  *newzt __P((const int, const char *));
+
+#endif /* atalkd/zip.h */
diff --git a/etc/papd/Makefile b/etc/papd/Makefile
new file mode 100644 (file)
index 0000000..648e2b4
--- /dev/null
@@ -0,0 +1,88 @@
+SRC=    main.c printcap.c session.c file.c comment.c lp.c ppd.c \
+       magics.c headers.c queries.c
+OBJ=   main.o printcap.o session.o file.o comment.o lp.o ppd.o \
+       magics.o headers.o queries.o
+
+INCPATH = -I../../include ${KRBINCPATH} ${ABSINCPATH}
+CFLAGS=        ${DEFS} ${KRBDEFS} ${ABSDEFS} ${OPTOPTS} ${INCPATH}
+TAGSFILE=      tags
+LIBDIRS=       -L../../libatalk ${KRBLIBDIRS} ${ABSLIBDIRS}
+LIBS=  -latalk ${ABSLIBS} ${KRBLIBS} ${ADDLIBS}
+CC=    cc
+INSTALL=       install
+
+all :
+       if [ x"${KRBDIR}" != x ]; then \
+           KRBLIBS="-lkrb -ldes"; \
+           KRBLIBDIRS="-L${KRBDIR}/lib"; \
+           KRBINCPATH="-I${KRBDIR}/include"; \
+           KRBDEFS="-DKRB"; \
+       fi; \
+        ${MAKE} ${MFLAGS} CC="${CC}" ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" \
+           OPTOPTS="${OPTOPTS}" DESTDIR="${DESTDIR}" \
+           SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+           ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+           KRBLIBS="$${KRBLIBS}" KRBLIBDIRS="$${KRBLIBDIRS}" \
+           KRBINCPATH="$${KRBINCPATH}" KRBDEFS="$${KRBDEFS}" papd
+
+# UMICH stuff.  Don't ask, I'm not telling.
+CRAP=/afs/umich.edu/group/itd/software/packages
+ABSPATH=${CRAP}/a/abs1.5/sun4m_412
+AFSPATH=${CRAP}/a/afs-3.3a/sun4m_412
+papd.abs: FRC
+       ${MAKE} ${MFLAGS} \
+           KRBLIBS="-lkrb -ldes" \
+           KRBLIBDIRS="-L${KRBDIR}/lib" \
+           KRBINCPATH="-I${KRBDIR}/include" \
+           KRBDEFS="-DKRB" \
+           ABSLIBS="-labs -lsys -lrx -llwp -lrxkad -lrx \
+               -lubik -lkauth -lcom_err -lauth -lrxkad \
+               ${AFSPATH}/lib/afs/util.a" \
+           ABSLIBDIRS="-L${ABSPATH}/lib -L${AFSPATH}/lib \
+               -L${AFSPATH}/lib/afs" \
+           ABSINCPATH="-I${ABSPATH}/include -I${AFSPATH}/include" \
+           ABSDEFS="-DABS_PRINT" \
+           CC="gcc" SRC="${SRC} abs.c" OBJ="${OBJ} abs.o" \
+           papd
+
+FRC:
+
+papd : ${OBJ} ../../libatalk/libatalk.a
+       ${CC} ${CFLAGS} ${LDFLAGS} -o papd ${OBJ} ${LIBDIRS} ${LIBS}
+
+main.o : main.c
+       ${CC} ${CFLAGS} -D_PATH_PAPDCONF=\"${ETCDIR}/papd.conf\" \
+           -DVERSION=\"`cat ../../VERSION`\" \
+           ${CPPFLAGS} -c main.c
+
+install : all
+       ${INSTALL} -c papd ${SBINDIR}
+
+clean :
+       rm -f a.out core* *.o *.bak *[Ee]rrs tags
+       rm -f papd
+
+tags : ${SRC}
+       cwd=`pwd`; \
+       for i in ${SRC}; do \
+           ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+       done
+
+depend :
+       for i in ${SRC} ; do \
+           ${CC} -M ${DEFS} ${INCPATH} $$i | \
+           awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+               else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+               else rec = rec " " $$2 } } \
+               END { print rec } ' >> makedep; done
+       sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+       cat makedep >> Makefile.tmp
+       rm makedep
+       echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+       echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+       echo '# see make depend above' >> Makefile.tmp
+       rm -f Makefile.bak
+       cp Makefile Makefile.bak
+       mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
diff --git a/etc/papd/comment.c b/etc/papd/comment.c
new file mode 100644 (file)
index 0000000..db89202
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#include <sys/syslog.h>
+#include <sys/param.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "comment.h"
+
+struct comstate        *comstate;
+
+char   *comcont = "%%+";
+
+compop()
+{
+    struct comstate    *cs;
+
+    cs = comstate;
+    comstate = cs->cs_prev;
+    free( cs );
+}
+
+compush( comment )
+    struct comment     *comment;
+{
+    struct comstate    *cs;
+
+    if (( cs = (struct comstate *)malloc( sizeof( struct comstate ))) ==
+           NULL ) {
+       syslog( LOG_ERR, "malloc: %m" );
+       exit( 1 );
+    }
+
+    cs->cs_comment = comment;
+    cs->cs_prev = comstate;
+    cs->cs_flags = 0;
+    comstate = cs;
+}
+
+comswitch( comments, handler )
+    struct comment     *comments;
+    int                        (*handler)();
+{
+    struct comment     *c, *comment = NULL;
+
+    for ( c = comments; c->c_begin; c++ ) {
+       if ( c->c_handler == handler ) {
+           comment = c;
+       }
+    }
+    if ( comment == NULL || comment->c_handler != handler ) {
+       syslog( LOG_ERR, "comswitch: can't find handler!" );
+       return( -1 );
+    }
+    compop();
+    compush( comment );
+    return( 0 );
+}
+
+comcmp( start, stop, str, how )
+    char       *start, *stop, *str;
+    int                how;
+{
+    int                cc, len;
+
+    len = stop - start;
+    cc = strlen( str );
+    if ( how & C_FULL ) {
+       if ( cc == len & strncmp( str, start, cc ) == 0 ) {
+           return( 0 );
+       }
+    } else {
+       if ( cc <= len && strncmp( str, start, cc ) == 0 ) {
+           return( 0 );
+       }
+    }
+
+    return( 1 );
+}
+
+    struct comment *
+commatch( start, stop, comments )
+    char               *start, *stop;
+    struct comment     comments[];
+{
+    struct comment     *comment;
+
+    for ( comment = comments; comment->c_begin; comment++ ) {
+       if ( comcmp( start, stop, comment->c_begin, comment->c_flags ) == 0 ) {
+           break;
+       }
+    }
+    if ( comment->c_begin ) {
+       return( comment );
+    } else {
+       return( NULL );
+    }
+}
+
+    char *
+comtoken( start, stop, pos, delim )
+    char       *start, *stop, *pos, *delim;
+{
+    if ( pos < start || pos > stop ) {
+       abort();
+    }
+
+    for ( ; pos < stop; pos++ ) {
+       if ( index( delim, *pos )) {
+           break;
+       }
+    }
+    if ( ++pos < stop ) {
+       return( pos );
+    } else {
+       return( NULL );
+    }
+}
diff --git a/etc/papd/comment.h b/etc/papd/comment.h
new file mode 100644 (file)
index 0000000..2e12765
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+struct comment {
+    char               *c_begin;
+    char               *c_end;
+    int                        (*c_handler)();
+    int                        c_flags;
+};
+
+#define CH_DONE                0
+#define CH_MORE                1
+#define CH_ERROR       -1
+
+struct comstate {
+    struct comment     *cs_comment;
+    struct comstate    *cs_prev;
+    int                        cs_flags;
+};
+
+extern struct comment  *commatch();
+extern struct comstate *comstate;
+extern struct comment  magics[];
+extern struct comment  queries[];
+extern struct comment  headers[];
+extern char            *comcont;
+
+#define compeek()      (comstate==NULL?NULL:(comstate->cs_comment))
+#define comgetflags()  (comstate->cs_flags)
+#define comsetflags(f) (comstate->cs_flags=(f))
+
+/*
+ * Comment flags.  0-15 reserved for "global" flags, 16-31 for specific
+ * subtypes.
+ */
+#define C_FULL         (1<<0)                          /* or prefix */
+#define C_CONTINUE     (1<<1)
+
+/*
+ * Query subtypes.
+ */
+
+/*
+ * Magic "number" subtypes.
+ */
+#define CM_NOPRINT     (1<<16)                         /* or print */
diff --git a/etc/papd/file.c b/etc/papd/file.c
new file mode 100644 (file)
index 0000000..63f08a0
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#include <sys/syslog.h>
+#include <sys/param.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "file.h"
+
+markline( start, stop, pf )
+    char               **start, **stop;
+    struct papfile     *pf;
+{
+    char               *p;
+
+    if ( PF_BUFSIZ( pf ) == 0 && ( pf->pf_state & PF_EOF )) {
+       return( 0 );
+    }
+
+    /* get a line */
+    for ( p = pf->pf_cur; p < pf->pf_end; p++ ) {
+       if ( *p == '\n' || *p == '\r' ) {
+           break;
+       }
+    }
+    if ( p >= pf->pf_end ) {
+       if ( pf->pf_state & PF_EOF ) {
+           APPEND( pf, "\n", 1 );
+       } else {
+           return( -1 );
+       }
+    }
+
+    *start = pf->pf_cur;
+    *stop = p;
+    if ( *stop == *start ) {
+       return( 1 );                    /* don't return len 0 lines */
+    } else {
+       return( *stop - *start );
+    }
+}
+
+consumetomark( start, stop, pf )
+    char               *start, *stop;
+    struct papfile     *pf;
+{
+    if ( start != pf->pf_cur || pf->pf_cur > stop || stop > pf->pf_end ) {
+       abort();
+    }
+
+    pf->pf_cur = stop + 1;             /* past the stop char */
+    if ( pf->pf_cur > pf->pf_end ) {
+       abort();
+    }
+    if ( pf->pf_cur == pf->pf_end ) {
+       pf->pf_cur = pf->pf_end = pf->pf_buf;
+    }
+
+    return;
+}
+
+morespace( pf, data, len )
+    struct papfile     *pf;
+    char               *data;
+    int                        len;
+{
+    char               *nbuf;
+    int                        nsize;
+
+    if ( pf->pf_cur != pf->pf_buf ) {                  /* pull up */
+       bcopy( pf->pf_cur, pf->pf_buf, PF_BUFSIZ( pf ));
+       pf->pf_end = pf->pf_buf + PF_BUFSIZ( pf );
+       pf->pf_cur = pf->pf_buf;
+    }
+
+    if ( pf->pf_end + len > pf->pf_buf + pf->pf_len ) {        /* make more space */
+       nsize = (( pf->pf_len + len ) / PF_MORESPACE +
+               (( pf->pf_len + len ) % PF_MORESPACE != 0 )) * PF_MORESPACE;
+       if ( pf->pf_buf ) {
+           if (( nbuf = (char *)realloc( pf->pf_buf, nsize )) == 0 ) {
+               exit( 1 );
+           }
+       } else {
+           if (( nbuf = (char *)malloc( nsize )) == 0 ) {
+               exit( 1 );
+           }
+       }
+       pf->pf_len = nsize;
+       pf->pf_end = nbuf + ( pf->pf_end - pf->pf_buf );
+       pf->pf_cur = nbuf + ( pf->pf_cur - pf->pf_buf );
+       pf->pf_buf = nbuf;
+    }
+
+    bcopy( data, pf->pf_end, len );
+    pf->pf_end += len;
+}
+
+spoolerror( out, str )
+    struct papfile     *out;
+    char               *str;
+{
+    char       *pserr1 = "%%[ Error: ";
+    char       *pserr2 = " ]%%\n";
+
+    if ( str == NULL ) {
+       str = "Spooler error.";
+    }
+
+    APPEND( out, pserr1, strlen( pserr1 ));
+    APPEND( out, str, strlen( str ));
+    APPEND( out, pserr2, strlen( pserr2 ));
+}
diff --git a/etc/papd/file.h b/etc/papd/file.h
new file mode 100644 (file)
index 0000000..e6ae71e
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+struct papfile {
+    int                        pf_state;
+    struct state       *pf_xstate;
+    int                        pf_len;
+    char               *pf_buf;
+    char               *pf_cur;
+    char               *pf_end;
+};
+
+#define PF_BOT         (1<<0)
+#define PF_EOF         (1<<1)
+#define PF_QUERY       (1<<2)
+
+#define APPEND( pf, data, len )        \
+       if ( (pf)->pf_end + (len) > (pf)->pf_buf + (pf)->pf_len ) { \
+               morespace( (pf), (data), (len)); \
+       } else { \
+               bcopy( (data), (pf)->pf_end, (len)); \
+               (pf)->pf_end += (len); \
+       }
+#define PF_BUFSIZ( pf )                ((pf)->pf_end - (pf)->pf_cur)
+#define CONSUME( pf, len )     (((pf)->pf_cur += (len)), \
+       (((pf)->pf_cur >= (pf)->pf_end) && \
+       ((pf)->pf_cur = (pf)->pf_end = (pf)->pf_buf)))
+
+#define PF_MORESPACE   1024
diff --git a/etc/papd/headers.c b/etc/papd/headers.c
new file mode 100644 (file)
index 0000000..12104be
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#include <sys/syslog.h>
+#include <sys/param.h>
+#include <stdio.h>
+
+#include "file.h"
+#include "comment.h"
+
+ch_title( in, out )
+    struct papfile     *in, *out;
+{
+    char               *start, *stop, *p, *q, c;
+    struct comment     *comment = compeek();
+
+    switch ( markline( &start, &stop, in )) {
+    case 0 :
+       return( 0 );
+
+    case -1 :
+       return( CH_MORE );
+    }
+
+    for ( p = start; p < stop; p++ ) {
+       if ( *p == ':' ) {
+           break;
+       }
+    }
+
+    for ( ; p < stop; p++ ) {
+       if ( *p == '(' ) {
+           break;
+       }
+    }
+
+    for ( q = p; q < stop; q++ ) {
+       if ( *q == ')' ) {
+           break;
+       }
+    }
+
+    if ( q < stop && p < stop ) {
+       p++;
+       c = *q;
+       *q = '\0';
+       lp_job( p );
+       *q = c;
+    }
+
+    *stop = '\n';
+    lp_write( start, stop - start + 1 );
+    compop();
+    consumetomark( start, stop, in );
+    return( CH_DONE );
+}
+
+/*
+ * "Header" comments.
+ */
+struct comment headers[] = {
+    { "%%Title:",                      0,              ch_title,       0 },
+    { 0 },
+};
diff --git a/etc/papd/lp.c b/etc/papd/lp.c
new file mode 100644 (file)
index 0000000..be1e6c7
--- /dev/null
@@ -0,0 +1,719 @@
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ *
+ * Portions:
+ * Copyright (c) 1983 Regents of the University of California.
+ * 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 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.
+ */
+
+/*
+ * Interface to lpr system.
+ */
+
+#include <sys/param.h>
+#include <sys/syslog.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#if defined( sun ) && defined( __svr4__ )
+#include </usr/ucbinclude/sys/file.h>
+#else sun __svr4__
+#include <sys/file.h>
+#endif sun __svr4__
+#include <sys/un.h>
+#include <netinet/in.h>
+#undef s_net
+#include <netatalk/at.h>
+#include <atalk/atp.h>
+#include <atalk/paths.h>
+
+#ifdef ABS_PRINT
+#include <math.h>
+#endif ABS_PRINT
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <fcntl.h>
+
+#include "printer.h"
+#include "file.h"
+
+char   hostname[ MAXHOSTNAMELEN ];
+
+/* initialize printing interface */
+int    lp_init();
+/* cancel current job */
+int    lp_cancel();
+/* print current job */
+int    lp_print();
+
+/* open a file for spooling */
+int    lp_open();
+/* open a buffer to the current open file */
+int    lp_write();
+/* close current spooling file */
+int    lp_close();
+
+struct lp {
+    int                        lp_flags;
+    FILE               *lp_stream;
+    int                        lp_seq;
+    char               lp_letter;
+    char               *lp_person;
+    char               *lp_host;
+    char               *lp_job;
+} lp;
+#define LP_INIT                (1<<0)
+#define LP_OPEN                (1<<1)
+#define LP_PIPE                (1<<2)
+#define LP_CONNECT     (1<<3)
+#define LP_QUEUE       (1<<4)
+
+lp_person( person )
+    char       *person;
+{
+    if ( lp.lp_person != NULL ) {
+       free( lp.lp_person );
+    }
+    if (( lp.lp_person = (char *)malloc( strlen( person ) + 1 )) == NULL ) {
+       syslog( LOG_ERR, "malloc: %m" );
+       exit( 1 );
+    }
+    strcpy( lp.lp_person, person );
+}
+
+#ifdef ABS_PRINT
+lp_pagecost()
+{
+    char       cost[ 22 ];
+    char       balance[ 22 ];
+    int                err;
+
+    if ( lp.lp_person == NULL ) {
+       return( -1 );
+    }
+    err = ABS_canprint( lp.lp_person, printer->p_role, printer->p_srvid,
+           cost, balance );
+    printer->p_pagecost = floor( atof( cost ) * 10000.0 );
+    printer->p_balance = atof( balance ) + atof( cost );
+    return( err < 0 ? -1 : 0 );
+}
+#endif ABS_PRINT
+
+lp_host( host )
+    char       *host;
+{
+    if ( lp.lp_host != NULL ) {
+       free( lp.lp_host );
+    }
+    if (( lp.lp_host = (char *)malloc( strlen( host ) + 1 )) == NULL ) {
+       syslog( LOG_ERR, "malloc: %m" );
+       exit( 1 );
+    }
+    strcpy( lp.lp_host, host );
+}
+
+lp_job( job )
+    char       *job;
+{
+    char       *p, *q;
+
+    if ( lp.lp_job != NULL ) {
+       free( lp.lp_job );
+    }
+    if (( lp.lp_job = (char *)malloc( strlen( job ) + 1 )) == NULL ) {
+       syslog( LOG_ERR, "malloc: %m" );
+       exit( 1 );
+    }
+    for ( p = job, q = lp.lp_job; *p != '\0'; p++, q++ ) {
+       if ( !isascii( *p ) || !isprint( *p ) || *p == '\\' ) {
+           *q = '.';
+       } else {
+           *q = *p;
+       }
+    }
+    *q = '\0';
+}
+
+lp_init( out )
+    struct papfile     *out;
+{
+    int                fd, n, len;
+    char       *cp, buf[ BUFSIZ ];
+    struct stat        st;
+#ifdef ABS_PRINT
+    char       cost[ 22 ];
+    char       balance[ 22 ];
+#endif ABS_PRINT
+
+    if ( printer->p_flags & P_AUTH ) {
+       if ( lp.lp_person == NULL ) {
+           syslog( LOG_ERR, "lp_init: must authenticate" );
+           spoolerror( out, "Authentication required." );
+           return( -1 );
+       }
+#ifdef ABS_PRINT
+       if (( printer->p_flags & P_ACCOUNT ) && printer->p_pagecost > 0 &&
+               ! ABS_canprint( lp.lp_person, printer->p_role,
+               printer->p_srvid, cost, balance )) {
+           syslog( LOG_ERR, "lp_init: no ABS funds" );
+           spoolerror( out, "No ABS funds available." );
+           return( -1 );
+       }
+#endif ABS_PRINT
+    }
+
+    if ( gethostname( hostname, sizeof( hostname )) < 0 ) {
+       syslog( LOG_ERR, "gethostname: %m" );
+       exit( 1 );
+    }
+
+    if ( lp.lp_flags & LP_INIT ) {
+       syslog( LOG_ERR, "lp_init: already inited, die!" );
+       abort();
+    }
+
+    lp.lp_flags = 0;
+    lp.lp_stream = NULL;
+    lp.lp_letter = 'A';
+
+    if ( printer->p_flags & P_SPOOLED ) {
+       /* check if queuing is enabled: mode & 010 on lock file */
+       if ( stat( printer->p_lock, &st ) < 0 ) {
+           syslog( LOG_ERR, "lp_init: %s: %m", printer->p_lock );
+           spoolerror( out, NULL );
+           return( -1 );
+       }
+       if ( st.st_mode & 010 ) {
+           syslog( LOG_INFO, "lp_init: queuing is disabled" );
+           spoolerror( out, "Queuing is disabled." );
+           return( -1 );
+       }
+
+       if (( fd = open( ".seq", O_RDWR|O_CREAT, 0661 )) < 0 ) {
+           syslog( LOG_ERR, "lp_init: can't create .seq" );
+           spoolerror( out, NULL );
+           return( -1 );
+       }
+
+       if ( flock( fd, LOCK_EX ) < 0 ) {
+           syslog( LOG_ERR, "lp_init: can't lock .seq" );
+           spoolerror( out, NULL );
+           return( -1 );
+       }
+
+       n = 0;
+       if (( len = read( fd, buf, sizeof( buf ))) < 0 ) {
+           syslog( LOG_ERR, "lp_init read: %m" );
+           spoolerror( out, NULL );
+           return( -1 );
+       }
+       if ( len > 0 ) {
+           for ( cp = buf; len; len--, cp++ ) {
+               if ( *cp < '0' || *cp > '9' ) {
+                   break;
+               }
+               n = n * 10 + ( *cp - '0' );
+           }
+       }
+       lp.lp_seq = n;
+
+       n = ( n + 1 ) % 1000;
+       sprintf( buf, "%03d\n", n );
+       lseek( fd, 0L, 0 );
+       write( fd, buf, strlen( buf ));
+       close( fd );
+    } else {
+       lp.lp_flags |= LP_PIPE;
+       lp.lp_seq = getpid();
+    }
+
+    lp.lp_flags |= LP_INIT;
+    return( 0 );
+}
+
+lp_open( out )
+    struct papfile     *out;
+{
+    char       name[ MAXPATHLEN ];
+    int                fd;
+
+    if (( lp.lp_flags & LP_INIT ) == 0 && lp_init( out ) != 0 ) {
+       return( -1 );
+    }
+    if ( lp.lp_flags & LP_OPEN ) {
+       syslog( LOG_ERR, "lp_open already open" );
+       abort();
+    }
+
+    if ( lp.lp_flags & LP_PIPE ) {
+       /* go right to program */
+       if (( lp.lp_stream = popen( printer->p_printer, "w" )) == NULL ) {
+           syslog( LOG_ERR, "lp_open popen %s: %m", printer->p_printer );
+           spoolerror( out, NULL );
+           return( -1 );
+       }
+    } else {
+       sprintf( name, "df%c%03d%s", lp.lp_letter++, lp.lp_seq, hostname );
+
+       if (( fd = open( name, O_WRONLY|O_CREAT|O_EXCL, 0660 )) < 0 ) {
+           syslog( LOG_ERR, "lp_open %s: %m", name );
+           spoolerror( out, NULL );
+           return( -1 );
+       }
+       if (( lp.lp_stream = fdopen( fd, "w" )) == NULL ) {
+           syslog( LOG_ERR, "lp_open fdopen: %m" );
+           spoolerror( out, NULL );
+           return( -1 );
+       }
+    }
+    lp.lp_flags |= LP_OPEN;
+
+    return( 0 );
+}
+
+lp_close()
+{
+    if (( lp.lp_flags & LP_INIT ) == 0 || ( lp.lp_flags & LP_OPEN ) == 0 ) {
+       return;
+    }
+    fclose( lp.lp_stream );
+    lp.lp_stream = NULL;
+    lp.lp_flags &= ~LP_OPEN;
+    return;
+}
+
+lp_write( buf, len )
+    char       *buf;
+    int                len;
+{
+    if (( lp.lp_flags & LP_OPEN ) == 0 ) {
+       return( -1 );
+    }
+
+    if ( fwrite( buf, 1, len, lp.lp_stream ) != len ) {
+       syslog( LOG_ERR, "lp_write: %m" );
+       abort();
+    }
+    return( 0 );
+}
+
+lp_cancel()
+{
+    char       name[ MAXPATHLEN ];
+    char       letter;
+
+    if (( lp.lp_flags & LP_INIT ) == 0 || lp.lp_letter == 'A' ) {
+       return;
+    }
+
+    if ( lp.lp_flags & LP_OPEN ) {
+       lp_close();
+    }
+
+    for ( letter = 'A'; letter < lp.lp_letter; letter++ ) {
+       sprintf( name, "df%c%03d%s", letter, lp.lp_seq, hostname );
+       if ( unlink( name ) < 0 ) {
+           syslog( LOG_ERR, "lp_cancel unlink %s: %m", name );
+       }
+    }
+
+    return;
+}
+
+/*
+ * Create printcap control file, signal printer.  Errors here should
+ * remove queue files.
+ *
+ * XXX piped?
+ */
+lp_print()
+{
+    char               buf[ MAXPATHLEN ];
+    char               tfname[ MAXPATHLEN ];
+    char               cfname[ MAXPATHLEN ];
+    char               letter;
+    int                        fd, n, s;
+    FILE               *cfile;
+
+    if (( lp.lp_flags & LP_INIT ) == 0 || lp.lp_letter == 'A' ) {
+       return;
+    }
+    lp_close();
+
+    if ( printer->p_flags & P_SPOOLED ) {
+       sprintf( tfname, "tfA%03d%s", lp.lp_seq, hostname );
+       if (( fd = open( tfname, O_WRONLY|O_EXCL|O_CREAT, 0660 )) < 0 ) {
+           syslog( LOG_ERR, "lp_print %s: %m", tfname );
+           return;
+       }
+       if (( cfile = fdopen( fd, "w" )) == NULL ) {
+           syslog( LOG_ERR, "lp_print %s: %m", tfname );
+           return;
+       }
+       fprintf( cfile, "H%s\n", hostname );    /* XXX lp_host? */
+
+       if ( lp.lp_person ) {
+           fprintf( cfile, "P%s\n", lp.lp_person );
+       } else {
+           fprintf( cfile, "P%s\n", printer->p_operator );
+       }
+
+       if ( lp.lp_job && *lp.lp_job ) {
+           fprintf( cfile, "J%s\n", lp.lp_job );
+           fprintf( cfile, "T%s\n", lp.lp_job );
+       } else {
+           fprintf( cfile, "JMac Job\n" );
+           fprintf( cfile, "TMac Job\n" );
+       }
+
+       fprintf( cfile, "C%s\n", hostname );    /* XXX lp_host? */
+
+       if ( lp.lp_person ) {
+           fprintf( cfile, "L%s\n", lp.lp_person );
+       } else {
+           fprintf( cfile, "L%s\n", printer->p_operator );
+       }
+
+       for ( letter = 'A'; letter < lp.lp_letter; letter++ ) {
+           fprintf( cfile, "fdf%c%03d%s\n", letter, lp.lp_seq, hostname );
+           fprintf( cfile, "Udf%c%03d%s\n", letter, lp.lp_seq, hostname );
+       }
+
+       if ( lp.lp_job && *lp.lp_job ) {
+           fprintf( cfile, "N%s\n", lp.lp_job );
+       } else {
+           fprintf( cfile, "NMac Job\n" );
+       }
+       fclose( cfile );
+
+       sprintf( cfname, "cfA%03d%s", lp.lp_seq, hostname );
+       if ( link( tfname, cfname ) < 0 ) {
+           syslog( LOG_ERR, "lp_print can't link %s to %s: %m", cfname,
+                   tfname );
+           return;
+       }
+       unlink( tfname );
+
+       if (( s = lp_conn_unix()) < 0 ) {
+           syslog( LOG_ERR, "lp_print: lp_conn_unix: %m" );
+           return;
+       }
+
+       sprintf( buf, "\1%s\n", printer->p_printer );
+       n = strlen( buf );
+       if ( write( s, buf, n ) != n ) {
+           syslog( LOG_ERR, "lp_print write: %m" );
+           return;
+       }
+       if ( read( s, buf, 1 ) != 1 ) {
+           syslog( LOG_ERR, "lp_print read: %m" );
+           return;
+       }
+
+       lp_disconn_unix( s );
+
+       if ( buf[ 0 ] != '\0' ) {
+           syslog( LOG_ERR, "lp_print lpd said %c: %m", buf[ 0 ] );
+           return;
+       }
+    }
+    syslog( LOG_INFO, "lp_print queued" );
+    return;
+}
+
+lp_disconn_unix( fd )
+{
+    return( close( fd ));
+}
+
+lp_conn_unix()
+{
+    int                        s;
+    struct sockaddr_un saun;
+
+    if (( s = socket( AF_UNIX, SOCK_STREAM, 0 )) < 0 ) {
+       syslog( LOG_ERR, "lp_conn_unix socket: %m" );
+       return( -1 );
+    }
+    bzero( &saun, sizeof( struct sockaddr_un ));
+    saun.sun_family = AF_UNIX;
+    strcpy( saun.sun_path, _PATH_DEVPRINTER );
+    if ( connect( s, (struct sockaddr *)&saun,
+           strlen( saun.sun_path ) + 2 ) < 0 ) {
+       syslog( LOG_ERR, "lp_conn_unix connect %s: %m", saun.sun_path );
+       close( s );
+       return( -1 );
+    }
+
+    return( s );
+}
+
+lp_disconn_inet( fd )
+{
+    return( close( fd ));
+}
+
+lp_conn_inet()
+{
+    int                        privfd, port = IPPORT_RESERVED - 1;
+    struct sockaddr_in sin;
+    struct servent     *sp;
+    struct hostent     *hp;
+
+    if (( sp = getservbyname( "printer", "tcp" )) == NULL ) {
+       syslog( LOG_ERR, "printer/tcp: unknown service\n" );
+       return( -1 );
+    }
+
+    if ( gethostname( hostname, sizeof( hostname )) < 0 ) {
+       syslog( LOG_ERR, "gethostname: %m" );
+       exit( 1 );
+    }
+
+    if (( hp = gethostbyname( hostname )) == NULL ) {
+       syslog( LOG_ERR, "%s: unknown host\n", hostname );
+       return( -1 );
+    }
+
+    if (( privfd = rresvport( &port )) < 0 ) {
+       syslog( LOG_ERR, "lp_connect: socket: %m" );
+       close( privfd );
+       return( -1 );
+    }
+
+    bzero( &sin, sizeof( struct sockaddr_in ));
+    sin.sin_family = AF_INET;
+/*    sin.sin_addr.s_addr = htonl( INADDR_LOOPBACK ); */
+    bcopy( hp->h_addr, &sin.sin_addr, hp->h_length );
+    sin.sin_port = sp->s_port;
+
+    if ( connect( privfd, (struct sockaddr *)&sin,
+           sizeof( struct sockaddr_in )) < 0 ) {
+       syslog( LOG_ERR, "lp_connect: %m" );
+       close( privfd );
+       return( -1 );
+    }
+
+    return( privfd );
+}
+
+lp_rmjob( job )
+    int                job;
+{
+    char       buf[ 1024 ];
+    int                n, s;
+
+    if (( s = lp_conn_inet()) < 0 ) {
+       syslog( LOG_ERR, "lp_rmjob: %m" );
+       return( -1 );
+    }
+
+    if ( lp.lp_person == NULL ) {
+       return( -1 );
+    }
+
+    sprintf( buf, "\5%s %s %d\n", printer->p_printer, lp.lp_person, job );
+    n = strlen( buf );
+    if ( write( s, buf, n ) != n ) {
+       syslog( LOG_ERR, "lp_rmjob write: %m" );
+       lp_disconn_inet( s );
+       return( -1 );
+    }
+    while (( n = read( s, buf, sizeof( buf ))) > 0 ) {
+       syslog( LOG_DEBUG, "read %.*s", n, buf );
+    }
+
+    lp_disconn_inet( s );
+    return( 0 );
+}
+
+char   *kw_rank = "Rank";
+char   *kw_active = "active";
+
+char   *tag_rank = "rank: ";
+char   *tag_owner = "owner: ";
+char   *tag_job = "job: ";
+char   *tag_files = "files: ";
+char   *tag_size = "size: ";
+char   *tag_status = "status: ";
+
+lp_queue( out )
+    struct papfile     *out;
+{
+    char                       buf[ 1024 ], *start, *stop, *p, *q;
+    static struct papfile      pf;
+    int                                n, len, s;
+       
+    if (( s = lp_conn_unix()) < 0 ) {
+       syslog( LOG_ERR, "lp_queue: %m" );
+       return( -1 );
+    }
+
+    sprintf( buf, "\3%s\n", printer->p_printer );
+    n = strlen( buf );
+    if ( write( s, buf, n ) != n ) {
+       syslog( LOG_ERR, "lp_queue write: %m" );
+       lp_disconn_unix( s );
+       return( -1 );
+    }
+    pf.pf_state = PF_BOT;
+
+    while (( n = read( s, buf, sizeof( buf ))) > 0 ) {
+       APPEND( &pf, buf, n );
+    }
+
+    for (;;) {
+       if ( markline( &start, &stop, &pf ) > 0 ) {
+           /* parse */
+           for ( p = start; p < stop; p++ ) {
+               if ( *p == ' ' || *p == '\t' ) {
+                   break;
+               }
+           }
+           if ( p >= stop ) {
+               consumetomark( start, stop, &pf );
+               continue;
+           }
+
+           /*
+            * Keys: "Rank", a number, "active"
+            * Anything else is status.
+            */
+           len = p - start;
+           if ( len == strlen( kw_rank ) &&
+                   strncmp( kw_rank, start, len ) == 0 ) {
+               consumetomark( start, stop, &pf );
+               continue;
+           }
+           if (( len == strlen( kw_active ) &&
+                   strncmp( kw_active, start, len ) == 0 ) ||
+                   isdigit( *start )) {                /* a job line */
+               APPEND( out, tag_rank, strlen( tag_rank ));
+               APPEND( out, start, p - start );
+               APPEND( out, "\n", 1 );
+
+               for ( ; p < stop; p++ ) {
+                   if ( *p != ' ' && *p != '\t' ) {
+                       break;
+                   }
+               }
+               for ( q = p; p < stop; p++ ) {
+                   if ( *p == ' ' || *p == '\t' ) {
+                       break;
+                   }
+               }
+               if ( p >= stop ) {
+                   APPEND( out, ".\n", 2 );
+                   consumetomark( start, stop, &pf );
+                   continue;
+               }
+               APPEND( out, tag_owner, strlen( tag_owner ));
+               APPEND( out, q, p - q );
+               APPEND( out, "\n", 1 );
+
+               for ( ; p < stop; p++ ) {
+                   if ( *p != ' ' && *p != '\t' ) {
+                       break;
+                   }
+               }
+               for ( q = p; p < stop; p++ ) {
+                   if ( *p == ' ' || *p == '\t' ) {
+                       break;
+                   }
+               }
+               if ( p >= stop ) {
+                   APPEND( out, ".\n", 2 );
+                   consumetomark( start, stop, &pf );
+                   continue;
+               }
+               APPEND( out, tag_job, strlen( tag_job ));
+               APPEND( out, q, p - q );
+               APPEND( out, "\n", 1 );
+
+               for ( ; p < stop; p++ ) {
+                   if ( *p != ' ' && *p != '\t' ) {
+                       break;
+                   }
+               }
+               for ( q = p, p = stop; p > q; p-- ) {
+                   if ( *p == ' ' || *p == '\t' ) {
+                       break;
+                   }
+               }
+               for ( ; p > q; p-- ) {
+                   if ( *p != ' ' && *p != '\t' ) {
+                       break;
+                   }
+               }
+               for ( ; p > q; p-- ) {
+                   if ( *p == ' ' || *p == '\t' ) {
+                       break;
+                   }
+               }
+               if ( p <= q ) {
+                   APPEND( out, ".\n", 2 );
+                   consumetomark( start, stop, &pf );
+                   continue;
+               }
+               APPEND( out, tag_files, strlen( tag_files ));
+               APPEND( out, q, p - q );
+               APPEND( out, "\n", 1 );
+
+               for ( ; p < stop; p++ ) {
+                   if ( *p != ' ' && *p != '\t' ) {
+                       break;
+                   }
+               }
+               APPEND( out, tag_size, strlen( tag_size ));
+               APPEND( out, p, stop - p );
+               APPEND( out, "\n.\n", 3 );
+
+               consumetomark( start, stop, &pf );
+               continue;
+           }
+
+           /* status */
+           APPEND( out, tag_status, strlen( tag_status ));
+           APPEND( out, start, stop - start );
+           APPEND( out, "\n.\n", 3 );
+
+           consumetomark( start, stop, &pf );
+       } else {
+           APPEND( out, "*\n", 2 );
+           lp_disconn_unix( s );
+           return( 0 );
+       }
+    }
+}
diff --git a/etc/papd/magics.c b/etc/papd/magics.c
new file mode 100644 (file)
index 0000000..dea2c00
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#include <sys/syslog.h>
+#include <sys/param.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "file.h"
+#include "comment.h"
+
+ps( infile, outfile )
+    struct papfile     *infile, *outfile;
+{
+    char                       *start, *stop;
+    struct comment             *comment;
+
+    for (;;) {
+       if ( comment = compeek()) {
+           switch( (*comment->c_handler)( infile, outfile )) {
+           case CH_DONE :
+               continue;
+
+           case CH_MORE :
+               return( CH_MORE );
+
+           default :
+               return( CH_ERROR );
+           }
+
+       } else {
+           switch ( markline( &start, &stop, infile )) {
+           case 0 :
+               /* eof on infile */
+               outfile->pf_state |= PF_EOF;
+               lp_close();
+               return( 0 );
+
+           case -1 :
+               return( 0 );
+           }
+
+           if ( infile->pf_state & PF_BOT ) {
+               if (( comment = commatch( start, stop, magics )) != NULL ) {
+                   compush( comment );
+                   continue;   /* top of for (;;) */
+               }
+               infile->pf_state &= ~PF_BOT;
+
+               /* set up spool file */
+               if ( lp_open( outfile ) < 0 ) {
+                   syslog( LOG_ERR, "lp_open failed" );
+                   spoolerror( outfile, "Ignoring job." );
+               }
+           }
+
+           /* write to file */
+           *stop = '\n';
+           lp_write( start, stop - start + 1 );
+           consumetomark( start, stop, infile );
+       }
+    }
+}
+
+cm_psquery( in, out )
+    struct papfile     *in, *out;
+{
+    struct comment     *comment;
+    char               *start, *stop;
+
+    for (;;) {
+       switch ( markline( &start, &stop, in )) {
+       case 0 :
+           /* eof on infile */
+           out->pf_state |= PF_EOF;
+           compop();
+           return( CH_DONE );
+
+       case -1 :
+           return( CH_MORE );
+       }
+
+       if ( in->pf_state & PF_BOT ) {
+           in->pf_state &= ~PF_BOT;
+       } else {
+           if (( comment = commatch( start, stop, queries )) != NULL ) {
+               compush( comment );
+               return( CH_DONE );
+           }
+       }
+
+       consumetomark( start, stop, in );
+    }
+}
+
+cm_psadobe( in, out )
+    struct papfile     *in, *out;
+{
+    char               *start, *stop;
+    struct comment     *comment = compeek();
+
+    for (;;) {
+       switch ( markline( &start, &stop, in )) {
+       case 0 :
+           /* eof on infile */
+           out->pf_state |= PF_EOF;
+           compop();
+           return( CH_DONE );
+
+       case -1 :
+           return( CH_MORE );
+       }
+
+       if ( in->pf_state & PF_BOT ) {
+           in->pf_state &= ~PF_BOT;
+           if ( lp_open( out ) < 0 ) {
+               syslog( LOG_ERR, "lp_open failed" );
+               spoolerror( out, "Ignoring job." );
+           }
+       } else {
+           if (( comment = commatch( start, stop, headers )) != NULL ) {
+               compush( comment );
+               return( CH_DONE );
+           }
+       }
+
+       *stop = '\n';
+       lp_write( start, stop - start + 1 );
+       consumetomark( start, stop, in );
+    }
+}
+
+char   *Query = "Query";
+
+cm_psswitch( in, out )
+    struct papfile     *in, *out;
+{
+    char               *start, *stop, *p;
+    struct comment     *comment = compeek();
+
+    switch ( markline( &start, &stop, in )) {
+    case 0 :
+       /* eof on infile */
+       out->pf_state |= PF_EOF;
+       compop();
+       return( 0 );
+
+    case -1 :
+       return( CH_MORE );
+    }
+
+    for ( p = start; p < stop; p++ ) {
+       if ( *p == ' ' || *p == '\t' ) {
+           break;
+       }
+    }
+    for ( ; p < stop; p++ ) {
+       if ( *p != ' ' && *p != '\t' ) {
+           break;
+       }
+    }
+
+    if ( stop - p >= strlen( Query ) &&
+           strncmp( p, Query, strlen( Query )) == 0 ) {
+       if ( comswitch( magics, cm_psquery ) < 0 ) {
+           syslog( LOG_ERR, "cm_psswitch: can't find psquery!" );
+           exit( 1 );
+       }
+    } else {
+       if ( comswitch( magics, cm_psadobe ) < 0 ) {
+           syslog( LOG_ERR, "cm_psswitch: can't find psadobe!" );
+           exit( 1 );
+       }
+    }
+    return( CH_DONE );
+}
+
+struct comment magics[] = {
+    { "%!PS-Adobe-3.0 Query",  0,                      cm_psquery, C_FULL },
+    { "%!PS-Adobe-3.0",                0,                      cm_psadobe, C_FULL },
+    { "%!PS-Adobe-",           0,                      cm_psswitch,    0 },
+    { 0 },
+};
diff --git a/etc/papd/main.c b/etc/papd/main.c
new file mode 100644 (file)
index 0000000..4b2239b
--- /dev/null
@@ -0,0 +1,727 @@
+/*
+ * Copyright (c) 1990,1995 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#if defined( sun ) && defined( __svr4__ )
+#include </usr/ucbinclude/sys/file.h>
+#else sun __svr4__
+#include <sys/file.h>
+#endif sun __svr4__
+#include <sys/socket.h>
+#include <sys/syslog.h>
+#include <sys/wait.h>
+#include <errno.h>
+
+#include <string.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <atalk/compat.h>
+#include <atalk/atp.h>
+#include <atalk/pap.h>
+#include <atalk/paths.h>
+#include <atalk/util.h>
+
+#include "printer.h"
+
+#define _PATH_PAPDPPDFILE      ".ppd"
+
+#define PIPED_STATUS   "status: print spooler processing job"
+
+struct printer defprinter;
+struct printer *printers = NULL;
+
+int            debug = 0;
+char           *conffile = _PATH_PAPDCONF;
+char           *printcap = _PATH_PAPDPRINTCAP;
+unsigned char  connid, quantum, sock, oquantum = PAP_MAXQUANTUM;
+char           *cannedstatus = PIPED_STATUS;
+struct printer *printer = NULL;
+char           *version = VERSION;
+static char      *pidfile = _PATH_PAPDLOCK;
+
+/* this only needs to be used by the server process */
+static void papd_exit(const int i)
+{
+  server_unlock(pidfile);
+  exit(i);
+}
+
+#if !defined( ibm032 ) && !defined( _IBMR2 )
+    void
+#endif ibm032 _IBMR2
+die( n )
+    int                        n;
+{
+    struct printer     *pr;
+
+    for ( pr = printers; pr; pr = pr->p_next ) {
+       if ( pr->p_flags & P_REGISTERED ) {
+           if ( nbp_unrgstr( pr->p_name, pr->p_type, pr->p_zone ) < 0 ) {
+               syslog( LOG_ERR, "can't unregister %s:%s@%s\n", pr->p_name,
+                       pr->p_type, pr->p_zone );
+               papd_exit( n + 1 );
+           }
+           syslog( LOG_ERR, "unregister %s:%s@%s\n", pr->p_name, pr->p_type,
+                   pr->p_zone );
+       }
+    }
+    papd_exit( n );
+}
+
+#if !defined( ibm032 ) && !defined( _IBMR2 )
+    void
+#endif ibm032 _IBMR2
+reap()
+{
+    int                status;
+    int                pid;
+
+    while (( pid = wait3( &status, WNOHANG, 0 )) > 0 ) {
+       if ( WIFEXITED( status )) {
+           if ( WEXITSTATUS( status )) {
+               syslog( LOG_ERR, "child %d exited with %d", pid,
+                       WEXITSTATUS( status ));
+           } else {
+               syslog( LOG_INFO, "child %d done", pid );
+           }
+       } else {
+           if ( WIFSIGNALED( status )) {
+               syslog( LOG_ERR, "child %d killed with %d", pid,
+                       WTERMSIG( status ));
+           } else {
+               syslog( LOG_ERR, "child %d died", pid );
+           }
+       }
+    }
+    return;
+}
+
+char           rbuf[ 255 + 1 + 8 ];
+
+main( ac, av )
+    int                ac;
+    char       **av;
+{
+    extern char         *optarg;
+    extern int          optind;
+
+    ATP                        atp;
+    struct atp_block   atpb;
+    struct sockaddr_at sat;
+    struct sigaction   sv;
+    struct iovec       iov;
+    fd_set             fdset;
+    struct printer     *pr;
+    char               *p, hostname[ MAXHOSTNAMELEN ];
+    char               cbuf[ 8 ];
+    int                        c;
+
+    if ( gethostname( hostname, sizeof( hostname )) < 0 ) {
+       perror( "gethostname" );
+       exit( 1 );
+    }
+    if (( p = strchr( hostname, '.' )) != 0 ) {
+       *p = '\0';
+    }
+    if (( defprinter.p_name = (char *)malloc( strlen( hostname ) + 1 ))
+           == NULL ) {
+       perror( "malloc" );
+       exit( 1 );
+    }
+    strcpy( defprinter.p_name, hostname );
+    defprinter.p_type = "LaserWriter";
+    defprinter.p_zone = "*";
+    memset(&defprinter.p_addr, 0, sizeof(defprinter.p_addr));
+    defprinter.p_ppdfile = _PATH_PAPDPPDFILE;
+#ifdef __svr4__
+    defprinter.p_flags = P_PIPED;
+    defprinter.p_printer = "/usr/bin/lp -T PS";
+#else
+    defprinter.p_flags = P_SPOOLED;
+    defprinter.p_printer = "lp";
+#endif
+    defprinter.p_operator = "operator";
+    defprinter.p_spool = _PATH_PAPDSPOOLDIR;
+#ifdef ABS_PRINT
+    defprinter.p_role = NULL;
+    defprinter.p_srvid = 0;
+#endif ABS_PRINT
+    defprinter.p_pagecost = 200;               /* default cost */
+    defprinter.p_pagecost_msg = NULL;
+    defprinter.p_lock = "lock";
+
+    while (( c = getopt( ac, av, "adf:p:P:" )) != EOF ) {
+       switch ( c ) {
+       case 'a' :              /* for compatibility with old papd */
+           break;
+
+       case 'd' :              /* debug */
+           debug++;
+           break;
+
+       case 'f' :              /* conffile */
+           conffile = optarg;
+           break;
+
+       case 'p' :              /* printcap */
+           printcap = optarg;
+           break;
+
+       case 'P' :
+           pidfile = optarg;
+           break;
+
+       default :
+           fprintf( stderr,
+                   "Usage:\t%s [ -d ] [ -f conffile ] [ -p printcap ]\n",
+                   *av );
+           exit( 1 );
+       }
+    }
+
+    getprinters( conffile );
+
+    switch (server_lock("papd", pidfile, debug)) {
+    case 0: /* open a couple things again in the child */
+      if ((c = open("/", O_RDONLY)) >= 0) {
+       dup2(c, 1);
+       dup2(c, 2);
+      }
+      break;
+    case -1:
+      exit(1);
+    default:
+      exit(0);
+    }      
+
+    /*
+     * Start logging.
+     */
+    if (( p = strrchr( av[ 0 ], '/' )) == NULL ) {
+       p = av[ 0 ];
+    } else {
+       p++;
+    }
+#ifdef ultrix
+    openlog( p, LOG_PID );
+#else ultrix
+    openlog( p, LOG_NDELAY|LOG_PID, LOG_LPR );
+#endif ultrix
+
+    syslog( LOG_INFO, "restart (%s)", version );
+
+    for ( pr = printers; pr; pr = pr->p_next ) {
+       if (( pr->p_flags & P_SPOOLED ) && rprintcap( pr ) < 0 ) {
+           syslog( LOG_ERR, "printcap problem: %s", pr->p_printer );
+       }
+       if (( pr->p_atp = atp_open( ATADDR_ANYPORT, &pr->p_addr )) == NULL ) {
+           syslog( LOG_ERR, "atp_open: %m" );
+           papd_exit( 1 );
+       }
+       if ( nbp_rgstr( atp_sockaddr( pr->p_atp ), pr->p_name, pr->p_type,
+               pr->p_zone ) < 0 ) {
+           syslog( LOG_ERR, "can't register %s:%s@%s", pr->p_name, pr->p_type,
+                   pr->p_zone );
+           die( 1 );
+       }
+       syslog( LOG_INFO, "register %s:%s@%s", pr->p_name, pr->p_type,
+               pr->p_zone );
+       pr->p_flags |= P_REGISTERED;
+    }
+
+    memset(&sv, 0, sizeof(sv));
+    sv.sa_handler = die;
+    sigemptyset( &sv.sa_mask );
+    sv.sa_flags = SA_RESTART;
+    if ( sigaction( SIGTERM, &sv, 0 ) < 0 ) {
+       syslog( LOG_ERR, "sigaction: %m" );
+       papd_exit( 1 );
+    }
+
+    sv.sa_handler = reap;
+    sigemptyset( &sv.sa_mask );
+    sv.sa_flags = SA_RESTART;
+    if ( sigaction( SIGCHLD, &sv, 0 ) < 0 ) {
+       syslog( LOG_ERR, "sigaction: %m" );
+       papd_exit( 1 );
+    }
+
+    /*
+     * Begin accepting connections.
+     */
+    FD_ZERO( &fdset );
+    for (;;) {
+       for ( pr = printers; pr; pr = pr->p_next ) {
+           FD_SET( atp_fileno( pr->p_atp ), &fdset );
+       }
+       if (( c = select( FD_SETSIZE, &fdset, 0, 0, 0 )) < 0 ) {
+           if ( errno == EINTR ) {
+               continue;
+           }
+           syslog( LOG_ERR, "select: %m" );
+           papd_exit( 1 );
+       }
+
+       for ( pr = printers; pr; pr = pr->p_next ) {
+           if ( FD_ISSET( atp_fileno( pr->p_atp ), &fdset )) {
+               int             err = 0;
+
+               bzero( &sat, sizeof( struct sockaddr_at ));
+#ifdef BSD4_4
+               sat.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+               sat.sat_family = AF_APPLETALK;
+               sat.sat_addr.s_net = ATADDR_ANYNET;
+               sat.sat_addr.s_node = ATADDR_ANYNODE;
+               sat.sat_port = ATADDR_ANYPORT;
+               /* do an atp_rsel(), to prevent hangs */
+               if (( c = atp_rsel( pr->p_atp, &sat, ATP_TREQ )) != ATP_TREQ ) {
+                   continue;
+               }
+               atpb.atp_saddr = &sat;
+               atpb.atp_rreqdata = cbuf;
+               atpb.atp_rreqdlen = sizeof( cbuf );
+               if ( atp_rreq( pr->p_atp, &atpb ) < 0 ) {
+                   syslog( LOG_ERR, "atp_rreq: %m" );
+                   continue;
+               }
+
+               /* should check length of req buf */
+
+               switch( cbuf[ 1 ] ) {
+               case PAP_OPEN :
+                   connid = (unsigned char)cbuf[ 0 ];
+                   sock = (unsigned char)cbuf[ 4 ];
+                   quantum = (unsigned char)cbuf[ 5 ];
+                   rbuf[ 0 ] = cbuf[ 0 ];
+                   rbuf[ 1 ] = PAP_OPENREPLY;
+                   rbuf[ 2 ] = rbuf[ 3 ] = 0;
+
+                   if (( pr->p_flags & P_SPOOLED ) && rprintcap( pr ) != 0 ) {
+                       syslog( LOG_ERR, "printcap problem: %s",
+                               pr->p_printer );
+                       rbuf[ 2 ] = rbuf[ 3 ] = 0xff;
+                       err = 1;
+                   }
+
+                   /*
+                    * If this fails, we've run out of sockets. Rather than
+                    * just die(), let's try to continue. Maybe some sockets
+                    * will close, and we can continue;
+                    */
+                   if (( atp = atp_open( ATADDR_ANYPORT, 
+                                         &pr->p_addr)) == NULL ) {
+                       syslog( LOG_ERR, "atp_open: %m" );
+                       rbuf[ 2 ] = rbuf[ 3 ] = 0xff;
+                       err = 1;
+                   }
+                   rbuf[ 4 ] = atp_sockaddr( atp )->sat_port;
+                   rbuf[ 5 ] = oquantum;
+                   rbuf[ 6 ] = rbuf[ 7 ] = 0;
+
+                   iov.iov_base = rbuf;
+                   iov.iov_len = 8 + getstatus( pr, &rbuf[ 8 ] );
+                   atpb.atp_sresiov = &iov;
+                   atpb.atp_sresiovcnt = 1;
+                   /*
+                    * This may error out if we lose a route, so we won't die().
+                    */
+                   if ( atp_sresp( pr->p_atp, &atpb ) < 0 ) {
+                       syslog( LOG_ERR, "atp_sresp: %m" );
+                       continue;
+                   }
+
+                   if ( err ) {
+                       continue;
+                   }
+
+                   switch ( c = fork()) {
+                   case -1 :
+                       syslog( LOG_ERR, "fork: %m" );
+                       continue;
+
+                   case 0 : /* child */
+                       printer = pr;
+
+                       if (( printer->p_flags & P_SPOOLED ) &&
+                               chdir( printer->p_spool ) < 0 ) {
+                           syslog( LOG_ERR, "chdir %s: %m", printer->p_spool );
+                           exit( 1 );
+                       }
+
+                       sv.sa_handler = SIG_DFL;
+                       sigemptyset( &sv.sa_mask );
+                       sv.sa_flags = SA_RESTART;
+                       if ( sigaction( SIGTERM, &sv, 0 ) < 0 ) {
+                           syslog( LOG_ERR, "sigaction: %m" );
+                           exit( 1 );
+                       }
+
+                       for ( pr = printers; pr; pr = pr->p_next ) {
+                           atp_close( pr->p_atp );
+                       }
+                       sat.sat_port = sock;
+                       if ( session( atp, &sat ) < 0 ) {
+                           syslog( LOG_ERR, "bad session" );
+                           exit( 1 );
+                       }
+                       exit( 0 );
+                       break;
+
+                   default : /* parent */
+                       syslog( LOG_INFO, "child %d for \"%s\" from %u.%u",
+                               c, pr->p_name, ntohs( sat.sat_addr.s_net ),
+                               sat.sat_addr.s_node);
+                       atp_close( atp );
+                   }
+                   break;
+
+               case PAP_SENDSTATUS :
+                   rbuf[ 0 ] = 0;
+                   rbuf[ 1 ] = PAP_STATUS;
+                   rbuf[ 2 ] = rbuf[ 3 ] = 0;
+                   rbuf[ 4 ] = rbuf[ 5 ] = 0;
+                   rbuf[ 6 ] = rbuf[ 7 ] = 0;
+
+                   iov.iov_base = rbuf;
+                   iov.iov_len = 8 + getstatus( pr, &rbuf[ 8 ] );
+                   atpb.atp_sresiov = &iov;
+                   atpb.atp_sresiovcnt = 1;
+                   /*
+                    * This may error out if we lose a route, so we won't die().
+                    */
+                   if ( atp_sresp( pr->p_atp, &atpb ) < 0 ) {
+                       syslog( LOG_ERR, "atp_sresp: %m" );
+                   }
+                   break;
+
+               default :
+                   syslog( LOG_ERR, "Bad request from %u.%u!",
+                           ntohs( sat.sat_addr.s_net ), sat.sat_addr.s_node );
+                   continue;
+                   break;
+               }
+
+#ifdef notdef
+               /*
+                * Sometimes the child process will send its first READ
+                * before the parent has sent the OPEN REPLY.  Moving this
+                * code into the OPEN/STATUS switch fixes this problem.
+                */
+               iov.iov_base = rbuf;
+               iov.iov_len = 8 + getstatus( pr, &rbuf[ 8 ] );
+               atpb.atp_sresiov = &iov;
+               atpb.atp_sresiovcnt = 1;
+               /*
+                * This may error out if we lose a route, so we won't die().
+                */
+               if ( atp_sresp( pr->p_atp, &atpb ) < 0 ) {
+                   syslog( LOG_ERR, "atp_sresp: %m" );
+               }
+#endif notdef
+           }
+       }
+    }
+}
+
+/*
+ * We assume buf is big enough for 255 bytes of data and a length byte.
+ */
+    int
+getstatus( pr, buf )
+    struct printer     *pr;
+    char               *buf;
+{
+    char               path[ MAXPATHLEN ];
+    int                        fd = -1, rc;
+
+    if ( pr->p_flags & P_SPOOLED && ( pr->p_spool != NULL )) {
+       strcpy( path, pr->p_spool );
+       strcat( path, "/status" );
+       fd = open( path, O_RDONLY);
+    }
+
+    if (( pr->p_flags & P_PIPED ) || ( fd < 0 )) {
+       *buf = strlen( cannedstatus );
+       strncpy( &buf[ 1 ], cannedstatus, *buf );
+       return( *buf + 1 );
+    } else {
+       if (( rc = read( fd, &buf[ 1 ], 255 )) < 0 ) {
+           rc = 0;
+       }
+       close( fd );
+       if ( rc && buf[ rc ] == '\n' ) {        /* remove trailing newline */
+           rc--;
+       }
+       *buf = rc;
+       return( rc + 1 );
+    }
+}
+
+char   *pgetstr();
+char   *getpname();
+
+getprinters( cf )
+    char       *cf;
+{
+    char               buf[ 1024 ], area[ 1024 ], *a, *p, *name, *type, *zone;
+    struct printer     *pr;
+    int                        c;
+
+    while (( c = getprent( cf, buf )) > 0 ) {
+       a = area;
+       /*
+        * Get the printer's nbp name.
+        */
+       if (( p = getpname( &a )) == NULL ) {
+           fprintf( stderr, "No printer name\n" );
+           exit( 1 );
+       }
+
+       if (( pr = (struct printer *)malloc( sizeof( struct printer )))
+               == NULL ) {
+           perror( "malloc" );
+           exit( 1 );
+       }
+       bzero( pr, sizeof( struct printer ));
+
+       name = defprinter.p_name;
+       type = defprinter.p_type;
+       zone = defprinter.p_zone;
+       if ( nbp_name( p, &name, &type, &zone )) {
+           fprintf( stderr, "Can't parse \"%s\"\n", name );
+           exit( 1 );
+       }
+       if ( name != defprinter.p_name ) {
+           if (( pr->p_name = (char *)malloc( strlen( name ) + 1 )) == NULL ) {
+               perror( "malloc" );
+               exit( 1 );
+           }
+           strcpy( pr->p_name, name );
+       } else {
+           pr->p_name = name;
+       }
+       if ( type != defprinter.p_type ) {
+           if (( pr->p_type = (char *)malloc( strlen( type ) + 1 )) == NULL ) {
+               perror( "malloc" );
+               exit( 1 );
+           }
+           strcpy( pr->p_type, type );
+       } else {
+           pr->p_type = type;
+       }
+       if ( zone != defprinter.p_zone ) {
+           if (( pr->p_zone = (char *)malloc( strlen( zone ) + 1 )) == NULL ) {
+               perror( "malloc" );
+               exit( 1 );
+           }
+           strcpy( pr->p_zone, zone );
+       } else {
+           pr->p_zone = zone;
+       }
+
+       if ( pnchktc( cf ) != 1 ) {
+           fprintf( stderr, "Bad papcap entry\n" );
+           exit( 1 );
+       }
+
+       /*
+        * Get PPD file.
+        */
+       if (( p = pgetstr( "pd", &a )) == NULL ) {
+           pr->p_ppdfile = defprinter.p_ppdfile;
+       } else {
+           if (( pr->p_ppdfile = (char *)malloc( strlen( p ) + 1 )) == NULL ) {
+               perror( "malloc" );
+               exit( 1 );
+           }
+           strcpy( pr->p_ppdfile, p );
+       }
+
+       /*
+        * Get lpd printer name.
+        */
+       if (( p = pgetstr( "pr", &a )) == NULL ) {
+           pr->p_printer = defprinter.p_printer;
+           pr->p_flags = defprinter.p_flags;
+       } else {
+           if ( *p == '|' ) {
+               p++;
+               pr->p_flags = P_PIPED;
+           } else {
+               pr->p_flags = P_SPOOLED;
+           }
+           if (( pr->p_printer = (char *)malloc( strlen( p ) + 1 )) == NULL ) {
+               perror( "malloc" );
+               exit( 1 );
+           }
+           strcpy( pr->p_printer, p );
+       }
+
+       if ( pr->p_flags & P_SPOOLED ) {
+           /*
+            * Get operator name.
+            */
+           if (( p = pgetstr( "op", &a )) == NULL ) {
+               pr->p_operator = defprinter.p_operator;
+           } else {
+               if (( pr->p_operator = (char *)malloc( strlen( p ) + 1 ))
+                       == NULL ) {
+                   perror( "malloc" );
+                   exit( 1 );
+               }
+               strcpy( pr->p_operator, p );
+           }
+       }
+
+       /* get printer's appletalk address. */
+       if (( p = pgetstr( "pa", &a )) == NULL ) 
+           memcpy(&pr->p_addr, &defprinter.p_addr, sizeof(pr->p_addr));
+       else 
+           atalk_aton(p, &pr->p_addr);
+
+       pr->p_next = printers;
+       printers = pr;
+    }
+    if ( c == 0 ) {
+       endprent();
+    } else {                   /* No capability file, do default */
+       printers = &defprinter;
+    }
+}
+
+rprintcap( pr )
+    struct printer     *pr;
+{
+    char               buf[ 1024 ], area[ 1024 ], *a, *p;
+    int                        c;
+
+    /*
+     * Spool directory from printcap file.
+     */
+    if ( pr->p_flags & P_SPOOLED ) {
+       if ( pgetent( printcap, buf, pr->p_printer ) != 1 ) {
+           syslog( LOG_ERR, "No such printer: %s", pr->p_printer );
+           return( -1 );
+       }
+
+       /*
+        * Spool directory.
+        */
+       if ( pr->p_spool != NULL && pr->p_spool != defprinter.p_spool ) {
+           free( pr->p_spool );
+       }
+       a = area;
+       if (( p = pgetstr( "sd", &a )) == NULL ) {
+           pr->p_spool = defprinter.p_spool;
+       } else {
+           if (( pr->p_spool = (char *)malloc( strlen( p ) + 1 )) == NULL ) {
+               syslog( LOG_ERR, "malloc: %m" );
+               exit( 1 );
+           }
+           strcpy( pr->p_spool, p );
+       }
+
+       /*
+        * Is accounting on?
+        */
+       a = area;
+       if ( pgetstr( "af", &a ) == NULL ) {
+           pr->p_flags &= ~P_ACCOUNT;
+       } else {
+           pr->p_flags |= P_ACCOUNT;
+#ifdef ABS_PRINT
+           if ( pr->p_role != NULL && pr->p_role != defprinter.p_role ) {
+               free( pr->p_role );
+           }
+           a = area;
+           if (( p = pgetstr( "ro", &a )) == NULL ) {
+               pr->p_role = defprinter.p_role;
+           } else {
+               if (( pr->p_role =
+                       (char *)malloc( strlen( p ) + 1 )) == NULL ) {
+                   syslog( LOG_ERR, "malloc: %m" );
+                   exit( 1 );
+               }
+               strcpy( pr->p_role, p );
+           }
+
+           if (( c = pgetnum( "si" )) < 0 ) {
+               pr->p_srvid = defprinter.p_srvid;
+           } else {
+               pr->p_srvid = c;
+           }
+#endif ABS_PRINT
+       }
+
+
+       /*
+        * Cost of printer.
+        */
+       if ( pr->p_pagecost_msg != NULL &&
+               pr->p_pagecost_msg != defprinter.p_pagecost_msg ) {
+           free( pr->p_pagecost_msg );
+       }
+       a = area;
+       if (( p = pgetstr( "pc", &a )) != NULL ) {
+           if (( pr->p_pagecost_msg =
+                   (char *)malloc( strlen( p ) + 1 )) == NULL ) {
+               syslog( LOG_ERR, "malloc: %m" );
+               exit( 1 );
+           }
+           strcpy( pr->p_pagecost_msg, p );
+           pr->p_pagecost = 0;
+       } else if ( pr->p_flags & P_ACCOUNT ) {
+           if (( c = pgetnum( "pc" )) < 0 ) {
+               pr->p_pagecost = defprinter.p_pagecost;
+           } else {
+               pr->p_pagecost = c;
+           }
+           pr->p_pagecost_msg = NULL;
+       }
+
+       /*
+        * Get lpd lock file.
+        */
+       if ( pr->p_lock != NULL && pr->p_lock != defprinter.p_lock ) {
+           free( pr->p_lock );
+       }
+       a = area;
+       if (( p = pgetstr( "lo", &a )) == NULL ) {
+           pr->p_lock = defprinter.p_lock;
+       } else {
+           if (( pr->p_lock = (char *)malloc( strlen( p ) + 1 )) == NULL ) {
+               syslog( LOG_ERR, "malloc: %m" );
+               exit( 1 );
+           }
+           strcpy( pr->p_lock, p );
+       }
+
+#ifdef KRB
+       /*
+        * Must Kerberos authenticate?
+        */
+       if ( pgetflag( "ka" ) == 1 ) {
+           pr->p_flags |= P_AUTH;
+       } else {
+           pr->p_flags &= ~P_AUTH;
+       }
+#endif
+
+       endprent();
+    }
+
+    return( 0 );
+}
diff --git a/etc/papd/ppd.c b/etc/papd/ppd.c
new file mode 100644 (file)
index 0000000..783719f
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 1995 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/syslog.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <netatalk/at.h>
+#include <atalk/atp.h>
+
+#include "printer.h"
+#include "ppd.h"
+
+struct ppd_font                *ppd_fonts = NULL;
+
+struct ppd_feature     ppd_features[] = {
+    { "*LanguageLevel",        0 },
+    { "*PSVersion",    0 },
+    { "*FreeVM",       0 },
+    { "*Product",      0 },
+    { "*PCFileName",   0 },
+    { "*ModelName",    0 },
+    { "*NickName",     0 },
+    { "*ColorDevice",  0 },
+    { "*FaxSupport",   0 },
+    { "*TTRasterizer", 0 },
+    { 0, 0 },
+};
+
+struct ppdent {
+    char       *pe_main;
+    char       *pe_option;
+    char       *pe_translation;
+    char       *pe_value;
+};
+
+#ifdef notdef
+main( ac, av )
+    int                ac;
+    char       **av;
+{
+    struct ppd_feature *pfe;
+    struct ppd_font    *pfo;
+
+    if ( ac != 2 ) {
+       fprintf( stderr, "Usage:\t%s ppdfile\n", av[ 0 ] );
+       exit( 1 );
+    }
+
+    read_ppd( av[ 1 ], 0 );
+    for ( pfo = ppd_fonts; pfo; pfo = pfo->pd_next ) {
+       printf( "Font: %s\n", pfo->pd_font );
+    }
+    for ( pfe = ppd_features; pfe->pd_name; pfe++ ) {
+       printf( "Feature: %s %s\n", pfe->pd_name, pfe->pd_value );
+    }
+
+    exit( 0 );
+}
+#endif notdef
+
+int ppd_inited = 0;
+
+ppd_init()
+{
+    if ( ppd_inited ) {
+       return( -1 );
+    }
+    ppd_inited++;
+
+    read_ppd( printer->p_ppdfile, 0 );
+}
+
+    struct ppdent *
+getppdent( stream )
+    FILE       *stream;
+{
+    static char                        buf[ 1024 ];
+    static struct ppdent       ppdent;
+    char                       *p, *q;
+
+    ppdent.pe_main = ppdent.pe_option = ppdent.pe_translation =
+           ppdent.pe_value = NULL;
+
+    while (( p = fgets( buf, sizeof( buf ), stream )) != NULL ) {
+       if ( *p != '*' ) {      /* main key word */
+           continue;
+       }
+       if ( p[ strlen( p ) - 1 ] != '\n' ) {
+           syslog( LOG_ERR, "getppdent: line too long" );
+           continue;
+       }
+
+       q = p;
+       while ( *p != ' ' && *p != '\t' && *p != ':' && *p != '\n' ) {
+           p++;
+       }
+       if ( *( q + 1 ) == '%' || *( q + 1 ) == '?' ) { /* comments & queries */
+           continue;
+       }
+       ppdent.pe_main = q;
+       if ( *p == '\n' ) {
+           *p = '\0';
+           ppdent.pe_option = ppdent.pe_translation = ppdent.pe_value = NULL;
+           return( &ppdent );
+       }
+
+       if ( *p != ':' ) {      /* option key word */
+           *p++ = '\0';
+
+           while ( *p == ' ' || *p == '\t' ) {
+               p++;
+           }
+
+           q = p;
+           while ( *p != ':' && *p != '/' && *p != '\n' ) {
+               p++;
+           }
+
+           if ( *p == '\n' ) {
+               continue;
+           }
+
+           ppdent.pe_option = q;
+           if ( *p == '/' ) {  /* translation string */
+               *p++ = '\0';
+               q = p;
+               while ( *p != ':' && *p != '\n' ) {
+                   p++;
+               }
+               if ( *p != ':' ) {
+                   continue;
+               }
+
+               ppdent.pe_translation = q;
+           } else {
+               ppdent.pe_translation = NULL;
+           }
+       }
+       *p++ = '\0';
+
+       while ( *p == ' ' || *p == '\t' ) {
+           p++;
+       }
+
+       /* value */
+       if ( *p == '"' ) {
+           p++;
+           q = p;
+
+           while ( *p != '"' && *p != '\n' ) {
+               p++;
+           }
+
+           if ( *p == '\n' ) {
+               continue;
+           }
+           *p = '\0';
+           ppdent.pe_value = q;
+       } else {
+           q = p;
+           while ( *p != '\n' ) {
+               p++;
+           }
+           *p = '\0';
+           ppdent.pe_value = q;
+       }
+       return( &ppdent );
+    }
+
+    return( NULL );
+}
+
+read_ppd( file, fcnt )
+    char       *file;
+    int                fcnt;
+{
+    FILE               *ppdfile;
+    struct ppdent      *pe;
+    struct ppd_feature *pfe;
+    struct ppd_font    *pfo;
+
+    if ( fcnt > 20 ) {
+       syslog( LOG_ERR, "read_ppd: %s: Too many files!", file );
+       return( -1 );
+    }
+
+    if (( ppdfile = fopen( file, "r" )) == NULL ) {
+       syslog( LOG_ERR, "read_ppd %s: %m", file );
+       return( -1 );
+    }
+
+    while (( pe = getppdent( ppdfile )) != NULL ) {
+       /* *Include files */
+       if ( strcmp( pe->pe_main, "*Include" ) == 0 ) {
+           read_ppd( pe->pe_value, fcnt + 1 );
+           continue;
+       }
+
+       /* *Font */
+       if ( strcmp( pe->pe_main, "*Font" ) == 0 ) {
+           for ( pfo = ppd_fonts; pfo; pfo = pfo->pd_next ) {
+               if ( strcmp( pfo->pd_font, pe->pe_option ) == 0 ) {
+                   break;
+               }
+           }
+           if ( pfo ) {
+               continue;
+           }
+
+           if (( pfo = (struct ppd_font *)malloc( sizeof( struct ppd_font )))
+                   == NULL ) {
+               syslog( LOG_ERR, "malloc: %m" );
+               exit( 1 );
+           }
+           if (( pfo->pd_font =
+                   (char *)malloc( strlen( pe->pe_option ) + 1 )) == NULL ) {
+               syslog( LOG_ERR, "malloc: %m" );
+               exit( 1 );
+           }
+           strcpy( pfo->pd_font, pe->pe_option );
+           pfo->pd_next = ppd_fonts;
+           ppd_fonts = pfo;
+           continue;
+       }
+
+
+       /* Features */
+       for ( pfe = ppd_features; pfe->pd_name; pfe++ ) {
+           if ( strcmp( pe->pe_main, pfe->pd_name ) == 0 ) {
+               break;
+           }
+       }
+       if ( pfe->pd_name && pfe->pd_value == NULL ) {
+           if (( pfe->pd_value =
+                   (char *)malloc( strlen( pe->pe_value ) + 1 )) == NULL ) {
+               syslog( LOG_ERR, "malloc: %m" );
+               exit( 1 );
+           }
+
+           strcpy( pfe->pd_value, pe->pe_value );
+           continue;
+       }
+    }
+
+    fclose( ppdfile );
+    return( 0 );
+}
+
+    struct ppd_font *
+ppd_font( font )
+    char       *font;
+{
+    struct ppd_font    *pfo;
+
+    if ( ! ppd_inited ) {
+       ppd_init();
+    }
+
+    for ( pfo = ppd_fonts; pfo; pfo = pfo->pd_next ) {
+       if ( strcmp( pfo->pd_font, font ) == 0 ) {
+           return( pfo );
+       }
+    }
+    return( NULL );
+}
+
+    struct ppd_feature *
+ppd_feature( feature, len )
+    char       *feature;
+    int                len;
+{
+    struct ppd_feature *pfe;
+    char               main[ 256 ];
+    char               *end, *p, *q;
+
+    if ( ! ppd_inited ) {
+       ppd_init();
+    }
+
+    for ( end = feature + len, p = feature, q = main;
+           p <= end && *p != '\n' && *p != '\r'; p++, q++ ) {
+       *q = *p;
+    }
+    if ( p > end ) {
+       return( NULL );
+    }
+    *q = '\0';
+
+    for ( pfe = ppd_features; pfe->pd_name; pfe++ ) {
+       if ( strcmp( pfe->pd_name, main ) == 0 && pfe->pd_value ) {
+           return( pfe );
+       }
+    }
+
+    return( NULL );
+}
diff --git a/etc/papd/ppd.h b/etc/papd/ppd.h
new file mode 100644 (file)
index 0000000..b519533
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 1995 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+struct ppd_font {
+    char               *pd_font;
+    struct ppd_font    *pd_next;
+};
+
+struct ppd_feature {
+    char       *pd_name;
+    char       *pd_value;
+};
+
+struct ppd_feature     *ppd_feature();
+struct ppd_font                *ppd_font();
diff --git a/etc/papd/printcap.c b/etc/papd/printcap.c
new file mode 100644 (file)
index 0000000..2459e1a
--- /dev/null
@@ -0,0 +1,513 @@
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * 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 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)printcap.c 5.7 (Berkeley) 3/4/91";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <atalk/paths.h>
+
+#ifndef BUFSIZ
+#define        BUFSIZ  1024
+#endif
+#define MAXHOP 32      /* max number of tc= indirections */
+
+/*
+ * termcap - routines for dealing with the terminal capability data base
+ *
+ * BUG:                Should use a "last" pointer in tbuf, so that searching
+ *             for capabilities alphabetically would not be a n**2/2
+ *             process when large numbers of capabilities are given.
+ * Note:       If we add a last pointer now we will screw up the
+ *             tc capability. We really should compile termcap.
+ *
+ * Essentially all the work here is scanning and decoding escapes
+ * in string capabilities.  We don't use stdio because the editor
+ * doesn't, and because living w/o it is not hard.
+ */
+
+#define PRINTCAP
+
+#ifdef PRINTCAP
+#define tgetent        pgetent
+#define tskip  pskip
+#define tgetstr        pgetstr
+#define tdecode pdecode
+#define tgetnum        pgetnum
+#define        tgetflag pgetflag
+#define tdecode pdecode
+#define tnchktc        pnchktc
+#define        tnamatch pnamatch
+#define V6
+#endif
+
+static FILE *pfp = NULL;       /* printcap data base file pointer */
+static char *tbuf;
+static int hopcount;           /* detect infinite loops in termcap, init 0 */
+static char    *tskip();
+char   *tgetstr();
+static char    *tdecode();
+char   *getenv();
+
+/*
+ * Similar to tgetent except it returns the next entry instead of
+ * doing a lookup.
+ *
+ * Added a "cap" parameter, so we can use these calls for printcap
+ * and papd.conf.
+ */
+getprent( cap, bp)
+       register char *cap;
+       register char *bp;
+{
+       register int c, skip = 0;
+
+       if (pfp == NULL && (pfp = fopen( cap, "r")) == NULL)
+               return(-1);
+       tbuf = bp;
+       for (;;) {
+               switch (c = getc(pfp)) {
+               case EOF:
+                       fclose(pfp);
+                       pfp = NULL;
+                       return(0);
+               case '\n':
+                       if (bp == tbuf) {
+                               skip = 0;
+                               continue;
+                       }
+                       if (bp[-1] == '\\') {
+                               bp--;
+                               continue;
+                       }
+                       *bp = '\0';
+                       return(1);
+               case '#':
+                       if (bp == tbuf)
+                               skip++;
+               default:
+                       if (skip)
+                               continue;
+                       if (bp >= tbuf+BUFSIZ) {
+                               write(2, "Termcap entry too long\n", 23);
+                               *bp = '\0';
+                               return(1);
+                       }
+                       *bp++ = c;
+               }
+       }
+}
+
+endprent()
+{
+       if (pfp != NULL)
+               fclose(pfp);
+}
+
+/*
+ * Get an entry for terminal name in buffer bp,
+ * from the termcap file.  Parse is very rudimentary;
+ * we just notice escaped newlines.
+ *
+ * Added a "cap" parameter, so we can use these calls for printcap
+ * and papd.conf.
+ */
+tgetent( cap, bp, name)
+       char *cap, *bp, *name;
+{
+       register char *cp;
+       register int c;
+       register int i = 0, cnt = 0;
+       char ibuf[BUFSIZ];
+       int tf;
+
+       hopcount = 0;
+       tbuf = bp;
+       tf = 0;
+#ifndef V6
+       cp = getenv("TERMCAP");
+       /*
+        * TERMCAP can have one of two things in it. It can be the
+        * name of a file to use instead of /etc/termcap. In this
+        * case it better start with a "/". Or it can be an entry to
+        * use so we don't have to read the file. In this case it
+        * has to already have the newlines crunched out.
+        */
+       if (cp && *cp) {
+               if (*cp!='/') {
+                       cp2 = getenv("TERM");
+                       if (cp2==(char *) 0 || strcmp(name,cp2)==0) {
+                               strcpy(bp,cp);
+                               return(tnchktc());
+                       } else {
+                               tf = open(cap, 0);
+                       }
+               } else
+                       tf = open(cp, 0);
+       }
+       if (tf==0)
+               tf = open(cap, 0);
+#else
+       tf = open(cap, 0);
+#endif
+       if (tf < 0)
+               return (-1);
+       for (;;) {
+               cp = bp;
+               for (;;) {
+                       if (i == cnt) {
+                               cnt = read(tf, ibuf, BUFSIZ);
+                               if (cnt <= 0) {
+                                       close(tf);
+                                       return (0);
+                               }
+                               i = 0;
+                       }
+                       c = ibuf[i++];
+                       if (c == '\n') {
+                               if (cp > bp && cp[-1] == '\\'){
+                                       cp--;
+                                       continue;
+                               }
+                               break;
+                       }
+                       if (cp >= bp+BUFSIZ) {
+                               write(2,"Termcap entry too long\n", 23);
+                               break;
+                       } else
+                               *cp++ = c;
+               }
+               *cp = 0;
+
+               /*
+                * The real work for the match.
+                */
+               if (tnamatch(name)) {
+                       close(tf);
+                       return(tnchktc());
+               }
+       }
+}
+
+/*
+ * tnchktc: check the last entry, see if it's tc=xxx. If so,
+ * recursively find xxx and append that entry (minus the names)
+ * to take the place of the tc=xxx entry. This allows termcap
+ * entries to say "like an HP2621 but doesn't turn on the labels".
+ * Note that this works because of the left to right scan.
+ *
+ * Added a "cap" parameter, so we can use these calls for printcap
+ * and papd.conf.
+ */
+tnchktc( cap )
+    char *cap;
+{
+       register char *p, *q;
+       char tcname[16];        /* name of similar terminal */
+       char tcbuf[BUFSIZ];
+       char *holdtbuf = tbuf;
+       int l;
+
+       p = tbuf + strlen(tbuf) - 2;    /* before the last colon */
+       while (*--p != ':')
+               if (p<tbuf) {
+                       write(2, "Bad termcap entry\n", 18);
+                       return (0);
+               }
+       p++;
+       /* p now points to beginning of last field */
+       if (p[0] != 't' || p[1] != 'c')
+               return(1);
+       strcpy(tcname,p+3);
+       q = tcname;
+       while (q && *q != ':')
+               q++;
+       *q = 0;
+       if (++hopcount > MAXHOP) {
+               write(2, "Infinite tc= loop\n", 18);
+               return (0);
+       }
+       if (tgetent( cap, tcbuf, tcname) != 1)
+               return(0);
+       for (q=tcbuf; *q != ':'; q++)
+               ;
+       l = p - holdtbuf + strlen(q);
+       if (l > BUFSIZ) {
+               write(2, "Termcap entry too long\n", 23);
+               q[BUFSIZ - (p-tbuf)] = 0;
+       }
+       strcpy(p, q+1);
+       tbuf = holdtbuf;
+       return(1);
+}
+
+/*
+ * Tnamatch deals with name matching.  The first field of the termcap
+ * entry is a sequence of names separated by |'s, so we compare
+ * against each such name.  The normal : terminator after the last
+ * name (before the first field) stops us.
+ */
+tnamatch(np)
+       char *np;
+{
+       register char *Np, *Bp;
+
+       Bp = tbuf;
+       if (*Bp == '#')
+               return(0);
+       for (;;) {
+               for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
+                       continue;
+               if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
+                       return (1);
+               while (*Bp && *Bp != ':' && *Bp != '|')
+                       Bp++;
+               if (*Bp == 0 || *Bp == ':')
+                       return (0);
+               Bp++;
+       }
+}
+
+/*
+ * Skip to the next field.  Notice that this is very dumb, not
+ * knowing about \: escapes or any such.  If necessary, :'s can be put
+ * into the termcap file in octal.
+ */
+static char *
+tskip(bp)
+       register char *bp;
+{
+
+       while (*bp && *bp != ':')
+               bp++;
+       if (*bp == ':')
+               bp++;
+       return (bp);
+}
+
+/*
+ * Return the (numeric) option id.
+ * Numeric options look like
+ *     li#80
+ * i.e. the option string is separated from the numeric value by
+ * a # character.  If the option is not found we return -1.
+ * Note that we handle octal numbers beginning with 0.
+ */
+tgetnum(id)
+       char *id;
+{
+       register int i, base;
+       register char *bp = tbuf;
+
+       for (;;) {
+               bp = tskip(bp);
+               if (*bp == 0)
+                       return (-1);
+               if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
+                       continue;
+               if (*bp == '@')
+                       return(-1);
+               if (*bp != '#')
+                       continue;
+               bp++;
+               base = 10;
+               if (*bp == '0')
+                       base = 8;
+               i = 0;
+               while (isdigit(*bp))
+                       i *= base, i += *bp++ - '0';
+               return (i);
+       }
+}
+
+/*
+ * Handle a flag option.
+ * Flag options are given "naked", i.e. followed by a : or the end
+ * of the buffer.  Return 1 if we find the option, or 0 if it is
+ * not given.
+ */
+tgetflag(id)
+       char *id;
+{
+       register char *bp = tbuf;
+
+       for (;;) {
+               bp = tskip(bp);
+               if (!*bp)
+                       return (0);
+               if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
+                       if (!*bp || *bp == ':')
+                               return (1);
+                       else if (*bp == '@')
+                               return(0);
+               }
+       }
+}
+
+/*
+ * Get a string valued option.
+ * These are given as
+ *     cl=^Z
+ * Much decoding is done on the strings, and the strings are
+ * placed in area, which is a ref parameter which is updated.
+ * No checking on area overflow.
+ */
+char *
+tgetstr(id, area)
+       char *id, **area;
+{
+       register char *bp = tbuf;
+
+       for (;;) {
+               bp = tskip(bp);
+               if (!*bp)
+                       return (0);
+               if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
+                       continue;
+               if (*bp == '@')
+                       return(0);
+               if (*bp != '=')
+                       continue;
+               bp++;
+               return (tdecode(bp, area));
+       }
+}
+
+/*
+ * Tdecode does the grung work to decode the
+ * string capability escapes.
+ */
+static char *
+tdecode(str, area)
+       register char *str;
+       char **area;
+{
+       register char *cp;
+       register int c;
+       register char *dp;
+       int i;
+
+       cp = *area;
+       while ((c = *str++) && c != ':') {
+               switch (c) {
+
+               case '^':
+                       c = *str++ & 037;
+                       break;
+
+               case '\\':
+                       dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
+                       c = *str++;
+nextc:
+                       if (*dp++ == c) {
+                               c = *dp++;
+                               break;
+                       }
+                       dp++;
+                       if (*dp)
+                               goto nextc;
+                       if (isdigit(c)) {
+                               c -= '0', i = 2;
+                               do
+                                       c <<= 3, c |= *str++ - '0';
+                               while (--i && isdigit(*str));
+                       }
+                       break;
+               }
+               *cp++ = c;
+       }
+       *cp++ = 0;
+       str = *area;
+       *area = cp;
+       return (str);
+}
+
+static char *
+decodename(str, area)
+       register char *str;
+       char **area;
+{
+       register char *cp;
+       register int c;
+       register char *dp;
+       int i;
+
+       cp = *area;
+       while ((c = *str++) && c != ':' && c != '|' ) {
+               switch (c) {
+
+               case '^':
+                       c = *str++ & 037;
+                       break;
+
+               case '\\':
+                       dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
+                       c = *str++;
+nextc:
+                       if (*dp++ == c) {
+                               c = *dp++;
+                               break;
+                       }
+                       dp++;
+                       if (*dp)
+                               goto nextc;
+                       if (isdigit(c)) {
+                               c -= '0', i = 2;
+                               do
+                                       c <<= 3, c |= *str++ - '0';
+                               while (--i && isdigit(*str));
+                       }
+                       break;
+               }
+               *cp++ = c;
+       }
+       *cp++ = 0;
+       str = *area;
+       *area = cp;
+       return (str);
+}
+
+char *
+getpname( area )
+    char       **area;
+{
+    return( decodename( tbuf, area ));
+}
diff --git a/etc/papd/printer.h b/etc/papd/printer.h
new file mode 100644 (file)
index 0000000..72ce5b6
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1990,1995 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+struct printer {
+    char               *p_name;
+    char               *p_type;
+    char               *p_zone;
+#ifdef notdef
+    char               *p_fonts;
+    char               *p_psetdir;
+#endif notdef
+    char               *p_ppdfile;
+    int                        p_flags;
+    struct at_addr      p_addr;
+    union {
+       struct {
+           char                *pr_printer;
+           char                *pr_operator;
+           char                *pr_spool;
+#ifdef ABS_PRINT
+           char                *pr_role;
+           double              pr_balance;
+           int                 pr_srvid;
+#endif ABS_PRINT
+           int                 pr_pagecost;
+           char                *pr_pagecost_msg;
+           char                *pr_lock;
+       } pu_pr;
+       char            *pu_cmd;
+    } p_un;
+    ATP                        p_atp;
+    struct printer     *p_next;
+};
+#define p_cmd          p_un.pu_cmd
+#define p_printer      p_un.pu_pr.pr_printer
+#define p_operator     p_un.pu_pr.pr_operator
+#define p_spool                p_un.pu_pr.pr_spool
+#ifdef ABS_PRINT
+#define p_role         p_un.pu_pr.pr_role
+#define p_balance      p_un.pu_pr.pr_balance
+#define p_srvid                p_un.pu_pr.pr_srvid
+#endif ABS_PRINT
+#define p_pagecost     p_un.pu_pr.pr_pagecost
+#define p_pagecost_msg p_un.pu_pr.pr_pagecost_msg
+#define p_lock         p_un.pu_pr.pr_lock
+
+#define P_PIPED                (1<<0)
+#define P_SPOOLED      (1<<1)
+#define P_REGISTERED   (1<<2)
+#define P_ACCOUNT      (1<<3)
+#define P_AUTH         (1<<4)
+
+extern struct printer  *printer;
diff --git a/etc/papd/queries.c b/etc/papd/queries.c
new file mode 100644 (file)
index 0000000..28df080
--- /dev/null
@@ -0,0 +1,592 @@
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#include <string.h>
+#include <sys/syslog.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <netatalk/at.h>
+#include <atalk/atp.h>
+
+#ifdef KRB
+#ifdef SOLARIS
+#include <kerberos/krb.h>
+#else
+#include <krb.h>
+#endif
+#endif KRB
+
+#include "file.h"
+#include "comment.h"
+#include "printer.h"
+#include "ppd.h"
+
+cq_default( in, out )
+    struct papfile     *in, *out;
+{
+    char               *start, *stop, *p;
+    struct comment     *comment = compeek();
+
+    for (;;) {
+       switch ( markline( &start, &stop, in )) {
+       case 0 :
+           return( 0 );
+
+       case -1 :
+           return( CH_MORE );
+       }
+
+       if ( comgetflags() == 0 ) {     /* started */
+           if ( comment->c_end ) {
+               comsetflags( 1 );
+           } else {
+               compop();
+               consumetomark( start, stop, in );
+               return( CH_DONE );
+           }
+       } else {
+           /* return default */
+           if ( comcmp( start, stop, comment->c_end, 0 ) == 0 ) {
+               for ( p = start; p < stop; p++ ) {
+                   if ( *p == ':' ) {
+                       break;
+                   }
+               }
+               p++;
+               while ( *p == ' ' ) {
+                   p++;
+               }
+
+               *stop = '\n';
+               APPEND( out, p, stop - p + 1 );
+               compop();
+               consumetomark( start, stop, in );
+               return( CH_DONE );
+           }
+       }
+
+       consumetomark( start, stop, in );
+    }
+}
+
+#ifdef KRB
+char   *LoginOK = "LoginOK\n";
+char   *LoginFailed = "LoginFailed\n";
+
+#define h2b(x) (isdigit((x))?(x)-'0':(isupper((x))?(x)-'A':(x)-'a')+10)
+
+cq_k4login( in, out )
+    struct papfile     *in, *out;
+{
+    char               *start, *stop, *p;
+    unsigned char      *t;
+    struct comment     *comment = compeek();
+    KTEXT_ST           tkt;
+    AUTH_DAT           ad;
+    int                        rc, i;
+
+    switch ( markline( &start, &stop, in )) {
+    case 0 :
+       return( 0 );
+
+    case -1 :
+       return( CH_MORE );
+    }
+
+    p = start + strlen( comment->c_begin );
+    while ( *p == ' ' ) {
+       p++;
+    }
+
+    bzero( &tkt, sizeof( tkt ));
+    for ( i = 0, t = tkt.dat; p < stop; p += 2, t++, i++ ) {
+       *t = ( h2b( (unsigned char)*p ) << 4 ) +
+               h2b( (unsigned char)*( p + 1 ));
+    }
+    tkt.length = i;
+
+    if (( rc = krb_rd_req( &tkt, "LaserWriter", printer->p_name,
+           0, &ad, "" )) != RD_AP_OK ) {
+       syslog( LOG_ERR, "cq_k4login: %s", krb_err_txt[ rc ] );
+       APPEND( out, LoginFailed, strlen( LoginFailed ));
+       compop();
+       consumetomark( start, stop, in );
+       return( CH_DONE );
+    }
+    syslog( LOG_INFO, "cq_k4login: %s.%s@%s", ad.pname, ad.pinst,
+           ad.prealm );
+    lp_person( ad.pname );
+    lp_host( ad.prealm );
+
+    APPEND( out, LoginOK, strlen( LoginOK ));
+    compop();
+    consumetomark( start, stop, in );
+    return( CH_DONE );
+}
+
+char   *uameth = "UMICHKerberosIV\n*\n";
+
+cq_uameth( in, out )
+    struct papfile     *in, *out;
+{
+    char               *start, *stop;
+    struct comment     *c, *comment = compeek();
+
+    for (;;) {
+       switch ( markline( &start, &stop, in )) {
+       case 0 :
+           return( 0 );
+
+       case -1 :
+           return( CH_MORE );
+       }
+
+       if ( comgetflags() == 0 ) {     /* start */
+           if (( printer->p_flags & P_AUTH ) == 0 ) {  /* no kerberos */
+               if ( comswitch( queries, cq_default ) < 0 ) {
+                   syslog( LOG_ERR, "cq_uameth: can't find default!" );
+                   exit( 1 );
+               }
+               return( CH_DONE );
+           }
+           comsetflags( 1 );
+       } else {
+           if ( comcmp( start, stop, comment->c_end, 0 ) == 0 ) { /* end */
+               APPEND( out, uameth, strlen( uameth ));
+               compop();
+               return( CH_DONE );
+           }
+       }
+
+       consumetomark( start, stop, in );
+    }
+}
+#endif KRB
+
+gq_true( out )
+    struct papfile     *out;
+{
+    if ( printer->p_flags & P_SPOOLED ) {
+       APPEND( out, "true\n", 5 );
+       return( 0 );
+    } else {
+       return( -1 );
+    }
+}
+
+gq_pagecost( out )
+    struct papfile     *out;
+{
+    char               cost[ 60 ];
+
+    /* check for spooler? XXX */
+    if ( printer->p_pagecost_msg != NULL ) {
+       APPEND( out, printer->p_pagecost_msg,
+               strlen( printer->p_pagecost_msg ));
+    } else if ( printer->p_flags & P_ACCOUNT ) {
+#ifdef ABS_PRINT
+       lp_pagecost();
+#endif ABS_PRINT
+       sprintf( cost, "%d", printer->p_pagecost );
+       APPEND( out, cost, strlen( cost ));
+    } else {
+       return( -1 );
+    }
+    APPEND( out, "\n", 1 );
+    return( 0 );
+}
+
+#ifdef ABS_PRINT
+gq_balance( out )
+    struct papfile     *out;
+{
+    char               balance[ 60 ];
+
+    if ( lp_pagecost() != 0 ) {
+       return( -1 );
+    }
+    sprintf( balance, "$%1.2f\n", printer->p_balance );
+    APPEND( out, balance, strlen( balance ));
+    return( 0 );
+}
+#endif ABS_PRINT
+
+struct genquery {
+    char       *gq_name;
+    int                (*gq_handler)();
+} genqueries[] = {
+    { "UMICHCostPerPage", gq_pagecost },
+#ifdef notdef
+    { "UMICHUserBalance", gq_balance },
+#endif 
+    { "UMICHListQueue", gq_true },
+    { "UMICHDeleteJob", gq_true },
+    { NULL },
+};
+
+cq_query( in, out )
+    struct papfile     *in, *out;
+{
+    char               *start, *stop, *p, *q;
+    struct comment     *comment = compeek();
+    struct genquery    *gq;
+
+
+    for (;;) {
+       switch ( markline( &start, &stop, in )) {
+       case 0 :
+           return( 0 );
+
+       case -1 :
+           return( CH_MORE );
+       }
+
+       if ( comgetflags() == 0 ) {     /* started */
+           comsetflags( 1 );
+
+           for ( p = start; p < stop; p++ ) {
+               if ( *p == ':' ) {
+                   break;
+               }
+           }
+
+           for ( p++; p < stop; p++ ) {
+               if ( *p != ' ' && *p != '\t' ) {
+                   break;
+               }
+           }
+
+           for ( q = p; q < stop; q++ ) {
+               if ( *q == ' ' || *q == '\t' || *q == '\r' || *q == '\n' ) {
+                   break;
+               }
+           }
+
+           for ( gq = genqueries; gq->gq_name; gq++ ) {
+               if (( strlen( gq->gq_name ) == q - p ) &&
+                       ( strncmp( gq->gq_name, p, q - p ) == 0 )) {
+                   break;
+               }
+           }
+           if ( gq->gq_name == NULL || gq->gq_handler == NULL ||
+                   (gq->gq_handler)( out ) < 0 ) {
+               if ( comswitch( queries, cq_default ) < 0 ) {
+                   syslog( LOG_ERR, "cq_feature: can't find default!" );
+                   exit( 1 );
+               }
+               return( CH_DONE );
+           }
+       } else {
+           if ( comcmp( start, stop, comment->c_end, 0 ) == 0 ) {
+               compop();
+               consumetomark( start, stop, in );
+               return( CH_DONE );
+           }
+       }
+
+       consumetomark( start, stop, in );
+    }
+}
+
+cq_font_answer( start, stop, out )
+    char               *start, *stop;
+    struct papfile     *out;
+{
+    char               *p, *q, buf[ 256 ];
+    struct ppd_font    *pfo;
+
+    p = start;
+    while ( p < stop ) {
+       while (( *p == ' ' || *p == '\t' ) && p < stop ) {
+           p++;
+       }
+
+       q = buf;
+       while ( *p != ' ' && *p != '\t' &&
+               *p != '\n' && *p != '\r' && p < stop ) {
+           *q++ = *p++;
+       }
+
+       if ( q != buf ) {
+           *q = '\0';
+
+           APPEND( out, "/", 1 );
+           APPEND( out, buf, strlen( buf ));
+           APPEND( out, ":", 1 );
+
+           if (( pfo = ppd_font( buf )) == NULL ) {
+               APPEND( out, "No\n", 3 );
+           } else {
+               APPEND( out, "Yes\n", 4 );
+           }
+       }
+    }
+
+    return;
+}
+
+cq_font( in, out )
+    struct papfile     *in, *out;
+{
+    char               *start, *stop, *p;
+    struct comment     *comment = compeek();
+
+    for (;;) {
+       switch ( markline( &start, &stop, in )) {
+       case 0 :
+           return( 0 );
+
+       case -1 :
+           return( CH_MORE );
+       }
+
+       if ( comgetflags() == 0 ) {
+           comsetflags( 1 );
+
+           for ( p = start; p < stop; p++ ) {
+               if ( *p == ':' ) {
+                   break;
+               }
+           }
+           p++;
+
+           cq_font_answer( p, stop, out );
+       } else {
+           if ( comgetflags() == 1 &&
+                   comcmp( start, stop, comcont, 0 ) == 0 ) {
+               /* continuation */
+
+               for ( p = start; p < stop; p++ ) {
+                   if ( *p == ' ' ) {
+                       break;
+                   }
+               }
+               p++;
+
+               cq_font_answer( p, stop, out );
+           } else {
+               comsetflags( 2 );
+               if ( comcmp( start, stop, comment->c_end, 0 ) == 0 ) {
+                   APPEND( out, "*\n", 2 );
+                   compop();
+                   consumetomark( start, stop, in );
+                   return( CH_DONE );
+               }
+           }
+       }
+
+       consumetomark( start, stop, in );
+    }
+}
+
+cq_feature( in, out )
+    struct papfile     *in, *out;
+{
+    char               *start, *stop, *p;
+    struct comment     *comment = compeek();
+    struct ppd_feature *pfe;
+
+    for (;;) {
+       switch ( markline( &start, &stop, in )) {
+       case 0 :
+           return( 0 );
+
+       case -1 :
+           return( CH_MORE );
+       }
+
+       if ( comgetflags() == 0 ) {
+           comsetflags( 1 );
+
+           /* parse for feature */
+           for ( p = start; p < stop; p++ ) {
+               if ( *p == ':' ) {
+                   break;
+               }
+           }
+           p++;
+           while ( *p == ' ' ) {
+               p++;
+           }
+
+           if (( pfe = ppd_feature( p, stop - p )) == NULL ) {
+               if ( comswitch( queries, cq_default ) < 0 ) {
+                   syslog( LOG_ERR, "cq_feature: can't find default!" );
+                   exit( 1 );
+               }
+               return( CH_DONE );
+           }
+
+           APPEND( out, pfe->pd_value, strlen( pfe->pd_value ));
+           APPEND( out, "\r", 1 );
+       } else {
+           if ( comcmp( start, stop, comment->c_end, 0 ) == 0 ) {
+               compop();
+               consumetomark( start, stop, in );
+               return( CH_DONE );
+           }
+       }
+
+       consumetomark( start, stop, in );
+    }
+}
+
+static const char      *psver = "*PSVersion\n";
+static const char      *prod = "*Product\n";
+
+cq_printer( in, out )
+    struct papfile     *in, *out;
+{
+    char               *start, *stop, *p;
+    struct comment     *c, *comment = compeek();
+    struct ppd_feature *pdpsver, *pdprod;
+
+    for (;;) {
+       switch ( markline( &start, &stop, in )) {
+       case 0 :
+           return( 0 );
+
+       case -1 :
+           return( CH_MORE );
+       }
+
+       if ( comgetflags() == 0 ) {
+           comsetflags( 1 );
+
+           if (( pdpsver = ppd_feature( psver, strlen( psver ))) == NULL ) {
+               if ( comswitch( queries, cq_default ) < 0 ) {
+                   syslog( LOG_ERR, "cq_printer: can't find default!" );
+                   exit( 1 );
+               }
+               return( CH_DONE );
+           }
+
+           for ( p = pdpsver->pd_value; *p != '\0'; p++ ) {
+               if ( *p == ' ' ) {
+                   break;
+               }
+           }
+           if ( *p == '\0' ) {
+               syslog( LOG_ERR, "cq_printer: can't parse PSVersion!" );
+               if ( comswitch( queries, cq_default ) < 0 ) {
+                   syslog( LOG_ERR, "cq_printer: can't find default!" );
+                   exit( 1 );
+               }
+               return( CH_DONE );
+           }
+
+           if (( pdprod = ppd_feature( prod, strlen( prod ))) == NULL ) {
+               if ( comswitch( queries, cq_default ) < 0 ) {
+                   syslog( LOG_ERR, "cq_printer: can't find default!" );
+                   exit( 1 );
+               }
+               return( CH_DONE );
+           }
+
+           /* revision */
+           APPEND( out, p + 1, strlen( p + 1 ));
+           APPEND( out, "\r", 1 );
+
+           /* version */
+           APPEND( out, pdpsver->pd_value, p - pdpsver->pd_value );
+           APPEND( out, "\r", 1 );
+
+           /* product */
+           APPEND( out, pdprod->pd_value, strlen( pdprod->pd_value ));
+           APPEND( out, "\r", 1 );
+       } else {
+           if ( comcmp( start, stop, comment->c_end, 0 ) == 0 ) {
+               compop();
+               consumetomark( start, stop, in );
+               return( CH_DONE );
+           }
+       }
+
+       consumetomark( start, stop, in );
+    }
+}
+
+static const char      *rmjobfailed = "Failed\n";
+static const char      *rmjobok = "Ok\n";
+
+cq_rmjob( in, out )
+    struct papfile     *in, *out;
+{
+    char               *start, *stop, *p;
+    int                        job;
+
+    switch ( markline( &start, &stop, in )) {
+    case 0 :
+       return( 0 );
+
+    case -1 :
+       return( CH_MORE );
+    }
+
+    for ( p = start; p < stop; p++ ) {
+       if ( *p == ' ' || *p == '\t' ) {
+           break;
+       }
+    }
+    for ( ; p < stop; p++ ) {
+       if ( *p != ' ' && *p != '\t' ) {
+           break;
+       }
+    }
+
+    *stop = '\0';
+    if ( p < stop && ( job = atoi( p )) > 0 ) {
+       lp_rmjob( job );
+       APPEND( out, rmjobok, strlen( rmjobok ));
+    } else {
+       APPEND( out, rmjobfailed, strlen( rmjobfailed ));
+    }
+
+    compop();
+    consumetomark( start, stop, in );
+    return( CH_DONE );
+}
+
+cq_listq( in, out )
+    struct papfile     *in, *out;
+{
+    char               *start, *stop;
+
+    switch ( markline( &start, &stop, in )) {
+    case 0 :
+       return( 0 );
+
+    case -1 :
+       return( CH_MORE );
+    }
+
+    if ( lp_queue( out )) {
+       syslog( LOG_ERR, "cq_listq: lp_queue failed" );
+    }
+
+    compop();
+    consumetomark( start, stop, in );
+    return( CH_DONE );
+}
+
+/*
+ * All queries start with %%?Begin and end with %%?End.  Note that the
+ * "Begin"/"End" general queries have to be last.
+ */
+struct comment queries[] = {
+#ifdef KRB
+    { "%%Login: UMICHKerberosIV", 0,                   cq_k4login,     0 },
+    { "%%?BeginUAMethodsQuery",        "%%?EndUAMethodsQuery:", cq_uameth, C_FULL },
+#endif KRB
+    { "%UMICHListQueue", 0,                            cq_listq, C_FULL },
+    { "%UMICHDeleteJob", 0,                            cq_rmjob,       0 },
+    { "%%?BeginQuery",         "%%?EndQuery",          cq_query,       0 },
+    { "%%?BeginFeatureQuery",  "%%?EndFeatureQuery",   cq_feature,     0 },
+    { "%%?BeginFontQuery",     "%%?EndFontQuery",      cq_font,        0 },
+    { "%%?BeginPrinterQuery",  "%%?EndPrinterQuery",   cq_printer, C_FULL },
+    { "%%?Begin",              "%%?End",               cq_default,     0 },
+    { 0 },
+};
diff --git a/etc/papd/session.c b/etc/papd/session.c
new file mode 100644 (file)
index 0000000..c58ac9a
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/syslog.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <atalk/atp.h>
+#include <atalk/pap.h>
+
+#include "file.h"
+
+extern unsigned char   connid, quantum, oquantum;
+
+char           buf[ PAP_MAXQUANTUM ][ 4 + PAP_MAXDATA ];
+struct iovec   niov[ PAP_MAXQUANTUM ] = {
+    { buf[ 0 ],        0 },
+    { buf[ 1 ],        0 },
+    { buf[ 2 ],        0 },
+    { buf[ 3 ],        0 },
+    { buf[ 4 ],        0 },
+    { buf[ 5 ],        0 },
+    { buf[ 6 ],        0 },
+    { buf[ 7 ],        0 },
+};
+struct iovec   iov[ PAP_MAXQUANTUM ] = {
+    { buf[ 0 ] + 4,    0 },
+    { buf[ 1 ] + 4,    0 },
+    { buf[ 2 ] + 4,    0 },
+    { buf[ 3 ] + 4,    0 },
+    { buf[ 4 ] + 4,    0 },
+    { buf[ 5 ] + 4,    0 },
+    { buf[ 6 ] + 4,    0 },
+    { buf[ 7 ] + 4,    0 },
+};
+
+/*
+ * Accept files until the client closes the connection.
+ * Read lines of a file, until the client sends eof, after
+ * which we'll send eof also.
+ */
+session( atp, sat )
+    ATP                        atp;
+    struct sockaddr_at *sat;
+{
+    struct timeval     tv;
+    struct atp_block   atpb;
+    struct sockaddr_at ssat;
+    struct papfile     infile, outfile;
+    fd_set             fds;
+    char               cbuf[ 578 ];
+    int                        i, cc, timeout = 0, readpending = 0;
+    u_int16_t          seq = 0, rseq = 1, netseq;
+    u_char             readport;
+
+    infile.pf_state = PF_BOT;
+    infile.pf_len = 0;
+    infile.pf_buf = 0;
+    infile.pf_cur = 0;
+    infile.pf_end = 0;
+
+    outfile.pf_state = PF_BOT;
+    outfile.pf_len = 0;
+    outfile.pf_buf = 0;
+    outfile.pf_cur = 0;
+    outfile.pf_end = 0;
+
+    /*
+     * Ask for data.
+     */
+    cbuf[ 0 ] = connid;
+    cbuf[ 1 ] = PAP_READ;
+    if (++seq == 0) seq = 1;
+    netseq = htons( seq );
+    bcopy( &netseq, &cbuf[ 2 ], sizeof( netseq ));
+    atpb.atp_saddr = sat;
+    atpb.atp_sreqdata = cbuf;
+    atpb.atp_sreqdlen = 4;             /* bytes in SendData request */
+    atpb.atp_sreqto = 5;               /* retry timer */
+    atpb.atp_sreqtries = -1;           /* infinite retries */
+    if ( atp_sreq( atp, &atpb, oquantum, ATP_XO )) {
+       syslog( LOG_ERR, "atp_sreq: %m" );
+       exit( 1 );
+    }
+
+    for (;;) {
+       /*
+        * Time between tickles.
+        */
+       tv.tv_sec = 60;
+       tv.tv_usec = 0;
+
+       /*
+        * If we don't get anything for a while, time out.
+        */
+       FD_ZERO( &fds );
+       FD_SET( atp_fileno( atp ), &fds );
+
+       if (( cc = select( FD_SETSIZE, &fds, 0, 0, &tv )) < 0 ) {
+           syslog( LOG_ERR, "select: %m" );
+           exit( 1 );
+       }
+       if ( cc == 0 ) {
+           if ( timeout++ > 2 ) {
+               syslog( LOG_ERR, "connection timed out" );
+               lp_cancel();
+               exit( 1 );
+           }
+
+           /*
+            * Send a tickle.
+            */
+           cbuf[ 0 ] = connid;
+           cbuf[ 1 ] = PAP_TICKLE;
+           cbuf[ 2 ] = cbuf[ 3 ] = 0;
+           atpb.atp_saddr = sat;
+           atpb.atp_sreqdata = cbuf;
+           atpb.atp_sreqdlen = 4;              /* bytes in Tickle request */
+           atpb.atp_sreqto = 0;                /* best effort */
+           atpb.atp_sreqtries = 1;             /* try once */
+           if ( atp_sreq( atp, &atpb, 0, 0 )) {
+               syslog( LOG_ERR, "atp_sreq: %m" );
+               exit( 1 );
+           }
+           continue;
+       } else {
+           timeout = 0;
+       }
+
+       bzero( &ssat, sizeof( struct sockaddr_at ));
+       switch( atp_rsel( atp, &ssat, ATP_TRESP | ATP_TREQ )) {
+       case ATP_TREQ :
+           atpb.atp_saddr = &ssat;
+           atpb.atp_rreqdata = cbuf;
+           atpb.atp_rreqdlen = sizeof( cbuf );
+           if ( atp_rreq( atp, &atpb ) < 0 ) {
+               syslog( LOG_ERR, "atp_rreq: %m" );
+               exit( 1 );
+           }
+           /* sanity */
+           if ( (unsigned char)cbuf[ 0 ] != connid ) {
+               syslog( LOG_ERR, "Bad ATP request!" );
+               continue;
+           }
+
+           switch( cbuf[ 1 ] ) {
+           case PAP_READ :
+               /*
+                * Other side is ready for some data.
+                */
+               bcopy( &cbuf[ 2 ], &netseq, sizeof( netseq ));
+               if ( netseq != 0 ) {
+                   if ( rseq != ntohs( netseq )) {
+                       break;
+                   }
+                   if ( rseq++ == 0xffff ) rseq = 1;
+               }
+               readpending = 1;
+               readport = ssat.sat_port;
+               break;
+
+           case PAP_CLOSE :
+               /*
+                * Respond to the close request.
+                * If we're in the middle of a file, clean up.
+                */
+               if (( infile.pf_state & PF_BOT ) ||
+                       ( PF_BUFSIZ( &infile ) == 0 &&
+                       ( infile.pf_state & PF_EOF ))) {
+                   lp_print();
+               } else {
+                   lp_cancel();
+               }
+
+               niov[ 0 ].iov_len = 4;
+               ((char *)niov[ 0 ].iov_base)[ 0 ] = connid;
+               ((char *)niov[ 0 ].iov_base)[ 1 ] = PAP_CLOSEREPLY;
+               ((char *)niov[ 0 ].iov_base)[ 2 ] =
+                       ((char *)niov[ 0 ].iov_base)[ 3 ] = 0;
+               atpb.atp_sresiov = niov;
+               atpb.atp_sresiovcnt = 1;
+               if ( atp_sresp( atp, &atpb ) < 0 ) {
+                   syslog( LOG_ERR, "atp_sresp: %m" );
+                   exit( 1 );
+               }
+               exit( 0 );
+               break;
+
+           case PAP_TICKLE :
+               break;
+           default :
+               syslog( LOG_ERR, "Bad PAP request!" );
+           }
+
+           break;
+
+       case ATP_TRESP :
+           atpb.atp_saddr = &ssat;
+           for ( i = 0; i < oquantum; i++ ) {
+               niov[ i ].iov_len = PAP_MAXDATA + 4;
+           }
+           atpb.atp_rresiov = niov;
+           atpb.atp_rresiovcnt = oquantum;
+           if ( atp_rresp( atp, &atpb ) < 0 ) {
+               syslog( LOG_ERR, "atp_rresp: %m" );
+               exit( 1 );
+           }
+
+           /* sanity */
+           if ( ((unsigned char *)niov[ 0 ].iov_base)[ 0 ] != connid ||
+                   ((char *)niov[ 0 ].iov_base)[ 1 ] != PAP_DATA ) {
+               syslog( LOG_ERR, "Bad data response!" );
+               continue;
+           }
+
+           for ( i = 0; i < atpb.atp_rresiovcnt; i++ ) {
+               APPEND( &infile,
+                       niov[ i ].iov_base + 4, niov[ i ].iov_len - 4 );
+               if (( infile.pf_state & PF_EOF ) == 0 &&
+                       ((char *)niov[ 0 ].iov_base)[ 2 ] ) {
+                   infile.pf_state |= PF_EOF;
+               }
+           }
+
+           /* move data */
+           if ( ps( &infile, &outfile ) < 0 ) {
+               syslog( LOG_ERR, "parse: bad return" );
+               exit( 1 );      /* really?  close? */
+           }
+
+           /*
+            * Ask for more data.
+            */
+           cbuf[ 0 ] = connid;
+           cbuf[ 1 ] = PAP_READ;
+           if ( ++seq == 0 ) seq = 1;
+           netseq = htons( seq );
+           bcopy( &netseq, &cbuf[ 2 ], sizeof( netseq ));
+           atpb.atp_saddr = sat;
+           atpb.atp_sreqdata = cbuf;
+           atpb.atp_sreqdlen = 4;              /* bytes in SendData request */
+           atpb.atp_sreqto = 5;                /* retry timer */
+           atpb.atp_sreqtries = -1;            /* infinite retries */
+           if ( atp_sreq( atp, &atpb, oquantum, ATP_XO )) {
+               syslog( LOG_ERR, "atp_sreq: %m" );
+               exit( 1 );
+           }
+           break;
+
+       case 0:
+           break;
+
+       default :
+           syslog( LOG_ERR, "atp_rsel: %m" );
+           exit( 1 );
+       }
+
+       /* send any data that we have */
+       if ( readpending &&
+               ( PF_BUFSIZ( &outfile ) || ( outfile.pf_state & PF_EOF ))) {
+           for ( i = 0; i < quantum; i++ ) {
+               ((char *)niov[ i ].iov_base)[ 0 ] = connid;
+               ((char *)niov[ i ].iov_base)[ 1 ] = PAP_DATA;
+               ((char *)niov[ i ].iov_base)[ 2 ] =
+                       ((char *)niov[ i ].iov_base)[ 3 ] = 0;
+
+               if ( PF_BUFSIZ( &outfile ) > PAP_MAXDATA  ) {
+                   cc = PAP_MAXDATA;
+               } else {
+                   cc = PF_BUFSIZ( &outfile );
+                   if ( outfile.pf_state & PF_EOF ) {
+                       ((char *)niov[ 0 ].iov_base)[ 2 ] = 1;  /* eof */
+                       outfile.pf_state = PF_BOT;
+                       infile.pf_state = PF_BOT;
+                   }
+               }
+
+               niov[ i ].iov_len = 4 + cc;
+               bcopy( outfile.pf_cur, niov[ i ].iov_base + 4, cc );
+               CONSUME( &outfile, cc );
+               if ( PF_BUFSIZ( &outfile ) == 0 ) {
+                   i++;
+                   break;
+               }
+           }
+           ssat.sat_port = readport;
+           atpb.atp_saddr = &ssat;
+           atpb.atp_sresiov = niov;
+           atpb.atp_sresiovcnt = i;    /* reported by stevebn@pc1.eos.co.uk */
+           if ( atp_sresp( atp, &atpb ) < 0 ) {
+               syslog( LOG_ERR, "atp_sresp: %m" );
+               exit( 1 );
+           }
+           readpending = 0;
+       }
+    }
+}
diff --git a/etc/psf/Makefile b/etc/psf/Makefile
new file mode 100644 (file)
index 0000000..e666e5e
--- /dev/null
@@ -0,0 +1,72 @@
+TARGETS=       psf psa
+SRC=   psf.c psa.c
+
+INCPATH=       -I../../include
+CFLAGS=        ${DEFS} ${OPTOPTS} ${INCPATH} -DZEROWIDTH
+LIBS=  ${ADDLIBS}
+TAGSFILE=      tags
+CC=    cc
+INSTALL=       install
+LIBDIRS=
+
+LINKS= ofpap ifpap tfpap ifpaprev tfpaprev \
+       ofwpap ifwpap tfwpap ifwpaprev tfwpaprev \
+       ofmpap ifmpap tfmpap ifmpaprev tfmpaprev \
+       ofwmpap ifwmpap tfwmpap ifwmpaprev tfwmpaprev
+
+all : ${TARGETS} ${LINKS}
+
+${LINKS} :
+       -ln -sf ${SBINDIR}/psf $@
+
+psf : psf.o
+       ${CC} ${CFLAGS} -o psf psf.o ${LIBDIRS} ${LIBS}
+
+psa : psa.o
+       ${CC} ${CFLAGS} -o psa psa.o ${LIBDIRS} ${LIBS}
+
+psf.o : psf.c
+       ${CC} ${CFLAGS} -D_PATH_PAP=\"${BINDIR}/pap\" \
+           -D_PATH_PSORDER=\"${BINDIR}/psorder\" \
+           -D_PATH_PSA=\"${SBINDIR}/psa\" \
+           -D_PATH_PSFILTER=\"${SBINDIR}/etc2ps\" \
+           -D_PATH_PAGECOUNT=\"${RESDIR}/pagecount.ps\" \
+           ${CPPFLAGS} -c psf.c
+
+install : all
+       -mkdir -p ${RESDIR}/filters
+       ${INSTALL} -c psa ${SBINDIR}
+       ${INSTALL} -c etc2ps.sh ${SBINDIR}/etc2ps
+       ${INSTALL} -c pagecount.ps ${RESDIR}
+       ${INSTALL} -c psf ${SBINDIR}
+       tar cBf - ${LINKS} | (cd ${RESDIR}/filters; tar xBf -)
+
+clean :
+       rm -f a.out core* *.o *.bak *[Ee]rrs tags
+       rm -f ${TARGETS} ${LINKS}
+
+tags : ${SRC}
+       cwd=`pwd`; \
+       for i in ${SRC}; do \
+           ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+       done
+
+depend :
+       for i in ${SRC} ; do \
+           ${CC} -M ${DEFS} ${INCPATH} $$i | \
+           awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+               else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+               else rec = rec " " $$2 } } \
+               END { print rec } ' >> makedep; done
+       sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+       cat makedep >> Makefile.tmp
+       rm makedep
+       echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+       echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+       echo '# see make depend above' >> Makefile.tmp
+       rm -f Makefile.bak
+       cp Makefile Makefile.bak
+       mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
diff --git a/etc/psf/etc2ps.sh b/etc/psf/etc2ps.sh
new file mode 100644 (file)
index 0000000..5c3c60d
--- /dev/null
@@ -0,0 +1,56 @@
+#!/bin/sh
+#
+# This filter is called by psf to convert "other" formats to PostScript.
+# psf handles text and PostScript native.  "Other" formats, e.g. DVI, C/A/T,
+# need to be converted before the page reverser and the printer can use
+# them.
+#
+# $0 begins with the filter name, e.g. df, tf.  Each format is a separate
+# tag in the case.
+#
+
+DVIPSPATH=/usr/local/tex/bin
+DVIPS=/usr/local/tex/bin/dvips
+DVIPSARGS="-f -q"
+
+TROFF2PS=/usr/local/psroff/troff2/troff2ps
+TROFF2PSARGS="-Z -O-.10"
+
+PATH=/usr/bin:$DVIPSPATH; export PATH
+
+case $1 in
+
+#
+# Use "dvips" by Radical Eye Software to convert TeX DVI files to PostScript.
+# Note that you *must* have METAFONT, etc, in your path.
+#
+df*)
+    if [ -x "$DVIPS" ]; then
+       cat > /tmp/psfilter.$$
+       $DVIPS $DVIPSARGS < /tmp/psfilter.$$
+       rm -f /tmp/psfilter.$$
+    else
+       echo "$0: filter dvips uninstalled" 1>&2
+       exit 2
+    fi
+    ;;
+
+#
+# troff2ps is from psroff by Chris Lewis.
+#
+tf*)
+    if [ -x "$TROFF2PS" ]; then
+       exec $TROFF2PS $TROFF2PSARGS
+    else
+       echo "$0: filter troff2ps uninstalled" 1>&2
+       exit 2
+    fi
+    ;;
+
+*)
+    echo "$0: filter $1 unavailable" 1>&2
+    exit 2
+    ;;
+esac
+
+exit 0
diff --git a/etc/psf/pagecount.ps b/etc/psf/pagecount.ps
new file mode 100644 (file)
index 0000000..fe650e1
--- /dev/null
@@ -0,0 +1,5 @@
+%!PS-Adobe-3.0 Query
+%%?BeginQuery: PageCount
+statusdict begin pagecount (*) print = flush end
+%%?EndQuery: Unknown
+%%EOF
diff --git a/etc/psf/psa.c b/etc/psf/psa.c
new file mode 100644 (file)
index 0000000..32675c2
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 1990,1995 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+/*
+ * PostScript Accounting, psa.
+ *
+ * psa is invoked by psf, as output for a communication program.  The
+ * communication program is expected to send a small program before and
+ * after each job, which causes the page count to be emitted in a well
+ * known format.  psa reads its input, looking for page counts and other
+ * interesting data.  Any data that it doesn't understand, it emits to
+ * stderr, the lpd log file.  Data that it does understand may be written
+ * to a status file or logged.  Once all input has been received, psa
+ * subtracts the beginning and end page counts, and log an accounting
+ * record in the accounting file.
+ */
+
+#include <stdio.h>
+
+main( ac, av )
+    int                ac;
+    char       **av;
+{
+    FILE       *af;
+    char       *acc, *user, *host;
+    char       buf[ 1024 ], *p, *end;
+    int                cc, n, ipc = -1, fpc = -1;
+
+    if ( ac != 4 ) {
+       fprintf( stderr, "Usage:\t%s accounting-file user host\n", av[ 0 ] );
+       exit( 2 );
+    }
+
+    acc = av[ 1 ];
+    user = av[ 2 ];
+    host = av[ 3 ];
+
+    /*
+     * Explain n = !n ...  Is there no beauty in truth?
+     */
+    while (( cc = read( 0, buf, sizeof( buf ))) > 0 ) {
+       if ( ipc < 0 && *buf == '*' ) {
+           /* find initial pagecount */
+           for ( p = buf, end = buf + cc; p < end; p++ ) {
+               if ( *p == '\n' || *p == '\r' ) {
+                   break;
+               }
+           }
+           if ( p == end ) {
+               fprintf( stderr, "Can't find initial page count!\n" );
+               exit( 2 );
+           }
+
+           p++;
+           ipc = atoi( buf + 1 );
+           cc -= ( p - buf );
+           if ( cc != 0 ) {
+               bcopy( p, buf, cc );
+           }
+       } else {
+           /* find final pagecount */
+           for ( p = buf + cc - 1; p >= buf; p-- ) {
+               if ( *p != '\n' && *p != '\r' ) {
+                   break;
+               }
+           }
+           if ( p < buf ) {
+               fprintf( stderr, "Can't find final page count!\n" );
+               exit( 2 );
+           }
+
+           for ( ; p >= buf; p-- ) {
+               if ( *p == '\n' || *p == '\r' ) {
+                   break;
+               }
+           }
+
+           if ( p < buf ) {
+               p = buf;
+           } else {
+               cc -= p - buf;
+               p++;
+           }
+
+           if ( *p == '*' ) {
+               n = atoi( p + 1 );
+#define max(x,y)       ((x)>(y)?(x):(y))
+               fpc = max( n, fpc );
+           }
+       }
+       if ( cc != 0 && write( 2, buf, cc ) != cc ) {
+           fprintf( stderr, "write 1: 2 %X %d\n", buf, cc );
+           perror( "write" );
+           exit( 2 );
+       }
+    }
+    if ( cc < 0 ) {
+       perror( "read" );
+       exit( 2 );
+    }
+
+    if ( ipc < 0 ) {
+       fprintf( stderr, "Didn't find initial page count!\n" );
+       exit( 2 );
+    }
+
+    if ( fpc < 0 ) {
+       fprintf( stderr, "Didn't find final page count!\n" );
+       exit( 2 );
+    }
+
+    /*
+     * Write accounting record.
+     */
+    if (( af = fopen( acc, "a" )) != NULL ) {
+       fprintf( af, "%7.2f\t%s:%s\n", (float)( fpc - ipc ), host, user );
+    } else {
+       perror( acc );
+       exit( 2 );
+    }
+
+    exit( 0 );
+}
diff --git a/etc/psf/psf.c b/etc/psf/psf.c
new file mode 100644 (file)
index 0000000..e92c56f
--- /dev/null
@@ -0,0 +1,694 @@
+/*
+ * Copyright (c) 1990,1995 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+/*
+ * PostScript Filter, psf.
+ *
+ * Handles both PostScript files and text files.  Files with the
+ * '%!' PostScript header are sent directly to the printer,
+ * unmodified.  Text files are first converted to PostScript,
+ * then sent.  Printers may be directly attached or on an AppleTalk
+ * network.  Other media are possible.  Currently, psf invokes
+ * pap to send files to AppleTalk-ed printers.  Replace the pap*
+ * variables to use another program for communication.  psf only
+ * converts plain-text.  If called as "tf" or "df", psf will invoke
+ * a troff or dvi to PostScript converter.
+ */
+
+#define FUCKED
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/file.h>
+#include <sys/syslog.h>
+#include <atalk/paths.h>
+#include <stdio.h>
+#include <strings.h>
+#include <ctype.h>
+#include <signal.h>
+
+char           psapath[] = _PATH_PSA;
+char           *psaargv[] = { "psa", 0, 0, 0, 0 };
+
+/*
+ * If we're not doing accounting, we just call pap as below.
+ * If we are doing accounting, we call pap twice.  The first time,
+ * we call it with "-E" in arg 2, pagecount.ps in arg 3, and "-" in
+ * arg 4.  The second time, we call it with "-c" in arg 2, pagecount.ps
+ * in arg 3, and 0 in arg 4.
+ */
+char           pappath[] = _PATH_PAP;
+char           *papargv[] = { "pap", "-sstatus", 0, 0, 0, 0, 0, 0 };
+
+char           revpath[] = _PATH_PSORDER;
+char           *revargv[] = { "psorder", "-d", 0 };
+
+char           *filtargv[] = { 0, 0, 0 };
+
+char           inbuf[ 1024 * 8 ];
+int            inlen;
+
+FILE           *acctfile = NULL;
+int            literal;
+int            width = 80, length = 66, indent = 0;
+char           *prog, *name, *host;
+
+struct papersize {
+    int                width;
+    int                length;
+   float       win;
+    float      lin;
+} papersizes[] = {
+    { 80, 66, 8.5, 11.0 },                     /* US Letter */
+    { 80, 70, 8.27, 11.69 },                   /* A4 */
+};
+
+main( ac, av ) 
+    int                ac;
+    char       **av;
+{
+    int                        c, rc, children = 0;
+#ifdef FUCKED
+    int                        psafileno, multiconn = 0, waitidle = 0, waitidle2 = 0;
+#endif FUCKED
+    int                        status;
+    extern char                *optarg;
+    extern int         optind, opterr;
+
+    opterr = 0;
+    if (( prog = rindex( av[ 0 ], '/' )) == NULL ) {
+       prog = av[ 0 ];
+    } else {
+       prog++;
+    }
+#ifdef ultrix
+    openlog( prog, LOG_PID );
+#else ultrix
+    openlog( prog, LOG_PID, LOG_LPR );
+#endif ultrix
+
+    while (( c = getopt( ac, av, "P:C:D:F:L:J:x:y:n:h:w:l:i:c" )) != EOF ) {
+       switch ( c ) {
+       case 'n' :
+           name = optarg;
+           break;
+
+       case 'h' :
+           host = optarg;
+           break;
+
+       case 'w' :
+           width = atoi( optarg );
+#ifdef ZEROWIDTH
+           /*
+            * Some version of lpd pass 0 for the page width.
+            */
+           if ( width == 0 ) {
+               width = 80;
+           }
+#endif ZEROWIDTH
+           break;
+
+       case 'l' :
+           length = atoi( optarg );
+           break;
+
+       case 'i' :
+           indent = atoi( optarg );
+           break;
+
+       case 'c' :      /* Print control chars */
+           literal++;
+           break;
+
+       case 'F' :
+       case 'L' :
+       case 'J' :
+       case 'P' :
+       case 'x' :
+       case 'y' :
+           break;
+       
+#ifdef notdef
+       default :
+           syslog( LOG_ERR, "bad option: %c", c );
+           exit( 2 );
+#endif notdef
+       }
+    }
+    if ( ac - optind > 1 ) {
+       syslog( LOG_ERR, "Too many arguments" );
+       exit( 2 );
+    }
+#ifdef FUCKED
+    if ( index( prog, 'w' )) {
+       waitidle++;
+    }
+    if ( index( prog, 'W' )) {
+       waitidle2++;
+    }
+    if ( index( prog, 'm' )) {
+       multiconn++;
+    }
+#endif FUCKED
+
+    syslog( LOG_INFO, "starting for %s", name ? name : "?" );
+
+restart:
+    if (( inlen = read( 0, inbuf, sizeof( inbuf ))) < 0 ) {
+       syslog( LOG_ERR, "read: %m" );
+       exit( 1 );
+    }
+    if ( inlen == 0 ) {        /* nothing to be done */
+       syslog( LOG_INFO, "done" );
+       exit( 0 );
+    }
+
+    /*
+     * If we've been given an accounting file, start the accounting
+     * process.
+     */
+    if ( optind < ac ) {
+       /* build arguments */
+       psaargv[ 1 ] = av[ optind ];
+       psaargv[ 2 ] = name;
+       psaargv[ 3 ] = host;
+       if (( c = pexecv( psapath, psaargv )) < 0 ) {
+           syslog( LOG_ERR, "%s: %m", psapath );
+           exit( 2 );
+       }
+       children++;
+       syslog( LOG_INFO, "accounting with psa[%d]", c );
+    }
+
+    /*
+     * Check prog's name to decide what programs to execute.
+     */
+    if ( strstr( prog, "pap" ) != NULL ) {
+       if ( optind < ac ) {    /* accounting */
+#ifdef FUCKED
+           if ( multiconn ) {
+               psafileno = getdtablesize();
+               psafileno--;
+               dup2( 1, psafileno );
+
+               if ( waitidle2 ) {
+                   papargv[ 2 ] = "-W";
+                   papargv[ 3 ] = "-c";
+                   papargv[ 4 ] = "-E";
+                   papargv[ 5 ] = _PATH_PAGECOUNT;
+                   papargv[ 6 ] = "-";
+                   papargv[ 7 ] = 0;
+               } else if ( waitidle ) {
+                   papargv[ 2 ] = "-w";
+                   papargv[ 3 ] = "-c";
+                   papargv[ 4 ] = "-E";
+                   papargv[ 5 ] = _PATH_PAGECOUNT;
+                   papargv[ 6 ] = "-";
+                   papargv[ 7 ] = 0;
+               } else {
+                   papargv[ 2 ] = "-c";
+                   papargv[ 3 ] = "-E";
+                   papargv[ 4 ] = _PATH_PAGECOUNT;
+                   papargv[ 5 ] = "-";
+                   papargv[ 6 ] = 0;
+               }
+           } else {
+               /*
+                * This is how it should be done.
+                */
+               papargv[ 2 ] = "-c";
+               papargv[ 3 ] = _PATH_PAGECOUNT;
+               papargv[ 4 ] = "-";
+               papargv[ 5 ] = _PATH_PAGECOUNT;
+               papargv[ 6 ] = 0;
+           }
+#endif FUCKED
+       } else {
+           papargv[ 2 ] = "-c";
+           papargv[ 3 ] = "-E";
+           papargv[ 4 ] = 0;
+       }
+
+       if (( c = pexecv( pappath, papargv )) < 0 ) {
+           syslog( LOG_ERR, "%s: %m", pappath );
+           exit( 2 );
+       }
+       children++;
+       syslog( LOG_INFO, "sending to pap[%d]", c );
+    }
+
+    /*
+     * Might be a good idea to have both a "forw" and a "rev", so that
+     * reversed documents can be reordered for the printing device.
+     */
+    if ( strstr( prog, "rev" ) != NULL ) {
+       if (( c = pexecv( revpath, revargv )) < 0 ) {
+           syslog( LOG_ERR, "%s: %m", revpath );
+           exit( 2 );
+       }
+       syslog( LOG_INFO, "sending to rev[%d]", c );
+       children++;
+    }
+
+    /*
+     * Invoke an external (script) filter to produce PostScript from
+     * non-text input.
+     */
+    if ( *prog != 'i' && *prog != 'o' && *( prog + 1 ) == 'f' ) {
+       filtargv[ 0 ] = filtargv[ 1 ] = prog;
+       if (( c = pexecv( _PATH_PSFILTER, filtargv )) < 0 ) {
+           syslog( LOG_ERR, "%s: %m", _PATH_PSFILTER );
+           exit( 2 );
+       }
+       syslog( LOG_INFO, "external filter[%d]", c );
+       children++;
+       rc = copyio();          /* external filter */
+    } else {
+       if ( inlen >= 2 && inbuf[ 0 ] == '%' && inbuf[ 1 ] == '!' ) {
+           syslog( LOG_INFO, "PostScript" );
+           rc = copyio();      /* PostScript */
+       } else if ( inlen >= 2 && inbuf[ 0 ] == '\033' && inbuf[ 1 ] == '%' ) {
+           syslog( LOG_INFO, "PostScript w/PJL" );
+           rc = copyio();      /* PostScript */
+       } else {
+           syslog( LOG_INFO, "straight text" );
+           rc = textps();      /* straight text */
+       }
+    }
+
+#ifdef FUCKED
+    if ( strstr( prog, "pap" ) != NULL && optind < ac && multiconn ) {
+       dup2( psafileno, 1 );
+       close( psafileno );
+       papargv[ 2 ] = "-c";
+       if ( waitidle2 ) {
+           papargv[ 3 ] = "-W";
+           papargv[ 4 ] = _PATH_PAGECOUNT;
+           papargv[ 5 ] = 0;
+       } else if ( waitidle ) {
+           papargv[ 3 ] = "-w";
+           papargv[ 4 ] = _PATH_PAGECOUNT;
+           papargv[ 5 ] = 0;
+       } else {
+           papargv[ 3 ] = _PATH_PAGECOUNT;
+           papargv[ 4 ] = 0;
+       }
+
+       if (( c = pexecv( pappath, papargv )) < 0 ) {
+           syslog( LOG_ERR, "%s: %m", pappath );
+           exit( 2 );
+       }
+       children++;
+       syslog( LOG_INFO, "pagecount with pap[%d]", c );
+    }
+#endif FUCKED
+
+    if ( children ) {
+       close( 1 );
+    }
+    while ( children ) {
+       if (( c = wait3( &status, 0, 0 )) < 0 ) {
+           syslog( LOG_ERR, "wait3: %m" );
+           exit( 1 );
+       }
+       if ( WIFEXITED( status )) {
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(x) ((x).w_status)
+#endif WEXITSTATUS
+           if ( WEXITSTATUS( status ) != 0 ) {
+               syslog( LOG_ERR, "%d died with %d", c, WEXITSTATUS( status ));
+               exit( WEXITSTATUS( status ));
+           } else {
+               syslog( LOG_INFO, "%d done", c );
+               children--;
+           }
+       } else {
+           syslog( LOG_ERR, "%d died badly", c );
+           exit( 1 );
+       }
+    }
+
+    if ( rc == 3 ) {
+       syslog( LOG_INFO, "pausing" );
+       kill( getpid(), SIGSTOP );
+       syslog( LOG_INFO, "restarting" );
+       goto restart;
+    }
+
+    syslog( LOG_INFO, "done" );
+    exit( rc );
+}
+
+copyio()
+{
+    /* implement the FSM needed to do the suspend. Note that
+     * the last characters will be \031\001 so don't worry
+     * Fun things: 1. \031\001 should not be written to output device
+     *  2. The \031 can be last char of one read, \001 first of next
+     *      - we need to write \031 if not followed by \001
+     */
+    struct timeval     tv;
+    fd_set             fdset;
+    int                        ctl = 0, i;
+
+notdone:
+    do {
+       /*
+        * First, \031 and \001 *must* be the last things in the buffer
+        * (\001 can be the first thing in the next buffer).  There's no
+        * need to scan any of the intervening bytes.  Second, if there's
+        * more input, the escape sequence was bogus, and we should keep
+        * reading.
+        */
+       if ( inlen == 1 ) {
+           if ( ctl == 1 ) {
+               if ( inbuf[ 0 ] == '\001' ) {
+                   ctl = 2;
+                   break;
+               }
+               if ( write( 1, "\031", 1 ) != 1 ) {
+                   syslog( LOG_ERR, "write: %m" );
+                   return( 1 );
+               }
+               ctl = 0;
+           }
+
+           if ( inbuf[ 0 ] == '\031' ) {
+               ctl = 1;
+           }
+
+       } else {
+           if ( ctl == 1 ) {
+               if ( write( 1, "\031", 1 ) != 1 ) {
+                   syslog( LOG_ERR, "write: %m" );
+                   return( 1 );
+               }
+           }
+           ctl = 0;
+           if ( inbuf[ inlen - 2 ] == '\031' &&
+                   inbuf[ inlen - 1 ] == '\001' ) {
+               ctl = 2;
+           } else if ( inbuf[ inlen - 1 ] == '\031' ) {
+               ctl = 1;
+           }
+       }
+
+       inlen -= ctl;
+       if (( inlen > 0 ) && ( write( 1, inbuf, inlen ) != inlen )) {
+           syslog( LOG_ERR, "write: %m" );
+           return( 1 );
+       }
+       if ( ctl == 2 ) {
+           break;
+       }
+    } while (( inlen = read( 0, inbuf, sizeof( inbuf ))) > 0 );
+
+    if ( ctl == 2 ) {
+       tv.tv_sec = 2;
+       tv.tv_usec = 0;
+       FD_ZERO( &fdset );
+       FD_SET( 0, &fdset );
+       if ( select( 1, &fdset, NULL, NULL, &tv ) != 0 ) {
+           if (( inlen = read( 0, inbuf, sizeof( inbuf ))) > 0 ) {
+               goto notdone;
+           }
+       }
+    }
+
+    if ( inlen < 0 ) {
+       syslog( LOG_ERR, "read: %m" );
+       return( 1 );
+    }
+
+    if ( ctl == 1 ) {
+       if ( write( 1, "\031", 1 ) != 1 ) {
+           syslog( LOG_ERR, "write: %m" );
+           return( 1 );
+       }
+    } else if ( ctl == 2 ) {
+       return( 3 );
+    }
+    return( 0 );
+}
+
+char           *font = "Courier";
+int            point = 11;
+
+char           pspro[] = "\
+/GSV save def                                          % global VM\n\
+/SP {\n\
+       /SV save def                                    % save vmstate\n\
+       dup /H exch def                                 % save font height\n\
+       exch findfont exch scalefont setfont            % select font\n\
+       ( ) stringwidth pop /W exch def                 % save font width\n\
+       0.5 sub 72 mul /CY exch def                     % save start Y\n\
+       pop 0.5 add 72 mul /CX exch def                 % save start X\n\
+       CX CY moveto                                    % make current point\n\
+} bind def\n\
+/S /show load def\n\
+/NL { CX CY H sub dup /CY exch def moveto } bind def\n\
+/CR { CX CY moveto } bind def\n\
+/B { W neg 0 rmoveto}bind def\n\
+/T { W mul 0 rmoveto}bind def\n\
+/EP { SV restore showpage } bind def\n\
+%%EndProlog\n";
+
+textps()
+{
+    struct papersize   papersize;
+    int                        state = 0, line = 0, col = 0, npages = 0, rc, i;
+    char               *p, *end;
+
+#define elements(x)    (sizeof(x)/sizeof((x)[0]))
+    for ( i = 0; i < elements( papersizes ); i++ ) {
+       if ( width == papersizes[ 0 ].width &&
+               length == papersizes[ 0 ].length ) {
+           papersize = papersizes[ i ];
+           break;
+       }
+    }
+    if ( i >= elements( papersizes )) {
+       papersize = papersizes[ 0 ];            /* default */
+    }
+
+#define ST_AVAIL               (1<<0)
+#define ST_CONTROL             (1<<1)
+#define ST_PAGE                        (1<<2)
+    /*
+     * convert text lines to postscript.
+     * A grungy little state machine. If I was more creative, I could
+     * probably think of a better way of doing this...
+     */
+    do {
+       p = inbuf;
+       end = inbuf + inlen;
+       while ( p < end ) {
+           if (( state & ST_PAGE ) == 0 && *p != '\031' && *p != '\001' ) {
+               if ( npages == 0 ) {
+                   printf( "%%!PS-Adobe-2.0\n%%%%Pages: (atend)\n" );
+                   printf( "%%%%DocumentFonts: %s\n", font );
+                   fflush( stdout );
+
+                   /* output postscript prologue: */
+                   if ( write( 1, pspro, sizeof( pspro ) - 1 ) !=
+                           sizeof( pspro ) - 1 ) {
+                       syslog( LOG_ERR, "write prologue: %m" );
+                       return( 1 );
+                   }
+                   if ( name && host ) {
+                       printf( "statusdict /jobname (%s@%s) put\n", name,
+                               host );
+                   }
+               }
+
+               printf( "%%%%Page: ? %d\n", ++npages );
+               printf( "%d %f %f /%s %d SP\n", indent,
+                       papersize.win, papersize.lin, font, point );
+               state |= ST_PAGE;
+           }
+           if ( state & ST_CONTROL && *p != '\001' ) {
+               if ( !literal ) {
+                   fprintf( stderr, "unprintable character (0x%x)!\n",
+                           (unsigned char)031 );
+                   return( 2 );        /* Toss job */
+               }
+               printf( "\\%o", (unsigned char)031 );
+               state &= ~ST_CONTROL;
+               col++;
+           }
+
+           switch ( *p ) {
+           case '\n' :         /* end of line */
+               if ( state & ST_AVAIL ) {
+                   printf( ")S\n" );
+                   state &= ~ST_AVAIL;
+               }
+               printf( "NL\n" );
+               line++;
+               col = 0;
+               if ( line >= length ) {
+                   printf( "EP\n" );
+                   state &= ~ST_PAGE;
+                   line = 0;
+               }
+               break;
+
+           case '\r' :         /* carriage return (for overtyping) */
+               if ( state & ST_AVAIL ) {
+                   printf( ")S CR\n" );
+                   state &= ~ST_AVAIL;
+               }
+               col = 0;
+               break;
+
+           case '\f' :         /* form feed */
+               if ( state & ST_AVAIL ) {
+                   printf( ")S\n" );
+                   state &= ~ST_AVAIL;
+               }
+               printf( "EP\n" );
+               state &= ~ST_PAGE;
+               line = 0;
+               col = 0;
+               break;
+
+           case '\b' :         /* backspace */
+               /* show line, back up one character */
+               if ( state & ST_AVAIL ) {
+                   printf( ")S\n" );
+                   state &= ~ST_AVAIL;
+               }
+               printf( "B\n" );
+               col--;
+               break;
+
+           case '\t' :         /* tab */
+               if ( state & ST_AVAIL ) {
+                   printf( ")S\n" );
+                   state &= ~ST_AVAIL;
+               }
+               printf( "%d T\n", 8 - ( col % 8 ));
+               col += 8 - ( col % 8 );
+               break;
+
+           /*
+            * beginning of lpr control sequence
+            */
+           case '\031' :
+               state |= ST_CONTROL;
+               break;
+
+           case '\001' :       /* lpr control sequence */
+               if ( state & ST_CONTROL ) {
+                   rc = 3;
+                   goto out;
+               }
+               /* FALLTHROUGH */
+
+           case '\\' :
+           case ')' :
+           case '(' :
+               if (( state & ST_AVAIL ) == 0 ) {
+                   printf( "(" );
+                   state |= ST_AVAIL;
+               }
+               putchar( '\\' );
+               /* FALLTHROUGH */
+
+           default :
+               if (( state & ST_AVAIL ) == 0 ) {
+                   printf( "(" );
+                   state |= ST_AVAIL;
+               }
+               if ( !isascii( *p ) || !isprint( *p )) {
+                   if ( !literal ) {
+                       fprintf( stderr, "unprintable character (0x%x)!\n",
+                               (unsigned char)*p );
+                       return( 2 );    /* Toss job */
+                   }
+                   printf( "\\%o", (unsigned char)*p );
+               } else {
+                   putchar( *p );
+               }
+               col++;
+               break;
+           }
+       p++;
+       }
+    } while (( inlen = read( 0, inbuf, sizeof( inbuf ))) > 0 );
+    if ( inlen < 0 ) {
+       syslog( LOG_ERR, "read: %m" );
+       return( 1 );
+    }
+    rc = 0;
+
+out:
+    if ( state & ST_AVAIL ) {
+       printf( ")S\n" );
+       state &= ~ST_AVAIL;
+    }
+
+    if ( state & ST_PAGE ) {
+       printf( "EP\n" );
+       state &= ~ST_PAGE;
+    }
+
+    if ( npages > 0 ) {
+       printf( "%%%%Trailer\nGSV restore\n%%%%Pages: %d\n%%%%EOF\n", npages );
+       fflush( stdout );
+    }
+
+    return( rc );
+}
+
+/*
+ * Interface to pipe and exec, for starting children in pipelines.
+ *
+ * Manipulates file descriptors 0, 1, and 2, such that the new child
+ * is reading from the parent's output.
+ */
+pexecv( path, argv )
+    char       *path, *argv[];
+{
+    int                fd[ 2 ], c;
+
+    if ( pipe( fd ) < 0 ) {
+       return( -1 );
+    }
+
+    switch ( c = fork()) {
+    case -1 :
+       return( -1 );
+       /* NOTREACHED */
+
+    case 0 :
+       if ( close( fd[ 1 ] ) < 0 ) {
+           return( -1 );
+       }
+       if ( dup2( fd[ 0 ], 0 ) < 0 ) {
+           return( -1 );
+       }
+       if ( close( fd[ 0 ] ) < 0 ) {
+           return( -1 );
+       }
+       execv( path, argv );
+       return( -1 );
+       /* NOTREACHED */
+
+    default :
+       if ( close( fd[ 0 ] ) < 0 ) {
+           return( -1 );
+       }
+       if ( dup2( fd[ 1 ], 1 ) < 0 ) {
+           return( -1 );
+       }
+       if ( close( fd[ 1 ] ) < 0 ) {
+           return( -1 );
+       }
+       return( c );
+    }
+}
diff --git a/etc/uams/Makefile b/etc/uams/Makefile
new file mode 100644 (file)
index 0000000..ca97c7d
--- /dev/null
@@ -0,0 +1,171 @@
+
+##########################################################################
+
+SRC = uams_pam.c uams_passwd.c uams_randnum.c uams_guest.c uams_dhx_pam.c \
+      uams_dhx_passwd.c
+OBJ = uams_pam.o uams_passwd.o uams_randnum.o uams_guest.o uams_dhx_pam.o \
+      uams_dhx_passwd.o
+SHAREDOBJS = uams_pam.so uams_passwd.so uams_randnum.so uams_guest.so \
+            uams_dhx_pam.so uams_dhx_passwd.so
+
+CFLAGS=        -I../../include ${CSHAREDFLAGS} ${DEFS} ${AFSDEFS} ${OPTOPTS} 
+TAGSFILE=      tags
+CC=    cc
+INSTALL=       install
+
+SUBDIRS=
+
+all:: 
+       if [ x"${KRBDIR}" != x ]; then \
+           KRBLIBS="-lkrb -ldes"; \
+           KRBLIBDIRS="-L${KRBDIR}/lib"; \
+           KRBINCPATH="-I${KRBDIR}/include"; \
+           KRBDEFS="-DKRB"; \
+       fi; \
+       if [ x"${AFSDIR}" != x ]; then \
+           AFSLIBS="-lkauth -lprot -lubik -lauth -lsys -lrxkad -lrx -laudit \
+               -llwp -lcmd -lcom_err ${AFSDIR}/lib/afs/util.a -ldes"; \
+           AFSLIBDIRS="-L${AFSDIR}/lib -L${AFSDIR}/lib/afs"; \
+           AFSINCPATH="-I${AFSDIR}/include"; \
+           AFSDEFS="-DAFS"; \
+       fi; \
+       if [ x"${DESDIR}" != x ]; then \
+           CRYPTOLIBS="-ldes"; \
+           if [ "${DESDIR}" != "/usr" ]; then \
+             CRYPTOLIBDIRS="-L${DESDIR}/lib"; \
+             CRYPTOINCPATH="-I${DESDIR}/include"; \
+           fi; \
+           CRYPTODEFS="-DUAM_RNDNUM"; \
+       fi; \
+       if [ x"${CRYPTODIR}" != x ]; then \
+           CRYPTOLIBS="-lcrypto"; \
+           if [ "${CRYPTODIR}" != "/usr" ]; then \
+             CRYPTOLIBDIRS="-L${CRYPTODIR}/lib"; \
+             CRYPTOINCPATH="-I${CRYPTODIR}/include -I${CRYPTODIR}/include/openssl"; \
+           fi; \
+           CRYPTODEFS="-DUAM_RNDNUM -DUAM_DHX"; \
+       fi; \
+       if [ x"${PAMDIR}" != x ]; then \
+           PAMLIBS="-lpam"; \
+           if [ "${PAMDIR}" != "/usr" ]; then \
+             PAMLIBDIRS="-L${PAMDIR}/lib"; \
+             PAMINCPATH="-I${PAMDIR}/include"; \
+           fi; \
+           PAMDEFS="-DUSE_PAM"; \
+       fi; \
+       if [ x"${CRACKDIR}" != x ]; then \
+           CRACKLIBS="-lcrack"; \
+           if [ "${CRACKDIR}" != "/usr" ]; then \
+             CRACKLIBDIRS="-L${CRACKDIR}/lib"; \
+             CRACKINCPATH="-I${CRACKDIR}/include"; \
+           fi; \
+           CRACKDEFS="-DUSE_CRACKLIB -D_PATH_CRACKLIB=\\\"/usr/lib/cracklib_dict\\\""; \
+       fi; \
+       if [ -f /lib/libcrypt.a -o -f /usr/lib/libcrypt.a ] ; then \
+          if [ x"${USE_CRYPTLIB}" != x"no" ]; then \
+           CRYPTLIB="-lcrypt"; \
+          fi; \
+        fi; \
+       if [ ! -f /usr/include/crypt.h -a ! -f /usr/local/include/crypt.h ]; then \
+           CRYPTDEFS="-DNO_CRYPT_H"; \
+       fi; \
+       ${MAKE} ${MFLAGS} CC="${CC}" ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" \
+           OPTOPTS="${OPTOPTS}" DESDIR="${DESDIR}" \
+           PAMDIR="${PAMDIR}" \
+           KRBLIBS="$${KRBLIBS}" KRBLIBDIRS="$${KRBLIBDIRS}" \
+           KRBINCPATH="$${KRBINCPATH}" KRBDEFS="$${KRBDEFS}" \
+           AFSLIBS="$${AFSLIBS}" AFSLIBDIRS="$${AFSLIBDIRS}" \
+           CRYPTOLIBS="$${CRYPTOLIBS}" CRYPTOLIBDIRS="$${CRYPTOLIBDIRS}" \
+           PAMLIBS="$${PAMLIBS}" PAMLIBDIRS="$${PAMLIBDIRS}" \
+           CRYPTLIB="$${CRYPTLIB}" CRYPTDEFS="$${CRYPTDEFS}" \
+           CRYPTOINCPATH="$${CRYPTOINCPATH}" CRYPTODEFS="$${CRYPTODEFS}" \
+           PAMINCPATH="$${PAMINCPATH}" PAMDEFS="$${PAMDEFS}" \
+           AFSINCPATH="$${AFSINCPATH}" AFSDEFS="$${AFSDEFS}" \
+           CRACKLIBS="$${CRACKLIBS}" \
+           CRACKINCPATH="$${CRACKINCPATH}" CRACKDEFS="$${CRACKDEFS}" \
+           ${SHAREDOBJS} ${SUBDIRS}
+
+.SUFFIXES: .o .so
+
+.c.so:
+       ${CC} ${CFLAGS} -c $<
+       ${LDSHARED} ${LDSHAREDFLAGS} -o $@ $*.o 
+
+uams_passwd.so: uams_passwd.c
+       ${CC} ${CFLAGS} ${CRYPTDEFS} -c $<
+       ${LDSHARED} ${LDSHAREDFLAGS} -o $@ $*.o ${CRYPTLIB}
+
+uams_pam.so: uams_pam.c
+       ${CC} ${CFLAGS} ${PAMINCPATH} ${PAMDEFS} -c $<
+       ${LDSHARED} ${LDSHAREDFLAGS} -o $@ $*.o ${PAMLIBDIRS} ${PAMLIBS}
+
+uams_randnum.so: uams_randnum.c
+       ${CC} ${CFLAGS} ${CRYPTOINCPATH} ${CRYPTODEFS} ${CRACKINCPATH} \
+       ${CRACKDEFS} -c $<
+       ${LDSHARED} ${LDSHAREDFLAGS} -o $@ $*.o ${CRYPTOLIBDIRS} \
+       ${CRYPTOLIBS} ${CRACKLIBS}
+
+uams_dhx_passwd.so: uams_dhx_passwd.c
+       ${CC} ${CFLAGS} ${CRYPTDEFS} ${CRYPTOINCPATH} ${CRYPTODEFS} -c $<
+       ${LDSHARED} ${LDSHAREDFLAGS} -o $@ $*.o ${CRYPTOLIBDIRS} ${CRYPTOLIBS}
+
+uams_dhx_pam.so: uams_dhx_pam.c
+       ${CC} ${CFLAGS} ${PAMINCPATH} ${CRYPTOINCPATH} ${CRYPTODEFS} \
+       ${PAMDEFS} -c $<
+       ${LDSHARED} ${LDSHAREDFLAGS} -o $@ $*.o ${CRYPTOLIBDIRS} \
+       ${CRYPTOLIBS} ${PAMLIBDIRS} ${PAMLIBS}
+
+# this is getting pushed into a subdirectory
+uams_kerberos.so: uams_kerberos.c      
+       ${CC} ${CFLAGS} ${KRBINCPATH} ${KRBDEFS} -c $<
+       ${LDSHARED} ${LDSHAREDFLAGS} -o $@ $< ${KRBLIBDIRS} ${KRBLIBS} 
+
+install : all
+       -for i in ${SUBDIRS}; \
+       do (cd $$i; ${MAKE} ${MFLAGS} CC="${CC}" \
+               ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+               SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+               ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+               DESTDIR="${DESTDIR}" INSTALL="${INSTALL}" \
+               install); \
+       done
+       -mkdir ${RESDIR}/uams
+       ${INSTALL} -c *.so ${RESDIR}/uams
+       -rm ${RESDIR}/uams/uams_clrtxt.so ${RESDIR}/uams/uams_dhx.so
+       if [ x"${PAMDIR}" != x ]; then \
+               (cd ${RESDIR}/uams; ln -s uams_pam.so uams_clrtxt.so; \
+                ln -s uams_dhx_pam.so uams_dhx.so); \
+       else \
+               (cd ${RESDIR}/uams; ln -s uams_passwd.so uams_clrtxt.so; \
+                ln -s uams_dhx_passwd.so uams_dhx.so); \
+       fi;
+
+clean :
+       rm -f a.out core* *.o *.bak *[Ee]rrs tags
+       rm -f *.so
+
+tags : ${SRC}
+       cwd=`pwd`; \
+       for i in ${SRC}; do \
+           ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+       done
+
+depend :
+       for i in ${SRC} ; do \
+           ${CC} -M ${DEFS} ${INCPATH} $$i | \
+           awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+               else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+               else rec = rec " " $$2 } } \
+               END { print rec } ' >> makedep; done
+       sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+       cat makedep >> Makefile.tmp
+       rm makedep
+       echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+       echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+       echo '# see make depend above' >> Makefile.tmp
+       rm -f Makefile.bak
+       cp Makefile Makefile.bak
+       mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
diff --git a/etc/uams/uams_dhx_pam.c b/etc/uams/uams_dhx_pam.c
new file mode 100644 (file)
index 0000000..e65b6f6
--- /dev/null
@@ -0,0 +1,523 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu) 
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+#if defined(USE_PAM) && defined(UAM_DHX)
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include <security/pam_appl.h>
+
+#include <bn.h>
+#include <dh.h>
+#include <cast.h>
+
+#include <atalk/afp.h>
+#include <atalk/uam.h>
+
+#define KEYSIZE 16
+#define PASSWDLEN 64
+#define CRYPTBUFLEN  (KEYSIZE*2)
+#define CRYPT2BUFLEN (KEYSIZE + PASSWDLEN)
+#define CHANGEPWBUFLEN (KEYSIZE + 2*PASSWDLEN)
+
+/* hash a number to a 16-bit quantity */
+#define dhxhash(a) ((((unsigned long) (a) >> 8) ^ \
+                    (unsigned long) (a)) & 0xffff)
+
+/* the secret key */
+static CAST_KEY castkey;
+static struct passwd *dhxpwd;
+static u_int8_t randbuf[KEYSIZE];
+
+/* diffie-hellman bits */
+static unsigned char msg2_iv[] = "CJalbert";
+static unsigned char msg3_iv[] = "LWallace";
+static const u_int8_t p[] = {0xBA, 0x28, 0x73, 0xDF, 0xB0, 0x60, 0x57, 0xD4,
+                            0x3F, 0x20, 0x24, 0x74, 0x4C, 0xEE, 0xE7, 0x5B};
+static const u_int8_t g = 0x07;
+
+
+/* Static variables used to communicate between the conversation function
+ * and the server_login function
+ */
+static pam_handle_t *pamh = NULL; 
+static char *PAM_username;
+static char *PAM_password;
+
+/* PAM conversation function
+ * Here we assume (for now, at least) that echo on means login name, and
+ * echo off means password.
+ */
+static int PAM_conv (int num_msg,
+                     const struct pam_message **msg,
+                     struct pam_response **resp,
+                     void *appdata_ptr) {
+  int count = 0;
+  struct pam_response *reply;
+  
+#define COPY_STRING(s) (s) ? strdup(s) : NULL
+  
+  if (num_msg < 1)
+    return PAM_CONV_ERR;
+
+  reply = (struct pam_response *) 
+    calloc(num_msg, sizeof(struct pam_response));
+
+  if (!reply)
+    return PAM_CONV_ERR;
+
+  for (count = 0; count < num_msg; count++) {
+    char *string = NULL;
+
+    switch (msg[count]->msg_style) {
+    case PAM_PROMPT_ECHO_ON:
+      if (!(string = COPY_STRING(PAM_username)))
+       goto pam_fail_conv;
+      break;
+    case PAM_PROMPT_ECHO_OFF:
+      if (!(string = COPY_STRING(PAM_password)))
+       goto pam_fail_conv;
+      break;
+    case PAM_TEXT_INFO:
+#ifdef PAM_BINARY_PROMPT
+    case PAM_BINARY_PROMPT:
+#endif
+      /* ignore it... */
+      break;
+    case PAM_ERROR_MSG:
+    default:
+      goto pam_fail_conv;
+    }
+
+    if (string) {  
+      reply[count].resp_retcode = 0;
+      reply[count].resp = string;
+      string = NULL;
+    }
+  }
+
+  *resp = reply;
+  return PAM_SUCCESS;
+
+pam_fail_conv:
+  for (count = 0; count < num_msg; count++) {
+    if (!reply[count].resp)
+       continue;
+    switch (msg[count]->msg_style) {
+    case PAM_PROMPT_ECHO_OFF:
+    case PAM_PROMPT_ECHO_ON:
+      free(reply[count].resp);
+      break;      
+    }
+  }
+  free(reply);
+  return PAM_CONV_ERR;
+}
+
+static struct pam_conv PAM_conversation = {
+  &PAM_conv,
+  NULL
+};
+
+
+static int dhx_setup(void *obj, char *ibuf, int ibuflen, 
+                    char *rbuf, int *rbuflen)
+{
+    u_int16_t sessid;
+    int i;
+    BIGNUM *bn, *gbn, *pbn;
+    DH *dh;
+
+    /* get the client's public key */
+    if (!(bn = BN_bin2bn(ibuf, KEYSIZE, NULL)))
+      return AFPERR_PARAM;
+
+    /* get our primes */
+    if (!(gbn = BN_bin2bn(&g, sizeof(g), NULL))) {
+      BN_clear_free(bn);
+      return AFPERR_PARAM;
+    }
+
+    if (!(pbn = BN_bin2bn(p, sizeof(p), NULL))) {
+      BN_free(gbn);
+      BN_clear_free(bn);
+      return AFPERR_PARAM;
+    }
+
+    /* okay, we're ready */
+    if (!(dh = DH_new())) {
+      BN_free(pbn);
+      BN_free(gbn);
+      BN_clear_free(bn);
+      return AFPERR_PARAM;
+    }
+
+    /* generate key and make sure that we have enough space */
+    dh->p = pbn;
+    dh->g = gbn;
+    if (!DH_generate_key(dh) || (BN_num_bytes(dh->pub_key) > KEYSIZE)) {
+      goto pam_fail;
+    }
+
+    /* figure out the key. store the key in rbuf for now. */
+    i = DH_compute_key(rbuf, bn, dh);
+    
+    /* set the key */
+    CAST_set_key(&castkey, i, rbuf);
+    
+    /* session id. it's just a hashed version of the object pointer. */
+    sessid = dhxhash(obj);
+    memcpy(rbuf, &sessid, sizeof(sessid));
+    rbuf += sizeof(sessid);
+    *rbuflen += sizeof(sessid);
+    
+    /* public key */
+    BN_bn2bin(dh->pub_key, rbuf); 
+    rbuf += KEYSIZE;
+    *rbuflen += KEYSIZE;
+
+    /* buffer to be encrypted */
+    i = sizeof(randbuf);
+    if (uam_afpserver_option(obj, UAM_OPTION_RANDNUM, (void *) randbuf,
+                            &i) < 0) {
+      *rbuflen = 0;
+      goto pam_fail;
+    }    
+    memcpy(rbuf, &randbuf, sizeof(randbuf));
+
+    /* get the signature. it's always 16 bytes. */
+#if 0
+    if (uam_afpserver_option(obj, UAM_OPTION_SIGNATURE, 
+                            (void *) &buf, NULL) < 0) {
+      *rbuflen = 0;
+      goto pam_fail;
+    }
+    memcpy(rbuf + KEYSIZE, buf, KEYSIZE); 
+#else
+    memset(rbuf + KEYSIZE, 0, KEYSIZE); 
+#endif
+
+    /* encrypt using cast */
+    CAST_cbc_encrypt(rbuf, rbuf, CRYPTBUFLEN, &castkey, msg2_iv, 
+                    CAST_ENCRYPT);
+    *rbuflen += CRYPTBUFLEN;
+    BN_free(bn);
+    DH_free(dh);
+    return AFPERR_AUTHCONT;
+
+pam_fail:
+    BN_free(bn);
+    DH_free(dh);
+    return AFPERR_PARAM;
+}
+
+
+/* dhx login: things are done in a slightly bizarre order to avoid
+ * having to clean things up if there's an error. */
+static int pam_login(void *obj, struct passwd **uam_pwd,
+                    char *ibuf, int ibuflen,
+                    char *rbuf, int *rbuflen)
+{
+    char *buf;
+    int len, i;
+
+    *rbuflen = 0;
+
+    /* grab some of the options */
+    if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, (void *) &buf,
+                            &i) < 0)
+      return AFPERR_PARAM;
+
+    len = (unsigned char) *ibuf++;
+    if ( len > i ) {
+       return( AFPERR_PARAM );
+    }
+
+    memcpy(buf, ibuf, len );
+    ibuf += len;
+    buf[ len ] = '\0';
+    if ((unsigned long) ibuf & 1) /* pad to even boundary */
+      ++ibuf;
+
+    if (( dhxpwd = uam_getname(buf, i)) == NULL ) {
+       return AFPERR_PARAM;
+    }
+
+    PAM_username = buf;
+    syslog( LOG_INFO, "dhx login: %s", buf);
+    return dhx_setup(obj, ibuf, ibuflen, rbuf, rbuflen);
+}
+
+
+static int pam_logincont(void *obj, struct passwd **uam_pwd,
+                        char *ibuf, int ibuflen, 
+                        char *rbuf, int *rbuflen)
+{
+    char *hostname;
+    BIGNUM *bn1, *bn2, *bn3;
+    u_int16_t sessid;
+    int err, PAM_error;
+
+    *rbuflen = 0;
+
+    /* check for session id */
+    memcpy(&sessid, ibuf, sizeof(sessid));
+    if (sessid != dhxhash(obj))
+      return AFPERR_PARAM;
+    ibuf += sizeof(sessid);
+    
+    if (uam_afpserver_option(obj, UAM_OPTION_HOSTNAME,
+                            (void *) &hostname, NULL) < 0)
+      return AFPERR_MISC;
+
+    CAST_cbc_encrypt(ibuf, rbuf, CRYPT2BUFLEN, &castkey,
+                    msg3_iv, CAST_DECRYPT);
+    memset(&castkey, 0, sizeof(castkey));
+
+    /* check to make sure that the random number is the same. we
+     * get sent back an incremented random number. */
+    if (!(bn1 = BN_bin2bn(rbuf, KEYSIZE, NULL)))
+      return AFPERR_PARAM;
+
+    if (!(bn2 = BN_bin2bn(randbuf, sizeof(randbuf), NULL))) {
+      BN_free(bn1);
+      return AFPERR_PARAM;
+    }
+      
+    /* zero out the random number */
+    memset(rbuf, 0, sizeof(randbuf));
+    memset(randbuf, 0, sizeof(randbuf));
+    rbuf += KEYSIZE;
+
+    if (!(bn3 = BN_new())) {
+      BN_free(bn2);
+      BN_free(bn1);
+      return AFPERR_PARAM;
+    }
+
+    BN_sub(bn3, bn1, bn2);
+    BN_free(bn2);
+    BN_free(bn1);
+
+    /* okay. is it one more? */
+    if (!BN_is_one(bn3)) {
+      BN_free(bn3);
+      return AFPERR_PARAM;
+    }
+    BN_free(bn3);
+
+    /* Set these things up for the conv function */
+    rbuf[PASSWDLEN] = '\0';
+    PAM_password = rbuf;
+
+    err = AFPERR_NOTAUTH;
+    PAM_error = pam_start("netatalk", PAM_username, &PAM_conversation,
+                         &pamh);
+    if (PAM_error != PAM_SUCCESS)
+      goto logincont_err;
+
+    /* solaris craps out if PAM_TTY and PAM_RHOST aren't set. */
+    pam_set_item(pamh, PAM_TTY, "afpd");
+    pam_set_item(pamh, PAM_RHOST, hostname);
+    PAM_error = pam_authenticate(pamh,0);
+    if (PAM_error != PAM_SUCCESS) {
+      if (PAM_error == PAM_MAXTRIES) 
+       err = AFPERR_PWDEXPR;
+      goto logincont_err;
+    }      
+
+    PAM_error = pam_acct_mgmt(pamh, 0);
+    if (PAM_error != PAM_SUCCESS) {
+      if (PAM_error == PAM_ACCT_EXPIRED)
+       err = AFPERR_PWDEXPR;
+#ifdef PAM_AUTHTOKEN_REQD
+      else if (PAM_error == PAM_AUTHTOKEN_REQD) 
+       err = AFPERR_PWDCHNG;
+#endif
+      goto logincont_err;
+    }
+
+#ifndef PAM_CRED_ESTABLISH
+#define PAM_CRED_ESTABLISH PAM_ESTABLISH_CRED
+#endif
+    PAM_error = pam_setcred(pamh, PAM_CRED_ESTABLISH);
+    if (PAM_error != PAM_SUCCESS)
+      goto logincont_err;
+
+    PAM_error = pam_open_session(pamh, 0);
+    if (PAM_error != PAM_SUCCESS)
+      goto logincont_err;
+
+    memset(rbuf, 0, PASSWDLEN); /* zero out the password */
+    *uam_pwd = dhxpwd;
+    return AFP_OK;
+
+logincont_err:
+    pam_end(pamh, PAM_error);
+    pamh = NULL;
+    memset(rbuf, 0, CRYPT2BUFLEN);
+    return err;
+}
+
+/* logout */
+static void pam_logout() {
+    pam_close_session(pamh, 0);
+    pam_end(pamh, 0);
+    pamh = NULL;
+}
+
+
+/* change pw for dhx needs a couple passes to get everything all
+ * right. basically, it's like the login/logincont sequence */
+static int pam_changepw(void *obj, char *username,
+                       struct passwd *pwd, char *ibuf, int ibuflen,
+                       char *rbuf, int *rbuflen)
+{
+    BIGNUM *bn1, *bn2, *bn3;
+
+    char *hostname;
+    pam_handle_t *lpamh;
+    uid_t uid;
+    u_int16_t sessid;
+    int PAM_error;
+
+    /* grab the id */
+    memcpy(&sessid, ibuf, sizeof(sessid));
+    ibuf += sizeof(sessid);
+    
+    if (!sessid) {  /* no sessid -> initialization phase */
+      PAM_username = username;
+      ibuflen -= sizeof(sessid);
+      return dhx_setup(obj, ibuf, ibuflen, rbuf, rbuflen);
+    }
+
+
+    /* otherwise, it's like logincont but different. */
+
+    /* check out the session id */
+    if (sessid != dhxhash(obj))
+      return AFPERR_PARAM;
+
+    /* we need this for pam */
+    if (uam_afpserver_option(obj, UAM_OPTION_HOSTNAME,
+                            (void *) &hostname, NULL) < 0)
+      return AFPERR_MISC;
+
+    /* grab the client's nonce, old password, and new password. */
+    CAST_cbc_encrypt(ibuf, ibuf, CHANGEPWBUFLEN, &castkey,
+                    msg3_iv, CAST_DECRYPT);
+    memset(&castkey, 0, sizeof(castkey));
+
+    /* check to make sure that the random number is the same. we
+     * get sent back an incremented random number. */
+    if (!(bn1 = BN_bin2bn(ibuf, KEYSIZE, NULL)))
+      return AFPERR_PARAM;
+
+    if (!(bn2 = BN_bin2bn(randbuf, sizeof(randbuf), NULL))) {
+      BN_free(bn1);
+      return AFPERR_PARAM;
+    }
+      
+    /* zero out the random number */
+    memset(rbuf, 0, sizeof(randbuf));
+    memset(randbuf, 0, sizeof(randbuf));
+
+    if (!(bn3 = BN_new())) {
+      BN_free(bn2);
+      BN_free(bn1);
+      return AFPERR_PARAM;
+    }
+
+    BN_sub(bn3, bn1, bn2);
+    BN_free(bn2);
+    BN_free(bn1);
+
+    /* okay. is it one more? */
+#if 0
+    if (!BN_is_one(bn3)) {
+      BN_free(bn3);
+      return AFPERR_PARAM;
+    }
+#endif
+    BN_free(bn3);
+
+    /* Set these things up for the conv function. the old password
+     * is at the end. */
+    ibuf += KEYSIZE;
+    ibuf[PASSWDLEN + PASSWDLEN] = '\0';
+    PAM_password = ibuf + PASSWDLEN;
+
+    PAM_error = pam_start("netatalk", username, &PAM_conversation,
+                         &lpamh);
+    if (PAM_error != PAM_SUCCESS) 
+      return AFPERR_PARAM;
+    pam_set_item(lpamh, PAM_TTY, "afpd");
+    pam_set_item(lpamh, PAM_RHOST, hostname);
+
+    /* we might need to do this as root */
+    uid = geteuid();
+    seteuid(0);
+    PAM_error = pam_authenticate(lpamh, 0);
+    if (PAM_error != PAM_SUCCESS) {
+      seteuid(uid);
+      pam_end(lpamh, PAM_error);
+      return AFPERR_NOTAUTH;
+    }
+
+    /* clear out old passwd */
+    memset(ibuf + PASSWDLEN, 0, PASSWDLEN);
+
+    /* new password */
+    PAM_password = ibuf;
+    ibuf[PASSWDLEN] = '\0';
+
+    /* this really does need to be done as root */
+    PAM_error = pam_chauthtok(lpamh, 0);
+    seteuid(uid); /* un-root ourselves. */
+    memset(ibuf, 0, PASSWDLEN);
+    if (PAM_error != PAM_SUCCESS) {
+      pam_end(lpamh, PAM_error);
+      return AFPERR_ACCESS;
+    }
+
+    pam_end(lpamh, 0);
+    return AFP_OK;
+}
+
+
+static int uam_setup(const char *path)
+{
+  if (uam_register(UAM_SERVER_LOGIN, path, "DHCAST128", pam_login, 
+                  pam_logincont, pam_logout) < 0)
+    return -1;
+
+  if (uam_register(UAM_SERVER_CHANGEPW, path, "DHCAST128", 
+                  pam_changepw) < 0) {
+    uam_unregister(UAM_SERVER_LOGIN, "DHCAST128");
+    return -1;
+  }
+
+  /*uam_register(UAM_SERVER_PRINTAUTH, path, "DHCAST128",
+    pam_printer);*/
+
+  return 0;
+}
+
+static void uam_cleanup(void)
+{
+  uam_unregister(UAM_SERVER_LOGIN, "DHCAST128");
+  uam_unregister(UAM_SERVER_CHANGEPW, "DHCAST128");
+  /*uam_unregister(UAM_SERVER_PRINTAUTH, "DHCAST128"); */
+}
+
+UAM_MODULE_EXPORT struct uam_export uams_dhx = {
+  UAM_MODULE_SERVER,
+  UAM_MODULE_VERSION,
+  uam_setup, uam_cleanup
+};
+
+#endif /* USE_PAM && UAM_DHX */
diff --git a/etc/uams/uams_dhx_passwd.c b/etc/uams/uams_dhx_passwd.c
new file mode 100644 (file)
index 0000000..934847c
--- /dev/null
@@ -0,0 +1,269 @@
+/* Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu) 
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#ifdef UAM_DHX
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#ifndef NO_CRYPT_H
+#include <crypt.h>
+#endif
+#include <pwd.h>
+#include <syslog.h>
+
+#ifdef SOLARIS
+#define SHADOWPW
+#endif SOLARIS
+
+#ifdef SHADOWPW
+#include <shadow.h>
+#endif SHADOWPW
+
+#include <bn.h>
+#include <dh.h>
+#include <cast.h>
+
+#include <atalk/afp.h>
+#include <atalk/uam.h>
+
+#define KEYSIZE 16
+#define PASSWDLEN 64
+#define CRYPTBUFLEN  (KEYSIZE*2)
+#define CRYPT2BUFLEN (KEYSIZE + PASSWDLEN)
+
+/* hash a number to a 16-bit quantity */
+#define dhxhash(a) ((((unsigned long) (a) >> 8) ^ \
+                    (unsigned long) (a)) & 0xffff)
+
+/* the secret key */
+static CAST_KEY castkey;
+static struct passwd *dhxpwd;
+static u_int8_t randbuf[16];
+
+/* dhx passwd */
+static int passwd_login(void *obj, struct passwd **uam_pwd,
+                       char *ibuf, int ibuflen,
+                       char *rbuf, int *rbuflen)
+{
+    unsigned char iv[] = "CJalbert";
+    u_int8_t p[] = {0xBA, 0x28, 0x73, 0xDF, 0xB0, 0x60, 0x57, 0xD4,
+                   0x3F, 0x20, 0x24, 0x74, 0x4C, 0xEE, 0xE7, 0x5B };
+    u_int8_t g = 0x07;
+#ifdef SHADOWPW
+    struct spwd *sp;
+#endif
+    BIGNUM *bn, *gbn, *pbn;
+    u_int16_t sessid;
+    int len, i;
+    char *name;
+    DH *dh;
+
+    *rbuflen = 0;
+
+    if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, (void *) &name, &i) < 0)
+      return AFPERR_PARAM;
+
+    len = (unsigned char) *ibuf++;
+    if ( len > i ) {
+       return( AFPERR_PARAM );
+    }
+
+    memcpy(name, ibuf, len );
+    ibuf += len;
+    name[ len ] = '\0';
+    if ((unsigned long) ibuf & 1) /* padding */
+      ++ibuf;
+
+    if (( dhxpwd = uam_getname(name, i)) == NULL ) {
+      return AFPERR_PARAM;
+    }
+
+    syslog( LOG_INFO, "dhx login: %s", name);
+    if (uam_checkuser(dhxpwd) < 0)
+      return AFPERR_NOTAUTH;
+
+#ifdef SHADOWPW
+    if (( sp = getspnam( dhxpwd->pw_name )) == NULL ) {
+       syslog( LOG_INFO, "no shadow passwd entry for %s", name);
+       return AFPERR_NOTAUTH;
+    }
+    dhxpwd->pw_passwd = sp->sp_pwdp;
+#endif SHADOWPW
+
+    if (!dhxpwd->pw_passwd)
+      return AFPERR_NOTAUTH;
+
+    /* get the client's public key */
+    if (!(bn = BN_bin2bn(ibuf, KEYSIZE, NULL))) {
+      return AFPERR_PARAM;
+    }
+
+    /* get our primes */
+    if (!(gbn = BN_bin2bn(&g, sizeof(g), NULL))) {
+      BN_free(bn);
+      return AFPERR_PARAM;
+    }
+
+    if (!(pbn = BN_bin2bn(p, sizeof(p), NULL))) {
+      BN_free(gbn);
+      BN_free(bn);
+      return AFPERR_PARAM;
+    }
+
+    /* okay, we're ready */
+    if (!(dh = DH_new())) {
+      BN_free(pbn);
+      BN_free(gbn);
+      BN_free(bn);
+      return AFPERR_PARAM;
+    }
+
+    /* generate key and make sure we have enough space */
+    dh->p = pbn;
+    dh->g = gbn;
+    if (!DH_generate_key(dh) || (BN_num_bytes(dh->pub_key) > KEYSIZE)) {
+      goto passwd_fail;
+    }
+
+    /* figure out the key. use rbuf as a temporary buffer. */
+    i = DH_compute_key(rbuf, bn, dh);
+    
+    /* set the key */
+    CAST_set_key(&castkey, i, rbuf);
+    
+    /* session id. it's just a hashed version of the object pointer. */
+    sessid = dhxhash(obj);
+    memcpy(rbuf, &sessid, sizeof(sessid));
+    rbuf += sizeof(sessid);
+    *rbuflen += sizeof(sessid);
+    
+    /* send our public key */
+    BN_bn2bin(dh->pub_key, rbuf); 
+    rbuf += KEYSIZE;
+    *rbuflen += KEYSIZE;
+
+    /* buffer to be encrypted */
+    i = sizeof(randbuf);
+    if (uam_afpserver_option(obj, UAM_OPTION_RANDNUM, (void *) randbuf,
+                            &i) < 0) {
+      *rbuflen = 0;
+      goto passwd_fail;
+    }    
+    memcpy(rbuf, &randbuf, sizeof(randbuf));
+
+#if 0
+    /* get the signature. it's always 16 bytes. */
+    if (uam_afpserver_option(obj, UAM_OPTION_SIGNATURE, 
+                            (void *) &name, NULL) < 0) {
+      *rbuflen = 0;
+      goto passwd_fail;
+    }
+    memcpy(rbuf + KEYSIZE, name, KEYSIZE); 
+#else
+    memset(rbuf + KEYSIZE, 0, KEYSIZE);
+#endif
+
+    /* encrypt using cast */
+    CAST_cbc_encrypt(rbuf, rbuf, CRYPTBUFLEN, &castkey, iv, CAST_ENCRYPT);
+    *rbuflen += CRYPTBUFLEN;
+    BN_free(bn);
+    DH_free(dh);
+    return AFPERR_AUTHCONT;
+
+passwd_fail:
+    BN_free(bn);
+    DH_free(dh);
+    return AFPERR_PARAM;
+}
+
+static int passwd_logincont(void *obj, struct passwd **uam_pwd,
+                           char *ibuf, int ibuflen, 
+                           char *rbuf, int *rbuflen)
+{
+    unsigned char iv[] = "LWallace";
+    BIGNUM *bn1, *bn2, *bn3;
+    u_int16_t sessid;
+    char *p;
+
+    *rbuflen = 0;
+
+    /* check for session id */
+    memcpy(&sessid, ibuf, sizeof(sessid));
+    if (sessid != dhxhash(obj))
+      return AFPERR_PARAM;
+    ibuf += sizeof(sessid);
+   
+    /* use rbuf as scratch space */
+    CAST_cbc_encrypt(ibuf, rbuf, CRYPT2BUFLEN, &castkey,
+                    iv, CAST_DECRYPT);
+    
+    /* check to make sure that the random number is the same. we
+     * get sent back an incremented random number. */
+    if (!(bn1 = BN_bin2bn(rbuf, KEYSIZE, NULL)))
+      return AFPERR_PARAM;
+
+    if (!(bn2 = BN_bin2bn(randbuf, sizeof(randbuf), NULL))) {
+      BN_free(bn1);
+      return AFPERR_PARAM;
+    }
+      
+    /* zero out the random number */
+    memset(rbuf, 0, sizeof(randbuf));
+    memset(randbuf, 0, sizeof(randbuf));
+    rbuf += KEYSIZE;
+
+    if (!(bn3 = BN_new())) {
+      BN_free(bn2);
+      BN_free(bn1);
+      return AFPERR_PARAM;
+    }
+
+    BN_sub(bn3, bn1, bn2);
+    BN_free(bn2);
+    BN_free(bn1);
+
+    /* okay. is it one more? */
+    if (!BN_is_one(bn3)) {
+      BN_free(bn3);
+      return AFPERR_PARAM;
+    }
+    BN_free(bn3);
+
+    rbuf[PASSWDLEN] = '\0';
+    p = crypt( rbuf, dhxpwd->pw_passwd );
+    memset(rbuf, 0, PASSWDLEN);
+    if ( strcmp( p, dhxpwd->pw_passwd ) == 0 ) {
+      *uam_pwd = dhxpwd;
+      return AFP_OK;
+    }
+
+    return AFPERR_NOTAUTH;
+}
+
+
+static int uam_setup(const char *path)
+{
+  if (uam_register(UAM_SERVER_LOGIN, path, "DHCAST128",
+                  passwd_login, passwd_logincont, NULL) < 0)
+    return -1;
+  /*uam_register(UAM_SERVER_PRINTAUTH, path, "DHCAST128",
+    passwd_printer);*/
+
+  return 0;
+}
+
+static void uam_cleanup(void)
+{
+  uam_unregister(UAM_SERVER_LOGIN, "DHCAST128");
+  /*uam_unregister(UAM_SERVER_PRINTAUTH, "DHCAST128"); */
+}
+
+UAM_MODULE_EXPORT struct uam_export uams_dhx = {
+  UAM_MODULE_SERVER,
+  UAM_MODULE_VERSION,
+  uam_setup, uam_cleanup
+};
+#endif
diff --git a/etc/uams/uams_guest.c b/etc/uams/uams_guest.c
new file mode 100644 (file)
index 0000000..41e2d70
--- /dev/null
@@ -0,0 +1,66 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pwd.h>
+#include <syslog.h>
+
+#include <atalk/afp.h>
+#include <atalk/uam.h>
+
+static int noauth_login(void *obj, struct passwd **uam_pwd,
+                       char *ibuf, int ibuflen, 
+                       char *rbuf, int *rbuflen)
+{
+    struct passwd *pwent;
+    char *guest, *username;
+
+    *rbuflen = 0;
+    syslog( LOG_INFO, "login noauth" );
+
+    if (uam_afpserver_option(obj, UAM_OPTION_GUEST, (void *) &guest,
+                            NULL) < 0)
+      return AFPERR_MISC;
+
+    if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, 
+                            (void *) &username, NULL) < 0)
+      return AFPERR_MISC;
+
+    strcpy(username, guest);
+    if ((pwent = getpwnam(guest)) == NULL) {
+       syslog( LOG_ERR, "noauth_login: getpwnam( %s ): %m", guest);
+       return( AFPERR_BADUAM );
+    }
+
+#ifdef AFS
+    if ( setpag() < 0 ) {
+       syslog( LOG_ERR, "noauth_login: setpag: %m" );
+       return( AFPERR_BADUAM );
+    }
+#endif /* AFS */
+
+    *uam_pwd = pwent;
+    return( AFP_OK );
+}
+
+static int uam_setup(const char *path)
+{
+  if (uam_register(UAM_SERVER_LOGIN, path, "No User Authent",
+              noauth_login, NULL, NULL) < 0)
+    return -1;
+  /* uam_register(UAM_SERVER_PRINTAUTH, path,
+     "No User Authent", noauth_printer); */
+
+  return 0;
+}
+
+static void uam_cleanup()
+{
+  uam_unregister(UAM_SERVER_LOGIN, "No User Authent");
+  /* uam_unregister(UAM_SERVER_PRINTAUTH, "No User Authent"); */
+}
+
+UAM_MODULE_EXPORT struct uam_export uams_guest = {
+  UAM_MODULE_SERVER,
+  UAM_MODULE_VERSION,
+  uam_setup, uam_cleanup
+};
diff --git a/etc/uams/uams_krb4/kuam.c b/etc/uams/uams_krb4/kuam.c
new file mode 100644 (file)
index 0000000..a78f4ae
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+# ifdef UAM_AFSKRB
+
+#include <mit-copyright.h>
+#include <krb.h>
+#include <des.h>
+#include <prot.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+/* use the bsd time.h struct defs for PC too! */
+#include <sys/time.h>
+#include <sys/types.h>
+
+int     swap_bytes;
+
+/*
+ * krb_get_in_tkt() gets a ticket for a given principal to use a given
+ * service and stores the returned ticket and session key for future
+ * use.
+ *
+ * The "user", "instance", and "realm" arguments give the identity of
+ * the client who will use the ticket.  The "service" and "sinstance"
+ * arguments give the identity of the server that the client wishes
+ * to use.  (The realm of the server is the same as the Kerberos server
+ * to whom the request is sent.)  The "life" argument indicates the
+ * desired lifetime of the ticket; the "key_proc" argument is a pointer
+ * to the routine used for getting the client's private key to decrypt
+ * the reply from Kerberos.  The "decrypt_proc" argument is a pointer
+ * to the routine used to decrypt the reply from Kerberos; and "arg"
+ * is an argument to be passed on to the "key_proc" routine.
+ *
+ * If all goes well, krb_get_in_tkt() returns INTK_OK, otherwise it
+ * returns an error code:  If an AUTH_MSG_ERR_REPLY packet is returned
+ * by Kerberos, then the error code it contains is returned.  Other
+ * error codes returned by this routine include INTK_PROT to indicate
+ * wrong protocol version, INTK_BADPW to indicate bad password (if
+ * decrypted ticket didn't make sense), INTK_ERR if the ticket was for
+ * the wrong server or the ticket store couldn't be initialized.
+ *
+ * The format of the message sent to Kerberos is as follows:
+ *
+ * Size                        Variable                Field
+ * ----                        --------                -----
+ *
+ * 1 byte              KRB_PROT_VERSION        protocol version number
+ * 1 byte              AUTH_MSG_KDC_REQUEST |  message type
+ *                     HOST_BYTE_ORDER         local byte order in lsb
+ * string              user                    client's name
+ * string              instance                client's instance
+ * string              realm                   client's realm
+ * 4 bytes             tlocal.tv_sec           timestamp in seconds
+ * 1 byte              life                    desired lifetime
+ * string              service                 service's name
+ * string              sinstance               service's instance
+ */
+
+kuam_get_in_tkt(user, instance, realm, service, sinstance, life, rpkt )
+    char       *user;
+    char       *instance;
+    char       *realm;
+    char       *service;
+    char       *sinstance;
+    int                life;
+    KTEXT      rpkt;
+{
+    KTEXT_ST pkt_st;
+    KTEXT pkt = &pkt_st;       /* Packet to KDC */
+    KTEXT_ST cip_st;
+    KTEXT cip = &cip_st;       /* Returned Ciphertext */
+    KTEXT_ST tkt_st;
+    KTEXT tkt = &tkt_st;       /* Current ticket */
+    unsigned char *v = pkt->dat; /* Prot vers no */
+    unsigned char *t = (pkt->dat+1); /* Prot msg type */
+    int msg_byte_order;
+    int kerror;
+    struct timeval t_local;
+    u_int32_t rep_err_code;
+
+
+    /* BUILD REQUEST PACKET */
+
+    /* Set up the fixed part of the packet */
+    *v = (unsigned char) KRB_PROT_VERSION;
+    *t = (unsigned char) AUTH_MSG_KDC_REQUEST;
+    *t |= HOST_BYTE_ORDER;
+
+    /* Now for the variable info */
+    (void) strcpy((char *)(pkt->dat+2),user); /* aname */
+    pkt->length = 3 + strlen(user);
+    (void) strcpy((char *)(pkt->dat+pkt->length),
+                 instance);    /* instance */
+    pkt->length += 1 + strlen(instance);
+    (void) strcpy((char *)(pkt->dat+pkt->length),realm); /* realm */
+    pkt->length += 1 + strlen(realm);
+
+    (void) gettimeofday(&t_local,(struct timezone *) 0);
+    /* timestamp */
+    memcpy((pkt->dat+pkt->length), &(t_local.tv_sec), 4);
+    pkt->length += 4;
+
+    *(pkt->dat+(pkt->length)++) = (char) life;
+    (void) strcpy((char *)(pkt->dat+pkt->length),service);
+    pkt->length += 1 + strlen(service);
+    (void) strcpy((char *)(pkt->dat+pkt->length),sinstance);
+    pkt->length += 1 + strlen(sinstance);
+
+    rpkt->length = 0;
+
+    /* SEND THE REQUEST AND RECEIVE THE RETURN PACKET */
+
+    if (kerror = send_to_kdc(pkt, rpkt, realm)) return(kerror);
+
+    /* check packet version of the returned packet */
+    if (pkt_version(rpkt) != KRB_PROT_VERSION)
+        return(INTK_PROT);
+
+    /* Check byte order */
+    msg_byte_order = pkt_msg_type(rpkt) & 1;
+    swap_bytes = 0;
+    if (msg_byte_order != HOST_BYTE_ORDER) {
+        swap_bytes++;
+    }
+
+    switch (pkt_msg_type(rpkt) & ~1) {
+    case AUTH_MSG_KDC_REPLY:
+        break;
+    case AUTH_MSG_ERR_REPLY:
+        memcpy(&rep_err_code,pkt_err_code(rpkt),4);
+        if (swap_bytes) swap_u_long(rep_err_code);
+        return((int)rep_err_code);
+    default:
+        return(INTK_PROT);
+    }
+
+    return( INTK_OK );
+}
+
+kuam_set_in_tkt( user, instance, realm, service, sinstance, ptr)
+    char       *user, *instance, *realm, *service, *sinstance, *ptr;
+{
+    KTEXT_ST           tkt_st;
+    KTEXT              tkt = &tkt_st;
+    struct timeval     t_local;
+    int                        lifetime, kvno, kerror;
+    int32_t            kdc_time;
+    C_Block            ses;
+    char               s_name[ SNAME_SZ ], s_instance[ INST_SZ ];
+    char               rlm[ REALM_SZ ];
+
+    /* extract session key */
+    memcpy(ses, ptr, 8);
+    ptr += 8;
+
+    /* extract server's name */
+    (void) strcpy(s_name,ptr);
+    ptr += strlen(s_name) + 1;
+
+    /* extract server's instance */
+    (void) strcpy(s_instance,ptr);
+    ptr += strlen(s_instance) + 1;
+
+    /* extract server's realm */
+    (void) strcpy(rlm,ptr);
+    ptr += strlen(rlm) + 1;
+
+    /* extract ticket lifetime, server key version, ticket length */
+    /* be sure to avoid sign extension on lifetime! */
+    lifetime = (unsigned char) ptr[0];
+    kvno = (unsigned char) ptr[1];
+    tkt->length = (unsigned char) ptr[2];
+    ptr += 3;
+
+    /* extract ticket itself */
+    memcpy( tkt->dat, ptr, tkt->length);
+    ptr += tkt->length;
+
+    if (strcmp(s_name, service) || strcmp(s_instance, sinstance) ||
+        strcmp(rlm, realm))    /* not what we asked for */
+       return(INTK_ERR);       /* we need a better code here XXX */
+
+    /* check KDC time stamp */
+    memcpy(&kdc_time, ptr, 4); /* Time (coarse) */
+    if (swap_bytes) swap_u_long(kdc_time);
+
+    ptr += 4;
+
+    (void) gettimeofday(&t_local,(struct timezone *) 0);
+    if (abs((int)(t_local.tv_sec - kdc_time)) > CLOCK_SKEW) {
+        return(RD_AP_TIME);            /* XXX should probably be better
+                                          code */
+    }
+
+    /* initialize ticket cache */
+    if (in_tkt(user,instance) != KSUCCESS)
+       return(INTK_ERR);
+
+    /* stash ticket, session key, etc. for future use */
+    if (kerror = save_credentials(s_name, s_instance, rlm, ses,
+                                 lifetime, kvno, tkt, t_local.tv_sec))
+       return(kerror);
+
+    return(INTK_OK);
+}
+# endif UAM_AFSKRB
diff --git a/etc/uams/uams_krb4/lifetime.c b/etc/uams/uams_krb4/lifetime.c
new file mode 100644 (file)
index 0000000..582533f
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Ticket lifetime.  This defines the table used to lookup lifetime
+ * for the fixed part of rande of the one byte lifetime field.  Values
+ * less than 0x80 are intrpreted as the number of 5 minute intervals.
+ * Values from 0x80 to 0xBF should be looked up in this table.  The
+ * value of 0x80 is the same using both methods: 10 and two-thirds
+ * hours .  The lifetime of 0xBF is 30 days.  The intervening values
+ * of have a fixed ratio of roughly 1.06914.  The value 0xFF is
+ * defined to mean a ticket has no expiration time.  This should be
+ * used advisedly since individual servers may impose defacto
+ * upperbounds on ticket lifetimes.
+ */
+
+#include <sys/types.h>
+#include <netatalk/endian.h>
+
+#define TKTLIFENUMFIXED 64
+#define TKTLIFEMINFIXED 0x80
+#define TKTLIFEMAXFIXED 0xBF
+#define TKTLIFENOEXPIRE 0xFF
+#define MAXTKTLIFETIME (30*24*3600)    /* 30 days */
+#ifndef NEVERDATE
+#define NEVERDATE ((u_int32_t)-1L)
+#endif
+
+static int tkt_lifetimes[TKTLIFENUMFIXED] = {
+    38400,                             /* 10.67 hours, 0.44 days */ 
+    41055,                             /* 11.40 hours, 0.48 days */ 
+    43894,                             /* 12.19 hours, 0.51 days */ 
+    46929,                             /* 13.04 hours, 0.54 days */ 
+    50174,                             /* 13.94 hours, 0.58 days */ 
+    53643,                             /* 14.90 hours, 0.62 days */ 
+    57352,                             /* 15.93 hours, 0.66 days */ 
+    61318,                             /* 17.03 hours, 0.71 days */ 
+    65558,                             /* 18.21 hours, 0.76 days */ 
+    70091,                             /* 19.47 hours, 0.81 days */ 
+    74937,                             /* 20.82 hours, 0.87 days */ 
+    80119,                             /* 22.26 hours, 0.93 days */ 
+    85658,                             /* 23.79 hours, 0.99 days */ 
+    91581,                             /* 25.44 hours, 1.06 days */ 
+    97914,                             /* 27.20 hours, 1.13 days */ 
+    104684,                            /* 29.08 hours, 1.21 days */ 
+    111922,                            /* 31.09 hours, 1.30 days */ 
+    119661,                            /* 33.24 hours, 1.38 days */ 
+    127935,                            /* 35.54 hours, 1.48 days */ 
+    136781,                            /* 37.99 hours, 1.58 days */ 
+    146239,                            /* 40.62 hours, 1.69 days */ 
+    156350,                            /* 43.43 hours, 1.81 days */ 
+    167161,                            /* 46.43 hours, 1.93 days */ 
+    178720,                            /* 49.64 hours, 2.07 days */ 
+    191077,                            /* 53.08 hours, 2.21 days */ 
+    204289,                            /* 56.75 hours, 2.36 days */ 
+    218415,                            /* 60.67 hours, 2.53 days */ 
+    233517,                            /* 64.87 hours, 2.70 days */ 
+    249664,                            /* 69.35 hours, 2.89 days */ 
+    266926,                            /* 74.15 hours, 3.09 days */ 
+    285383,                            /* 79.27 hours, 3.30 days */ 
+    305116,                            /* 84.75 hours, 3.53 days */ 
+    326213,                            /* 90.61 hours, 3.78 days */ 
+    348769,                            /* 96.88 hours, 4.04 days */ 
+    372885,                            /* 103.58 hours, 4.32 days */ 
+    398668,                            /* 110.74 hours, 4.61 days */ 
+    426234,                            /* 118.40 hours, 4.93 days */ 
+    455705,                            /* 126.58 hours, 5.27 days */ 
+    487215,                            /* 135.34 hours, 5.64 days */ 
+    520904,                            /* 144.70 hours, 6.03 days */ 
+    556921,                            /* 154.70 hours, 6.45 days */ 
+    595430,                            /* 165.40 hours, 6.89 days */ 
+    636601,                            /* 176.83 hours, 7.37 days */ 
+    680618,                            /* 189.06 hours, 7.88 days */ 
+    727680,                            /* 202.13 hours, 8.42 days */ 
+    777995,                            /* 216.11 hours, 9.00 days */ 
+    831789,                            /* 231.05 hours, 9.63 days */ 
+    889303,                            /* 247.03 hours, 10.29 days */ 
+    950794,                            /* 264.11 hours, 11.00 days */ 
+    1016537,                           /* 282.37 hours, 11.77 days */ 
+    1086825,                           /* 301.90 hours, 12.58 days */ 
+    1161973,                           /* 322.77 hours, 13.45 days */ 
+    1242318,                           /* 345.09 hours, 14.38 days */ 
+    1328218,                           /* 368.95 hours, 15.37 days */ 
+    1420057,                           /* 394.46 hours, 16.44 days */ 
+    1518247,                           /* 421.74 hours, 17.57 days */ 
+    1623226,                           /* 450.90 hours, 18.79 days */ 
+    1735464,                           /* 482.07 hours, 20.09 days */ 
+    1855462,                           /* 515.41 hours, 21.48 days */ 
+    1983758,                           /* 551.04 hours, 22.96 days */ 
+    2120925,                           /* 589.15 hours, 24.55 days */ 
+    2267576,                           /* 629.88 hours, 26.25 days */ 
+    2424367,                           /* 673.44 hours, 28.06 days */ 
+    2592000};                          /* 720.00 hours, 30.00 days */ 
+
+/*
+ * krb_life_to_time - takes a start time and a Kerberos standard
+ * lifetime char and returns the corresponding end time.  There are
+ * four simple cases to be handled.  The first is a life of 0xff,
+ * meaning no expiration, and results in an end time of 0xffffffff.
+ * The second is when life is less than the values covered by the
+ * table.  In this case, the end time is the start time plus the
+ * number of 5 minute intervals specified by life.  The third case
+ * returns start plus the MAXTKTLIFETIME if life is greater than
+ * TKTLIFEMAXFIXED.  The last case, uses the life value (minus
+ * TKTLIFEMINFIXED) as an index into the table to extract the lifetime
+ * in seconds, which is added to start to produce the end time.
+ */
+u_int32_t krb_life_to_time(start, life)
+u_int32_t start;
+int life;
+{
+    life = (unsigned char) life;
+    if (life == TKTLIFENOEXPIRE) return NEVERDATE;
+    if (life < TKTLIFEMINFIXED) return start + life*5*60;
+    if (life > TKTLIFEMAXFIXED) return start + MAXTKTLIFETIME;
+    return start + tkt_lifetimes[life - TKTLIFEMINFIXED];
+}
+
+/*
+ * krb_time_to_life - takes start and end times for the ticket and
+ * returns a Kerberos standard lifetime char, possibily using the
+ * tkt_lifetimes table for lifetimes above 127*5 minutes.  First, the
+ * special case of (end == NEVERDATE) is handled to mean no
+ * expiration.  Then negative lifetimes and those greater than the
+ * maximum ticket lifetime are rejected.  Then lifetimes less than the
+ * first table entry are handled by rounding the requested lifetime
+ * *up* to the next 5 minute interval.  The final step is to search
+ * the table for the smallest entry *greater than or equal* to the
+ * requested entry.
+ */
+int krb_time_to_life(start, end)
+u_int32_t start;
+u_int32_t end;
+{
+    int32_t lifetime;
+    int i;
+
+    if (end == NEVERDATE) return TKTLIFENOEXPIRE;
+    lifetime = end - start;
+    if (lifetime > MAXTKTLIFETIME || lifetime <= 0) return 0;
+    if (lifetime < tkt_lifetimes[0]) return (lifetime + 5*60 - 1)/(5*60);
+    for (i=0; i<TKTLIFENUMFIXED; i++) {
+       if (lifetime <= tkt_lifetimes[i]) {
+           return i+TKTLIFEMINFIXED;
+       }
+    }
+    return 0;
+}
diff --git a/etc/uams/uams_krb4/send_to_kdc.c b/etc/uams/uams_krb4/send_to_kdc.c
new file mode 100644 (file)
index 0000000..a2aae87
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+/*
+ * $Source: /home/ralph/netatalk/rsync/netatalk/etc/uams/uams_krb4/send_to_kdc.c,v $
+ * $Author: rufustfirefly $
+ *
+ * Copyright 1987, 1988 by the Massachusetts Institute of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ */
+
+#ifndef lint
+static char rcsid_send_to_kdc_c[] =
+"$Id: send_to_kdc.c,v 1.1 2000-07-25 21:09:02 rufustfirefly Exp $";
+#endif /* lint */
+
+# ifdef UAM_AFSKRB
+
+#include <mit-copyright.h>
+
+#include <krb.h>
+#include <prot.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#ifdef lint
+#include <sys/uio.h>            /* struct iovec to make lint happy */
+#endif /* lint */
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <string.h>
+
+#define S_AD_SZ sizeof(struct sockaddr_in)
+
+extern int krb_debug;
+
+extern char *malloc(), *calloc(), *realloc();
+
+int krb_udp_port = 0;
+
+/* CLIENT_KRB_TIMEOUT indicates the time to wait before
+ * retrying a server.  It's defined in "krb.h".
+ */
+static struct timeval timeout = { CLIENT_KRB_TIMEOUT, 0};
+static char *prog = "send_to_kdc";
+static send_recv();
+
+/*
+ * This file contains two routines, send_to_kdc() and send_recv().
+ * send_recv() is a static routine used by send_to_kdc().
+ */
+
+/*
+ * send_to_kdc() sends a message to the Kerberos authentication
+ * server(s) in the given realm and returns the reply message.
+ * The "pkt" argument points to the message to be sent to Kerberos;
+ * the "rpkt" argument will be filled in with Kerberos' reply.
+ * The "realm" argument indicates the realm of the Kerberos server(s)
+ * to transact with.  If the realm is null, the local realm is used.
+ *
+ * If more than one Kerberos server is known for a given realm,
+ * different servers will be queried until one of them replies.
+ * Several attempts (retries) are made for each server before
+ * giving up entirely.
+ *
+ * If an answer was received from a Kerberos host, KSUCCESS is
+ * returned.  The following errors can be returned:
+ *
+ * SKDC_CANT    - can't get local realm
+ *              - can't find "kerberos" in /etc/services database
+ *              - can't open socket
+ *              - can't bind socket
+ *              - all ports in use
+ *              - couldn't find any Kerberos host
+ *
+ * SKDC_RETRY   - couldn't get an answer from any Kerberos server,
+ *               after several retries
+ */
+
+send_to_kdc(pkt,rpkt,realm)
+    KTEXT pkt;
+    KTEXT rpkt;
+    char *realm;
+{
+    int i, f;
+    int no_host; /* was a kerberos host found? */
+    int retry;
+    int n_hosts;
+    int retval;
+    struct sockaddr_in to;
+    struct hostent *host, *hostlist;
+    char *cp;
+    char krbhst[MAX_HSTNM];
+    char lrealm[REALM_SZ];
+
+    /*
+     * If "realm" is non-null, use that, otherwise get the
+     * local realm.
+     */
+    if (realm)
+       (void) strcpy(lrealm, realm);
+    else
+       if (krb_get_lrealm(lrealm,1)) {
+           if (krb_debug)
+               fprintf(stderr, "%s: can't get local realm\n", prog);
+           return(SKDC_CANT);
+       }
+    if (krb_debug)
+        printf("lrealm is %s\n", lrealm);
+    if (krb_udp_port == 0) {
+        register struct servent *sp;
+        if ((sp = getservbyname("kerberos","udp")) == 0) {
+            if (krb_debug)
+                fprintf(stderr, "%s: Can't get kerberos/udp service\n",
+                        prog);
+            return(SKDC_CANT);
+        }
+        krb_udp_port = sp->s_port;
+        if (krb_debug)
+            printf("krb_udp_port is %d\n", krb_udp_port);
+    }
+    memset(&to, 0, S_AD_SZ);
+    hostlist = (struct hostent *) malloc(sizeof(struct hostent));
+    if (!hostlist)
+        return (/*errno */SKDC_CANT);
+    if ((f = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+        if (krb_debug)
+            fprintf(stderr,"%s: Can't open socket\n", prog);
+        return(SKDC_CANT);
+    }
+    /* from now on, exit through rtn label for cleanup */
+
+    no_host = 1;
+    /* get an initial allocation */
+    n_hosts = 0;
+    for (i = 1; krb_get_krbhst(krbhst, lrealm, i) == KSUCCESS; ++i) {
+        if (krb_debug) {
+            printf("Getting host entry for %s...",krbhst);
+            (void) fflush(stdout);
+        }
+        host = gethostbyname(krbhst);
+        if (krb_debug) {
+            printf("%s.\n",
+                   host ? "Got it" : "Didn't get it");
+            (void) fflush(stdout);
+        }
+        if (!host)
+            continue;
+        no_host = 0;    /* found at least one */
+        n_hosts++;
+        /* preserve host network address to check later
+         * (would be better to preserve *all* addresses,
+         * take care of that later)
+         */
+        hostlist = (struct hostent *)
+            realloc((char *)hostlist,
+                    (unsigned)
+                    sizeof(struct hostent)*(n_hosts+1));
+        if (!hostlist)
+            return /*errno */SKDC_CANT;
+        memcpy(&hostlist[n_hosts-1], host, sizeof(struct hostent));
+        host = &hostlist[n_hosts-1];
+        cp = malloc((unsigned)host->h_length);
+        if (!cp) {
+            retval = /*errno */SKDC_CANT;
+            goto rtn;
+        }
+        memcpy(cp, host->h_addr, host->h_length);
+/* At least Sun OS version 3.2 (or worse) and Ultrix version 2.2
+   (or worse) only return one name ... */
+#if !(defined(ULTRIX022) || (defined(SunOS) && SunOS < 40))
+        host->h_addr_list = (char **)malloc(sizeof(char *));
+        if (!host->h_addr_list) {
+            retval = /*errno */SKDC_CANT;
+            goto rtn;
+        }
+#endif /* ULTRIX022 || SunOS */
+        host->h_addr = cp;
+        memset(&hostlist[n_hosts], 0, sizeof(struct hostent));
+        to.sin_family = host->h_addrtype;
+        memcpy(&to.sin_addr, host->h_addr, host->h_length);
+        to.sin_port = krb_udp_port;
+        if (send_recv(pkt, rpkt, f, &to, hostlist)) {
+            retval = KSUCCESS;
+            goto rtn;
+        }
+        if (krb_debug) {
+            printf("Timeout, error, or wrong descriptor\n");
+            (void) fflush(stdout);
+        }
+    }
+    if (no_host) {
+       if (krb_debug)
+           fprintf(stderr, "%s: can't find any Kerberos host.\n",
+                   prog);
+        retval = SKDC_CANT;
+        goto rtn;
+    }
+    /* retry each host in sequence */
+    for (retry = 0; retry < CLIENT_KRB_RETRY; ++retry) {
+        for (host = hostlist; host->h_name != (char *)NULL; host++) {
+            to.sin_family = host->h_addrtype;
+            memcpy(&to.sin_addr, host->h_addr, host->h_length);
+            if (send_recv(pkt, rpkt, f, &to, hostlist)) {
+                retval = KSUCCESS;
+                goto rtn;
+            }
+        }
+    }
+    retval = SKDC_RETRY;
+rtn:
+    (void) close(f);
+    if (hostlist) {
+        register struct hostent *hp;
+        for (hp = hostlist; hp->h_name; hp++)
+#if !(defined(ULTRIX022) || (defined(SunOS) && SunOS < 40))
+            if (hp->h_addr_list) {
+#endif /* ULTRIX022 || SunOS */
+                if (hp->h_addr)
+                    free(hp->h_addr);
+#if !(defined(ULTRIX022) || (defined(SunOS) && SunOS < 40))
+                free((char *)hp->h_addr_list);
+            }
+#endif /* ULTRIX022 || SunOS */
+        free((char *)hostlist);
+    }
+    return(retval);
+}
+
+/*
+ * try to send out and receive message.
+ * return 1 on success, 0 on failure
+ */
+
+static send_recv(pkt,rpkt,f,_to,addrs)
+    KTEXT pkt;
+    KTEXT rpkt;
+    int f;
+    struct sockaddr_in *_to;
+    struct hostent *addrs;
+{
+    fd_set readfds;
+    register struct hostent *hp;
+    struct sockaddr_in from;
+    int sin_size, rc;
+    int numsent;
+
+    if (krb_debug) {
+        if (_to->sin_family == AF_INET)
+            printf("Sending message to %s...",
+                   inet_ntoa(_to->sin_addr));
+        else
+            printf("Sending message...");
+        (void) fflush(stdout);
+    }
+    if ((numsent = sendto(f,(char *)(pkt->dat), pkt->length, 0, 
+                         (struct sockaddr *)_to,
+                          S_AD_SZ)) != pkt->length) {
+        if (krb_debug)
+            printf("sent only %d/%d\n",numsent, pkt->length);
+        return 0;
+    }
+    if (krb_debug) {
+        printf("Sent\nWaiting for reply...");
+        (void) fflush(stdout);
+    }
+    FD_ZERO(&readfds);
+    FD_SET(f, &readfds);
+    errno = 0;
+    /* select - either recv is ready, or timeout */
+    /* see if timeout or error or wrong descriptor */
+    if (select(f + 1, &readfds, (fd_set *)0, (fd_set *)0, &timeout) < 1
+        || !FD_ISSET(f, &readfds)) {
+        if (krb_debug) {
+            fprintf(stderr, "select failed: readfds=%x",
+                    readfds);
+            perror("");
+        }
+        return 0;
+    }
+    sin_size = sizeof(from);
+    if (( rc = recvfrom(f, (char *)(rpkt->dat), sizeof(rpkt->dat), 0,
+                (struct sockaddr *)&from, &sin_size)) < 0) {
+        if (krb_debug)
+            perror("recvfrom");
+        return 0;
+    }
+    rpkt->length = rc;
+    if (krb_debug) {
+        printf("received packet from %s\n", inet_ntoa(from.sin_addr));
+        fflush(stdout);
+    }
+    for (hp = addrs; hp->h_name != (char *)NULL; hp++) {
+        if (!memcmp(hp->h_addr, (char *)&from.sin_addr.s_addr,
+                  hp->h_length)) {
+            if (krb_debug) {
+                printf("Received it\n");
+                (void) fflush(stdout);
+            }
+            return 1;
+        }
+        if (krb_debug)
+            fprintf(stderr,
+                    "packet not from %x\n",
+                    hp->h_addr);
+    }
+    if (krb_debug)
+        fprintf(stderr, "%s: received packet from wrong host! (%x)\n",
+                "send_to_kdc(send_rcv)", from.sin_addr.s_addr);
+    return 0;
+}
+# endif UAM_AFSKRB
diff --git a/etc/uams/uams_krb4/uams_krb4.c b/etc/uams/uams_krb4/uams_krb4.c
new file mode 100644 (file)
index 0000000..e0cd40a
--- /dev/null
@@ -0,0 +1,590 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+
+#if defined( KRB ) || defined( UAM_AFSKRB )
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <limits.h>
+#include <string.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <syslog.h>
+#include <netinet/in.h>
+#include <des.h>
+#include <krb.h>
+#include <prot.h>
+
+#include <netatalk/endian.h>
+#include <atalk/afp.h>
+#include <atalk/compat.h>
+#include <atalk/util.h>
+
+static C_Block                 seskey;
+static Key_schedule            seskeysched;
+
+static char            realm[ REALM_SZ ];
+
+#ifdef UAM_AFSKRB
+static int             validseskey = 0;
+static int             logged = 0;
+static char            *tktfile;
+static char            instance[ INST_SZ ], name[ ANAME_SZ ];
+#endif /*UAM_AFSKRB*/
+
+#ifdef AFS
+#include <afs/stds.h>
+#include <rx/rxkad.h>
+#include <afs/afs.h>
+#include <afs/venus.h>
+#include <afs/afsint.h>
+
+char *ka_LocalCell();
+
+struct ClearToken {
+    int32_t AuthHandle;
+    char HandShakeKey[8];
+    int32_t ViceId;
+    int32_t BeginTimestamp;
+    int32_t EndTimestamp;
+};
+#endif /*AFS*/
+
+
+#ifdef KRB
+
+static __inline__ void lcase( p )
+    char       *p;
+{
+    for (; *p; p++ ) {
+       if ( isupper( *p )) {
+           *p = tolower( *p );
+       }
+    }
+    return;
+}
+
+static __inline__ void ucase( p )
+    char       *p;
+{
+    for (; *p; p++ ) {
+       if ( islower( *p )) {
+           *p = toupper( *p );
+       }
+    }
+    return;
+}
+
+#define KRB4CMD_HELO   1
+#define KRB4RPL_REALM  2
+#define KRB4WRT_SESS   3
+#define KRB4RPL_DONE   4
+#define KRB4RPL_PRINC  5
+#define KRB4WRT_TOKEN  6
+#define KRB4WRT_SKIP   7
+#define KRB4RPL_DONEMUT        8
+
+
+static int krb4_login(void *obj, struct passwd **uam_pwd,
+                     char *ibuf, int ibuflen,
+                     char *rbuf, int *rbuflen )
+{
+    char               *p;
+    int                        len;
+
+    *rbuflen = 0;
+    if ( *ibuf != KRB4CMD_HELO ) {
+       syslog( LOG_INFO, "krb4_login: bad command %d", *ibuf );
+       return( AFPERR_NOTAUTH );
+    }
+
+    p = rbuf;
+    if ( krb_get_lrealm( realm, 1 ) != KSUCCESS ) {
+       syslog( LOG_ERR, "krb4_login: can't get local realm!" );
+       return( AFPERR_NOTAUTH );
+    }
+
+    *p++ = KRB4RPL_REALM;
+    *p++ = 1;
+    len = strlen( realm );
+    *p++ = len;
+    strcpy( p, realm );
+    p += len + 1;
+
+#ifdef AFS
+    if ( setpag() < 0 ) {
+       syslog( LOG_ERR, "krb_login: setpag: %m" );
+       return( AFPERR_BADUAM );
+    }
+#endif /*AFS*/
+
+    *rbuflen = p - rbuf;
+    return( AFPERR_AUTHCONT );
+}
+
+static int krb4_logincont(void *obj, struct passwd **uam_pwd,
+                         char *ibuf, int ibuflen,
+                         char *rbuf, int *rbuflen)
+{
+    static struct passwd       *pwd;
+    KTEXT_ST           tkt;
+    static AUTH_DAT    ad;
+    int                        rc;
+    u_int16_t          len;
+    char               *p, *username;
+    CREDENTIALS                cr;
+#ifdef AFS
+    struct ViceIoctl   vi;
+    struct ClearToken  ct;
+#endif /*AFS*/
+    char               buf[ 1024 ];
+    int                        aint, ulen;
+
+    if (uam_afp_read(obj, rbuf, rbuflen) < 0) /* read in the rest. */
+      return AFPERR_PARAM;
+
+    *rbuflen = 0;
+    if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, &username) < 0)
+      return AFPERR_MISC;
+    
+    if (uam_afpserver_option(obj, UAM_OPTION_USERNAMELEN, &ulen) < 0)
+      return AFPERR_MISC;
+
+    p = rbuf;
+
+    switch ( rc = *p++ ) {
+    case KRB4WRT_SESS :
+       memcpy( &len, p, sizeof( len ));
+       tkt.length = ntohs( len );
+       p += sizeof( short );
+
+       if ( tkt.length <= 0 || tkt.length > MAX_KTXT_LEN ) {
+           return( AFPERR_BADUAM );
+       }
+       memcpy( tkt.dat, p, tkt.length );
+       p += tkt.length;
+
+       if (( rc = krb_rd_req( &tkt, "afpserver", obj->Obj, 0, &ad, "" ))
+               != RD_AP_OK ) {
+           syslog( LOG_ERR, "krb4_logincont: krb_rd_req: %s",
+                   krb_err_txt[ rc ] );
+           return( AFPERR_BADUAM );
+       }
+
+       syslog( LOG_INFO, "krb4_login: %s.%s@%s", ad.pname, ad.pinst,
+               ad.prealm );
+       memcpy(realm, ad.prealm, sizeof(realm));
+       memcpy(seskey, ad.session, sizeof( C_Block ));
+       key_sched((C_Block *) seskey, seskeysched );
+
+       strncpy(username, ad.pname, ulen);
+
+       p = rbuf;
+#ifndef AFS
+       *p = KRB4RPL_DONE;      /* XXX */
+       *rbuflen = 1;
+
+       if (( pwd = uam_getname( ad.pname, strlen(ad.pname) )) == NULL ) {
+           return( AFPERR_PARAM );
+       }
+       *uam_pwd = pwd;
+       return AFP_OK;
+#else AFS
+       /* get principals */
+       *p++ = KRB4RPL_PRINC;
+       len = strlen( realm );
+       *p++ = len + 1;
+       *p++ = '@';
+       strcpy( p, realm );
+       p += len + 1;
+       *rbuflen = p - rbuf;
+       return( AFPERR_AUTHCONT );
+
+    case KRB4WRT_TOKEN :
+       memcpy( &len, p, sizeof( len ));
+       len = ntohs( len );
+       p += sizeof( len );
+       memcpy( &cr, p, len );
+
+       pcbc_encrypt((C_Block *)&cr, (C_Block *)&cr, len, seskeysched,
+               seskey, DES_DECRYPT );
+
+       p = buf;
+       cr.ticket_st.length = ntohl( cr.ticket_st.length );
+       memcpy( p, &cr.ticket_st.length, sizeof( int ));
+       p += sizeof( int );
+       memcpy( p, cr.ticket_st.dat, cr.ticket_st.length );
+       p += cr.ticket_st.length;
+
+       ct.AuthHandle = ntohl( cr.kvno );
+       memcpy( ct.HandShakeKey, cr.session, sizeof( cr.session ));
+       ct.ViceId = 0;
+       ct.BeginTimestamp = ntohl( cr.issue_date );
+       ct.EndTimestamp = krb_life_to_time( ntohl( cr.issue_date ),
+               ntohl( cr.lifetime ));
+
+       aint = sizeof( struct ClearToken );
+       memcpy( p, &aint, sizeof( int ));
+       p += sizeof( int );
+       memcpy( p, &ct, sizeof( struct ClearToken ));
+       p += sizeof( struct ClearToken );
+
+       aint = 0;
+       memcpy( p, &aint, sizeof( int ));
+       p += sizeof( int );
+
+       lcase( realm );
+       strcpy( p, realm );
+       p += strlen( realm ) + 1;
+
+       vi.in = buf;
+       vi.in_size = p - buf;
+       vi.out = buf;
+       vi.out_size = sizeof( buf );
+       if ( pioctl( 0, VIOCSETTOK, &vi, 0 ) < 0 ) {
+           syslog( LOG_ERR, "krb4_logincont: pioctl: %m" );
+           return( AFPERR_BADUAM );
+       }
+       /* FALL THROUGH */
+
+    case KRB4WRT_SKIP :
+       p = rbuf;
+       *p = KRB4RPL_DONE;      /* XXX */
+       *rbuflen = 1;
+
+       if (( pwd = uam_getname( ad.pname, strlen(ad.pname) )) == NULL ) {
+           return( AFPERR_PARAM );
+       }
+       *uam_pwd = pwd;
+       return AFP_OK;
+#endif /*AFS*/
+
+    default :
+       syslog( LOG_INFO, "krb4_logincont: bad command %d", rc );
+       return( AFPERR_NOTAUTH );
+       break;
+    }
+}
+
+#endif /*KRB*/
+
+
+#ifdef AFS
+#include <rx/rxkad.h>
+#include <afs/afsint.h>
+
+char *ka_LocalCell();
+
+static void
+addrealm(realm,cells)
+    char *realm;
+       char ***cells;
+{
+    char **ptr;
+       int temp;
+
+       ptr= *cells;
+
+    for(;*ptr != 0 ;ptr++)
+        if(!strcmp(realm,*ptr))
+            return;
+
+       temp=ptr- *cells;
+       *cells=(char**)realloc(*cells,((2+temp)*sizeof(char*)));
+       ptr= *cells+temp;
+
+    *ptr=(char*)malloc(strlen(realm)+1);
+    strcpy(*ptr++,realm);
+       *ptr=0;
+    return;
+}
+
+static int kcheckuser(pwd,passwd)
+       struct passwd *pwd;
+       char *passwd;
+{
+       int32_t code;
+       char *instance="";
+       char realm[MAXKTCREALMLEN];
+       char lorealm[MAXKTCREALMLEN];
+       char *cell;
+       Date lifetime=MAXKTCTICKETLIFETIME;
+       int rval;
+       char **cells=(char **)malloc(sizeof(char*));
+       char *temp;
+       int rc,cellNum;
+       struct ktc_principal serviceName;
+
+       *cells=0;
+
+       code = ka_Init(0);
+
+       {
+               char *temp,*temp1;
+               temp=(char*)malloc(strlen(pwd->pw_dir)+1);
+               strcpy(temp,pwd->pw_dir);
+               temp1=temp;
+               temp=strtok(temp,"/");
+               temp=strtok('\0',"/");
+               ka_CellToRealm(temp,realm,0);
+               addrealm(realm,&cells);
+               free(temp1);
+       }
+
+       setpag();
+       authenticate(cells,pwd->pw_name,passwd);
+       cellNum=0;
+       rc=ktc_ListTokens(cellNum,&cellNum,&serviceName);
+       if(rc)
+               rval=1;
+       else{
+               rval=0;
+       }
+
+       return(rval);
+}
+
+static void authenticate(cells,name,passwd)
+       char **cells;
+       char *name;
+       char *passwd;
+{
+       char **ptr=cells;
+       char *errorstring;
+
+       while(*ptr){
+           ka_UserAuthenticate(name,/*instance*/"",/*cell*/*ptr++,
+                   passwd,/*setpag*/0,&errorstring);
+       }
+}
+#endif /*AFS*/
+
+#if defined( UAM_AFSKRB ) && defined( AFS )
+static int afskrb_login(void *obj, struct passwd *uam_pwd,
+                       char *ibuf, int ibuflen, 
+                       char *rbuf, int *rbuflen )
+{
+    KTEXT_ST   authent, rpkt;
+    CREDENTIALS        cr;
+    char       *p, *q, *username;
+    int                len, rc, whoserealm, ulen;
+    short      slen;
+
+    *rbuflen = 0;
+    if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, &username) < 0)
+      return AFPERR_MISC;
+    
+    if (uam_afpserver_option(obj, UAM_OPTION_USERNAMELEN, &ulen) < 0)
+      return AFPERR_MISC;
+
+    len = (unsigned char) *ibuf++;
+    ibuf[ len ] = '\0';
+    if (( p = strchr( ibuf, '@' )) != NULL ) {
+       *p++ = '\0';
+       strcpy( realm, p );
+       ucase( realm );
+       whoserealm = 0;
+    } else {
+       if ( krb_get_lrealm( realm, 1 ) != KSUCCESS ) {
+           return AFPERR_BADUAM;
+       }
+       whoserealm = 1;
+    }
+    if (( p = strchr( ibuf, '.' )) != NULL ) {
+       *p++ = '\0';
+       strcpy( instance, p );
+    } else {
+       *instance = '\0';
+    }
+    strcpy( name, ibuf );
+    /*
+     * We don't have the session key, yet. Get one.
+     */
+    p = rbuf;
+    if ( validseskey == 0 ) {
+       if ( setpag() < 0 ) {
+           syslog( LOG_ERR, "krb_login: setpag: %m" );
+           return AFPERR_BADUAM;
+       }
+       krb_set_tkt_string(( tktfile = mktemp( _PATH_AFPTKT )));
+       if (( rc =  krb_get_svc_in_tkt( "afpserver", Obj, realm,
+               TICKET_GRANTING_TICKET, realm, 255, KEYFILE )) != INTK_OK ) {
+           syslog( LOG_ERR, "krb_login: can't get ticket-granting-ticket" );
+           return (( whoserealm ) ? AFPERR_BADUAM : AFPERR_PARAM );
+       }
+       if ( krb_mk_req( &authent, name, instance, realm, 0 ) != KSUCCESS ) {
+           return ( AFPERR_PARAM );
+       }
+       if ( krb_get_cred( name, instance, realm, &cr ) != KSUCCESS ) {
+           return ( AFPERR_BADUAM );
+       }
+
+       if ( unlink( tktfile ) < 0 ) {
+           syslog( LOG_ERR, "krb_login: unlink %s: %m", tktfile );
+           return ( AFPERR_BADUAM );
+       }
+
+       memcpy( seskey, cr.session, sizeof( C_Block ));
+       key_sched((C_Block *) seskey, seskeysched );
+       validseskey = 1;
+       strncpy(username, name, ulen);
+
+       memcpy( p, authent.dat, authent.length );
+       p += authent.length;
+    }
+
+    if ( kuam_get_in_tkt( name, instance, realm, TICKET_GRANTING_TICKET,
+           realm, 255, &rpkt ) != INTK_OK ) {
+       return ( AFPERR_PARAM );
+    }
+
+
+    q = (char *)rpkt.dat;
+    *p++ = *q++;
+    *p++ = *q++;
+    while ( *q++ )
+       ;
+    while ( *q++ )
+       ;
+    while ( *q++ )
+       ;
+    q += 10;
+
+    len = strlen( realm );
+    strcpy( p, realm );
+    p += len + 1;
+    memcpy( &slen, q, sizeof( short ));
+    memcpy( p, &slen, sizeof( short ));
+    p += sizeof( short );
+    q += sizeof( short );
+    memcpy( p, q, slen );
+    p += slen;
+
+    *rbuflen = p - rbuf;
+    return( AFPERR_AUTHCONT );
+}
+
+static int afskrb_logincont(void *obj, struct passwd *uam_pwd,
+                           char *ibuf, int ibuflen, 
+                           char *rbuf, int *rbuflen )
+{
+    CREDENTIALS                cr;
+    struct ViceIoctl   vi;
+    struct ClearToken  ct;
+    struct passwd      *pwd;
+    char               buf[ 1024 ], *p;
+    int                        aint;
+    short              clen;
+
+    *rbuflen = 0;
+    if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, &username) < 0)
+      return AFPERR_MISC;
+    
+    ibuf += 2;
+    memcpy( &clen, ibuf, sizeof( short ));
+    clen = ntohs( clen );
+    ibuf += sizeof( short );
+
+    pcbc_encrypt((C_Block *)ibuf, (C_Block *)ibuf,
+           clen, seskeysched, seskey, DES_DECRYPT );
+    if ( kuam_set_in_tkt( name, instance, realm, TICKET_GRANTING_TICKET,
+           realm, ibuf ) != INTK_OK ) {
+       return ( AFPERR_PARAM );
+    }
+
+    if ( get_ad_tkt( "afs", "", realm, 255 ) != KSUCCESS ) {
+       return ( AFPERR_PARAM );
+    }
+    if ( krb_get_cred( "afs", "", realm, &cr ) != KSUCCESS ) {
+       return ( AFPERR_PARAM );
+    }
+
+    p = buf;
+    memcpy( p, &cr.ticket_st.length, sizeof( int ));
+    p += sizeof( int );
+    memcpy( p, cr.ticket_st.dat, cr.ticket_st.length );
+    p += cr.ticket_st.length;
+
+    ct.AuthHandle = cr.kvno;
+    memcpy( ct.HandShakeKey, cr.session, sizeof( cr.session ));
+    ct.ViceId = 0;
+    ct.BeginTimestamp = cr.issue_date;
+    /* ct.EndTimestamp = cr.issue_date + ( cr.lifetime * 5 * 60 ); */
+    ct.EndTimestamp = krb_life_to_time( cr.issue_date, cr.lifetime );
+
+    aint = sizeof( struct ClearToken );
+    memcpy( p, &aint, sizeof( int ));
+    p += sizeof( int );
+    memcpy( p, &ct, sizeof( struct ClearToken ));
+    p += sizeof( struct ClearToken );
+
+    aint = 0;
+    memcpy( p, &aint, sizeof( int ));
+    p += sizeof( int );
+
+    lcase( realm );
+    strcpy( p, realm );
+    p += strlen( realm ) + 1;
+
+    vi.in = buf;
+    vi.in_size = p - buf;
+    vi.out = buf;
+    vi.out_size = sizeof( buf );
+    if ( pioctl( 0, VIOCSETTOK, &vi, 0 ) < 0 ) {
+       syslog( LOG_ERR, "krb_logincont: pioctl: %m" );
+       return ( AFPERR_BADUAM );
+    }
+
+    if ( unlink( tktfile ) < 0 ) {
+       syslog( LOG_ERR, "krb_logincont: %s: %m", tktfile );
+       return ( AFPERR_BADUAM );
+    }
+
+    if (( pwd = uam_getname( username )) == NULL ) {
+       return ( AFPERR_PARAM );
+    }
+
+    if ( logged == 0 ) {
+       logged = 1;
+       syslog( LOG_INFO, "authenticated %s.%s@%s", name, instance, realm );
+       *uam_pwd = pwd;
+       return AFP_OK;
+    }
+    syslog( LOG_INFO, "re-authenticated %s.%s@%s", name, instance, realm );
+    return( AFP_OK );
+}
+#endif /* UAM_AFSKRB AFS */
+
+int uam_setup()
+{
+#ifdef KRB
+   uam_register(UAM_SERVER_LOGIN, "Kerberos IV", krb4_login, 
+               krb4_logincont, NULL);
+   /* uam_afpserver_action(); */
+#endif
+#ifdef UAM_AFSKRB
+   uam_register(UAM_SERVER_LOGIN, "AFS Kerberos", afskrb_login, 
+               afskrb_logincont, NULL);
+   /* uam_afpserver_action(); */
+#endif
+}
+
+int uam_cleanup()
+{
+#ifdef KRB
+   /* uam_afpserver_action(); */
+   uam_unregister(UAM_SERVER_LOGIN, "Kerberos IV");
+#endif
+#ifdef UAM_AFSKRB
+   /* uam_afpserver_action(); */
+   uam_unregister(UAM_SERVER_LOGIN, "AFS Kerberos");
+#endif
+}
+
+#endif /* KRB or UAM_AFSKRB */
diff --git a/etc/uams/uams_pam.c b/etc/uams/uams_pam.c
new file mode 100644 (file)
index 0000000..3475913
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu) 
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+#ifdef USE_PAM
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+
+#include <security/pam_appl.h>
+
+#include <atalk/afp.h>
+#include <atalk/uam.h>
+
+#define PASSWDLEN 8
+
+/* Static variables used to communicate between the conversation function
+ * and the server_login function
+ */
+static pam_handle_t *pamh = NULL; 
+static char *hostname;
+static char *PAM_username;
+static char *PAM_password;
+
+/* PAM conversation function
+ * Here we assume (for now, at least) that echo on means login name, and
+ * echo off means password.
+ */
+static int PAM_conv (int num_msg,
+                     const struct pam_message **msg,
+                     struct pam_response **resp,
+                     void *appdata_ptr) 
+{
+  struct pam_response *reply;
+  int count;
+  
+#define COPY_STRING(s) (s) ? strdup(s) : NULL
+  
+  if (num_msg < 1)
+    return PAM_CONV_ERR;
+
+  reply = (struct pam_response *) 
+    calloc(num_msg, sizeof(struct pam_response));
+
+  if (!reply)
+    return PAM_CONV_ERR;
+
+  for (count = 0; count < num_msg; count++) {
+    char *string = NULL;
+
+    switch (msg[count]->msg_style) {
+    case PAM_PROMPT_ECHO_ON:
+      if (!(string = COPY_STRING(PAM_username)))
+       goto pam_fail_conv;
+      break;
+    case PAM_PROMPT_ECHO_OFF:
+      if (!(string = COPY_STRING(PAM_password)))
+       goto pam_fail_conv;
+      break;
+    case PAM_TEXT_INFO:
+#ifdef PAM_BINARY_PROMPT
+    case PAM_BINARY_PROMPT:
+#endif
+      /* ignore it... */
+      break;
+    case PAM_ERROR_MSG:
+    default:
+      goto pam_fail_conv;
+    }
+
+    if (string) {  
+      reply[count].resp_retcode = 0;
+      reply[count].resp = string;
+      string = NULL;
+    }
+  }
+
+  *resp = reply;
+  return PAM_SUCCESS;
+
+pam_fail_conv:
+  for (count = 0; count < num_msg; count++) {
+    if (!reply[count].resp)
+      continue;
+    switch (msg[count]->msg_style) {
+    case PAM_PROMPT_ECHO_OFF:
+    case PAM_PROMPT_ECHO_ON:
+      free(reply[count].resp);
+      break;      
+    }
+  }
+  free(reply);
+  return PAM_CONV_ERR;
+}
+
+static struct pam_conv PAM_conversation = {
+  &PAM_conv,
+  NULL
+};
+
+
+/* cleartxt login */
+static int pam_login(void *obj, struct passwd **uam_pwd,
+                    char *ibuf, int ibuflen,
+                    char *rbuf, int *rbuflen)
+{
+    struct passwd *pwd;
+    char *username; 
+    int err, len, ulen, PAM_error;
+
+    *rbuflen = 0;
+
+    if (uam_afpserver_option(obj, UAM_OPTION_USERNAME,
+                            (void *) &username, &ulen) < 0)
+      return AFPERR_MISC;
+
+    if (uam_afpserver_option(obj, UAM_OPTION_HOSTNAME,
+                            (void *) &hostname, NULL) < 0)
+      return AFPERR_MISC;
+
+    len = (unsigned char) *ibuf++;
+    if ( len > ulen ) {
+       return( AFPERR_PARAM );
+    }
+
+    memcpy(username, ibuf, len );
+    ibuf += len;
+    username[ len ] = '\0';
+    if ((unsigned long) ibuf & 1)  /* pad character */
+      ++ibuf;
+    ibuf[ PASSWDLEN ] = '\0';
+
+    if (( pwd = uam_getname(username, ulen)) == NULL ) {
+       return AFPERR_PARAM;
+    }
+
+    syslog(LOG_INFO, "cleartext login: %s", username);
+    PAM_username = username;
+    PAM_password = ibuf; /* Set these things up for the conv function */
+
+    err = AFPERR_NOTAUTH;
+    PAM_error = pam_start("netatalk", username, &PAM_conversation,
+                         &pamh);
+    if (PAM_error != PAM_SUCCESS)
+      goto login_err;
+
+    pam_set_item(pamh, PAM_TTY, "afpd");
+    pam_set_item(pamh, PAM_RHOST, hostname);
+    /* use PAM_DISALLOW_NULL_AUTHTOK if passwdminlen > 0 */
+    PAM_error = pam_authenticate(pamh,0);
+    if (PAM_error != PAM_SUCCESS) {
+      if (PAM_error == PAM_MAXTRIES) 
+       err = AFPERR_PWDEXPR;
+      goto login_err;
+    }      
+
+    PAM_error = pam_acct_mgmt(pamh, 0);
+    if (PAM_error != PAM_SUCCESS) {
+      if (PAM_error == PAM_ACCT_EXPIRED)
+       err = AFPERR_PWDEXPR;
+#ifdef PAM_AUTHTOKEN_REQD
+      else if (PAM_error == PAM_AUTHTOKEN_REQD) 
+       err = AFPERR_PWDCHNG;
+#endif
+      goto login_err;
+    }
+
+#ifndef PAM_CRED_ESTABLISH
+#define PAM_CRED_ESTABLISH PAM_ESTABLISH_CRED
+#endif
+    PAM_error = pam_setcred(pamh, PAM_CRED_ESTABLISH);
+    if (PAM_error != PAM_SUCCESS)
+      goto login_err;
+
+    PAM_error = pam_open_session(pamh, 0);
+    if (PAM_error != PAM_SUCCESS)
+      goto login_err;
+
+    *uam_pwd = pwd;
+    return AFP_OK;
+
+login_err:
+    pam_end(pamh, PAM_error);
+    pamh = NULL;
+    return err;
+}
+
+/* logout */
+static void pam_logout() {
+    pam_close_session(pamh, 0);
+    pam_end(pamh, 0);
+    pamh = NULL;
+}
+
+/* change passwd */
+static int pam_changepw(void *obj, char *username,
+                       struct passwd *pwd, char *ibuf, int ibuflen,
+                       char *rbuf, int *rbuflen)
+{
+    char pw[PASSWDLEN + 1];
+    pam_handle_t *lpamh;
+    uid_t uid;
+    int PAM_error;
+
+    /* old password */
+    memcpy(pw, ibuf, PASSWDLEN);
+    memset(ibuf, 0, PASSWDLEN);
+    pw[PASSWDLEN] = '\0';
+
+    /* let's do a quick check for the same password */
+    if (memcmp(pw, ibuf + PASSWDLEN, PASSWDLEN) == 0)
+      return AFPERR_PWDSAME;
+
+    /* Set these things up for the conv function */
+    PAM_username = username;
+    PAM_password = pw; 
+
+    PAM_error = pam_start("netatalk", username, &PAM_conversation,
+                         &lpamh);
+    if (PAM_error != PAM_SUCCESS) 
+      return AFPERR_PARAM;
+    pam_set_item(lpamh, PAM_TTY, "afpd");
+    pam_set_item(lpamh, PAM_RHOST, hostname);
+
+    /* we might need to do this as root */
+    uid = geteuid();
+    seteuid(0);
+    PAM_error = pam_authenticate(lpamh,0);
+    if (PAM_error != PAM_SUCCESS) {
+      seteuid(uid);
+      pam_end(lpamh, PAM_error);
+      return AFPERR_NOTAUTH;
+    }
+
+    /* new password */
+    ibuf += PASSWDLEN;
+    PAM_password = ibuf;
+    ibuf[PASSWDLEN] = '\0';
+    
+    /* this really does need to be done as root */
+    PAM_error = pam_chauthtok(lpamh, 0);
+    seteuid(uid); /* un-root ourselves. */
+    memset(pw, 0, PASSWDLEN);
+    memset(ibuf, 0, PASSWDLEN);
+    if (PAM_error != PAM_SUCCESS) {
+      pam_end(lpamh, PAM_error);
+      return AFPERR_ACCESS;
+    }
+
+    pam_end(lpamh, 0);
+    return AFP_OK;
+}
+
+
+static int uam_setup(const char *path)
+{
+  if (uam_register(UAM_SERVER_LOGIN, path, "Cleartxt Passwrd", 
+                  pam_login, NULL, pam_logout) < 0)
+    return -1;
+
+  if (uam_register(UAM_SERVER_CHANGEPW, path, "Cleartxt Passwrd",
+                  pam_changepw) < 0) {
+    uam_unregister(UAM_SERVER_LOGIN, "Cleartxt Passwrd");
+    return -1;
+  }
+
+  /*uam_register(UAM_SERVER_PRINTAUTH, path, "Cleartxt Passwrd",
+    pam_printer);*/
+
+  return 0;
+}
+
+static void uam_cleanup(void)
+{
+  uam_unregister(UAM_SERVER_LOGIN, "Cleartxt Passwrd");
+  uam_unregister(UAM_SERVER_CHANGEPW, "Cleartxt Passwrd");
+  /*uam_unregister(UAM_SERVER_PRINTAUTH, "Cleartxt Passwrd"); */
+}
+
+UAM_MODULE_EXPORT struct uam_export uams_clrtxt = {
+  UAM_MODULE_SERVER,
+  UAM_MODULE_VERSION,
+  uam_setup, uam_cleanup
+};
+
+#endif /* USE_PAM */
diff --git a/etc/uams/uams_passwd.c b/etc/uams/uams_passwd.c
new file mode 100644 (file)
index 0000000..67699a9
--- /dev/null
@@ -0,0 +1,153 @@
+/* Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu) 
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#ifndef NO_CRYPT_H
+#include <crypt.h>
+#endif
+#include <pwd.h>
+#include <syslog.h>
+
+#ifdef SOLARIS
+#define SHADOWPW
+#endif SOLARIS
+
+#ifdef SHADOWPW
+#include <shadow.h>
+#endif SHADOWPW
+
+#include <atalk/afp.h>
+#include <atalk/uam.h>
+
+#define PASSWDLEN 8
+
+/* cleartxt login */
+static int passwd_login(void *obj, struct passwd **uam_pwd,
+                       char *ibuf, int ibuflen,
+                       char *rbuf, int *rbuflen)
+{
+    struct passwd *pwd;
+#ifdef SHADOWPW
+    struct spwd *sp;
+#endif
+    char *username, *p;
+    int len, ulen;
+
+    *rbuflen = 0;
+
+    if (uam_afpserver_option(obj, UAM_OPTION_USERNAME,
+                            (void *) &username, &ulen) < 0)
+      return AFPERR_MISC;
+
+    len = (unsigned char) *ibuf++;
+    if ( len > ulen ) {
+       return( AFPERR_PARAM );
+    }
+
+    memcpy(username, ibuf, len );
+    ibuf += len;
+    username[ len ] = '\0';
+    if ((unsigned long) ibuf & 1) /* pad character */
+      ++ibuf;
+    ibuf[ PASSWDLEN ] = '\0';
+
+    if (( pwd = uam_getname(username, ulen)) == NULL ) {
+       return AFPERR_PARAM;
+    }
+
+    syslog(LOG_INFO, "cleartext login: %s", username);
+    if (uam_checkuser(pwd) < 0)
+      return AFPERR_NOTAUTH;
+
+#ifdef SHADOWPW
+    if (( sp = getspnam( pwd->pw_name )) == NULL ) {
+       syslog( LOG_INFO, "no shadow passwd entry for %s", username);
+       return AFPERR_NOTAUTH;
+    }
+    pwd->pw_passwd = sp->sp_pwdp;
+#endif SHADOWPW
+
+    if (!pwd->pw_passwd)
+      return AFPERR_NOTAUTH;
+
+    *uam_pwd = pwd;
+
+    p = crypt( ibuf, pwd->pw_passwd );
+    if ( strcmp( p, pwd->pw_passwd ) == 0 ) 
+      return AFP_OK;
+
+    return AFPERR_NOTAUTH;
+}
+
+
+#if 0
+/* change passwd */
+static int passwd_changepw(void *obj, char *username,
+                          struct passwd *pwd, char *ibuf,
+                          int ibuflen, char *rbuf, int *rbuflen)
+{
+#ifdef SHADOWPW
+    struct spwd *sp;
+#endif
+    char pw[PASSWDLEN + 1], *p;
+    uid_t uid = geteuid();
+
+    if (uam_checkuser(pwd) < 0)
+      return AFPERR_ACCESS;
+
+    /* old password */
+    memcpy(pw, ibuf, PASSWDLEN);
+    memset(ibuf, 0, PASSWDLEN);
+    pw[PASSWDLEN] = '\0';
+
+#ifdef SHADOWPW
+    if (( sp = getspnam( pwd->pw_name )) == NULL ) {
+       syslog( LOG_INFO, "no shadow passwd entry for %s", username);
+       return AFPERR_PARAM;
+    }
+    pwd->pw_passwd = sp->sp_pwdp;
+#endif SHADOWPW
+
+    p = crypt(pw, pwd->pw_passwd );
+    if (strcmp( p, pwd->pw_passwd )) {
+      memset(pw, 0, sizeof(pw));
+      return AFPERR_NOTAUTH;
+    }
+
+    /* new password */
+    ibuf += PASSWDLEN;
+    ibuf[PASSWDLEN] = '\0';
+    
+#ifdef SHADOWPW
+#else
+#endif
+    return AFP_OK;
+}
+#endif
+
+static int uam_setup(const char *path)
+{
+  if (uam_register(UAM_SERVER_LOGIN, path, 
+              "Cleartxt Passwrd", passwd_login, NULL, NULL) < 0)
+    return -1;
+  /*uam_register(UAM_SERVER_PRINTAUTH, path, "Cleartxt Passwrd",
+    passwd_printer);*/
+  return 0;
+}
+
+static void uam_cleanup(void)
+{
+  uam_unregister(UAM_SERVER_LOGIN, "Cleartxt Passwrd");
+  /*uam_unregister(UAM_SERVER_PRINTAUTH, "Cleartxt Passwrd"); */
+}
+
+UAM_MODULE_EXPORT struct uam_export uams_clrtxt = {
+  UAM_MODULE_SERVER,
+  UAM_MODULE_VERSION,
+  uam_setup, uam_cleanup
+};
diff --git a/etc/uams/uams_pgp.c b/etc/uams/uams_pgp.c
new file mode 100644 (file)
index 0000000..823b060
--- /dev/null
@@ -0,0 +1,172 @@
+/* Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu) 
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#ifdef UAM_PGP
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <syslog.h>
+
+#include <bn.h>
+
+#include <atalk/afp.h>
+#include <atalk/uam.h>
+
+#define KEYSIZE 16
+#define PASSWDLEN 64
+#define CRYPTBUFLEN  (KEYSIZE*2)
+#define CRYPT2BUFLEN (KEYSIZE + PASSWDLEN)
+
+/* hash a number to a 16-bit quantity */
+#define pgphash(a) ((((unsigned long) (a) >> 8) ^ \
+                    (unsigned long) (a)) & 0xffff)
+
+/* the secret key */
+static struct passwd *pgppwd;
+
+
+/* pgp passwd */
+static int pgp_login(void *obj, struct passwd **uam_pwd,
+                    char *ibuf, int ibuflen,
+                    char *rbuf, int *rbuflen)
+{
+    BIGNUM *bn, *gbn, *pbn;
+    u_int16_t sessid;
+    int len, i;
+    char *name;
+
+    *rbuflen = 0;
+
+    if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, (void *) &name, &i) < 0)
+      return AFPERR_PARAM;
+
+    len = (unsigned char) *ibuf++;
+    if ( len > i ) {
+       return( AFPERR_PARAM );
+    }
+
+    memcpy(name, ibuf, len );
+    ibuf += len;
+    name[ len ] = '\0';
+    if ((unsigned long) ibuf & 1) /* padding */
+      ++ibuf;
+
+    if (( pgppwd = uam_getname(name, i)) == NULL ) {
+      return AFPERR_PARAM;
+    }
+
+    syslog( LOG_INFO, "pgp login: %s", name);
+    if (uam_checkuser(pgppwd) < 0)
+      return AFPERR_NOTAUTH;
+
+    /* get the challenge */
+    len = (unsigned char) *ibuf++;
+    /* challenge */
+    
+    /* get the signature. it's always 16 bytes. */
+    if (uam_afpserver_option(obj, UAM_OPTION_SIGNATURE, 
+                            (void *) &name, NULL) < 0) {
+      *rbuflen = 0;
+      goto passwd_fail;
+    }
+    memcpy(rbuf + KEYSIZE, name, KEYSIZE); 
+
+pgp_fail:
+    return AFPERR_PARAM;
+}
+
+static int pgp_logincont(void *obj, struct passwd **uam_pwd,
+                        char *ibuf, int ibuflen, 
+                        char *rbuf, int *rbuflen)
+{
+    BIGNUM *bn1, *bn2, *bn3;
+    u_int16_t sessid;
+    char *p;
+
+    *rbuflen = 0;
+
+    /* check for session id */
+    memcpy(&sessid, ibuf, sizeof(sessid));
+    if (sessid != pgphash(obj))
+      return AFPERR_PARAM;
+    ibuf += sizeof(sessid);
+   
+    /* use rbuf as scratch space */
+    CAST_cbc_encrypt(ibuf, rbuf, CRYPT2BUFLEN, &castkey,
+                    iv, CAST_DECRYPT);
+    
+    /* check to make sure that the random number is the same. we
+     * get sent back an incremented random number. */
+    if (!(bn1 = BN_bin2bn(rbuf, KEYSIZE, NULL)))
+      return AFPERR_PARAM;
+
+    if (!(bn2 = BN_bin2bn(randbuf, sizeof(randbuf), NULL))) {
+      BN_free(bn1);
+      return AFPERR_PARAM;
+    }
+      
+    /* zero out the random number */
+    memset(rbuf, 0, sizeof(randbuf));
+    memset(randbuf, 0, sizeof(randbuf));
+    rbuf += KEYSIZE;
+
+    if (!(bn3 = BN_new())) {
+      BN_free(bn2);
+      BN_free(bn1);
+      return AFPERR_PARAM;
+    }
+
+    BN_sub(bn3, bn1, bn2);
+    BN_free(bn2);
+    BN_free(bn1);
+
+    /* okay. is it one more? */
+    if (!BN_is_one(bn3)) {
+      BN_free(bn3);
+      return AFPERR_PARAM;
+    }
+    BN_free(bn3);
+
+#ifdef AFS
+    if ( kcheckuser(*uam_pwd, rbuf) == 0) {
+      *uam_pwd = pgppwd;
+      return AFP_OK;
+    }
+#endif AFS
+
+    rbuf[PASSWDLEN] = '\0';
+    p = crypt( rbuf, pgppwd->pw_passwd );
+    memset(rbuf, 0, PASSWDLEN);
+    if ( strcmp( p, pgppwd->pw_passwd ) == 0 ) {
+      *uam_pwd = pgppwd;
+      return AFP_OK;
+    }
+
+    return AFPERR_NOTAUTH;
+}
+
+
+static int uam_setup(const char *path)
+{
+  if (uam_register(UAM_SERVER_LOGIN, path, "PGPuam 1.0",
+                  pgp_login, pgp_logincont, NULL) < 0)
+    return -1;
+
+  return 0;
+}
+
+static void uam_cleanup(void)
+{
+  uam_unregister(UAM_SERVER_LOGIN, "PGPuam 1.0");
+}
+
+UAM_MODULE_EXPORT struct uam_export uams_pgp = {
+  UAM_MODULE_SERVER,
+  UAM_MODULE_VERSION,
+  uam_setup, uam_cleanup
+};
+#endif
diff --git a/etc/uams/uams_randnum.c b/etc/uams/uams_randnum.c
new file mode 100644 (file)
index 0000000..42ac453
--- /dev/null
@@ -0,0 +1,522 @@
+/* 
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu) 
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+
+#include <syslog.h>
+
+#include <netatalk/endian.h>
+
+#include <atalk/afp.h>
+#include <atalk/uam.h>
+
+
+#ifdef UAM_RNDNUM
+#include <des.h>
+
+#ifdef USE_CRACKLIB
+#include <crack.h>
+#endif
+
+#ifndef __inline__
+#define __inline__
+#endif
+
+#define PASSWDLEN 8
+
+static C_Block         seskey;
+static Key_schedule    seskeysched;
+static struct passwd   *randpwd;
+static u_int8_t         randbuf[8];
+
+/* hash to a 16-bit number. this will generate completely harmless 
+ * warnings on 64-bit machines. */
+#define randhash(a) (((((unsigned long) a) >> 8) ^ \
+                     ((unsigned long)a)) & 0xffff)
+
+
+/* handle ~/.passwd. courtesy of shirsch@ibm.net. */
+static  __inline__ int home_passwd(const struct passwd *pwd, 
+                                  const char *path, const int pathlen, 
+                                  char *passwd, const int len,
+                                  const int set)
+{
+  struct stat st;
+  int fd, i;
+  
+  if ( (fd = open(path, (set) ? O_WRONLY : O_RDONLY)) < 0 ) {
+    syslog( LOG_ERR, "Failed to open %s", path);
+    return AFPERR_ACCESS;
+  }
+
+  if ( fstat( fd, &st ) < 0 ) 
+    goto home_passwd_fail;
+  
+  /* If any of these are true, disallow login: 
+   * - not a regular file
+   * - gid or uid don't match user
+   * - anyone else has permissions of any sort
+   */
+  if (!S_ISREG(st.st_mode) || (pwd->pw_uid != st.st_uid) ||
+      (pwd->pw_gid != st.st_gid) ||
+      (st.st_mode & ( S_IRWXG | S_IRWXO )) ) {
+    syslog( LOG_INFO, "Insecure permissions found for %s.", path);
+    goto home_passwd_fail;
+  }
+
+  /* get the password */
+  if (set) {
+    if (write(fd, passwd, len) < 0) {
+      syslog( LOG_ERR, "Failed to write to %s", path );
+      goto home_passwd_fail;
+    }
+  } else {
+    if (read(fd, passwd, len) < 0) {
+      syslog( LOG_ERR, "Failed to read from %s", path );
+      goto home_passwd_fail;
+    }
+  
+  /* get rid of pesky characters */
+  for (i = 0; i < len; i++)
+    if ((passwd[i] != ' ') && isspace(passwd[i]))
+      passwd[i] = '\0';
+  }
+
+  close(fd);
+  return AFP_OK;
+
+home_passwd_fail:
+  close(fd);
+  return AFPERR_ACCESS;
+}
+
+
+
+/* 
+ * handle /path/afppasswd with an optional key file. we're a lot more
+ * trusting of this file. NOTE: we use our own password entry writing
+ * bits as we want to avoid tromping over global variables. in addition,
+ * we look for a key file and use that if it's there. here are the 
+ * formats: 
+ * password file:
+ * username:password:last login date:failedcount
+ *
+ * password is just the hex equivalent of either the ASCII password
+ * (if the key file doesn't exist) or the des encrypted password.
+ *
+ * key file: 
+ * key (in hex) */
+#define PASSWD_ILLEGAL '*'
+#define unhex(x)  (isdigit(x) ? (x) - '0' : toupper(x) + 10 - 'A')
+static int afppasswd(const struct passwd *pwd, 
+                    const char *path, const int pathlen, 
+                    char *passwd, int len, 
+                    const int set)
+{
+  u_int8_t key[DES_KEY_SZ*2];
+  char buf[MAXPATHLEN + 1], *p;
+  Key_schedule schedule;
+  FILE *fp;
+  int i, j, keyfd = -1, err = 0;
+  off_t pos;
+  
+  if ((fp = fopen(path, (set) ? "r+" : "r")) < 0) {
+    syslog( LOG_ERR, "Failed to open %s", path);
+    return AFPERR_ACCESS;
+  }
+  
+  /* open the key file if it exists */
+  strcpy(buf, path);
+  if (pathlen < sizeof(buf) - 5) {
+    strcat(buf, ".key");
+    keyfd = open(buf, O_RDONLY);
+  } 
+  
+  pos = ftell(fp);
+  memset(buf, 0, sizeof(buf));
+  while (fgets(buf, sizeof(buf), fp)) {
+    if ((p = strchr(buf, ':'))) {
+      if (strncmp(buf, pwd->pw_name, p - buf) == 0) {
+       p++;
+       if (*p == PASSWD_ILLEGAL) {
+         syslog(LOG_INFO, "invalid password entry for %s", pwd->pw_name);
+         err = AFPERR_ACCESS;
+         goto afppasswd_done;
+       }
+       goto afppasswd_found;
+      }
+    }
+    pos = ftell(fp);
+    memset(buf, 0, sizeof(buf));
+  }
+  err = AFPERR_PARAM;
+  goto afppasswd_done;
+
+afppasswd_found:
+  if (!set) {
+    /* convert to binary. */
+    for (i = j = 0; i < sizeof(key); i += 2, j++)
+      p[j] = (unhex(p[i]) << 4) | unhex(p[i + 1]);
+    if (j <= DES_KEY_SZ)
+      memset(p + j, 0, sizeof(key) - j);
+  }
+
+  if (keyfd > -1) {
+      /* read in the hex representation of an 8-byte key */
+      read(keyfd, key, sizeof(key));
+
+      /* convert to binary key */
+      for (i = j = 0; i < strlen(key); i += 2, j++)
+       key[j] = (unhex(key[i]) << 4) | unhex(key[i + 1]);
+      if (j <= DES_KEY_SZ)
+       memset(key + j, 0, sizeof(key) - j);
+      key_sched((C_Block *) key, schedule);
+      memset(key, 0, sizeof(key));
+
+      if (set) {
+       /* NOTE: this takes advantage of the fact that passwd doesn't
+        *       get used after this call if it's being set. */
+       ecb_encrypt((C_Block *) passwd, (C_Block *) passwd, schedule,
+                   DES_ENCRYPT);
+      } else {
+       /* decrypt the password */
+       ecb_encrypt((C_Block *) p, (C_Block *) p, schedule, DES_DECRYPT);
+      }
+      memset(schedule, 0, sizeof(schedule));
+  }
+
+  if (set) {
+    const unsigned char hextable[] = "0123456789ABCDEF";
+    struct flock lock;
+    int fd = fileno(fp);
+
+    /* convert to hex password */
+    for (i = j = 0; i < DES_KEY_SZ; i++, j += 2) {
+      key[j] = hextable[(passwd[i] & 0xF0) >> 4];
+      key[j + 1] = hextable[passwd[i] & 0x0F];
+    }
+    memcpy(p, key, sizeof(key));
+
+    /* get exclusive access to the user's password entry. we don't
+     * worry so much on reads. in the worse possible case there, the 
+     * user will just need to re-enter their password. */
+    lock.l_type = F_WRLCK;
+    lock.l_start = pos;
+    lock.l_len = 1;
+    lock.l_whence = SEEK_SET;
+
+    fseek(fp, pos, SEEK_SET);
+    fcntl(fd, F_SETLKW, &lock);
+    fwrite(buf, p - buf + sizeof(key), 1, fp);
+    lock.l_type = F_UNLCK;
+    fcntl(fd, F_SETLK, &lock);
+  } else 
+    memcpy(passwd, p, len);
+
+  memset(buf, 0, sizeof(buf));
+
+afppasswd_done:
+  if (keyfd > -1)
+    close(keyfd);
+  fclose(fp);
+  return err;
+}
+
+
+/* this sets the uid. it needs to do slightly different things
+ * depending upon whether or not the password is in ~/.passwd
+ * or in a global location */
+static int randpass(const struct passwd *pwd, const char *file,
+                   char *passwd, const int len, const int set) 
+{
+  int i;
+  uid_t uid = geteuid();
+
+  /* Build pathname to user's '.passwd' file */
+  i = strlen(file);
+  if (*file == '~') {
+    char path[MAXPATHLEN + 1];
+
+    if ( (strlen(pwd->pw_dir) + i - 1) > MAXPATHLEN)
+    return AFPERR_PARAM;
+  
+    strcpy(path,  pwd->pw_dir );
+    strcat(path, "/" );
+    strcat(path, file + 2);
+    if (!uid)
+      seteuid(pwd->pw_uid); /* change ourselves to the user */
+    i = home_passwd(pwd, path, i, passwd, len, set);
+    if (!uid)
+      seteuid(0); /* change ourselves back to root */
+    return i;
+  } 
+
+  if (i > MAXPATHLEN)
+    return AFPERR_PARAM;
+
+  /* handle afppasswd file. we need to make sure that we're root
+   * when we do this. */
+  if (uid)
+    seteuid(0);
+  i = afppasswd(pwd, file, i, passwd, len, set);
+  if (uid)
+    seteuid(uid);
+  return i;
+}
+
+  
+/* randnum sends an 8-byte number and uses the user's password to
+ * check against the encrypted reply. */
+static int randnum_login(void *obj, struct passwd **uam_pwd,
+                        char *ibuf, int ibuflen,
+                        char *rbuf, int *rbuflen)
+{
+  char *username, *passwdfile;
+  u_int16_t sessid;
+  int len, ulen, err;
+  
+  *rbuflen = 0;
+  
+  if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, 
+                          (void *) &username, &ulen) < 0)
+    return AFPERR_PARAM;
+
+  len = UAM_PASSWD_FILENAME;
+  if (uam_afpserver_option(obj, UAM_OPTION_PASSWDOPT, 
+                            (void *) &passwdfile, &len) < 0)
+    return AFPERR_PARAM;
+
+  len = (unsigned char) *ibuf++;
+  if ( len > ulen ) {
+       return AFPERR_PARAM;
+  }
+  memcpy(username, ibuf, len );
+  ibuf += len;
+  username[ len ] = '\0';
+  if ((unsigned long) ibuf & 1) /* padding */
+    ++ibuf;
+  
+  if (( randpwd = uam_getname(username, ulen)) == NULL )
+    return AFPERR_PARAM; /* unknown user */
+  
+  syslog( LOG_INFO, "randnum/rand2num login: %s", username);
+  if (uam_checkuser(randpwd) < 0)
+    return AFPERR_NOTAUTH;
+
+  if ((err = randpass(randpwd, passwdfile, seskey,
+                     sizeof(seskey), 0)) != AFP_OK)
+    return err;
+
+  /* get a random number */
+  len = sizeof(randbuf);
+  if (uam_afpserver_option(obj, UAM_OPTION_RANDNUM,
+                          (void *) randbuf, &len) < 0)
+    return AFPERR_PARAM;
+
+  /* session id is a hashed version of the obj pointer */
+  sessid = randhash(obj);
+  memcpy(rbuf, &sessid, sizeof(sessid));
+  rbuf += sizeof(sessid);
+  *rbuflen = sizeof(sessid);
+  
+  /* send the random number off */
+  memcpy(rbuf, randbuf, sizeof(randbuf));
+  *rbuflen += sizeof(randbuf);
+  return AFPERR_AUTHCONT;
+}
+
+
+/* check encrypted reply. we actually setup the encryption stuff
+ * here as the first part of randnum and rand2num are identical. */
+static int randnum_logincont(void *obj, struct passwd **uam_pwd,
+                            char *ibuf, int ibuflen, 
+                            char *rbuf, int *rbuflen)
+{
+  u_int16_t sessid;
+
+  *rbuflen = 0;
+
+  memcpy(&sessid, ibuf, sizeof(sessid));
+  if (sessid != randhash(obj))
+    return AFPERR_PARAM;
+
+  ibuf += sizeof(sessid);
+
+  /* encrypt. this saves a little space by using the fact that
+   * des can encrypt in-place without side-effects. */
+  key_sched((C_Block *) seskey, seskeysched);
+  memset(seskey, 0, sizeof(seskey));
+  ecb_encrypt((C_Block *) randbuf, (C_Block *) randbuf,
+              seskeysched, DES_ENCRYPT);
+  memset(seskeysched, 0, sizeof(seskeysched));
+
+  /* test against what the client sent */
+  if (memcmp( randbuf, ibuf, sizeof(randbuf) )) { /* != */
+    memset(randbuf, 0, sizeof(randbuf));
+    return AFPERR_NOTAUTH;
+  }
+
+  memset(randbuf, 0, sizeof(randbuf));
+  *uam_pwd = randpwd;
+  return AFP_OK;
+}
+
+
+/* differences from randnum:
+ * 1) each byte of the key is shifted left one bit
+ * 2) client sends the server a 64-bit number. the server encrypts it
+ *    and sends it back as part of the reply.
+ */
+static int rand2num_logincont(void *obj, struct passwd **uam_pwd,
+                             char *ibuf, int ibuflen, 
+                             char *rbuf, int *rbuflen)
+{
+  u_int16_t sessid;
+  int i;
+
+  *rbuflen = 0;
+
+  /* compare session id */
+  memcpy(&sessid, ibuf, sizeof(sessid));
+  if (sessid != randhash(obj))
+    return AFPERR_PARAM;
+
+  ibuf += sizeof(sessid);
+
+  /* shift key elements left one bit */
+  for (i = 0; i < sizeof(seskey); i++)
+    seskey[i] <<= 1;
+
+  /* encrypt randbuf */
+  key_sched((C_Block *) seskey, seskeysched);
+  memset(seskey, 0, sizeof(seskey));
+  ecb_encrypt( (C_Block *) randbuf, (C_Block *) randbuf,
+              seskeysched, DES_ENCRYPT);
+
+  /* test against client's reply */
+  if (memcmp(randbuf, ibuf, sizeof(randbuf))) { /* != */
+    memset(randbuf, 0, sizeof(randbuf));
+    memset(seskeysched, 0, sizeof(seskeysched));
+    return AFPERR_NOTAUTH;
+  }
+  ibuf += sizeof(randbuf);
+  memset(randbuf, 0, sizeof(randbuf));
+
+  /* encrypt client's challenge and send back */
+  ecb_encrypt( (C_Block *) ibuf, (C_Block *) rbuf,
+              seskeysched, DES_ENCRYPT);
+  memset(seskeysched, 0, sizeof(seskeysched));
+  *rbuflen = sizeof(randbuf);
+  
+  *uam_pwd = randpwd;
+  return AFP_OK;
+}
+
+/* change password  --
+ * NOTE: an FPLogin must already have completed successfully for this
+ *       to work. 
+ */
+static int randnum_changepw(void *obj, const char *username, 
+                           struct passwd *pwd, char *ibuf,
+                           int ibuflen, char *rbuf, int *rbuflen)
+{
+    char *passwdfile;
+    int err, len;
+
+    if (uam_checkuser(pwd) < 0)
+      return AFPERR_ACCESS;
+
+    len = UAM_PASSWD_FILENAME;
+    if (uam_afpserver_option(obj, UAM_OPTION_PASSWDOPT, 
+                            (void *) &passwdfile, &len) < 0)
+      return AFPERR_PARAM;
+
+    /* old password is encrypted with new password and new password is
+     * encrypted with old. */
+    if ((err = randpass(pwd, passwdfile, seskey, 
+                       sizeof(seskey), 0)) != AFP_OK)
+      return err;
+
+    /* use old passwd to decrypt new passwd */
+    key_sched((C_Block *) seskey, seskeysched);
+    ibuf += PASSWDLEN; /* new passwd */
+    ibuf[PASSWDLEN] = '\0';
+    ecb_encrypt( (C_Block *) ibuf, (C_Block *) ibuf, seskeysched, DES_DECRYPT);
+
+    /* now use new passwd to decrypt old passwd */
+    key_sched((C_Block *) ibuf, seskeysched);
+    ibuf -= PASSWDLEN; /* old passwd */
+    ecb_encrypt((C_Block *) ibuf, (C_Block *) ibuf, seskeysched, DES_DECRYPT);
+    if (memcmp(seskey, ibuf, sizeof(seskey))) 
+       err = AFPERR_NOTAUTH;
+    else if (memcmp(seskey, ibuf + PASSWDLEN, sizeof(seskey)) == 0)
+        err = AFPERR_PWDSAME;
+#ifdef USE_CRACKLIB
+    else if (FascistCheck(ibuf + PASSWDLEN, _PATH_CRACKLIB))
+        err = AFPERR_PWDPOLCY;
+#endif
+
+    if (!err) 
+      err = randpass(pwd, passwdfile, ibuf + PASSWDLEN, sizeof(seskey), 1);
+
+    /* zero out some fields */
+    memset(seskeysched, 0, sizeof(seskeysched));
+    memset(seskey, 0, sizeof(seskey));
+    memset(ibuf, 0, sizeof(seskey)); /* old passwd */
+    memset(ibuf + PASSWDLEN, 0, sizeof(seskey)); /* new passwd */
+
+    if (err)
+      return err;
+
+  return( AFP_OK );
+}
+
+static int uam_setup(const char *path)
+{
+  if (uam_register(UAM_SERVER_LOGIN, path, "Randnum exchange", 
+                  randnum_login, randnum_logincont, NULL) < 0)
+    return -1;
+  if (uam_register(UAM_SERVER_LOGIN, path, "2-Way Randnum exchange",
+                  randnum_login, rand2num_logincont, NULL) < 0) {
+    uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
+    return -1;
+  }
+    
+  if (uam_register(UAM_SERVER_CHANGEPW, path, "Randnum Exchange", 
+                  randnum_changepw) < 0) {
+    uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
+    uam_unregister(UAM_SERVER_LOGIN, "2-Way Randnum exchange");
+    return -1;
+  }
+  /*uam_register(UAM_SERVER_PRINTAUTH, path, "Randnum Exchange",
+    pam_printer);*/
+
+  return 0;
+}
+
+static void uam_cleanup(void)
+{
+  uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
+  uam_unregister(UAM_SERVER_LOGIN, "2-Way Randnum exchange");
+  uam_unregister(UAM_SERVER_CHANGEPW, "Randnum Exchange");
+  /*uam_unregister(UAM_SERVER_PRINTAUTH, "Randnum Exchange");*/
+}
+
+UAM_MODULE_EXPORT struct uam_export uams_randnum = {
+  UAM_MODULE_SERVER,
+  UAM_MODULE_VERSION,
+  uam_setup, uam_cleanup
+};
+
+#endif /* UAM_RNDNUM */
diff --git a/include/Makefile b/include/Makefile
new file mode 100644 (file)
index 0000000..a120b5b
--- /dev/null
@@ -0,0 +1,23 @@
+TARGETS=       atalk netatalk
+
+INSTALL=       install
+
+all :
+
+install : all
+       -mkdir ${DESTDIR}
+       -mkdir ${INCDIR}
+       for i in ${TARGETS}; do \
+           rm -rf ${INCDIR}/$$i ; \
+           mkdir ${INCDIR}/$$i ; \
+           cp $$i/*.h ${INCDIR}/$$i ; \
+       done
+
+clean :
+
+tags :
+
+depend :
+
+# DO NOT DELETE THIS LINE
+
diff --git a/include/atalk/adouble.h b/include/atalk/adouble.h
new file mode 100644 (file)
index 0000000..931a40b
--- /dev/null
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#ifndef _ATALK_ADOUBLE_H
+#define _ATALK_ADOUBLE_H
+
+#include <unistd.h>
+
+#if defined(sun) && defined(__svr4__)
+#include </usr/ucbinclude/sys/file.h>
+#else
+#include <sys/file.h>
+#endif
+#include <fcntl.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <netatalk/endian.h>
+
+
+/* XXX: this is the wrong place to put this. 
+ * NOTE: as of 2.2.1, linux can't do a sendfile from a socket. */
+#ifdef SENDFILE_FLAVOR_LINUX
+#define HAVE_SENDFILE_READ
+#define HAVE_SENDFILE_WRITE
+#include <asm/unistd.h>
+#ifdef __NR_sendfile
+static inline int sendfile(int fdout, int fdin, off_t *off, size_t count)
+{
+  return syscall(__NR_sendfile, fdout, fdin, off, count);
+}
+#else
+#include <sys/sendfile.h>
+#endif
+#endif
+
+#ifdef SENDFILE_FLAVOR_BSD
+#define HAVE_SENDFILE_READ
+#endif
+
+/*
+ * AppleDouble entry IDs. 
+ */
+#define ADEID_DFORK            1
+#define ADEID_RFORK            2
+#define ADEID_NAME             3
+#define ADEID_COMMENT          4
+#define ADEID_ICONBW           5
+#define ADEID_ICONCOL          6
+#define ADEID_FILEI            7  /* v1, replaced by: */
+#define ADEID_FILEDATESI       8  /* this */
+#define ADEID_FINDERI          9
+#define ADEID_MACFILEI         10 /* we don't use this */
+#define ADEID_PRODOSFILEI      11 /* we store prodos info here */
+#define ADEID_MSDOSFILEI       12 /* we don't use this */
+#define ADEID_SHORTNAME                13
+#define ADEID_AFPFILEI         14 /* where the rest of the FILEI info goes */
+#define ADEID_DID              15
+
+#define ADEID_MAX              16
+
+/* magic */
+#define AD_APPLESINGLE_MAGIC 0x00051600
+#define AD_APPLEDOUBLE_MAGIC 0x00051607
+#define AD_MAGIC            AD_APPLEDOUBLE_MAGIC
+
+/* version info */
+#define AD_VERSION1    0x00010000
+#define AD_VERSION2    0x00020000
+#define AD_VERSION     AD_VERSION1
+
+/* sizes of relevant entry bits */
+#define ADEDLEN_MAGIC       4
+#define ADEDLEN_VERSION     4
+#define ADEDLEN_FILLER      16
+#define ADEDLEN_NENTRIES    2
+
+#define AD_HEADER_LEN       (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
+                            ADEDLEN_FILLER + ADEDLEN_NENTRIES)
+#define AD_ENTRY_LEN        12  /* size of a single entry header */
+
+/* v1 field widths */
+#define ADEDLEN_NAME        255
+#define ADEDLEN_COMMENT     200
+#define ADEDLEN_FILEI      16
+#define ADEDLEN_FINDERI            32
+
+/* v2 field widths */
+#define ADEDLEN_FILEDATESI     16
+#define ADEDLEN_SHORTNAME       12 /* length up to 8.3 */
+#define ADEDLEN_AFPFILEI        4
+#define ADEDLEN_MACFILEI        4
+#define ADEDLEN_PRODOSFILEI     8
+#define ADEDLEN_MSDOSFILEI      2
+#define ADEDLEN_DID             4
+
+
+#define AD_DATASZ1      589  
+#define AD_DATASZ2      665  /* v1 + 4 new entries (entry desc. + entry) */
+#define AD_DATASZ_MAX   1024
+#if AD_VERSION == AD_VERSION1
+#define AD_DATASZ      AD_DATASZ1 /* hold enough for the entries */
+#else if AD_VERSION == AD_VERSION2
+#define AD_DATASZ       AD_DATASZ2
+#endif
+
+/*
+ * The header of the AppleDouble Header File looks like this:
+ *
+ *     NAME                    SIZE
+ *     ====                    ====
+ *     Magic                   4
+ *     Version                 4
+ *     Home File System        16  (this becomes filler in ad v2)
+ *     Number of Entries       2
+ *     Entry Descriptors for each entry:
+ *             Entry ID        4
+ *             Offset          4
+ *             Length          4
+ */
+
+struct ad_entry {
+    u_int32_t  ade_off;
+    u_int32_t  ade_len;
+};
+
+typedef struct adf_lock_t {
+  struct flock lock;
+  int user;
+  int *refcount; /* handle read locks with multiple users */
+} adf_lock_t;
+
+struct ad_fd {
+    int                 adf_fd;
+    off_t       adf_off;
+    int                 adf_flags;
+    adf_lock_t   *adf_lock;
+    int          adf_refcount, adf_lockcount, adf_lockmax;
+};
+
+/* some header protection */
+#define AD_INITED  0xad494e54  /* ad"INT" */
+struct adouble {
+    u_int32_t          ad_magic;
+    u_int32_t          ad_version;
+    char               ad_filler[ 16 ];
+    struct ad_entry    ad_eid[ ADEID_MAX ];
+    struct ad_fd       ad_df, ad_hf;
+    int                 ad_flags, ad_inited;
+#ifdef USE_MMAPPED_HEADERS
+    char                *ad_data;
+#else
+    char               ad_data[AD_DATASZ_MAX];
+#endif
+};
+
+#define ADFLAGS_DF       (1<<0)
+#define ADFLAGS_HF       (1<<1)
+#define ADFLAGS_DIR      (1<<2)
+#define ADFLAGS_NOADOUBLE (1<<3)
+#define ADFLAGS_V1COMPAT  (1<<4)
+
+/* lock flags */
+#define ADLOCK_CLR      (0)
+#define ADLOCK_RD       (1<<0)
+#define ADLOCK_WR       (1<<1)
+#define ADLOCK_MASK     (ADLOCK_RD | ADLOCK_WR)
+#define ADLOCK_UPGRADE  (1<<2)
+#define ADLOCK_FILELOCK (1<<3)
+
+/* we use this so that we can use the same mechanism for both byte
+ * locks and file synchronization locks. i do this by co-opting either
+ * first bits on 32-bit machines or shifting above the last bit on
+ * 64-bit machines. this only matters for the data fork. */
+#if defined(TRY_64BITOFF_T) && (~0UL > 0xFFFFFFFFU)
+/* synchronization locks */
+#define AD_FILELOCK_BASE (0x80000000)
+#define AD_FILELOCK_WR   (AD_FILELOCK_BASE + 0)
+#define AD_FILELOCK_RD   (AD_FILELOCK_BASE + 1)
+#else
+#define AD_FILELOCK_BASE (0x7FFFFFFE)
+#define AD_FILELOCK_WR   (AD_FILELOCK_BASE + 0)
+#define AD_FILELOCK_RD   (AD_FILELOCK_BASE + 1)
+#endif
+
+/* time stuff. we overload the bits a little.  */
+#define AD_DATE_CREATE        0
+#define AD_DATE_MODIFY        4
+#define AD_DATE_BACKUP        8
+#define AD_DATE_ACCESS        12 
+#define AD_DATE_MASK          (AD_DATE_CREATE | AD_DATE_MODIFY | \
+                              AD_DATE_BACKUP | AD_DATE_ACCESS)
+#define AD_DATE_UNIX          (1 << 10)
+#define AD_DATE_START         htonl(0x80000000)
+#define AD_DATE_DELTA         946684800
+#define AD_DATE_FROM_UNIX(x)  htonl((x) - AD_DATE_DELTA)
+#define AD_DATE_TO_UNIX(x)    (ntohl(x) + AD_DATE_DELTA)
+/* private AFPFileInfo bits */
+#define AD_AFPFILEI_OWNER       (1 << 0) /* any owner */
+#define AD_AFPFILEI_GROUP       (1 << 1) /* ignore group */
+#define AD_AFPFILEI_BLANKACCESS (1 << 2) /* blank access permissions */
+
+#define ad_dfileno(ad)         ((ad)->ad_df.adf_fd)
+#define ad_hfileno(ad)         ((ad)->ad_hf.adf_fd)
+#define ad_getversion(ad)      ((ad)->ad_version)
+
+#define ad_getentrylen(ad,eid) ((ad)->ad_eid[(eid)].ade_len)
+#define ad_setentrylen(ad,eid,len) \
+       ((ad)->ad_eid[(eid)].ade_len = (len))
+#define ad_getentryoff(ad,eid)  ((ad)->ad_eid[(eid)].ade_off)
+#define ad_entry(ad,eid)        ((caddr_t)(ad)->ad_data + \
+                                (ad)->ad_eid[(eid)].ade_off)     
+#define ad_getoflags(ad,adf)   (((adf)&ADFLAGS_HF) ? \
+       (ad)->ad_hf.adf_flags : (ad)->ad_df.adf_flags)
+
+/* ad_flush.c */
+extern void ad_rebuild_header __P((struct adouble *));
+extern int ad_flush           __P((struct adouble *, int));
+extern int ad_close           __P((struct adouble *, int));
+
+/* ad_lock.c */
+extern int ad_flock_lock __P((struct adouble *, const u_int32_t /*eid*/,
+                             const int /*type*/, const off_t /*offset*/,
+                             const size_t /*len*/, const int /*user*/));
+extern int ad_fcntl_lock __P((struct adouble *, const u_int32_t /*eid*/,
+                             const int /*type*/, const off_t /*offset*/,
+                             const size_t /*len*/, const int /*user*/));
+extern void ad_fcntl_unlock __P((struct adouble *, const int /*user*/));
+
+extern int ad_flock_tmplock __P((struct adouble *, const u_int32_t /*eid*/,
+                                const int /*type*/, const off_t /*offset*/,
+                                const size_t /*len*/));
+extern int ad_fcntl_tmplock __P((struct adouble *, const u_int32_t /*eid*/,
+                                const int /*type*/, const off_t /*offset*/,
+                                const size_t /*len*/));
+
+#ifdef USE_FLOCK_LOCKS
+#define ad_lock ad_flock_lock
+#define ad_tmplock ad_flock_tmplock
+#define ad_unlock(a,b) 
+#else
+#define ad_lock ad_fcntl_lock
+#define ad_tmplock ad_fcntl_tmplock
+#define ad_unlock ad_fcntl_unlock
+#endif
+
+/* ad_open.c */
+extern char *ad_path  __P((char *, int));
+extern int ad_mode    __P((char *, int));
+extern int ad_mkdir   __P((char *, int));
+extern int ad_open    __P((char *, int, int, int, struct adouble *)); 
+extern int ad_refresh __P((struct adouble *));
+
+/* ad_read.c/ad_write.c */
+extern ssize_t ad_read __P((struct adouble *, const u_int32_t, 
+                           const off_t, char *, const size_t));
+extern ssize_t ad_write __P((struct adouble *, const u_int32_t, off_t,
+                            const int, const char *, const size_t));
+extern int ad_dtruncate __P((struct adouble *, const size_t));
+extern int ad_rtruncate __P((struct adouble *, const size_t));
+
+/* ad_size.c */
+extern off_t ad_size __P((const struct adouble *, const u_int32_t ));
+
+/* ad_mmap.c */
+extern void *ad_mmapread __P((struct adouble *, const u_int32_t,
+                             const off_t, const size_t));
+extern void *ad_mmapwrite __P((struct adouble *, const u_int32_t,
+                              const off_t, const int, const size_t));
+#define ad_munmap(buf, len)  (munmap((buf), (len)))
+
+/* ad_date.c */
+extern int ad_setdate __P((const struct adouble *, unsigned int, u_int32_t));
+extern int ad_getdate __P((const struct adouble *, unsigned int, u_int32_t *));
+
+/* ad_attr.c */
+extern int ad_setattr __P((const struct adouble *, const u_int16_t));
+extern int ad_getattr __P((const struct adouble *, u_int16_t *));
+
+#ifdef HAVE_SENDFILE_READ
+extern ssize_t ad_readfile __P((const struct adouble *, const int, 
+                               const int, off_t, const size_t));
+#endif
+
+#if 0
+#ifdef HAVE_SENDFILE_WRITE
+extern ssize_t ad_writefile __P((struct adouble *, const int, 
+                                const int, off_t, const int, const size_t));
+#endif
+#endif
+     
+#endif
diff --git a/include/atalk/aep.h b/include/atalk/aep.h
new file mode 100644 (file)
index 0000000..69a419a
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#ifndef _ATALK_AEP_H
+#define _ATALK_AEP_H 1
+
+#define AEPOP_REQUEST  1
+#define AEPOP_REPLY    2
+
+#endif
diff --git a/include/atalk/afp.h b/include/atalk/afp.h
new file mode 100644 (file)
index 0000000..3ee2f10
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#ifndef _ATALK_AFP_H
+#define _ATALK_AFP_H 1
+
+#include <sys/types.h>
+#include <netatalk/endian.h>
+
+typedef u_int16_t AFPUserBytes;
+
+/* protocols */
+#define AFPPROTO_ASP           1
+#define AFPPROTO_DSI           2
+
+/* actual transports. the DSI ones (tcp right now) need to be
+ * kept in sync w/ <atalk/dsi.h>. 
+ * convention: AFPTRANS_* = (1 << DSI_*) 
+ */
+#define AFPTRANS_NONE          0
+#define AFPTRANS_DDP          (1 << 0)
+#define AFPTRANS_TCP          (1 << 1)
+#define AFPTRANS_ALL          (AFPTRANS_DDP | AFPTRANS_TCP)
+
+/* server flags */
+#define AFPSRVRINFO_COPY        (1<<0)  /* supports copyfile */
+#define AFPSRVRINFO_PASSWD      (1<<1)  /* supports change password */
+#define AFPSRVRINFO_NOSAVEPASSWD (1<<2)  /* don't allow save password */
+#define AFPSRVRINFO_SRVMSGS      (1<<3)  /* supports server messages */
+#define AFPSRVRINFO_SRVSIGNATURE (1<<4)  /* supports server signature */
+#define AFPSRVRINFO_TCPIP        (1<<5)  /* supports tcpip */
+#define AFPSRVRINFO_SRVNOTIFY    (1<<6)  /* supports server notifications */ 
+#define AFPSRVRINFO_FASTBOZO    (1<<15) /* fast copying */
+
+#define AFP_OK         0
+#define AFPERR_ACCESS  -5000   /* permission denied */
+#define AFPERR_AUTHCONT        -5001   /* logincont */
+#define AFPERR_BADUAM  -5002   /* uam doesn't exist */
+#define AFPERR_BADVERS -5003   /* bad afp version number */
+#define AFPERR_BITMAP  -5004   /* invalid bitmap */
+#define AFPERR_CANTMOVE -5005   /* can't move file */
+#define AFPERR_DENYCONF        -5006   /* file synchronization locks conflict */
+#define AFPERR_DIRNEMPT        -5007   /* directory not empty */
+#define AFPERR_DFULL   -5008   /* disk full */
+#define AFPERR_EOF     -5009   /* end of file -- catsearch and afp_read */
+#define AFPERR_BUSY    -5010   /* FileBusy */
+#define AFPERR_FLATVOL  -5011   /* volume doesn't support directories */
+#define AFPERR_NOITEM  -5012   /* ItemNotFound */
+#define AFPERR_LOCK     -5013   /* LockErr */
+#define AFPERR_MISC     -5014   /* misc. err */
+#define AFPERR_NLOCK    -5015   /* no more locks */
+#define AFPERR_NOSRVR  -5016   /* no response by server at that address */
+#define AFPERR_EXIST   -5017   /* object already exists */
+#define AFPERR_NOOBJ   -5018   /* object not found */
+#define AFPERR_PARAM   -5019   /* parameter error */
+#define AFPERR_NORANGE  -5020   /* no range lock */
+#define AFPERR_RANGEOVR -5021   /* range overlap */
+#define AFPERR_SESSCLOS -5022   /* session closed */
+#define AFPERR_NOTAUTH -5023   /* user not authenticated */
+#define AFPERR_NOOP    -5024   /* command not supported */
+#define AFPERR_BADTYPE -5025   /* object is the wrong type */
+#define AFPERR_NFILE   -5026   /* too many files open */
+#define AFPERR_SHUTDOWN        -5027   /* server is going down */
+#define AFPERR_NORENAME -5028   /* can't rename */
+#define AFPERR_NODIR   -5029   /* couldn't find directory */
+#define AFPERR_ITYPE   -5030   /* wrong icon type */
+#define AFPERR_VLOCK   -5031   /* volume locked */
+#define AFPERR_OLOCK    -5032   /* object locked */
+#define AFPERR_CTNSHRD  -5033   /* share point contains a share point */
+#define AFPERR_NOID     -5034   /* file thread not found */
+#define AFPERR_EXISTID  -5035   /* file already has an id */
+#define AFPERR_DIFFVOL  -5036   /* different volume */
+#define AFPERR_CATCHNG  -5037   /* catalog has changed */
+#define AFPERR_SAMEOBJ  -5038   /* source file == destination file */
+#define AFPERR_BADID    -5039   /* non-existent file id */
+#define AFPERR_PWDSAME  -5040   /* same password/can't change password */
+#define AFPERR_PWDSHORT -5041   /* password too short */
+#define AFPERR_PWDEXPR  -5042   /* password expired */
+#define AFPERR_INSHRD   -5043   /* folder being shared is inside a
+                                  shared folder. may be returned by
+                                  afpMoveAndRename. */
+#define AFPERR_INTRASH  -5044   /* shared folder in trash. */
+#define AFPERR_PWDCHNG  -5045   /* password needs to be changed */
+#define AFPERR_PWDPOLCY -5046   /* password fails policy check */
+#define AFPERR_USRLOGIN -5047   /* user already logged on */
+
+/* AFP Attention Codes -- 4 bits */
+#define AFPATTN_SHUTDOWN     (1 << 15)            /* shutdown/disconnect */
+#define AFPATTN_CRASH        (1 << 14)            /* server crashed */
+#define AFPATTN_MESG         (1 << 13)            /* server has message */
+#define AFPATTN_NORECONNECT  (1 << 12)            /* don't reconnect */
+/* server notification */
+#define AFPATTN_NOTIFY       (AFPATTN_MESG | AFPATTN_NORECONNECT) 
+
+/* extended bitmap -- 12 bits. volchanged is only useful w/ a server
+ * notification, and time is only useful for shutdown. */
+#define AFPATTN_VOLCHANGED   (1 << 0)             /* volume has changed */
+#define AFPATTN_TIME(x)      ((x) & 0xfff)        /* time in minutes */
+
+typedef enum {
+  AFPMESG_LOGIN = 0,
+  AFPMESG_SERVER = 1
+} afpmessage_t;
+
+#endif
diff --git a/include/atalk/asp.h b/include/atalk/asp.h
new file mode 100644 (file)
index 0000000..fd569df
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#ifndef _ATALK_ASP_H
+#define _ATALK_ASP_H 1
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <atalk/atp.h>
+#include <atalk/afp.h>
+#include <atalk/server_child.h>
+
+#define ASP_HDRSIZ        4
+#define ASP_CMDSIZ        578
+
+#define ASP_MAXPACKETS    8
+#define ASP_CMDMAXSIZ     (ASP_CMDSIZ + ASP_HDRSIZ)
+#define ASP_DATASIZ       (ASP_CMDSIZ*ASP_MAXPACKETS)
+#define ASP_DATAMAXSIZ    ((ASP_CMDSIZ + ASP_HDRSIZ)*ASP_MAXPACKETS)
+
+typedef struct ASP {
+    ATP                        asp_atp;
+    struct sockaddr_at asp_sat;
+    u_int8_t           asp_wss;
+    u_int8_t            asp_sid;
+    union {
+       struct {
+           char                        *as_status;
+           int                         as_slen;
+       }                       asu_status;
+       u_int16_t               asu_seq;
+    }                  asp_u;
+#define asp_status     asp_u.asu_status.as_status
+#define asp_slen       asp_u.asu_status.as_slen
+#define asp_seq                asp_u.asu_seq
+    int                        asp_flags;
+    char               child, inited, *commands;
+    char                cmdbuf[ASP_CMDMAXSIZ];
+    char                data[ASP_DATAMAXSIZ];  
+    int                 cmdlen, datalen;
+    size_t             read_count, write_count;
+} *ASP;
+
+#define ASPFL_SLS      1
+#define ASPFL_SSS      2
+
+#define ASPFUNC_CLOSE  1
+#define ASPFUNC_CMD    2
+#define ASPFUNC_STAT   3
+#define ASPFUNC_OPEN   4
+#define ASPFUNC_TICKLE 5
+#define ASPFUNC_WRITE  6
+#define ASPFUNC_WRTCONT        7
+#define ASPFUNC_ATTN   8
+
+#define ASPERR_OK      0x0000
+#define ASPERR_BADVERS 0xfbd6
+#define ASPERR_BUFSMALL        0xfbd5
+#define ASPERR_NOSESS  0xfbd4
+#define ASPERR_NOSERV  0xfbd3
+#define ASPERR_PARM    0xfbd2
+#define ASPERR_SERVBUSY        0xfbd1
+#define ASPERR_SESSCLOS        0xfbd0
+#define ASPERR_SIZERR  0xfbcf
+#define ASPERR_TOOMANY 0xfbce
+#define ASPERR_NOACK   0xfbcd
+
+extern ASP asp_init         __P((ATP));
+extern void asp_setstatus   __P((ASP, char *, const int));
+extern ASP asp_getsession   __P((ASP, server_child *, const int));
+extern int asp_close        __P((ASP));
+extern int asp_shutdown     __P((ASP));
+extern int asp_attention    __P((ASP, AFPUserBytes));
+extern int asp_getrequest   __P((ASP));
+extern int asp_cmdreply     __P((ASP, int));
+extern int asp_wrtcont      __P((ASP, char *, int *));
+#define asp_wrtreply(a,b)   asp_cmdreply((a), (b))
+extern void asp_kill        __P((int));
+extern void asp_tickle      __P((ASP, const u_int8_t, struct sockaddr_at *));
+
+#endif
diff --git a/include/atalk/atp.h b/include/atalk/atp.h
new file mode 100644 (file)
index 0000000..24548c9
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#ifndef _ATALK_ATP_H
+#define _ATALK_ATP_H 1
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <netatalk/at.h>
+#include <netatalk/endian.h>
+
+/* ATP packet format
+
+ |----------------|
+ | link header    |
+ |      ...       |
+ |----------------|
+ | DDP header     |
+ |      ...       |
+ |   type = 3     |
+ |----------------|
+ | control info   | --> bits 7,6: function code
+ |----------------|            5: XO bit
+ | bitmap/seq no. |            4: EOM bit
+ |----------------|            3: STS bit
+ | TID (MSB)      |        2,1,0: release timer code (ignored under phase I)
+ |----------------|
+ | TID (LSB)      |
+ |----------------|
+ | user bytes (4) |
+ |----------------|
+ | data (0-578)   |
+ |      ...       |
+ |----------------|
+*/
+struct atphdr {
+    u_int8_t   atphd_ctrlinfo; /* control information */
+    u_int8_t   atphd_bitmap;   /* bitmap or sequence number */
+    u_int16_t  atphd_tid;      /* transaction id. */
+};
+
+/* ATP protocol parameters
+*/
+#define ATP_MAXDATA    (578+4)         /* maximum ATP data size */
+#define ATP_BUFSIZ     587             /* maximum packet size */
+#define ATP_HDRSIZE    5               /* includes DDP type field */
+
+#define ATP_TRELMASK   0x07            /* mask all but TREL */
+#define ATP_RELTIME    30              /* base release timer (in secs) */
+
+#define ATP_TREL30     0x0             /* release time codes */
+#define ATP_TREL1M     0x1             /* these are passed in flags of */
+#define ATP_TREL2M     0x2             /* atp_sreq call, and set in the */
+#define ATP_TREL4M     0x3             /* packet control info. */
+#define ATP_TREL8M     0x4
+
+#define ATP_TRIES_INFINITE     -1      /* for atp_sreq, etc */
+
+struct atpxobuf {
+    u_int16_t          atpxo_tid;
+    struct timeval     atpxo_tv;
+    int                        atpxo_reltime;
+    struct atpbuf      *atpxo_packet[8];
+};
+
+struct atpbuf {
+    struct atpbuf      *atpbuf_next;           /* next buffer in chain */
+    short              atpbuf_dlen;            /* data length <= ATP_BUFSIZ */
+    struct sockaddr_at atpbuf_addr;            /* net address sent/recvd */
+    union {
+       char            atpbuf_data[ ATP_BUFSIZ ];      /* the data */
+       struct atpxobuf atpbuf_xo;                      /* for XO requests */
+    } atpbuf_info;
+};
+
+struct atp_handle {
+    int                        atph_socket;            /* ddp socket */
+    struct sockaddr_at atph_saddr;             /* address */
+    u_int16_t          atph_tid;               /* last tid used */
+    u_int16_t          atph_rtid;              /* last received (rreq) */
+    u_int8_t           atph_rxo;               /* XO flag from last rreq */
+    int                        atph_rreltime;          /* release time (secs) */
+    struct atpbuf      *atph_sent;             /* packets we send (XO) */
+    struct atpbuf      *atph_queue;            /* queue of pending packets */
+    int                        atph_reqtries;          /* retry count for request */
+    int                        atph_reqto;             /* retry timeout for request */
+    int                        atph_rrespcount;        /* expected # of responses */
+    u_int8_t           atph_rbitmap;           /* bitmap for request */
+    struct atpbuf      *atph_reqpkt;           /* last request packet */
+    struct timeval     atph_reqtv;             /* when we last sent request */
+    struct atpbuf      *atph_resppkt[8];       /* response to request */
+};
+
+typedef struct atp_handle *ATP;
+
+#define atp_sockaddr( h )      (&(h)->atph_saddr)
+#define atp_fileno(x)          ((x)->atph_socket)
+
+struct sreq_st {
+    char           *atpd_data;         /* request data */
+    int                    atpd_dlen;
+    int                    atpd_tries;         /* max. retry count */
+    int                    atpd_to;            /* retry interval */
+};
+
+struct rres_st {
+    struct iovec    *atpd_iov;         /* for response */
+    int                    atpd_iovcnt;
+};
+
+struct rreq_st {
+    char           *atpd_data;         /* request data */
+    int                    atpd_dlen;
+};
+
+struct sres_st {
+    struct iovec    *atpd_iov;         /* for response */
+    int                    atpd_iovcnt;
+};
+
+struct atp_block {
+    struct sockaddr_at *atp_saddr;             /* from/to address */
+    union {
+       struct sreq_st  sreqdata;
+#define atp_sreqdata   atp_data.sreqdata.atpd_data
+#define atp_sreqdlen   atp_data.sreqdata.atpd_dlen
+#define atp_sreqtries  atp_data.sreqdata.atpd_tries
+#define atp_sreqto     atp_data.sreqdata.atpd_to
+
+       struct rres_st  rresdata;
+#define atp_rresiov    atp_data.rresdata.atpd_iov
+#define atp_rresiovcnt atp_data.rresdata.atpd_iovcnt
+
+       struct rreq_st  rreqdata;
+#define atp_rreqdata   atp_data.rreqdata.atpd_data
+#define atp_rreqdlen   atp_data.rreqdata.atpd_dlen
+
+       struct sres_st  sresdata;
+#define atp_sresiov    atp_data.sresdata.atpd_iov
+#define atp_sresiovcnt atp_data.sresdata.atpd_iovcnt
+    } atp_data;
+    u_int8_t           atp_bitmap;     /* response buffer bitmap */
+};
+
+
+/* flags for ATP options (and control byte)
+*/
+#define ATP_STS                (1<<3)          /* Send Transaction Status */
+#define ATP_EOM                (1<<4)          /* End Of Message */
+#define ATP_XO         (1<<5)          /* eXactly Once mode */
+
+/* function codes
+*/
+#define ATP_FUNCMASK   (3<<6)          /* mask all but function */
+
+#define ATP_TREQ       (1<<6)          /* Trans. REQuest */
+#define ATP_TRESP      (2<<6)          /* Trans. RESPonse */
+#define ATP_TREL       (3<<6)          /* Trans. RELease */
+
+extern ATP             atp_open  __P((const u_int8_t, 
+                                      const struct at_addr *));
+extern int             atp_close __P((ATP));
+extern int             atp_sreq  __P((ATP, struct atp_block *, int, 
+                                      u_int8_t));
+extern int             atp_rresp __P((ATP, struct atp_block *));
+extern int             atp_rsel  __P((ATP, struct sockaddr_at *, int));
+extern int             atp_rreq  __P((ATP, struct atp_block *));
+extern int             atp_sresp __P((ATP, struct atp_block *));
+
+#endif
diff --git a/include/atalk/cnid.h b/include/atalk/cnid.h
new file mode 100644 (file)
index 0000000..19c8417
--- /dev/null
@@ -0,0 +1,44 @@
+/* 
+ * interface for database access to cnids. i do it this way to abstract
+ * things a bit in case we want to change the underlying implementation.
+ */
+
+#ifndef _ATALK_CNID_H
+#define _ATALK_CNID_H 1
+
+#include <sys/cdefs.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <netatalk/endian.h>
+
+typedef u_int32_t cnid_t;
+
+/* cnid_open.c */
+extern void *cnid_open __P((const char *));
+
+/* cnid_close.c */
+extern void cnid_close __P((void *));
+
+/* cnid_add.c */
+extern cnid_t cnid_add __P((void *, const struct stat *, const cnid_t,
+                           const char *, const int, cnid_t));
+
+/* cnid_get.c */
+extern cnid_t cnid_get __P((void *, const cnid_t, const char *, const int)); 
+extern char *cnid_resolve __P((void *, cnid_t *)); 
+extern cnid_t cnid_lookup __P((void *, const struct stat *, const cnid_t,
+                              const char *, const int));
+
+/* cnid_update.c */
+extern int cnid_update __P((void *, const cnid_t, const struct stat *,
+                           const cnid_t, const char *, int));
+
+/* cnid_delete.c */
+extern int cnid_delete __P((void *, const cnid_t));
+
+/* cnid_nextid.c */
+extern cnid_t cnid_nextid __P((void *));
+
+#endif /* include/atalk/cnid.h */
diff --git a/include/atalk/compat.h b/include/atalk/compat.h
new file mode 100644 (file)
index 0000000..e283220
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 1996 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ *
+ * NOTE: SunOS 4 and ultrix are pretty much the only reason why there
+ * are checks for EINTR everywhere. 
+ */
+
+#include <sys/cdefs.h>
+#include <signal.h>
+
+#ifdef __svr4__
+/*
+ * SunOS 5 (solaris) has SA_RESTART, but no SA_INTERRUPT.
+ */
+#ifndef SA_INTERRUPT
+#define SA_INTERRUPT   0
+#endif
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+extern int flock __P((int, int));
+extern int inet_aton __P((const char *, struct in_addr *));
+#else /* __svr4__ */
+
+#ifdef sun
+/*
+ * SunOS 4 has SA_INTERRUPT, but no SA_RESTART.
+ */
+#ifndef SA_RESTART
+#define SA_RESTART     0
+#endif
+#endif /* sun */
+
+#endif /* __svr4__ */
+
+#ifdef linux
+/*
+ * Linux has SA_RESTART, but no SA_INTERRUPT.  Note that the documentation
+ * seems to be wrong on several counts.  First, SA_ONESHOT is not the default,
+ * and second, SA_RESTART does what you'd expect (the same as svr4) and has
+ * nothing to do with SA_ONESHOT.
+ */
+#ifndef SA_INTERRUPT
+#define SA_INTERRUPT   0
+#endif /* SA_INTERRUPT */
+#endif /* linux */
+
+#ifdef ultrix 
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+/*
+ * Here's the really confusing one...  Under Ultrix, sigaction() works just
+ * like sigvec(), except that SV_INTERRUPT is always set.  Hence, there is
+ * no SA_INTERRUPT flag.  Unfortunately, there's also no SA_RESTART, so
+ * there's no way to suppress the interrupt.  Sigh.
+ */
+#ifndef SA_INTERRUPT
+#define SA_INTERRUPT   0
+#endif
+#ifndef SA_RESTART
+#define SA_RESTART     0
+#endif
+
+extern char *strdup __P((const char *));
+extern int inet_aton __P((const char *, struct in_addr *));
+#endif /* ultrix */
+
+#ifdef BSD4_4
+#ifndef SA_INTERRUPT
+#define SA_INTERRUPT   0
+#endif
+#endif /* BSD4_4 */
+
+#if defined(ultrix) || defined(_IBMR2) || defined(NEED_GETUSERSHELL)
+extern char *getusershell __P((void));
+#endif
diff --git a/include/atalk/ddp.h b/include/atalk/ddp.h
new file mode 100644 (file)
index 0000000..1c3fff1
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#ifndef _ATALK_DDP_H
+#define _ATALK_DDP_H 1
+
+#define DDPTYPE_RTMPRD 1
+#define DDPTYPE_NBP    2
+#define DDPTYPE_ATP    3
+#define DDPTYPE_AEP    4
+#define DDPTYPE_RTMPR  5
+#define DDPTYPE_ZIP    6
+#define DDPTYPE_ADSP   7
+
+#endif
diff --git a/include/atalk/dsi.h b/include/atalk/dsi.h
new file mode 100644 (file)
index 0000000..63509c1
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved.
+ */
+
+#ifndef _ATALK_DSI_H 
+#define _ATALK_DSI_H
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <signal.h>
+
+#include <netinet/in.h>
+#include <atalk/afp.h>
+#include <atalk/server_child.h>
+#include <netatalk/endian.h>
+
+/* What a DSI packet looks like:
+ 0                               32
+ |-------------------------------|
+ |flags  |command| requestID     |
+ |-------------------------------|
+ |error code/enclosed data offset|
+ |-------------------------------|
+ |total data length              |
+ |-------------------------------|
+ |reserved field                 |
+ |-------------------------------|
+
+ CONVENTION: anything with a dsi_ prefix is kept in network byte order.
+*/
+
+/* these need to be kept in sync w/ AFPTRANS_* in <atalk/afp.h>. 
+ * convention: AFPTRANS_* = (1 << DSI_*) */
+typedef enum {
+  DSI_MIN = 1,
+  DSI_TCPIP = 1,
+  DSI_MAX = 1
+} dsi_proto;
+
+#define DSI_BLOCKSIZ 16
+struct dsi_block {
+  u_int8_t dsi_flags;       /* packet type: request or reply */
+  u_int8_t dsi_command;     /* command */
+  u_int16_t dsi_requestID;  /* request ID */
+  u_int32_t dsi_code;       /* error code or data offset */
+  u_int32_t dsi_len;        /* total data length */
+  u_int32_t dsi_reserved;   /* reserved field */
+};
+
+#define DSI_CMDSIZ        800
+#define DSI_DATASIZ       8192
+/* child and parent processes might interpret a couple of these
+ * differently. */
+typedef struct DSI {
+  dsi_proto protocol;
+  struct dsi_block header;
+  struct sockaddr_in server, client;
+  sigset_t sigblockset;
+  struct itimerval timer, savetimer;
+  u_int32_t attn_quantum, datasize, server_quantum;
+  u_int16_t serverID, clientID;
+  u_int8_t *status, commands[DSI_CMDSIZ], data[DSI_DATASIZ];
+  int statuslen, datalen, cmdlen;
+  size_t read_count, write_count;
+  /* inited = initialized?, child = a child?, noreply = send reply? */
+  char child, inited, noreply;
+  const char *program; 
+  int socket, serversock;
+
+  /* protocol specific open/close, send/receive
+   * send/receive fill in the header and use dsi->commands.
+   * write/read just write/read data */
+  pid_t  (*proto_open)(struct DSI *);
+  void   (*proto_close)(struct DSI *);
+} DSI;
+  
+/* DSI flags */
+#define DSIFL_REQUEST    0x00
+#define DSIFL_REPLY      0x01
+#define DSIFL_MAX        0x01
+
+/* DSI session options */
+#define DSIOPT_SERVQUANT 0x00   /* server request quantum */
+#define DSIOPT_ATTNQUANT 0x01   /* attention quantum */
+
+/* DSI Commands */
+#define DSIFUNC_CLOSE   1       /* DSICloseSession */
+#define DSIFUNC_CMD     2       /* DSICommand */
+#define DSIFUNC_STAT    3       /* DSIGetStatus */
+#define DSIFUNC_OPEN    4       /* DSIOpenSession */
+#define DSIFUNC_TICKLE  5       /* DSITickle */
+#define DSIFUNC_WRITE   6       /* DSIWrite */
+#define DSIFUNC_ATTN    8       /* DSIAttention */
+#define DSIFUNC_MAX     8       /* largest command */
+
+/* DSI Error codes: most of these aren't used. */
+#define DSIERR_OK      0x0000
+#define DSIERR_BADVERS 0xfbd6
+#define DSIERR_BUFSMALL        0xfbd5
+#define DSIERR_NOSESS  0xfbd4
+#define DSIERR_NOSERV  0xfbd3
+#define DSIERR_PARM    0xfbd2
+#define DSIERR_SERVBUSY        0xfbd1
+#define DSIERR_SESSCLOS        0xfbd0
+#define DSIERR_SIZERR  0xfbcf
+#define DSIERR_TOOMANY 0xfbce
+#define DSIERR_NOACK   0xfbcd
+
+/* server and client quanta */
+#define DSI_DEFQUANT        2           /* default attention quantum size */
+#define DSI_SERVQUANT_MAX   0xffffffffL /* server quantum */
+#define DSI_SERVQUANT_MIN   0x0004A2E0L /* minimum server quantum */
+#define DSI_SERVQUANT_DEF   DSI_SERVQUANT_MIN /* default server quantum */
+
+/* default port number */
+#define DSI_AFPOVERTCP_PORT 548
+
+/* basic initialization: dsi_init.c */
+extern DSI *dsi_init __P((const dsi_proto /*protocol*/,
+                         const char * /*program*/, 
+                         const char * /*host*/, const char * /*address*/,
+                         const int /*port*/, const int /*proxy*/,
+                         const u_int32_t /* server quantum */));
+extern void dsi_setstatus __P((DSI *, u_int8_t *, const int));
+
+/* in dsi_getsess.c */
+extern DSI *dsi_getsession __P((DSI *, server_child *, const int));
+extern void dsi_kill __P((int));
+
+
+/* DSI Commands: individual files */
+extern void dsi_opensession __P((DSI *));
+extern int  dsi_attention __P((DSI *, AFPUserBytes));
+extern int  dsi_cmdreply __P((DSI *, const int));
+extern void dsi_tickle __P((DSI *));
+extern void dsi_getstatus __P((DSI *));
+extern void dsi_close __P((DSI *));
+
+/* low-level stream commands -- in dsi_stream.c */
+extern size_t dsi_stream_write __P((DSI *, void *, const size_t));
+extern size_t dsi_stream_read __P((DSI *, void *, const size_t));
+extern int dsi_stream_send __P((DSI *, void *, size_t));
+extern int dsi_stream_receive __P((DSI *, void *, const int, int *));
+
+/* client writes -- dsi_write.c */
+extern size_t dsi_writeinit __P((DSI *, void *, const size_t));
+extern size_t dsi_write __P((DSI *, void *, const size_t));
+extern void   dsi_writeflush __P((DSI *));
+#define dsi_wrtreply(a,b)  dsi_cmdreply(a,b)
+
+/* client reads -- dsi_read.c */
+extern ssize_t dsi_readinit __P((DSI *, void *, const size_t, const size_t,
+                                const int));
+extern ssize_t dsi_read __P((DSI *, void *, const size_t));
+extern void dsi_readdone __P((DSI *));
+
+/* some useful macros */
+#define dsi_serverID(x)   ((x)->serverID++)
+#define dsi_send(x)       do { \
+    (x)->header.dsi_len = htonl((x)->cmdlen); \
+    dsi_stream_send((x), (x)->commands, (x)->cmdlen); \
+} while (0)
+#define dsi_receive(x)    (dsi_stream_receive((x), (x)->commands, \
+                                             DSI_CMDSIZ, &(x)->cmdlen))
+#endif /* atalk/dsi.h */
diff --git a/include/atalk/nbp.h b/include/atalk/nbp.h
new file mode 100644 (file)
index 0000000..56334fe
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#ifndef _ATALK_NBP_H
+#define _ATALK_NBP_H 1
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <netatalk/at.h>
+#include <netatalk/endian.h>
+
+struct nbphdr {
+#if BYTE_ORDER == BIG_ENDIAN
+    unsigned   nh_op : 4,
+               nh_cnt : 4,
+#else BYTE_ORDER
+    unsigned   nh_cnt : 4,
+               nh_op : 4,
+#endif BYTE_ORDER
+               nh_id : 8;
+};
+
+#define SZ_NBPHDR      2
+
+struct nbptuple {
+    u_int16_t   nt_net;
+    u_int8_t    nt_node;
+    u_int8_t    nt_port;
+    u_int8_t    nt_enum;
+};
+#define SZ_NBPTUPLE    5
+
+#define NBPSTRLEN      32
+/*
+ * Name Binding Protocol Network Visible Entity
+ */
+struct nbpnve {
+    struct sockaddr_at nn_sat;
+    u_char             nn_objlen;
+    char               nn_obj[ NBPSTRLEN ];
+    u_char             nn_typelen;
+    char               nn_type[ NBPSTRLEN ];
+    u_char             nn_zonelen;
+    char               nn_zone[ NBPSTRLEN ];
+};
+
+/*
+ * Note that NBPOP_ERROR is not standard. As Apple adds more NBPOPs,
+ * we need to check for collisions with our extra values.  */
+#define NBPOP_BRRQ      0x1
+#define NBPOP_LKUP      0x2
+#define NBPOP_LKUPREPLY         0x3
+#define NBPOP_FWD       0x4
+#define NBPOP_RGSTR     0x7
+#define NBPOP_UNRGSTR   0x8
+#define NBPOP_CONFIRM    0x9
+#define NBPOP_OK        0xa  /* NBPOP_STATUS_REPLY */
+#define NBPOP_CLOSE_NOTE 0xb
+
+#define NBPOP_ERROR     0xf
+
+#define NBPMATCH_NOGLOB        (1<<1)
+#define NBPMATCH_NOZONE        (1<<2)
+
+extern int nbp_name __P((const char *, char **, char **, char **));
+extern int nbp_lookup __P((const char *, const char *, const char *,
+                          struct nbpnve *, const int, 
+                          const struct at_addr *));
+extern int nbp_rgstr __P((struct sockaddr_at *, 
+                         const char *, const char *, const char *));
+extern int nbp_unrgstr __P((const char *, const char *, const char *,
+                           const struct at_addr *));
+
+#endif
diff --git a/include/atalk/netddp.h b/include/atalk/netddp.h
new file mode 100644 (file)
index 0000000..ed9ddbc
--- /dev/null
@@ -0,0 +1,36 @@
+/* 
+ * Copyright (c) 1999 Adrian Sun (asun@zoology.washington.edu)
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ * this provides a generic interface to the ddp layer. with this, we
+ * should be able to interact with any appletalk stack that allows
+ * direct access to the ddp layer. right now, only os x server's ddp
+ * layer and the generic socket based interfaces are understood.  
+ */
+
+#ifndef _ATALK_NETDDP_H
+#define _ATALK_NETDDP_H 1
+
+#include <sys/cdefs.h>
+#include <sys/socket.h>
+#include <netatalk/at.h>
+
+extern int netddp_open   __P((struct sockaddr_at *, struct sockaddr_at *));
+
+#if !defined(NO_DDP) && defined(MACOSX_SERVER)
+extern int netddp_sendto __P((int, void *, int, unsigned int, 
+                          const struct sockaddr *, unsigned int));
+extern int netddp_recvfrom __P((int, void *, int, unsigned int, 
+                            struct sockaddr *, unsigned int *));
+#define netddp_close(a)  ddp_close(a)
+#else
+#include <unistd.h>
+#include <sys/types.h>
+
+#define netddp_close(a)  close(a)
+#define netddp_sendto    sendto
+#define netddp_recvfrom  recvfrom
+#endif
+
+#endif /* netddp.h */
+
diff --git a/include/atalk/pap.h b/include/atalk/pap.h
new file mode 100644 (file)
index 0000000..4b0e7a8
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#ifndef _ATALK_PAP_H
+#define _ATALK_PAP_H 1
+
+#define PAP_OPEN       1
+#define PAP_OPENREPLY  2
+#define PAP_READ       3
+#define PAP_DATA       4
+#define PAP_TICKLE     5
+#define PAP_CLOSE      6
+#define PAP_CLOSEREPLY 7
+#define PAP_SENDSTATUS 8
+#define PAP_STATUS     9
+
+#define PAP_MAXDATA    512
+#define PAP_MAXQUANTUM 8
+
+#endif
diff --git a/include/atalk/paths.h b/include/atalk/paths.h
new file mode 100644 (file)
index 0000000..ac77f15
--- /dev/null
@@ -0,0 +1,68 @@
+#ifndef ATALK_PATHS_H
+#define ATALK_PATHS_H 1
+
+/* we need a way of concatenating strings */
+#ifdef __STDC__
+#ifdef HAVE_BROKEN_CPP
+#define BROKEN_ECHO(a)    a
+#define ATALKPATHCAT(a,b) BROKEN_ECHO(a)##BROKEN_ECHO(b)
+#else
+#define ATALKPATHCAT(a,b) a##b
+#endif
+#else
+#define ATALKPATHCAT(a,b) a/**/b
+#endif
+
+
+/* lock file path. this should be re-organized a bit. */
+#ifndef _PATH_LOCKDIR
+#ifdef BSD4_4
+#ifdef MACOSX_SERVER
+#define _PATH_LOCKDIR          "/var/run/"
+#else
+#define _PATH_LOCKDIR           "/var/spool/lock/"
+#endif
+#else
+#ifdef linux
+#define _PATH_LOCKDIR           "/var/lock/"
+#else
+#define _PATH_LOCKDIR           "/var/spool/locks/"
+#endif /* linux */
+#endif /* BSD4_4 */
+#endif
+
+/*
+ * papd paths
+ */
+#define _PATH_PAPDPRINTCAP     "/etc/printcap"
+#ifdef ultrix
+#define _PATH_PAPDSPOOLDIR     "/usr/spool/lpd"
+#else ultrix
+#define _PATH_PAPDSPOOLDIR     "/var/spool/lpd"
+#endif ultrix
+#ifdef BSD4_4
+#define _PATH_DEVPRINTER       "/var/run/printer"
+#else BSD4_4
+#define _PATH_DEVPRINTER       "/dev/printer"
+#endif BSD4_4
+
+/*
+ * atalkd paths
+ */
+#define _PATH_ATALKDEBUG       "/tmp/atalkd.debug"
+#define _PATH_ATALKDTMP                "atalkd.tmp"
+#define _PATH_ATALKDLOCK       ATALKPATHCAT(_PATH_LOCKDIR,"atalkd")
+
+/*
+ * psorder paths
+ */
+#define _PATH_TMPPAGEORDER     "/tmp/psorderXXXXXX"
+#define _PATH_PAPDLOCK         ATALKPATHCAT(_PATH_LOCKDIR,"papd")
+
+/*
+ * afpd paths
+ */
+#define _PATH_AFPTKT           "/tmp/AFPtktXXXXXX"
+#define _PATH_AFPDLOCK         ATALKPATHCAT(_PATH_LOCKDIR,"afpd")
+
+#endif /* atalk/paths.h */
diff --git a/include/atalk/rtmp.h b/include/atalk/rtmp.h
new file mode 100644 (file)
index 0000000..e5921c2
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#ifndef _ATALK_RTMP_H
+#define _ATALK_RTMP_H 1
+
+#include <netatalk/endian.h>
+
+#define RTMPROP_REQUEST        1
+
+struct rtmpent {
+    u_int16_t   re_net;
+    u_int8_t    re_hops;
+};
+
+#define RTMPHOPS_MAX   15
+#define RTMPHOPS_POISON        31
+
+struct rtmprdhdr {
+    u_int16_t   rrdh_snet;
+    u_int8_t    rrdh_idlen;
+    u_int8_t    rrdh_id;
+};
+
+#endif
diff --git a/include/atalk/server_child.h b/include/atalk/server_child.h
new file mode 100644 (file)
index 0000000..c983a86
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved.
+ */
+
+#ifndef _ATALK_SERVER_CHILD_H
+#define _ATALK_SERVER_CHILD_H 1
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <netatalk/endian.h>
+
+/* useful stuff for child processes. most of this is hidden in 
+ * server_child.c to ease changes in implementation */
+
+#define CHILD_NFORKS   2
+#define CHILD_ASPFORK  0
+#define CHILD_PAPFORK  0
+#define CHILD_DSIFORK  1
+
+typedef struct server_child {
+  void *fork;
+  int count, nsessions, nforks;
+} server_child;
+
+/* server_child.c */
+extern server_child *server_child_alloc __P((const int, const int));
+extern int server_child_add __P((server_child *, const int, const pid_t));
+extern int server_child_remove __P((server_child *, const int, const pid_t));
+extern void server_child_free __P((server_child *));
+
+extern void server_child_kill __P((server_child *, const int, const int));
+extern void server_child_setup __P((server_child *, const int, void (*)()));
+extern void server_child_handler __P((server_child *));
+
+#endif
diff --git a/include/atalk/uam.h b/include/atalk/uam.h
new file mode 100644 (file)
index 0000000..9e6f9f9
--- /dev/null
@@ -0,0 +1,71 @@
+/* Copyright (c) 1999 Adrian Sun (asun@zoology.washington.edu)
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#ifndef UAM_H
+#define UAM_H 1
+
+#include <sys/cdefs.h>
+#include <pwd.h>
+#include <stdarg.h>
+
+/* just a label for exported bits */
+#define UAM_MODULE_EXPORT
+
+/* type of uam */
+#define UAM_MODULE_SERVER       1
+#define UAM_MODULE_CLIENT       2
+
+/* in case something drastic has to change */
+#define UAM_MODULE_VERSION       1
+
+/* things for which we can have uams */
+#define UAM_SERVER_LOGIN         (1 << 0)
+#define UAM_SERVER_CHANGEPW      (1 << 1)
+#define UAM_SERVER_PRINTAUTH     (1 << 2) 
+
+/* options */
+#define UAM_OPTION_USERNAME     (1 << 0) /* get space for username */
+#define UAM_OPTION_GUEST        (1 << 1) /* get guest user */
+#define UAM_OPTION_PASSWDOPT    (1 << 2) /* get the password file */
+#define UAM_OPTION_SIGNATURE    (1 << 3) /* get server signature */
+#define UAM_OPTION_RANDNUM      (1 << 4) /* request a random number */
+#define UAM_OPTION_HOSTNAME     (1 << 5) /* get host name */
+#define UAM_OPTION_COOKIE       (1 << 6) /* cookie handle */
+
+/* some password options. you pass these in the length parameter and
+ * get back the corresponding option. not all of these are implemented. */
+#define UAM_PASSWD_FILENAME     (1 << 0)
+#define UAM_PASSWD_MINLENGTH    (1 << 1)
+#define UAM_PASSWD_MAXFAIL      (1 << 2) /* not implemented yet. */
+#define UAM_PASSWD_EXPIRETIME   (1 << 3) /* not implemented yet. */
+
+/* i'm doing things this way because os x server's dynamic linker
+ * support is braindead. it also allows me to do a little versioning. */
+struct uam_export {
+  int uam_type, uam_version;
+  int (*uam_setup)(const char *);
+  void (*uam_cleanup)(void);
+};
+
+/* register and unregister uams with these functions */
+extern int uam_register __P((const int, const char *, const char *, ...));
+extern void uam_unregister __P((const int, const char *));
+
+/* helper functions */
+extern struct passwd *uam_getname __P((char *, const int));
+extern int uam_checkuser __P((const struct passwd *));
+
+/* afp helper functions */
+extern int uam_afp_read __P((void *, char *, int *,
+                            int (*)(void *, void *, const int)));
+extern int uam_afpserver_option __P((void *, const int, void *, int *));
+
+/* switch.c */
+#define UAM_AFPSERVER_PREAUTH  (0)
+#define UAM_AFPSERVER_POSTAUTH (1 << 0)
+
+extern int uam_afpserver_action __P((const int /*id*/, const int /*switch*/, 
+                                    int (**)(), int (**)()));
+
+#endif
diff --git a/include/atalk/util.h b/include/atalk/util.h
new file mode 100644 (file)
index 0000000..01309be
--- /dev/null
@@ -0,0 +1,46 @@
+#ifndef _ATALK_UTIL_H
+#define _ATALK_UTIL_H 1
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <netatalk/at.h>
+
+extern unsigned const char _diacasemap[], _dialowermap[];
+
+extern char **getifacelist(void);
+extern void freeifacelist(char **);
+
+
+#define diatolower(x)     _dialowermap[(x)]
+#define diatoupper(x)     _diacasemap[(x)]
+extern int atalk_aton     __P((char *, struct at_addr *));
+extern void bprint        __P((char *, int));
+extern int strdiacasecmp  __P((const unsigned char *, const unsigned char *));
+extern int strndiacasecmp __P((const unsigned char *, const unsigned char *,
+                              int));
+extern pid_t server_lock  __P((char * /*program*/, char * /*file*/, 
+                              int /*debug*/));
+#define server_unlock(x)  (unlink(x))
+
+#ifdef NO_DLFCN_H
+extern void *mod_open    __P((const char *));
+extern void *mod_symbol  __P((void *, const char *));
+extern void mod_close    __P((void *));
+#define mod_error()      ""
+#else
+#include <dlfcn.h>
+#ifndef RTLD_NOW
+#define RTLD_NOW 1
+#endif
+#define mod_open(a)      dlopen(a, RTLD_NOW)
+#ifndef DLSYM_PREPEND_UNDERSCORE
+#define mod_symbol(a, b) dlsym(a, b)
+#else
+extern void *mod_symbol  __P((void *, const char *));
+#endif
+#define mod_error()      dlerror()
+#define mod_close(a)     dlclose(a)
+#endif
+
+#endif
diff --git a/include/atalk/zip.h b/include/atalk/zip.h
new file mode 100644 (file)
index 0000000..9079fc1
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+
+#ifndef _ATALK_ZIP_H
+#define _ATALK_ZIP_H 1
+
+#include <netatalk/endian.h>
+
+struct ziphdr {
+    u_int8_t    zh_op;
+    u_int8_t    zh_cnt;
+#define zh_count       zh_cnt
+#define zh_zero                zh_cnt
+#define zh_flags       zh_cnt
+};
+
+struct zipreplent {
+    u_int16_t   zre_net;
+    u_int8_t    zre_zonelen;
+};
+
+#define ZIPOP_QUERY    1
+#define ZIPOP_REPLY    2
+#define ZIPOP_TAKEDOWN 3       /* XXX */
+#define ZIPOP_BRINGUP  4       /* XXX */
+#define ZIPOP_GNI      5
+#define ZIPOP_GNIREPLY 6
+#define ZIPOP_NOTIFY   7
+#define ZIPOP_EREPLY   8
+#define ZIPOP_GETMYZONE        7
+#define ZIPOP_GETZONELIST 8
+#define ZIPOP_GETLOCALZONES 9
+
+#define ZIPGNI_INVALID 0x80
+#define ZIPGNI_ONEZONE 0x20
+
+#endif
diff --git a/libatalk/Makefile b/libatalk/Makefile
new file mode 100644 (file)
index 0000000..08be055
--- /dev/null
@@ -0,0 +1,77 @@
+ALL=   asp dsi atp nbp adouble util compat netddp 
+# cnid
+LIBS=  asp/asplib atp/atplib nbp/nbplib adouble/adoublelib \
+       dsi/dsilib util/utillib compat/compatlib netddp/netddplib 
+# cnid/cnidlib
+PLIBS= asp/asplib_p atp/atplib_p nbp/nbplib_p adouble/adoublelib_p \
+       dsi/dsilib_p util/utillib_p compat/compatlib_p  \
+       netddp/netddplib 
+# cnid/cnidlib_p
+TAGSFILE=tags
+CC=cc
+INSTALL=install
+RANLIB=ranlib
+
+all:   libatalk.a
+
+libatalk.a: ${LIBS}
+       rm -rf tmp
+       -mkdir tmp
+#      touch tmp/________64ELEL_
+       for i in ${ALL}; do (cd tmp; ar x ../$$i/$${i}lib); done
+       (cd tmp; ar cr libatalk.a  *.o; ${RANLIB} libatalk.a)
+       mv tmp/libatalk.a libatalk.a
+       ${RANLIB} libatalk.a
+       rm -rf tmp tmp_p
+       -mkdir tmp_p
+#      touch tmp_p/________64ELEL_
+       for i in ${ALL}; do (cd tmp_p; ar x ../$$i/$${i}lib_p); done
+       (cd tmp_p; ar cr libatalk_p.a  *.o; ${RANLIB} libatalk_p.a)
+       mv tmp_p/libatalk_p.a libatalk_p.a
+       ${RANLIB} libatalk_p.a
+       rm -rf tmp_p
+
+${ALL}: FRC
+       if [ ! -f /usr/lib/librpcsvc.a -a ! -f /lib/librpcsvc.a ]; then \
+           RPCSVCDEFS=-DNEED_RQUOTA; \
+       fi; \
+       cd $@; ${MAKE} ${MFLAGS} CC="${CC}" DEFS="${DEFS}" \
+           RPCSVCDEFS="$${RPCSVCDEFS}" \
+           OPTOPTS="${OPTOPTS}" TCPWRAPDIR="${TCPWRAPDIR}" DB2DIR="${DB2DIR}"
+
+asp/asplib:    asp
+dsi/dsilib:     dsi
+atp/atplib:    atp
+nbp/nbplib:    nbp
+adouble/adoublelib:    adouble
+util/utillib:  util
+compat/compatlib:      compat
+cnid/cnidlib:  cnid
+netddp/netddplib:      netddp
+
+FRC:
+
+tags:
+       for i in ${ALL}; do \
+           (cd $$i; ${MAKE} ${MFLAGS} DEFS="${DEFS}" \
+               TAGSFILE=../${TAGSFILE} tags); \
+       done
+
+install: all
+       -mkdir ${LIBDIR}
+       ${INSTALL} -c libatalk.a ${LIBDIR}/libatalk.a
+       (cd ${LIBDIR}; ranlib ${LIBDIR}/libatalk.a)
+       ${INSTALL} -c libatalk_p.a ${LIBDIR}/libatalk_p.a
+       (cd ${LIBDIR}; ranlib ${LIBDIR}/libatalk_p.a)
+
+clean:
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} clean); \
+       done
+       rm -f *[Ee]rrs libatalk.a libatalk_p.a
+       rm -rf tmp tmp_p
+
+depend:
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} DEFS=${DEFS} depend); \
+       done
diff --git a/libatalk/adouble/Makefile b/libatalk/adouble/Makefile
new file mode 100644 (file)
index 0000000..1f549e3
--- /dev/null
@@ -0,0 +1,58 @@
+SRC = ad_open.c ad_flush.c ad_read.c ad_write.c ad_size.c ad_mmap.c \
+      ad_lock.c ad_date.c ad_attr.c ad_sendfile.c
+OBJ = ad_open.o ad_flush.o ad_read.o ad_write.o ad_size.o ad_mmap.o \
+      ad_lock.o ad_date.o ad_attr.o ad_sendfile.o
+
+INCPATH=       -I../../include
+CFLAGS=        ${DEFS} ${OPTOPTS} ${INCPATH}
+TAGSFILE=      tags
+CC=    cc
+
+all : profiled adoublelib
+
+profiled:
+       -mkdir profiled
+
+adoublelib adoublelib_p : ${OBJ}
+       @echo "building profiled adoublelib"
+       @cd profiled; ar cru ../adoublelib_p ${OBJ}
+       @echo "building normal adoublelib"
+       @ar cru adoublelib ${OBJ}
+
+.c.o :
+       ${CC} -p ${CFLAGS} -c $*.c
+       -ld -r -o $*.o- $*.o
+       mv $*.o- profiled/$*.o
+       ${CC} ${CFLAGS} -c $*.c
+       -ld -r -o $*.o- $*.o
+       mv $*.o- $*.o
+
+clean :
+       rm -f *.o profiled/*.o *.bak *[Ee]rrs tags
+       rm -f adoublelib adoublelib_p
+
+tags : ${SRC}
+       cwd=`pwd`; \
+       for i in ${SRC}; do \
+           ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+       done
+
+depend :
+       for i in ${SRC} ; do \
+           ${CC} -M ${DEFS} ${INCPATH} $$i | \
+           awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+               else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+               else rec = rec " " $$2 } } \
+               END { print rec } ' >> makedep; done
+       sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+       cat makedep >> Makefile.tmp
+       rm makedep
+       echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+       echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+       echo '# see make depend above' >> Makefile.tmp
+       rm -f Makefile.bak
+       cp Makefile Makefile.bak
+       mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
diff --git a/libatalk/adouble/ad_attr.c b/libatalk/adouble/ad_attr.c
new file mode 100644 (file)
index 0000000..fd00a10
--- /dev/null
@@ -0,0 +1,33 @@
+#include <string.h>
+#include <atalk/adouble.h>
+
+#define FILEIOFF_ATTR 14
+#define AFPFILEIOFF_ATTR 2
+
+int ad_getattr(const struct adouble *ad, u_int16_t *attr)
+{
+  if (ad->ad_version == AD_VERSION1)
+    memcpy(attr, ad_entry(ad, ADEID_FILEI) + FILEIOFF_ATTR,
+          sizeof(u_int16_t));
+  else if (ad->ad_version == AD_VERSION2)
+    memcpy(attr, ad_entry(ad, ADEID_AFPFILEI) + AFPFILEIOFF_ATTR,
+          sizeof(u_int16_t));
+  else 
+    return -1;
+
+  return 0;
+}
+
+int ad_setattr(const struct adouble *ad, const u_int16_t attr)
+{
+  if (ad->ad_version == AD_VERSION1)
+    memcpy(ad_entry(ad, ADEID_FILEI) + FILEIOFF_ATTR, &attr,
+          sizeof(attr));
+  else if (ad->ad_version == AD_VERSION2)
+    memcpy(ad_entry(ad, ADEID_AFPFILEI) + AFPFILEIOFF_ATTR, &attr,
+          sizeof(attr));
+  else 
+    return -1;
+
+  return 0;
+}
diff --git a/libatalk/adouble/ad_date.c b/libatalk/adouble/ad_date.c
new file mode 100644 (file)
index 0000000..0302cc4
--- /dev/null
@@ -0,0 +1,54 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <atalk/adouble.h>
+
+int ad_setdate(const struct adouble *ad, 
+              unsigned int dateoff, u_int32_t date)
+{
+  int xlate = (dateoff & AD_DATE_UNIX);
+
+  dateoff &= AD_DATE_MASK;
+  if (xlate)
+    date = AD_DATE_FROM_UNIX(date);
+
+  if (ad->ad_version == AD_VERSION1) {
+    if (dateoff > AD_DATE_BACKUP)
+      return -1;
+    memcpy(ad_entry(ad, ADEID_FILEI) + dateoff, &date, sizeof(date));
+
+  } else if (ad->ad_version == AD_VERSION2) {
+    if (dateoff > AD_DATE_ACCESS)
+      return -1;
+    memcpy(ad_entry(ad, ADEID_FILEDATESI) + dateoff, &date, sizeof(date));
+
+  } else 
+    return -1;
+
+  return 0;
+}
+
+int ad_getdate(const struct adouble *ad,
+              unsigned int dateoff, u_int32_t *date) 
+{
+  int xlate = (dateoff & AD_DATE_UNIX);
+
+  dateoff &= AD_DATE_MASK;
+  if (ad->ad_version == AD_VERSION1) {
+    if (dateoff > AD_DATE_BACKUP)
+      return -1;
+    memcpy(date, ad_entry(ad, ADEID_FILEI) + dateoff, sizeof(u_int32_t));
+
+  } else if (ad->ad_version == AD_VERSION2) {
+    if (dateoff > AD_DATE_ACCESS)
+      return -1;
+    memcpy(date, ad_entry(ad, ADEID_FILEDATESI) + dateoff, sizeof(u_int32_t));
+
+  } else 
+    return -1;
+
+  if (xlate)
+    *date = AD_DATE_TO_UNIX(*date);
+  return 0;
+}
diff --git a/libatalk/adouble/ad_flush.c b/libatalk/adouble/ad_flush.c
new file mode 100644 (file)
index 0000000..180b33d
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <errno.h>
+
+#include <netatalk/endian.h>
+#include <atalk/adouble.h>
+
+#include "ad_private.h"
+
+/* rebuild the header */
+void ad_rebuild_header(struct adouble *ad)
+{
+    u_int32_t          eid;
+    u_int16_t          nent;
+    char               *buf, *nentp;
+
+    /*
+     * Rebuild any header information that might have changed.
+     */
+    buf = ad->ad_data;
+    ad->ad_magic = htonl( ad->ad_magic );
+    memcpy(buf, &ad->ad_magic, sizeof( ad->ad_magic ));
+    ad->ad_magic = ntohl( ad->ad_magic );
+    buf += sizeof( ad->ad_magic );
+    
+    ad->ad_version = htonl( ad->ad_version );
+    memcpy(buf, &ad->ad_version, sizeof( ad->ad_version ));
+    ad->ad_version = ntohl( ad->ad_version );
+    buf += sizeof( ad->ad_version );
+    memcpy(buf, ad->ad_filler, sizeof( ad->ad_filler ));
+    buf += sizeof( ad->ad_filler );
+    
+    nentp = buf;
+    buf += sizeof( nent );
+    for ( eid = 0, nent = 0; eid < ADEID_MAX; eid++ ) {
+      if ( ad->ad_eid[ eid ].ade_off == 0 ) {
+       continue;
+      }
+      eid = htonl( eid );
+      memcpy(buf, &eid, sizeof( eid ));
+      eid = ntohl( eid );
+      buf += sizeof( eid );
+      ad->ad_eid[ eid ].ade_off = htonl( ad->ad_eid[ eid ].ade_off );
+      memcpy(buf, &ad->ad_eid[ eid ].ade_off,
+            sizeof( ad->ad_eid[ eid ].ade_off ));
+      ad->ad_eid[ eid ].ade_off = ntohl( ad->ad_eid[ eid ].ade_off );
+      buf += sizeof( ad->ad_eid[ eid ].ade_off );
+      ad->ad_eid[ eid ].ade_len = htonl( ad->ad_eid[ eid ].ade_len );
+      memcpy(buf, &ad->ad_eid[ eid ].ade_len, 
+            sizeof( ad->ad_eid[ eid ].ade_len ));
+      ad->ad_eid[ eid ].ade_len = ntohl( ad->ad_eid[ eid ].ade_len );
+      buf += sizeof( ad->ad_eid[ eid ].ade_len );
+      nent++;
+    }
+    nent = htons( nent );
+    memcpy(nentp, &nent, sizeof( nent ));
+}
+
+
+int ad_flush( ad, adflags )
+    struct adouble     *ad;
+    int                        adflags;
+{
+#ifndef USE_MMAPPED_HEADERS
+    int len;
+#endif
+
+    if (( adflags & ADFLAGS_HF ) && ( ad->ad_hf.adf_flags & O_RDWR )) {
+       /* sync our header */
+        ad_rebuild_header(ad);
+
+#ifdef USE_MMAPPED_HEADERS
+       /* now sync it */
+#ifdef MS_SYNC
+       msync(ad->ad_data, ad_getentryoff(ad, ADEID_RFORK),
+             MS_SYNC | MS_INVALIDATE);
+#else
+       msync(ad->ad_data, ad_getentryoff(ad, ADEID_RFORK));
+#endif
+
+#else
+       if ( ad->ad_hf.adf_off != 0 ) {
+           if ( lseek( ad->ad_hf.adf_fd, 0L, SEEK_SET ) < 0L ) {
+               return( -1 );
+           }
+           ad->ad_hf.adf_off = 0;
+       }
+
+       /* now flush it out */
+       len = ad_getentryoff(ad, ADEID_RFORK);
+       if (write( ad->ad_hf.adf_fd, ad->ad_data, len) != len) {
+           if ( errno == 0 ) {
+               errno = EIO;
+           }
+           return( -1 );
+       }
+       ad->ad_hf.adf_off = len;
+#endif
+    }
+
+    return( 0 );
+}
+
+/* use refcounts so that we don't have to re-establish fcntl locks. */
+int ad_close( ad, adflags )
+    struct adouble     *ad;
+    int                        adflags;
+{
+    int                        err = 0;
+
+    if (( adflags & ADFLAGS_DF ) && ad->ad_df.adf_fd != -1 &&
+       !(--ad->ad_df.adf_refcount)) {
+       if ( close( ad->ad_df.adf_fd ) < 0 ) {
+           err = -1;
+       }
+       ad->ad_df.adf_fd = -1;
+       adf_lock_free(&ad->ad_df);
+    }
+
+    if (( adflags & ADFLAGS_HF ) && ad->ad_hf.adf_fd != -1 &&
+       !(--ad->ad_hf.adf_refcount)) {
+#ifdef USE_MMAPPED_HEADERS
+        if (ad->ad_data != MAP_FAILED)
+         munmap(ad->ad_data, ad_getentryoff(ad, ADEID_RFORK));
+#endif
+       if ( close( ad->ad_hf.adf_fd ) < 0 ) {
+           err = -1;
+       }
+       ad->ad_hf.adf_fd = -1;
+       adf_lock_free(&ad->ad_hf);
+    }
+
+    return( err );
+}
diff --git a/libatalk/adouble/ad_lock.c b/libatalk/adouble/ad_lock.c
new file mode 100644 (file)
index 0000000..d848ccd
--- /dev/null
@@ -0,0 +1,405 @@
+/* 
+ * Copyright (c) 1998,1999 Adrian Sun (asun@zoology.washington.edu)
+ * All Rights Reserved. See COPYRIGHT for more information.
+ *
+ * Byte-range locks. This uses either whole-file flocks to fake byte
+ * locks or fcntl-based actual byte locks. Because fcntl locks are
+ * process-oriented, we need to keep around a list of file descriptors
+ * that refer to the same file. Currently, this doesn't serialize access 
+ * to the locks. as a result, there's the potential for race conditions. 
+ *
+ * TODO: fix the race when reading/writing.
+ *       keep a pool of both locks and reference counters around so that
+ *       we can save on mallocs. we should also use a tree to keep things
+ *       sorted.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <atalk/adouble.h>
+
+#include "ad_private.h"
+
+/* translate between ADLOCK styles and specific locking mechanisms */
+#define XLATE_FLOCK(type) ((type) == ADLOCK_RD ? LOCK_SH : \
+((type) == ADLOCK_WR ? LOCK_EX : \
+ ((type) == ADLOCK_CLR ? LOCK_UN : -1)))
+
+#define XLATE_FCNTL_LOCK(type) ((type) == ADLOCK_RD ? F_RDLCK : \
+((type) == ADLOCK_WR ? F_WRLCK : \
+ ((type) == ADLOCK_CLR ? F_UNLCK : -1))) 
+     
+#define OVERLAP(a,alen,b,blen) ((!(alen) && (a) <= (b)) || \
+                               (!(blen) && (b) <= (a)) || \
+                               ((((a) + (alen)) > (b)) && \
+                               (((b) + (blen)) > (a))))
+
+
+/* allocation for lock regions. we allocate aggressively and shrink
+ * only in large chunks. */
+#define ARRAY_BLOCK_SIZE 10 
+#define ARRAY_FREE_DELTA 100
+
+/* remove a lock and compact space if necessary */
+static __inline__ void adf_freelock(struct ad_fd *ad, const int i)
+{
+    adf_lock_t *lock = ad->adf_lock + i;
+
+    if (--(*lock->refcount) < 1) {
+       free(lock->refcount); 
+       lock->lock.l_type = F_UNLCK;
+       fcntl(ad->adf_fd, F_SETLK, &lock->lock); /* unlock */
+    }
+
+    ad->adf_lockcount--;
+
+    /* move another lock into the empty space */
+    if (i < ad->adf_lockcount) {
+        memcpy(lock, lock + ad->adf_lockcount - i, sizeof(adf_lock_t));
+    }
+
+    /* free extra cruft if we go past a boundary. we always want to
+     * keep at least some stuff around for allocations. this wastes
+     * a bit of space to save time on reallocations. */
+    if ((ad->adf_lockmax > ARRAY_FREE_DELTA) &&
+       (ad->adf_lockcount + ARRAY_FREE_DELTA < ad->adf_lockmax)) {
+           struct adf_lock_t *tmp;
+
+           tmp = (struct adf_lock_t *) 
+                   realloc(ad->adf_lock, sizeof(adf_lock_t)*
+                           (ad->adf_lockcount + ARRAY_FREE_DELTA));
+           if (tmp) {
+               ad->adf_lock = tmp;
+               ad->adf_lockmax = ad->adf_lockcount + ARRAY_FREE_DELTA;
+           }
+    }
+}
+
+
+/* this needs to deal with the following cases:
+ * 1) user is the only user of the lock 
+ * 2) user shares a read lock with another user
+ *
+ * i converted to using arrays of locks. everytime a lock
+ * gets removed, we shift all of the locks down.
+ */
+static __inline__ void adf_unlock(struct ad_fd *ad, int fd, const int user)
+{
+    adf_lock_t *lock = ad->adf_lock;
+    int i;
+
+    for (i = 0; i < ad->adf_lockcount; i++) {
+      if (lock[i].user == user) {
+       /* we're really going to delete this lock. note: read locks
+           are the only ones that allow refcounts > 1 */
+        adf_freelock(ad, i);
+        i--; /* we shifted things down, so we need to backtrack */
+       }
+    }
+}
+
+/* relock any byte lock that overlaps off/len. unlock everything
+ * else. */
+static __inline__ void adf_relockrange(struct ad_fd *ad, int fd,
+                                      const off_t off, const size_t len)
+{
+    adf_lock_t *lock = ad->adf_lock;
+    int i;
+    
+    for (i = 0; i < ad->adf_lockcount; i++) {
+      if (OVERLAP(off, len, lock[i].lock.l_start, lock[i].lock.l_len)) 
+       fcntl(fd, F_SETLK, &lock[i].lock);
+    }
+}
+
+
+/* find a byte lock that overlaps off/len for a particular user */
+static __inline__ int adf_findlock(struct ad_fd *ad,
+                                  const int user, const int type,
+                                  const off_t off,
+                                  const size_t len)
+{
+  adf_lock_t *lock = ad->adf_lock;
+  int i;
+  
+  for (i = 0; i < ad->adf_lockcount; i++) {
+    if ((((type & ADLOCK_RD) && (lock[i].lock.l_type == F_RDLCK)) ||
+       ((type & ADLOCK_WR) && (lock[i].lock.l_type == F_WRLCK))) &&
+       (lock[i].user == user) && 
+       OVERLAP(off, len, lock[i].lock.l_start, lock[i].lock.l_len)) {
+      return i;
+    }
+  }
+
+  return -1;
+}
+
+
+/* search other user lock lists */
+static __inline__  int adf_findxlock(struct ad_fd *ad, 
+                                    const int user, const int type,
+                                    const off_t off,
+                                    const size_t len)
+{
+  adf_lock_t *lock = ad->adf_lock;
+  int i;
+  
+  for (i = 0; i < ad->adf_lockcount; i++) {
+    if ((((type & ADLOCK_RD) && (lock[i].lock.l_type == F_RDLCK)) ||
+        ((type & ADLOCK_WR) && (lock[i].lock.l_type == F_WRLCK))) &&
+       (lock[i].user != user) && 
+       OVERLAP(off, len, lock[i].lock.l_start, lock[i].lock.l_len)) 
+           return i;
+  } 
+  return -1;
+}
+
+/* okay, this needs to do the following:
+ * 1) check current list of locks. error on conflict.
+ * 2) apply the lock. error on conflict with another process.
+ * 3) update the list of locks this file has.
+ * 
+ * NOTE: this treats synchronization locks a little differently. we
+ *       do the following things for those:
+ *       1) if the header file exists, all the locks go in the beginning
+ *          of that.
+ *       2) if the header file doesn't exist, we stick the locks
+ *          in the locations specified by AD_FILELOCK_RD/WR.
+ */
+#define LOCK_RSRC_RD (0)
+#define LOCK_RSRC_WR (1)
+#define LOCK_DATA_RD (2)
+#define LOCK_DATA_WR (3)
+int ad_fcntl_lock(struct adouble *ad, const u_int32_t eid, const int type,
+                 const off_t off, const size_t len, const int user)
+{
+  struct flock lock;
+  struct ad_fd *adf;
+  adf_lock_t *adflock, *oldlock;
+  int i;
+  
+  lock.l_start = off;
+  if (eid == ADEID_DFORK) {
+    if ((type & ADLOCK_FILELOCK) && (ad_hfileno(ad) != -1)) {
+      adf = &ad->ad_hf;
+      if (off == AD_FILELOCK_WR)
+       lock.l_start = LOCK_DATA_WR;
+      else if (off == AD_FILELOCK_RD)
+       lock.l_start = LOCK_DATA_RD;
+    } else
+      adf = &ad->ad_df;
+
+  } else { /* rfork */
+    adf = &ad->ad_hf;
+    if (type & ADLOCK_FILELOCK) {
+      if (off == AD_FILELOCK_WR)
+       lock.l_start = LOCK_RSRC_WR;
+      else if (off == AD_FILELOCK_RD)
+       lock.l_start = LOCK_RSRC_RD;
+    } else
+      lock.l_start += ad_getentryoff(ad, eid);
+  }
+
+  lock.l_type = XLATE_FCNTL_LOCK(type & ADLOCK_MASK);
+
+  /* see if it's locked by another user. 
+   * NOTE: this guarantees that any existing locks must be at most
+   * read locks. we use ADLOCK_WR/RD because F_RD/WRLCK aren't
+   * guaranteed to be ORable. */
+  if (adf_findxlock(adf, user, ADLOCK_WR | 
+                   ((type & ADLOCK_WR) ? ADLOCK_RD : 0), 
+                   lock.l_start, len) > -1) {
+    errno = EACCES;
+    return -1;
+  }
+  
+  /* look for any existing lock that we may have */
+  i = adf_findlock(adf, user, ADLOCK_RD | ADLOCK_WR, lock.l_start, len);
+  adflock = (i < 0) ? NULL : adf->adf_lock + i;
+
+  /* here's what we check for:
+     1) we're trying to re-lock a lock, but we didn't specify an update.
+     2) we're trying to free only part of a lock. 
+     3) we're trying to free a non-existent lock. */
+  if ((!adflock && (lock.l_type == F_UNLCK)) ||
+      (adflock && !(type & ADLOCK_UPGRADE) && 
+       ((lock.l_type != F_UNLCK) || (adflock->lock.l_start != lock.l_start) ||
+       (adflock->lock.l_len != len)))) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  lock.l_whence = SEEK_SET;
+  lock.l_len = len;
+
+  /* now, update our list of locks */
+  /* clear the lock */
+  if (lock.l_type == F_UNLCK) { 
+    adf_freelock(adf, i);
+    return 0;
+  }
+
+  /* attempt to lock the file. */
+  if (fcntl(adf->adf_fd, F_SETLK, &lock) < 0) 
+    return -1;
+
+  /* we upgraded this lock. */
+  if (adflock && (type & ADLOCK_UPGRADE)) {
+    memcpy(&adflock->lock, &lock, sizeof(lock));
+    return 0;
+  } 
+
+  /* it wasn't an upgrade */
+  oldlock = NULL;
+  if ((lock.l_type = F_RDLCK) &&
+      ((i = adf_findxlock(adf, user, ADLOCK_RD, lock.l_start, len)) > -1)) {
+    oldlock = adf->adf_lock + i;
+  } 
+    
+  /* no more space. this will also happen if lockmax == lockcount == 0 */
+  if (adf->adf_lockmax == adf->adf_lockcount) { 
+    adf_lock_t *tmp = (adf_lock_t *) 
+           realloc(adf->adf_lock, sizeof(adf_lock_t)*
+                   (adf->adf_lockmax + ARRAY_BLOCK_SIZE));
+    if (!tmp)
+      goto fcntl_lock_err;
+    adf->adf_lock = tmp;
+    adf->adf_lockmax += ARRAY_BLOCK_SIZE;
+  } 
+  adflock = adf->adf_lock + adf->adf_lockcount;
+
+  /* fill in fields */
+  memcpy(&adflock->lock, &lock, sizeof(lock));
+  adflock->user = user;
+  if (oldlock)
+    adflock->refcount = oldlock->refcount;
+  else if ((adflock->refcount = calloc(1, sizeof(int))) == NULL) {
+    goto fcntl_lock_err;
+  }
+  
+  (*adflock->refcount)++;
+  adf->adf_lockcount++;
+  return 0;
+
+fcntl_lock_err:
+  lock.l_type = F_UNLCK;
+  fcntl(adf->adf_fd, F_SETLK, &lock);
+  return -1;
+}
+
+
+/* with temp locks, we don't need to distinguish within the same
+ * process as everything is single-threaded. in addition, if
+ * multi-threading gets added, it will only be in a few areas. */
+int ad_fcntl_tmplock(struct adouble *ad, const u_int32_t eid, const int type,
+                    const off_t off, const size_t len)
+{
+  struct flock lock;
+  struct ad_fd *adf;
+  int err;
+
+  lock.l_start = off;
+  if (eid == ADEID_DFORK) {
+    adf = &ad->ad_df;
+  } else {
+    adf = &ad->ad_hf;
+    lock.l_start += ad_getentryoff(ad, eid);
+  }
+  lock.l_type = XLATE_FCNTL_LOCK(type & ADLOCK_MASK);
+  lock.l_whence = SEEK_SET;
+  lock.l_len = len;
+
+  /* okay, we might have ranges byte-locked. we need to make sure that
+   * we restore the appropriate ranges once we're done. so, we check
+   * for overlap on an unlock and relock. 
+   * XXX: in the future, all the byte locks will be sorted and contiguous.
+   *      we just want to upgrade all the locks and then downgrade them
+   *      here. */
+  err = fcntl(adf->adf_fd, F_SETLK, &lock);
+  if (!err && (lock.l_type == F_UNLCK))
+    adf_relockrange(adf, adf->adf_fd, lock.l_start, len);
+
+  return err;
+}
+
+
+void ad_fcntl_unlock(struct adouble *ad, const int user)
+{
+  if (ad->ad_df.adf_fd != -1) {
+    adf_unlock(&ad->ad_df, ad->ad_df.adf_fd, user);
+  }
+  if (ad->ad_hf.adf_fd != -1) {
+    adf_unlock(&ad->ad_hf, ad->ad_hf.adf_fd, user);
+  }
+}
+
+/* byte-range locks. ad_lock is used by afp_bytelock and afp_openfork
+ * to establish locks. both ad_lock and ad_tmplock take 0, 0, 0 to
+ * signify locking of the entire file. in the absence of working 
+ * byte-range locks, this will default to file-wide flock-style locks.
+ */
+int ad_flock_lock(struct adouble *ad, const u_int32_t eid, const int type,
+                 const off_t off, const size_t len, const int user)
+{
+  int err, lock_type;
+  
+  lock_type = XLATE_FLOCK(type & ADLOCK_MASK);
+  if (eid == ADEID_DFORK) {
+    if ((err = flock(ad_dfileno(ad), lock_type | LOCK_NB)) == 0)
+      ad->ad_df.adf_lockcount = lock_type;
+  } else if ((err = flock(ad_hfileno(ad), lock_type | LOCK_NB)) == 0)
+    ad->ad_hf.adf_lockcount = lock_type;
+
+  if (err) {
+    if ((EWOULDBLOCK != EAGAIN) && (errno == EWOULDBLOCK))
+      errno = EAGAIN;
+    return -1;
+  } 
+
+  return 0; 
+}
+
+/* ad_tmplock is used by afpd to lock actual read/write operations. 
+ * it saves the current lock state before attempting to lock to prevent
+ * mixups. if byte-locks don't exist, it will lock the entire file with
+ * an flock. we can be a little smart here by just upgrading/downgrading
+ * locks. */
+int ad_flock_tmplock(struct adouble *ad, const u_int32_t eid, const int type,
+                    const off_t off, const size_t len) 
+{
+  int fd, oldlock, lock_type;
+  
+  if (eid == ADEID_DFORK) {
+    oldlock = ad->ad_df.adf_lockcount;
+    fd = ad_dfileno(ad);
+  } else {
+    oldlock = ad->ad_hf.adf_lockcount;
+    fd = ad_hfileno(ad);
+  }
+
+  /* if we already have a write lock, we don't need to do anything */
+  if (oldlock == LOCK_EX) {
+    return 0;
+  }
+
+  /* if we have a read lock, upgrade it if necessary */
+  lock_type = XLATE_FLOCK(type & ADLOCK_MASK);
+  if (oldlock == LOCK_SH) {
+    if (lock_type == LOCK_EX) 
+      return flock(fd, LOCK_EX | LOCK_NB);
+    else if (lock_type == LOCK_UN) /* reset it */
+      return flock(fd, LOCK_SH | LOCK_NB);
+    else /* do nothing */
+      return 0;
+  }
+
+  /* if we don't already have a lock, just do it. */
+  return flock(fd, lock_type | LOCK_NB);
+}
+
+
diff --git a/libatalk/adouble/ad_mmap.c b/libatalk/adouble/ad_mmap.c
new file mode 100644 (file)
index 0000000..433249c
--- /dev/null
@@ -0,0 +1,104 @@
+/* ad_mmap provides interfaces to memory mapped files. as this is the
+ * case, we don't have to deal w/ temporary buffers such as
+ * ad_data. the ad_mmap routines are designed to not interact w/ the
+ * ad_read/ad_write routines to avoid confusion.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <atalk/adouble.h>
+
+#include "ad_private.h"
+
+static __inline__ void *ad_mmap(const size_t length, const int prot,
+                               const int flags, const int fd, 
+                               const off_t offset)
+{
+  return mmap(0, length, prot, flags, fd, offset);
+}
+
+/* this just sets things up for mmap. as mmap can handle offsets,
+ * we need to reset the file position before handing it off */
+void *ad_mmapread(struct adouble *ad, const u_int32_t eid, 
+                 const off_t off, const size_t buflen)
+{
+    /* data fork */
+    if ( eid == ADEID_DFORK ) {
+      if ( lseek( ad->ad_df.adf_fd, 0, SEEK_SET ) < 0 ) {
+       perror( "df lseek" );
+       return (void *) -1;
+      }
+      ad->ad_df.adf_off = 0;
+      return ad_mmap(buflen, PROT_READ | PROT_WRITE, MAP_PRIVATE, 
+                    ad->ad_df.adf_fd, off);
+
+    }
+
+    /* resource fork */
+    if ( lseek( ad->ad_hf.adf_fd, 0, SEEK_SET ) < 0 ) {
+      perror( "hf lseek" );
+      return (void *) -1;
+    }
+    ad->ad_hf.adf_off = 0;
+    return ad_mmap(buflen, PROT_READ | PROT_WRITE, MAP_PRIVATE, 
+                  ad->ad_hf.adf_fd, ad->ad_eid[eid].ade_off + off);
+}
+
+
+/* to do writeable mmaps correctly, we actually need to make sure that
+ * the file to be mapped is large enough. that's what all the initial
+ * mess is for. */
+void *ad_mmapwrite(struct adouble *ad, const u_int32_t eid,
+                  off_t off, const int end, const size_t buflen)
+{
+    struct stat st;
+
+    /* data fork */
+    if ( eid == ADEID_DFORK ) {
+        if ( fstat( ad->ad_df.adf_fd, &st ) < 0 ) {
+           return (void *) -1;
+        }
+
+       if ( end ) {
+           off = st.st_size - off;
+       }
+
+       /* make sure the file is large enough */
+       if (st.st_size < buflen + off) 
+         ftruncate(ad->ad_df.adf_fd, buflen + off);
+
+       if ( lseek( ad->ad_df.adf_fd, 0, SEEK_SET ) < 0 ) {
+         return (void *) -1;
+       }
+       ad->ad_df.adf_off = 0;
+       return ad_mmap(buflen, PROT_READ | PROT_WRITE, MAP_SHARED,
+                      ad->ad_df.adf_fd, off);
+    }
+
+    
+    if ( fstat( ad->ad_hf.adf_fd, &st ) < 0 ) {
+        return (void *) -1;
+    }
+    
+    if ( end ) {
+       off = ad->ad_eid[ eid ].ade_len - off;
+    }
+    
+    off += ad->ad_eid[eid].ade_off;
+
+    /* make sure the file is large enough */
+    if (st.st_size < buflen + off) 
+      ftruncate(ad->ad_hf.adf_fd, buflen + off);
+
+    if ( lseek( ad->ad_hf.adf_fd, 0, SEEK_SET ) < 0 ) {
+      return (void *) -1;
+    }
+    ad->ad_hf.adf_off = 0;
+    return ad_mmap(buflen, PROT_READ | PROT_WRITE, MAP_SHARED,
+                  ad->ad_hf.adf_fd, off);
+}
diff --git a/libatalk/adouble/ad_open.c b/libatalk/adouble/ad_open.c
new file mode 100644 (file)
index 0000000..e685a34
--- /dev/null
@@ -0,0 +1,792 @@
+/*
+ * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu)
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <syslog.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+
+#include <netatalk/endian.h>
+#include <atalk/adouble.h>
+
+#include "ad_private.h"
+
+#ifndef MAX
+#define MAX(a, b)  ((a) < (b) ? (b) : (a))
+#endif
+
+/*
+ * AppleDouble entry default offsets.
+ * The layout looks like this:
+ *
+ * this is the v1 layout:
+ *       255     200             16      32              N
+ *     |  NAME |    COMMENT    | FILEI |    FINDERI    | RFORK |
+ *
+ * we need to change it to look like this:
+ *
+ * v2 layout:
+ * field       length (in bytes)
+ * NAME        255
+ * COMMENT     200
+ * FILEDATESI  16     replaces FILEI
+ * FINDERI     32  
+ * DID          4     new
+ * AFPFILEI     4     new
+ * SHORTNAME   12     8.3 new
+ * RFORK        N
+ * 
+ * so, all we need to do is replace FILEI with FILEDATESI, move RFORK,
+ * and add in the new fields.
+ *
+ * NOTE: the HFS module will need similar modifications to interact with
+ * afpd correctly.
+ */
+#define ADEDOFF_MAGIC        (0)
+#define ADEDOFF_VERSION      (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
+#define ADEDOFF_FILLER       (ADEDOFF_VERSION + ADEDLEN_VERSION)
+#define ADEDOFF_NENTRIES     (ADEDOFF_FILLER + ADEDLEN_FILLER)
+
+/* initial lengths of some of the fields */
+#define ADEDLEN_INIT     0
+
+#define ADEID_NUM_V1         5
+#define ADEDOFF_NAME_V1             (AD_HEADER_LEN + ADEID_NUM_V1*AD_ENTRY_LEN)
+#define ADEDOFF_COMMENT_V1   (ADEDOFF_NAME_V1 + ADEDLEN_NAME)
+#define ADEDOFF_FILEI        (ADEDOFF_COMMENT_V1 + ADEDLEN_COMMENT)
+#define ADEDOFF_FINDERI_V1   (ADEDOFF_FILEI + ADEDLEN_FILEI)
+#define ADEDOFF_RFORK_V1     (ADEDOFF_FINDERI_V1 + ADEDLEN_FINDERI)
+
+/* i stick things in a slightly different order than their eid order in 
+ * case i ever want to separate RootInfo behaviour from the rest of the 
+ * stuff. */
+#define ADEID_NUM_V2         9
+#define ADEDOFF_NAME_V2      (AD_HEADER_LEN + ADEID_NUM_V2*AD_ENTRY_LEN)
+#define ADEDOFF_COMMENT_V2   (ADEDOFF_NAME_V2 + ADEDLEN_NAME)
+#define ADEDOFF_FILEDATESI   (ADEDOFF_COMMENT_V2 + ADEDLEN_COMMENT)
+#define ADEDOFF_FINDERI_V2   (ADEDOFF_FILEDATESI + ADEDLEN_FILEDATESI)
+#define ADEDOFF_DID         (ADEDOFF_FINDERI_V2 + ADEDLEN_FINDERI)
+#define ADEDOFF_AFPFILEI     (ADEDOFF_DID + ADEDLEN_DID)
+#define ADEDOFF_SHORTNAME    (ADEDOFF_AFPFILEI + ADEDLEN_AFPFILEI)
+#define ADEDOFF_PRODOSFILEI  (ADEDOFF_SHORTNAME + ADEDLEN_SHORTNAME)
+#define ADEDOFF_RFORK_V2     (ADEDOFF_PRODOSFILEI + ADEDLEN_PRODOSFILEI)
+
+
+
+/* we keep local copies of a bunch of stuff so that we can initialize things 
+ * correctly. */
+
+/* Bits in the finderinfo data. 
+ * see etc/afpd/{directory.c,file.c} for the finderinfo structure
+ * layout. */
+#define FINDERINFO_CUSTOMICON 0x4
+#define FINDERINFO_CLOSEDVIEW 0x100
+
+/* offsets in finderinfo */
+#define FINDERINFO_FRTYPEOFF   0
+#define FINDERINFO_FRCREATOFF  4
+#define FINDERINFO_FRFLAGOFF   8
+#define FINDERINFO_FRVIEWOFF  14
+
+/* invisible bit for dot files */
+#define ATTRBIT_INVISIBLE     (1 << 0)
+#define FINDERINFO_INVISIBLE  (1 << 14)
+
+/* this is to prevent changing timezones from causing problems with
+   localtime volumes. the screw-up is 30 years. we use a delta of 5
+   years.  */
+#define TIMEWARP_DELTA 157680000
+
+
+struct entry {
+  u_int32_t id, offset, len;
+};
+
+#if AD_VERSION == AD_VERSION1 
+static const struct entry entry_order[] = {
+  {ADEID_NAME, ADEDOFF_NAME_V1, ADEDLEN_INIT},
+  {ADEID_COMMENT, ADEDOFF_COMMENT_V1, ADEDLEN_INIT},
+  {ADEID_FILEI, ADEDOFF_FILEI, ADEDLEN_FILEI},
+  {ADEID_FINDERI, ADEDOFF_FINDERI_V1, ADEDLEN_FINDERI},
+  {ADEID_RFORK, ADEDOFF_RFORK_V1, ADEDLEN_INIT},
+  {0, 0, 0}
+};
+#else if AD_VERSION == AD_VERSION2
+static const struct entry entry_order[] = {
+  {ADEID_NAME, ADEDOFF_NAME_V2, ADEDLEN_INIT},
+  {ADEID_COMMENT, ADEDOFF_COMMENT_V2, ADEDLEN_INIT},
+  {ADEID_FILEDATESI, ADEDOFF_FILEDATESI, ADEDLEN_FILEDATESI},
+  {ADEID_FINDERI, ADEDOFF_FINDERI_V2, ADEDLEN_FINDERI},
+  {ADEID_DID, ADEDOFF_DID, ADEDLEN_DID},
+  {ADEID_AFPFILEI, ADEDOFF_AFPFILEI, ADEDLEN_AFPFILEI},
+  {ADEID_SHORTNAME, ADEDOFF_SHORTNAME, ADEDLEN_INIT},
+  {ADEID_PRODOSFILEI, ADEDOFF_PRODOSFILEI, ADEDLEN_PRODOSFILEI},
+  {ADEID_RFORK, ADEDOFF_RFORK_V2, ADEDLEN_INIT},
+  {0, 0, 0}
+};
+#endif
+
+#if AD_VERSION == AD_VERSION2
+
+
+static __inline__ int ad_v1tov2(struct adouble *ad, const char *path)
+{
+  struct stat st;
+  struct timeval tv;
+  u_int16_t attr;
+  char *buf;
+  int fd, off;
+  
+  /* check to see if we should convert this header. */
+  if (!path || (ad->ad_version != AD_VERSION1))
+    return 0;
+
+  /* convert from v1 to v2. what does this mean?
+   *  1) change FILEI into FILEDATESI
+   *  2) create space for SHORTNAME, AFPFILEI, DID, and PRODOSI
+   *  3) move FILEI attributes into AFPFILEI
+   *  4) initialize ACCESS field of FILEDATESI.
+   *
+   *  so, we need 4*12 (entry ids) + 12 (shortname) + 4 (afpfilei) +
+   *  4 (did) + 8 (prodosi) = 76 more bytes.  */
+  
+#define SHIFTDATA (AD_DATASZ2 - AD_DATASZ1)
+
+  /* bail if we can't get a lock */
+  if (ad_tmplock(ad, ADEID_RFORK, ADLOCK_WR, 0, 0) < 0) 
+    goto bail_err;
+  
+  if ((fd = open(path, O_RDWR)) < 0) 
+    goto bail_lock;
+  
+  if (gettimeofday(&tv, NULL) < 0) 
+    goto bail_lock;
+  
+  if (fstat(fd, &st) ||
+      ftruncate(fd, st.st_size + SHIFTDATA) < 0) {
+    goto bail_open;
+  }
+  
+  /* last place for failure. */
+  if ((void *) (buf = (char *) 
+               mmap(NULL, st.st_size + SHIFTDATA,
+                    PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == 
+         MAP_FAILED) {
+    goto bail_truncate;
+  }
+  
+  off = ad->ad_eid[ADEID_RFORK].ade_off;
+
+#ifdef USE_MMAPPED_HEADERS
+  /* okay, unmap our old ad header and point it to our local copy */
+  munmap(ad->ad_data, off);
+  ad->ad_data = buf;
+#endif
+
+  /* move the RFORK. this assumes that the RFORK is at the end */
+  memmove(buf + off + SHIFTDATA, buf + off, 
+         ad->ad_eid[ADEID_RFORK].ade_len);
+  
+  /* now, fix up our copy of the header */
+  memset(ad->ad_filler, 0, sizeof(ad->ad_filler));
+  
+  /* replace FILEI with FILEDATESI */
+  ad_getattr(ad, &attr);
+  ad->ad_eid[ADEID_FILEDATESI].ade_off = ADEDOFF_FILEDATESI;
+  ad->ad_eid[ADEID_FILEDATESI].ade_len = ADEDLEN_FILEDATESI;
+  ad->ad_eid[ADEID_FILEI].ade_off = 0;
+  ad->ad_eid[ADEID_FILEI].ade_len = 0;
+  
+  /* add in the new entries */
+  ad->ad_eid[ADEID_DID].ade_off = ADEDOFF_DID;
+  ad->ad_eid[ADEID_DID].ade_len = ADEDLEN_DID;
+  ad->ad_eid[ADEID_AFPFILEI].ade_off = ADEDOFF_AFPFILEI;
+  ad->ad_eid[ADEID_AFPFILEI].ade_len = ADEDLEN_AFPFILEI;
+  ad->ad_eid[ADEID_SHORTNAME].ade_off = ADEDOFF_SHORTNAME;
+  ad->ad_eid[ADEID_SHORTNAME].ade_len = ADEDLEN_INIT;
+  ad->ad_eid[ADEID_PRODOSFILEI].ade_off = ADEDOFF_PRODOSFILEI;
+  ad->ad_eid[ADEID_PRODOSFILEI].ade_len = ADEDLEN_PRODOSFILEI;
+  
+  /* shift the old entries (NAME, COMMENT, FINDERI, RFORK) */
+  ad->ad_eid[ADEID_NAME].ade_off = ADEDOFF_NAME_V2;
+  ad->ad_eid[ADEID_COMMENT].ade_off = ADEDOFF_COMMENT_V2;
+  ad->ad_eid[ADEID_FINDERI].ade_off = ADEDOFF_FINDERI_V2;
+  ad->ad_eid[ADEID_RFORK].ade_off = ADEDOFF_RFORK_V2;
+  
+  /* switch to v2 */
+  ad->ad_version = AD_VERSION2;
+  
+  /* move our data buffer to make space for the new entries. */
+  memmove(buf + ADEDOFF_NAME_V2, buf + ADEDOFF_NAME_V1,
+         ADEDOFF_RFORK_V1 - ADEDOFF_NAME_V1);
+  
+  /* now, fill in the space with appropriate stuff. we're
+     operating as a v2 file now. */
+  ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, tv.tv_sec);
+  memset(ad_entry(ad, ADEID_DID), 0, ADEDLEN_DID);
+  memset(ad_entry(ad, ADEID_AFPFILEI), 0, ADEDLEN_AFPFILEI);
+  ad_setattr(ad, attr);
+  memset(ad_entry(ad, ADEID_SHORTNAME), 0, ADEDLEN_SHORTNAME);
+  memset(ad_entry(ad, ADEID_PRODOSFILEI), 0, ADEDLEN_PRODOSFILEI);
+  
+  /* rebuild the header and cleanup */
+  ad_rebuild_header(ad);
+  munmap(buf, st.st_size + SHIFTDATA);
+  close(fd);
+  ad_tmplock(ad, ADEID_RFORK, ADLOCK_CLR, 0, 0);
+
+#ifdef USE_MMAPPED_HEADERS
+  /* now remap our header */
+  ad->ad_data = mmap(NULL, ADEDOFF_RFORK_V2, PROT_READ | PROT_WRITE,
+                    (ad_getoflags(ad, ADFLAGS_HF) & O_RDWR) ? MAP_SHARED :
+                    MAP_PRIVATE, ad->ad_hf.adf_fd, 0);
+  if (ad->ad_data == MAP_FAILED)
+    goto bail_err;
+#endif
+
+  return 0;
+  
+bail_truncate:
+  ftruncate(fd, st.st_size);
+bail_open:
+  close(fd);
+bail_lock:
+  ad_tmplock(ad, ADEID_RFORK, ADLOCK_CLR, 0, 0);
+bail_err:
+  return -1;
+}
+#endif
+
+
+/* read in the entries */
+static __inline__ void parse_entries(struct adouble *ad, char *buf,
+                                   u_int16_t nentries)
+{
+    u_int32_t          eid, len, off;
+
+    /* now, read in the entry bits */
+    for (; nentries > 0; nentries-- ) {
+       memcpy(&eid, buf, sizeof( eid ));
+       eid = ntohl( eid );
+       buf += sizeof( eid );
+       memcpy(&off, buf, sizeof( off ));
+       off = ntohl( off );
+       buf += sizeof( off );
+       memcpy(&len, buf, sizeof( len ));
+       len = ntohl( len );
+       buf += sizeof( len );
+
+       if ( 0 < eid && eid < ADEID_MAX ) {
+           ad->ad_eid[ eid ].ade_off = off;
+           ad->ad_eid[ eid ].ade_len = len;
+       } else {
+           syslog( LOG_DEBUG, "ad_refresh: nentries %hd  eid %d\n",
+                   nentries, eid );
+       }
+    }
+}
+
+
+/* this reads enough of the header so that we can figure out all of
+ * the entry lengths and offsets. once that's done, we just read/mmap
+ * the rest of the header in.
+ *
+ * NOTE: we're assuming that the resource fork is kept at the end of
+ *       the file. also, mmapping won't work for the hfs fs until it
+ *       understands how to mmap header files. */
+static __inline__ int ad_header_read(struct adouble *ad)
+{
+#ifdef USE_MMAPPED_HEADERS
+    char                buf[AD_ENTRY_LEN*ADEID_MAX];
+#else 
+    char                *buf = ad->ad_data;
+#endif
+    u_int16_t           nentries;
+    int                 len;
+    static int          warning = 0;
+
+    /* read the header */
+    if ( ad->ad_hf.adf_off != 0 ) {
+      if ( lseek( ad->ad_hf.adf_fd, 0L, SEEK_SET ) < 0L ) {
+               return( -1 );
+      }
+      ad->ad_hf.adf_off = 0;
+    }
+
+    if (read( ad->ad_hf.adf_fd, buf, AD_HEADER_LEN) != AD_HEADER_LEN) {
+       if ( errno == 0 ) {
+           errno = EIO;
+       }
+       return( -1 );
+    }
+    ad->ad_hf.adf_off = AD_HEADER_LEN;
+
+    memcpy(&ad->ad_magic, buf, sizeof( ad->ad_magic ));
+    memcpy(&ad->ad_version, buf + ADEDOFF_VERSION, 
+          sizeof( ad->ad_version ));
+
+    /* tag broken v1 headers. just assume they're all right. 
+     * we detect two cases: null magic/version
+     *                      byte swapped magic/version
+     * XXX: in the future, you'll need the v1compat flag. 
+     * (ad->ad_flags & ADFLAGS_V1COMPAT) */
+    if (!ad->ad_magic && !ad->ad_version) {
+      if (!warning) {
+       syslog(LOG_DEBUG, "notice: fixing up null v1 magic/version.");
+       warning++;
+      }
+      ad->ad_magic = AD_MAGIC;
+      ad->ad_version = AD_VERSION1;
+
+    } else if ((ad->ad_magic == AD_MAGIC) && 
+              (ad->ad_version == AD_VERSION1)) {
+      if (!warning) {
+       syslog(LOG_DEBUG, "notice: fixing up byte-swapped v1 magic/version.");
+       warning++;
+      }
+
+    } else {
+      ad->ad_magic = ntohl( ad->ad_magic );
+      ad->ad_version = ntohl( ad->ad_version );
+    }
+
+    if ((ad->ad_magic != AD_MAGIC) || ((ad->ad_version != AD_VERSION1)
+#if AD_VERSION == AD_VERSION2
+                                      && (ad->ad_version != AD_VERSION2)
+#endif
+                                      )) {
+      errno = EIO;
+      syslog(LOG_DEBUG, "ad_open: can't parse AppleDouble header.");
+      return -1;
+    }
+
+    memcpy(ad->ad_filler, buf + ADEDOFF_FILLER, sizeof( ad->ad_filler ));
+    memcpy(&nentries, buf + ADEDOFF_NENTRIES, sizeof( nentries ));
+    nentries = ntohs( nentries );
+
+    /* read in all the entry headers. if we have more than the 
+     * maximum, just hope that the rfork is specified early on. */
+    len = nentries*AD_ENTRY_LEN;
+#ifdef USE_MMAPPED_HEADERS
+    if (len > sizeof(buf))
+      len = sizeof(buf);
+#else
+    if (len + AD_HEADER_LEN > sizeof(ad->ad_data))
+      len = sizeof(ad->ad_data) - AD_HEADER_LEN;
+    buf += AD_HEADER_LEN;
+#endif
+    if (read(ad->ad_hf.adf_fd, buf, len) != len) {
+        if (errno == 0)
+           errno = EIO;
+       syslog(LOG_DEBUG, "ad_header_read: can't read entry info.");
+       return -1;
+    }
+    ad->ad_hf.adf_off += len;
+
+    /* figure out all of the entry offsets and lengths. if we aren't
+     * able to read a resource fork entry, bail. */
+    parse_entries(ad, buf, nentries);
+    if (!ad_getentryoff(ad, ADEID_RFORK)
+#ifndef USE_MMAPPED_HEADERS
+       || (ad_getentryoff(ad, ADEID_RFORK) > sizeof(ad->ad_data))
+#endif
+       ) {
+      syslog(LOG_DEBUG, "ad_header_read: problem with rfork entry offset."); 
+      return -1;
+    }
+
+    /* read/mmap up to the beginning of the resource fork. */
+#ifdef USE_MMAPPED_HEADERS
+    ad->ad_data = mmap(NULL, ad_getentryoff(ad, ADEID_RFORK),
+                      PROT_READ | PROT_WRITE,
+                      (ad_getoflags(ad, ADFLAGS_HF) & O_RDWR) ? MAP_SHARED :
+                      MAP_PRIVATE, ad->ad_hf.adf_fd, 0);
+    if (ad->ad_data == MAP_FAILED) 
+      return -1;
+#else
+    buf += len;
+    len = ad_getentryoff(ad, ADEID_RFORK) - ad->ad_hf.adf_off;
+    if (read(ad->ad_hf.adf_fd, buf, len) != len) {
+        if (errno == 0)
+           errno = EIO;
+       syslog(LOG_DEBUG, "ad_header_read: can't read in entries.");
+       return -1;
+    }
+#endif
+    
+    /* fix up broken dates */
+    if (ad->ad_version == AD_VERSION1) {
+      struct stat st;
+      int32_t aint;
+      
+      if (fstat(ad->ad_hf.adf_fd, &st) < 0) {
+       return 1; /* fail silently */
+      }
+      
+      /* check to see if the ad date is wrong. just see if we have
+      * a modification date in the future. */
+      if (((ad_getdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, &aint)) == 0) &&
+         (aint > TIMEWARP_DELTA + st.st_mtime)) {
+       ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, aint - AD_DATE_DELTA);
+       ad_getdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, &aint);
+       ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, aint - AD_DATE_DELTA);
+       ad_getdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, &aint);
+       ad_setdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, aint - AD_DATE_DELTA);
+      }
+    }
+
+    return 0;
+}
+
+
+/*
+ * Put the .AppleDouble where it needs to be:
+ *
+ *         /   a/.AppleDouble/b
+ *     a/b
+ *         \   b/.AppleDouble/.Parent
+ */
+char *
+ad_path( path, adflags )
+    char       *path;
+    int                adflags;
+{
+    static char        pathbuf[ MAXPATHLEN + 1];
+    char       c, *slash, buf[MAXPATHLEN + 1];
+
+    strncpy(buf, path, MAXPATHLEN);
+    if ( adflags & ADFLAGS_DIR ) {
+       strncpy( pathbuf, buf, MAXPATHLEN );
+       if ( *buf != '\0' ) {
+           strcat( pathbuf, "/" );
+       }
+       slash = ".Parent";
+    } else {
+       if (( slash = strrchr( buf, '/' )) != NULL ) {
+           c = *++slash;
+           *slash = '\0';
+           strncpy( pathbuf, buf, MAXPATHLEN);
+           *slash = c;
+       } else {
+           pathbuf[ 0 ] = '\0';
+           slash = buf;
+       }
+    }
+    strncat( pathbuf, ".AppleDouble/", MAXPATHLEN - strlen(pathbuf));
+    strncat( pathbuf, slash, MAXPATHLEN - strlen(pathbuf));
+
+    return( pathbuf );
+}
+
+/*
+ * Support inherited protection modes for AppleDouble files.  The supplied
+ * mode is ANDed with the parent directory's mask value in lieu of "umask",
+ * and that value is returned.
+ */
+
+#define DEFMASK 7700   /* be conservative */
+
+int
+ad_mode( path, mode )
+    char               *path;
+    int                        mode;
+{
+    static char                modebuf[ MAXPATHLEN + 1];
+    struct stat                stbuf;
+    char               *slash;
+
+    if ( mode == 0 ) {
+       return( mode );         /* save on syscalls */
+    }
+
+    if ( strlen( path ) >= MAXPATHLEN ) {
+       return( mode & DEFMASK );  /* can't do it */
+    }
+
+    /*
+     * For a path with directories in it, remove the final component
+     * (path or subdirectory name) to get the name we want to stat.
+     * For a path which is just a filename, use "." instead.
+     */
+    strcpy( modebuf, path );
+    if (( slash = strrchr( modebuf, '/' )) != NULL ) {
+       *slash = '\0';          /* remove pathname component */
+    } else {
+       modebuf[0] = '.';       /* use current directory */
+       modebuf[1] = '\0';
+    }
+
+    if ( stat( modebuf, &stbuf ) != 0 ) {
+       return( mode & DEFMASK );       /* bail out... can't stat dir? */
+    }
+
+    return( mode & stbuf.st_mode );
+}
+
+/*
+ * Use mkdir() with mode bits taken from ad_mode().
+ */
+int
+ad_mkdir( path, mode )
+    char               *path;
+    int                        mode;
+{
+    return mkdir( path, ad_mode( path, mode ) );
+}
+
+
+/*
+ * It's not possible to open the header file O_RDONLY -- the read
+ * will fail and return an error. this refcounts things now. 
+ */
+int ad_open( path, adflags, oflags, mode, ad )
+    char               *path;
+    int                        adflags, oflags, mode;
+    struct adouble     *ad;
+{
+    const struct entry  *eid;
+    struct stat         st;
+    char               *slash, *ad_p;
+    int                        hoflags, admode;
+    u_int16_t           ashort;
+
+    if (ad->ad_inited != AD_INITED) {
+        ad_dfileno(ad) = -1;
+        ad_hfileno(ad) = -1;
+       adf_lock_init(&ad->ad_df);
+       adf_lock_init(&ad->ad_hf);
+#ifdef USE_MMAPPED_HEADERS
+       ad->ad_data = MAP_FAILED;
+#endif
+        ad->ad_inited = AD_INITED;
+    }
+
+    if (adflags & ADFLAGS_DF) { 
+        if (ad_dfileno(ad) == -1) {
+         if (( ad->ad_df.adf_fd =
+               open( path, oflags, ad_mode( path, mode ) )) < 0 ) {
+           return( -1 );
+         }
+         ad->ad_df.adf_off = 0;
+         ad->ad_df.adf_flags = oflags;
+       } 
+       ad->ad_df.adf_refcount++;
+    }
+
+    if (adflags & ADFLAGS_HF) {
+        if (ad_hfileno(ad) == -1) {
+         ad_p = ad_path( path, adflags );
+         admode = ad_mode( ad_p, mode );
+         
+         hoflags = oflags & ~O_CREAT;
+         if (( ad->ad_hf.adf_fd = open( ad_p, hoflags, admode )) < 0 ) {
+           if ( errno == ENOENT && hoflags != oflags ) {
+             /*
+              * We're expecting to create a new adouble header file,
+              * here.
+              */
+             errno = 0;
+             if (( ad->ad_hf.adf_fd = open( ad_p, oflags,
+                                            admode )) < 0 ) {
+               /*
+                * Probably .AppleDouble doesn't exist, try to
+                * mkdir it.
+                */
+               if ((errno == ENOENT) && 
+                   ((adflags & ADFLAGS_NOADOUBLE) == 0)) {
+                 if (( slash = strrchr( ad_p, '/' )) == NULL ) {
+                   ad_close( ad, adflags );
+                   return( -1 );
+                 }
+                 *slash = '\0';
+                 errno = 0;
+                 if ( ad_mkdir( ad_p, 0777 ) < 0 ) {
+                   ad_close( ad, adflags );
+                   return( -1 );
+                 }
+                 *slash = '/';
+                 if (( ad->ad_hf.adf_fd = 
+                       open( ad_p, oflags, ad_mode( ad_p, mode) )) < 0 ) {
+                   ad_close( ad, adflags );
+                   return( -1 );
+                 }
+               } else {
+                 ad_close( ad, adflags );
+                 return( -1 );
+               }
+             }
+             ad->ad_hf.adf_flags = oflags;
+           } else {
+             ad_close( ad, adflags );
+             return( -1 );
+           }
+         } else if ((fstat(ad->ad_hf.adf_fd, &st) == 0) && 
+                    (st.st_size == 0)) {
+           /* for 0 length files, treat them as new. */
+           ad->ad_hf.adf_flags = oflags;
+         } else {
+           ad->ad_hf.adf_flags = hoflags;
+         }
+         ad->ad_hf.adf_off = 0;
+         
+         /*
+          * This is a new adouble header file. Initialize the structure,
+          * instead of reading it.
+          */
+         memset(ad->ad_eid, 0, sizeof( ad->ad_eid ));
+         if ( ad->ad_hf.adf_flags & ( O_TRUNC | O_CREAT )) {
+           struct timeval tv;
+
+           ad->ad_magic = AD_MAGIC;
+           ad->ad_version = AD_VERSION;
+           memset(ad->ad_filler, 0, sizeof( ad->ad_filler ));
+
+#ifdef USE_MMAPPED_HEADERS
+           /* truncate the header file and mmap it. */
+           ftruncate(ad->ad_hf.adf_fd, AD_DATASZ);
+           ad->ad_data = mmap(NULL, AD_DATASZ, PROT_READ | PROT_WRITE,
+                              MAP_SHARED, ad->ad_hf.adf_fd, 0);
+           if (ad->ad_data == MAP_FAILED) {
+             ad_close(ad, adflags);
+             return -1;
+           }       
+#else
+           memset(ad->ad_data, 0, sizeof(ad->ad_data));
+#endif
+
+           eid = entry_order;
+           while (eid->id) {
+             ad->ad_eid[eid->id].ade_off = eid->offset;
+             ad->ad_eid[eid->id].ade_len = eid->len;
+             eid++;
+           }
+           
+           /* put something sane in the directory finderinfo */
+           if (adflags & ADFLAGS_DIR) {
+             /* set default view */
+             ashort = htons(FINDERINFO_CLOSEDVIEW);
+             memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRVIEWOFF, 
+                    &ashort, sizeof(ashort));
+           } else {
+             /* set default creator/type fields */
+             memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRTYPEOFF,
+                    "TEXT", 4);
+             memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRCREATOFF,
+                    "UNIX", 4);
+           }
+
+           /* make things invisible */
+           if ((*path == '.') && strcmp(path, ".") && strcmp(path, "..")) {
+             ashort = htons(ATTRBIT_INVISIBLE);
+             ad_setattr(ad, ashort);
+             ashort = htons(FINDERINFO_INVISIBLE);
+             memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF,
+                    &ashort, sizeof(ashort));
+           }
+
+           if (gettimeofday(&tv, NULL) < 0) {
+             ad_close(ad, adflags);
+             return -1;
+           }
+           
+           /* put something sane in the date fields */
+           ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, tv.tv_sec);
+           ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, tv.tv_sec);
+           ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, tv.tv_sec);
+           ad_setdate(ad, AD_DATE_BACKUP, AD_DATE_START);
+
+         } else {
+           /*
+            * Read the adouble header in and parse it.
+            */
+           if ((ad_header_read( ad ) < 0)
+#if AD_VERSION == AD_VERSION2
+               || (ad_v1tov2(ad, ad_p) < 0)
+#endif
+               ) {
+             ad_close( ad, adflags );
+             return( -1 );
+           }
+         }
+       }
+       ad->ad_hf.adf_refcount++;
+    }
+       
+    return( 0 );
+}
+
+/* to do this with mmap, we need the hfs fs to understand how to mmap
+   header files. */
+int ad_refresh(struct adouble *ad)
+{
+#ifdef USE_MMAPPED_HEADERS
+  off_t off;
+#endif
+
+  if (ad->ad_hf.adf_fd < -1)
+    return -1;
+
+#ifdef USE_MMAPPED_HEADERS
+  if (ad->ad_data == MAP_FAILED)
+    return -1;
+  
+  /* re-read the header */
+  off = ad_getentryoff(ad, ADEID_RFORK);
+  memcpy(&nentries, ad->ad_data + ADEDOFF_NENTRIES, sizeof(nentries));
+  nentries = ntohs(nentries);
+  parse_entries(ad, ad->ad_data + AD_HEADER_LEN, nentries);
+
+  /* check to see if something screwy happened */
+  if (!ad_getentryoff(ad, ADEID_RFORK))
+    return -1;
+
+  /* if there's a length discrepancy, remap the header. this shouldn't
+   * really ever happen. */
+  if (off != ad_getentryoff(ad, ADEID_RFORK)) {
+    char *buf = ad->ad_data;
+    buf = ad->ad_data;
+    ad->ad_data = mmap(NULL, ad_getentryoff(ad, ADEID_RFORK), 
+                      PROT_READ | PROT_WRITE,
+                      (ad_getoflags(ad, ADFLAGS_HF) & O_RDWR) ? 
+                      MAP_SHARED : MAP_PRIVATE, ad->ad_hf.adf_fd, 0);
+    if (ad->ad_data == MAP_FAILED) {
+      ad->ad_data = buf;
+      return -1;
+    }
+    munmap(buf, off);
+  }
+  return 0;
+
+#else
+  return ad_header_read(ad);
+#endif
+}
diff --git a/libatalk/adouble/ad_private.h b/libatalk/adouble/ad_private.h
new file mode 100644 (file)
index 0000000..3814e64
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef LIBATALK_ADOUBLE_AD_PRIVATE_H
+#define LIBATALK_ADOUBLE_AD_PRIVATE_H 1
+
+#include <atalk/adouble.h>
+
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void *) -1)
+#endif
+
+#ifndef __inline__
+#define __inline__
+#endif
+
+/* this is so that we can keep lists of fds referencing the same file
+ * around. that way, we can honor locks created by the same process
+ * with the same file. */
+#ifdef USE_FLOCK_LOCKS
+#define adf_lock_init(a)
+#define adf_lock_free(a)
+#else
+
+#define adf_lock_init(a) do { \
+       (a)->adf_lockmax = (a)->adf_lockcount = 0; \
+       (a)->adf_lock = NULL; \
+} while (0)
+
+#define adf_lock_free(a) do { \
+       if (!(a)->adf_lock) \
+               break; \
+       free((a)->adf_lock); \
+       adf_lock_init(a); \
+} while (0)
+#endif
+
+#endif /* libatalk/adouble/ad_private.h */
diff --git a/libatalk/adouble/ad_read.c b/libatalk/adouble/ad_read.c
new file mode 100644 (file)
index 0000000..b286f55
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <atalk/adouble.h>
+#include <unistd.h>
+
+#ifndef MIN
+#define MIN(a,b)       ((a)<(b)?(a):(b))
+#endif
+
+/* XXX: this would probably benefit from pread. 
+ *      locks have to be checked before each stream of consecutive 
+ *      ad_reads to prevent a denial in the middle from causing
+ *      problems. */
+ssize_t ad_read( ad, eid, off, buf, buflen)
+    struct adouble     *ad;
+    const u_int32_t    eid;
+    off_t               off;
+    char               *buf;
+    const size_t       buflen;
+{
+    ssize_t            cc;
+
+    /* We're either reading the data fork (and thus the data file)
+     * or we're reading anything else (and thus the header file). */
+    if ( eid == ADEID_DFORK ) {
+       if ( ad->ad_df.adf_off != off ) {
+           if ( lseek( ad->ad_df.adf_fd, (off_t) off, SEEK_SET ) < 0 ) {
+               return( -1 );
+           }
+           ad->ad_df.adf_off = off;
+       }
+       if (( cc = read( ad->ad_df.adf_fd, buf, buflen )) < 0 ) {
+           return( -1 );
+       }
+       ad->ad_df.adf_off += cc;
+    } else {
+        cc = ad->ad_eid[eid].ade_off + off;
+
+#ifdef USE_MMAPPED_HEADERS
+       if (eid != ADEID_RFORK) {
+         memcpy(buf, ad->ad_data + cc, buflen);
+         cc = buflen;
+         goto ad_read_done;
+       }
+#endif 
+       if ( ad->ad_hf.adf_off != cc ) {
+         if ( lseek( ad->ad_hf.adf_fd, (off_t) cc, SEEK_SET ) < 0 ) {
+             return( -1 );
+         }
+         ad->ad_hf.adf_off = cc;
+       }
+         
+       if (( cc = read( ad->ad_hf.adf_fd, buf, buflen )) < 0 ) {
+         return( -1 );
+       }
+         
+#ifndef USE_MMAPPED_HEADERS
+       /*
+        * We've just read in bytes from the disk that we read earlier
+        * into ad_data. If we're going to write this buffer out later,
+        * we need to update ad_data.
+        */
+       if (ad->ad_hf.adf_off < ad_getentryoff(ad, ADEID_RFORK)) {
+           if ( ad->ad_hf.adf_flags & O_RDWR ) {
+             memcpy(buf, ad->ad_data + ad->ad_hf.adf_off,
+                    MIN(sizeof( ad->ad_data ) - ad->ad_hf.adf_off, cc));
+           } else {
+             memcpy(ad->ad_data + ad->ad_hf.adf_off, buf,
+                    MIN(sizeof( ad->ad_data ) - ad->ad_hf.adf_off, cc));
+           }
+       }
+       ad->ad_hf.adf_off += cc;
+#else
+ad_read_done:
+#endif
+    }
+
+    return( cc );
+}
diff --git a/libatalk/adouble/ad_sendfile.c b/libatalk/adouble/ad_sendfile.c
new file mode 100644 (file)
index 0000000..9a5ac2a
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 1999 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ *
+ * NOTE: the following uses the fact that sendfile() only exists on
+ * machines with SA_RESTART behaviour. this is all very machine specific. 
+ *
+ * 
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <atalk/adouble.h>
+
+#include <syslog.h>
+
+#include "ad_private.h"
+
+static int _ad_sendfile_dummy;
+
+#if defined(HAVE_SENDFILE_READ) || defined(HAVE_SENDFILE_WRITE)
+static __inline__ int ad_sendfile_init(const struct adouble *ad, 
+                                      const int eid, off_t *off,
+                                      const int end)
+{
+  int fd;
+
+  if (end) 
+    *off = ad_size(ad, eid) - *off;
+
+  if (eid == ADEID_DFORK) {
+    fd = ad_dfileno(ad);
+  } else {
+    *off += ad_getentryoff(ad, eid);
+    fd = ad_hfileno(ad);
+  }
+
+  return fd;
+}
+#endif
+
+
+/* read from adouble file and write to socket. sendfile doesn't change
+ * the file pointer position. */
+#ifdef HAVE_SENDFILE_READ
+ssize_t ad_readfile(const struct adouble *ad, const int eid, 
+                   const int sock, off_t off, const size_t len)
+{
+  off_t cc;
+  int fd;
+
+  fd = ad_sendfile_init(ad, eid, &off, 0);
+#ifdef __linux__
+  cc = sendfile(sock, fd, &off, len);
+#endif
+
+#ifdef BSD4_4
+  if (sendfile(fd, sock, off, len, NULL, &cc, 0) < 0)
+    return -1;
+#endif
+
+  return cc;
+}
+#endif
+
+#if 0
+#ifdef HAVE_SENDFILE_WRITE
+/* read from a socket and write to an adouble file */
+ssize_t ad_writefile(struct adouble *ad, const int eid, 
+                    const int sock, off_t off, const int end,
+                    const size_t len)
+{
+#ifdef __linux__
+  ssize_t cc;
+  int fd;
+
+  fd = ad_sendfile_init(ad, eid, &off, end);
+  if ((cc = sendfile(fd, sock, &off, len)) < 0)
+    return -1;
+
+  if ((eid != ADEID_DFORK) && (off > ad_getentrylen(ad, eid))) 
+    ad_setentrylen(ad, eid, off);
+
+  return cc;
+#endif
+}
+#endif
+#endif
diff --git a/libatalk/adouble/ad_size.c b/libatalk/adouble/ad_size.c
new file mode 100644 (file)
index 0000000..5773cae
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ *
+ * if we could depend upon inline functions, this would be one.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <syslog.h>
+
+#include <atalk/adouble.h>
+
+off_t ad_size(const struct adouble *ad, const u_int32_t eid)
+{
+  if (eid == ADEID_DFORK) {
+    struct stat st;
+    
+    if (fstat(ad_dfileno(ad), &st) < 0)
+      return 0;
+    return st.st_size;
+  }  
+
+  return ad_getentrylen(ad, eid);
+} 
diff --git a/libatalk/adouble/ad_write.c b/libatalk/adouble/ad_write.c
new file mode 100644 (file)
index 0000000..b0e69ef
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 1990,1995 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <atalk/adouble.h>
+
+#ifndef MIN
+#define MIN(a,b)       ((a)<(b)?(a):(b))
+#endif
+
+/* XXX: this would benefit from pwrite. 
+ *      locking has to be checked before each stream of consecutive
+ *      ad_writes to prevent a lock in the middle from causing problems. 
+ */
+ssize_t ad_write( ad, eid, off, end, buf, buflen )
+    struct adouble     *ad;
+    const u_int32_t    eid;
+    off_t               off;
+    const int          end;
+    const char         *buf;
+    const size_t       buflen;
+{
+    struct stat                st;
+    ssize_t            cc;
+
+    if ( eid == ADEID_DFORK ) {
+       if ( end ) {
+           if ( fstat( ad->ad_df.adf_fd, &st ) < 0 ) {
+               return( -1 );
+           }
+           off = st.st_size - off;
+       }
+
+       if ( ad->ad_df.adf_off != off ) {
+           if ( lseek( ad->ad_df.adf_fd, (off_t) off, SEEK_SET ) < 0 ) {
+               return( -1 );
+           }
+           ad->ad_df.adf_off = off;
+       }
+       cc = write( ad->ad_df.adf_fd, buf, buflen );
+       if ( cc < 0 ) {
+           return( -1 );
+       }
+       ad->ad_df.adf_off += cc;
+    } else {
+       if ( end ) {
+           off = ad->ad_eid[ eid ].ade_len - off;
+       }
+       cc = ad->ad_eid[eid].ade_off + off;
+
+#ifdef USE_MMAPPED_HEADERS
+       if (eid != ADEID_RFORK) {
+         memcpy(ad->ad_data + cc, buf, buflen);
+         cc = buflen;
+         goto ad_write_done;
+       }         
+#endif
+
+       if ( ad->ad_hf.adf_off != cc ) {
+         if ( lseek( ad->ad_hf.adf_fd, (off_t) cc, SEEK_SET ) < 0 ) {
+             return( -1 );
+         }
+         ad->ad_hf.adf_off = cc;
+       }
+         
+       if ((cc = write( ad->ad_hf.adf_fd, buf, buflen )) < 0)
+         return( -1 );
+       ad->ad_hf.adf_off += cc;
+       
+#ifndef USE_MMAPPED_HEADERS
+       /* sync up our internal buffer */
+       if (ad->ad_hf.adf_off < ad_getentryoff(ad, ADEID_RFORK))
+         memcpy(ad->ad_data + ad->ad_hf.adf_off, buf,
+                MIN(sizeof(ad->ad_data) - ad->ad_hf.adf_off, cc));
+#else    
+ad_write_done:
+#endif
+         if ( ad->ad_eid[ eid ].ade_len < off + cc ) {
+           ad->ad_eid[ eid ].ade_len = off + cc;
+         }
+    }
+
+    return( cc );
+}
+
+/* set locks here */
+int ad_rtruncate( ad, size )
+    struct adouble     *ad;
+    const size_t       size;
+{
+    int err;
+
+    if (ad_tmplock(ad, ADEID_RFORK, ADLOCK_WR, 0, 0) < 0)
+      return -2;
+
+    if ( ftruncate( ad->ad_hf.adf_fd,
+           size + ad->ad_eid[ ADEID_RFORK ].ade_off ) < 0 ) {
+        err = errno;
+        ad_tmplock(ad, ADEID_RFORK, ADLOCK_CLR, 0, 0);
+       errno = err;
+       return( -1 );
+    }
+
+    ad->ad_eid[ ADEID_RFORK ].ade_len = size;
+    if ( lseek( ad->ad_hf.adf_fd, ad->ad_eid[ADEID_RFORK].ade_off, 
+               SEEK_SET ) < 0 ) {
+        err = errno;
+        ad_tmplock(ad, ADEID_RFORK, ADLOCK_CLR, 0, 0);
+       errno = err;
+       return( -1 );
+    }
+
+    ad->ad_hf.adf_off = ad->ad_eid[ADEID_RFORK].ade_off;
+    ad_tmplock(ad, ADEID_RFORK, ADLOCK_CLR, 0, 0);
+    return( 0 );
+}
+
+int ad_dtruncate(ad, size)
+    struct adouble     *ad;
+    const size_t       size;
+{
+    int err;
+
+    if (ad_tmplock(ad, ADEID_DFORK, ADLOCK_WR, 0, 0) < 0)
+      return -2;
+
+    if (ftruncate(ad->ad_df.adf_fd, size) < 0) {
+      err = errno;
+      ad_tmplock(ad, ADEID_DFORK, ADLOCK_CLR, 0, 0);
+      errno = err;
+    } else 
+      ad_tmplock(ad, ADEID_DFORK, ADLOCK_CLR, 0, 0);
+
+    return 0;
+}
diff --git a/libatalk/asp/Makefile b/libatalk/asp/Makefile
new file mode 100644 (file)
index 0000000..5c5af68
--- /dev/null
@@ -0,0 +1,60 @@
+SRC = asp_attn.c asp_close.c asp_cmdreply.c asp_getreq.c \
+       asp_getsess.c asp_init.c asp_write.c asp_shutdown.c \
+       asp_tickle.c
+OBJ = asp_attn.o asp_close.o asp_cmdreply.o asp_getreq.o \
+       asp_getsess.o asp_init.o asp_write.o asp_shutdown.o \
+       asp_tickle.o
+
+INCPATH=       -I../../include
+CFLAGS=        ${DEFS} ${OPTOPTS} ${INCPATH}
+TAGSFILE=      tags
+CC=    cc
+
+all : profiled asplib
+
+profiled:
+       -mkdir profiled
+
+asplib asplib_p : ${OBJ}
+       @echo "building profiled asplib"
+       @cd profiled; ar cru ../asplib_p ${OBJ}
+       @echo "building normal asplib"
+       @ar cru asplib ${OBJ}
+
+.c.o :
+       ${CC} -p ${CFLAGS} -c $*.c
+       -ld -r -o $*.o- $*.o
+       mv $*.o- profiled/$*.o
+       ${CC} ${CFLAGS} -c $*.c
+       -ld -r -o $*.o- $*.o
+       mv $*.o- $*.o
+
+clean :
+       rm -f *.o profiled/*.o *.bak *[Ee]rrs tags
+       rm -f asplib asplib_p
+
+tags : ${SRC}
+       cwd=`pwd`; \
+       for i in ${SRC}; do \
+           ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+       done
+
+depend :
+       for i in ${SRC} ; do \
+           ${CC} -M ${DEFS} ${INCPATH} $$i | \
+           awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+               else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+               else rec = rec " " $$2 } } \
+               END { print rec } ' >> makedep; done
+       sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+       cat makedep >> Makefile.tmp
+       rm makedep
+       echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+       echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+       echo '# see make depend above' >> Makefile.tmp
+       rm -f Makefile.bak
+       cp Makefile Makefile.bak
+       mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
diff --git a/libatalk/asp/asp_attn.c b/libatalk/asp/asp_attn.c
new file mode 100644 (file)
index 0000000..9aa95bd
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include <atalk/atp.h>
+#include <atalk/asp.h>
+#include <atalk/afp.h>
+
+/* attentions can get sent at any time. as a consequence, we don't
+ * want to touch anything that might be used elsewhere. */
+int asp_attention(ASP asp, AFPUserBytes flags)
+{
+    char cmds[ASP_HDRSIZ], data[ASP_HDRSIZ];
+    struct sockaddr_at  sat;
+    struct atp_block   atpb;
+    struct iovec       iov[ 1 ];
+
+    cmds[0] = ASPFUNC_ATTN;
+    cmds[1] = asp->asp_sid;
+    flags = htons(flags);
+    memcpy(cmds + 2, &flags, sizeof(flags));
+
+    sat = asp->asp_sat;
+    sat.sat_port = asp->asp_wss;
+    atpb.atp_saddr = &sat;
+    atpb.atp_sreqdata = cmds;
+    atpb.atp_sreqdlen = sizeof(cmds);
+    atpb.atp_sreqto = 2;
+    atpb.atp_sreqtries = 5;
+
+    if ( atp_sreq( asp->asp_atp, &atpb, 1, 0 ) < 0 ) {
+       syslog( LOG_ERR, "atp_sreq: %m" );
+       return -1;
+    }
+
+    iov[ 0 ].iov_base = data;
+    iov[ 0 ].iov_len = sizeof( data );
+    atpb.atp_rresiov = iov;
+    atpb.atp_rresiovcnt = sizeof( iov )/sizeof( iov[ 0 ] );
+    if ( atp_rresp( asp->asp_atp, &atpb ) < 0 ) {
+       syslog( LOG_ERR, "atp_rresp: %m" );
+       return -1;
+    }
+
+    return 0;
+}
diff --git a/libatalk/asp/asp_child.h b/libatalk/asp/asp_child.h
new file mode 100644 (file)
index 0000000..d3d3b83
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+struct asp_child {
+    int                        ac_pid;
+    int                        ac_state;
+    struct sockaddr_at ac_sat;
+};
+
+#define ACSTATE_DEAD   0
+#define ACSTATE_OK     1
+#define ACSTATE_BAD    7
diff --git a/libatalk/asp/asp_close.c b/libatalk/asp/asp_close.c
new file mode 100644 (file)
index 0000000..b830788
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <netatalk/at.h>
+#include <atalk/atp.h>
+#include <atalk/asp.h>
+
+int asp_close( asp )
+    ASP                asp;
+{
+    struct atp_block   atpb;
+    struct iovec       iov[ 1 ];
+    int err = 0;
+
+    memset(asp->commands, 0, sizeof(u_int32_t));
+
+    atpb.atp_saddr = &asp->asp_sat;
+    iov[ 0 ].iov_base = asp->commands;
+    iov[ 0 ].iov_len = sizeof(u_int32_t);
+    atpb.atp_sresiov = iov;
+    atpb.atp_sresiovcnt = 1;
+
+    if (atp_sresp( asp->asp_atp, &atpb ) < 0)
+      err = -1;
+
+    if (atp_close( asp->asp_atp ) < 0)
+      err = -1;
+
+    free( asp );
+    return err;
+}
diff --git a/libatalk/asp/asp_cmdreply.c b/libatalk/asp/asp_cmdreply.c
new file mode 100644 (file)
index 0000000..9acd7d9
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <atalk/atp.h>
+#include <atalk/asp.h>
+
+#if defined(BSD) || defined(BSD4_3)
+#define memmove(a, b, n)   bcopy((b), (a), (n))
+#endif
+
+int asp_cmdreply( asp, result)
+    ASP                asp;
+    int                result;
+{
+    struct iovec       iov[ ASP_MAXPACKETS ];
+    struct atp_block   atpb;
+    int                        iovcnt, buflen;
+    char                *buf;
+
+    /* unpack data into a format that atp likes. it needs to get
+     * 4-byte headers prepended before each ASP_CMDSIZ chunk. */
+    buf = (char *) asp->data;
+    buflen = asp->datalen;
+    asp->write_count += buflen;
+    result = htonl(result);
+
+    iovcnt = 0;
+    do {
+       iov[ iovcnt ].iov_base = buf;
+       memmove(buf + ASP_HDRSIZ, buf, buflen);
+
+       if ( iovcnt == 0 ) {
+           memcpy( iov[ iovcnt ].iov_base, &result, ASP_HDRSIZ );
+       } else {
+           memset( iov[ iovcnt ].iov_base, 0, ASP_HDRSIZ );
+       }
+
+       if ( buflen > ASP_CMDSIZ ) {
+         buf += ASP_CMDMAXSIZ;
+         buflen -= ASP_CMDSIZ;
+         iov[ iovcnt ].iov_len = ASP_CMDMAXSIZ;
+       } else {
+         iov[ iovcnt ].iov_len = buflen + ASP_HDRSIZ;
+         buflen = 0;
+       }
+       iovcnt++;
+    } while ( buflen > 0 );
+
+    atpb.atp_saddr = &asp->asp_sat;
+    atpb.atp_sresiov = iov;
+    atpb.atp_sresiovcnt = iovcnt;
+    if ( atp_sresp( asp->asp_atp, &atpb ) < 0 ) {
+       return( -1 );
+    }
+    asp->asp_seq++;
+
+    return( 0 );
+}
diff --git a/libatalk/asp/asp_getreq.c b/libatalk/asp/asp_getreq.c
new file mode 100644 (file)
index 0000000..60cdd0c
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ *
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <atalk/atp.h>
+#include <atalk/asp.h>
+
+int asp_getrequest(ASP asp)
+{
+    struct atp_block   atpb;
+    u_int16_t          seq;
+
+    asp->asp_sat.sat_port = ATADDR_ANYPORT;
+    atpb.atp_saddr = &asp->asp_sat;
+    atpb.atp_rreqdata = asp->cmdbuf;
+    atpb.atp_rreqdlen = sizeof(asp->cmdbuf);
+
+    if ( atp_rreq( asp->asp_atp, &atpb ) < 0 ) {
+       return( -1 );
+    }
+
+    asp->cmdlen = atpb.atp_rreqdlen - 4;
+    asp->read_count += asp->cmdlen;
+    memcpy( &seq, asp->cmdbuf + 2, sizeof(seq));
+    seq = ntohs( seq );
+
+    if ((asp->cmdbuf[0] != ASPFUNC_CLOSE) && (seq != asp->asp_seq)) {
+       return( -2 );
+    }
+    if ( asp->cmdbuf[1] != asp->asp_sid ) {
+       return( -3 );
+    }
+
+    return( asp->cmdbuf[0] ); /* the command */
+}
diff --git a/libatalk/asp/asp_getsess.c b/libatalk/asp/asp_getsess.c
new file mode 100644 (file)
index 0000000..df6c39f
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+ * Copyright (c) 1990,1996 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <syslog.h>
+#include <errno.h>
+#include <sys/signal.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <netatalk/at.h>
+#include <atalk/compat.h>
+#include <atalk/atp.h>
+#include <atalk/asp.h>
+
+#include <atalk/server_child.h>
+#include "asp_child.h"
+
+static ASP server_asp;
+static struct server_child *children = NULL;
+static struct asp_child    **asp_ac = NULL;
+
+/* send tickles and check tickle status of connections
+ * thoughts on using a hashed list:
+ * + child_cleanup, finding slots 
+ * - tickle_handler, freeing, tickles 
+ * if setup for a large number of connections,
+ * + space: if actual connections < potential
+ * - space: actual connections ~ potential
+ */
+static void tickle_handler()
+{
+  int sid;
+  
+  /* check status */
+  for (sid = 0; sid < children->nsessions; sid++) {
+    if (asp_ac[sid] == NULL || asp_ac[sid]->ac_state == ACSTATE_DEAD) 
+      continue;
+    
+    if (++asp_ac[sid]->ac_state >= ACSTATE_BAD) {
+      /* kill. if already dead, just continue */
+      if (kill( asp_ac[ sid ]->ac_pid, SIGTERM) == 0)
+       syslog( LOG_INFO, "asp_alrm: %d timed out",
+               asp_ac[ sid ]->ac_pid );
+
+      asp_ac[ sid ]->ac_state = ACSTATE_DEAD;
+      continue;
+    }
+
+    /* send off a tickle */
+    asp_tickle(server_asp, sid, &asp_ac[sid]->ac_sat);
+  }
+}
+
+static void child_cleanup(const pid_t pid)
+{
+  int i;
+
+  for (i = 0; i < children->nsessions; i++)
+    if (asp_ac[i] && (asp_ac[i]->ac_pid == pid)) {
+      asp_ac[i]->ac_state = ACSTATE_DEAD;
+      break;
+    }
+}
+
+
+/* kill children */
+void asp_kill(int sig)
+{
+  if (children) 
+    server_child_kill(children, CHILD_ASPFORK, sig);
+}
+
+
+/*
+ * This call handles open, tickle, and getstatus requests. On a
+ * successful open, it forks a child process. 
+ * It returns an ASP to the child and parent and NULL if there is
+ * an error.
+ */
+ASP asp_getsession(ASP asp, server_child *server_children, 
+                  const int tickleval)
+{
+    struct sigaction action;
+    struct itimerval timer;
+    struct sockaddr_at sat;
+    struct atp_block   atpb;
+    ATP                 atp;
+    struct iovec       iov[ 8 ];
+    pid_t               pid;
+    int                        i, sid;
+    u_int16_t          asperr;
+
+    if (!asp->inited) {
+      if (!(children = server_children))
+       return NULL;
+
+      if ((asp_ac = (struct asp_child **) 
+          calloc(server_children->nsessions, sizeof(struct asp_child *)))
+          == NULL)
+       return NULL;
+
+      server_asp = asp;
+
+      /* install cleanup pointer */
+      server_child_setup(children, CHILD_ASPFORK, child_cleanup);
+
+      /* install tickle handler */
+      memset(&action, 0, sizeof(action));
+      action.sa_handler = tickle_handler;
+      sigemptyset(&action.sa_mask);
+      action.sa_flags = SA_RESTART;
+
+      timer.it_interval.tv_sec = timer.it_value.tv_sec = tickleval;
+      timer.it_interval.tv_usec = timer.it_value.tv_usec = 0;
+      if ((sigaction(SIGALRM, &action, NULL) < 0) ||
+         (setitimer(ITIMER_REAL, &timer, NULL) < 0)) {
+       free(asp_ac);
+       return NULL;
+      }
+
+      asp->inited = 1;
+    }
+                   
+    memset( &sat, 0, sizeof( struct sockaddr_at ));
+#ifdef BSD4_4
+    sat.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+    sat.sat_family = AF_APPLETALK;
+    sat.sat_addr.s_net = ATADDR_ANYNET;
+    sat.sat_addr.s_node = ATADDR_ANYNODE;
+    sat.sat_port = ATADDR_ANYPORT;
+    atpb.atp_saddr = &sat;
+    atpb.atp_rreqdata = asp->cmdbuf;
+    atpb.atp_rreqdlen = sizeof( asp->cmdbuf );
+    while ( atp_rreq( asp->asp_atp, &atpb ) < 0 ) {
+      if ( errno == EINTR || errno == EAGAIN ) {
+       continue;
+      }
+      return( NULL );
+    }
+    
+    switch ( asp->cmdbuf[ 0 ] ) {
+    case ASPFUNC_TICKLE:
+      sid = asp->cmdbuf[1];
+      if ((asp_ac[sid] != NULL) && (asp_ac[sid]->ac_state != ACSTATE_DEAD))
+       asp_ac[sid]->ac_state = ACSTATE_OK;
+      break;
+
+    case ASPFUNC_STAT :
+#ifdef EBUG
+      printf( "asp stat\n" );
+#endif EBUG
+      if ( asp->asp_slen > 0 ) {
+       asp->cmdbuf[0] = 0;
+       memcpy( asp->cmdbuf + 4, asp->asp_status, asp->asp_slen );
+       iov[ 0 ].iov_base = asp->cmdbuf;
+       iov[ 0 ].iov_len = 4 + asp->asp_slen;
+       atpb.atp_sresiov = iov;
+       atpb.atp_sresiovcnt = 1;
+       atp_sresp( asp->asp_atp, &atpb );
+      }
+      break;
+
+    case ASPFUNC_OPEN :
+      if (children->count < children->nsessions) {
+
+       /* find a slot */
+       for (sid = 0; sid < children->nsessions; sid++) {
+         if (asp_ac[sid] == NULL)
+           break;
+
+         if (asp_ac[sid]->ac_state == ACSTATE_DEAD) {
+           free(asp_ac[sid]);
+           asp_ac[sid] = NULL;
+           break;
+         }
+       }
+
+       if ((atp = atp_open(ATADDR_ANYPORT, 
+                           &(atp_sockaddr(asp->asp_atp)->sat_addr))) == NULL) 
+         return NULL;
+
+       switch ((pid = fork())) {
+       case 0 : /* child */
+         signal(SIGTERM, SIG_DFL);
+         signal(SIGHUP, SIG_DFL);
+         /* free/close some things */
+         for (i = 0; i < children->nsessions; i++ ) {
+           if ( asp_ac[i] != NULL )
+             free( asp_ac[i] );
+         }
+         free(asp_ac);
+         
+         server_child_free(children);
+         children = NULL;
+         atp_close(asp->asp_atp);
+
+         asp->child = 1;
+         asp->asp_atp = atp;
+         asp->asp_sat = sat;
+         asp->asp_wss = asp->cmdbuf[1];
+         asp->asp_seq = 0;
+         asp->asp_sid = sid;
+         asp->asp_flags = ASPFL_SSS;
+         return asp;
+         
+       case -1 : /* error */
+         asp->cmdbuf[ 0 ] = 0;
+         asp->cmdbuf[ 1 ] = 0;
+         asperr = ASPERR_SERVBUSY;
+         break;
+         
+       default : /* parent process */
+         switch (server_child_add(children, CHILD_ASPFORK, pid)) {
+         case 0: /* added child */
+           if ((asp_ac[sid] = (struct asp_child *) 
+                malloc(sizeof(struct asp_child)))) {
+             asp_ac[sid]->ac_pid = pid;
+             asp_ac[sid]->ac_state = ACSTATE_OK;
+             asp_ac[sid]->ac_sat = sat;
+             asp_ac[sid]->ac_sat.sat_port = asp->cmdbuf[1];
+             
+             asp->cmdbuf[0] = atp_sockaddr(atp)->sat_port;
+             asp->cmdbuf[1] = sid;
+             asperr = ASPERR_OK;
+             break;
+           } /* fall through if malloc fails */
+         case -1: /* bad error */
+           kill(pid, SIGQUIT); 
+           break;
+         default: /* non-fatal error */
+           break;
+         }
+         atp_close(atp);
+         break;
+       }
+       
+      } else {
+       asp->cmdbuf[0] = asp->cmdbuf[1] = 0;
+       asperr = ASPERR_SERVBUSY;
+      }
+
+      memcpy( asp->cmdbuf + 2, &asperr, sizeof(asperr));
+      iov[ 0 ].iov_base = asp->cmdbuf;
+      iov[ 0 ].iov_len = 4;
+      atpb.atp_sresiov = iov;
+      atpb.atp_sresiovcnt = 1;
+      atp_sresp( asp->asp_atp, &atpb );
+      break;
+
+    default:
+      syslog(LOG_INFO, "ASPUnknown %d", asp->cmdbuf[0]);
+      break;
+    }
+
+    return asp;
+}
diff --git a/libatalk/asp/asp_init.c b/libatalk/asp/asp_init.c
new file mode 100644 (file)
index 0000000..431d80d
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <netatalk/at.h>
+#include <atalk/atp.h>
+#include <atalk/asp.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+ASP asp_init( atp )
+    ATP                atp;
+{
+    ASP                asp;
+
+    if (( asp = (struct ASP *)calloc(1, sizeof( struct ASP ))) == NULL ) {
+       return( NULL );
+    }
+
+    asp->asp_atp = atp;
+#ifdef BSD4_4
+    asp->asp_sat.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+    asp->asp_sat.sat_family = AF_APPLETALK;
+    asp->asp_sat.sat_addr.s_net = ATADDR_ANYNET;
+    asp->asp_sat.sat_addr.s_node = ATADDR_ANYNODE;
+    asp->asp_sat.sat_port = ATADDR_ANYPORT;
+    asp->asp_status = NULL;
+    asp->asp_slen = 0;
+    asp->asp_sid = 0;
+    asp->asp_flags = ASPFL_SLS;
+    asp->cmdlen = asp->datalen = 0;
+    asp->read_count = asp->write_count = 0;
+    asp->commands = asp->cmdbuf + 4;
+
+    return( asp );
+}
+
+void asp_setstatus( asp, status, slen )
+    ASP                asp;
+    char       *status;
+    const int  slen;
+{
+    asp->asp_status = status;
+    asp->asp_slen = slen;
+}
diff --git a/libatalk/asp/asp_shutdown.c b/libatalk/asp/asp_shutdown.c
new file mode 100644 (file)
index 0000000..afe3581
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 1996 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <atalk/atp.h>
+#include <atalk/asp.h>
+
+int asp_shutdown( asp )
+    ASP                        asp;
+{
+    struct atp_block   atpb;
+    struct iovec       iov;
+    char               *p;
+    u_int16_t          seq;
+    u_int8_t           oport;
+
+
+    p = asp->commands;
+    *p++ = ASPFUNC_CLOSE;
+    *p++ = asp->asp_sid;
+    seq = 0;
+    memcpy( p, &seq, sizeof(seq));
+    p += sizeof(seq);
+
+    oport = asp->asp_sat.sat_port;
+    atpb.atp_saddr = &asp->asp_sat;
+    atpb.atp_saddr->sat_port = asp->asp_wss;
+    atpb.atp_sreqdata = asp->commands;
+    atpb.atp_sreqdlen = p - asp->commands;
+    atpb.atp_sreqto = 2;
+    atpb.atp_sreqtries = 5;
+
+    if ( atp_sreq( asp->asp_atp, &atpb, 1, ATP_XO ) < 0 ) {
+       asp->asp_sat.sat_port = oport;
+       return( -1 );
+    }
+
+    iov.iov_base = asp->commands;
+    iov.iov_len = ASP_CMDSIZ;
+    atpb.atp_rresiov = &iov;
+    atpb.atp_rresiovcnt = 1;
+
+    if ( atp_rresp( asp->asp_atp, &atpb ) < 0 ) {
+       asp->asp_sat.sat_port = oport;
+       return( -1 );
+    }
+    asp->asp_sat.sat_port = oport;
+
+    return( 0 );
+}
diff --git a/libatalk/asp/asp_tickle.c b/libatalk/asp/asp_tickle.c
new file mode 100644 (file)
index 0000000..a591db4
--- /dev/null
@@ -0,0 +1,23 @@
+#include <syslog.h>
+#include <atalk/atp.h>
+#include <atalk/asp.h>
+
+/* send off a tickle */
+void asp_tickle(ASP asp, const u_int8_t sid, struct sockaddr_at *sat)
+{
+  struct atp_block atpb;
+  char buf[ASP_HDRSIZ];
+
+  buf[ 0 ] = ASPFUNC_TICKLE;
+  buf[ 1 ] = sid;
+  buf[ 2 ] = buf[ 3 ] = 0;
+
+  atpb.atp_saddr = sat;
+  atpb.atp_sreqdata = buf;
+  atpb.atp_sreqdlen = sizeof(buf);
+  atpb.atp_sreqto = 0;
+  atpb.atp_sreqtries = 1;
+  if ( atp_sreq( asp->asp_atp, &atpb, 0, 0 ) < 0 ) {
+    syslog( LOG_ERR, "atp_sreq: %m" );
+  }
+}
diff --git a/libatalk/asp/asp_write.c b/libatalk/asp/asp_write.c
new file mode 100644 (file)
index 0000000..411eb0b
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <atalk/atp.h>
+#include <atalk/asp.h>
+
+#if defined(BSD) || defined(BSD4_3)
+#define memmove(a, b, n)   bcopy((b), (a), (n))
+#endif
+
+int asp_wrtcont(ASP asp, char *buf, int *buflen)
+{
+    struct iovec       iov[ ASP_MAXPACKETS ];
+    struct atp_block   atpb;
+    char               *p;
+    int                        iovcnt = ASP_MAXPACKETS;
+    u_int16_t          blen, seq;
+    u_int8_t           oport;
+
+    p = buf;
+    *p++ = ASPFUNC_WRTCONT;
+    *p++ = asp->asp_sid;
+    seq = htons( asp->asp_seq );
+    memcpy( p, &seq, sizeof(seq));
+    p += sizeof(seq);
+    blen = htons(*buflen);
+    memcpy( p, &blen, sizeof(blen));
+    p += sizeof(blen);
+
+    for ( iovcnt = 0; iovcnt < ASP_MAXPACKETS; iovcnt++ ) {
+        iov[iovcnt].iov_base = buf + iovcnt*ASP_CMDMAXSIZ;
+       iov[ iovcnt ].iov_len = ASP_CMDMAXSIZ;
+    }
+
+    oport = asp->asp_sat.sat_port;
+    atpb.atp_saddr = &asp->asp_sat;
+    atpb.atp_saddr->sat_port = asp->asp_wss;
+    atpb.atp_sreqdata = buf;
+    atpb.atp_sreqdlen = p - buf;
+    atpb.atp_sreqto = 2;
+    atpb.atp_sreqtries = 5;
+
+    if ( atp_sreq( asp->asp_atp, &atpb, iovcnt, ATP_XO ) < 0 ) {
+       asp->asp_sat.sat_port = oport;
+       return( -1 );
+    }
+    asp->write_count += atpb.atp_sreqdlen;
+
+    atpb.atp_rresiov = iov;
+    atpb.atp_rresiovcnt = iovcnt;
+    if ( atp_rresp( asp->asp_atp, &atpb ) < 0 ) {
+       asp->asp_sat.sat_port = oport;
+       return( -1 );
+    }
+
+    asp->asp_sat.sat_port = oport;
+
+    /* get rid of the 4-byte headers */
+    p = buf;
+    for ( iovcnt = 0; iovcnt < atpb.atp_rresiovcnt; iovcnt++ ) {
+       memmove(p, (char *) iov[ iovcnt ].iov_base + ASP_HDRSIZ, 
+               iov[ iovcnt ].iov_len - ASP_HDRSIZ );
+       p += ( iov[ iovcnt ].iov_len - ASP_HDRSIZ );
+    }
+
+    *buflen = p - buf;
+    asp->read_count += *buflen;
+    return 0;
+}
diff --git a/libatalk/atp/Makefile b/libatalk/atp/Makefile
new file mode 100644 (file)
index 0000000..8ef895c
--- /dev/null
@@ -0,0 +1,58 @@
+SRC=   atp_bufs.c atp_close.c atp_open.c atp_packet.c atp_rreq.c \
+       atp_rresp.c atp_rsel.c atp_sreq.c atp_sresp.c
+OBJ=   atp_bufs.o atp_close.o atp_open.o atp_packet.o atp_rreq.o \
+       atp_rresp.o atp_rsel.o atp_sreq.o atp_sresp.o
+
+INCPATH=       -I../../include
+CFLAGS=        ${DEFS} ${OPTOPTS} ${INCPATH} # -DDROP_ATPTREL -DEBUG -DDROPPACKETS -g
+TAGSFILE=      tags
+CC=    cc
+
+all: profiled atplib
+
+profiled:
+       -mkdir profiled
+
+atplib atplib_p : ${OBJ}
+       @echo "building profiled atplib"
+       @cd profiled; ar cru ../atplib_p ${OBJ}
+       @echo "building normal atplib"
+       @ar cru atplib ${OBJ}
+
+.c.o :
+       ${CC} -p ${CFLAGS} -c $*.c
+       -ld -r -o $*.o- $*.o
+       mv $*.o- profiled/$*.o
+       ${CC} ${CFLAGS} -c $*.c
+       -ld -r -o $*.o- $*.o
+       mv $*.o- $*.o
+
+clean :
+       rm -f *.o profiled/*.o *.bak *[Ee]rrs tags
+       rm -f atplib atplib_p
+
+tags : ${SRC}
+       cwd=`pwd`; \
+       for i in ${SRC}; do \
+           ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+       done
+
+depend :
+       for i in ${SRC} ; do \
+           ${CC} -M ${DEFS} ${INCPATH} $$i | \
+           awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+               else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+               else rec = rec " " $$2 } } \
+               END { print rec } ' >> makedep; done
+       sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+       cat makedep >> Makefile.tmp
+       rm makedep
+       echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+       echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+       echo '# see make depend above' >> Makefile.tmp
+       rm -f Makefile.bak
+       cp Makefile Makefile.bak
+       mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
diff --git a/libatalk/atp/atp_bprint.c b/libatalk/atp/atp_bprint.c
new file mode 100644 (file)
index 0000000..6d141b7
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#define BPLEN  48
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+char   hexdig[] = "0123456789abcdef";
+
+bprint( data, len )
+    char       *data;
+    int                len;
+{
+    char       out[ BPLEN ];
+    int                i = 0;
+
+    memset( out, 0, BPLEN );
+    for ( ;; ) {
+       if ( len < 1 ) {
+           printf( "\t%s\n", ( i == 0 ) ? "(end)" : out );
+           break;
+       }
+
+       if ( isgraph( (unsigned char)*data )) {
+           out[ i ] = ' ';
+           out[ i+1 ] = *data;
+       } else {
+           out[ i ] = hexdig[ ( *data & 0xf0 ) >> 4 ];
+           out[ i+1 ] = hexdig[ *data & 0x0f ];
+       }
+       i += 2;
+       len--;
+       data++;
+
+       if ( i > BPLEN - 2 ) {
+           printf( "\t%s\n", out );
+           memset( out, 0, BPLEN );
+           i = 0;
+           continue;
+       }
+       out[ i++ ] = ' ';
+    }
+}
diff --git a/libatalk/atp/atp_bufs.c b/libatalk/atp/atp_bufs.c
new file mode 100644 (file)
index 0000000..ae79074
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+/* 
+ * Our own memory maintenance for atp
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <netatalk/at.h>
+#include <atalk/atp.h>
+#include "atp_internals.h"
+
+#define                        N_MORE_BUFS             10
+
+static struct atpbuf   *free_list = NULL;      /* free buffers */
+
+#ifdef EBUG
+static int             numbufs = 0;
+#endif EBUG
+
+/* only call this when the free_list is empty...
+ * N_MORE_BUFS must be >= one
+*/
+static int more_bufs(void)
+{
+    int                        i;
+    char               *mem;
+    struct atpbuf      *bp;
+
+    /* get the whole chunk in one malloc call
+    */
+    if (( mem = malloc( N_MORE_BUFS * sizeof( struct atpbuf ))) == NULL ) {
+       errno = ENOBUFS;
+       return -1;
+    }
+    /* now split into separate bufs
+    */
+    bp = free_list = (struct atpbuf *) mem;
+    for ( i = 1; i < N_MORE_BUFS; ++i ) {
+       bp->atpbuf_next = (struct atpbuf *) ( mem += sizeof( struct atpbuf ));
+       bp = bp->atpbuf_next;
+    }
+    bp->atpbuf_next = NULL;
+
+    return 0;
+}
+
+
+#ifdef EBUG
+void atp_print_bufuse( ah, s )
+    ATP                ah;
+    char       *s;
+{
+    struct atpbuf      *bp;
+    int                        i, sentcount, incount, respcount;
+
+    sentcount = 0;
+    for ( bp = ah->atph_sent; bp != NULL; bp = bp->atpbuf_next ) {
+       ++sentcount;
+       for ( i = 0; i < 8; ++i ) {
+           if ( bp->atpbuf_info.atpbuf_xo.atpxo_packet[ i ] != NULL ) {
+               ++sentcount;
+           }
+       }
+    }
+
+    if ( ah->atph_reqpkt != NULL ) {
+       ++sentcount;
+    }
+
+
+    incount = 0;
+    for ( bp = ah->atph_queue; bp != NULL; bp = bp->atpbuf_next, ++incount );
+
+    respcount = 0;
+    for ( i = 0; i < 8; ++i ) {
+        if ( ah->atph_resppkt[ i ] != NULL ) {
+           ++respcount;
+       }
+    }
+
+    printf( "<%d> %s: bufs total %d  sent %d  incoming %d  req %d  resp %d\n",
+       getpid(), s, numbufs, sentcount, incount,
+       ( ah->atph_reqpkt != NULL ) ? 1: 0, respcount );
+}
+#endif EBUG
+
+
+struct atpbuf *atp_alloc_buf(void)
+{
+    struct atpbuf *bp;
+
+    if ( free_list == NULL && more_bufs() ) return NULL;
+
+    bp = free_list;
+    free_list = free_list->atpbuf_next;
+#ifdef EBUG
+    ++numbufs;
+#endif EBUG
+    return bp;
+}
+
+
+int atp_free_buf( bp )
+    struct atpbuf      *bp;
+{
+    if ( bp == NULL ) {
+       return -1;
+    }
+    bp->atpbuf_next = free_list;
+    free_list = bp;
+#ifdef EBUG
+    --numbufs;
+#endif EBUG
+    return 0;
+}
+
+
diff --git a/libatalk/atp/atp_close.c b/libatalk/atp/atp_close.c
new file mode 100644 (file)
index 0000000..47aa9ab
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 1990,1997 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <netatalk/at.h>
+#include <atalk/netddp.h>
+#include <atalk/atp.h>
+#include "atp_internals.h"
+#ifdef EBUG
+#include <stdio.h>
+#endif EBUG
+
+int atp_close( ah )
+    ATP                ah;
+{
+    struct atpbuf      *cq;
+    int                        i;
+
+    /* remove from list of open atp sockets & discard queued data
+    */
+#ifdef EBUG
+    print_bufuse( ah, "atp_close");
+#endif EBUG
+
+    while ( ah->atph_queue != NULL ) {
+       cq = ah->atph_queue;
+       ah->atph_queue = cq->atpbuf_next;
+       atp_free_buf( cq );
+    }
+
+    while ( ah->atph_sent != NULL ) {
+       cq = ah->atph_sent;
+       for ( i = 0; i < 8; ++i ) {
+           if ( cq->atpbuf_info.atpbuf_xo.atpxo_packet[ i ] != NULL ) {
+               atp_free_buf( cq->atpbuf_info.atpbuf_xo.atpxo_packet[ i ] );
+           }
+       }
+       ah->atph_sent = cq->atpbuf_next;
+       atp_free_buf( cq );
+    }
+
+    if ( ah->atph_reqpkt != NULL ) {
+       atp_free_buf( ah->atph_reqpkt );
+       ah->atph_reqpkt = NULL;
+    }
+
+    for ( i = 0; i < 8; ++i ) {
+       if ( ah->atph_resppkt[ i ] != NULL ) {
+           atp_free_buf( ah->atph_resppkt[ i ] );
+           ah->atph_resppkt[ i ] = NULL;
+       }
+    }
+
+#ifdef EBUG
+    print_bufuse( ah, "atp_close end");
+#endif EBUG
+
+    i = ah->atph_socket;
+    atp_free_buf( (struct atpbuf *) ah );
+
+    if (netddp_close(i) < 0)
+      return -1;
+
+    return 0;
+}
diff --git a/libatalk/atp/atp_internals.h b/libatalk/atp/atp_internals.h
new file mode 100644 (file)
index 0000000..70959d8
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#ifndef ATP_INTERNALS_H
+#define ATP_INTERNALS_H 1
+
+#include <sys/cdefs.h>
+#include <netatalk/at.h>
+#include <netatalk/endian.h>
+#include <atalk/atp.h>
+
+/*
+ * masks for matching incoming packets
+ */
+#define ATP_FUNCANY    ATP_TREQ | ATP_TRESP | ATP_TREL
+#define ATP_TIDANY     0xffff
+
+/* in atp_bufs.c */
+extern struct atpbuf *atp_alloc_buf __P((void));
+extern void atp_print_bufuse        __P((ATP, char *));
+extern int atp_free_buf             __P((struct atpbuf *));
+
+/* in atp_packet.c */
+extern int at_addr_eq               __P((struct sockaddr_at *, 
+                                        struct sockaddr_at *));
+extern void atp_build_req_packet    __P((struct atpbuf *, u_int16_t, 
+                                        u_int8_t, struct atp_block *));
+extern void atp_build_resp_packet   __P((struct atpbuf *, u_int16_t,
+                                        u_int8_t, struct atp_block *,
+                                        u_int8_t));
+extern int atp_recv_atp             __P((ATP, struct sockaddr_at *, 
+                                        u_int8_t *, u_int16_t, char *,
+                                        int));
+#ifdef EBUG
+extern void atp_print_addr          __P((char *, struct sockaddr_at *));
+#endif
+
+#endif
diff --git a/libatalk/atp/atp_open.c b/libatalk/atp/atp_open.c
new file mode 100644 (file)
index 0000000..cc690f7
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/param.h>
+
+#include <netatalk/at.h>
+#include <netatalk/endian.h>
+
+#include <atalk/netddp.h>
+#include <atalk/ddp.h>
+#include <atalk/atp.h>
+
+#include "atp_internals.h"
+
+ATP atp_open(port, saddr)
+    u_int8_t port;
+    const struct at_addr *saddr;
+{
+    struct sockaddr_at  addr;
+    int                        s;
+    ATP                        atp;
+    struct timeval     tv;
+    int                        pid;
+
+#ifdef EBUG
+    printf( "<%d> atp_open\n", getpid());
+#endif
+
+    memset(&addr, 0, sizeof(addr));
+    addr.sat_port = port;
+    if (saddr) 
+      memcpy(&addr.sat_addr, saddr, sizeof(struct at_addr));
+    if ((s = netddp_open(&addr, NULL)) < 0)
+        return NULL;
+
+    if (( atp = (ATP) atp_alloc_buf()) == NULL ) {
+        netddp_close(s);
+       return NULL;
+    }
+
+    /* initialize the atp handle */
+    memset(atp, 0, sizeof( struct atp_handle ));
+    memcpy(&atp->atph_saddr, &addr, sizeof(addr));
+
+    atp->atph_socket = s;
+    atp->atph_reqto = -1;
+    gettimeofday( &tv, (struct timezone *) 0 );
+    pid = getpid();
+    atp->atph_tid = tv.tv_sec ^ ((( pid << 8 ) & 0xff00 ) | ( pid >> 8 ));
+
+#ifdef EBUG
+srandom( tv.tv_sec );
+#endif
+
+    return atp;
+}
diff --git a/libatalk/atp/atp_packet.c b/libatalk/atp/atp_packet.c
new file mode 100644 (file)
index 0000000..6e02922
--- /dev/null
@@ -0,0 +1,329 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/param.h>
+#include <netinet/in.h>
+
+#include <netatalk/at.h>
+#include <netatalk/endian.h>
+
+#include <atalk/netddp.h>
+#include <atalk/ddp.h>
+#include <atalk/atp.h>
+#include <atalk/util.h>
+
+#include "atp_internals.h"
+
+/* FIXME/SOCKLEN_T: socklen_t is a unix98 feature. */
+#ifndef SOCKLEN_T
+#define SOCKLEN_T unsigned int
+#endif
+
+#ifdef EBUG
+#include <stdio.h>
+
+static void print_func( ctrlinfo )
+    u_int8_t   ctrlinfo;
+{
+    switch ( ctrlinfo & ATP_FUNCMASK ) {
+    case ATP_TREQ:
+       printf( "TREQ" );
+       break;
+    case ATP_TRESP:
+       printf( "TRESP" );
+       break;
+    case ATP_TREL:
+       printf( "ANY/TREL" );
+       break;
+    case ATP_TIDANY:
+       printf( "*" );
+       break;
+    default:
+       printf( "%x", ctrlinfo & ATP_FUNCMASK );
+    }
+}
+
+static void dump_packet( buf, len )
+    char       *buf;
+    int                len;
+{
+    int                i;
+
+    for ( i = 0; i < len; ++i ) {
+       printf( "%x-%c ", buf[i], buf[i] );
+    }
+    putchar( '\n' );
+}
+
+void atp_print_addr( s, saddr )
+    char               *s;
+    struct sockaddr_at *saddr;
+{
+    printf( "%s ", s );
+    saddr->sat_family == AF_APPLETALK ? printf( "at." ) :
+      printf( "%d.", saddr->sat_family );
+    saddr->sat_addr.s_net == ATADDR_ANYNET ? printf( "*." ) :
+      printf( "%d.", ntohs( saddr->sat_addr.s_net ));
+    saddr->sat_addr.s_node == ATADDR_ANYNODE ? printf( "*." ) :
+      printf( "%d.", saddr->sat_addr.s_node );
+    saddr->sat_port == ATADDR_ANYPORT ? printf( "*" ) :
+      printf( "%d", saddr->sat_port );
+}
+#endif
+
+
+void atp_build_req_packet( pktbuf, tid, ctrl, atpb )
+    struct atpbuf      *pktbuf;
+    u_int16_t          tid;
+    u_int8_t           ctrl;
+    struct atp_block   *atpb;
+{
+    struct atphdr      hdr;
+
+    /* fill in the packet fields
+    */
+    hdr.atphd_ctrlinfo = ctrl;
+    hdr.atphd_bitmap = atpb->atp_bitmap;
+    hdr.atphd_tid = htons( tid );
+    *(pktbuf->atpbuf_info.atpbuf_data) = DDPTYPE_ATP;
+    memcpy(pktbuf->atpbuf_info.atpbuf_data + 1, &hdr, sizeof( struct atphdr ));
+    memcpy(pktbuf->atpbuf_info.atpbuf_data + ATP_HDRSIZE, 
+          atpb->atp_sreqdata, atpb->atp_sreqdlen ); 
+
+    /* set length
+    */
+    pktbuf->atpbuf_dlen = ATP_HDRSIZE + atpb->atp_sreqdlen;
+}
+
+void atp_build_resp_packet( pktbuf, tid, ctrl, atpb, seqnum )
+    struct atpbuf      *pktbuf;
+    u_int16_t          tid;
+    u_int8_t           ctrl;
+    struct atp_block   *atpb;
+    u_int8_t           seqnum;
+{
+    struct atphdr      hdr;
+
+    /* fill in the packet fields */
+    *(pktbuf->atpbuf_info.atpbuf_data) = DDPTYPE_ATP;
+    hdr.atphd_ctrlinfo = ctrl;
+    hdr.atphd_bitmap = seqnum;
+    hdr.atphd_tid = htons( tid );
+    memcpy(pktbuf->atpbuf_info.atpbuf_data + 1, &hdr, 
+          sizeof( struct atphdr ));
+    memcpy(pktbuf->atpbuf_info.atpbuf_data + ATP_HDRSIZE,
+         atpb->atp_sresiov[ seqnum ].iov_base,
+         atpb->atp_sresiov[ seqnum ].iov_len ); 
+
+    /* set length
+    */
+    pktbuf->atpbuf_dlen = ATP_HDRSIZE + atpb->atp_sresiov[ seqnum ].iov_len;
+}
+
+
+int
+atp_recv_atp( ah, fromaddr, func, tid, rbuf, wait )
+    ATP                        ah;
+    struct sockaddr_at *fromaddr;
+    u_int8_t           *func;
+    u_int16_t          tid;
+    char               *rbuf;
+    int                        wait;
+{
+/* 
+  Receive a packet from address fromaddr of the correct function type
+  and with the correct tid.  fromaddr = AT_ANY... and function == ATP_TYPEANY
+  and tid == ATP_TIDANY can be used to wildcard match.
+  
+  recv_atp returns the length of the packet received (or -1 if error)
+  The function code for the packet received is returned in *func (ATP_TREQ or
+    ATP_TRESP).
+*/
+    struct atpbuf      *pq, *cq;
+    struct atphdr      ahdr;
+    u_int16_t          rfunc;
+    u_int16_t          rtid;
+    int                        i;
+    int                        dlen = -1;
+    int                        recvlen;
+    struct sockaddr_at faddr;
+    SOCKLEN_T          faddrlen;
+    struct atpbuf      *inbuf;
+
+    tid = htons( tid );
+
+    /* first check the queue
+    */
+#ifdef EBUG
+    atp_print_bufuse( ah, "recv_atp checking queue" );
+#endif
+    for ( pq = NULL, cq = ah->atph_queue; cq != NULL;
+      pq = cq, cq = cq->atpbuf_next ) {
+       memcpy(&ahdr, cq->atpbuf_info.atpbuf_data + 1, 
+              sizeof( struct atphdr ));
+       rfunc = ahdr.atphd_ctrlinfo & ATP_FUNCMASK;
+#ifdef EBUG
+       printf( "<%d> checking", getpid());
+       printf( " tid=%hu func=", ntohs( ahdr.atphd_tid ));
+       print_func( rfunc );
+       atp_print_addr( " from", &cq->atpbuf_addr );
+       putchar( '\n' );
+#endif
+       if ((( tid & ahdr.atphd_tid ) == ahdr.atphd_tid ) &&
+           (( *func & rfunc ) == rfunc )
+           && at_addr_eq( fromaddr, &cq->atpbuf_addr )) {
+           break;
+       }
+    }
+    if ( cq != NULL ) {
+       /* we found one in the queue -- copy to rbuf
+       */
+       dlen = cq->atpbuf_dlen;
+       *func = rfunc;
+       memcpy( fromaddr, &cq->atpbuf_addr, sizeof( struct sockaddr_at ));
+       memcpy( rbuf, cq->atpbuf_info.atpbuf_data, cq->atpbuf_dlen );
+
+       /* remove packet from queue and free buffer
+       */
+       if ( pq == NULL ) {
+           ah->atph_queue = NULL;
+       } else {
+           pq->atpbuf_next = cq->atpbuf_next;
+       }
+       atp_free_buf( cq );
+       return( dlen );
+    }
+
+    /* we need to get it the net -- call on ddp to receive a packet
+    */
+#ifdef EBUG
+    printf( "<%d>", getpid());
+    atp_print_addr( " waiting on address", &ah->atph_saddr );
+    printf( "\nfor tid=%hu func=", ntohs( tid ));
+    print_func( *func );
+    atp_print_addr( " from", fromaddr );
+    putchar( '\n' );
+#endif
+
+    do {
+#ifdef EBUG
+    fflush( stdout );
+#endif
+       faddrlen = sizeof( struct sockaddr_at );
+       memset( &faddr, 0, sizeof( struct sockaddr_at ));
+
+       if (( recvlen = netddp_recvfrom( ah->atph_socket, rbuf, 
+                                        ATP_BUFSIZ, 0,
+                                        (struct sockaddr *) &faddr,
+                                        &faddrlen )) < 0 ) {
+           return -1;
+       }
+       memcpy( &ahdr, rbuf + 1, sizeof( struct atphdr ));
+       if ( recvlen >= ATP_HDRSIZE && *rbuf == DDPTYPE_ATP) {
+           /* this is a valid ATP packet -- check for a match */
+           rfunc = ahdr.atphd_ctrlinfo & ATP_FUNCMASK;
+           rtid = ahdr.atphd_tid;
+#ifdef EBUG
+           printf( "<%d> got tid=%hu func=", getpid(), ntohs( rtid ));
+           print_func( rfunc );
+           atp_print_addr( " from", &faddr );
+           putchar( '\n' );
+           bprint( rbuf, recvlen );
+#endif
+           if ( rfunc == ATP_TREL ) {
+               /* remove response from sent list */
+               for ( pq = NULL, cq = ah->atph_sent; cq != NULL;
+                 pq = cq, cq = cq->atpbuf_next ) {
+                   if ( at_addr_eq( &faddr, &cq->atpbuf_addr ) &&
+                     cq->atpbuf_info.atpbuf_xo.atpxo_tid == ntohs( rtid )) 
+                       break;
+               }
+               if ( cq != NULL ) {
+#ifdef EBUG
+       printf( "<%d> releasing transaction %hu\n", getpid(), ntohs( rtid ));
+#endif
+                   if ( pq == NULL ) {
+                       ah->atph_sent = cq->atpbuf_next;
+                   } else {
+                       pq->atpbuf_next = cq->atpbuf_next;
+                   }
+                   for ( i = 0; i < 8; ++i ) {
+                       if ( cq->atpbuf_info.atpbuf_xo.atpxo_packet[ i ]
+                                   != NULL ) {
+                           atp_free_buf ( cq->atpbuf_info.atpbuf_xo.atpxo_packet[ i ] );
+                       }
+                   }
+                   atp_free_buf( cq );
+               }
+
+           } else if ((( tid & rtid ) == rtid ) &&
+                   (( *func & rfunc ) == rfunc ) &&
+                   at_addr_eq( fromaddr, &faddr )) { /* got what we wanted */
+               *func = rfunc;
+               dlen = recvlen;
+               memcpy( fromaddr, &faddr, sizeof( struct sockaddr_at ));
+
+           } else {
+               /* add packet to incoming queue */
+#ifdef EBUG
+       printf( "<%d> queuing incoming...\n", getpid() );
+#endif
+               if (( inbuf = atp_alloc_buf()) == NULL ) {
+                   return -1;
+               }
+               memcpy( &inbuf->atpbuf_addr, &faddr, 
+                       sizeof( struct sockaddr_at ));
+               inbuf->atpbuf_next = ah->atph_queue;
+               inbuf->atpbuf_dlen = recvlen;
+               memcpy( inbuf->atpbuf_info.atpbuf_data, rbuf, recvlen );
+           }
+       }
+       if ( !wait && dlen < 0 ) {
+           return( 0 );
+       }
+
+    } while ( dlen < 0 );
+
+    return( dlen );
+}
+
+
+int at_addr_eq( paddr, saddr )
+    struct sockaddr_at *paddr;         /* primary address */
+    struct sockaddr_at *saddr;         /* secondary address */
+{
+/* compare two atalk addresses -- only check the non-zero fields
+    of paddr against saddr.
+   return zero if not equal, non-zero if equal
+*/
+    return (( paddr->sat_port == ATADDR_ANYPORT || paddr->sat_port == saddr->sat_port )
+       &&  ( paddr->sat_addr.s_net == ATADDR_ANYNET ||
+             paddr->sat_addr.s_net == saddr->sat_addr.s_net )
+       &&  ( paddr->sat_addr.s_node == ATADDR_ANYNODE ||
+             paddr->sat_addr.s_node == saddr->sat_addr.s_node ));
+}
+
diff --git a/libatalk/atp/atp_rreq.c b/libatalk/atp/atp_rreq.c
new file mode 100644 (file)
index 0000000..be11f42
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#include <netinet/in.h>
+#include <netatalk/at.h>
+#include <netatalk/endian.h>
+#include <atalk/atp.h>
+
+#include "atp_internals.h"
+
+
+/* wait for a tranasaction service request
+*/
+int atp_rreq( ah, atpb )
+    ATP                        ah;             /* open atp handle */
+    struct atp_block   *atpb;          /* parameter block */
+{
+    struct atpbuf      *req_buf;       /* for receiving request packet */
+    struct atphdr      req_hdr;        /* request header overlay */
+    struct sockaddr_at faddr;          /* sender's address */
+    int                        recvlen;        /* length of received packet */
+    u_int16_t          tid;
+    int                        rc;
+    u_int8_t           func;
+
+#ifdef EBUG
+    atp_print_bufuse( ah, "atp_rreq" );
+#endif
+
+    while (( rc = atp_rsel( ah, atpb->atp_saddr, ATP_TREQ )) == 0 ) {
+       ;
+    }
+
+    if ( rc != ATP_TREQ ) {
+#ifdef EBUG
+       printf( "<%d> atp_rreq: atp_rsel returns err %d\n", getpid(), rc );
+#endif EBUG
+       return( rc );
+    }
+
+    /* allocate a buffer for receiving request
+    */
+    if (( req_buf = atp_alloc_buf()) == NULL ) {
+       return -1;
+    }
+
+    memcpy( &faddr, atpb->atp_saddr, sizeof( struct sockaddr_at ));
+    func = ATP_TREQ;
+    if (( recvlen = atp_recv_atp( ah, &faddr, &func, ATP_TIDANY,
+         req_buf->atpbuf_info.atpbuf_data, 1 )) < 0 ) {
+       atp_free_buf( req_buf );
+       return -1;
+    }
+
+    memcpy( &req_hdr, req_buf->atpbuf_info.atpbuf_data + 1, 
+           sizeof( struct atphdr ));
+    tid = ntohs( req_hdr.atphd_tid );
+
+    ah->atph_rtid = tid;
+    if (( ah->atph_rxo = req_hdr.atphd_ctrlinfo & ATP_XO ) != 0 ) {
+       ah->atph_rreltime = ATP_RELTIME *
+               ( 1 << ( req_hdr.atphd_ctrlinfo & ATP_TRELMASK ));
+    }
+
+    memcpy( atpb->atp_saddr, &faddr, sizeof( struct sockaddr_at ));
+
+    if ( recvlen - ATP_HDRSIZE > atpb->atp_rreqdlen ) {
+       atp_free_buf( req_buf );
+       errno = EMSGSIZE;
+       return -1;
+    }
+
+    atpb->atp_rreqdlen = recvlen - ATP_HDRSIZE;
+    memcpy( atpb->atp_rreqdata, 
+           req_buf->atpbuf_info.atpbuf_data + ATP_HDRSIZE,
+           recvlen - ATP_HDRSIZE );
+    atpb->atp_bitmap = req_hdr.atphd_bitmap;
+    atp_free_buf( req_buf );
+    return( 0 );
+}
diff --git a/libatalk/atp/atp_rresp.c b/libatalk/atp/atp_rresp.c
new file mode 100644 (file)
index 0000000..dd9cb17
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <sys/uio.h>
+
+#include <netatalk/at.h>
+#include <atalk/atp.h>
+#include <atalk/util.h>
+
+#ifdef EBUG
+#include <stdio.h>
+#endif
+
+#include "atp_internals.h"
+
+int
+atp_rresp( ah, atpb )
+    ATP                        ah;             /* open atp handle */
+    struct atp_block   *atpb;          /* parameter block */
+{
+    int                len, i, rc;
+
+#ifdef EBUG
+    atp_print_bufuse( ah, "atp_rresp" );
+#endif
+    /* check parameters
+    */
+    if ( atpb->atp_rresiovcnt <= 0 || atpb->atp_rresiovcnt > 8 ) {
+       errno = EINVAL;
+       return( -1 );
+    }
+
+    while (( rc = atp_rsel( ah, atpb->atp_saddr, ATP_TRESP )) == 0 ) {
+       ;
+    }
+
+    if ( rc != ATP_TRESP ) {
+       return( rc );
+    }
+
+    for ( i = 0; i < 8; ++i ) {
+       if ( ah->atph_resppkt[ i ] == NULL ) {
+           break;
+       }
+       len = ah->atph_resppkt[ i ]->atpbuf_dlen - ATP_HDRSIZE;
+       if ( i > atpb->atp_rresiovcnt - 1 ||
+               len > atpb->atp_rresiov[ i ].iov_len ) {
+           errno = EMSGSIZE;
+           return( -1 );
+       }
+#ifdef EBUG
+       fprintf( stderr, "atp_rresp copying %d bytes packet %d\n",
+               len, i );
+       bprint( (char *)ah->atph_resppkt[ i ]->atpbuf_info.atpbuf_data,
+               len + ATP_HDRSIZE );
+#endif
+       memcpy(atpb->atp_rresiov[ i ].iov_base,
+              ah->atph_resppkt[ i ]->atpbuf_info.atpbuf_data + ATP_HDRSIZE,
+              len );
+       atpb->atp_rresiov[ i ].iov_len = len;
+       atp_free_buf( ah->atph_resppkt[ i ] );
+       ah->atph_resppkt[ i ] = NULL;
+    }
+    atpb->atp_rresiovcnt = i;
+
+    return( 0 );
+}
diff --git a/libatalk/atp/atp_rsel.c b/libatalk/atp/atp_rsel.c
new file mode 100644 (file)
index 0000000..a183f49
--- /dev/null
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 1990,1997 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <signal.h>
+#include <errno.h>
+
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+
+#include <atalk/netddp.h>
+#include <atalk/compat.h>
+#include <atalk/atp.h>
+#include <atalk/util.h>
+
+#include "atp_internals.h"
+
+#ifdef DROP_ATPTREL
+static int     release_count = 0;
+#endif DROP_ATPTREL
+
+
+static int
+resend_request( ah )
+    ATP                ah;
+{
+    /*
+     * update bitmap and send request packet
+     */
+    struct atphdr      req_hdr;
+
+#ifdef EBUG
+    printf( "\n<%d> resend_request: resending %d byte request packet",
+           getpid(), ah->atph_reqpkt->atpbuf_dlen );
+    atp_print_addr( " to", &ah->atph_reqpkt->atpbuf_addr );
+    putchar( '\n' );
+    bprint( ah->atph_reqpkt->atpbuf_info.atpbuf_data,
+           ah->atph_reqpkt->atpbuf_dlen );
+#endif
+
+    memcpy( &req_hdr, ah->atph_reqpkt->atpbuf_info.atpbuf_data + 1, 
+       sizeof( struct atphdr ));
+    req_hdr.atphd_bitmap = ah->atph_rbitmap;
+    memcpy( ah->atph_reqpkt->atpbuf_info.atpbuf_data + 1, &req_hdr, 
+           sizeof( struct atphdr ));
+
+    gettimeofday( &ah->atph_reqtv, (struct timezone *)0 );
+    if ( netddp_sendto( ah->atph_socket,
+           ah->atph_reqpkt->atpbuf_info.atpbuf_data,
+           ah->atph_reqpkt->atpbuf_dlen, 0,
+           (struct sockaddr *) &ah->atph_reqpkt->atpbuf_addr,
+           sizeof( struct sockaddr_at )) != ah->atph_reqpkt->atpbuf_dlen ) {
+       return( -1 );
+    }
+
+    if ( ah->atph_reqtries > 0 ) {
+       --(ah->atph_reqtries);
+    }
+
+    return( 0 );
+}
+
+int
+atp_rsel( ah, faddr, func )
+    ATP                        ah;             /* open atp handle */
+    struct sockaddr_at *faddr;         /* address to receive from */
+    int                        func;           /* which function(s) to wait for;
+                                          0 means request or response */
+{
+    struct atpbuf      *abuf, *pb, *cb;
+    struct atphdr      req_hdr, resp_hdr;
+    fd_set             fds;
+    int                        i, recvlen, requesting, mask, c;
+    u_int8_t           rfunc;
+    u_int16_t          tid;
+    struct timeval     tv;
+    struct sockaddr_at saddr;
+
+#ifdef EBUG
+    atp_print_bufuse( ah, "atp_rsel at top" );
+#endif
+    if ( func == 0 ) {
+       func = ATP_FUNCANY;
+    }
+
+    requesting = ( func & ATP_TRESP ) && ah->atph_rrespcount > 0 &&
+       ( ah->atph_reqtries > 0 || ah->atph_reqtries == ATP_TRIES_INFINITE );
+
+    if ( requesting && ah->atph_rbitmap == 0 ) {
+       /*
+        * we already have a complete atp response; just return
+        */
+       return( ATP_TRESP );
+    }
+
+    if (( abuf = atp_alloc_buf()) == NULL ) {
+       return( -1 );
+    }
+
+    if ( requesting ) {
+#ifdef EBUG
+       printf( "<%d> atp_rsel: request pending\n", getpid());
+#endif
+       gettimeofday( &tv, (struct timezone *)0 );
+       if ( tv.tv_sec - ah->atph_reqtv.tv_sec > ah->atph_reqto ) {
+           if ( resend_request( ah ) < 0 ) {
+               atp_free_buf( abuf );
+               return( -1 );
+           }
+       }
+    }
+
+    for ( ;; ) {
+       rfunc = func;
+       if ( requesting ) {
+           FD_ZERO( &fds );
+           FD_SET( ah->atph_socket, &fds );
+           tv.tv_sec = ah->atph_reqto;
+           tv.tv_usec = 0;
+           if (( c = select( ah->atph_socket + 1, &fds, NULL, NULL,
+                   &tv )) < 0 ) {
+               atp_free_buf( abuf );
+               return( -1 );
+           }
+           if ( c == 0 || FD_ISSET( ah->atph_socket, &fds ) == 0 ) {
+               recvlen = -1;
+               errno = EINTR;
+               goto timeout;
+           }
+       }
+       memcpy( &saddr, faddr, sizeof( struct sockaddr_at ));
+#ifdef EBUG
+       printf( "<%d> atp_rsel calling recv_atp,", getpid());
+       atp_print_addr( " accepting from: ", &saddr );
+       putchar( '\n' );
+#endif EBUG
+       if (( recvlen = atp_recv_atp( ah, &saddr, &rfunc, ATP_TIDANY,
+               abuf->atpbuf_info.atpbuf_data, 0 )) >= 0 ) {
+           break;      /* we received something */
+       }
+
+timeout :
+       if ( !requesting || errno != EINTR ) {
+           break;      /* error */
+       }
+           
+       if ( ah->atph_reqtries <= 0 &&
+               ah->atph_reqtries != ATP_TRIES_INFINITE ) {
+           errno = ETIMEDOUT;
+           break;
+       }
+       
+       if ( resend_request( ah ) < 0 ) {
+           break;      /* error */
+       }
+    }
+
+    if ( recvlen <= 0 ) {      /* error */
+       atp_free_buf( abuf );
+       return( recvlen );
+    }
+
+#ifdef EBUG
+    printf( "<%d> atp_rsel: rcvd %d bytes", getpid(), recvlen );
+    atp_print_addr( " from: ", &saddr );
+    putchar( '\n' );
+    bprint( abuf->atpbuf_info.atpbuf_data, recvlen );
+#endif
+
+    abuf->atpbuf_dlen = recvlen;
+    memcpy( &resp_hdr, abuf->atpbuf_info.atpbuf_data + 1,
+           sizeof( struct atphdr ));
+
+    if ( rfunc == ATP_TREQ ) {
+       /*
+        * we got a request: check to see if it is a duplicate (XO)
+        * while we are at it, we expire old XO responses from sent list
+        */
+       memcpy( &req_hdr, abuf->atpbuf_info.atpbuf_data + 1, 
+               sizeof( struct atphdr ));
+       tid = ntohs( req_hdr.atphd_tid );
+       gettimeofday( &tv, (struct timezone *)0 );
+       for ( pb = NULL, cb = ah->atph_sent; cb != NULL;
+               pb = cb, cb = cb->atpbuf_next ) {
+#ifdef EBUG
+           printf( "<%d>", getpid());
+           atp_print_addr( " examining", &cb->atpbuf_addr );
+           printf( " %hu", cb->atpbuf_info.atpbuf_xo.atpxo_tid );
+           atp_print_addr( " (looking for", &saddr );
+           printf( " %hu)\n", tid );
+#endif
+           if ( tv.tv_sec - cb->atpbuf_info.atpbuf_xo.atpxo_tv.tv_sec
+                   > cb->atpbuf_info.atpbuf_xo.atpxo_reltime ) {
+               /* discard expired response */
+#ifdef EBUG
+               printf( "<%d> expiring tid %hu\n", getpid(),
+                       cb->atpbuf_info.atpbuf_xo.atpxo_tid );
+#endif
+               if ( pb == NULL ) {
+                   ah->atph_sent = cb->atpbuf_next;
+               } else {
+                   pb->atpbuf_next = cb->atpbuf_next;
+               }
+
+               for ( i = 0; i < 8; ++i ) {
+                   if ( cb->atpbuf_info.atpbuf_xo.atpxo_packet[ i ]
+                           != NULL ) {
+                       atp_free_buf( cb->atpbuf_info.atpbuf_xo.atpxo_packet[ i ] );
+                   }
+               }
+               atp_free_buf( cb );
+
+               if (( cb = pb ) == NULL )
+                   break;
+
+           } else if ( at_addr_eq( &saddr, &cb->atpbuf_addr )) {
+               if ( cb->atpbuf_info.atpbuf_xo.atpxo_tid == tid ) {
+                   break;
+               }
+           }
+       }
+
+       if ( cb != NULL ) {
+#ifdef EBUG
+           printf( "<%d> duplicate request -- re-sending XO resp\n",
+                   getpid());
+#endif
+           /* matches an old response -- just re-send and reset expire */
+           cb->atpbuf_info.atpbuf_xo.atpxo_tv = tv;
+           for ( i = 0; i < 8; ++i ) {
+               if ( cb->atpbuf_info.atpbuf_xo.atpxo_packet[i] != NULL &&
+                       req_hdr.atphd_bitmap & ( 1 << i )) {
+                   netddp_sendto( ah->atph_socket,
+                           cb->atpbuf_info.atpbuf_xo.atpxo_packet[i]->atpbuf_info.atpbuf_data,
+                           cb->atpbuf_info.atpbuf_xo.atpxo_packet[i]->atpbuf_dlen,
+                           0, (struct sockaddr *) &saddr, sizeof( struct sockaddr_at));
+               }
+           }
+       }
+
+       if ( cb == NULL ) {
+           /* new request -- queue it and return */
+           memcpy( &abuf->atpbuf_addr, &saddr, sizeof( struct sockaddr_at ));
+           memcpy( faddr, &saddr, sizeof( struct sockaddr_at ));
+           abuf->atpbuf_next = ah->atph_queue;
+           ah->atph_queue = abuf;
+           return( ATP_TREQ );
+       } else {
+           atp_free_buf( abuf );
+           return( 0 );
+       }
+    }
+
+    /*
+     * we got a response: update bitmap
+     */
+    memcpy( &req_hdr, ah->atph_reqpkt->atpbuf_info.atpbuf_data + 1, 
+           sizeof( struct atphdr ));
+    if ( requesting && ah->atph_rbitmap & ( 1<<resp_hdr.atphd_bitmap )
+               && req_hdr.atphd_tid == resp_hdr.atphd_tid ) {
+       ah->atph_rbitmap &= ~( 1<<resp_hdr.atphd_bitmap );
+
+       if ( ah->atph_resppkt[ resp_hdr.atphd_bitmap ] != NULL ) {
+           atp_free_buf( ah->atph_resppkt[ resp_hdr.atphd_bitmap ] );
+       }
+       ah->atph_resppkt[ resp_hdr.atphd_bitmap ] = abuf;
+
+       /* if End Of Message, clear all higher bitmap bits
+       */
+       if ( resp_hdr.atphd_ctrlinfo & ATP_EOM ) {
+#ifdef EBUG
+           printf( "<%d> EOM -- seq num %d  current bitmap %d\n",
+               getpid(), resp_hdr.atphd_bitmap, ah->atph_rbitmap );
+#endif
+           mask = 1 << resp_hdr.atphd_bitmap;
+           ah->atph_rbitmap &= ( mask | (mask-1) );
+       }
+
+       /* if Send Trans. Status, send updated request
+       */
+       if ( resp_hdr.atphd_ctrlinfo & ATP_STS ) {
+#ifdef EBUG
+           puts( "STS" );
+#endif
+           req_hdr.atphd_bitmap = ah->atph_rbitmap;
+           memcpy(ah->atph_reqpkt->atpbuf_info.atpbuf_data + 1,
+                  &req_hdr, sizeof( struct atphdr ));
+           if ( netddp_sendto( ah->atph_socket,
+                   ah->atph_reqpkt->atpbuf_info.atpbuf_data,
+                   ah->atph_reqpkt->atpbuf_dlen, 0,
+                   (struct sockaddr *) &ah->atph_reqpkt->atpbuf_addr,
+                   sizeof( struct sockaddr_at )) !=
+                   ah->atph_reqpkt->atpbuf_dlen ) {
+               atp_free_buf( abuf );
+               return( -1 );
+           }
+       }
+    } else {
+       /*
+        * we are not expecting this response -- toss it
+        */
+       atp_free_buf( abuf );
+#ifdef EBUG
+       printf( "atp_rsel: ignoring resp bm=%x tid=%d (expected %x/%d)\n",
+               resp_hdr.atphd_bitmap, ntohs( resp_hdr.atphd_tid ),
+               ah->atph_rbitmap, ah->atph_tid );
+#endif EBUG
+    }
+
+    if ( !ah->atph_rbitmap && ( req_hdr.atphd_ctrlinfo & ATP_XO )) {
+       /*
+        * successful completion - send release
+        * the release consists of DDP type byte + ATP header + 4 user bytes
+        */
+       req_hdr.atphd_ctrlinfo = ATP_TREL;
+       memcpy( ah->atph_reqpkt->atpbuf_info.atpbuf_data + 1, &req_hdr, 
+           sizeof( struct atphdr ));
+       memset( ah->atph_reqpkt->atpbuf_info.atpbuf_data + ATP_HDRSIZE, 0, 4 );
+       ah->atph_reqpkt->atpbuf_dlen = sizeof( struct atphdr ) + ATP_HDRSIZE;
+#ifdef EBUG
+       printf( "<%d> sending TREL", getpid() );
+       bprint( ah->atph_reqpkt->atpbuf_info.atpbuf_data,
+               ah->atph_reqpkt->atpbuf_dlen );
+#endif
+#ifdef DROP_ATPTREL
+       if (( ++release_count % 10 ) != 0 ) {
+#endif DROP_ATPTREL
+       netddp_sendto( ah->atph_socket, 
+                      ah->atph_reqpkt->atpbuf_info.atpbuf_data,
+                      ah->atph_reqpkt->atpbuf_dlen, 0,
+                      (struct sockaddr *) &ah->atph_reqpkt->atpbuf_addr,
+                      sizeof( struct sockaddr_at));
+#ifdef DROP_ATPTREL
+       }
+#endif DROP_ATPTREL
+    }
+
+    if ( ah->atph_rbitmap != 0 ) {
+       if ( ah->atph_reqtries > 0
+               || ah->atph_reqtries == ATP_TRIES_INFINITE ) {
+           return( 0 );
+       } else {
+           errno = ETIMEDOUT;
+           return( -1 );
+       }
+    }
+
+    memcpy( faddr, &saddr, sizeof( struct sockaddr_at ));
+    return( ATP_TRESP );
+}
diff --git a/libatalk/atp/atp_sreq.c b/libatalk/atp/atp_sreq.c
new file mode 100644 (file)
index 0000000..0ec1b19
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <sys/uio.h>
+#include <signal.h>
+
+#include <netinet/in.h>
+#include <netatalk/at.h>
+#include <netatalk/endian.h>
+
+#include <atalk/netddp.h>
+#include <atalk/atp.h>
+#include <atalk/util.h>
+
+#include "atp_internals.h"
+
+int
+atp_sreq( ah, atpb, respcount, flags )
+    ATP                        ah;             /* open atp handle */
+    struct atp_block   *atpb;          /* parameter block */
+    int                        respcount;      /* buffers available for response */
+    u_int8_t           flags;          /* ATP_XO, ATP_TREL?? */
+{
+    struct atpbuf      *req_buf;
+    int                        i;
+
+#ifdef EBUG
+    atp_print_bufuse( ah, "atp_sreq" );
+#endif
+
+    /* check parameters
+    */
+    if ( atpb->atp_sreqdlen < 4 || atpb->atp_sreqdlen > ATP_MAXDATA
+           || ( respcount < 0 ) || ( respcount > 8 )
+           || ( atpb->atp_sreqto < 0 ) || (( atpb->atp_sreqtries < 1 )
+           && ( atpb->atp_sreqtries != ATP_TRIES_INFINITE ))) {
+       errno = EINVAL;
+       return -1;
+    }
+    /* clean up any packet fragments left from last request
+    */
+    for ( i = 0; i < 8; ++i ) {
+       if ( ah->atph_resppkt[ i ] != NULL ) {
+           atp_free_buf( ah->atph_resppkt[ i ] );
+           ah->atph_resppkt[ i ] = NULL;
+       }
+    }
+
+    /* generate bitmap, tid and ctrlinfo
+    */
+    atpb->atp_bitmap = ( 1 << respcount ) - 1;
+
+    /* allocate a new buffer and build request packet
+    */
+    if (( req_buf = atp_alloc_buf()) == NULL ) {
+       return( -1 );
+    }
+    atp_build_req_packet( req_buf, ah->atph_tid++, flags | ATP_TREQ, atpb );
+    memcpy( &req_buf->atpbuf_addr, atpb->atp_saddr,
+           sizeof( struct sockaddr_at ));
+
+    /* send the initial request
+    */
+#ifdef EBUG
+    printf( "\n<%d> atp_sreq: sending a %d byte packet ", getpid(),
+           req_buf->atpbuf_dlen );
+    atp_print_addr( " to", atpb->atp_saddr );
+    putchar( '\n' );
+    bprint( req_buf->atpbuf_info.atpbuf_data, req_buf->atpbuf_dlen );
+#endif
+
+    gettimeofday( &ah->atph_reqtv, (struct timezone *)0 );
+#ifdef DROPPACKETS
+if (( random() % 3 ) != 2 ) {
+#endif
+    if ( netddp_sendto( ah->atph_socket, req_buf->atpbuf_info.atpbuf_data,
+           req_buf->atpbuf_dlen, 0, (struct sockaddr *) atpb->atp_saddr,
+           sizeof( struct sockaddr_at )) != req_buf->atpbuf_dlen ) {
+       atp_free_buf( req_buf );
+       return( -1 );
+    }
+#ifdef DROPPACKETS
+} else printf( "<%d> atp_sreq: dropped request\n", getpid() );
+#endif
+
+    if ( atpb->atp_sreqto != 0 ) {
+       if ( ah->atph_reqpkt != NULL ) {
+           atp_free_buf( ah->atph_reqpkt );
+       }
+       ah->atph_reqto = atpb->atp_sreqto;
+       if ( atpb->atp_sreqtries == ATP_TRIES_INFINITE ) {
+           ah->atph_reqtries = ATP_TRIES_INFINITE;
+       } else {
+           /* we already sent one */
+           ah->atph_reqtries = atpb->atp_sreqtries - 1;
+       }
+       ah->atph_reqpkt = req_buf;
+       ah->atph_rbitmap = ( 1 << respcount ) - 1;
+       ah->atph_rrespcount = respcount;
+    } else {
+       atp_free_buf( req_buf );
+       ah->atph_rrespcount = 0;
+    }
+
+    return( 0 );
+}
diff --git a/libatalk/atp/atp_sresp.c b/libatalk/atp/atp_sresp.c
new file mode 100644 (file)
index 0000000..3d73be7
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+
+#include <netatalk/at.h>
+#include <netatalk/endian.h>
+
+#include <atalk/netddp.h>
+#include <atalk/atp.h>
+#include <atalk/util.h>
+
+#include "atp_internals.h"
+
+/* send a transaction response
+*/
+int atp_sresp( ah, atpb )
+    ATP                        ah;             /* open atp handle */
+    struct atp_block   *atpb;          /* parameter block */
+{
+    int                        i;
+    u_int8_t           ctrlinfo;
+    struct atpbuf      *resp_buf;
+    struct atpbuf      *save_buf;
+
+#ifdef EBUG
+    atp_print_bufuse( ah, "atp_sresp" );
+#endif
+
+    /* check parameters
+    */
+    for ( i = atpb->atp_sresiovcnt - 1; i >= 0; --i ) {
+       if ( atpb->atp_sresiov[ i ].iov_len > ATP_MAXDATA ) 
+           break;
+    }
+    if ( i >= 0 || atpb->atp_sresiovcnt < 1 || atpb->atp_sresiovcnt > 8 ) {
+       errno = EINVAL;
+       return -1;
+    }
+
+    /* allocate a new buffer for reponse packet construction
+    */
+    if (( resp_buf = atp_alloc_buf()) == NULL ) {
+       return -1;
+    }
+
+    /* send all the response packets
+     * also need to attach list to ah for dup. detection (if XO)
+    */
+#ifdef EBUG
+    printf( "<%d> preparing to send %s response tid=%hu ", getpid(),
+           ah->atph_rxo ? "XO" : "", ah->atph_rtid );
+    atp_print_addr( " to", atpb->atp_saddr );
+    putchar( '\n' );
+#endif
+    if ( ah->atph_rxo ) {
+       if (( save_buf = atp_alloc_buf()) == NULL ) {
+           return -1;
+       }
+       for ( i = 0; i < 8;
+               save_buf->atpbuf_info.atpbuf_xo.atpxo_packet[ i++ ] = NULL );
+    }
+    for ( i = 0; i < atpb->atp_sresiovcnt; ++i ) {
+       ctrlinfo = ATP_TRESP;
+#ifdef STS_RESPONSES
+       ctrlinfo |= ATP_STS;
+#endif STS_RESPONSES
+       if ( i == atpb->atp_sresiovcnt-1 ) {
+           ctrlinfo |= ATP_EOM;
+       }
+       atp_build_resp_packet( resp_buf, ah->atph_rtid, ctrlinfo, atpb, i );
+
+       if ( ah->atph_rxo ) {
+           save_buf->atpbuf_info.atpbuf_xo.atpxo_packet[i] = resp_buf;
+       }
+#ifdef DROPPACKETS
+if (( random() % 3 ) != 2 ) {
+#endif
+#ifdef EBUG
+printf( "<%d> sending packet tid=%hu serial no.=%d\n", getpid(),
+  ah->atph_rtid, i );
+bprint( resp_buf->atpbuf_info.atpbuf_data, resp_buf->atpbuf_dlen );
+#endif
+       if ( netddp_sendto( ah->atph_socket, resp_buf->atpbuf_info.atpbuf_data,
+         resp_buf->atpbuf_dlen, 0, (struct sockaddr *) atpb->atp_saddr,
+         sizeof( struct sockaddr_at )) != resp_buf->atpbuf_dlen ) {
+           if ( ah->atph_rxo ) {
+               for ( ; i >= 0; --i ) {
+                   atp_free_buf( save_buf->atpbuf_info.atpbuf_xo.atpxo_packet[ i ] );
+               }
+               atp_free_buf( save_buf );
+           }
+           return -1;
+       }
+#ifdef DROPPACKETS
+} else printf( "<%d> atp_sresp: dropped serial no. %d\n", getpid(),  i );
+#endif
+       /* allocate a buffer for next packet (if XO mode)
+       */
+       if ( ah->atph_rxo && ( resp_buf = atp_alloc_buf()) == NULL ) {
+           return -1;
+       }
+    }
+    atp_free_buf( resp_buf );
+    if ( ah->atph_rxo ) {
+       /* record timestamp, tid, release time, and destination address
+       */
+       gettimeofday( &save_buf->atpbuf_info.atpbuf_xo.atpxo_tv,
+               (struct timezone *) 0 );
+       save_buf->atpbuf_info.atpbuf_xo.atpxo_tid = ah->atph_rtid;
+       save_buf->atpbuf_info.atpbuf_xo.atpxo_reltime = ah->atph_rreltime;
+       memcpy( &save_buf->atpbuf_addr, atpb->atp_saddr, 
+               sizeof( struct sockaddr_at ));
+
+       /* add to list of packets we have sent
+       */
+       save_buf->atpbuf_next = ah->atph_sent;
+       ah->atph_sent = save_buf;
+#ifdef EBUG
+printf( "<%d> saved XO response\n", getpid());
+#endif
+    }
+    return 0;
+}
diff --git a/libatalk/cnid/Makefile b/libatalk/cnid/Makefile
new file mode 100644 (file)
index 0000000..fdeafe8
--- /dev/null
@@ -0,0 +1,58 @@
+SRC = cnid_open.c cnid_close.c cnid_add.c cnid_get.c cnid_delete.c \
+      cnid_update.c cnid_resolve.c cnid_lookup.c cnid_nextid.c
+OBJ = cnid_open.o cnid_close.o cnid_add.o cnid_get.o cnid_delete.o \
+      cnid_update.o cnid_resolve.o cnid_lookup.o cnid_nextid.o
+
+INCPATH= -I../../include -I${DB2DIR}/include
+CFLAGS= ${DEFS} ${OPTOPTS} ${INCPATH} 
+
+TAGSFILE=      tags
+CC=    cc
+
+all : profiled cnidlib 
+
+profiled:
+       -mkdir profiled
+
+cnidlib cnidlib_p : ${OBJ}
+       @echo "building profiled cnidlib"
+       @cd profiled; ar cru ../cnidlib_p ${OBJ}
+       @echo "building normal cnidlib"
+       @ar cru cnidlib ${OBJ}
+
+.c.o :
+       ${CC} -p ${CFLAGS} -c $*.c
+       -ld -r -o $*.o- $*.o
+       mv $*.o- profiled/$*.o
+       ${CC} ${CFLAGS} -c $*.c
+       -ld -r -o $*.o- $*.o
+       mv $*.o- $*.o
+
+clean :
+       rm -f *.o profiled/*.o *.bak *[Ee]rrs tags
+       rm -f cnidlib cnidlib_p
+
+tags : ${SRC}
+       cwd=`pwd`; \
+       for i in ${SRC}; do \
+           ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+       done
+
+depend :
+       for i in ${SRC} ; do \
+           ${CC} -M ${DEFS} ${INCPATH} $$i | \
+           awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+               else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+               else rec = rec " " $$2 } } \
+               END { print rec } ' >> makedep; done
+       sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+       cat makedep >> Makefile.tmp
+       rm makedep
+       echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+       echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+       echo '# see make depend above' >> Makefile.tmp
+       rm -f Makefile.bak
+       cp Makefile Makefile.bak
+       mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
diff --git a/libatalk/cnid/README b/libatalk/cnid/README
new file mode 100644 (file)
index 0000000..51e85ac
--- /dev/null
@@ -0,0 +1,37 @@
+the catalog database keeps track of three mappings:
+    CNID     -> dev/ino and did/name
+    dev/ino  -> CNID
+    did/name -> CNID
+
+dev/ino is used to keep track of magically moved files. did/name is
+for quick lookups of CNIDs. 
+
+NOTE: the database will append a nul byte to the end of name. in
+addition, name should be given as it appears on disk. this allows the
+creation of cnid updating/cleaning programs that don't have to deal
+with knowing what the particular codepage is.
+
+here's the ritual:
+       1) open a volume. call cnid_open.
+       2) every time you need a CNID, call cnid_add(). it will
+          automatically look for an existing cnid and add a new one
+          if one isn't already there. you can pass a hint if you
+          want. the only use this has right now is to enable
+          consistency between AFP and HFS. in the future, it would
+          allow people to write conversion utilities that
+          pre-instantiate a database without needing to re-assign
+          CNIDs.
+       3) if you want to just look for a CNID without automatically
+          adding one in, you have two choices:
+            a) cnid_resolve takes a CNID, returns name, and
+               over-writes the CNID given with the parent DID. this
+               is good for FPResolveID.
+             b) cnid_lookup returns a CNID corresponding to the
+               dev/ino,did/name keys. it will auto-update the catalog
+               database if there's a discrepancy. 
+               NOTE: cnid_add calls this before adding a new CNID. 
+       4) when you delete a file or directory, you need to call
+          cnid_delete with the CNID for that file/directory.
+       5) if you're just moving a CNID, call cnid_move with the CNID
+          and the new dev/ino,did/name pairs.
+       6) call cnid_close when closing the volume.
diff --git a/libatalk/cnid/cnid_add.c b/libatalk/cnid/cnid_add.c
new file mode 100644 (file)
index 0000000..81ac2a7
--- /dev/null
@@ -0,0 +1,185 @@
+/* 
+ * Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu)
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ * cnid_add (db, dev, ino, did, name, hint): 
+ * add a name to the CNID database. we use both dev/ino and did/name
+ * to keep track of things. */
+#include <stdio.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <syslog.h>
+
+#include <db.h>
+#include <netatalk/endian.h>
+
+#include <atalk/adouble.h>
+#include <atalk/cnid.h>
+#include <atalk/util.h>
+
+#include "cnid_private.h"
+
+/* add an entry to the CNID databases. we do this as a transaction
+ * to prevent messiness. */
+static int add_cnid(CNID_private *db, DBT *key, DBT *data)
+{
+  DBT altkey, altdata;
+  DB_TXN *tid;
+  DB_TXNMGR *txnp;
+
+  txnp = db->dbenv.tx_info;
+  memset(&altkey, 0, sizeof(altkey));
+  memset(&altdata, 0, sizeof(altdata));
+  
+retry:
+  if (errno = txn_begin(txnp, NULL, &tid)) {
+    return errno;
+  }
+
+  /* main database */
+  if (errno = db->db_cnid->put(db->db_cnid, tid, 
+                              key, data, DB_NOOVERWRITE)) {
+    txn_abort(tid);
+    if (errno == EAGAIN)
+      goto retry;
+
+    return errno;
+  }
+
+  /* dev/ino database */
+  altkey.data = data->data;
+  altkey.size = CNID_DEVINO_LEN;
+  altdata.data = key->data;
+  altdata.size = key->size;
+  if ((errno = db->db_devino->put(db->db_devino, tid,
+                                 &altkey, &altdata, 0))) {
+    txn_abort(tid);
+    if (errno == EAGAIN)
+      goto retry;
+
+    return errno;
+  }
+
+  /* did/name database */
+  altkey.data = data->data + CNID_DEVINO_LEN;
+  altkey.size = data->size - CNID_DEVINO_LEN;
+  if (errno = db->db_didname->put(db->db_didname, tid,
+                                   &altkey, &altdata, 0)) {
+    txn_abort(tid);
+    if (errno == EAGAIN)
+      goto retry;
+
+    return errno;
+  }
+
+  return txn_commit(tid);
+}
+                   
+/* 0 is not a valid cnid. this will do a cnid_lookup beforehand and
+   return that cnid if it exists.  */
+cnid_t cnid_add(void *CNID, const struct stat *st, 
+               const cnid_t did, const char *name, const int len,
+               cnid_t hint)
+{
+  CNID_private *db;
+  DBT key, data;
+  struct flock lock;
+  cnid_t id, save;
+  
+  
+  if (!(db = CNID) || !st || !name)
+    return 0;
+  
+  /* just do a lookup if RootInfo is read-only. */
+  if (db->flags & (CNIDFLAG_ROOTINFO_RO | CNIDFLAG_DB_RO))
+    return cnid_lookup(db, st, did, name, len);
+
+  /* initialize everything */
+  memset(&key, 0, sizeof(key));
+  memset(&data, 0, sizeof(data));
+
+  /* acquire a lock on RootInfo. as the cnid database is the only user 
+   * of RootInfo, we just use our own locks. 
+   *
+   * NOTE: we lock it here to serialize access to the database. */
+  lock.l_type = F_WRLCK;
+  lock.l_whence = SEEK_SET;
+  lock.l_start = ad_getentryoff(&db->rootinfo, ADEID_DID);
+  lock.l_len = ad_getentrylen(&db->rootinfo, ADEID_DID);
+  if (fcntl(ad_hfileno(&db->rootinfo), F_SETLKW,  &lock) < 0) {
+    syslog(LOG_ERR, "cnid_add: can't establish lock: %m");
+    goto cleanup_err;
+  }
+
+  /* if it's already stored, just return it */
+  if ((id = cnid_lookup(db, st, did, name, len))) {
+    hint = id;
+    goto cleanup_unlock;
+  }
+  
+  /* just set hint, and the key will change. */
+  key.data = &hint;
+  key.size = sizeof(hint);
+  
+  if ((data.data = 
+       make_cnid_data(st, did, name, len)) == NULL) {
+    syslog(LOG_ERR, "cnid_add: path name too long.");
+    goto cleanup_unlock;
+  }
+  data.size = CNID_HEADER_LEN + len + 1;
+  
+  /* start off with the hint. it should be in network byte order. 
+   * we need to make sure that somebody doesn't add in restricted
+   * cnid's to the database. */
+  if (ntohl(hint) >= CNID_START) {
+    /* if the key doesn't exist, add it in. don't fiddle with nextID. */
+    errno = add_cnid(db, &key, &data);
+    switch (errno) {
+    case DB_KEYEXIST: /* need to use RootInfo after all. */
+      break;
+    default:
+      syslog(LOG_ERR, "cnid_add: unable to add CNID(%x)", hint); 
+      hint = 0;
+      /* fall through */
+    case 0:
+      goto cleanup_unlock;
+    }
+  }    
+    
+  /* no need to refresh the header file */
+  memcpy(&hint, ad_entry(&db->rootinfo, ADEID_DID), sizeof(hint));
+
+  /* search for a new id. we keep the first id around to check for
+   * wrap-around. NOTE: i do it this way so that we can go back and
+   * fill in holes. */
+  save = id = ntohl(hint);
+  while (errno = add_cnid(db, &key, &data)) {
+    /* don't use any of the special CNIDs */
+    if (++id < CNID_START)
+      id = CNID_START;
+
+    if ((errno != DB_KEYEXIST) || (save == id)) {
+      syslog(LOG_ERR, "cnid_add: unable to add CNID(%x)", hint);
+      hint = 0;
+      goto cleanup_unlock;
+    }
+    hint = htonl(id);
+  }
+
+  /* update RootInfo with the next id. */
+  id = htonl(++id);
+  memcpy(ad_entry(&db->rootinfo, ADEID_DID), &id, sizeof(id));
+  ad_flush(&db->rootinfo, ADFLAGS_HF);
+
+cleanup_unlock:
+  lock.l_type = F_UNLCK;
+  fcntl(ad_hfileno(&db->rootinfo), F_SETLK, &lock);
+  return hint;
+
+cleanup_err:
+  return 0;
+}
diff --git a/libatalk/cnid/cnid_close.c b/libatalk/cnid/cnid_close.c
new file mode 100644 (file)
index 0000000..f5e1d09
--- /dev/null
@@ -0,0 +1,57 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <db.h>
+#include <errno.h>
+
+#include <atalk/cnid.h>
+
+#include "cnid_private.h"
+
+void cnid_close(void *CNID)
+{
+  CNID_private *db;
+
+  if (!(db = CNID))
+    return;
+
+  /* flush the transaction log and delete the log file if we can. */
+  if ((db->lockfd > -1) && ((db->flags & CNIDFLAG_DB_RO) == 0)) {
+    struct flock lock;
+
+    lock.l_type = F_WRLCK;
+    lock.l_whence = SEEK_SET;
+    lock.l_start = lock.l_len = 0;
+    if (fcntl(db->lockfd, F_SETLK, &lock) == 0) {
+      DB_TXNMGR *txnp;
+      char **list, **first;
+      
+      txnp = db->dbenv.tx_info;
+      errno = txn_checkpoint(txnp, 0, 0);
+      while (errno == DB_INCOMPLETE)
+       errno = txn_checkpoint(txnp, 0, 0);
+      
+      /* we've checkpointed, so clean up the log files. 
+       * NOTE: any real problems will make log_archive return an error. */
+      chdir(db->dbenv.db_log_dir ? db->dbenv.db_log_dir : db->dbenv.db_home);
+      if (!log_archive(db->dbenv.lg_info, &first, DB_ARCH_LOG, NULL)) {
+       list = first;
+       while (*list) {
+         if (truncate(*list, 0) < 0)
+           syslog(LOG_INFO, "cnid_close: failed to truncate %s: %m", *list);
+         list++;
+       }
+       free(first); 
+      }
+    }
+  }
+
+  db->db_didname->close(db->db_didname, 0);
+  db->db_devino->close(db->db_devino, 0);
+  db->db_cnid->close(db->db_cnid, 0);
+  db_appexit(&db->dbenv);
+  if (db->lockfd > -1)
+    close(db->lockfd); /* this will also close any lock we have. */
+  ad_close(&db->rootinfo, ADFLAGS_HF);
+  free(db);
+}
diff --git a/libatalk/cnid/cnid_delete.c b/libatalk/cnid/cnid_delete.c
new file mode 100644 (file)
index 0000000..7354576
--- /dev/null
@@ -0,0 +1,109 @@
+/* 
+ * Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu)
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ * cnid_delete: delete a CNID from the database 
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <syslog.h>
+
+#include <db.h>
+#include <netatalk/endian.h>
+#include <atalk/adouble.h>
+#include <atalk/cnid.h>
+
+#include "cnid_private.h"
+
+int cnid_delete(void *CNID, const cnid_t id)
+{
+  CNID_private *db;
+  DBT key, data;
+  DB_TXN *tid;
+  DB_TXNMGR *txnp;
+
+  if (!(db = CNID) || !id || (db->flags & CNIDFLAG_DB_RO))
+    return -1;
+
+  txnp = db->dbenv.tx_info;
+  memset(&key, 0, sizeof(key));
+  memset(&data, 0, sizeof(data));
+  
+
+retry:
+  if (errno = txn_begin(txnp, NULL, &tid)) {
+    return errno;
+  }
+
+  /* get from main database */
+  key.data = &id;
+  key.size = sizeof(id);
+  if (errno = db->db_cnid->get(db->db_cnid, tid, &key, &data, 0)) {
+    txn_abort(tid);
+    switch (errno) {
+    case EAGAIN:
+      goto retry;
+      
+    case DB_NOTFOUND:
+      syslog(LOG_INFO, "cnid_delete: CNID(%x) not in database", id);
+      return 0;
+    default:
+      syslog(LOG_ERR, "cnid_delete: can't delete entry");
+      return errno;
+    }
+  }
+  
+  /* now delete from dev/ino database */
+  key.data = data.data;
+  key.size = CNID_DEVINO_LEN;
+  if (errno = db->db_devino->del(db->db_devino, tid, &key, 0)) {
+    if (errno == EAGAIN) {
+      txn_abort(tid);
+      goto retry;
+    }
+
+    /* be silent if there isn't an entry */
+    if (errno != DB_NOTFOUND) {
+      txn_abort(tid);
+      goto abort_err;
+    }
+  }
+
+  /* get data from the did/name database */
+  /* free from did/macname, did/shortname, and did/longname databases */
+
+  /* delete from did/name database */
+  key.data = data.data + CNID_DEVINO_LEN;
+  key.size = data.size - CNID_DEVINO_LEN;
+  if (errno = db->db_didname->del(db->db_didname, tid, &key, 0)) {
+    if (errno == EAGAIN) {
+      txn_abort(tid);
+      goto retry;
+    }
+    
+    /* be silent if there isn't an entry */
+    if (errno != DB_NOTFOUND) {
+      txn_abort(tid);
+      goto abort_err;
+    }
+  }
+
+  /* now delete from main database */
+  key.data = &id;
+  key.size = sizeof(id);
+  if (errno = db->db_cnid->del(db->db_cnid, tid, &key, 0)) {
+    txn_abort(tid);
+    if (errno == EAGAIN) {
+      goto retry;
+    }
+    goto abort_err;
+  }
+
+  return txn_commit(tid);
+
+abort_err:
+  syslog(LOG_ERR, "cnid_del: unable to delete CNID(%x)", id);
+  return errno;
+}
diff --git a/libatalk/cnid/cnid_get.c b/libatalk/cnid/cnid_get.c
new file mode 100644 (file)
index 0000000..d1f8297
--- /dev/null
@@ -0,0 +1,51 @@
+#include <stdio.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <syslog.h>
+#include <errno.h>
+
+#include <db.h>
+#include <netatalk/endian.h>
+#include <atalk/adouble.h>
+#include <atalk/cnid.h>
+
+#include "cnid_private.h"
+
+/* return CNID for a given did/name */
+cnid_t cnid_get(void *CNID, const cnid_t did, const char *name,
+               const int len) 
+{
+  char start[CNID_DID_LEN + MAXPATHLEN + 1], *buf;
+  CNID_private *db;
+  DBT key, data;
+  cnid_t id;
+
+  if (!(db = CNID) || (len > MAXPATHLEN))
+    return 0;
+
+  memset(&key, 0, sizeof(key));
+  memset(&data, 0, sizeof(data));
+
+  buf = start;
+  memcpy(buf, &did, sizeof(did));
+  buf += sizeof(did);
+  memcpy(buf, name, len);
+  *(buf + len) = '\0'; /* make sure to nul terminate. */
+  key.data = start;
+  key.size = CNID_DID_LEN + len + 1;
+
+  while (errno = db->db_didname->get(db->db_didname, NULL,
+                                    &key, &data, 0)) {
+    if (errno == EAGAIN) 
+      continue;
+
+    if (errno != DB_NOTFOUND)
+      syslog(LOG_ERR, "cnid_get: can't get CNID(%u:%s)", did, name);
+
+    return 0;
+  }
+
+  memcpy(&id, data.data, sizeof(id));
+  return id;
+}
diff --git a/libatalk/cnid/cnid_lookup.c b/libatalk/cnid/cnid_lookup.c
new file mode 100644 (file)
index 0000000..d862544
--- /dev/null
@@ -0,0 +1,105 @@
+#include <stdio.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <syslog.h>
+#include <errno.h>
+
+#include <db.h>
+#include <netatalk/endian.h>
+#include <atalk/adouble.h>
+#include <atalk/cnid.h>
+
+#include "cnid_private.h"
+
+#define LOGFILEMAX    100  /* kbytes */
+#define CHECKTIMEMAX   30  /* minutes */
+
+/* this returns the cnid corresponding to a particular file. it will
+   also fix up the various databases if there's a problem. */
+cnid_t cnid_lookup(void *CNID,
+                  const struct stat *st, const cnid_t did, 
+                  const char *name, const int len)
+{
+  char *buf;
+  CNID_private *db;
+  DBT key, devdata, diddata;
+  DB_TXNMGR *txnp;
+  int devino = 1, didname = 1;
+  cnid_t id = 0;
+  
+  if (!(db = CNID) || !st || !name)
+    return 0;
+
+  /* do a little checkpointing if necessary. i stuck it here as 
+   * cnid_lookup gets called when we do directory lookups. only do 
+   * this if we're using a read-write database. */
+  if ((db->flags & CNIDFLAG_DB_RO) == 0) {
+    txnp = db->dbenv.tx_info;
+    errno = txn_checkpoint(txnp, LOGFILEMAX, CHECKTIMEMAX);
+    while (errno == DB_INCOMPLETE)
+      errno = txn_checkpoint(txnp, 0, 0);
+  }
+
+ if ((buf = make_cnid_data(st, did, name, len)) == NULL) {
+    syslog(LOG_ERR, "cnid_lookup: path name too long");
+    return 0;
+  }
+
+  memset(&key, 0, sizeof(key));
+  memset(&devdata, 0, sizeof(devdata));
+
+  /* look for a CNID. we have two options: dev/ino or did/name. if we
+     only get a match on one of them, that means a file has moved. */
+  key.data = buf; /* dev/ino is the first part of the buffer */
+  key.size = CNID_DEVINO_LEN;
+  while (errno = db->db_devino->get(db->db_devino, NULL,
+                                   &key, &devdata, 0)) {
+    if (errno == EAGAIN)
+      continue;
+    
+    if (errno == DB_NOTFOUND) {
+      devino = 0;
+      break;
+    }
+    
+    syslog(LOG_ERR, "cnid_lookup: can't get CNID(%u/%u)",
+          st->st_dev, st->st_ino);
+    return 0;
+  }
+
+  /* did/name is right afterwards. */
+  key.data = buf + CNID_DEVINO_LEN; 
+  key.size = CNID_DID_LEN + len + 1;
+  memset(&diddata, 0, sizeof(diddata));
+  while (errno = db->db_didname->get(db->db_didname, NULL,
+                                      &key, &diddata, 0)) {
+    if (errno == EAGAIN)
+      continue;
+
+    if (errno == DB_NOTFOUND) {
+      didname = 0;
+      break;
+    }
+
+    syslog(LOG_ERR, "cnid_lookup: can't get CNID(%u:%s)",
+          did, name);
+    return 0;
+  }
+
+  /* set id. honor did/name over dev/ino as dev/ino isn't necessarily 
+   * 1-1. */
+  if (didname) {
+    memcpy(&id, diddata.data, sizeof(id));
+  } else if (devino) {
+    memcpy(&id, devdata.data, sizeof(id));
+  } 
+
+  /* either entries in both databases exist or neither of them do. */
+  if ((devino && didname) || !(devino || didname)) 
+    return id;
+
+  /* fix up the databases */
+  cnid_update(db, id, st, did, name, len);
+  return id;
+}
diff --git a/libatalk/cnid/cnid_meta.c b/libatalk/cnid/cnid_meta.c
new file mode 100644 (file)
index 0000000..803a7ac
--- /dev/null
@@ -0,0 +1,7 @@
+/*
+ * Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu)
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ * deal with metadata
+ *
+ */
diff --git a/libatalk/cnid/cnid_meta.h b/libatalk/cnid/cnid_meta.h
new file mode 100644 (file)
index 0000000..5eb3d64
--- /dev/null
@@ -0,0 +1,47 @@
+#define CNID_META_CNID_LEN      4
+#define CNID_META_MDATE_LEN     4  /* space for 8 */
+#define CNID_META_CDATE_LEN     4  /* space for 8 */
+#define CNID_META_BDATE_LEN     4  /* ditto */
+#define CNID_META_ADATE_LEN     4  /* ditto */
+#define CNID_META_AFPI_LEN      4  /* plus permission bits */
+#define CNID_META_FINDERI_LEN   32 
+#define CNID_META_PRODOSI_LEN   8
+#define CNID_META_RFORKLEN_LEN  4  /* space for 8 */
+#define CNID_META_MACNAME_LEN   32 /* maximum size */
+#define CNID_META_SHORTNAME_LEN 12 /* max size (8.3) */
+#define CNID_META_FILLER_LEN    4
+
+#define CNID_META_CNID_OFF     0
+#define CNID_META_MDATE_OFF    (CNID_META_CNID_OFF + CNID_META_CNID_LEN + \
+                               CNID_META_FILLER_LEN)
+#define CNID_META_CDATE_OFF    (CNID_META_MDATE_OFF + CNID_META_MDATE_LEN + \
+                               CNID_META_FILLER_LEN)
+#define CNID_META_BDATE_OFF    (CNID_META_CDATE_OFF + CNID_META_CDATE_LEN + \
+                               CNID_META_FILLER_LEN)
+#define CNID_META_ADATE_OFF    (CNID_META_BDATE_OFF + CNID_META_BDATE_LEN + \
+                               CNID_META_FILLER_LEN)
+#define CNID_META_AFPI_OFF     (CNID_META_ADATE_OFF + CNID_META_ADATE_LEN)
+#define CNID_META_FINDERI_OFF  (CNID_META_AFPI_OFF + CNID_META_AFPI_LEN)
+#define CNID_META_PRODOSI_OFF  (CNID_META_FINDERI_OFF + CNID_META_FINDERI_LEN)
+#define CNID_META_RFORKLEN_OFF (CNID_META_PRODOSI_OFF + CNID_META_PRODOSI_LEN)
+#define CNID_META_MACNAME_OFF  (CNID_META_RFORKLEN_OFF + \
+                               CNID_META_RFORKLEN_LEN)
+#define CNID_META_SHORTNAME_OFF (CNID_META_MACNAME_OFF + 
+
+
+#define cnid_meta_clear(a)  
+#define cnid_meta_get(id)
+
+#define cnid_meta_cnid(a)  
+#define cnid_meta_modifydate(a)
+#define cnid_meta_createdate(a)
+#define cnid_meta_backupdate(a)
+#define cnid_meta_accessdate(a)
+#define cnid_meta_afpi(a)
+#define cnid_meta_finderi(a)
+#define cnid_meta_prodosi(a)
+#define cnid_meta_rforklen(a)
+#define cnid_meta_macname(a)
+#define cnid_meta_shortname(a)
+#define cnid_meta_longname(a)
+
diff --git a/libatalk/cnid/cnid_nextid.c b/libatalk/cnid/cnid_nextid.c
new file mode 100644 (file)
index 0000000..f26418f
--- /dev/null
@@ -0,0 +1,22 @@
+#include <db.h>
+
+#include <atalk/adouble.h>
+#include <atalk/cnid.h>
+
+#include <syslog.h>
+
+#include "cnid_private.h"
+
+/* return the next id. we use the fact that ad files are memory
+ * mapped. */
+cnid_t cnid_nextid(void *CNID)
+{
+  CNID_private *db;
+  cnid_t id;
+
+  if (!(db = CNID)) 
+    return 0;
+
+  memcpy(&id, ad_entry(&db->rootinfo, ADEID_DID), sizeof(id));
+  return id;
+}
diff --git a/libatalk/cnid/cnid_open.c b/libatalk/cnid/cnid_open.c
new file mode 100644 (file)
index 0000000..10dda27
--- /dev/null
@@ -0,0 +1,369 @@
+/* 
+ * Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu)
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ * CNID database support. 
+ *
+ * here's the deal:
+ *  1) afpd already caches did's. 
+ *  2) the database stores cnid's as both did/name and dev/ino pairs. 
+ *  3) RootInfo holds the value of the NextID.
+ *  4) the cnid database gets called in the following manner --
+ *     start a database:
+ *     cnid = cnid_open(root_dir);
+ *
+ *     allocate a new id: 
+ *     newid = cnid_add(cnid, dev, ino, parent did,
+ *     name, id); id is a hint for a specific id. pass 0 if you don't
+ *     care. if the id is already assigned, you won't get what you
+ *     requested.
+ *
+ *     given an id, get a did/name and dev/ino pair.
+ *     name = cnid_get(cnid, &id); given an id, return the corresponding
+ *     info.
+ *     return code = cnid_delete(cnid, id); delete an entry. 
+ *
+ * with AFP, CNIDs 0-2 have special meanings. here they are:
+ * 0 -- invalid cnid
+ * 1 -- parent of root directory (handled by afpd) 
+ * 2 -- root directory (handled by afpd)
+ *
+ * so, CNID_START begins at 3.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <syslog.h>
+
+#include <db.h>
+
+#include <atalk/adouble.h>
+#include <atalk/cnid.h>
+#include <atalk/util.h>
+
+#include "cnid_private.h"
+
+#ifndef MIN
+#define MIN(a, b)  ((a) < (b) ? (a) : (b))
+#endif
+
+#define ROOTINFO     "RootInfo"
+#define ROOTINFO_LEN 8
+
+#define DBHOME       ".AppleDB"
+#define DBCNID       "cnid.db"
+#define DBDEVINO     "devino.db"
+#define DBDIDNAME    "didname.db"   /* did/full name mapping */
+#define DBSHORTNAME  "shortname.db" /* did/8+3 mapping */
+#define DBMACNAME    "macname.db"   /* did/31 mapping */
+#define DBLONGNAME   "longname.db"  /* did/unicode mapping */
+#define DBLOCKFILE   "/cnid.lock"
+
+#define DBHOMELEN    8
+#define DBLEN        10
+
+/* we version the did/name database so that we can change the format
+ * if necessary. the key is in the form of a did/name pair. in this case,
+ * we use 0/0. */
+#define DBVERSION_KEY    "\0\0\0\0\0"
+#define DBVERSION_KEYLEN 5
+#define DBVERSION1       0x00000001U
+#define DBVERSION        DBVERSION1
+
+#define DBOPTIONS    (DB_CREATE | DB_INIT_MPOOL | DB_INIT_LOCK | \
+DB_INIT_LOG | DB_INIT_TXN | DB_TXN_NOSYNC | DB_RECOVER)
+
+#define MAXITER     0xFFFF /* maximum number of simultaneously open CNID 
+                           * databases. */
+
+/* the first compare that's always done. */
+static __inline__ int compare_did(const DBT *a, const DBT *b)
+{
+  u_int32_t dida, didb;
+
+  memcpy(&dida, a->data, sizeof(dida));
+  memcpy(&didb, b->data, sizeof(didb));
+  return dida - didb;
+}
+
+/* sort did's and then names. this is for unix paths.
+ * i.e., did/unixname lookups. */
+static int compare_unix(const DBT *a, const DBT *b)
+{
+  u_int8_t *sa, *sb;
+  int len, ret;
+
+  /* sort by did */
+  if (ret = compare_did(a, b))
+    return ret;
+
+  sa = a->data + 4; /* shift past did */
+  sb = b->data + 4;
+  for (len = MIN(a->size, b->size); len-- > 4; sa++, sb++)
+    if (ret = (*sa - *sb))
+      return ret; /* sort by lexical ordering */
+  return a->size - b->size; /* sort by length */
+}
+
+/* sort did's and then names. this is for macified paths (i.e.,
+ * did/macname, and did/shortname. i think did/longname needs a
+ * unicode table to work. also, we can't use strdiacasecmp as that
+ * returns a match if a < b. */
+static int compare_mac(const DBT *a, const DBT *b) 
+{
+  u_int8_t *sa, *sb;
+  int len, ret;
+  
+  /* sort by did */
+  if (ret = compare_did(a, b))
+    return ret;
+
+  sa = a->data + 4;
+  sb = b->data + 4;
+  for (len = MIN(a->size, b->size); len-- > 4; sa++, sb++)
+    if (ret = (_diacasemap[*sa] - _diacasemap[*sb]))
+      return ret; /* sort by lexical ordering */
+  return a->size - b->size; /* sort by length */
+}
+
+
+/* for unicode names -- right now it's the same as compare_mac. */
+#define compare_unicode(a, b) compare_mac((a), (b))
+
+void *cnid_open(const char *dir)
+{
+  struct stat st;
+  struct flock lock;
+  char path[MAXPATHLEN + 1];
+  CNID_private *db;
+  DB_INFO dbi;
+  DBT key, data;
+  int open_flag, len;
+
+  if (!dir)
+    return NULL;
+
+  /* this checks both RootInfo and .AppleDB */
+  if ((len = strlen(dir)) > (MAXPATHLEN - DBLEN - 1)) {
+    syslog(LOG_ERR, "cnid_open: path too large");
+    return NULL;
+  }
+  
+  if ((db = (CNID_private *) calloc(1, sizeof(CNID_private))) == NULL) {
+    syslog(LOG_ERR, "cnid_open: unable to allocate memory");
+    return NULL;
+  }
+  db->magic = CNID_DB_MAGIC;
+    
+  strcpy(path, dir);
+  if (path[len - 1] != '/') {
+    strcat(path, "/");
+    len++;
+  }
+
+  lock.l_type = F_WRLCK;
+  lock.l_whence = SEEK_SET;
+
+  /* we create and initialize RootInfo if it doesn't exist. */
+  strcat(path, ROOTINFO);
+  if (ad_open(path, ADFLAGS_HF, O_RDWR, 0666, &db->rootinfo) < 0) {
+    cnid_t id;
+
+    /* see if we can open it read-only. if it's read-only, we can't
+     * add CNIDs. */
+    memset(&db->rootinfo, 0, sizeof(db->rootinfo));
+    if (ad_open(path, ADFLAGS_HF, O_RDONLY, 0666, &db->rootinfo) == 0) {
+      db->flags = CNIDFLAG_ROOTINFO_RO;
+      syslog(LOG_INFO, "cnid_open: read-only RootInfo");
+      goto mkdir_appledb;
+    }
+
+    /* create the file */
+    memset(&db->rootinfo, 0, sizeof(db->rootinfo));
+    if (ad_open(path, ADFLAGS_HF, O_CREAT | O_RDWR, 0666, 
+               &db->rootinfo) < 0) {
+      syslog(LOG_ERR, "cnid_open: ad_open(RootInfo)");
+      goto fail_db;
+    }
+
+    /* lock the RootInfo file. this and cnid_add are the only places
+     * that should fiddle with RootInfo. */
+    lock.l_start = ad_getentryoff(&db->rootinfo, ADEID_DID);
+    lock.l_len = ad_getentrylen(&db->rootinfo, ADEID_DID);
+    if (fcntl(ad_hfileno(&db->rootinfo), F_SETLKW,  &lock) < 0) {
+      syslog(LOG_ERR, "cnid_open: can't establish lock: %m");
+      goto fail_adouble;
+    }
+    
+    /* store the beginning CNID */
+    id = htonl(CNID_START);
+    memcpy(ad_entry(&db->rootinfo, ADEID_DID), &id, sizeof(id));
+    ad_flush(&db->rootinfo, ADFLAGS_HF);
+
+    /* unlock it */
+    lock.l_type = F_UNLCK;
+    fcntl(ad_hfileno(&db->rootinfo), F_SETLK, &lock);
+    lock.l_type = F_WRLCK;
+  }
+
+mkdir_appledb:
+  strcpy(path + len, DBHOME);
+  if ((stat(path, &st) < 0) && (ad_mkdir(path, 0777) < 0)) {
+    syslog(LOG_ERR, "cnid_open: mkdir failed");
+    goto fail_adouble;
+  }
+
+  /* search for a byte lock. this allows us to cleanup the log files
+   * at cnid_close() in a clean fashion.
+   *
+   * NOTE: this won't work if multiple volumes for the same user refer
+   * to the same directory. */
+  strcat(path, DBLOCKFILE);
+  if ((db->lockfd = open(path, O_RDWR | O_CREAT, 0666)) > -1) {
+    lock.l_start = 0;
+    lock.l_len = 1;
+    while (fcntl(db->lockfd, F_SETLK, &lock) < 0) {
+      if (++lock.l_start > MAXITER) {
+       syslog(LOG_INFO, "cnid_open: can't establish logfile cleanup lock.");
+       close(db->lockfd);
+       db->lockfd = -1;
+       break;
+      }      
+    }
+  } 
+
+  path[len + DBHOMELEN] = '\0';
+  open_flag = DB_CREATE;
+  /* try a full-blown transactional environment */
+  if (db_appinit(path, NULL, &db->dbenv, DBOPTIONS)) {
+
+    /* try with a shared memory pool */
+    memset(&db->dbenv, 0, sizeof(db->dbenv));
+    if (db_appinit(path, NULL, &db->dbenv, DB_INIT_MPOOL)) {
+
+      /* try without any options. */
+      memset(&db->dbenv, 0, sizeof(db->dbenv));
+      if (db_appinit(path, NULL, &db->dbenv, 0)) {
+       syslog(LOG_ERR, "cnid_open: db_appinit failed");
+       goto fail_lock;
+      }
+    }
+    db->flags |= CNIDFLAG_DB_RO;
+    open_flag = DB_RDONLY;
+    syslog(LOG_INFO, "cnid_open: read-only CNID database");
+  }
+
+  memset(&dbi, 0, sizeof(dbi));
+
+  /* did/name reverse mapping. we use a btree for this one. */
+  dbi.bt_compare = compare_unix;
+  if (db_open(DBDIDNAME, DB_BTREE, open_flag, 0666, &db->dbenv, &dbi,
+             &db->db_didname)) {
+    goto fail_appinit;
+  }
+
+  /* check for version. this way we can update the database if we need
+     to change the format in any way. */
+  memset(&key, 0, sizeof(key));
+  memset(&data, 0, sizeof(data));
+  key.data = DBVERSION_KEY;
+  key.len = DBVERSION_KEY_LEN;
+  while (errno = db->db_didname->get(db->db_didname, NULL, &key, &data, 0)) {
+    switch (errno) {
+    case EAGAIN:
+      continue;
+    
+    case DB_NOTFOUND:
+      u_int32_t version = htonl(DBVERSION);
+
+      data.data = &version;
+      data.len = sizeof(version);
+dbversion_retry:
+      if (db->db_didname->put(db->db_didname, NULL, &key, &data,
+                             DB_NOOVERWRITE))
+       if (errno == EAGAIN)
+         goto dbversion_retry;
+      break;
+    default:
+      /* uh oh. something bad happened. bail. */
+      db->db_didname->close(db->db_didname, 0);
+      goto fail_appinit;
+    }
+  }
+  
+  /* XXX: in the future, we might check for version number here. */
+#if 0
+  memcpy(&version, data.data, sizeof(version));
+  if (version != htonl(DBVERSION)) {
+    /* fix up stuff */
+  }
+#endif
+
+  /* did/macname mapping. btree this one. */
+  dbi.bt_compare = compare_mac;
+  if (db_open(DBMACNAME, DB_BTREE, open_flag, 0666, &db->dbenv, &dbi,
+             &db->db_macname)) {
+    db->db_didname->close(db->db_didname, 0);
+    goto fail_appinit;
+  }
+
+  /* did/shortname mapping */
+  if (db_open(DBSHORTNAME, DB_BTREE, open_flag, 0666, &db->dbenv, &dbi,
+             &db->db_shortname)) {
+    db->db_didname->close(db->db_didname, 0);
+    db->db_macname->close(db->db_macname, 0);
+    goto fail_appinit;
+  }
+
+  /* did/longname mapping */
+  dbi.bt_compare = compare_unicode;
+  if (db_open(DBLONGNAME, DB_BTREE, open_flag, 0666, &db->dbenv, &dbi,
+             &db->db_longname)) {
+    db->db_didname->close(db->db_didname, 0);
+    db->db_macname->close(db->db_macname, 0);
+    db->db_shortname->close(db->db_shortname, 0);
+    goto fail_appinit;
+  }
+
+  /* dev/ino reverse mapping. we hash this one. */
+  dbi.bt_compare = NULL;
+  if (db_open(DBDEVINO, DB_HASH, open_flag, 0666, &db->dbenv, &dbi,
+             &db->db_devino)) {
+    db->db_didname->close(db->db_didname, 0);
+    db->db_macname->close(db->db_macname, 0);
+    db->db_shortname->close(db->db_shortname, 0);
+    db->db_longname->close(db->db_longname, 0);
+    goto fail_appinit;
+  }
+
+  /* main cnid database. we hash this one as well. */
+  if (db_open(DBCNID, DB_HASH, open_flag, 0666, &db->dbenv, &dbi,
+             &db->db_cnid)) {
+    db->db_didname->close(db->db_didname, 0);
+    db->db_macname->close(db->db_macname, 0);
+    db->db_shortname->close(db->db_shortname, 0);
+    db->db_longname->close(db->db_longname, 0);
+    db->db_devino->close(db->db_devino, 0);
+    goto fail_appinit;
+  }
+  return db;
+  
+fail_appinit:
+  syslog(LOG_ERR, "cnid_open: db_open failed");
+  db_appexit(&db->dbenv);
+
+fail_lock:
+  if (db->lockfd > -1)
+    close(db->lockfd);
+
+fail_adouble:
+  ad_close(&db->rootinfo, ADFLAGS_HF);
+
+fail_db:
+  free(db);
+  return NULL;
+}
diff --git a/libatalk/cnid/cnid_private.h b/libatalk/cnid/cnid_private.h
new file mode 100644 (file)
index 0000000..3eacb1a
--- /dev/null
@@ -0,0 +1,120 @@
+#ifndef LIBATALK_CNID_PRIVATE_H
+#define LIBATALK_CNID_PRIVATE_H 1
+
+#include <string.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <db.h>
+
+#include <atalk/adouble.h>
+#include <atalk/cnid.h>
+
+#define CNID_DB_MAGIC   0x434E4944U  /* CNID */
+#define CNID_DATA_MAGIC 0x434E4945U  /* CNIE */
+
+#define CNID_DEVINO_LEN          8
+#define CNID_DID_LEN             4
+#define CNID_HEADER_LEN          (CNID_DEVINO_LEN + CNID_DID_LEN)
+
+#define CNID_START               3
+
+#define CNIDFLAG_ROOTINFO_RO     (1 << 0)
+#define CNIDFLAG_DB_RO           (1 << 1)
+
+typedef struct CNID_private {
+       u_int32_t magic;
+       DB *db_cnid, *db_didname, *db_devino,
+         *db_shortname, *db_macname, *db_longname;
+       DB_ENV dbenv;
+       struct adouble rootinfo;
+        int lockfd, flags;
+} CNID_private;
+
+/* on-disk data format (in network byte order where appropriate) --
+ * db_cnid:      (key: cnid)
+ * name          size (in bytes)
+ * dev           4
+ * ino           4
+ * did           4
+ * unix name     strlen(name) + 1 
+ *
+ * db_didname:   (key: did/unix name)
+ * -- this also caches the bits of .AppleDouble used by FPGetFilDirParam
+ *    so that we don't have to open the header file.
+ *    NOTE: FPCatSearch has to search through all of the directories as
+ *          this stuff doesn't get entered until needed.
+ *          if the entire volume is in the database, though, we can use
+ *          cursor operations to make this faster.
+ *
+ *    version number is stored with did/name key of 0/0
+ *
+ * cnid          4
+ * modfiller     4 (dates only use 4 bytes right now, but we leave space 
+ * moddate       4  for 8. moddate is also used to keep this info 
+ * createfiller  4  up-to-date.)
+ * createdate    4
+ * backfiller    4
+ * backupdate    4
+ * accfiller     4 (unused)
+ * accdate       4 (unused)
+ * AFP info      4 (stores a couple permission bits as well)
+ * finder info   32
+ * prodos info   8
+ * rforkfiller   4
+ * rforklen      4
+ * macname       32 (nul-terminated)
+ * shortname     12 (nul-terminated)
+ * longname      longnamelen (nul-terminated)
+ * ---------------
+ *             132 bytes + longnamelen
+ * 
+ * db_devino:    (key: dev/ino) 
+ * -- this is only used for consistency checks and isn't 1-1
+ * cnid          4 
+ *
+ * these correspond to the different path types. longname is for the
+ * 255 unicode character names (path type == ?), macname is for the
+ * 32-character names (path type == 2), and shortname is for the
+ * 8+3-character names (path type == 1).
+ *
+ * db_longname: (key: did/longname)
+ * name          namelen = strlen(name) + 1
+ *
+ * db_macname:   (key: did/macname)
+ * name          namelen = strlen(name) + 1
+ *
+ * db_shortname: (key: did/shortname)
+ * name namelen = strlen(name) + 1 
+ */
+
+#ifndef __inline__
+#define __inline__
+#endif
+
+/* construct db_cnid data. NOTE: this is not re-entrant.  */
+static __inline__ char *make_cnid_data(const struct stat *st,
+                                      const cnid_t did, 
+                                      const char *name, const int len)
+{
+  static char start[CNID_HEADER_LEN + MAXPATHLEN + 1];
+  u_int32_t i;
+  
+  if (len > MAXPATHLEN)
+    return NULL;
+
+  i = htonl(st->st_dev);
+  memcpy(start, &i, sizeof(i));
+  i = htonl(st->st_ino);
+  memcpy(start + sizeof(i), &i, sizeof(i));
+  /* did is already in network byte order */
+  memcpy(start + CNID_DEVINO_LEN, &did, sizeof(did)); 
+  memcpy(start + CNID_HEADER_LEN, name, len);
+  *(buf + len) = '\0';
+  buf += len + 1;
+
+  return start;
+}
+
+#endif /* atalk/cnid/cnid_private.h */
diff --git a/libatalk/cnid/cnid_resolve.c b/libatalk/cnid/cnid_resolve.c
new file mode 100644 (file)
index 0000000..744a7d7
--- /dev/null
@@ -0,0 +1,42 @@
+#include <stdio.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <syslog.h>
+#include <errno.h>
+
+#include <db.h>
+#include <netatalk/endian.h>
+#include <atalk/adouble.h>
+#include <atalk/cnid.h>
+
+#include "cnid_private.h"
+
+/* return the did/name pair corresponding to a CNID. */
+char *cnid_resolve(void *CNID, cnid_t *id)
+{
+  CNID_private *db;
+  DBT key, data;
+
+  if (!(db = CNID) || !id || !(*id))
+    return NULL;
+
+  memset(&key, 0, sizeof(key));
+  memset(&data, 0, sizeof(data));
+
+  key.data = id;
+  key.size = sizeof(*id);
+  while (errno = db->db_cnid->get(db->db_cnid, NULL, &key, &data, 0)) {
+    if (errno == EAGAIN)
+      continue;
+
+    if (errno != DB_NOTFOUND) 
+      syslog(LOG_ERR, "cnid_resolve: can't get did/name");
+
+    *id = 0;
+    return NULL;
+  }
+  
+  memcpy(id, data.data + CNID_DEVINO_LEN, sizeof(*id));
+  return data.data + CNID_HEADER_LEN;
+}
diff --git a/libatalk/cnid/cnid_update.c b/libatalk/cnid/cnid_update.c
new file mode 100644 (file)
index 0000000..1c7a021
--- /dev/null
@@ -0,0 +1,132 @@
+#include <stdio.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <syslog.h>
+
+#include <db.h>
+#include <netatalk/endian.h>
+#include <atalk/adouble.h>
+#include <atalk/cnid.h>
+
+#include "cnid_private.h"
+
+
+/* cnid_update: takes the given cnid and updates the metadata. to
+   handle the did/name data, there are a bunch of functions to get
+   and set the various fields. */
+int cnid_update(void *CNID, cnid_t id, const struct stat *st, 
+               const cnid_t did, const char *name, const int len,
+               const char *info, const int infolen)
+{
+  CNID_private *db;
+  DBT key, data, altdata;
+  DB_TXN *tid;
+  DB_TXNMGR *txnp;
+  
+  if (!(db = CNID) || !id || !st || !name || (db->flags & CNIDFLAG_DB_RO))
+    return -1;
+
+  memset(&key, 0, sizeof(key));
+  memset(&data, 0, sizeof(data));
+  memset(&altdata, 0, sizeof(altdata));
+  txnp = db->dbenv.tx_info;
+
+  /* begin a transaction */
+retry:
+  if (errno = txn_begin(txnp, NULL, &tid)) {
+    return errno;
+  }
+
+  /* get the old info */
+  key.data = &id;
+  key.size = sizeof(id);
+  if (errno = db->db_cnid->get(db->db_cnid, tid, &key, &data, 0)) {
+    txn_abort(tid);
+    if (errno == EAGAIN)
+      goto retry;
+    goto update_err;
+  }
+
+  /* delete the old dev/ino mapping */
+  key.data = data.data;
+  key.size = CNID_DEVINO_LEN;
+  if (errno = db->db_devino->del(db->db_devino, tid, &key, 0)) {
+    if (errno == EAGAIN) {
+      txn_abort(tid);
+      goto retry;
+    }
+      
+    /* silently fail on a non-existent entry */
+    if (errno != DB_NOTFOUND) {
+      txn_abort(tid);
+      goto update_err;
+    }
+  }
+
+  /* delete the old did/name mapping */
+  key.data = data.data + CNID_DEVINO_LEN;
+  key.size = data.size - CNID_DEVINO_LEN;
+  if (errno = db->db_didname->del(db->db_didname, tid, &key, 0)) {
+    if (errno == EAGAIN) {
+      txn_abort(tid);
+      goto retry;
+    }
+
+    /* silently fail on a non-existent entry */
+    if (errno != DB_NOTFOUND) {
+      txn_abort(tid);
+      goto update_err;
+    }
+  }
+  
+  /* delete the old aliases if necessary */
+
+
+  /* make a new entry */
+  data.data = make_cnid_data(st, did, name, len);
+  data.size = CNID_HEADER_LEN + len + 1;
+  
+  /* put a new dev/ino mapping in */
+  key.data = data.data;
+  key.size = CNID_DEVINO_LEN;
+  altdata.data = &id;
+  altdata.size = sizeof(id);
+  if (errno = db->db_devino->put(db->db_devino, tid, &key, &altdata, 0)) {
+    txn_abort(tid);
+    if (errno == EAGAIN) {
+      goto retry;
+    }
+    goto update_err;
+  }
+  
+  /* put a new did/name mapping in */
+  key.data = data.data + CNID_DEVINO_LEN;
+  key.size = data.size - CNID_DEVINO_LEN;
+  if (errno = db->db_didname->put(db->db_didname, tid, &key, &altdata, 0)) {
+    txn_abort(tid);
+    if (errno == EAGAIN) {
+      goto retry;
+    }
+    goto update_err;
+  }
+  
+  /* update the old CNID with the new info */
+  key.data = &id;
+  key.size = sizeof(id);
+  if (errno = db->db_cnid->put(db->db_cnid, tid, &key, &data, 0)) {
+    txn_abort(tid);
+    if (errno == EAGAIN) {
+      goto retry;
+    }
+    goto update_err;
+  }
+  
+  /* end transaction */
+  return txn_commit(tid);
+
+update_err:
+  syslog(LOG_ERR, "cnid_update: can't update CNID(%x)", id);
+  return -1;
+}
diff --git a/libatalk/compat/Makefile b/libatalk/compat/Makefile
new file mode 100644 (file)
index 0000000..bb606de
--- /dev/null
@@ -0,0 +1,58 @@
+SRC=   mktemp.c getusershell.c strcasecmp.c strstr.c flock.c strdup.c \
+       inet_aton.c rquota_xdr.c
+OBJ=   mktemp.o getusershell.o strcasecmp.o strstr.o flock.o strdup.o \
+       inet_aton.o rquota_xdr.o
+
+INCPATH=       -I../../include
+CFLAGS=        ${DEFS} ${RPCSVCDEFS} ${OPTOPTS} ${INCPATH} 
+TAGSFILE=      tags
+CC=    cc
+
+all : profiled compatlib
+
+profiled:
+       -mkdir profiled
+
+compatlib compatlib_p : ${OBJ}
+       @echo "building profiled compatlib"
+       @cd profiled; ar cru ../compatlib_p ${OBJ}
+       @echo "building normal compatlib"
+       @ar cru compatlib ${OBJ}
+
+.c.o :
+       ${CC} -p ${CFLAGS} -c $*.c
+       -ld -r -o $*.o- $*.o
+       mv $*.o- profiled/$*.o
+       ${CC} ${CFLAGS} -c $*.c
+       -ld -r -o $*.o- $*.o
+       mv $*.o- $*.o
+
+clean :
+       rm -f *.o profiled/*.o *.bak *[Ee]rrs tags
+       rm -f compatlib compatlib_p
+
+tags : ${SRC}
+       cwd=`pwd`; \
+       for i in ${SRC}; do \
+           ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+       done
+
+depend :
+       for i in ${SRC} ; do \
+           ${CC} -M ${DEFS} ${INCPATH} $$i | \
+           awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+               else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+               else rec = rec " " $$2 } } \
+               END { print rec } ' >> makedep; done
+       sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+       cat makedep >> Makefile.tmp
+       rm makedep
+       echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+       echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+       echo '# see make depend above' >> Makefile.tmp
+       rm -f Makefile.bak
+       cp Makefile Makefile.bak
+       mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
diff --git a/libatalk/compat/flock.c b/libatalk/compat/flock.c
new file mode 100644 (file)
index 0000000..45ec45d
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 1996 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+static int     _flock_dummy;
+
+# if defined( sun ) && defined( __svr4__ )
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include </usr/ucbinclude/sys/file.h>
+
+int flock( fd, operation )
+    int                fd;
+    int                operation;
+{
+    flock_t    l;
+    int                rc, op;
+
+    if ( operation & LOCK_NB ) {
+       op = F_SETLK;
+    } else {
+       op = F_SETLKW;
+    }
+
+    if ( operation & LOCK_EX ) {
+       l.l_type = F_WRLCK;
+    }
+
+    if ( operation & LOCK_SH ) {
+       l.l_type = F_RDLCK;
+    }
+
+    if ( operation & LOCK_UN ) {
+       l.l_type = F_UNLCK;
+    }
+
+    l.l_whence = 0;
+    l.l_start = 0;
+    l.l_len = 0;
+
+    if (( rc = fcntl( fd, F_SETLK, &l )) < 0 ) {
+       if ( errno == EAGAIN || errno == EACCES ) {
+           errno = EWOULDBLOCK;
+       }
+    }
+    return( rc );
+}
+# endif sun __svr4__
diff --git a/libatalk/compat/getusershell.c b/libatalk/compat/getusershell.c
new file mode 100644 (file)
index 0000000..bd79d4f
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 1985 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that: (1) source distributions retain this entire copyright
+ * notice and comment, and (2) distributions including binaries display
+ * the following acknowledgement:  ``This product includes software
+ * developed by the University of California, Berkeley and its contributors''
+ * in the documentation or other materials provided with the distribution
+ * and in all advertising materials mentioning features or use of this
+ * software. 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)getusershell.c     5.6 (Berkeley) 6/1/90";
+#endif /* LIBC_SCCS and not lint */
+
+static int     _getusershell_dummy;
+
+#if defined(ultrix) || defined(_IBMR2) || defined(NEED_GETUSERSHELL)
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <stdio.h>
+
+#define SHELLS "/etc/shells"
+
+/*
+ * Do not add local shells here.  They should be added in /etc/shells
+ */
+static char *okshells[] = {
+    "/bin/sh", "/bin/csh",
+#ifdef _IBMR2
+    "/bin/ksh",
+#endif _IBMR2
+    0
+};
+
+static char **shells, *strings;
+static char **curshell = NULL;
+extern char **initshells();
+
+/*
+ * Get a list of shells from SHELLS, if it exists.
+ */
+char *
+getusershell()
+{
+       char *ret;
+
+       if (curshell == NULL)
+               curshell = initshells();
+       ret = *curshell;
+       if (ret != NULL)
+               curshell++;
+       return (ret);
+}
+
+endusershell()
+{
+       
+       if (shells != NULL)
+               free((char *)shells);
+       shells = NULL;
+       if (strings != NULL)
+               free(strings);
+       strings = NULL;
+       curshell = NULL;
+}
+
+setusershell()
+{
+
+       curshell = initshells();
+}
+
+static char **
+initshells()
+{
+       register char **sp, *cp;
+       register FILE *fp;
+       struct stat statb;
+       extern char *malloc(), *calloc();
+
+       if (shells != NULL)
+               free((char *)shells);
+       shells = NULL;
+       if (strings != NULL)
+               free(strings);
+       strings = NULL;
+       if ((fp = fopen(SHELLS, "r")) == (FILE *)0)
+               return(okshells);
+       if (fstat(fileno(fp), &statb) == -1) {
+               (void)fclose(fp);
+               return(okshells);
+       }
+       if ((strings = malloc((unsigned)statb.st_size)) == NULL) {
+               (void)fclose(fp);
+               return(okshells);
+       }
+       shells = (char **)calloc((unsigned)statb.st_size / 3, sizeof (char *));
+       if (shells == NULL) {
+               (void)fclose(fp);
+               free(strings);
+               strings = NULL;
+               return(okshells);
+       }
+       sp = shells;
+       cp = strings;
+       while (fgets(cp, MAXPATHLEN + 1, fp) != NULL) {
+               while (*cp != '#' && *cp != '/' && *cp != '\0')
+                       cp++;
+               if (*cp == '#' || *cp == '\0')
+                       continue;
+               *sp++ = cp;
+               while (!isspace(*cp) && *cp != '#' && *cp != '\0')
+                       cp++;
+               *cp++ = '\0';
+       }
+       *sp = (char *)0;
+       (void)fclose(fp);
+       return (shells);
+}
+
+# endif ultrix
diff --git a/libatalk/compat/inet_aton.c b/libatalk/compat/inet_aton.c
new file mode 100644 (file)
index 0000000..b073b52
--- /dev/null
@@ -0,0 +1,19 @@
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+static int _inet_aton_dummy;
+
+#if defined(ultrix) || (defined(sun) && defined(__svr4__))
+#ifndef INADDR_NONE
+#define INADDR_NONE ((unsigned) 0xffffffff)
+#endif
+
+int inet_aton(const char *name, struct in_addr *addr)
+{
+  if ((addr->s_addr = inet_addr(name)) == htonl(INADDR_NONE))
+    return 0;
+
+  return 1;
+}
+#endif
diff --git a/libatalk/compat/mktemp.c b/libatalk/compat/mktemp.c
new file mode 100644 (file)
index 0000000..f08bd1e
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 1987 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that: (1) source distributions retain this entire copyright
+ * notice and comment, and (2) distributions including binaries display
+ * the following acknowledgement:  ``This product includes software
+ * developed by the University of California, Berkeley and its contributors''
+ * in the documentation or other materials provided with the distribution
+ * and in all advertising materials mentioning features or use of this
+ * software. 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)mktemp.c   5.9 (Berkeley) 6/1/90";
+#endif /* LIBC_SCCS and not lint */
+
+static int     _mktemp_dummy;
+
+# ifdef ultrix
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdio.h>
+#include <ctype.h>
+
+mkstemp(path)
+       char *path;
+{
+       int fd;
+
+       return (_gettemp(path, &fd) ? fd : -1);
+}
+
+char *
+mktemp(path)
+       char *path;
+{
+       return(_gettemp(path, (int *)NULL) ? path : (char *)NULL);
+}
+
+static
+_gettemp(path, doopen)
+       char *path;
+       register int *doopen;
+{
+       extern int errno;
+       register char *start, *trv;
+       struct stat sbuf;
+       u_int pid;
+
+       pid = getpid();
+       for (trv = path; *trv; ++trv);          /* extra X's get set to 0's */
+       while (*--trv == 'X') {
+               *trv = (pid % 10) + '0';
+               pid /= 10;
+       }
+
+       /*
+        * check the target directory; if you have six X's and it
+        * doesn't exist this runs for a *very* long time.
+        */
+       for (start = trv + 1;; --trv) {
+               if (trv <= path)
+                       break;
+               if (*trv == '/') {
+                       *trv = '\0';
+                       if (stat(path, &sbuf))
+                               return(0);
+                       if (!S_ISDIR(sbuf.st_mode)) {
+                               errno = ENOTDIR;
+                               return(0);
+                       }
+                       *trv = '/';
+                       break;
+               }
+       }
+
+       for (;;) {
+               if (doopen) {
+                       if ((*doopen =
+                           open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0)
+                               return(1);
+                       if (errno != EEXIST)
+                               return(0);
+               }
+               else if (stat(path, &sbuf))
+                       return(errno == ENOENT ? 1 : 0);
+
+               /* tricky little algorithm for backward compatibility */
+               for (trv = start;;) {
+                       if (!*trv)
+                               return(0);
+                       if (*trv == 'z')
+                               *trv++ = 'a';
+                       else {
+                               if (isdigit(*trv))
+                                       *trv = 'a';
+                               else
+                                       ++*trv;
+                               break;
+                       }
+               }
+       }
+       /*NOTREACHED*/
+}
+
+# endif ultrix
diff --git a/libatalk/compat/rquota_xdr.c b/libatalk/compat/rquota_xdr.c
new file mode 100644 (file)
index 0000000..f06da23
--- /dev/null
@@ -0,0 +1,115 @@
+/* taken from the quota-1.55 used on linux. here's the bsd copyright:
+ *
+ * Copyright (c) 1980, 1990 Regents of the University of California. All
+ * rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by Robert Elz at
+ * The University of Melbourne.
+ */
+
+#include <stdio.h> /* to get __GNU_LIBRARY__ */
+
+static int _xdr_rquota_dummy;
+
+/* list of machines that don't have these functions:
+       solaris
+       linux libc5
+*/
+#if defined(NEED_RQUOTA) || (defined(sun) && defined(__svr4__)) || \
+(defined(__GNU_LIBRARY__) && __GNU_LIBRARY__ < 6)
+
+#include <rpc/rpc.h>
+#include <rpcsvc/rquota.h>
+
+bool_t
+xdr_getquota_args(xdrs, objp)
+       XDR *xdrs;
+       getquota_args *objp;
+{
+       if (!xdr_string(xdrs, &objp->gqa_pathp, RQ_PATHLEN)) {
+               return (FALSE);
+       }
+       if (!xdr_int(xdrs, &objp->gqa_uid)) {
+               return (FALSE);
+       }
+       return (TRUE);
+}
+
+
+bool_t
+xdr_rquota(xdrs, objp)
+       XDR *xdrs;
+       rquota *objp;
+{
+       if (!xdr_int(xdrs, &objp->rq_bsize)) {
+               return (FALSE);
+       }
+       if (!xdr_bool(xdrs, &objp->rq_active)) {
+               return (FALSE);
+       }
+       if (!xdr_u_int(xdrs, &objp->rq_bhardlimit)) {
+               return (FALSE);
+       }
+       if (!xdr_u_int(xdrs, &objp->rq_bsoftlimit)) {
+               return (FALSE);
+       }
+       if (!xdr_u_int(xdrs, &objp->rq_curblocks)) {
+               return (FALSE);
+       }
+       if (!xdr_u_int(xdrs, &objp->rq_fhardlimit)) {
+               return (FALSE);
+       }
+       if (!xdr_u_int(xdrs, &objp->rq_fsoftlimit)) {
+               return (FALSE);
+       }
+       if (!xdr_u_int(xdrs, &objp->rq_curfiles)) {
+               return (FALSE);
+       }
+       if (!xdr_u_int(xdrs, &objp->rq_btimeleft)) {
+               return (FALSE);
+       }
+       if (!xdr_u_int(xdrs, &objp->rq_ftimeleft)) {
+               return (FALSE);
+       }
+       return (TRUE);
+}
+
+
+
+
+bool_t
+xdr_gqr_status(xdrs, objp)
+       XDR *xdrs;
+       gqr_status *objp;
+{
+       if (!xdr_enum(xdrs, (enum_t *)objp)) {
+               return (FALSE);
+       }
+       return (TRUE);
+}
+
+
+bool_t
+xdr_getquota_rslt(xdrs, objp)
+       XDR *xdrs;
+       getquota_rslt *objp;
+{
+       if (!xdr_gqr_status(xdrs, &objp->status)) {
+               return (FALSE);
+       }
+       switch (objp->status) {
+       case Q_OK:
+               if (!xdr_rquota(xdrs, &objp->getquota_rslt_u.gqr_rquota)) {
+                       return (FALSE);
+               }
+               break;
+       case Q_NOQUOTA:
+               break;
+       case Q_EPERM:
+               break;
+       default:
+               return (FALSE);
+       }
+       return (TRUE);
+}
+#endif
diff --git a/libatalk/compat/strcasecmp.c b/libatalk/compat/strcasecmp.c
new file mode 100644 (file)
index 0000000..46acac3
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 1987 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that: (1) source distributions retain this entire copyright
+ * notice and comment, and (2) distributions including binaries display
+ * the following acknowledgement:  ``This product includes software
+ * developed by the University of California, Berkeley and its contributors''
+ * in the documentation or other materials provided with the distribution
+ * and in all advertising materials mentioning features or use of this
+ * software. 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+static int     _strcasecmp_dummy;
+
+# if defined( ibm032 )
+
+#include <sys/types.h>
+#include <string.h>
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static const char sccsid[] = "@(#)strcasecmp.c 5.9 (Berkeley) 6/1/90";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * This array is designed for mapping upper and lower case letter
+ * together for a case independent comparison.  The mappings are
+ * based upon ascii character sequences.
+ */
+static u_char charmap[] = {
+       '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007',
+       '\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017',
+       '\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027',
+       '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
+       '\040', '\041', '\042', '\043', '\044', '\045', '\046', '\047',
+       '\050', '\051', '\052', '\053', '\054', '\055', '\056', '\057',
+       '\060', '\061', '\062', '\063', '\064', '\065', '\066', '\067',
+       '\070', '\071', '\072', '\073', '\074', '\075', '\076', '\077',
+       '\100', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
+       '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
+       '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
+       '\170', '\171', '\172', '\133', '\134', '\135', '\136', '\137',
+       '\140', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
+       '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
+       '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
+       '\170', '\171', '\172', '\173', '\174', '\175', '\176', '\177',
+       '\200', '\201', '\202', '\203', '\204', '\205', '\206', '\207',
+       '\210', '\211', '\212', '\213', '\214', '\215', '\216', '\217',
+       '\220', '\221', '\222', '\223', '\224', '\225', '\226', '\227',
+       '\230', '\231', '\232', '\233', '\234', '\235', '\236', '\237',
+       '\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247',
+       '\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257',
+       '\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267',
+       '\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277',
+       '\300', '\301', '\302', '\303', '\304', '\305', '\306', '\307',
+       '\310', '\311', '\312', '\313', '\314', '\315', '\316', '\317',
+       '\320', '\321', '\322', '\323', '\324', '\325', '\326', '\327',
+       '\330', '\331', '\332', '\333', '\334', '\335', '\336', '\337',
+       '\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
+       '\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
+       '\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
+       '\370', '\371', '\372', '\373', '\374', '\375', '\376', '\377',
+};
+
+int
+strcasecmp(s1, s2)
+       char *s1, *s2;
+{
+       register u_char *cm = charmap,
+                       *us1 = (u_char *)s1,
+                       *us2 = (u_char *)s2;
+
+       while (cm[*us1] == cm[*us2++])
+               if (*us1++ == '\0')
+                       return (0);
+       return (cm[*us1] - cm[*--us2]);
+}
+
+int
+strncasecmp(s1, s2, n)
+       char *s1, *s2;
+       register size_t n;
+{
+       if (n != 0) {
+               register u_char *cm = charmap,
+                               *us1 = (u_char *)s1,
+                               *us2 = (u_char *)s2;
+
+               do {
+                       if (cm[*us1] != cm[*us2++])
+                               return (cm[*us1] - cm[*--us2]);
+                       if (*us1++ == '\0')
+                               break;
+               } while (--n != 0);
+       }
+       return (0);
+}
+
+# endif ibm032
diff --git a/libatalk/compat/strdup.c b/libatalk/compat/strdup.c
new file mode 100644 (file)
index 0000000..04a4275
--- /dev/null
@@ -0,0 +1,17 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int _strdup_dummy;
+
+#ifdef ultrix
+char *strdup(const char *string)
+{
+  char *new;
+  
+  if (new = (char *) malloc(strlen(string) + 1))
+    strcpy(new, string);
+
+  return new;
+}
+#endif
diff --git a/libatalk/compat/strstr.c b/libatalk/compat/strstr.c
new file mode 100644 (file)
index 0000000..2c3b2b4
--- /dev/null
@@ -0,0 +1,73 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * 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 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strstr.c   5.2 (Berkeley) 1/26/91";
+#endif /* LIBC_SCCS and not lint */
+
+static int     _strstr_dummy;
+
+# if defined(ibm032) || (defined(sun) && defined(i386))
+#ifdef sun
+#define const
+#endif sun
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Find the first occurrence of find in s.
+ */
+char *
+strstr(s, find)
+       register const char *s, *find;
+{
+       register char c, sc;
+       register size_t len;
+
+       if ((c = *find++) != 0) {
+               len = strlen(find);
+               do {
+                       do {
+                               if ((sc = *s++) == 0)
+                                       return (0);
+                       } while (sc != c);
+               } while (strncmp(s, find, len) != 0);
+               s--;
+       }
+       return ((char *)s);
+}
+# endif ibm03 sun i386
diff --git a/libatalk/dsi/Makefile b/libatalk/dsi/Makefile
new file mode 100644 (file)
index 0000000..507b22c
--- /dev/null
@@ -0,0 +1,69 @@
+SRC = dsi_attn.c dsi_close.c dsi_cmdreply.c dsi_getsess.c \
+       dsi_getstat.c dsi_init.c dsi_opensess.c dsi_read.c \
+       dsi_tcp.c dsi_tickle.c dsi_write.c dsi_stream.c 
+OBJ = dsi_attn.o dsi_close.o dsi_cmdreply.o dsi_getsess.o \
+       dsi_getstat.o dsi_init.o dsi_opensess.o dsi_read.o \
+       dsi_tcp.o dsi_tickle.o dsi_write.o dsi_stream.o 
+
+INCPATH=       -I../../include ${TCPWRAPINCPATH}
+CFLAGS = ${DEFS} ${OPTOPTS} ${INCPATH} ${TCPWRAPDEFS}
+TAGSFILE=      tags
+CC=    cc
+
+all : 
+       if [ x"${TCPWRAPDIR}" != x ]; then \
+         TCPWRAPDEFS="-DTCPWRAP"; \
+         if [ "${TCPWRAPDIR}" != "/usr" ]; then \
+           TCPWRAPINCPATH="-I${TCPWRAPDIR}/include"; \
+         fi; \
+       fi; \
+       ${MAKE} ${MFLAGS} CC="${CC}" DEFS="${DEFS}" \
+       OPTOPTS="${OPTOPTS}" \
+       TCPWRAPINCPATH="$${TCPWRAPINCPATH}" TCPWRAPDEFS="$${TCPWRAPDEFS}" \
+       dsilib
+
+profiled:
+       -mkdir profiled
+
+dsilib dsilib_p : profiled ${OBJ}
+       @echo "building profiled dsilib"
+       @cd profiled; ar cru ../dsilib_p ${OBJ}
+       @echo "building normal dsilib"
+       @ar cru dsilib ${OBJ}
+
+.c.o :
+       ${CC} -p ${CFLAGS} -c $*.c
+       -ld -r -o $*.o- $*.o
+       mv $*.o- profiled/$*.o
+       ${CC} ${CFLAGS} -c $*.c
+       -ld -r -o $*.o- $*.o
+       mv $*.o- $*.o
+
+clean :
+       rm -f *.o profiled/*.o *.bak *[Ee]rrs tags
+       rm -f dsilib dsilib_p
+
+tags : ${SRC}
+       cwd=`pwd`; \
+       for i in ${SRC}; do \
+           ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+       done
+
+depend :
+       for i in ${SRC} ; do \
+           ${CC} -M ${DEFS} ${INCPATH} $$i | \
+           awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+               else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+               else rec = rec " " $$2 } } \
+               END { print rec } ' >> makedep; done
+       sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+       cat makedep >> Makefile.tmp
+       rm makedep
+       echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+       echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+       echo '# see make depend above' >> Makefile.tmp
+       rm -f Makefile.bak
+       cp Makefile Makefile.bak
+       mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
diff --git a/libatalk/dsi/README b/libatalk/dsi/README
new file mode 100644 (file)
index 0000000..f34b81d
--- /dev/null
@@ -0,0 +1,45 @@
+SIGNALS AND WRITES TO CLIENT:
+because AFP/TCP uses a streaming protocol, we need to make sure that
+writes to the client are atomic. notably, signal handlers which write
+data can't interrupt data currently being written. in addition, some
+functions get called from the signal handlers or can write partial
+packets. we need to SIG_BLOCK/SIG_SETMASK those functions instead of
+SIG_BLOCK/SIG_UNBLOCK'ing them. furthermore, certain functions which
+write to the client can get called at any time. to avoid corruption,
+we need to make sure that these functions DO NOT touch any common-use
+buffers.
+
+signals that send data to the client and should block other signals
+(afp_dsi.c):
+       SIGALRM (tickle handler)
+       SIGHUP (attention) 
+       SIGTERM (attention)
+
+functions which need SIG_BLOCK/SIG_SETMASK: dsi_send, dsi_attention
+functions which can use SIG_BLOCK/SIG_UNBLOCK: dsi_read
+
+functions which need their own buffers: dsi_attention, dsi_tickle 
+
+
+PERFORMANCE TWEAKING:
+sending complete packets or the header and a partial packet to the
+client should always be handled by proto_send. for dsi_tcp.c,
+proto_send will coalesce the header and data by using writev if
+USE_WRITEV is defined. in addition, appleshare sessions often involve
+the sending and receiving of many small packets. as a consequence, i
+use TCP_NODELAY to speed up the turnaround time.
+
+because dsi_read can send incomplete packets, proto_send should not
+use the length parameter to specify the dsi_len field in the
+header. instead, anything that uses proto_send needs to specify
+dsi_len (in network byte order) before calling proto_send. the
+dsi_send() macro already does this. 
+
+functions that need to specify .dsi_len: dsi_readinit, dsi_cmdreply
+
+mmap doesn't actually help things that much, so i don't use it.
+
+to reduce the amount of tickles generated on a slow link, i actually
+turn off SIGALRM for the duration of a "known" large file transfer
+(i.e., dsi_read/write).
+
diff --git a/libatalk/dsi/dsi_attn.c b/libatalk/dsi/dsi_attn.c
new file mode 100644 (file)
index 0000000..1bbfd7e
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+
+#include <atalk/dsi.h>
+#include <atalk/afp.h>
+#include <netatalk/endian.h>
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+/* send an attention. this may get called at any time, so we can't use
+ * DSI buffers to send one. */
+int dsi_attention(DSI *dsi, AFPUserBytes flags)
+{
+  /* header + AFPUserBytes */
+  char block[DSI_BLOCKSIZ + sizeof(AFPUserBytes)];
+  sigset_t oldset;
+  u_int32_t len, nlen;
+  u_int16_t id;
+
+  id = htons(dsi_serverID(dsi));
+  flags = htons(flags);
+  len = MIN(sizeof(flags), dsi->attn_quantum);
+  nlen = htonl(len);
+
+  memset(block, 0, sizeof(block));
+  block[0] = DSIFL_REQUEST; /* sending a request */
+  block[1] = DSIFUNC_ATTN;  /* it's an attention */
+  memcpy(block + 2, &id, sizeof(id));
+  /* code = 0 */
+  memcpy(block + 8, &nlen, sizeof(nlen));
+  memcpy(block + 16, &flags, sizeof(flags));
+  /* reserved = 0 */
+
+  /* send an attention */
+  sigprocmask(SIG_BLOCK, &dsi->sigblockset, &oldset);
+  len = dsi_stream_write(dsi, block, DSI_BLOCKSIZ + len);
+  sigprocmask(SIG_SETMASK, &oldset, NULL);
+
+  return len;
+}
diff --git a/libatalk/dsi/dsi_close.c b/libatalk/dsi/dsi_close.c
new file mode 100644 (file)
index 0000000..3e7a2fb
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <atalk/dsi.h>
+#include <netatalk/endian.h>
+
+void dsi_close(DSI *dsi)
+{
+  /* server generated. need to set all the fields. */
+  dsi->header.dsi_flags = DSIFL_REQUEST;
+  dsi->header.dsi_command = DSIFUNC_CLOSE;
+  dsi->header.dsi_requestID = htons(dsi_serverID(dsi));
+  dsi->header.dsi_code = dsi->header.dsi_reserved = htonl(0);
+  dsi->cmdlen = 0; 
+
+  dsi_send(dsi);
+  dsi->proto_close(dsi);
+  free(dsi);
+}
diff --git a/libatalk/dsi/dsi_cmdreply.c b/libatalk/dsi/dsi_cmdreply.c
new file mode 100644 (file)
index 0000000..4459206
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <atalk/dsi.h>
+#include <netatalk/endian.h>
+
+/* this assumes that the reply follows right after the command, saving
+ * on a couple assignments. specifically, command, requestID, and
+ * reserved field are assumed to already be set. */ 
+int dsi_cmdreply(DSI *dsi, const int err)
+{
+  dsi->header.dsi_flags = DSIFL_REPLY;
+  /*dsi->header.dsi_command = DSIFUNC_CMD;*/
+  dsi->header.dsi_len = htonl(dsi->datalen);
+  dsi->header.dsi_code = htonl(err);
+
+  return dsi_stream_send(dsi, dsi->data, dsi->datalen);
+}
diff --git a/libatalk/dsi/dsi_getsess.c b/libatalk/dsi/dsi_getsess.c
new file mode 100644 (file)
index 0000000..d4c1c6f
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <syslog.h>
+
+#include <atalk/dsi.h>
+#include <atalk/server_child.h>
+
+static server_child *children = NULL;
+
+void dsi_kill(int sig)
+{
+  if (children)
+    server_child_kill(children, CHILD_DSIFORK, sig);
+}
+
+/* hand off the command. return child connection to the main program */
+DSI *dsi_getsession(DSI *dsi, server_child *serv_children, 
+                   const int tickleval)
+{
+  pid_t pid;
+  
+  /* do a couple things on first entry */
+  if (!dsi->inited) {
+    if (!(children = serv_children))
+      return NULL;
+    dsi->inited = 1;
+  }
+  
+  switch (pid = dsi->proto_open(dsi)) {
+  case -1:
+    /* if we fail, just return. it might work later */
+    syslog(LOG_ERR, "dsi_getsess: %m");
+    return dsi;
+
+  case 0: /* child. mostly handled below. */
+    dsi->child = 1;
+    break;
+
+  default: /* parent */
+    /* using SIGQUIT is hokey, but the child might not have
+     * re-established its signal handler for SIGTERM yet. */
+    if (server_child_add(children, CHILD_DSIFORK, pid) < 0) {
+      syslog(LOG_ERR, "dsi_getsess: %m");
+      dsi->header.dsi_flags = DSIFL_REPLY;
+      dsi->header.dsi_code = DSIERR_SERVBUSY;
+      dsi_send(dsi);
+      dsi->header.dsi_code = DSIERR_OK;
+      kill(pid, SIGQUIT);
+    }
+
+    dsi->proto_close(dsi);
+    return dsi;
+  }
+  
+  /* child: check number of open connections. this is one off the
+   * actual count. */
+  if ((children->count >= children->nsessions) &&
+      (dsi->header.dsi_command == DSIFUNC_OPEN)) {
+    syslog(LOG_INFO, "dsi_getsess: too many connections");
+    dsi->header.dsi_flags = DSIFL_REPLY;
+    dsi->header.dsi_code = DSIERR_TOOMANY;
+    dsi_send(dsi);
+    exit(1);
+  }
+
+  /* get rid of some stuff */
+  close(dsi->serversock);
+  server_child_free(children); 
+  children = NULL;
+
+  switch (dsi->header.dsi_command) {
+  case DSIFUNC_STAT: /* send off status and return */
+    {
+      /* OpenTransport 1.1.2 bug workaround: 
+       *
+       * OT code doesn't currently handle close sockets well. urk.
+       * the workaround: wait for the client to close its
+       * side. timeouts prevent indefinite resource use. 
+       */
+      
+      static struct timeval timeout = {120, 0};
+      fd_set readfds;
+      
+      dsi_getstatus(dsi);
+
+      FD_ZERO(&readfds);
+      FD_SET(dsi->socket, &readfds);
+      free(dsi);
+      select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);    
+      exit(0);
+    }
+    break;
+    
+  case DSIFUNC_OPEN: /* setup session */
+    /* set up the tickle timer */
+    dsi->timer.it_interval.tv_sec = dsi->timer.it_value.tv_sec = tickleval;
+    dsi->timer.it_interval.tv_usec = dsi->timer.it_value.tv_usec = 0;
+    signal(SIGPIPE, SIG_IGN); /* we catch these ourselves */
+    dsi_opensession(dsi);
+    return dsi;
+    break;
+
+  default: /* just close */
+    syslog(LOG_INFO, "DSIUnknown %d", dsi->header.dsi_command);
+    dsi->proto_close(dsi);
+    exit(1);
+  }
+}
diff --git a/libatalk/dsi/dsi_getstat.c b/libatalk/dsi/dsi_getstat.c
new file mode 100644 (file)
index 0000000..268caf4
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <atalk/dsi.h>
+#include <netatalk/endian.h>
+
+/* return the status and then delete the connection. most of the
+ * fields are already set. */
+void dsi_getstatus(DSI *dsi)
+{
+  dsi->header.dsi_flags = DSIFL_REPLY;
+  /*dsi->header.dsi_command = DSIFUNC_STAT;*/
+  
+  memcpy(dsi->commands, dsi->status, dsi->statuslen);
+  dsi->cmdlen = dsi->statuslen; 
+  dsi_send(dsi);
+}
diff --git a/libatalk/dsi/dsi_init.c b/libatalk/dsi/dsi_init.c
new file mode 100644 (file)
index 0000000..2fcc178
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <atalk/dsi.h>
+#include "dsi_private.h"
+
+DSI *dsi_init(const dsi_proto protocol, const char *program, 
+             const char *hostname, const char *address,
+             const int port, const int proxy, const u_int32_t quantum)
+{
+    DSI                *dsi;
+
+    if ((dsi = (DSI *) calloc(1, sizeof(DSI))) == NULL) {
+      return( NULL );
+    }
+    dsi->attn_quantum = DSI_DEFQUANT; /* default quantum size */
+    dsi->server_quantum = quantum; /* default server quantum */
+    dsi->program = program;
+
+    /* signals to block. we actually disable timers for "known" 
+     * large transfers (i.e., dsi_read/write). */
+    sigemptyset(&dsi->sigblockset);
+    sigaddset(&dsi->sigblockset, SIGTERM);
+    sigaddset(&dsi->sigblockset, SIGHUP);
+    sigaddset(&dsi->sigblockset, SIGALRM);
+    
+    switch (protocol) {
+      /* currently the only transport protocol that exists for dsi */
+    case DSI_TCPIP: 
+      if (!dsi_tcp_init(dsi, hostname, address, port, proxy)) {
+       free(dsi);
+       dsi = NULL;
+      }
+      break;
+
+    default: /* unknown protocol */
+      free(dsi);
+      dsi = NULL;
+      break;
+    }
+
+    return dsi;
+}
+
+void dsi_setstatus(DSI *dsi, u_int8_t *status, const int slen)
+{
+    dsi->status = status;
+    dsi->statuslen = slen;
+}
diff --git a/libatalk/dsi/dsi_opensess.c b/libatalk/dsi/dsi_opensess.c
new file mode 100644 (file)
index 0000000..8f16b13
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <atalk/dsi.h>
+
+/* OpenSession. set up the connection */
+void dsi_opensession(DSI *dsi)
+{
+  u_int32_t i = 0; /* this serves double duty. it must be 4-bytes long */
+
+  /* parse options */
+  while (i < dsi->cmdlen) {
+    switch (dsi->commands[i++]) {
+    case DSIOPT_ATTNQUANT:
+      memcpy(&dsi->attn_quantum, dsi->commands + i + 1, dsi->commands[i]);
+      dsi->attn_quantum = ntohl(dsi->attn_quantum);
+
+    case DSIOPT_SERVQUANT: /* just ignore these */
+    default:
+      i += dsi->commands[i] + 1; /* forward past length tag + length */
+      break;
+    }
+  }
+
+  /* let the client know the server quantum. we don't use the
+   * max server quantum due to a bug in appleshare client 3.8.6. */
+  dsi->header.dsi_flags = DSIFL_REPLY;
+  /* dsi->header.dsi_command = DSIFUNC_OPEN;*/
+  dsi->cmdlen = 2 + sizeof(i); /* length of data. dsi_send uses it. */
+  dsi->commands[0] = DSIOPT_SERVQUANT;
+  dsi->commands[1] = sizeof(i);
+  i = htonl(( dsi->server_quantum < DSI_SERVQUANT_MIN || 
+             dsi->server_quantum > DSI_SERVQUANT_MAX ) ? 
+           DSI_SERVQUANT_DEF : dsi->server_quantum);
+  memcpy(dsi->commands + 2, &i, sizeof(i));
+
+  dsi_send(dsi);
+}
diff --git a/libatalk/dsi/dsi_private.h b/libatalk/dsi/dsi_private.h
new file mode 100644 (file)
index 0000000..70509f7
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ */
+
+#ifndef _DSI_PRIVATE_H
+#define _DSI_PRIVATE_H 1
+
+/* this header handles interactions between protocol-specific code and
+ * dsi initialization. only dsi_init.c and dsi_<proto>.c should
+ * include it.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <netatalk/endian.h>
+
+extern int dsi_tcp_init __P((DSI *, const char * /*host*/, 
+                            const char * /*address*/,
+                            const u_int16_t /*port*/,
+                            const int /*proxy*/));
+
+#endif /* _DSI_PRIVATE_H */
diff --git a/libatalk/dsi/dsi_read.c b/libatalk/dsi/dsi_read.c
new file mode 100644 (file)
index 0000000..ca922e7
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <atalk/dsi.h>
+
+#ifndef min
+#define min(a,b)   ((a) < (b) ? (a) : (b))
+#endif
+
+/* streaming i/o for afp_read. this is all from the perspective of the
+ * client. it basically does the reverse of dsi_write. on first entry,
+ * it will send off the header plus whatever is in its command
+ * buffer. it returns the amount of stuff still to be read
+ * (constrained by the buffer size). */
+ssize_t dsi_readinit(DSI *dsi, void *buf, const size_t buflen,
+                   const size_t size, const int err)
+{
+  const struct itimerval none = {{0, 0}, {0, 0}};
+
+  dsi->noreply = 1; /* we will handle our own replies */
+  dsi->header.dsi_flags = DSIFL_REPLY;
+  /*dsi->header.dsi_command = DSIFUNC_CMD;*/
+  dsi->header.dsi_len = htonl(size);
+  dsi->header.dsi_code = htonl(err);
+
+  sigprocmask(SIG_BLOCK, &dsi->sigblockset, NULL);
+  setitimer(ITIMER_REAL, &none, &dsi->savetimer);
+  if (dsi_stream_send(dsi, buf, buflen)) {
+    dsi->datasize = size - buflen;
+    return min(dsi->datasize, buflen);
+  }
+
+  return -1; /* error */
+}
+
+void dsi_readdone(DSI *dsi)
+{
+  setitimer(ITIMER_REAL, &dsi->savetimer, NULL);
+  sigprocmask(SIG_UNBLOCK, &dsi->sigblockset, NULL);
+}
+
+/* send off the data */
+ssize_t dsi_read(DSI *dsi, void *buf, const size_t buflen)
+{
+  size_t len = dsi_stream_write(dsi, buf, buflen);
+
+  if (len == buflen) {
+    dsi->datasize -= len;
+    return min(dsi->datasize, buflen);
+  }
+
+  return -1;
+}
diff --git a/libatalk/dsi/dsi_stream.c b/libatalk/dsi/dsi_stream.c
new file mode 100644 (file)
index 0000000..5b275fa
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 1998 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ *
+ * this file provides the following functions:
+ * dsi_stream_write:    just write a bunch of bytes.
+ * dsi_stream_read:     just read a bunch of bytes.
+ * dsi_stream_send:     send a DSI header + data.
+ * dsi_stream_receive:  read a DSI header + data.
+ */
+
+#define USE_WRITEV
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#ifdef USE_WRITEV
+#include <sys/uio.h>
+#endif
+#include <syslog.h>
+
+#include <atalk/dsi.h>
+#include <netatalk/endian.h>
+
+#define min(a,b)  ((a) < (b) ? (a) : (b))
+
+
+/* write raw data. return actual bytes read. checks against EINTR
+ * aren't necessary if all of the signals have SA_RESTART
+ * specified. */
+size_t dsi_stream_write(DSI *dsi, void *data, const size_t length)
+{
+  size_t written;
+  ssize_t len;
+
+  written = 0;
+  while (written < length) {
+    if (((len = write(dsi->socket, (u_int8_t *) data + written,
+                     length - written)) == -1 && errno == EINTR) ||
+       !len)
+      continue;
+
+    if (len < 0) {
+      syslog(LOG_ERR, "dsi_stream_write: %m");
+      break;
+    }
+    
+    written += len;
+  }
+
+  dsi->write_count += written;
+  return written;
+}
+
+/* read raw data. return actual bytes read. this will wait until 
+ * it gets length bytes */
+size_t dsi_stream_read(DSI *dsi, void *data, const size_t length)
+{
+  size_t stored;
+  ssize_t len;
+  
+  stored = 0;
+  while (stored < length) {
+    if ((len = read(dsi->socket, (u_int8_t *) data + stored, 
+                   length - stored)) == -1 && errno == EINTR)
+      continue;
+
+    if (len > 0)
+      stored += len;
+    else {/* eof or error */
+      syslog(LOG_ERR, "dsi_stream_read(%d): %m", len);
+      break;
+    }
+  }
+
+  dsi->read_count += stored;
+  return stored;
+}
+
+
+/* write data. 0 on failure. this assumes that dsi_len will never
+ * cause an overflow in the data buffer. */
+int dsi_stream_send(DSI *dsi, void *buf, size_t length)
+{
+  char block[DSI_BLOCKSIZ];
+  sigset_t oldset;
+#ifdef USE_WRITEV
+  struct iovec iov[2];
+  size_t  towrite;
+  ssize_t len;
+#endif
+
+  block[0] = dsi->header.dsi_flags;
+  block[1] = dsi->header.dsi_command;
+  memcpy(block + 2, &dsi->header.dsi_requestID, 
+        sizeof(dsi->header.dsi_requestID));
+  memcpy(block + 4, &dsi->header.dsi_code, sizeof(dsi->header.dsi_code));
+  memcpy(block + 8, &dsi->header.dsi_len, sizeof(dsi->header.dsi_len));
+  memcpy(block + 12, &dsi->header.dsi_reserved,
+        sizeof(dsi->header.dsi_reserved));
+
+  /* block signals */
+  sigprocmask(SIG_BLOCK, &dsi->sigblockset, &oldset);
+
+  if (!length) { /* just write the header */
+    length = (dsi_stream_write(dsi, block, sizeof(block)) == sizeof(block));
+    sigprocmask(SIG_SETMASK, &oldset, NULL);
+    return length; /* really 0 on failure, 1 on success */
+  }
+  
+#ifdef USE_WRITEV
+  iov[0].iov_base = block;
+  iov[0].iov_len = sizeof(block);
+  iov[1].iov_base = buf;
+  iov[1].iov_len = length;
+  
+  towrite = sizeof(block) + length;
+  dsi->write_count += towrite;
+  while (towrite > 0) {
+    if (((len = writev(dsi->socket, iov, 2)) == -1 && errno == EINTR) || 
+       !len)
+      continue;
+    
+    if (len == towrite) /* wrote everything out */
+      break;
+    else if (len < 0) { /* error */
+      syslog(LOG_ERR, "dsi_stream_send: %m");
+      sigprocmask(SIG_SETMASK, &oldset, NULL);
+      return 0;
+    }
+    
+    towrite -= len;
+    if (towrite > length) { /* skip part of header */
+      iov[0].iov_base = (char *) iov[0].iov_base + len;
+      iov[0].iov_len -= len;
+    } else { /* skip to data */
+      if (iov[0].iov_len) {
+       len -= iov[0].iov_len;
+       iov[0].iov_len = 0;
+      }
+      iov[1].iov_base = (char *) iov[1].iov_base + len;
+      iov[1].iov_len -= len;
+    }
+  }
+  
+#else
+  /* write the header then data */
+  if ((dsi_stream_write(dsi, block, sizeof(block)) != sizeof(block)) ||
+      (dsi_stream_write(dsi, buf, length) != length)) {
+    sigprocmask(SIG_SETMASK, &oldset, NULL);
+    return 0;
+  }
+#endif
+
+  sigprocmask(SIG_SETMASK, &oldset, NULL);
+  return 1;
+}
+
+
+/* read data. function on success. 0 on failure. data length gets
+ * stored in length variable. this should really use size_t's, but
+ * that would require changes elsewhere. */
+int dsi_stream_receive(DSI *dsi, void *buf, const int ilength,
+                      int *rlength)
+{
+  char block[DSI_BLOCKSIZ];
+
+  /* read in the header */
+  if (dsi_stream_read(dsi, block, sizeof(block)) != sizeof(block)) 
+    return 0;
+
+  dsi->header.dsi_flags = block[0];
+  dsi->header.dsi_command = block[1];
+  memcpy(&dsi->header.dsi_requestID, block + 2, 
+        sizeof(dsi->header.dsi_requestID));
+  memcpy(&dsi->header.dsi_code, block + 4, sizeof(dsi->header.dsi_code));
+  memcpy(&dsi->header.dsi_len, block + 8, sizeof(dsi->header.dsi_len));
+  memcpy(&dsi->header.dsi_reserved, block + 12,
+        sizeof(dsi->header.dsi_reserved));
+  dsi->clientID = ntohs(dsi->header.dsi_requestID);
+  
+  /* make sure we don't over-write our buffers. */
+  *rlength = min(ntohl(dsi->header.dsi_len), ilength);
+  if (dsi_stream_read(dsi, buf, *rlength) != *rlength) 
+    return 0;
+
+  return block[1];
+}
diff --git a/libatalk/dsi/dsi_tcp.c b/libatalk/dsi/dsi_tcp.c
new file mode 100644 (file)
index 0000000..39c7b6a
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 1997, 1998 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ *
+ * this provides both proto_open() and proto_close() to account for
+ * protocol specific initialization and shutdown procedures. all the
+ * read/write stuff is done in dsi_stream.c.  */
+
+#define USE_TCP_NODELAY
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <netinet/tcp.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <signal.h>
+#include <syslog.h>
+
+#ifdef __svr4__
+#include <sys/sockio.h>
+#endif
+
+#ifdef TCPWRAP
+#include <tcpd.h>
+int allow_severity = LOG_INFO;
+int deny_severity = LOG_WARNING;
+#endif
+
+#include <atalk/dsi.h>
+#include <atalk/compat.h>
+#include <atalk/util.h>
+#include <netatalk/endian.h>
+#include "dsi_private.h"
+
+#define min(a,b)  ((a) < (b) ? (a) : (b))
+
+#ifndef DSI_TCPMAXPEND
+#define DSI_TCPMAXPEND      20       /* max # of pending connections */
+#endif
+
+#ifndef DSI_TCPTIMEOUT
+#define DSI_TCPTIMEOUT      120     /* timeout in seconds for connections */
+#endif
+
+
+/* FIXME/SOCKLEN_T: socklen_t is a unix98 feature. */
+#ifndef SOCKLEN_T
+#define SOCKLEN_T unsigned int
+#endif
+
+static void dsi_tcp_close(DSI *dsi)
+{
+  if (dsi->socket == -1)
+    return;
+
+  close(dsi->socket);
+  dsi->socket = -1;
+}
+
+/* alarm handler for tcp_open */
+static void timeout_handler()
+{
+  syslog(LOG_ERR, "dsi_tcp_open: connection timed out");
+  exit(1);
+}
+
+/* accept the socket and do a little sanity checking */
+static int dsi_tcp_open(DSI *dsi)
+{
+  pid_t pid;
+  SOCKLEN_T len;
+
+  len = sizeof(dsi->client);
+  dsi->socket = accept(dsi->serversock, (struct sockaddr *) &dsi->client,
+                      &len);
+
+#ifdef TCPWRAP
+  {
+    struct request_info req;
+    request_init(&req, RQ_DAEMON, dsi->program, RQ_FILE, dsi->socket, NULL);
+    fromhost(&req);
+    if (!hosts_access(&req)) {
+      syslog(deny_severity, "refused connect from %s", eval_client(&req));
+      close(dsi->socket);
+      errno = ECONNREFUSED;
+      dsi->socket = -1;
+    }
+  }
+#endif
+
+  if (dsi->socket < 0)
+    return -1;
+
+  if ((pid = fork()) == 0) { /* child */
+    static const struct itimerval timer = {{0, 0}, {DSI_TCPTIMEOUT, 0}};
+    struct sigaction newact, oldact;
+    u_int8_t block[DSI_BLOCKSIZ];
+    size_t stored;
+    
+    /* reset a couple signals */
+    signal(SIGTERM, SIG_DFL); 
+    signal(SIGHUP, SIG_DFL);
+
+    /* install an alarm to deal with non-responsive connections */
+    memset(&newact, 0, sizeof(newact));
+    newact.sa_handler = timeout_handler;
+    if ((sigaction(SIGALRM, &newact, &oldact) < 0) ||
+        (setitimer(ITIMER_REAL, &timer, NULL) < 0)) {
+       syslog(LOG_ERR, "dsi_tcp_open: %m");
+       exit(1);
+    }
+    
+    /* read in commands. this is similar to dsi_receive except
+     * for the fact that we do some sanity checking to prevent
+     * delinquent connections from causing mischief. */
+    
+    /* read in the first two bytes */
+    dsi_stream_read(dsi, block, 2);
+    if ((block[0] > DSIFL_MAX) || (block[1] > DSIFUNC_MAX)) {
+      syslog(LOG_ERR, "dsi_tcp_open: invalid header");
+      exit(1);
+    }      
+    
+    /* read in the rest of the header */
+    stored = 2;
+    while (stored < DSI_BLOCKSIZ) {
+      len = dsi_stream_read(dsi, block + stored, sizeof(block) - stored);
+      if (len > 0)
+       stored += len;
+      else {
+       syslog(LOG_ERR, "dsi_tcp_open: stream_read: %m");
+       exit(1);
+      }
+    }
+    
+    dsi->header.dsi_flags = block[0];
+    dsi->header.dsi_command = block[1];
+    memcpy(&dsi->header.dsi_requestID, block + 2, 
+          sizeof(dsi->header.dsi_requestID));
+    memcpy(&dsi->header.dsi_code, block + 4, sizeof(dsi->header.dsi_code));
+    memcpy(&dsi->header.dsi_len, block + 8, sizeof(dsi->header.dsi_len));
+    memcpy(&dsi->header.dsi_reserved, block + 12,
+          sizeof(dsi->header.dsi_reserved));
+    dsi->clientID = ntohs(dsi->header.dsi_requestID);
+    
+    /* make sure we don't over-write our buffers. */
+    dsi->cmdlen = min(ntohl(dsi->header.dsi_len), DSI_CMDSIZ);
+    
+    stored = 0;
+    while (stored < dsi->cmdlen) {
+      len = dsi_stream_read(dsi, dsi->commands + stored, dsi->cmdlen - stored);
+      if (len > 0)
+       stored += len;
+      else {
+       syslog(LOG_ERR, "dsi_tcp_open: stream_read: %m");
+       exit(1);
+      }
+    }
+    
+    /* restore signal */
+    sigaction(SIGALRM, &oldact, NULL);
+
+    syslog(LOG_INFO,"ASIP session:%u(%d) from %s:%u(%d)", 
+          ntohs(dsi->server.sin_port), dsi->serversock, 
+          inet_ntoa(dsi->client.sin_addr), ntohs(dsi->client.sin_port),
+          dsi->socket);
+  }
+  
+  /* send back our pid */
+  return pid;
+}
+
+/* this needs to accept passed in addresses */
+int dsi_tcp_init(DSI *dsi, const char *hostname, const char *address,
+                const u_int16_t ipport, const int proxy)
+{
+  struct servent     *service;
+  struct hostent     *host;
+  int                port;
+
+  dsi->protocol = DSI_TCPIP;
+
+  /* create a socket */
+  if (proxy)
+    dsi->serversock = -1;
+  else if ((dsi->serversock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
+    return 0;
+      
+  /* find port */
+  if (ipport)
+    port = htons(ipport);
+  else if ((service = getservbyname("afpovertcp", "tcp")))
+    port = service->s_port;
+  else
+    port = htons(DSI_AFPOVERTCP_PORT);
+
+  /* find address */
+  if (!address) 
+    dsi->server.sin_addr.s_addr = htonl(INADDR_ANY);
+  else if (inet_aton(address, &dsi->server.sin_addr) == 0) {
+    syslog(LOG_INFO, "dsi_tcp: invalid address (%s)", address);
+    return 0;
+  }
+
+  dsi->server.sin_family = AF_INET;
+  dsi->server.sin_port = port;
+
+  if (!proxy) {
+    /* this deals w/ quick close/opens */    
+#ifdef SO_REUSEADDR
+    port = 1;
+    setsockopt(dsi->serversock, SOL_SOCKET, SO_REUSEADDR, &port, sizeof(port));
+#endif
+
+#ifdef USE_TCP_NODELAY 
+#ifndef SOL_TCP
+#define SOL_TCP IPPROTO_TCP
+#endif
+    port = 1;
+    setsockopt(dsi->serversock, SOL_TCP, TCP_NODELAY, &port, sizeof(port));
+#endif
+
+    /* now, bind the socket and set it up for listening */
+    if ((bind(dsi->serversock, (struct sockaddr *) &dsi->server, 
+             sizeof(dsi->server)) < 0) || 
+       (listen(dsi->serversock, DSI_TCPMAXPEND) < 0)) {
+      close(dsi->serversock);
+      return 0;
+    }
+  }
+
+  /* get real address for GetStatus. we'll go through the list of 
+   * interfaces if necessary. */
+  if (!address) {
+    if ((host = gethostbyname(hostname))) /* we can resolve the name */
+      dsi->server.sin_addr.s_addr = ((struct in_addr *) host->h_addr)->s_addr;
+    else {
+      char **start, **list;
+      struct ifreq ifr;
+
+      /* get it from the interface list */
+      start = list = getifacelist();
+      while (list && *list) {
+       strncpy(ifr.ifr_name, *list, sizeof(ifr.ifr_name));
+       list++;
+
+#ifndef IFF_SLAVE
+#define IFF_SLAVE 0
+#endif
+       if (ioctl(dsi->serversock, SIOCGIFFLAGS, &ifr) < 0)
+         continue;
+
+       if (ifr.ifr_flags & (IFF_LOOPBACK | IFF_POINTOPOINT | IFF_SLAVE))
+         continue;
+
+       if ((ifr.ifr_flags & IFF_UP) == 0)
+         continue;
+
+       if (ioctl(dsi->serversock, SIOCGIFADDR, &ifr) < 0)
+         continue;
+       
+       dsi->server.sin_addr.s_addr = 
+         ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr;
+       syslog(LOG_INFO, "dsi_tcp: Can't resolve hostname (%s).\n"
+              "%s on interface %s will be used instead.", hostname,
+              inet_ntoa(dsi->server.sin_addr), ifr.ifr_name);
+       goto iflist_done;
+
+      }
+      syslog(LOG_INFO, "dsi_tcp (Chooser will not select afp/tcp)\n\
+Check to make sure %s is in /etc/hosts and the correct domain is in\n\
+/etc/resolv.conf: %m", hostname);
+
+iflist_done:
+      if (start)
+       freeifacelist(start);
+    }
+  }
+
+  /* everything's set up. now point protocol specific functions to 
+   * tcp versions */
+  dsi->proto_open = dsi_tcp_open;
+  dsi->proto_close = dsi_tcp_close;
+  return 1;
+
+}
+
diff --git a/libatalk/dsi/dsi_tickle.c b/libatalk/dsi/dsi_tickle.c
new file mode 100644 (file)
index 0000000..c0ba655
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <string.h>
+#include <signal.h>
+
+#include <atalk/dsi.h>
+#include <netatalk/endian.h>
+
+/* server generated tickles. as this is only called by the tickle handler,
+ * we don't need to block signals. well, actually, we might get it during
+ * a SIGHUP. */
+void dsi_tickle(DSI *dsi)
+{
+  char block[DSI_BLOCKSIZ];
+  sigset_t oldset;
+  u_int16_t id;
+
+  id = htons(dsi_serverID(dsi));
+
+  memset(block, 0, sizeof(block));
+  block[0] = DSIFL_REQUEST;
+  block[1] = DSIFUNC_TICKLE;
+  memcpy(block + 2, &id, sizeof(id));
+  /* code = len = reserved = 0 */
+
+  sigprocmask(SIG_BLOCK, &dsi->sigblockset, &oldset);
+  dsi_stream_write(dsi, block, DSI_BLOCKSIZ);
+  sigprocmask(SIG_SETMASK, &oldset, NULL);
+}
+
diff --git a/libatalk/dsi/dsi_write.c b/libatalk/dsi/dsi_write.c
new file mode 100644 (file)
index 0000000..0acad6c
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ *
+ * 7 Oct 1997 added checks for 0 data.
+ */
+
+/* this streams writes */
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <atalk/dsi.h>
+#include <netatalk/endian.h>
+
+#ifndef MIN
+#define MIN(a,b)     ((a) < (b) ? (a) : (b))
+#endif
+
+/* initialize relevant things for dsi_write. this returns the amount
+ * of data in the data buffer. the interface has been reworked to allow
+ * for arbitrary buffers. */
+size_t dsi_writeinit(DSI *dsi, void *buf, const size_t buflen)
+{
+  const struct itimerval none = {{0, 0}, {0, 0}};
+  size_t len, header;
+
+  /* figure out how much data we have. do a couple checks for 0 
+   * data */
+  header = ntohl(dsi->header.dsi_code);
+  dsi->datasize = header ? ntohl(dsi->header.dsi_len) - header : 0;
+  if (dsi->datasize > 0) {
+    len = MIN(sizeof(dsi->commands) - header, dsi->datasize);
+    
+    /* write last part of command buffer into buf */
+    memcpy(buf, dsi->commands + header, len);
+    
+    /* recalculate remaining data */
+    dsi->datasize -= len;
+  } else
+    len = 0;
+
+  /* deal with signals. i'm doing it this way to ensure that we don't
+   * get confused if a writeflush on zero remaining data is, for some
+   * reason, needed. */
+  sigprocmask(SIG_BLOCK, &dsi->sigblockset, NULL);
+  setitimer(ITIMER_REAL, &none, &dsi->savetimer);
+  return len;
+}
+
+/* fill up buf and then return. this should be called repeatedly
+ * until all the data has been read. i block alarm processing 
+ * during the transfer to avoid sending unnecessary tickles. */
+size_t dsi_write(DSI *dsi, void *buf, const size_t buflen)
+{
+  size_t length;
+
+  if (((length = MIN(buflen, dsi->datasize)) > 0) &&
+      ((length = dsi_stream_read(dsi, buf, length)) > 0)) {
+    dsi->datasize -= length;
+    return length;
+  }
+
+  setitimer(ITIMER_REAL, &dsi->savetimer, NULL);
+  sigprocmask(SIG_UNBLOCK, &dsi->sigblockset, NULL);
+  return 0;
+}
+
+/* flush any unread buffers. */
+void dsi_writeflush(DSI *dsi)
+{
+  size_t length;
+
+  while (dsi->datasize > 0) { 
+    length = dsi_stream_read(dsi, dsi->data,
+                            MIN(sizeof(dsi->data), dsi->datasize));
+    if (length > 0)
+      dsi->datasize -= length;
+    else
+      break;
+  }
+
+  setitimer(ITIMER_REAL, &dsi->savetimer, NULL);
+  sigprocmask(SIG_UNBLOCK, &dsi->sigblockset, NULL);
+}
diff --git a/libatalk/nbp/Makefile b/libatalk/nbp/Makefile
new file mode 100644 (file)
index 0000000..95befb5
--- /dev/null
@@ -0,0 +1,56 @@
+SRC = nbp_util.c nbp_lkup.c nbp_rgstr.c nbp_unrgstr.c
+OBJ = nbp_util.o nbp_lkup.o nbp_rgstr.o nbp_unrgstr.o
+
+INCPATH=       -I../../include
+CFLAGS=        ${DEFS} ${OPTOPTS} ${INCPATH}
+TAGSFILE=      tags
+CC=    cc
+
+all : profiled nbplib
+
+profiled:
+       -mkdir profiled
+
+nbplib nbplib_p : ${OBJ}
+       @echo "building profiled nbplib"
+       @cd profiled; ar cru ../nbplib_p ${OBJ}
+       @echo "building normal nbplib"
+       @ar cru nbplib ${OBJ}
+
+.c.o :
+       ${CC} -p ${CFLAGS} -c $*.c
+       -ld -r -o $*.o- $*.o
+       mv $*.o- profiled/$*.o
+       ${CC} ${CFLAGS} -c $*.c
+       -ld -r -o $*.o- $*.o
+       mv $*.o- $*.o
+
+clean :
+       rm -f *.o profiled/*.o *.bak *[Ee]rrs tags
+       rm -f nbplib nbplib_p
+
+tags : ${SRC}
+       cwd=`pwd`; \
+       for i in ${SRC}; do \
+           ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+       done
+
+depend :
+       for i in ${SRC} ; do \
+           ${CC} -M ${DEFS} ${INCPATH} $$i | \
+           awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+               else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+               else rec = rec " " $$2 } } \
+               END { print rec } ' >> makedep; done
+       sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+       cat makedep >> Makefile.tmp
+       rm makedep
+       echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+       echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+       echo '# see make depend above' >> Makefile.tmp
+       rm -f Makefile.bak
+       cp Makefile Makefile.bak
+       mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
diff --git a/libatalk/nbp/nbp_conf.h b/libatalk/nbp/nbp_conf.h
new file mode 100644 (file)
index 0000000..4ea004f
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#ifndef NBP_CONF_H
+#define NBP_CONF_H 1
+
+#include <sys/cdefs.h>
+#include <atalk/nbp.h>
+
+extern char            nbp_send[ 1024 ];
+extern char            nbp_recv[ 1024 ];
+extern u_char          nbp_port;
+extern unsigned char    nbp_id;
+
+
+int nbp_parse __P((char *, struct nbpnve *, int));
+int nbp_match __P((struct nbpnve *, struct nbpnve *, int));
+
+#endif
diff --git a/libatalk/nbp/nbp_lkup.c b/libatalk/nbp/nbp_lkup.c
new file mode 100644 (file)
index 0000000..d3327a4
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 1990,1997 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/signal.h>
+#include <sys/time.h>
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <netatalk/ddp.h>
+#include <atalk/compat.h>
+#include <atalk/nbp.h>
+#include <atalk/netddp.h>
+#include <atalk/ddp.h>
+
+#include <netdb.h>
+#include <errno.h>
+
+#include  "nbp_conf.h"
+
+/* FIXME/SOCKLEN_T: socklen_t is a unix98 feature. */
+#ifndef SOCKLEN_T
+#define SOCKLEN_T unsigned int
+#endif
+
+int nbp_lookup( obj, type, zone, nn, nncnt, ataddr )
+    const char         *obj, *type, *zone;
+    struct nbpnve      *nn;
+    int                        nncnt;
+    const struct at_addr *ataddr;
+{
+    struct sockaddr_at addr, from;
+    struct timeval     tv, tv_begin, tv_end;
+    fd_set             fds;
+    struct nbpnve      nve;
+    struct nbphdr      nh;
+    struct nbptuple    nt;
+    struct servent     *se;
+    char               *data = nbp_send;
+    SOCKLEN_T          namelen;
+    int                        s, cnt, tries, sc, cc, i, c;
+
+    memset(&addr, 0, sizeof(addr));
+    memset(&from, 0, sizeof(from));
+    if (ataddr) 
+      memcpy(&addr.sat_addr, ataddr, sizeof(struct at_addr));
+    if ((s = netddp_open(&addr, &from)) < 0)
+      return -1;
+
+    *data++ = DDPTYPE_NBP;
+#ifdef MACOSX_SERVER
+    nh.nh_op = from.sat_addr.s_node ? NBPOP_BRRQ : NBPOP_LKUP;
+#else
+    nh.nh_op = NBPOP_BRRQ;
+#endif
+
+    nh.nh_cnt = 1;
+    nh.nh_id = ++nbp_id;
+    memcpy( data, &nh, SZ_NBPHDR );
+    data += SZ_NBPHDR;
+
+    memset(&nt, 0, sizeof(nt));
+    nt.nt_net = addr.sat_addr.s_net;
+    nt.nt_node = addr.sat_addr.s_node;
+    nt.nt_port = addr.sat_port;
+
+    memcpy( data, &nt, SZ_NBPTUPLE);
+    data += SZ_NBPTUPLE;
+
+    if ( obj ) {
+       if (( cc = strlen( obj )) > NBPSTRLEN ) goto lookup_err;
+       *data++ = cc;
+       memcpy( data, obj, cc );
+       data += cc;
+    } else {
+       *data++ = 1;
+       *data++ = '='; /* match anything */
+    }
+
+    if ( type ) {
+       if (( cc = strlen( type )) > NBPSTRLEN ) goto lookup_err;
+       *data++ = cc;
+       memcpy( data, type, cc );
+       data += cc;
+    } else {
+       *data++ = 1;
+       *data++ = '='; /* match anything */
+    }
+
+    if ( zone ) {
+       if (( cc = strlen( zone )) > NBPSTRLEN ) goto lookup_err;
+       *data++ = cc;
+       memcpy( data, zone, cc );
+       data += cc;
+    } else {
+       *data++ = 1;
+       *data++ = '*'; /* default zone */
+    }
+
+    if ( nbp_port == 0 ) {
+      if (( se = getservbyname( "nbp", "ddp" )) == NULL ) {
+                   nbp_port = 2;
+      } else {
+                   nbp_port = ntohs( se->s_port );
+      }
+    }
+
+#ifdef MACOSX_SERVER
+    if (from.sat_addr.s_node) {
+      memcpy(&addr.sat_addr, &from.sat_addr, sizeof(addr.sat_addr));
+    } else {
+      addr.sat_addr.s_net = ATADDR_ANYNET;
+      addr.sat_addr.s_node = ATADDR_BCAST;
+    }
+#endif
+    addr.sat_port = nbp_port;
+
+    cnt = 0;
+    tries = 3;
+    sc = data - nbp_send;
+    while ( tries > 0 ) {
+       if ( netddp_sendto( s, nbp_send, sc, 0, (struct sockaddr *)&addr,
+               sizeof( struct sockaddr_at )) < 0 ) {
+           goto lookup_err;
+       }
+
+       tv.tv_sec = 2L;
+       tv.tv_usec = 0;
+
+       for (;;) {
+           FD_ZERO( &fds );
+           FD_SET( s, &fds );
+           if ( gettimeofday( &tv_begin, NULL ) < 0 ) {
+               goto lookup_err;
+           }
+           if (( c = select( s + 1, &fds, NULL, NULL, &tv )) < 0 ) {
+               goto lookup_err;
+           }
+           if ( c == 0 || FD_ISSET( s, &fds ) == 0 ) {
+               break;
+           }
+           if ( gettimeofday( &tv_end, NULL ) < 0 ) {
+               goto lookup_err;
+           }
+           if ( tv_begin.tv_usec > tv_end.tv_sec ) {
+               tv_end.tv_usec += 1000000;
+               tv_end.tv_sec -= 1;
+           }
+           if (( tv.tv_usec -= ( tv_end.tv_usec - tv_begin.tv_usec )) < 0 ) {
+               tv.tv_usec += 1000000;
+               tv.tv_sec -= 1;
+           }
+           if (( tv.tv_sec -= ( tv_end.tv_sec - tv_begin.tv_sec )) < 0 ) {
+               break;
+           }
+
+           namelen = sizeof( struct sockaddr_at );
+           if (( cc = netddp_recvfrom( s, nbp_recv, sizeof( nbp_recv ), 0,
+                   (struct sockaddr *)&from, &namelen )) < 0 ) {
+               goto lookup_err;
+           }
+
+           data = nbp_recv;
+           if ( *data++ != DDPTYPE_NBP ) {
+               continue;
+           }
+           cc--;
+
+           memcpy( &nh, data, SZ_NBPHDR );
+           data += SZ_NBPHDR;
+           if ( nh.nh_op != NBPOP_LKUPREPLY ) {
+               continue;
+           }
+           cc -= SZ_NBPHDR;
+
+           while (( i = nbp_parse( data, &nve, cc )) >= 0 ) {
+               data += cc - i;
+               cc = i;
+               /*
+                * Check to see if nve is already in nn. If not,
+                * put it in, and increment cnt.
+                */
+               for ( i = 0; i < cnt; i++ ) {
+                   if ( nbp_match( &nve, &nn[ i ],
+                           NBPMATCH_NOZONE|NBPMATCH_NOGLOB )) {
+                       break;
+                   }
+               }
+               if ( i == cnt ) {
+                   nn[ cnt++ ] = nve;
+               }
+               if ( cnt == nncnt ) {
+                   tries = 0;
+                   break;
+               }
+           }
+           if ( cnt == nncnt ) {
+               tries = 0;
+               break;
+           }
+       }
+       tries--;
+    }
+
+    netddp_close(s);
+    errno = 0;
+    return( cnt );
+
+lookup_err:
+    netddp_close(s);
+    return -1;
+}
diff --git a/libatalk/nbp/nbp_rgstr.c b/libatalk/nbp/nbp_rgstr.c
new file mode 100644 (file)
index 0000000..6d82069
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/signal.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#include <netatalk/at.h>
+#include <netatalk/endian.h>
+#include <atalk/nbp.h>
+#include <atalk/ddp.h>
+#include <atalk/netddp.h>
+
+#include <netdb.h>
+#include  "nbp_conf.h"
+
+/* FIXME/SOCKLEN_T: socklen_t is a unix98 feature. */
+#ifndef SOCKLEN_T
+#define SOCKLEN_T unsigned int
+#endif
+
+int nbp_rgstr( sat, obj, type, zone )
+    struct sockaddr_at *sat;
+    const char         *obj, *type, *zone;
+{
+    struct sockaddr_at to;
+    struct nbpnve      nn;
+    struct nbphdr      nh;
+    struct nbptuple    nt;
+    struct timeval     timeout;
+    fd_set             readfd;
+    struct servent     *se;
+    char               *data;
+    int                        s, cc;
+    SOCKLEN_T          namelen;
+
+    if ( nbp_lookup( obj, type, zone, &nn, 1, &sat->sat_addr ) > 0 ) {
+        errno = EADDRINUSE;
+       return( -1 );
+    }
+
+    memset(&to, 0, sizeof(to));
+    if ((s = netddp_open(&to, NULL)) < 0)
+        return -1;
+
+    data = nbp_send;
+    *data++ = DDPTYPE_NBP;
+    nh.nh_op = NBPOP_RGSTR;
+    nh.nh_cnt = 1;
+    nh.nh_id = ++nbp_id;
+    memcpy( data, &nh, SZ_NBPHDR );
+    data += SZ_NBPHDR;
+
+    memset(&nt, 0, sizeof(nt));
+    nt.nt_net = sat->sat_addr.s_net;
+    nt.nt_node = sat->sat_addr.s_node;
+    nt.nt_port = sat->sat_port;
+    memcpy( data, &nt, SZ_NBPTUPLE);
+    data += SZ_NBPTUPLE;
+
+    if ( obj ) {
+       if (( cc = strlen( obj )) > NBPSTRLEN ) return( -1 );
+       *data++ = cc;
+       memcpy( data, obj, cc );
+       data += cc;
+    } else {
+       *data++ = 0;
+    }
+
+    if ( type ) {
+       if (( cc = strlen( type )) > NBPSTRLEN ) return( -1 );
+       *data++ = cc;
+       memcpy( data, type, cc );
+       data += cc;
+    } else {
+       *data++ = 0;
+    }
+
+    if ( zone ) {
+       if (( cc = strlen( zone )) > NBPSTRLEN ) return( -1 );
+       *data++ = cc;
+       memcpy( data, zone, cc );
+       data += cc;
+    } else {
+       *data++ = 1;
+       *data++ = '*'; /* default zone */
+    }
+
+    
+    if ( nbp_port == 0 ) {
+       if (( se = getservbyname( "nbp", "ddp" )) == NULL ) {
+           nbp_port = 2;
+       } else {
+           nbp_port = ntohs( se->s_port );
+       }
+    }
+    to.sat_port = nbp_port;
+
+    if ( netddp_sendto( s, nbp_send, data - nbp_send, 0, 
+                       (struct sockaddr *)&to,
+                       sizeof( struct sockaddr_at )) < 0 ) {
+        goto register_err;
+    }
+
+    FD_ZERO( &readfd );
+    FD_SET( s, &readfd );
+    timeout.tv_sec = 2;
+    timeout.tv_usec = 0;
+    if (( cc = select( s + 1, &readfd, 0, 0, &timeout )) < 0 ) {
+        goto register_err;
+    }
+    if ( cc == 0 ) {
+       errno = ETIMEDOUT;
+       goto register_err;
+    }
+
+    namelen = sizeof( struct sockaddr_at );
+    if (( cc = netddp_recvfrom( s, nbp_recv, sizeof( nbp_recv ), 0,
+                       (struct sockaddr *)&to, &namelen )) < 0 ) {
+        goto register_err;
+    }
+
+    netddp_close( s );
+
+    data = nbp_recv;
+    if ( *data++ != DDPTYPE_NBP ) {
+       return( -1 );
+    }
+    memcpy( &nh, data, SZ_NBPHDR );
+    if ( nh.nh_op != NBPOP_OK ) {
+        return -1;
+    }
+    return( 0 );
+
+register_err:
+    netddp_close(s);
+    return -1;
+}
diff --git a/libatalk/nbp/nbp_unrgstr.c b/libatalk/nbp/nbp_unrgstr.c
new file mode 100644 (file)
index 0000000..49cf2ac
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 1990,1997 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/signal.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <atalk/nbp.h>
+#include <atalk/netddp.h>
+#include <atalk/ddp.h>
+
+#include <netdb.h>
+#include  "nbp_conf.h"
+
+/* FIXME/SOCKLEN_T: socklen_t is a unix98 feature. */
+#ifndef SOCKLEN_T
+#define SOCKLEN_T unsigned int
+#endif
+
+int nbp_unrgstr( obj, type, zone, addr )
+    const char         *obj, *type, *zone;
+    const struct at_addr *addr;
+{
+    struct sockaddr_at to;
+    struct nbphdr      nh;
+    struct timeval     timeout;
+    fd_set             readfd;
+    struct servent     *se;
+    char               *data;
+    int                        s, cc;
+    SOCKLEN_T          namelen;
+
+
+    memset(&to, 0, sizeof(to));
+    if ((s = netddp_open(&to, NULL)) < 0)
+       return -1;
+
+    data = nbp_send;
+    *data++ = DDPTYPE_NBP;
+    nh.nh_op = NBPOP_UNRGSTR;
+    nh.nh_cnt = 1;
+    nh.nh_id = ++nbp_id;
+    memcpy( data, &nh, SZ_NBPHDR );
+    data += SZ_NBPHDR;
+
+    memset(data, 0, SZ_NBPTUPLE);
+    data += SZ_NBPTUPLE;
+
+    if ( obj ) {
+       if (( cc = strlen( obj )) > NBPSTRLEN ) return( -1 );
+       *data++ = cc;
+       memcpy( data, obj, cc );
+       data += cc;
+    } else {
+       *data++ = 0;
+    }
+
+    if ( type ) {
+       if (( cc = strlen( type )) > NBPSTRLEN ) return( -1 );
+       *data++ = cc;
+       memcpy( data, type, cc );
+       data += cc;
+    } else {
+       *data++ = 0;
+    }
+
+    if ( zone ) {
+       if (( cc = strlen( zone )) > NBPSTRLEN ) return( -1 );
+       *data++ = cc;
+       memcpy( data, zone, cc );
+       data += cc;
+    } else {
+       *data++ = 0;
+    }
+
+    memset( &to, 0, sizeof( struct sockaddr_at ));
+    to.sat_family = AF_APPLETALK;
+    if (addr) 
+      memcpy(&to.sat_addr, addr, sizeof(struct at_addr));
+#ifdef BSD4_4
+    to.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+
+    if ( nbp_port == 0 ) {
+       if (( se = getservbyname( "nbp", "ddp" )) == NULL ) {
+           nbp_port = 2;
+       } else {
+           nbp_port = ntohs( se->s_port );
+       }
+    }
+    to.sat_port = nbp_port;
+
+    if ( netddp_sendto( s, nbp_send, data - nbp_send, 0,
+                       (struct sockaddr *)&to,
+                       sizeof( struct sockaddr_at )) < 0 ) {
+        goto unregister_err;
+    }
+
+    FD_ZERO( &readfd );
+    FD_SET( s, &readfd );
+    timeout.tv_sec = 2;
+    timeout.tv_usec = 0;
+    if (( cc = select( s + 1, &readfd, 0, 0, &timeout )) < 0 ) {
+        goto unregister_err;
+    }
+    if ( cc == 0 ) {
+       errno = ETIMEDOUT;
+        goto unregister_err;
+    }
+
+    namelen = sizeof( struct sockaddr_at );
+    if (( cc = netddp_recvfrom( s, nbp_recv, sizeof( nbp_recv ), 0,
+                       (struct sockaddr *)&to, &namelen )) < 0 ) {
+        goto unregister_err;
+    }
+    netddp_close( s );
+
+    data = nbp_recv;
+    if ( *data++ != DDPTYPE_NBP ) {
+       return( -1 );
+    }
+    memcpy( &nh, data, SZ_NBPHDR );
+    if ( nh.nh_op != NBPOP_OK ) {
+       return( -1 );
+    }
+    return( 0 );
+
+unregister_err:
+    netddp_close(s);
+    return -1;
+}
diff --git a/libatalk/nbp/nbp_util.c b/libatalk/nbp/nbp_util.c
new file mode 100644 (file)
index 0000000..83e9180
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 1990,1997 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <string.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/signal.h>
+#include <sys/time.h>
+
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+
+#include <atalk/nbp.h>
+#include <atalk/ddp.h>
+#include <atalk/util.h>
+
+#include  "nbp_conf.h"
+
+char           nbp_send[ 1024 ];
+char           nbp_recv[ 1024 ];
+u_char         nbp_port = 0;
+unsigned char   nbp_id = 0;
+
+int nbp_parse( data, nn, len )
+    char               *data;
+    struct nbpnve      *nn;
+    int                        len;
+{
+    struct nbptuple    nt;
+
+    memcpy( &nt, data, SZ_NBPTUPLE);
+    data += SZ_NBPTUPLE;
+    len -= SZ_NBPTUPLE;
+    if ( len < 0 ) {
+       return( -1 );
+    }
+
+#ifdef BSD4_4
+    nn->nn_sat.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+    nn->nn_sat.sat_family = AF_APPLETALK;
+    nn->nn_sat.sat_addr.s_net = nt.nt_net;
+    nn->nn_sat.sat_addr.s_node = nt.nt_node;
+    nn->nn_sat.sat_port = nt.nt_port;
+
+    nn->nn_objlen = *data++;
+    len -= nn->nn_objlen + 1;
+    if ( len < 0 ) {
+       return( -1 );
+    }
+    if ( nn->nn_objlen > NBPSTRLEN ) {
+       return( -1 );
+    }
+    memcpy( nn->nn_obj, data, nn->nn_objlen );
+    data += nn->nn_objlen;
+
+    nn->nn_typelen = *data++;
+    len -= nn->nn_typelen + 1;
+    if ( len < 0 ) {
+       return( -1 );
+    }
+    if ( nn->nn_typelen > NBPSTRLEN ) {
+       return( 1 );
+    }
+    memcpy( nn->nn_type, data, nn->nn_typelen );
+
+    data += nn->nn_typelen;
+    nn->nn_zonelen = *data++;
+    len -= nn->nn_zonelen + 1;
+    if ( len < 0 ) {
+       return( -1 );
+    }
+    if ( nn->nn_zonelen > NBPSTRLEN ) {
+       return( 1 );
+    }
+    memcpy( nn->nn_zone, data, nn->nn_zonelen );
+
+    return( len );
+}
+
+#define NBPM_OBJ       (1<<1)
+#define NBPM_TYPE      (1<<2)
+#define NBPM_ZONE      (1<<3)
+
+int nbp_match( n1, n2, flags )
+    struct nbpnve      *n1, *n2;
+    int                        flags;
+{
+    int                        match = 0;
+
+    if ( flags & NBPMATCH_NOZONE ) {
+       match |= NBPM_ZONE;
+    }
+
+    if ( !( flags & NBPMATCH_NOGLOB )) {
+       if ( n1->nn_objlen == 1 && n1->nn_obj[0] == '=' ) {
+           match |= NBPM_OBJ;
+       }
+       if ( n1->nn_typelen == 1 && n1->nn_type[0] == '=' ) {
+           match |= NBPM_TYPE;
+       }
+    }
+
+    if ( !( match & NBPM_OBJ )) {
+       if ( n1->nn_objlen != n2->nn_objlen ||
+               strndiacasecmp( n1->nn_obj, n2->nn_obj, n1->nn_objlen )) {
+           return( 0 );
+       }
+    }
+    if ( !( match & NBPM_TYPE )) {
+       if ( n1->nn_typelen != n2->nn_typelen ||
+               strndiacasecmp( n1->nn_type, n2->nn_type, n1->nn_typelen )) {
+           return( 0 );
+       }
+    }
+    if ( !( match & NBPM_ZONE )) {
+       if ( n1->nn_zonelen != n2->nn_zonelen ||
+               strndiacasecmp( n1->nn_zone, n2->nn_zone, n1->nn_zonelen )) {
+           return( 0 );
+       }
+    }
+
+    return( 1 );
+}
+
+int nbp_name( name, objp, typep, zonep )
+    const char  *name;
+    char       **objp, **typep, **zonep;
+{
+    static char        buf[ 32 + 1 + 32 + 1 + 32 + 1 ];
+    char       *p;
+
+    if ( name ) {
+       if ( strlen( name ) + 1 > sizeof( buf )) {
+           return( -1 );
+       }
+       strcpy( buf, name );
+
+       if (( p = strrchr( buf, '@' )) != NULL ) {
+           *p++ = '\0';
+           *zonep = p;
+       }
+       if (( p = strrchr( buf, ':' )) != NULL ) {
+           *p++ = '\0';
+           *typep = p;
+       }
+       if ( *buf != '\0' ) {
+           *objp = buf;
+       }
+    }
+
+    return( 0 );
+}
diff --git a/libatalk/netddp/Makefile b/libatalk/netddp/Makefile
new file mode 100644 (file)
index 0000000..b13668f
--- /dev/null
@@ -0,0 +1,56 @@
+SRC=   netddp_open.c netddp_sendto.c netddp_recvfrom.c
+OBJ=   netddp_open.o netddp_sendto.o netddp_recvfrom.o
+
+INCPATH=       -I../../include
+CFLAGS=        ${DEFS} ${OPTOPTS} ${INCPATH}
+TAGSFILE=      tags
+CC=    cc
+
+all: profiled netddplib
+
+profiled:
+       -mkdir profiled
+
+netddplib netddplib_p : ${OBJ}
+       @echo "building profiled netddplib"
+       @cd profiled; ar cru ../netddplib_p ${OBJ}
+       @echo "building normal netddplib"
+       @ar cru netddplib ${OBJ}
+
+.c.o :
+       ${CC} -p ${CFLAGS} -c $*.c
+       -ld -r -o $*.o- $*.o
+       mv $*.o- profiled/$*.o
+       ${CC} ${CFLAGS} -c $*.c
+       -ld -r -o $*.o- $*.o
+       mv $*.o- $*.o
+
+clean :
+       rm -f *.o profiled/*.o *.bak *[Ee]rrs tags
+       rm -f netddplib netddplib_p
+
+tags : ${SRC}
+       cwd=`pwd`; \
+       for i in ${SRC}; do \
+           ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+       done
+
+depend :
+       for i in ${SRC} ; do \
+           ${CC} -M ${DEFS} ${INCPATH} $$i | \
+           awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+               else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+               else rec = rec " " $$2 } } \
+               END { print rec } ' >> makedep; done
+       sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+       cat makedep >> Makefile.tmp
+       rm makedep
+       echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+       echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+       echo '# see make depend above' >> Makefile.tmp
+       rm -f Makefile.bak
+       cp Makefile Makefile.bak
+       mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
diff --git a/libatalk/netddp/netddp_open.c b/libatalk/netddp/netddp_open.c
new file mode 100644 (file)
index 0000000..999a243
--- /dev/null
@@ -0,0 +1,77 @@
+/* 
+ * Copyright (c) 1999 Adrian Sun (asun@zoology.washington.edu)
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ * open a ddp socket and return the port and address assigned. return
+ * various address info if requested as well.
+ */
+
+static int _netddp_open_dummy;
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#ifdef MACOSX_SERVER
+#include <at/appletalk.h>
+#include <at/ddp.h>
+#endif
+
+#include <netatalk/at.h>
+#include <atalk/netddp.h>
+
+int netddp_open(struct sockaddr_at *addr, struct sockaddr_at *bridge)
+{
+    int s;
+
+#ifdef MACOSX_SERVER
+    at_inet_t address, baddress;
+
+    if ((s = ddp_open(addr ? &addr->sat_port : NULL)) < 0)
+        return -1;
+
+    if (!addr)
+      return s;
+
+    if (rtmp_netinfo(s, &address, &baddress) < 0) {
+        ddp_close(s);
+       return -1;
+    }
+    
+    memcpy(&addr->sat_addr.s_net, &address.net, sizeof(addr->sat_addr.s_net));
+    addr->sat_addr.s_node = address.node;
+    addr->sat_port = address.socket;
+    if (bridge) {
+      memcpy(&bridge->sat_addr.s_net, &baddress.net, 
+            sizeof(bridge->sat_addr.s_net));
+      bridge->sat_addr.s_node = baddress.node;
+      bridge->sat_port = baddress.socket;
+    }
+#else
+    int len;
+
+    if ((s = socket( AF_APPLETALK, SOCK_DGRAM, 0 )) < 0) 
+       return -1;
+    
+    if (!addr)
+       return s;
+
+    addr->sat_family = AF_APPLETALK;
+    /* rest of address should be initialized by the caller */
+    if (bind(s, (struct sockaddr *) addr, sizeof( struct sockaddr_at )) < 0 ) {
+        close(s);
+       return -1;
+    }
+
+    /* get the real address from the kernel */
+    len = sizeof( struct sockaddr_at);
+    if ( getsockname( s, (struct sockaddr *) addr, &len ) != 0 ) {
+        close(s);
+       return -1;
+    }
+#endif
+
+    return s;
+}
diff --git a/libatalk/netddp/netddp_recvfrom.c b/libatalk/netddp/netddp_recvfrom.c
new file mode 100644 (file)
index 0000000..b797461
--- /dev/null
@@ -0,0 +1,61 @@
+/* 
+ * Copyright (c) 1999 Adrian Sun (asun@zoology.washington.edu)
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ * receive data.
+ */
+
+static int _netddp_recvfrom_dummy;
+
+#ifndef NO_DDP
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <errno.h>
+
+#ifdef MACOSX_SERVER
+#include <at/appletalk.h>
+#include <at/ddp.h>
+#endif
+
+#include <netatalk/at.h>
+#include <netatalk/endian.h>
+#include <netatalk/ddp.h>
+#include <atalk/netddp.h>
+
+#ifndef MAX
+#define MAX(a, b)  ((a) < (b) ? (b) : (a))
+#endif
+
+#ifdef MACOSX_SERVER
+int netddp_recvfrom(int fd, void *buf, int buflen, unsigned int dummy, 
+                    struct sockaddr *addr, unsigned int *addrlen)
+{
+    ssize_t i;
+    struct ddpehdr ddphdr;
+    struct sockaddr_at *sat = (struct sockaddr_at *) addr;
+    struct iovec iov[2];
+
+    iov[0].iov_base = (void *) &ddphdr;
+    iov[0].iov_len = sizeof(ddphdr);
+    iov[1].iov_base = buf;
+    iov[1].iov_len = buflen;
+
+    while ((i = readv(fd, iov, 2)) < 0) {
+      if (errno != EINTR)
+       return -1;
+    }
+
+    if (addr) {
+      sat->sat_addr.s_net = ddphdr.deh_snet;
+      sat->sat_addr.s_node = ddphdr.deh_snode;
+      sat->sat_port = ddphdr.deh_sport;
+    }
+
+    return MAX(0, i - sizeof(ddphdr));
+}
+
+#endif /* os x server */
+#endif /* no ddp */
diff --git a/libatalk/netddp/netddp_sendto.c b/libatalk/netddp/netddp_sendto.c
new file mode 100644 (file)
index 0000000..3e26e33
--- /dev/null
@@ -0,0 +1,64 @@
+/* 
+ * Copyright (c) 1999 Adrian Sun (asun@zoology.washington.edu)
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ * send data.
+ */
+
+static int _netddp_sendto_dummy;
+
+#ifndef NO_DDP
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <errno.h>
+
+#ifdef MACOSX_SERVER
+#include <at/appletalk.h>
+#include <at/ddp.h>
+#endif
+
+#include <netatalk/at.h>
+#include <netatalk/endian.h>
+#include <netatalk/ddp.h>
+#include <atalk/netddp.h>
+
+#ifndef MAX
+#define MAX(a, b)  ((a) < (b) ? (b) : (a))
+#endif
+
+#ifdef MACOSX_SERVER
+int netddp_sendto(int fd, void *buf, int buflen, unsigned int dummy, 
+                 const struct sockaddr *addr, unsigned int addrlen)
+{
+    ssize_t i;
+    struct ddpehdr ddphdr;
+    const struct sockaddr_at *sat = (const struct sockaddr_at *) addr;
+    struct iovec iov[2];
+
+    iov[0].iov_base = (void *) &ddphdr;
+    iov[0].iov_len = sizeof(ddphdr);
+    iov[1].iov_base = buf;
+    iov[1].iov_len = buflen;
+
+    if (!addr)
+      return -1;
+
+    memset(&ddphdr, 0, sizeof(ddphdr));
+    ddphdr.deh_len = htons(sizeof(ddphdr) + buflen);
+    ddphdr.deh_dnet = sat->sat_addr.s_net;
+    ddphdr.deh_dnode = sat->sat_addr.s_node;
+    ddphdr.deh_dport = sat->sat_port;
+    while ((i = writev(fd, iov, 2)) < 0) {
+      if (errno != EINTR)
+       return -1;
+    }
+
+    return MAX(0, i - sizeof(ddphdr));
+}
+
+#endif /* os x server */
+#endif /* no ddp */
diff --git a/libatalk/pap/pap_child.h b/libatalk/pap/pap_child.h
new file mode 100644 (file)
index 0000000..3d3bab8
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+struct pap_child {
+    int                        ac_pid;
+    int                        ac_state;
+    struct sockaddr_at ac_sat;
+};
+
+#define ACSTATE_DEAD   0
+#define ACSTATE_OK     1
+#define ACSTATE_BAD    7
diff --git a/libatalk/pap/pap_close.c b/libatalk/pap/pap_close.c
new file mode 100644 (file)
index 0000000..4257a46
--- /dev/null
@@ -0,0 +1,44 @@
+/* close the connection */
+
+int pap_close(PAP pap)
+{
+  struct atp_block atpb;
+  struct iovec iov;
+  unsigned char buf[PAP_HDRSIZ];
+  int err = -1;
+
+  buf[ 0 ] = pap->pap_connid;
+  buf[ 1 ] = PAP_CLOSE;
+  buf[ 2 ] = buf[ 3 ] = 0;
+
+  atpb.atp_saddr = &pap->pap_sat;
+  atpb.atp_sreqdata = buf;
+  atpb.atp_sreqdlen = sizeof(buf);     /* bytes in CloseConn request */
+  atpb.atp_sreqto = 2;                 /* retry timer */
+  atpb.atp_sreqtries = 5;              /* retry count */
+  if (atp_sreq( atp, &atpb, 1, ATP_XO ) < 0) {
+    goto close_done;
+  }
+
+  /* check for CloseConnReply */
+  iov.iov_base = pap->pap_data;
+  iov.iov_len = sizeof( pap->pap_data );
+  atpb.atp_rresiov = &iov;
+  atpb.atp_rresiovcnt = 1;
+  if ( atp_rresp( pap->pap_atp, &atpb ) < 0 ) {
+    goto close_done;
+  }
+  
+  /* sanity */
+  if ( iov.iov_len != 4 || pap->pap_data[ 0 ] != pap->pap_connid ||
+       pap->pap_data[ 1 ] != PAP_CLOSEREPLY ) {
+    syslog(LOG_ERR, "pap_close: Bad response!");
+    goto close_done;
+  }
+  err = 0;
+
+close_done:
+  atp_close(pap->pap_atp);
+  free(pap);
+  return err;
+}
diff --git a/libatalk/pap/pap_init.c b/libatalk/pap/pap_init.c
new file mode 100644 (file)
index 0000000..94d20a1
--- /dev/null
@@ -0,0 +1,25 @@
+
+PAP pap_init(ATP atp)
+{
+    PAP pap;
+
+    if ((pap = (struct PAP *) calloc(1, sizeof(struct PAP))) == NULL)
+      return NULL;
+
+    pap->pap_atp = atp;
+#ifdef BSD4_4
+    pap->pap_sat.sat_len = sizeof(struct sockaddr_at);
+#endif
+    pap->pap_sat.sat_family = AF_APPLETALK;
+    pap->pap_sat.sat_addr.s_net = ATADDR_ANYNET;
+    pap->pap_sat.sat_addr.s_node = ATADDR_ANYNODE;
+    pap->pap_sat.sat_port = ATADDR_ANYPORT;
+    pap->pap_status = NULL;
+    pap->pap_slen = 0;
+    pap->pap_sid = 0;
+    pap->pap_flags = PAPFL_SLS;
+    pap->cmdlen = pap->datalen = 0;
+    pap->read_count = pap->write_count = 0;
+
+    return( pap );
+}
diff --git a/libatalk/pap/pap_open.c b/libatalk/pap/pap_open.c
new file mode 100644 (file)
index 0000000..9d29625
--- /dev/null
@@ -0,0 +1,119 @@
+/* moved over from bin/pap/pap.c */
+
+static struct {
+  PAP pap;
+  int tickle;
+  pid_t pid;
+} client;
+
+static void tickle_handler()
+{
+  if (client.tickle++ < 4)
+    pap_tickle(client.pap, client.pap->pap_connid, &client.pap->pap_sat);
+  else {
+    kill(client.pid, SIGTERM);
+    syslog(LOG_ERR, "pap_alarm: connection timed out.");
+    exit(1);
+  }
+}
+
+
+PAP pap_open(PAP pap, struct nbpnve *nn, u_int8_t quantum, int flags)
+{
+    struct atp_block atpb;
+    struct timeval stv, tv;
+    u_int16_t waiting;
+    pid_t pid;
+
+    if (!pap->inited) {
+      pap->pap_connid = getpid() & 0xff;
+      pap->cmdbuf[ 0 ] = pap->pap_connid;
+      pap->cmdbuf[ 1 ] = PAP_OPEN;
+      pap->cmdbuf[ 2 ] = pap->cmdbuf[ 3 ] = 0;
+      pap->cmdbuf[ 4 ] = atp_sockaddr( pap->pap_atp )->sat_port;
+      pap->cmdbuf[ 5 ] = quantum;      /* flow quantum */
+      
+      if ( gettimeofday( &stv, NULL ) < 0 ) {
+       perror( "gettimeofday" );
+       return NULL;
+      }
+      
+      for (;;) {
+       if ( flags & PAPFLAG_CUTS ) {
+         waiting = 0xffff;
+       } else {
+         if ( gettimeofday( &tv, NULL ) < 0 ) {
+           perror( "gettimeofday" );
+           return NULL;
+         }
+         waiting = htons( tv.tv_sec - stv.tv_sec );
+       }
+       
+       memcpy(pap->cmdbuf + 6, &waiting, sizeof( waiting ));
+       atpb.atp_saddr = &nn->nn_sat;
+       atpb.atp_sreqdata = pap->cmdbuf;
+       atpb.atp_sreqdlen = 8;          /* bytes in OpenConn request */
+       atpb.atp_sreqto = 2;            /* retry timer */
+       atpb.atp_sreqtries = 5;         /* retry count */
+       if ( atp_sreq( atp, &atpb, 1, ATP_XO ) < 0 ) {
+         perror( "atp_sreq" );
+         return NULL;
+       }
+       
+       iov.iov_base = pap->data;
+       iov.iov_len = sizeof( rbuf );
+       atpb.atp_rresiov = &iov;
+       atpb.atp_rresiovcnt = 1;
+       if ( atp_rresp( atp, &atpb ) < 0 ) {
+         perror( "atp_rresp" );
+         if ( connattempts-- <= 0 ) {
+           fprintf( stderr, "Can't connect!\n" );
+           return NULL;
+         }
+         continue;
+       }
+       
+       /* sanity */
+       if ( iov.iov_len < 8 || pap->data[ 0 ] != pap->pap_connid ||
+            pap->data[ 1 ] != PAP_OPENREPLY ) {
+         fprintf( stderr, "Bad response!\n" );
+         continue;     /* This is weird, since TIDs must match... */
+       }
+       
+       if ( isatty( 1 )) {
+         printf( "%.*s\n", iov.iov_len - 9, iov.iov_base + 9 );
+       }
+       updatestatus( iov.iov_base + 9, iov.iov_len - 9 );
+       
+       bcopy( &rbuf[ 6 ], &result, sizeof( result ));
+       if ( result != 0 ) {
+         sleep( 2 );
+       } else {
+         memcpy(&pap->pap_sat, &nn.nn_sat, sizeof( struct sockaddr_at ));
+         pap->pap_sat.sat_port = rbuf[ 4 ];
+         pap->pap_quantum = rbuf[ 5 ];
+         break;
+       }
+      }
+      
+      if ( isatty( 1 )) {
+       printf( "Connected to %.*s:%.*s@%.*s.\n",
+               nn.nn_objlen, nn.nn_obj,
+               nn.nn_typelen, nn.nn_type,
+               nn.nn_zonelen, nn.nn_zone );
+      }
+
+    /* open a second atp connection */
+    if (( atp = atp_open( 0 )) == NULL ) {
+       perror( "atp_open" );
+       return NULL;
+    }
+
+    client.pap = pap;
+    client.pid = pid;
+    pap->inited = 1;
+    }
+
+    /* wait around for tickles */
+    
+}
diff --git a/libatalk/pap/pap_read.c b/libatalk/pap/pap_read.c
new file mode 100644 (file)
index 0000000..abd9014
--- /dev/null
@@ -0,0 +1,25 @@
+/* taken from bin/pap/pap.c */
+
+int pap_read(PAP pap)
+{
+    struct atp_block atpb;
+    u_int16_t seq;
+
+    pap->cmdbuf[0] = pap->pap_connid;
+    pap->cmdbuf[1] = PAP_READ;
+    if ( ++pap->pap_seq == 0 ) 
+      pap->pap_seq = 1;
+
+    seq = htons( pap->pap_seq );
+    memcpy(pap->cmdbuf + 2, &seq, sizeof(seq));
+    atpb.atp_saddr = &pap->pap_sat;
+    atpb.atp_sreqdata = pap->cmdbuf;
+    atpb.atp_sreqdlen = sizeof(pap->cmdbuf);   /* bytes in SendData request */
+    atpb.atp_sreqto = 15;              /* retry timer */
+    atpb.atp_sreqtries = ATP_TRIES_INFINITE;   /* retry count */
+    if ( atp_sreq( pap->pap_atp, &atpb, pap->oquantum, ATP_XO ) < 0 ) {
+      return -1;
+    }
+
+    return 0;
+}
diff --git a/libatalk/pap/pap_sendstatus.c b/libatalk/pap/pap_sendstatus.c
new file mode 100644 (file)
index 0000000..6c3ae9e
--- /dev/null
@@ -0,0 +1,21 @@
+
+
+int pap_sendstatus(PAP pap)
+{
+  struct atp_block atpb; 
+  u_int8_t buf[PAP_HDRSIZ];
+  
+  buf[ 0 ] = 0;
+  buf[ 1 ] = PAP_SENDSTATUS;
+  buf[ 2 ] = buf[ 3 ] = 0;
+  atpb.atp_saddr =&pap->pap_sat;
+  atpb.atp_sreqdata = buf;
+  atpb.atp_sreqdlen = 4;       /* bytes in SendStatus request */
+  atpb.atp_sreqto = 2;         /* retry timer */
+  atpb.atp_sreqtries = 5;              /* retry count */
+  if ( atp_sreq( satp, &atpb, 1, 0 ) < 0 ) {
+    return -1;
+  }
+
+  return 0;
+}
diff --git a/libatalk/pap/pap_slinit.c b/libatalk/pap/pap_slinit.c
new file mode 100644 (file)
index 0000000..07403e6
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 1990,1996 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <errno.h>
+#include <sys/signal.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <netatalk/at.h>
+#include <atalk/compat.h>
+#include <atalk/atp.h>
+#include <atalk/pap.h>
+
+#include <atalk/server_child.h>
+#include "pap_child.h"
+
+static PAP server_pap;
+static struct server_child *children = NULL;
+static struct pap_child    **pap_ac = NULL;
+
+/* send tickles and check tickle status of connections */
+static void tickle_handler()
+{
+  int sid;
+  
+  /* check status */
+  for (sid = 0; sid < children->nsessions; sid++) {
+    if (pap_ac[sid] == NULL || pap_ac[sid]->ac_state == ACSTATE_DEAD) 
+      continue;
+    
+    if (++pap_ac[sid]->ac_state >= ACSTATE_BAD) {
+      /* kill. if already dead, just continue */
+      if (kill( pap_ac[ sid ]->ac_pid, SIGTERM) == 0)
+       syslog( LOG_INFO, "pap_alrm: %d timed out",
+               pap_ac[ sid ]->ac_pid );
+
+      pap_ac[ sid ]->ac_state = ACSTATE_DEAD;
+      continue;
+    }
+
+    /* send off a tickle */
+    pap_tickle(server_pap, sid, &pap_ac[sid]->ac_sat);
+  }
+}
+
+static void child_cleanup(const pid_t pid)
+{
+  int i;
+
+  for (i = 0; i < children->nsessions; i++)
+    if (pap_ac[i] && (pap_ac[i]->ac_pid == pid)) {
+      pap_ac[i]->ac_state = ACSTATE_DEAD;
+      break;
+    }
+}
+
+
+/* kill children */
+void pap_kill(int sig)
+{
+  if (children) 
+    server_child_kill(children, CHILD_PAPFORK, sig);
+}
+
+
+/*
+ * This call handles open, tickle, and getstatus requests. On a
+ * successful open, it forks a child process. 
+ * It returns an PAP to the child and parent and NULL if there is
+ * an error.
+ */
+PAP pap_slinit(PAP pap, server_child *server_children, 
+              const int tickleval)
+{
+    struct sigaction action;
+    struct itimerval timer;
+    struct sockaddr_at sat;
+    struct atp_block   atpb;
+    ATP                 atp;
+    struct iovec       iov[ 8 ];
+    pid_t               pid;
+    int                        i, sid;
+    u_short            paperr;
+
+    if (!pap->inited) {
+      if (!(children = server_children))
+       return NULL;
+
+      if ((pap_ac = (struct pap_child **) 
+          calloc(server_children->nsessions, sizeof(struct pap_child *)))
+          == NULL)
+       return NULL;
+
+      server_pap = pap;
+
+      /* install cleanup pointer */
+      server_child_setup(children, CHILD_PAPFORK, child_cleanup);
+
+      /* install tickle handler */
+      action.sa_handler = tickle_handler;
+      sigemptyset(&action.sa_mask);
+      action.sa_flags = SA_RESTART;
+
+      timer.it_interval.tv_sec = timer.it_value.tv_sec = tickleval;
+      timer.it_interval.tv_usec = timer.it_value.tv_usec = 0;
+      if ((setitimer(ITIMER_REAL, &timer, NULL) < 0) ||
+         (sigaction(SIGALRM, &action, NULL) < 0)) {
+       exit(1);
+      }
+
+      pap->inited = 1;
+    }
+                   
+    memset( &sat, 0, sizeof( struct sockaddr_at ));
+#ifdef BSD4_4
+    sat.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+    sat.sat_family = AF_APPLETALK;
+    sat.sat_addr.s_net = ATADDR_ANYNET;
+    sat.sat_addr.s_node = ATADDR_ANYNODE;
+    sat.sat_port = ATADDR_ANYPORT;
+    atpb.atp_saddr = &sat;
+    atpb.atp_rreqdata = pap->cmdbuf;
+    atpb.atp_rreqdlen = sizeof( pap->cmdbuf );
+    while ( atp_rreq( pap->pap_atp, &atpb ) < 0 ) {
+      if ( errno == EINTR || errno == EAGAIN ) {
+       continue;
+      }
+      return( NULL );
+    }
+    
+    switch ( pap->cmdbuf[ 0 ] ) {
+    case PAPFUNC_TICKLE:
+      sid = pap->cmdbuf[1];
+      if ((pap_ac[sid] != NULL) && (pap_ac[sid]->ac_state != ACSTATE_DEAD))
+       pap_ac[sid]->ac_state = ACSTATE_OK;
+      break;
+
+    case PAPFUNC_STAT:
+#ifdef EBUG
+      printf( "pap stat\n" );
+#endif EBUG
+      if ( pap->pap_slen > 0 ) {
+       pap->cmdbuf[0] = 0;
+       bcopy( pap->pap_status, pap->cmdbuf + 4, pap->pap_slen );
+       iov[ 0 ].iov_base = pap->cmdbuf;
+       iov[ 0 ].iov_len = 4 + pap->pap_slen;
+       atpb.atp_sresiov = iov;
+       atpb.atp_sresiovcnt = 1;
+       atp_sresp( pap->pap_atp, &atpb );
+      }
+      break;
+
+    case PAPFUNC_OPEN :
+      if (children->count < children->nsessions) {
+
+       /* find a slot */
+       for (sid = 0; sid < children->nsessions; sid++) {
+         if (pap_ac[sid] == NULL)
+           break;
+
+         if (pap_ac[sid]->ac_state == ACSTATE_DEAD) {
+           free(pap_ac[sid]);
+           pap_ac[sid] = NULL;
+           break;
+         }
+       }
+
+       if ((atp = atp_open(0)) == NULL) 
+         return NULL;
+
+       switch (pid = fork()) {
+       case 0 : /* child */
+         signal(SIGTERM, SIG_DFL);
+         signal(SIGHUP, SIG_DFL);
+         /* free/close some things */
+         for (i = 0; i < children->nsessions; i++ ) {
+           if ( pap_ac[i] != NULL )
+             free( pap_ac[i] );
+         }
+         free(pap_ac);
+         
+         server_child_free(children);
+         children = NULL;
+         atp_close(pap->pap_atp);
+
+         pap->child = 1;
+         pap->pap_atp = atp;
+         pap->pap_sat = sat;
+         pap->pap_wss = pap->cmdbuf[1];
+         pap->pap_seq = 0;
+         pap->pap_sid = sid;
+         pap->pap_flags = PAPFL_SSS;
+         return pap;
+         
+       case -1 : /* error */
+         pap->cmdbuf[ 0 ] = 0;
+         pap->cmdbuf[ 1 ] = 0;
+         paperr = PAPERR_SERVBUSY;
+         break;
+         
+       default : /* parent process */
+         switch (server_child_add(children, CHILD_PAPFORK, pid)) {
+         case 0: /* added child */
+           if (pap_ac[sid] = (struct pap_child *) 
+               malloc(sizeof(struct pap_child))) {
+             pap_ac[sid]->ac_pid = pid;
+             pap_ac[sid]->ac_state = ACSTATE_OK;
+             pap_ac[sid]->ac_sat = sat;
+             pap_ac[sid]->ac_sat.sat_port = pap->cmdbuf[1];
+             
+             pap->cmdbuf[0] = atp_sockaddr(atp)->sat_port;
+             pap->cmdbuf[1] = sid;
+             paperr = PAPERR_OK;
+             break;
+           } /* fall through if malloc fails */
+         case -1: /* bad error */
+           kill(pid, SIGQUIT); 
+           break;
+         default: /* non-fatal error */
+           break;
+         }
+         atp_close(atp);
+         break;
+       }
+       
+      } else {
+       pap->cmdbuf[0] = pap->cmdbuf[1] = 0;
+       paperr = PAPERR_SERVBUSY;
+      }
+
+      bcopy( &paperr, pap->cmdbuf + 2, sizeof( u_short ));
+      iov[ 0 ].iov_base = pap->cmdbuf;
+      iov[ 0 ].iov_len = 4;
+      atpb.atp_sresiov = iov;
+      atpb.atp_sresiovcnt = 1;
+      atp_sresp( pap->pap_atp, &atpb );
+      break;
+
+    default:
+      syslog(LOG_INFO, "PAPUnknown %d", pap->cmdbuf[0]);
+      break;
+    }
+
+    return pap;
+}
diff --git a/libatalk/pap/pap_tickle.c b/libatalk/pap/pap_tickle.c
new file mode 100644 (file)
index 0000000..b7e9a97
--- /dev/null
@@ -0,0 +1,19 @@
+/* send a tickle */
+void pap_tickle(PAP pap, const u_int8_t connid, struct sockaddr_at *sat)
+{
+  struct atp_block atpb; 
+  u_int8_t buf[PAP_HDRSIZ];
+
+  buf[ 0 ] = connid;
+  buf[ 1 ] = PAP_TICKLE;
+  buf[ 2 ] = buf[ 3 ] = 0;
+
+  atpb.atp_saddr = sat;
+  atpb.atp_sreqdata = buf;
+  atpb.atp_sreqdlen = sizeof(buf);     /* bytes in Tickle request */
+  atpb.atp_sreqto = 0;         /* retry timer */
+  atpb.atp_sreqtries = 1;      /* retry count */
+  if ( atp_sreq( pap->pap_atp, &atpb, 0, 0 ) < 0 ) {
+    syslog(LOG_ERR, "atp_sreq: %m");
+  }
+}
diff --git a/libatalk/util/Makefile b/libatalk/util/Makefile
new file mode 100644 (file)
index 0000000..fa0839e
--- /dev/null
@@ -0,0 +1,58 @@
+SRC=   atalk_addr.c strdicasecmp.c server_child.c server_lock.c module.c \
+       bprint.c getiface.c
+OBJ=   atalk_addr.o strdicasecmp.o server_child.o server_lock.o module.o \
+       bprint.o getiface.o
+
+INCPATH=       -I../../include
+CFLAGS=        ${DEFS} ${OPTOPTS} ${INCPATH}
+TAGSFILE=      tags
+CC=    cc
+
+all : profiled utillib
+
+profiled:
+       -mkdir profiled
+
+utillib utillib_p : ${OBJ}
+       @echo "building profiled utillib"
+       @cd profiled; ar cru ../utillib_p ${OBJ}
+       @echo "building normal utillib"
+       @ar cru utillib ${OBJ}
+
+.c.o :
+       ${CC} -p ${CFLAGS} -c $*.c
+       -ld -r -o $*.o- $*.o
+       mv $*.o- profiled/$*.o
+       ${CC} ${CFLAGS} -c $*.c
+       -ld -r -o $*.o- $*.o
+       mv $*.o- $*.o
+
+clean :
+       rm -f *.o profiled/*.o *.bak *[Ee]rrs tags
+       rm -f utillib utillib_p
+
+tags : ${SRC}
+       cwd=`pwd`; \
+       for i in ${SRC}; do \
+           ctags -t -a -f ${TAGSFILE} $$cwd/$$i; \
+       done
+
+depend :
+       for i in ${SRC} ; do \
+           ${CC} -M ${DEFS} ${INCPATH} $$i | \
+           awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+               else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+               else rec = rec " " $$2 } } \
+               END { print rec } ' >> makedep; done
+       sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+       cat makedep >> Makefile.tmp
+       rm makedep
+       echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+       echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+       echo '# see make depend above' >> Makefile.tmp
+       rm -f Makefile.bak
+       cp Makefile Makefile.bak
+       mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
diff --git a/libatalk/util/atalk_addr.c b/libatalk/util/atalk_addr.c
new file mode 100644 (file)
index 0000000..0cf4cc8
--- /dev/null
@@ -0,0 +1,112 @@
+#include <sys/types.h>
+#include <netatalk/at.h>
+#include <netatalk/endian.h>
+#include <atalk/util.h>
+#include <ctype.h>
+
+/* 
+ * Check whether "cp" is a valid ascii representation
+ * of an AppleTalk address and convert to a binary address.
+ * Examples of accepted forms are (in decimal, net of 4321,
+ * node of 65):
+ *
+ *     4321.65
+ *     0x10E1.41
+ *     16.225.65
+ *     0x10.E1.41
+ *
+ * If hex is used, and the first digit is one of A-F, the leading
+ * 0x is redundant. Returns 1 if the address is valid, 0 if not.
+ *
+ * Unlike Internet addresses, AppleTalk addresses can have leading
+ * 0's. This means that we can't support octal addressing.
+ */
+
+int atalk_aton( cp, addr )
+    char               *cp;
+    struct at_addr     *addr;
+{
+    u_int32_t val, base, n;
+    char c;
+
+    val = 0; base = 10;
+    if ( *cp == '0' && ( *++cp == 'x' || *cp == 'X' )) {
+       base = 16, cp++;
+    }
+    if ( !isdigit( *cp ) && isxdigit( *cp )) {
+       base = 16;
+    }
+
+    for ( n = 0;; n++ ) {
+       while (( c = *cp ) != '\0') {
+           if ( isascii( c ) && isdigit( c )) {
+               val = (val * base) + (c - '0');
+               cp++;
+               continue;
+           }
+
+           if ( base == 16 && isascii( c ) && isxdigit( c )) {
+               val = ( val << 4 ) + ( c + 10 - ( islower( c ) ? 'a' : 'A' ));
+               cp++;
+               continue;
+           }
+           break;
+       }
+
+       if ( c != '.' && c != '\0' ) {
+           return( 0 );
+       }
+
+       switch ( n ) {
+       case 0:
+           if ( addr ) {
+               if ( val > 65535 ) {
+                   return( 0 );
+               }
+               addr->s_net = val;
+           }
+           if ( *cp++ ) {
+               val = 0;
+           } else {
+               break;
+           }
+           continue;
+
+       case 2:
+           if ( addr ) {
+               if ( addr->s_net > 255 ) {
+                   return( 0 );
+               }
+               addr->s_net <<= 8;
+               addr->s_net += addr->s_node;
+           }
+           /*FALLTHROUGH*/
+
+       case 1:
+           if ( addr ) {
+               if ( val > 255 ) {
+                   return( 0 );
+               }
+               addr->s_node = val;
+           }
+           if ( *cp++ ) {
+               val = 0;
+           } else {
+               break;
+           }
+           continue;
+
+       default:
+           return( 0 );
+       }
+       break;
+    }
+
+    if ( n < 1 ) {
+       return( 0 );
+    }
+    if ( addr ) {
+       addr->s_net = htons( addr->s_net );
+    }
+    return (1);
+}
diff --git a/libatalk/util/bprint.c b/libatalk/util/bprint.c
new file mode 100644 (file)
index 0000000..c192ba7
--- /dev/null
@@ -0,0 +1,46 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <atalk/util.h>
+
+static const char      hexdig[] = "0123456789ABCDEF";
+
+#define BPXLEN 50
+#define BPALEN 18
+
+void bprint( data, len )
+    char       *data;
+    int                len;
+{
+    char       xout[ BPXLEN ], aout[ BPALEN ];
+    int         i;
+
+    memset( xout, 0, BPXLEN );
+    memset( aout, 0, BPALEN );
+
+    for ( i = 0; len; len--, data++, i++ ) {
+       if ( i == 16 ) {
+           printf( "%-48s\t%-16s\n", xout, aout );
+           memset( xout, 0, BPXLEN );
+           memset( aout, 0, BPALEN );
+           i = 0;
+       }
+
+       if (isprint( (unsigned char)*data )) {
+           aout[ i ] = *data;
+       } else {
+           aout[ i ] = '.';
+       }
+
+       xout[ (i*3) ] = hexdig[ ( *data & 0xf0 ) >> 4 ];
+       xout[ (i*3) + 1 ] = hexdig[ *data & 0x0f ];
+       xout[ (i*3) + 2 ] = ' ';
+    }
+
+    if ( i )   
+       printf( "%-48s\t%-16s\n", xout, aout );
+
+    printf("(end)\n");
+}
diff --git a/libatalk/util/getiface.c b/libatalk/util/getiface.c
new file mode 100644 (file)
index 0000000..28aa030
--- /dev/null
@@ -0,0 +1,152 @@
+/* 
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * Copyright (c) 1999-2000 Adrian Sun. 
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <net/if.h>
+#include <errno.h>
+
+#ifdef __svr4__
+#include <sys/sockio.h>
+#endif
+
+#include <atalk/util.h>
+
+/* allocation size for interface list. */
+#define IFACE_NUM 5
+
+/* we leave all of the ioctl's to the application */
+static int addname(char **list, int *i, int *length, const char *name) 
+
+{
+    /* if we've run out of room, allocate some more. just return
+     * the present list if we can't. */
+     if (*i >= *length) {
+      char **new = realloc(list, sizeof(char **)*(*length + IFACE_NUM));
+       
+      if (!new) /* just break if we can't allocate anything */
+       return -1;
+      *length += IFACE_NUM;
+    }
+     
+    if ((list[*i] = strdup(name)) == NULL)
+      return -1;
+
+    (*i)++;
+    list[*i] = NULL; /* zero out the next entry */
+    return 0;
+}
+
+
+static int getifaces(const int sockfd, char **list, int *length)
+{
+#ifdef HAVE_IFNAMEINDEX
+      struct if_nameindex *ifstart, *ifs;
+      int i = 0;
+  
+      if (!list || *length < 1) 
+       return 0;
+
+      ifs = ifstart = if_nameindex();
+      while (ifs && ifs->if_name) {
+       /* just bail if there's a problem */
+       if (addname(list, &i, length, ifs->if_name) < 0)
+         break;
+       ifs++;
+      }
+
+      if_freenameindex(ifstart);
+      return i;
+
+#else
+    struct ifconf      ifc;
+    struct ifreq       ifrs[ 64 ], *ifr, *nextifr;
+    int                        ifrsize, i = 0;
+
+    if (!list || *length < 1)
+      return 0;
+
+    memset( &ifc, 0, sizeof( struct ifconf ));
+    ifc.ifc_len = sizeof( ifrs );
+    memset( ifrs, 0, sizeof( ifrs ));
+    ifc.ifc_buf = (caddr_t)ifrs;
+    if ( ioctl( sockfd, SIOCGIFCONF, &ifc ) < 0 ) {
+       return 0;
+    }
+
+    for ( ifr = ifc.ifc_req; ifc.ifc_len >= sizeof( struct ifreq );
+           ifc.ifc_len -= ifrsize, ifr = nextifr ) {
+#ifdef BSD4_4
+       ifrsize = sizeof(ifr->ifr_name) +
+         (ifr->ifr_addr.sa_len > sizeof(struct sockaddr)
+          ? ifr->ifr_addr.sa_len : sizeof(struct sockaddr));
+#else BSD4_4
+       ifrsize = sizeof( struct ifreq );
+#endif BSD4_4
+       nextifr = (struct ifreq *)((caddr_t)ifr + ifrsize );
+
+       /* just bail if there's a problem */
+       if (addname(list, &i, length, ifr->ifr_name) < 0)
+         break;
+    }
+    return i;
+#endif
+}
+
+
+/*
+ * Get interfaces from the kernel. we keep an extra null entry to signify
+ * the end of the interface list. 
+ */
+char **getifacelist()
+{
+  char **list = (char **) malloc(sizeof(char **)*(IFACE_NUM + 1));
+  char **new;
+  int length = IFACE_NUM, i, fd;
+
+  if (!list)
+    return NULL;
+      
+  if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
+    return NULL;
+
+  if ((i = getifaces(fd, list, &length)) == 0) {
+    free(list);
+    close(fd);
+    return NULL;
+  }
+  close(fd);
+
+  if ((i < length) && 
+      (new = (char **) realloc(list, sizeof(char **)*(i + 1))))
+    return new;
+
+  return list;
+}
+
+
+/* go through and free the interface list */
+void freeifacelist(char **ifacelist)
+{
+  char *value, **list = ifacelist;
+
+  if (!ifacelist)
+    return;
+
+  while (value = *list++) {
+    free(value);
+  }
+
+  free(ifacelist);
+}
diff --git a/libatalk/util/module.c b/libatalk/util/module.c
new file mode 100644 (file)
index 0000000..d325534
--- /dev/null
@@ -0,0 +1,66 @@
+#include <stdlib.h>
+#include <string.h>
+#include <atalk/util.h>
+
+static int _mod_dummy;
+
+#ifdef NO_DLFCN_H
+#ifdef MACOSX_SERVER
+#include <mach-o/dyld.h>
+
+void *mod_open(const char *path)
+{
+  NSObjectFileImage file;
+
+  if (NSCreateObjectFileImageFromFile(path, &file) != 
+      NSObjectFileImageSuccess)
+    return NULL;
+  return NSLinkModule(file, path, TRUE);
+}
+
+void *mod_symbol(void *module, const char *name)
+{
+   NSSymbol symbol;
+   char *underscore;
+
+   if ((underscore = (char *) malloc(strlen(name) + 2)) == NULL)
+     return NULL;
+   strcpy(underscore, "_");
+   strcat(underscore, name);
+   symbol = NSLookupAndBindSymbol(underscore);
+   free(underscore);
+
+   return NSAddressOfSymbol(symbol);
+}
+
+void mod_close(void *module)
+{
+  NSUnLinkModule(module, FALSE);
+}
+#endif
+
+#else
+
+#ifdef DLSYM_PREPEND_UNDERSCORE
+#include <dlfcn.h>
+
+void *mod_symbol(void *module, const char *name)
+{
+   void *symbol;
+   char *underscore;
+
+   if (!module)
+     return NULL;
+
+   if ((underscore = (char *) malloc(strlen(name) + 2)) == NULL)
+     return NULL;
+
+   strcpy(underscore, "_");
+   strcat(underscore, name);
+   symbol = dlsym(module, underscore);
+   free(underscore);
+
+   return symbol;
+}
+#endif /* DLSYM_PREPEND_UNDERSCORE */
+#endif /* NO_DLFCN */
diff --git a/libatalk/util/server_child.c b/libatalk/util/server_child.c
new file mode 100644 (file)
index 0000000..82b6199
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ *
+ *
+ * handle inserting, removing, and freeing of children. 
+ * this does it via a hash table. it incurs some overhead over
+ * a linear append/remove in total removal and kills, but it makes
+ * single-entry removals a fast operation. as total removals occur during
+ * child initialization and kills during server shutdown, this is 
+ * probably a win for a lot of connections and unimportant for a small
+ * number of connections.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/syslog.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <atalk/server_child.h>
+
+#ifndef __inline__
+#define __inline__
+#endif
+
+/* hash/child functions: hash OR's pid */
+#define CHILD_HASHSIZE 32
+#define HASH(i) ((((i) >> 8) ^ (i)) & (CHILD_HASHSIZE - 1))
+
+struct server_child_data {
+  pid_t pid; 
+  struct server_child_data **prevp, *next;
+};
+
+typedef struct server_child_fork {
+  struct server_child_data *table[CHILD_HASHSIZE];
+  void (*cleanup)(const pid_t);
+} server_child_fork;
+
+static __inline__ void hash_child(struct server_child_data **htable,
+                                 struct server_child_data *child)
+{
+  struct server_child_data **table;
+
+  table = &htable[HASH(child->pid)];
+  if ((child->next = *table) != NULL)
+    (*table)->prevp = &child->next;
+  *table = child;
+  child->prevp = table;
+}
+
+static __inline__ void unhash_child(struct server_child_data *child)
+{
+  if (child->prevp) {
+    if (child->next)
+      child->next->prevp = child->prevp;
+    *(child->prevp) = child->next;
+  }
+}
+
+static __inline__ struct server_child_data 
+*resolve_child(struct server_child_data **table, const pid_t pid)
+{
+  struct server_child_data *child;
+
+  for (child = table[HASH(pid)]; child; child = child->next) {
+    if (child->pid == pid)
+      break;
+  }
+
+  return child;
+}
+
+/* initialize server_child structure */
+server_child *server_child_alloc(const int connections, const int nforks)
+{
+  server_child *children;
+
+  children = (server_child *) calloc(1, sizeof(server_child));
+  if (!children)
+    return NULL;
+
+  children->nsessions = connections;
+  children->nforks = nforks;
+  children->fork = (void *) calloc(nforks, sizeof(server_child_fork));
+  
+  if (!children->fork) {
+    free(children);
+    return NULL;
+  } 
+
+  return children;
+}
+
+/* add a child in. return 0 on success, -1 on serious error, and
+ * > 0 for a non-serious error. */
+int server_child_add(server_child *children, const int forkid,
+                    const pid_t pid)
+{
+  server_child_fork *fork;
+  struct server_child_data *child;
+  sigset_t sig;
+
+  /* we need to prevent deletions from occuring before we get a 
+   * chance to add the child in. */
+  sigemptyset(&sig);
+  sigaddset(&sig, SIGCHLD);
+  sigprocmask(SIG_BLOCK, &sig, NULL);
+
+  /* it's possible that the child could have already died before the
+   * sigprocmask. we need to check for this. */
+  if (kill(pid, 0) < 0) {
+    sigprocmask(SIG_UNBLOCK, &sig, NULL);
+    return 1;
+  }
+
+  fork = (server_child_fork *) children->fork + forkid;
+
+  /* if we already have an entry. just return. */
+  if (resolve_child(fork->table, pid)) {
+    sigprocmask(SIG_UNBLOCK, &sig, NULL);
+    return 0;
+  }
+
+  if ((child = (struct server_child_data *) 
+       calloc(1, sizeof(struct server_child_data))) == NULL) {
+    sigprocmask(SIG_UNBLOCK, &sig, NULL);
+    return -1;
+  }
+
+  child->pid = pid;
+  hash_child(fork->table, child);
+  children->count++;
+  sigprocmask(SIG_UNBLOCK, &sig, NULL);
+
+  return 0;
+}
+
+/* remove a child and free it */
+int server_child_remove(server_child *children, const int forkid,
+                       const pid_t pid)
+{
+  server_child_fork *fork;
+  struct server_child_data *child;
+
+  fork = (server_child_fork *) children->fork + forkid;
+  if (!(child = resolve_child(fork->table, pid)))
+    return 0;
+  
+  unhash_child(child);
+  free(child);
+  children->count--;
+  return 1;
+}
+
+/* free everything: by using a hash table, this increases the cost of
+ * this part over a linked list by the size of the hash table */
+void server_child_free(server_child *children)
+{
+  server_child_fork *fork;
+  struct server_child_data *child, *tmp;
+  int i, j;
+
+  for (i = 0; i < children->nforks; i++) {
+    fork = (server_child_fork *) children->fork + i;
+    for (j = 0; j < CHILD_HASHSIZE; j++) {
+      child = fork->table[j]; /* start at the beginning */
+      while (child) {
+       tmp = child->next;
+       free(child);
+       child = tmp;
+      }
+    }
+  }
+  free(children->fork);
+  free(children);
+}
+
+/* send kill to child processes: this also has an increased cost over
+ * a plain-old linked list */
+void server_child_kill(server_child *children, const int forkid,
+                      const int sig)
+{
+  server_child_fork *fork;
+  struct server_child_data *child, *tmp;
+  int i;
+
+  fork = (server_child_fork *) children->fork + forkid;
+  for (i = 0; i < CHILD_HASHSIZE; i++) {
+    child = fork->table[i];
+    while (child) {
+      tmp = child->next;
+      kill(child->pid, sig);
+      child = tmp;
+    }
+  }
+}
+
+/* for extra cleanup if necessary */
+void server_child_setup(server_child *children, const int forkid,
+                         void (*fcn)(const pid_t))
+{
+  server_child_fork *fork;
+
+  fork = (server_child_fork *) children->fork + forkid;
+  fork->cleanup = fcn;
+}
+
+
+/* keep track of children. */
+void server_child_handler(server_child *children)
+{
+  server_child_fork *fork;
+  int status, i;
+  pid_t pid;
+  
+#ifndef WAIT_ANY
+#define WAIT_ANY (-1)
+#endif
+
+  while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) {
+    for (i = 0; i < children->nforks; i++) {
+      if (server_child_remove(children, i, pid)) {
+       fork = (server_child_fork *) children->fork + i;
+       if (fork->cleanup)
+         fork->cleanup(pid);
+       break;
+      }
+    }
+    
+    if (WIFEXITED(status)) {
+      if (WEXITSTATUS(status)) {
+       syslog(LOG_INFO, "server_child[%d] %d exited %d", i, pid, 
+              WEXITSTATUS(status));
+      } else {
+       syslog(LOG_INFO, "server_child[%d] %d done", i, pid);
+      }
+    } else {
+      if (WIFSIGNALED(status)) 
+       syslog(LOG_INFO, "server_child[%d] %d killed", i, pid);
+      else
+       syslog(LOG_INFO, "server_child[%d] %d died", i, pid);
+    }
+  }
+}
diff --git a/libatalk/util/server_lock.c b/libatalk/util/server_lock.c
new file mode 100644 (file)
index 0000000..2a5d43d
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
+ * All rights reserved. See COPYRIGHT.
+ *
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#include <atalk/compat.h>
+#include <atalk/util.h>
+
+/* this creates an open lock file which hangs around until the program
+ * dies. it returns the pid. due to problems w/ solaris, this has
+ * been changed to do the kill() thing. */
+pid_t server_lock(char *program, char *pidfile, int debug)
+{
+  char buf[10];
+  FILE *pf;
+  pid_t pid;
+  int mask;
+  
+  mask = umask(022);
+  /* check for pid. this can get fooled by stale pid's. */
+  if ((pf = fopen(pidfile, "r"))) {
+    if (fgets(buf, sizeof(buf), pf) && !kill(pid = atol(buf), 0)) {
+      fprintf( stderr, "%s is already running (pid = %d), or the lock file is stale.\n",
+              program, pid);      
+      fclose(pf);
+      return -1;
+    }
+    fclose(pf);
+  }
+
+  if ((pf = fopen(pidfile, "w")) == NULL) {
+    fprintf(stderr, "%s: can't open lock file, \"%s\"\n", program,
+           pidfile);
+    return -1;
+  }
+  umask(mask);
+
+  /*
+   * Disassociate from controlling tty.
+   */
+  if ( !debug ) {
+    int                i;
+
+    switch (pid = fork()) {
+    case 0 :
+      fclose(stdin);
+      fclose(stdout);
+      fclose(stderr);
+
+      if (( i = open( "/dev/tty", O_RDWR )) >= 0 ) {
+       (void)ioctl( i, TIOCNOTTY, 0 );
+       setpgid( 0, getpid());
+       (void) close(i);
+      }
+      break;
+    case -1 :  /* error */
+      perror( "fork" );
+    default :  /* server */
+      fclose(pf);
+      return pid;
+    }
+  } 
+
+  fprintf(pf, "%d\n", getpid());
+  fclose(pf);
+  return 0;
+}
diff --git a/libatalk/util/strdicasecmp.c b/libatalk/util/strdicasecmp.c
new file mode 100644 (file)
index 0000000..6f3b996
--- /dev/null
@@ -0,0 +1,544 @@
+#include <atalk/util.h>
+
+unsigned const char    _diacasemap[] = {
+       /* map  value   name */
+       0       /* 0    NUL*/,
+       1       /* 1    SOH*/,
+       2       /* 2    STX*/,
+       3       /* 3    ETX*/,
+       4       /* 4    EOT*/,
+       5       /* 5    ENQ*/,
+       6       /* 6    ACK*/,
+       7       /* 7    BEL*/,
+       8       /* 8    BS*/,
+       9       /* 9    HT*/,
+       10      /* 10   NL*/,
+       11      /* 11   VT*/,
+       12      /* 12   NP*/,
+       13      /* 13   CR*/,
+       14      /* 14   SO*/,
+       15      /* 15   SI*/,
+       16      /* 16   DLE*/,
+       17      /* 17   DC1*/,
+       18      /* 18   DC2*/,
+       19      /* 19   DC3*/,
+       20      /* 20   DC4*/,
+       21      /* 21   NAK*/,
+       22      /* 22   SYN*/,
+       23      /* 23   ETB*/,
+       24      /* 24   CAN*/,
+       25      /* 25   EM*/,
+       26      /* 26   SUB*/,
+       27      /* 27   ESC*/,
+       28      /* 28   FS*/,
+       29      /* 29   GS*/,
+       30      /* 30   RS*/,
+       31      /* 31   US*/,
+       32      /* 32   SP*/,
+       33      /* 33    ! */,
+       34      /* 34    " */,
+       35      /* 35    # */,
+       36      /* 36    $ */,
+       37      /* 37    % */,
+       38      /* 38    & */,
+       39      /* 39    ' */,
+       40      /* 40    ( */,
+       41      /* 41    ) */,
+       42      /* 42    * */,
+       43      /* 43    + */,
+       44      /* 44    , */,
+       45      /* 45    - */,
+       46      /* 46    . */,
+       47      /* 47    / */,
+       48      /* 48    0 */,
+       49      /* 49    1 */,
+       50      /* 50    2 */,
+       51      /* 51    3 */,
+       52      /* 52    4 */,
+       53      /* 53    5 */,
+       54      /* 54    6 */,
+       55      /* 55    7 */,
+       56      /* 56    8 */,
+       57      /* 57    9 */,
+       58      /* 58    : */,
+       59      /* 59    ; */,
+       60      /* 60    < */,
+       61      /* 61    = */,
+       62      /* 62    > */,
+       63      /* 63    ? */,
+       64      /* 64    @ */,
+       65      /* 65    A */,
+       66      /* 66    B */,
+       67      /* 67    C */,
+       68      /* 68    D */,
+       69      /* 69    E */,
+       70      /* 70    F */,
+       71      /* 71    G */,
+       72      /* 72    H */,
+       73      /* 73    I */,
+       74      /* 74    J */,
+       75      /* 75    K */,
+       76      /* 76    L */,
+       77      /* 77    M */,
+       78      /* 78    N */,
+       79      /* 79    O */,
+       80      /* 80    P */,
+       81      /* 81    Q */,
+       82      /* 82    R */,
+       83      /* 83    S */,
+       84      /* 84    T */,
+       85      /* 85    U */,
+       86      /* 86    V */,
+       87      /* 87    W */,
+       88      /* 88    X */,
+       89      /* 89    Y */,
+       90      /* 90    Z */,
+       91      /* 91    [ */,
+       92      /* 92    \ */,
+       93      /* 93    ] */,
+       94      /* 94    ^ */,
+       95      /* 95    _ */,
+       96      /* 96    ` */,
+       65      /* 97    a */,
+       66      /* 98    b */,
+       67      /* 99    c */,
+       68      /* 100   d */,
+       69      /* 101   e */,
+       70      /* 102   f */,
+       71      /* 103   g */,
+       72      /* 104   h */,
+       73      /* 105   i */,
+       74      /* 106   j */,
+       75      /* 107   k */,
+       76      /* 108   l */,
+       77      /* 109   m */,
+       78      /* 110   n */,
+       79      /* 111   o */,
+       80      /* 112   p */,
+       81      /* 113   q */,
+       82      /* 114   r */,
+       83      /* 115   s */,
+       84      /* 116   t */,
+       85      /* 117   u */,
+       86      /* 118   v */,
+       87      /* 119   w */,
+       88      /* 120   x */,
+       89      /* 121   y */,
+       90      /* 122   z */,
+       123     /* 123   { */,
+       124     /* 124   | */,
+       125     /* 125   } */,
+       126     /* 126   ~ */,
+       127     /* 127  DEL*/,
+       128     /* 128  Adieresis*/,
+       129     /* 129  Aring*/,
+       130     /* 130  Ccedilla*/,
+       131     /* 131  Eacute*/,
+       132     /* 132  Ntilda*/,
+       133     /* 133  Odieresis*/,
+       134     /* 134  Udieresis*/,
+       231     /* 135  aacute*/,
+       203     /* 136  agrave*/,
+       229     /* 137  acircumflex*/,
+       128     /* 138  adieresis*/,
+       204     /* 139  atilda*/,
+       129     /* 140  aring*/,
+       130     /* 141  ccedilla*/,
+       131     /* 142  eacute*/,
+       233     /* 143  egrave*/,
+       230     /* 144  ecircumflex*/,
+       232     /* 145  edieresis*/,
+       234     /* 146  iacute*/,
+       237     /* 147  igrave*/,
+       235     /* 148  icircumflex*/,
+       236     /* 149  idieresis*/,
+       132     /* 150  ntilda*/,
+       238     /* 151  oacute*/,
+       241     /* 152  ograve*/,
+       239     /* 153  ocircumflex*/,
+       133     /* 154  odieresis*/,
+       205     /* 155  otilda*/,
+       242     /* 156  uacute*/,
+       244     /* 157  ugrave*/,
+       243     /* 158  ucircumflex*/,
+       134     /* 159  udieresis*/,
+       160     /* 160  daggar*/,
+       161     /* 161  ring*/,
+       162     /* 162  cent*/,
+       163     /* 163  sterling*/,
+       164     /* 164  section*/,
+       165     /* 165  bullet*/,
+       166     /* 166  paragraph*/,
+       167     /* 167  germandbls*/,
+       168     /* 168  registered*/,
+       169     /* 169  copyright*/,
+       170     /* 170  trademark*/,
+       171     /* 171  acute*/,
+       172     /* 172  dieresis*/,
+       173     /* 173  notequal*/,
+       174     /* 174  AE*/,
+       175     /* 175  Oslash*/,
+       176     /* 176  infinity*/,
+       177     /* 177  plusminus*/,
+       178     /* 178  lessequal*/,
+       179     /* 179  greaterequal*/,
+       180     /* 180  yen*/,
+       181     /* 181  mu*/,
+       198     /* 182  delta*/,
+       183     /* 183  Sigma*/,
+       184     /* 184  Pi*/,
+       184     /* 185  pi*/,
+       186     /* 186  intergral*/,
+       187     /* 187  ordfeminine*/,
+       188     /* 188  ordmasculine*/,
+       189     /* 189  Omega*/,
+       174     /* 190  ae*/,
+       175     /* 191  oslash*/,
+       192     /* 192  questiondown*/,
+       193     /* 193  exclamdown*/,
+       194     /* 194  not*/,
+       195     /* 195  radical*/,
+       196     /* 196  florin*/,
+       197     /* 197  aprox*/,
+       198     /* 198  Delta*/,
+       199     /* 199  guillemotleft*/,
+       200     /* 200  guillemotright*/,
+       201     /* 201  ellipsis*/,
+       202     /* 202  */,
+       203     /* 203  Agrave*/,
+       204     /* 204  Atilda*/,
+       205     /* 205  Otilda*/,
+       206     /* 206  OE*/,
+       206     /* 207  oe*/,
+       208     /* 208  endash*/,
+       209     /* 209  emdash*/,
+       210     /* 210  quotedblleft*/,
+       211     /* 211  quotedblright*/,
+       212     /* 212  quoteleft*/,
+       213     /* 213  quoteright*/,
+       214     /* 214  divide*/,
+       215     /* 215  diamond*/,
+       217     /* 216  ydieresis*/,
+       217     /* 217  Ydieresis*/,
+       218     /* 218  fraction*/,
+       219     /* 219  currency*/,
+       220     /* 220  guilsinglleft*/,
+       221     /* 221  guilsinglright*/,
+       222     /* 222  fi*/,
+       223     /* 223  fl*/,
+       224     /* 224  daggardbl*/,
+       225     /* 225  periodcentered*/,
+       226     /* 226  quotesinglbase*/,
+       227     /* 227  quotedblbase*/,
+       228     /* 228  perthousand*/,
+       229     /* 229  Acircumflex*/,
+       230     /* 230  Ecircumflex*/,
+       231     /* 231  Aaccute*/,
+       232     /* 232  Edieresis*/,
+       233     /* 233  Egrave*/,
+       234     /* 234  Iaccute*/,
+       235     /* 235  Icircumflex*/,
+       236     /* 236  Idieresis*/,
+       237     /* 237  Igrave*/,
+       238     /* 238  Oaccute*/,
+       239     /* 239  Ocircumflex*/,
+       240     /* 240  apple*/,
+       241     /* 241  Ograve*/,
+       242     /* 242  Uaccute*/,
+       243     /* 243  Ucircumflex*/,
+       244     /* 244  Ugrave*/,
+       245     /* 245  dotlessi*/,
+       246     /* 246  circumflex*/,
+       247     /* 247  tilda*/,
+       248     /* 248  macron*/,
+       249     /* 249  breve*/,
+       250     /* 250  dotaccent*/,
+       251     /* 251  ring*/,
+       252     /* 252  cedilla*/,
+       253     /* 253  hungarumlaut*/,
+       254     /* 254  ogonek*/,
+       255     /* 255  caron*/,
+};
+
+unsigned const char    _dialowermap[] = {
+       /* map  value   name */
+       0       /* 0    NUL*/,
+       1       /* 1    SOH*/,
+       2       /* 2    STX*/,
+       3       /* 3    ETX*/,
+       4       /* 4    EOT*/,
+       5       /* 5    ENQ*/,
+       6       /* 6    ACK*/,
+       7       /* 7    BEL*/,
+       8       /* 8    BS*/,
+       9       /* 9    HT*/,
+       10      /* 10   NL*/,
+       11      /* 11   VT*/,
+       12      /* 12   NP*/,
+       13      /* 13   CR*/,
+       14      /* 14   SO*/,
+       15      /* 15   SI*/,
+       16      /* 16   DLE*/,
+       17      /* 17   DC1*/,
+       18      /* 18   DC2*/,
+       19      /* 19   DC3*/,
+       20      /* 20   DC4*/,
+       21      /* 21   NAK*/,
+       22      /* 22   SYN*/,
+       23      /* 23   ETB*/,
+       24      /* 24   CAN*/,
+       25      /* 25   EM*/,
+       26      /* 26   SUB*/,
+       27      /* 27   ESC*/,
+       28      /* 28   FS*/,
+       29      /* 29   GS*/,
+       30      /* 30   RS*/,
+       31      /* 31   US*/,
+       32      /* 32   SP*/,
+       33      /* 33    ! */,
+       34      /* 34    " */,
+       35      /* 35    # */,
+       36      /* 36    $ */,
+       37      /* 37    % */,
+       38      /* 38    & */,
+       39      /* 39    ' */,
+       40      /* 40    ( */,
+       41      /* 41    ) */,
+       42      /* 42    * */,
+       43      /* 43    + */,
+       44      /* 44    , */,
+       45      /* 45    - */,
+       46      /* 46    . */,
+       47      /* 47    / */,
+       48      /* 48    0 */,
+       49      /* 49    1 */,
+       50      /* 50    2 */,
+       51      /* 51    3 */,
+       52      /* 52    4 */,
+       53      /* 53    5 */,
+       54      /* 54    6 */,
+       55      /* 55    7 */,
+       56      /* 56    8 */,
+       57      /* 57    9 */,
+       58      /* 58    : */,
+       59      /* 59    ; */,
+       60      /* 60    < */,
+       61      /* 61    = */,
+       62      /* 62    > */,
+       63      /* 63    ? */,
+       64      /* 64    @ */,
+       97      /* 65    A */,
+       98      /* 66    B */,
+       99      /* 67    C */,
+       100     /* 68    D */,
+       101     /* 69    E */,
+       102     /* 70    F */,
+       103     /* 71    G */,
+       104     /* 72    H */,
+       105     /* 73    I */,
+       106     /* 74    J */,
+       107     /* 75    K */,
+       108     /* 76    L */,
+       109     /* 77    M */,
+       110     /* 78    N */,
+       111     /* 79    O */,
+       112     /* 80    P */,
+       113     /* 81    Q */,
+       114     /* 82    R */,
+       115     /* 83    S */,
+       116     /* 84    T */,
+       117     /* 85    U */,
+       118     /* 86    V */,
+       119     /* 87    W */,
+       120     /* 88    X */,
+       121     /* 89    Y */,
+       122     /* 90    Z */,
+       91      /* 91    [ */,
+       92      /* 92    \ */,
+       93      /* 93    ] */,
+       94      /* 94    ^ */,
+       95      /* 95    _ */,
+       96      /* 96    ` */,
+       97      /* 97    a */,
+       98      /* 98    b */,
+       99      /* 99    c */,
+       100     /* 100   d */,
+       101     /* 101   e */,
+       102     /* 102   f */,
+       103     /* 103   g */,
+       104     /* 104   h */,
+       105     /* 105   i */,
+       106     /* 106   j */,
+       107     /* 107   k */,
+       108     /* 108   l */,
+       109     /* 109   m */,
+       110     /* 110   n */,
+       111     /* 111   o */,
+       112     /* 112   p */,
+       113     /* 113   q */,
+       114     /* 114   r */,
+       115     /* 115   s */,
+       116     /* 116   t */,
+       117     /* 117   u */,
+       118     /* 118   v */,
+       119     /* 119   w */,
+       120     /* 120   x */,
+       121     /* 121   y */,
+       122     /* 122   z */,
+       123     /* 123   { */,
+       124     /* 124   | */,
+       125     /* 125   } */,
+       126     /* 126   ~ */,
+       127     /* 127  DEL*/,
+       138     /* 128  Adieresis*/,
+       140     /* 129  Aring*/,
+       141     /* 130  Ccedilla*/,
+       142     /* 131  Eacute*/,
+       150     /* 132  Ntilda*/,
+       154     /* 133  Odieresis*/,
+       159     /* 134  Udieresis*/,
+       135     /* 135  aacute*/,
+       136     /* 136  agrave*/,
+       137     /* 137  acircumflex*/,
+       138     /* 138  adieresis*/,
+       139     /* 139  atilda*/,
+       140     /* 140  aring*/,
+       141     /* 141  ccedilla*/,
+       142     /* 142  eacute*/,
+       143     /* 143  egrave*/,
+       144     /* 144  ecircumflex*/,
+       145     /* 145  edieresis*/,
+       146     /* 146  iacute*/,
+       147     /* 147  igrave*/,
+       148     /* 148  icircumflex*/,
+       149     /* 149  idieresis*/,
+       132     /* 150  ntilda*/,
+       151     /* 151  oacute*/,
+       152     /* 152  ograve*/,
+       153     /* 153  ocircumflex*/,
+       154     /* 154  odieresis*/,
+       155     /* 155  otilda*/,
+       156     /* 156  uacute*/,
+       157     /* 157  ugrave*/,
+       158     /* 158  ucircumflex*/,
+       159     /* 159  udieresis*/,
+       160     /* 160  daggar*/,
+       161     /* 161  ring*/,
+       162     /* 162  cent*/,
+       163     /* 163  sterling*/,
+       164     /* 164  section*/,
+       165     /* 165  bullet*/,
+       166     /* 166  paragraph*/,
+       167     /* 167  germandbls*/,
+       168     /* 168  registered*/,
+       169     /* 169  copyright*/,
+       170     /* 170  trademark*/,
+       171     /* 171  acute*/,
+       172     /* 172  dieresis*/,
+       173     /* 173  notequal*/,
+       190     /* 174  AE*/,
+       191     /* 175  Oslash*/,
+       176     /* 176  infinity*/,
+       177     /* 177  plusminus*/,
+       178     /* 178  lessequal*/,
+       179     /* 179  greaterequal*/,
+       180     /* 180  yen*/,
+       181     /* 181  mu*/,
+       198     /* 182  delta*/,
+       183     /* 183  Sigma*/,
+       185     /* 184  Pi*/,
+       185     /* 185  pi*/,
+       186     /* 186  intergral*/,
+       187     /* 187  ordfeminine*/,
+       188     /* 188  ordmasculine*/,
+       189     /* 189  Omega*/,
+       190     /* 190  ae*/,
+       191     /* 191  oslash*/,
+       192     /* 192  questiondown*/,
+       193     /* 193  exclamdown*/,
+       194     /* 194  not*/,
+       195     /* 195  radical*/,
+       196     /* 196  florin*/,
+       197     /* 197  aprox*/,
+       198     /* 198  Delta*/,
+       199     /* 199  guillemotleft*/,
+       200     /* 200  guillemotright*/,
+       201     /* 201  ellipsis*/,
+       202     /* 202  */,
+       136     /* 203  Agrave*/,
+       139     /* 204  Atilda*/,
+       155     /* 205  Otilda*/,
+       207     /* 206  OE*/,
+       207     /* 207  oe*/,
+       208     /* 208  endash*/,
+       209     /* 209  emdash*/,
+       210     /* 210  quotedblleft*/,
+       211     /* 211  quotedblright*/,
+       212     /* 212  quoteleft*/,
+       213     /* 213  quoteright*/,
+       214     /* 214  divide*/,
+       215     /* 215  diamond*/,
+       217     /* 216  ydieresis*/,
+       217     /* 217  Ydieresis*/,
+       218     /* 218  fraction*/,
+       219     /* 219  currency*/,
+       220     /* 220  guilsinglleft*/,
+       221     /* 221  guilsinglright*/,
+       222     /* 222  fi*/,
+       223     /* 223  fl*/,
+       224     /* 224  daggardbl*/,
+       225     /* 225  periodcentered*/,
+       226     /* 226  quotesinglbase*/,
+       227     /* 227  quotedblbase*/,
+       228     /* 228  perthousand*/,
+       137     /* 229  Acircumflex*/,
+       144     /* 230  Ecircumflex*/,
+       135     /* 231  Aacute*/,
+       145     /* 232  Edieresis*/,
+       143     /* 233  Egrave*/,
+       146     /* 234  Iaccute*/,
+       148     /* 235  Icircumflex*/,
+       149     /* 236  Idieresis*/,
+       147     /* 237  Igrave*/,
+       151     /* 238  Oacute*/,
+       153     /* 239  Ocircumflex*/,
+       240     /* 240  apple*/,
+       152     /* 241  Ograve*/,
+       156     /* 242  Uacute*/,
+       158     /* 243  Ucircumflex*/,
+       157     /* 244  Ugrave*/,
+       245     /* 245  dotlessi*/,
+       246     /* 246  circumflex*/,
+       247     /* 247  tilda*/,
+       248     /* 248  macron*/,
+       249     /* 249  breve*/,
+       250     /* 250  dotaccent*/,
+       251     /* 251  ring*/,
+       252     /* 252  cedilla*/,
+       253     /* 253  hungarumlaut*/,
+       254     /* 254  ogonek*/,
+       255     /* 255  caron*/,
+};
+
+int strdiacasecmp( s1, s2 )
+    const unsigned char        *s1, *s2;
+{
+    while ( _diacasemap[ *s1 ] == _diacasemap[ *s2++ ] ) {
+       if ( *s1++ == '\0' ) {
+           return( 0 );
+       }
+    }
+    return( _diacasemap[ *s1 ] - _diacasemap[ *--s2 ] );
+}
+
+int strndiacasecmp( s1, s2, n )
+    const unsigned char        *s1, *s2;
+    int                        n;
+{
+    while ( --n >= 0 && _diacasemap[ *s1 ] == _diacasemap[ *s2++ ] ) {
+       if ( *s1++ == '\0' ) {
+           return( 0 );
+       }
+    }
+    return( n < 0 ? 0 : _diacasemap[ *s1 ] - _diacasemap[ *--s2 ] );
+}
diff --git a/lp2pap.sh b/lp2pap.sh
new file mode 100755 (executable)
index 0000000..fea0cf4
--- /dev/null
+++ b/lp2pap.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+# pap script for lp systems
+
+chdir "/etc/lp/printers/`basename $0`"
+
+if [ -r "$6" ]; then
+    :BINDIR:/pap -E "$6"
+    exit $?
+fi
+
+exit 2
diff --git a/man/Makefile b/man/Makefile
new file mode 100644 (file)
index 0000000..9028cca
--- /dev/null
@@ -0,0 +1,41 @@
+ALL=   man1 man3 man4 man8
+TAGSFILE=tags
+CC=cc
+INSTALL=       install
+
+all:   ${ALL}
+
+${ALL}: FRC
+       cd $@; ${MAKE} ${MFLAGS} CC="${CC}" DEFS="${DEFS}" \
+           OPTOPTS="${OPTOPTS}" DESTDIR="${DESTDIR}" MANDIR="${MANDIR}" \
+           SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+           ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}"
+
+
+FRC:
+
+tags:
+       for i in ${ALL}; do \
+           (cd $$i; ${MAKE} ${MFLAGS} DEFS="${DEFS}" \
+               TAGSFILE=../${TAGSFILE} tags); \
+       done
+
+install:
+       -mkdir ${MANDIR}
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} \
+               DESTDIR="${DESTDIR}"  MANDIR="${MANDIR}" \
+               SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+               ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+               INSTALL="${INSTALL}" install); \
+       done
+
+clean:
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} clean); \
+       done
+
+depend:
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} DEFS=${DEFS} depend); \
+       done
diff --git a/man/man1/Makefile b/man/man1/Makefile
new file mode 100644 (file)
index 0000000..9276cbf
--- /dev/null
@@ -0,0 +1,46 @@
+SRC=   aecho.1 getzones.1 nbp.1 nbplkup.1 nbprgstr.1 \
+       pap.1 papstatus.1 psorder.1 megatron.1 unhex.1 unbin.1 \
+       unsingle.1 macbinary.1 hqx2bin.1 single2bin.1
+SRCTMP= aecho.1.tmp getzones.1.tmp nbp.1.tmp nbplkup.1.tmp nbprgstr.1.tmp \
+       pap.1.tmp papstatus.1.tmp psorder.1.tmp megatron.1.tmp \
+       unhex.1.tmp unbin.1.tmp unsingle.1.tmp macbinary.1.tmp \
+       hqx2bin.1.tmp single2bin.1.tmp
+
+INCPATH=
+CFLAGS=
+TAGSFILE=
+CC=
+INSTALL=       install
+
+LINKS=
+
+
+all: ${SRCTMP}
+
+${SRCTMP}: ../../Makefile
+       for i in ${SRC} ; do \
+               sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+               -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+               -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+               -e s@:INCDIR:@${INCDIR}@ \
+               < $$i > $$i.tmp; \
+       done
+
+install : ${SRCTMP}
+       -mkdir ${MANDIR}/man1
+       for i in ${SRC} ; do \
+           rm -f ${MANDIR}/man1/$$i; \
+           ${INSTALL} -m644 $$i.tmp ${MANDIR}/man1/$$i; \
+       done
+
+clean : 
+       for i in ${SRC}; do \
+           rm -f $$i.tmp; \
+       done
+
+tags : ${SRC}
+
+depend :
+
+# DO NOT DELETE THIS LINE
+
diff --git a/man/man1/aecho.1 b/man/man1/aecho.1
new file mode 100644 (file)
index 0000000..c816200
--- /dev/null
@@ -0,0 +1,68 @@
+.TH AECHO 1 "17 Dec 1991" "netatalk 1.2"
+.SH NAME
+aecho \- send AppleTalk Echo Protocol packets to network hosts
+.SH SYNOPSIS
+.B aecho
+[
+.B \-c\fI count
+]
+(
+.I address
+|
+.I nbpname
+)
+.SH DESCRIPTION
+.B aecho
+repeatedly sends an Apple Echo Protocol (AEP) packet to the host
+specified by the given AppleTalk
+.I address
+or
+.I nbpname
+and reports whether a reply was received.  Requests are sent at the
+rate of one per second.
+.LP
+.I address
+is parsed by
+.BR atalk_aton (3).
+.I nbpname
+is parsed by
+.BR nbp_name (3).
+The nbp type defaults to
+.RB ` Workstation '.
+.LP
+When
+.B aecho
+is terminated, it reports the number of packets sent, the number of
+responses received, and the percentage of packets lost.  If any
+responses were received, the minimum, average, and maximum round trip
+times are reported.
+.SH EXAMPLE
+Check to see if a particular host is up and responding to AEP packets:
+.sp
+.RS
+.nf
+       example% aecho bloodsport
+       11 bytes from 8195.13: aep_seq=0. time=10. ms
+       11 bytes from 8195.13: aep_seq=1. time=10. ms
+       11 bytes from 8195.13: aep_seq=2. time=10. ms
+       11 bytes from 8195.13: aep_seq=3. time=10. ms
+       11 bytes from 8195.13: aep_seq=4. time=10. ms
+       11 bytes from 8195.13: aep_seq=5. time=9. ms
+       ^C
+       ----8195.13 AEP Statistics----
+       6 packets sent, 6 packets received, 0% packet loss
+       round-trip (ms)  min/avg/max = 9/9/10
+.fi
+.RE
+.SH OPTIONS
+.TP
+.B \-c\fI count
+Stop after
+.I count
+packets.
+.SH SEE ALSO
+.BR ping (1),
+.BR atalk_aton (3),
+.BR nbp_name (3),
+.\" .BR aep (4),
+.BR atalkd (8).
diff --git a/man/man1/getzones.1 b/man/man1/getzones.1
new file mode 100644 (file)
index 0000000..188df2d
--- /dev/null
@@ -0,0 +1,39 @@
+.TH GETZONES 1 "17 Dec 1991" "netatalk 1.2"
+.SH NAME
+getzones \- list AppleTalk zone names
+.SH SYNOPSIS
+.B getzones
+[
+.B -m
+|
+.B -l
+] [
+.I address
+]
+.SH DESCRIPTION
+.B Getzones
+is used to obtain a list of AppleTalk zone names using the Zone
+Information Protocol (ZIP).  It sends a GetZoneList request to an
+AppleTalk router.  By default, it sends the request to the locally
+running
+.BR atalkd (8).
+.SH OPTIONS
+.TP
+.B -m
+List the name of the local zone only; this is accomplished by sending a
+ZIP GetMyZone request.
+.TP
+.B -l
+List the local zones; this is accomplished by sending a GetLocalZones
+request.
+.TP
+.I address
+Contact the AppleTalk router at
+.I address.
+.I address
+is parsed by
+.BR atalk_aton (3).
+.SH SEE ALSO
+.BR atalk_aton (3),
+.\" .BR zip (4),
+.BR atalkd (8).
diff --git a/man/man1/hqx2bin.1 b/man/man1/hqx2bin.1
new file mode 100644 (file)
index 0000000..40dd5c2
--- /dev/null
@@ -0,0 +1 @@
+.so man1/megatron.1
diff --git a/man/man1/macbinary.1 b/man/man1/macbinary.1
new file mode 100644 (file)
index 0000000..40dd5c2
--- /dev/null
@@ -0,0 +1 @@
+.so man1/megatron.1
diff --git a/man/man1/megatron.1 b/man/man1/megatron.1
new file mode 100644 (file)
index 0000000..1b8be12
--- /dev/null
@@ -0,0 +1,104 @@
+.TH MEGATRON 1 "8 Jan 1992" "netatalk 1.2"
+.SH NAME
+megatron, unhex, unbin, unsingle, hqx2bin, single2bin, macbinary \- Macintosh file format transformer
+.SH SYNOPSIS
+.B megatron
+[
+.I sourcefile...
+]
+.LP
+.B unbin
+[
+.I sourcefile...
+]
+.LP
+.B unhex
+[
+.I sourcefile...
+]
+.LP
+.B unsingle
+[
+.I sourcefile...
+]
+.LP
+.B hqx2bin
+[
+.I sourcefile...
+]
+.LP
+.B single2bin
+[
+.I sourcefile...
+]
+.LP
+.B macbinary
+[
+.I sourcefile...
+]
+.SH DESCRIPTION
+.B megatron
+is used to transform files from BinHex, MacBinary, AppleSingle, or 
+.B netatalk
+style AppleDouble formats into MacBinary or
+.B netatalk 
+style AppleDouble formats.  The
+.B netatalk
+style AppleDouble format is the file format used by
+.B afpd,
+the
+.B netatalk
+Apple Filing Protocol (AppleShare) server.  BinHex, MacBinary, and
+AppleSingle are commonly used formats for transferring Macintosh files
+between machines via email or file transfer protocols.
+.B megatron
+uses its name to determine what type of tranformation is being asked of
+it.
+.LP
+If
+.B megatron
+is called as
+.B unhex, unbin,
+or
+.B unsingle,
+it tries to convert file(s) from BinHex, MacBinary, or AppleSingle into
+AppleDouble format.  BinHex is the format most often used to send
+Macintosh files by e-mail.  Usually these files have an extension of
+".hqx".  MacBinary is the format most often used by terminal emulators
+"on the fly" when transferring Macintosh files in binary mode.
+MacBinary files often have an extension of ".bin".  Some Macintosh
+LAN-based email packages use uuencoded AppleSingle format to "attach"
+or "enclose" files in email.  AppleSingle files don't have a standard
+filename extension.
+.LP
+If
+.B megatron
+is called as
+.B hqx2bin, single2bin,
+or
+.B macbinary,
+it will try to convert the file(s) from BinHex, AppleSingle, or
+AppleDouble into MacBinary.  This last translation may be useful in
+moving Macintosh files from your
+.B afpd
+server to some other machine when you can't copy them from the server
+using a Macintosh for some reason.
+.LP
+If
+.B megatron
+is called with any other name, it uses the default translation, namely
+.B unhex.
+.LP
+If no source file is given, or if
+.I sourcefile
+is
+.RB ` - ',
+and if the conversion is from a BinHex or MacBinary file,
+.B megatron
+will read from standard input.
+.LP
+The filename used to store any output file is the filename that is
+encoded in the source file.  MacBinary files are created with a ".bin"
+extension.  In the case of conflicts, the old file is overwritten!
+.SH SEE ALSO
+.BR afpd (8)
diff --git a/man/man1/nbp.1 b/man/man1/nbp.1
new file mode 100644 (file)
index 0000000..fe2607e
--- /dev/null
@@ -0,0 +1,82 @@
+.TH NBP 1 "17 Dec 1991" "netatalk 1.2"
+.SH NAME
+nbplkup, nbprgstr, nbpunrgstr \- access NBP database
+.SH SYNOPSIS
+.B nbplkup
+[
+.B -r
+.I nresp
+]
+[
+.I nbpname
+]
+.sp
+.B nbprgstr
+[
+.B -p
+.I port
+]
+.I nbpname
+.sp
+.B nbpunrgstr
+.I nbpname
+.SH DESCRIPTION
+.B nbprgstr
+registers
+.I nbpname
+with
+.BR atalkd (8),
+at the given
+.IR port .
+.B nbpunrgstr
+informs
+.B atalkd
+that
+.I nbpname
+is no longer to be advertised.
+.LP
+.B nbplkup
+displays up to
+.I nresp
+(default 1000) entities registered on the AppleTalk internet.
+.I nbpname
+is parsed by
+.BR nbp_name (3).
+An
+.RB ` = '
+for the
+.I object
+or
+.I type
+matches anything, and an
+.RB ` * '
+for
+.I zone
+means the local zone. The default values are taken from the
+.B NBPLKUP
+environment variable, parsed as an
+.IR nbpname .
+.SH EXAMPLE
+Find all devices of type
+.B LaserWriter
+in the local zone.
+.sp
+.RS
+.nf
+example% nbplkup :LaserWriter
+               Petoskey:LaserWriter        7942.129:218
+             Gloucester:LaserWriter        8200.188:186
+                 Rahway:LaserWriter        7942.2:138
+             517 Center:LaserWriter        7942.2:132
+                  ionia:LaserWriter        7942.2:136
+     Evil DEC from Hell:LaserWriter        7942.2:130
+              Hamtramck:LaserWriter        7942.2:134
+         Iron Mountain :LaserWriter        7942.128:250
+example%
+.fi
+.RE
+.SH SEE ALSO
+.BR nbp_name (3),
+.\" .BR nbp (4),
+.\" .BR zip (4),
+.BR atalkd (8).
diff --git a/man/man1/nbplkup.1 b/man/man1/nbplkup.1
new file mode 100644 (file)
index 0000000..ac41808
--- /dev/null
@@ -0,0 +1 @@
+.so man1/nbp.1
diff --git a/man/man1/nbprgstr.1 b/man/man1/nbprgstr.1
new file mode 100644 (file)
index 0000000..ac41808
--- /dev/null
@@ -0,0 +1 @@
+.so man1/nbp.1
diff --git a/man/man1/nbpunrgstr.1 b/man/man1/nbpunrgstr.1
new file mode 100644 (file)
index 0000000..ac41808
--- /dev/null
@@ -0,0 +1 @@
+.so man1/nbp.1
diff --git a/man/man1/pap.1 b/man/man1/pap.1
new file mode 100644 (file)
index 0000000..8e3a05b
--- /dev/null
@@ -0,0 +1,123 @@
+.TH PAP 1 "3 Jun 1994" "netatalk 1.3"
+.SH NAME
+pap, papstatus \- client interface to remote printers using Printer Access Protocol
+.SH SYNOPSIS
+.B pap
+[
+.B -c
+] [
+.B -e
+] [
+.B -p
+.I nbpname
+] [
+.B -s
+.I statusfile
+] [
+.I files
+]
+.sp
+.B papstatus
+[
+.B -p
+.I nbpname
+]
+.SH DESCRIPTION
+.B pap
+is used to connect and send files to an AppleTalk connected printer using
+the Apple Printer Access Protocol (PAP).
+When
+.B pap
+starts execution, it
+tries to open a session with the printer using PAP, and
+then downloads the
+.I files
+to the printer.
+.LP
+If no
+.I files
+are given on the command line,
+.B pap
+begins reading from standard input.
+.LP
+If no printer is specified on the command line,
+.B pap
+looks for a file called
+.B .paprc
+in the current working directory and reads it to obtain the
+.I nbpname
+of a
+printer.
+Blank lines and lines that begin with a
+.RB ` # '
+are ignored.
+.I type
+and
+.I zone
+default to
+.B LaserWriter
+and the zone of the local host, respectively.
+.LP
+Note that
+.B pap
+is designed to be useful as a communication filter for sending
+.BR lpd (8)
+spooled print jobs to AppleTalk connected printers.  See
+.BR psf (8)
+for hints on how to use it this way.
+.SH OPTIONS
+.TP
+.B -c
+Take cuts.  Normally
+.B pap
+tells the printer how long it has been waiting.  When
+.B -c
+is specified,
+.B pap
+claims to have been waiting forever.
+.TP
+.B -e
+Send any message from the printer to stderr instead of stdout.
+.BR psf (8)
+invokes
+.B pap
+with this option.
+.HP
+.B -p
+.I nbpname
+.br
+Connect to the printer named
+.I nbpname
+and do not consult the
+.B .paprc
+file to find a printer name.  See
+.BR nbp_name (3)
+for the syntax of
+.IR nbpname .
+.HP
+.B -s
+.I statusfile
+.br
+Update the file called
+.I statusfile
+to contain the most recent status message from the printer.
+.B pap
+gets the status from the printer when it is waiting for the printer to
+process input.  The
+.I statusfile
+will contain a single line terminated with a newline.  This is useful
+when
+.B pap
+is invoked by
+.BR psf (8)
+within
+.BR lpd 's
+spool directory.
+.SH FILES
+.TP 20
+.B .paprc
+file that contains printer name
+.SH SEE ALSO
+.BR nbp_name (3),
+.BR lpd (8),
+.BR psf (8).
diff --git a/man/man1/papstatus.1 b/man/man1/papstatus.1
new file mode 100644 (file)
index 0000000..631797d
--- /dev/null
@@ -0,0 +1 @@
+.so man1/pap.1
diff --git a/man/man1/psorder.1 b/man/man1/psorder.1
new file mode 100644 (file)
index 0000000..41aa36b
--- /dev/null
@@ -0,0 +1,48 @@
+.TH PSORDER 1 "17 Dec 1991" "netatalk 1.2"
+.SH NAME
+psorder \- PostScript pageorder filter
+.SH SYNOPSIS
+.B psorder
+[
+.B -duf
+]
+.I sourcefile
+.SH DESCRIPTION
+.B psorder
+is a filter that re-orders the pages of a PostScript document.
+The result is written to the standard output.  By default,
+documents are processed into ascending order (the lowest numbered page
+is printed first).  Some PostScript documents specify that the order of
+their pages should never be changed; the default action of
+.B psorder
+is to follow this specification.
+.LP
+If no source file is given, or if 
+.I sourcefile
+is
+.RB ` - ',
+.B psorder
+reads from the standard input file.
+.br
+.SH OPTIONS
+.TP
+.B \-d
+Re-order the pages of the document in downward or descending
+order.  This is typically used to change the order of a document to
+be printed by a printer that stacks pages face-up,
+such as an Apple LaserWriter or LaserWriter Plus.
+.TP
+.B \-u
+Specifies forward order, and is the default.  It is used to
+try and ensure the correct ordering when a document will be printed
+by a printer that stacks the pages face-down.
+.TP
+.B \-f
+Force
+.B psorder
+to re-order the pages, even if the document claims that its page
+order is not to be trifled with.  This option should only be used 
+experimentally, as it may cause documents to print incorrectly.
+.SH SEE ALSO
+.BR psf (8),
+.BR lpr (1).
diff --git a/man/man1/single2bin.1 b/man/man1/single2bin.1
new file mode 100644 (file)
index 0000000..40dd5c2
--- /dev/null
@@ -0,0 +1 @@
+.so man1/megatron.1
diff --git a/man/man1/unbin.1 b/man/man1/unbin.1
new file mode 100644 (file)
index 0000000..40dd5c2
--- /dev/null
@@ -0,0 +1 @@
+.so man1/megatron.1
diff --git a/man/man1/unhex.1 b/man/man1/unhex.1
new file mode 100644 (file)
index 0000000..40dd5c2
--- /dev/null
@@ -0,0 +1 @@
+.so man1/megatron.1
diff --git a/man/man1/unsingle.1 b/man/man1/unsingle.1
new file mode 100644 (file)
index 0000000..40dd5c2
--- /dev/null
@@ -0,0 +1 @@
+.so man1/megatron.1
diff --git a/man/man3/Makefile b/man/man3/Makefile
new file mode 100644 (file)
index 0000000..a002eb9
--- /dev/null
@@ -0,0 +1,40 @@
+SRC=   atalk_aton.3 nbp_name.3
+SRCTMP= atalk_aton.3.tmp nbp_name.3.tmp
+
+INCPATH=
+CFLAGS=
+TAGSFILE=
+CC=
+INSTALL=       install
+
+LINKS=
+
+all : ${SRCTMP}
+
+${SRCTMP}: ../../Makefile
+       for i in ${SRC} ; do \
+           sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+               -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+               -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+               -e s@:INCDIR:@${INCDIR}@ \
+               < $$i > $$i.tmp; \
+       done
+
+install : ${SRCTMP}
+       -mkdir ${MANDIR}/man3
+       for i in ${SRC} ; do \
+           rm -f ${MANDIR}/man3/$$i; \
+           ${INSTALL} -m644 $$i.tmp ${MANDIR}/man3/$$i; \
+       done
+
+clean :
+       for i in ${SRC}; do \
+           rm -f $$i.tmp; \
+       done
+
+tags : ${SRC}
+
+depend :
+
+# DO NOT DELETE THIS LINE
+
diff --git a/man/man3/atalk_aton.3 b/man/man3/atalk_aton.3
new file mode 100644 (file)
index 0000000..f7949eb
--- /dev/null
@@ -0,0 +1,24 @@
+.TH ATALK_ATON 3 "12 Jan 1994" "netatalk 1.3"
+.SH NAME
+atalk_aton \- AppleTalk address parsing
+.SH SYNOPSIS
+.nf
+#include <sys/types.h>
+#include <netatalk/at.h>
+.LP
+atalk_aton( cp, ata )
+char *cp;
+struct at_addr *ata;
+.fi
+.SH DESCRIPTION
+The
+.B atalk_aton()
+routine converts an ascii representation of an AppleTalk address to a
+format appropriate for system calls.  Acceptable ascii representations
+include both hex and base 10, in triples or doubles.  For instance, the
+address `0x1f6b.77' has a network part of `8043' and a node part of
+`119'.  This same address could be written `8043.119', `31.107.119', or
+`0x1f.6b.77'.  If the address is in hex and the first digit is one of
+`A-F', a leading `0x' is redundant.
+.SH SEE ALSO
+.BR atalk (4).
diff --git a/man/man3/nbp_name.3 b/man/man3/nbp_name.3
new file mode 100644 (file)
index 0000000..9c41cc4
--- /dev/null
@@ -0,0 +1,79 @@
+.TH NBP_NAME 3 "12 Jan 1994" "netatalk 1.3"
+.SH NAME
+nbp_name \- NBP name parsing
+.SH SYNOPSIS
+.nf
+nbp_name( name, obj, type, zone )
+char *name, **obj, **type, **zone;
+.fi
+.SH DESCRIPTION
+.B nbp_name()
+parses user supplied names into their component object, type, and
+zone.
+.BR obj ,
+.BR type ,
+and
+.B zone
+should be passed by reference, and should point to the caller's default
+values.
+.B nbp_name()
+will change the pointers to the parsed-out values.
+.B name
+is of the form
+.IB object : \c
+.IB type @ \c
+.IR zone ,
+where each of
+.IR object ,
+.BI : type ,
+and
+.BI @ zone
+replace
+.BR obj ,
+.BR type ,
+and
+.BR zone,
+respectively.
+.I type
+must be proceeded by
+.RB ` : ',
+and
+.I zone
+must be preceded by
+.RB ` @ '.
+.SH EXAMPLE
+The argument of
+.BR afpd (8)'s
+.B -n
+option is parsed with
+.BR nbp_name() .
+The default value of
+.B obj
+is the first component of the machine's hostname (as returned by
+.BR gethostbyname (3)).
+The default value of
+.B type
+is ``AFPServer'', and of
+.B zone
+is ``*'', the default zone.  To cause
+.B afpd
+to register itself in some zone other than the default, one would
+invoke it as
+.sp
+.RS
+.nf
+afpd -n @some-other-zone
+.fi
+.RE
+.sp
+.B obj
+and
+.B type
+would retain their default values.
+.SH BUGS
+.BR obj ,
+.BR type ,
+and
+.B zone
+return pointers into static area which may be over-written on each
+call.
diff --git a/man/man4/Makefile b/man/man4/Makefile
new file mode 100644 (file)
index 0000000..4b9a7ac
--- /dev/null
@@ -0,0 +1,39 @@
+SRC=   atalk.4
+SRCTMP= atalk.4.tmp
+INCPATH=
+CFLAGS=
+TAGSFILE=
+CC=
+INSTALL=       install
+
+LINKS=
+
+all : ${SRCTMP}
+
+${SRCTMP}: ../../Makefile
+       for i in ${SRC} ; do \
+           sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+                   -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+                   -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+                   -e s@:INCDIR:@${INCDIR}@ \
+                   < $$i > $$i.tmp; \
+       done
+
+install : ${SRCTMP}
+       -mkdir ${MANDIR}/man4
+       for i in ${SRC} ; do \
+           rm -f ${MANDIR}/man4/$$i; \
+           ${INSTALL} -m644 $$i.tmp ${MANDIR}/man4/$$i; \
+       done
+
+clean :
+       for i in ${SRC}; do \
+           rm -f $$i.tmp; \
+       done
+
+tags : ${SRC}
+
+depend :
+
+# DO NOT DELETE THIS LINE
+
diff --git a/man/man4/atalk.4 b/man/man4/atalk.4
new file mode 100644 (file)
index 0000000..ecb5c27
--- /dev/null
@@ -0,0 +1,63 @@
+.TH ATALK 4F "17 Dec 1991" "netatalk 1.2"
+.SH NAME
+atalk \- AppleTalk protocol family
+.SH SYNOPSIS
+.B #include <sys/types.h>
+.br
+.B #include <netatalk/at.h>
+.SH DESCRIPTION
+The AppleTalk protocol family is a collection of protocols layered
+above the Datagram Delivery Protocol (DDP), and using AppleTalk address
+format. The AppleTalk family may provide SOCK_STREAM (ADSP), SOCK_DGRAM
+(DDP), SOCK_RDM (ATP), and SOCK_SEQPACKET (ASP). Currently, only DDP is
+implemented in the kernel; ATP and ASP are implemented in user level
+libraries; and ADSP is planned.
+.SH ADDRESSING
+AppleTalk addresses are three byte quantities, stored in network
+byte order. The include file
+.RB < netatalk/at.h >
+defines the AppleTalk address format.
+.LP
+Sockets in the AppleTalk protocol family use the following address
+structure:
+.sp 1
+.RS
+.nf
+struct sockaddr_at {
+       short           sat_family;
+       u_char          sat_port;
+       struct at_addr  sat_addr;
+       char            sat_zero[ 8 ];
+};
+.fi
+.RE
+.sp 1
+The port of a socket may be set with
+.BR bind (2).
+The node for
+.B bind
+must always be
+.BR ATADDR_ANYNODE :
+``this node.'' The net may be
+.B ATADDR_ANYNET
+or
+.BR ATADDR_LATENET .
+.B ATADDR_ANYNET
+coresponds to the machine's ``primary'' address (the first
+configured).
+.B ATADDR_LATENET
+causes the address in outgoing packets to be determined when a packet
+is sent, i.e. determined late.
+.B ATADDR_LATENET
+is equivalent to opening one socket for each network interface.  The
+port of a socket and either the primary address or
+.B ATADDR_LATENET
+are returned with
+.BR getsockname (2).
+.SH SEE ALSO
+.\" .BR ddp (4P),
+.\" .BR atp (3N),
+.\" .BR asp (3N),
+.BR bind (2),
+.BR getsockname (2),
+.BR atalkd (8).
diff --git a/man/man8/Makefile b/man/man8/Makefile
new file mode 100644 (file)
index 0000000..8e4bce6
--- /dev/null
@@ -0,0 +1,40 @@
+SRC=   afpd.8 atalkd.8 papd.8 psf.8
+SRCTMP= afpd.8.tmp atalkd.8.tmp papd.8.tmp psf.8.tmp
+
+INCPATH=
+CFLAGS=
+TAGSFILE=
+CC=
+INSTALL=       install
+
+LINKS=
+
+all : ${SRCTMP}
+
+${SRCTMP}: ../../Makefile
+       for i in ${SRC} ; do \
+           sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+               -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+               -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+               -e s@:INCDIR:@${INCDIR}@ \
+               < $$i > $$i.tmp; \
+       done
+
+install : ${SRCTMP}
+       -mkdir ${MANDIR}/man8
+       for i in ${SRC} ; do \
+           rm -f ${MANDIR}/man8/$$i; \
+           ${INSTALL} -m644 $$i.tmp ${MANDIR}/man8/$$i; \
+       done
+
+clean :
+       for i in ${SRC}; do \
+           rm -f $$i.tmp; \
+       done
+
+tags : ${SRC}
+
+depend :
+
+# DO NOT DELETE THIS LINE
+
diff --git a/man/man8/afpd.8 b/man/man8/afpd.8
new file mode 100644 (file)
index 0000000..e0be1b9
--- /dev/null
@@ -0,0 +1,261 @@
+.TH AFPD 8 "23 Feb 1999" "netatalk 1.4b2/asun 2.1.3"
+
+.SH NAME
+afpd \- AppleTalk Filing Protocol daemon
+.SH SYNOPSIS
+.B :SBINDIR:/afpd
+[
+.B -d
+]
+[
+.B -f
+.I defaultvolumes
+]
+[
+.B -s
+.I systemvolumes
+]
+[
+.B -u
+]
+[
+.B -n
+.I nbpname
+]
+[
+.B -c
+.I maxconnections
+]
+[
+.B -g
+.I guest
+]
+[
+.B -G
+]
+[
+.B -K
+]
+[
+.B -C
+]
+[
+.B -A
+]
+.SH DESCRIPTION
+.B afpd
+provides an AppleTalk Filing Protocol (AFP)
+interface to the Unix file system.  It is normally started at boot time
+from
+.BR /etc/rc .
+The list of volumes offered to the user is generated from
+.B :ETCDIR:/AppleVolumes.system
+and one of
+.BR :ETCDIR:/AppleVolumes.default ,
+.BR $HOME/AppleVolumes ,
+or
+.BR $HOME/.AppleVolumes .
+.LP
+The
+.B AppleVolumes
+files is used to specify volumes to mount and file name extension mappings.
+It is formatted as follows, one specification per line:
+.RS
+.sp
+.I pathname
+[
+.I volumename
+]
+.br
+.RI . extension
+[
+.I type
+[
+.I creator
+]
+]
+.sp
+.RE
+If
+.I volumename
+is unspecified, the last component of
+.I pathname
+is used.  No two volumes may have the same name.  If
+.I type
+is unspecified
+.RB ' ???? '
+is used.  If
+.I creator
+is unspecified
+.RB ' UNIX '
+is used.  The extension
+.RB ' . '
+sets the default creator and type for otherwise untyped Unix files.
+Blank lines and lines beginning with `#' are ignored.
+.SH OPTIONS
+.TP
+.B \-d
+Specifies that the daemon not fork, and that a trace of all AFP
+commands be written to stdout.
+.TP
+.BI \-f " defaultvolumes"
+Specifies that
+.I defaultvolumes
+should be read for a list of default volumes to offer, instead of
+.BR :ETCDIR:/AppleVolumes.default .
+.TP
+.BI \-s " systemvolumes"
+Specifies that
+.I systemvolumes
+should be read for a list of volume that all users will be offered,
+instead of
+.BR :ETCDIR:/AppleVolumes.system .
+.TP
+.B \-u
+Read the user's
+.B AppleVolumes
+file first.  This option causes volume names in the user's
+.B AppleVolumes
+file to override volume names in the system's
+.B AppleVolumes
+file.  The default is to read the system
+.B AppleVolumes
+file first.  Note that this option doesn't effect the precendence of
+filename extension mappings: the user's AppleVolumes file always has
+precedence.
+.TP
+.BI \-n " nbpname"
+Specifies that
+.I nbpname
+should be used for NBP registration, instead of the first component of
+the hostname in the local zone.
+.TP
+.BI \-c " maxconnections"
+Specifies the maximum number of connections to allow for this
+.BR afpd .
+The default is 5.
+.TP
+.BI \-g " guest"
+Specifies the name of the guest account.  The default is ``nobody''.
+.TP
+.B \-G
+.TP
+.B \-K
+.TP
+.B \-C
+.TP
+.B \-A
+Causes the server to not offer
+.BR NoUserAuthent ,
+.BR "Kerberos IV" ,
+.BR "Cleartxt Passwrd" ,
+and
+.B AFS Kerberos
+logins, respectively.  The default is to enable all available login methods.
+.SH AUTHENTICATION
+.B afpd
+currently understands three User Authentication Methods (UAMs):
+.BR NoUserAuthent ,
+or guest,
+.B Cleartxt
+.BR passwrd ,
+and
+.B Kerberos
+.BR IV .
+If a user uses
+.BR NoUserAuthent ,
+s/he will only be offered default volumes to mount, and will only be able
+to read and write files that are permitted to the guest user.  The
+.B -G
+option disables
+.BR NoUserAuthent .
+With
+.B Cleartxt passwd
+and
+.B Kerberos
+.BR IV ,
+.B afpd
+offers the user all volumes listed in
+.BR $HOME/AppleVolumes .
+The user may also read and write all files that s/he normally could.
+.B Cleartxt passwd
+is not recommended for AFS use.
+.B Kerberos IV
+is recommended for AFS use.
+A forth, depricated UAM is also included in the distribution,
+.B AFS
+.BR Kerberos .
+.SH CAVEATS
+.BR afpd 's
+Directory IDs are only fixed for the duration of a session.  This means
+that Mac aliases won't work correctly in all cases.
+.LP
+If a user renames a folder that has an application as its progeny, the
+.B APPL
+mapping for the application will not longer be available. This implies
+that double-clicking on one of the application's documents will no
+longer launch the application. The
+.B APPL
+mapping will be rebuilt by the mac, the next time the Finder see the
+application.
+.LP
+If
+.B afpd
+is configured to downcase Macintosh filenames, Unix filenames with
+mixed case will be unavailable.
+.LP
+If carriage return/line feed translation is enabled, it is not
+safe to copy Unix binaries to a Macintosh.
+.LP
+It is not possible to move directories between devices.
+.LP
+When mounting the parent of an existing volume, the desktop database of
+the existing volume will not be available to the parent volume.  The
+.B APPL
+mappings and icons of applications with the
+.B BNDL
+bit set will be generated in the parent volume as the applications are
+seen by the Finder.
+.LP
+If a user edits his
+.B $HOME/AppleVolumes
+so that his home directory is no longer offered, he will no longer be able
+to edit his
+.B $HOME/AppleVolumes
+from the Macintosh.
+.LP
+Unix files beginning with `.' are not accessible from the mac.
+.LP
+If the
+.I pathname
+in an
+.B $HOME/AppleVolumes
+file does not exist, the volume will not be offered in the Chooser.
+.LP
+Microsoft Word
+.B TEXT
+documents do not get carriage return/line feed translation.  This is
+because MS Word uses a type other than
+.B TEXT
+while writing the document, then changes the type to
+.BR TEXT .
+To allow users to edit their
+.BR $HOME/AppleVolumes ,
+.B afpd
+parses the files with either end of line character.
+.LP
+Unix filenames that are longer than 31 characters are inaccessible from
+the Macintosh.
+.SH FILES
+.TP 20
+.B :ETCDIR:/AppleVolumes.default
+list of default volumes to mount
+.TP 20
+.B :ETCDIR:/AppleVolumes.system
+list of volumes to offer all users
+.TP 20
+.B $HOME/AppleVolumes
+user's list of volumes to mount
+.SH BUGS
+A few calls from the AFP specification are not implemented, because the
+Macintosh does not use them.
diff --git a/man/man8/atalkd.8 b/man/man8/atalkd.8
new file mode 100644 (file)
index 0000000..c6e4d8b
--- /dev/null
@@ -0,0 +1,123 @@
+.TH ATALKD 8 "17 Nov 1995" "netatalk 1.3"
+.SH NAME
+atalkd \- AppleTalk RTMP, NBP, ZIP, and AEP manager
+.SH SYNOPSIS
+.B :SBINDIR:/atalkd
+[
+.B -f
+.I configfile
+] [
+.B -1
+|
+.B -2
+]
+.SH DESCRIPTION
+.B atalkd
+is responsible for all user level AppleTalk network management. This
+includes routing, name registration and lookup, zone lookup, and the
+AppleTalk Echo Protocol (similar to
+.BR ping (8)).
+.B atalkd
+is typically started at boot time, out of
+.B /etc/rc.
+It first reads from its configuration file,
+.BR :ETCDIR:/atalkd.conf .
+If there is no configuration file,
+.B atalkd
+will attempt to configure all available interfaces and will create a
+configuration file.  The file consists of a series of interfaces, one
+per line.  Lines with
+.RB ` # '
+in the first column are ignored, as are blank lines.  The syntax is
+.RS
+.sp
+.I interface
+[
+.B -seed
+] [
+.B -phase
+.I number
+] [
+.B -net
+.I net-range
+] [
+.B -addr
+.I address
+] [
+.B -zone
+.I zonename
+] ...
+.sp
+.RE
+Note that all field except the
+.I interface
+are optional.  The loopback interface is configured automatically.  If
+.B -seed
+is specified, all other fields must be present.  Also,
+.B atalkd
+will exit during bootstrapping, if a router disagrees with its seed
+information.  If
+.B -seed
+is not given, all other information may be overriden during
+auto-configuration.  If no
+.B -phase
+option is given, the default phase as given on the command line is used
+(the default is 2).  If
+.B -addr
+is given and
+.B -net
+is not, a
+.I net-range
+of one is assumed.
+.LP
+The first
+.B -zone
+directive for each interface is the ``default'' zone.  Under Phase 1, there
+is only one zone.  Under Phase 2, all routers on the network are
+configured with the default zone and must agree.
+.B atalkd
+maps ``*'' to the default zone of the first interface.  Note:  The
+default zone for a machine is determined by the configuration of the
+local routers; to appear in a non-default zone, each service, e.g.
+.BR afpd ,
+must individually specify the desired zone.  See also
+.BR nbp_name (3).
+.SH ROUTING
+If you are connecting a netatalk router to an existing AppleTalk
+internet, you should first contact your local network administrators to
+obtain appropriate network addresses.
+.LP
+.B atalkd
+can provide routing between interfaces by configuring multiple
+interfaces.  Each interface must be assigned a unique
+.I net-range
+between 1 and 65279 (0 and 65535 are illegal, and addresses between
+65280 and 65534 are reserved for startup).  It is best to choose the
+smallest useful
+.IR net-range ,
+i.e. if you have three machines on an Ethernet, don't chose a
+.I net-range
+of 1000-2000.  Each
+.I net-range
+may have an arbitrary list of zones associated with it.
+.SH EXAMPLE
+Below is an example configuration file for a sun4/40.  The machine has
+two interfaces, ``le0'' and ``le1''.  The ``le0'' interface is
+configured automatically from other routers on the network.  The
+machine is the only router for the ``le1'' interface.
+.sp
+.RS
+.nf
+le0
+le1 -seed -net 9461-9471 -zone netatalk -zone Argus
+.fi
+.RE
+.sp
+.B atalkd
+automatically acts as a router if there is more than one interface.
+.SH FILES
+.TP 30
+.B :ETCDIR:/atalkd.conf
+configuration file
+.SH BUGS
+On some systems, atalkd can not be restarted.
diff --git a/man/man8/pap.8 b/man/man8/pap.8
new file mode 100644 (file)
index 0000000..54bdb45
--- /dev/null
@@ -0,0 +1,165 @@
+.TH PAP 8 "13 Dec 1991" "netatalk 1.2"
+.SH NAME
+.B pap
+\- download files to or communicate interactively with an
+AppleTalk network connected printer
+.SH SYNOPSIS
+.B :SBINDIR:/pap
+[
+.BI -d
+] [
+.B -p
+.I printer
+] [
+.B -s
+.I statusfile
+] [
+.I files
+]
+.SH DESCRIPTION
+.B pap
+is used to connect and send files to an AppleTalk connected printer using
+the Apple Printer Access Protocol (PAP).
+.B pap
+can also be used to conduct an interactive session with a PostScript
+printer.  When
+.B pap
+starts execution, it first tries obtain the status of the printer.  It
+then tries to open a session with the printer using PAP, and
+then downloads the
+.I files
+to the printer.
+.LP
+If no
+.I files
+are given on the command line,
+.B pap
+begins reading from standard input.
+.LP
+If no printer is specified on the command line,
+.B pap
+looks for a file called
+.B .paprc
+in the current directory and reads it to obtain the name of a printer.  The
+.B .paprc
+file should contain a single line of the form
+.IB object : \c
+.IB type @ \c
+.I zone
+where each of
+.IR object ,
+.BI : type ,
+and
+.BI @ zone
+are optional.
+.I type
+and
+.I zone
+must be proceeded by
+.RB ` : '
+and
+.RB ` @ '
+respectively.  Lines the begin with a
+.RB ` # '
+are ignored.
+.I type
+and
+.I zone
+default to
+.B LaserWriter
+and the zone of the local host, respectively.
+.LP
+Note that
+.B pap
+is designed to be useful as a communication filter for sending
+.BR lpd (8)
+spooled print jobs to AppleTalk connected printers.  See
+.BR psf (8)
+for hints on how to use it this way.
+.SH OPTIONS
+.HP
+.B -p
+.I printer
+.br
+Connect to the printer named
+.I printer
+(do not consult the
+.B .paprc
+file to find a printer name).  The syntax for
+.I printer
+is the same as discussed above for the
+.B .paprc
+file.
+.HP
+.B -s
+.I statusfile
+.br
+Update the file called
+.I statusfile
+to contain the most recent status message from the printer.
+.B pap
+gets the status from the printer when it is waiting for the printer to
+process input.  The
+.I statusfile
+will contain a single line terminated with a newline.  This is useful
+when
+.B pap
+is invoked by
+.BR lpd (8)
+within
+.BR lpd 's
+spool directory.
+.HP
+.B -c
+.br
+Take cuts.  The PAP protocol specified a simple queuing procedure, such
+that the clients tell the printer how long they've been waiting to
+print.  This option causes
+.B pap
+to lie about how long it's been waiting.
+.HP
+.B -e
+.br
+Send stdout to stderr.  This causes information that the printer
+returns to be recorded as error output for lpd.
+.HP
+.B -E
+.br
+Don't wait for EOF from the printer.  This option is useful for
+printers which don't implement PAP correctly.  In a correct
+implementation, the client side should wait for the printer to return
+EOF before closing the connection.  Some clients don't wait, and hence
+some printers have related bugs in their implementation.
+.HP
+.B -w
+.br
+Wait for the printer's status to contain the word "waiting" before
+sending the job.  This is to defeat printer-side spool available on HP
+IV and V printers.
+.SH FILES
+.TP 20
+.B .paprc
+file that contains printer name
+.TP 20
+.B $HOME/.paprc
+secondary file to look in for printer name
+.SH SEE ALSO
+.BR nbp (1),
+.BR pap (4),
+.BR lpd (8),
+.BR papstatus (8),
+.BR psf (8).
+.SH BUGS
+.B pap
+will send a
+.B quit
+command to exit interactive mode when it gets an end-of-file
+on a tty.  If the user has already typed
+.B quit
+themselves, the
+.B quit
+that
+.B pap
+sends is spurious and will cause a PostScript error.  The fix would be for
+.B pap
+to watch what the user types and look for quit, but this is impractical.
diff --git a/man/man8/papd.8 b/man/man8/papd.8
new file mode 100644 (file)
index 0000000..d9d7a4e
--- /dev/null
@@ -0,0 +1,142 @@
+'\" t
+.TH PAPD 8 "17 Apr 1995" "netatalk 1.3.3"
+.SH NAME
+papd \- AppleTalk print server daemon
+.SH SYNOPSIS
+.B :SBINDIR:/papd
+[
+.B -d
+] [
+.B -f
+.I configfile
+] [
+.B -p
+.I printcap
+]
+.SH DESCRIPTION
+.B papd
+is the AppleTalk printer daemon.  This daemon accepts print jobs from
+AppleTalk clients (typically Macintosh computers) using the Printer
+Access Protocol (PAP).
+.B papd
+spools jobs directly into an
+.BR lpd (8)
+spool directory and wakes up
+.B lpd
+after accepting a job from the network to have it re-examine the
+appropriate spool directory.  The actual printing and spooling is
+handled entirely by
+.B lpd.
+.LP
+.B papd
+is typically started at boot time, out of
+.B /etc/rc.
+It first reads from its configuration file,
+.BR :ETCDIR:/papd.conf .
+The file is in the same format as
+.BR /etc/printcap .
+See
+.BR printcap (5)
+for details.  The name of the entry is registered with
+.BR NBP .
+There are currently three supported capabilities.
+.LP
+.TS
+c c l l
+cfB l l l .
+Name   Type    Default Descripton
+.sp .5
+pd     str     ``.ppd''        Pathname to PPD file
+pr     str     ``lp''  LPD printer name (or pipe to print command)
+op     str     ``operator''    Operator name for LPD spooling
+.TE
+.LP
+If no configuration file is given, the hostname of the machine is used
+as the NBP name and all options take their default value.
+.SH OPTIONS
+.TP
+.B -d
+Do not fork or disassociate from the terminal.  Write some
+debugging information to stderr.
+.HP
+.B -f
+.I configfile
+.br
+Consult
+.I configfile
+instead of
+.B :ETCDIR:/papd.conf
+for the configuration information.
+.HP
+.B -p
+.I printcap
+.br
+Consult
+.I printcap
+instead of
+.B /etc/printcap
+for LPD configuration information.
+.SH EXAMPLE
+The following papd configuration file sets up two print spoolers for the
+.B lpd
+printers named
+.B ps
+and
+.BR lp .
+The first spooler is known by the NBP name
+.B Mac Printer Spooler,
+and uses a PPD file located in
+.B /usr/share/lib/ppd.
+In addition, the user
+.B mcs
+will be the owner of all jobs that are spooled.
+The second spooler is known as
+.B HP Printer
+and all options are the default. The third spooler is known as
+.B Other Printer
+which prints with lpr.
+.sp
+.RS
+.nf
+Mac Printer Spooler:\\ 
+       :pr=ps:pd=/usr/share/lib/ppd/HPLJ_4M.PPD:op=mcs:
+
+HP Printer:\\ 
+       :
+
+Other Printer:\\
+       :pr=|/usr/bin/lpr -P Otherprinter -h:
+.fi
+.RE
+.SH FILES
+.TP 16
+.B :ETCDIR:/papd.conf
+Default configuration file.
+.TP 16
+.B /etc/printcap
+Printer capabilities database.
+.TP 16
+.B .ppd
+PostScript Printer Description file.
+.B papd
+answers configuration and font queries from printing clients by
+consulting the configured PPD file.  Such files are available from
+Adobe, Inc, via anonymous ftp from ftp.adobe.com in /pub/adobe/PPDfiles
+(ftp://ftp.adobe.com/pub/adobe/PPDfiles), or from the printer's
+manufacturer.  If no PPD file is configured,
+.B papd
+will return the default answer, possibly causing the client to send
+excessively large jobs.
+.SH SEE ALSO
+.BR lpr (1),
+.BR lprm (1),
+\." .BR pap (4),
+.BR printcap (5).
+.BR lpc (8),
+.BR lpd (8).
+.SH CAVEATS
+.I papd
+accepts characters with the high bit set (a full 8-bits) from the clients,
+but some PostScript printers (including Apple Computer's LaserWriter family)
+only accept 7-bit characters on their serial interface by default.  You will
+need to configure your printer to accept a full 8 bits.
diff --git a/man/man8/papstatus.8 b/man/man8/papstatus.8
new file mode 100644 (file)
index 0000000..8d8ab00
--- /dev/null
@@ -0,0 +1,84 @@
+.TH PAPSTATUS 8 "17 Dec 1991"
+.SH NAME
+papstatus \- get the status of an AppleTalk-connected printer
+.SH SYNOPSIS
+.B :SBINDIR:/papstatus
+[
+.BI -d
+] [
+.B -p
+.I printer
+] [
+.I retrytime
+]
+.SH DESCRIPTION
+.B papstatus
+is used to obtain the current status message from an AppleTalk connected
+printer.  It uses the Printer Access Protocol (PAP) to obtain the
+status information.
+.LP
+If no printer is specified on the command line,
+.B papstatus
+looks for a file called
+.B .paprc
+in the current directory and reads it to obtain the name of a printer.  The
+.B .paprc
+file should contain a single line of the form
+.IB object : \c
+.IB type @ \c
+.I zone
+where each of
+.IR object ,
+.BI : type ,
+and
+.BI @ zone
+are optional.
+.I type
+and
+.I zone
+must be proceeded by
+.RB ` : '
+and
+.RB ` @ '
+respectively.  Blank lines and lines the begin with a
+.RB ` # '
+are ignored.
+.I type
+and
+.I zone
+default to
+.B LaserWriter
+and the zone of the local host, respectively.
+.SH OPTIONS
+.TP
+.B -d
+Turns on a debugging mode that prints some extra information to standard error.
+.HP
+.B -p
+.I printer
+.br
+Get status from
+.I printer
+(do not consult any
+.B .paprc
+files to find a printer name).  The syntax for
+.I printer
+is the same as discussed above for the
+.B .paprc
+file.
+.TP
+.I retrytime
+Normally,
+.B papstatus
+only gets the status from the printer once.  If
+.I retrytime
+is specified, the status is obtained repeatedly, with a sleep of
+.I retrytime
+seconds between inquiring the printer.
+.SH FILES
+.TP 20
+.B .paprc
+file that contains printer name
+.SH SEE ALSO
+.BR nbp (1),
+.BR pap (8)
diff --git a/man/man8/psf.8 b/man/man8/psf.8
new file mode 100644 (file)
index 0000000..9fcb711
--- /dev/null
@@ -0,0 +1,124 @@
+.TH PSF 8 "17 Dec 1991" "netatalk 1.2"
+.SH NAME
+psf \- PostScript filter
+.SH SYNOPSIS
+.B psf
+[
+.B -n
+.I name
+] [
+.B -h
+.I host
+] [
+.B -w
+.I width
+] [
+.B -l
+.I length
+] [
+.B -i
+.I indent
+] [
+.B -c
+]
+.SH DESCRIPTION
+.B psf
+is an
+.B lpd
+filter for PostScript printing.
+.B psf
+interprets the name it was called with to determine what filters to
+invoke. First, if the string ``pap'' appears anywhere in the name,
+.B psf
+invokes
+.B pap
+to talk to a printer via AppleTalk. Next, if the string ``rev'' appears,
+.B psf
+invokes
+.B psorder
+to reverse the pages of the job. Finally, if
+.B psf
+was called with a filter's name as the leading string, it invokes that
+filter. If there is no filter to run,
+.B psf
+examines the magic number of the input, and if the input is not
+PostScript, converts it to PostScript.
+.SH KLUDGE
+In the default configuration,
+.B psf
+supports two kludges.  The first causes
+.B psf
+to check its name for the letter `m'.  If this letter is found and accounting
+is turned on,
+.B psf
+calls
+.B pap
+twice, once to get an initial page count and to print the job, and
+another time to get a final page count.  This is a work-around for bugs
+in a variety of PAP implementions that cause printers to never properly
+close the PAP output file.  A notable example is any printer by
+Hewlett-Packard.
+.LP
+The second kludge causes
+.B psf
+to examine its name for the letter `w'.  If this letter is found and
+accounting is turned on,
+.B psf
+calls
+.B pap
+with the
+.B \-w
+flag.  This flag causes
+.B pap
+to wait until the printer's status contains the string `idle'.  Once
+this string is found, the job is printed as normal.  This kludge is a
+work-around for printers, notably Hewlett-Packard's LaserJet IV, which
+will report a page count while a previous jobs is still printing.
+.SH EXAMPLE
+The sample
+.B printcap
+entry below invokes
+.B psf
+to print text files, PostScript files,
+.BR troff 's
+C/A/T output, and
+.BR TeX 's
+DVI output, to an AppleTalk connected LaserWriter Plus. Since the
+LaserWriter Plus stacks pages in descending order, we reverse the pages
+and print the burst page last.
+.sp
+.RS
+.nf
+laser|lp|LaserWriter Plus on AppleTalk:\\
+    :sd=/usr/spool/lpd/laser:\\
+    :lp=/usr/spool/lpd/laser/null:\\
+    :lf=/var/adm/lpd-errs:pw#80:hl:\\
+    :of=:LIBDIR:/filters/ofpap:\\
+    :if=:LIBDIR:/filters/ifpaprev:\\
+    :tf=:LIBDIR:/filters/tfpaprev:\\
+    :df=:LIBDIR:/filters/dfpaprev:
+.fi
+.RE
+.sp
+Note that if the host in question spools to more than one AppleTalk
+printer,
+.B /dev/null
+should not be used for the
+.B lp
+capability. Instead, a null device should be created with
+.B mknod
+for each printer, as has been done above.
+.LP
+Finally, there is a file in the spool directory,
+.BR /var/spool/lpd/laser ,
+called
+.BR .paprc ,
+which
+.B pap
+reads for the AppleTalk name of the printer.
+.SH SEE ALSO
+.BR psorder (1),
+.BR printcap (5),
+.BR lpd (8),
+.BR mknod (8),
+.BR pap (8).
diff --git a/services.atalk b/services.atalk
new file mode 100644 (file)
index 0000000..2d8555e
--- /dev/null
@@ -0,0 +1,7 @@
+rtmp           1/ddp           # Routing Table Maintenance Protocol
+nbp            2/ddp           # Name Binding Protocol
+echo           4/ddp           # AppleTalk Echo Protocol
+zip            6/ddp           # Zone Information Protocol
+
+afpovertcp     548/tcp         # AFP over TCP
+afpovertcp     548/udp
diff --git a/sys/freebsd/Makefile b/sys/freebsd/Makefile
new file mode 100644 (file)
index 0000000..c3bf7cf
--- /dev/null
@@ -0,0 +1,88 @@
+# FreeBSD specific defines, passed to subdirectories.
+DEFS=  -DBSD4_4 $$OSDEFS
+OPTOPTS=       -O2
+CC=    gcc
+CSHAREDFLAGS=-fPIC
+LDSHARED=ld
+LDSHAREDFLAGS=-shared
+LDFLAGS_EXPORT=-export-dynamic
+#LIBSHARED=-ldl
+INSTALL=       install
+AFPLIBS=
+ADDLIBS=       
+
+ALL=   ../../libatalk ../../include ../../bin ../../etc ../../man
+
+oops:
+       @echo "Read README again.  Don't type 'make' here."
+       @exit 1
+
+all:   ${ALL}
+
+../../bin ../../etc:   ../../libatalk
+
+${ALL}:        FRC
+       @case ${OSVERSION} in \
+       1.*) ;; \
+       2.*) ;; \
+       *) OSDEFS=-DSENDFILE_FLAVOR_BSD ;; \
+       esac; \
+       cd $@; ${MAKE} ${MFLAGS} CC="${CC}" \
+           ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+           SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+           ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+           DESTDIR="${DESTDIR}" AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" \
+           AFPLIBS="${AFPLIBS}" LDSHARED="${LDSHARED}" \
+           LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+           LDSHAREDFLAGS="${LDSHAREDFLAGS}" CSHAREDFLAGS="${CSHAREDFLAGS}" \
+           LIBSHARED="${LIBSHARED}" \
+           all
+
+FRC:
+
+install :
+       -mkdir ${DESTDIR} ${SBINDIR} ${BINDIR} ${ETCDIR} ${LIBDIR}
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} CC="${CC}" \
+               ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+               SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+               ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+               AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" AFPLIBS="${AFPLIBS}" \
+               LDSHARED="${LDSHARED}" LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+               LDSHAREDFLAGS="${LDSHAREDFLAGS}" \
+               CSHAREDFLAGS="${CSHAREDFLAGS}" LIBSHARED="${LIBSHARED}" \
+               INSTALL="${INSTALL}" $@); \
+       done
+       rm -f ${ETCDIR}/rc.atalk
+       sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+               -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+               -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+               -e s@:INCDIR:@${INCDIR}@ \
+           < ../../distrib/initscripts/rc.atalk.bsd > ${ETCDIR}/rc.atalk
+       if [ -f ${ETCDIR}/afpd.conf ]; then \
+               echo "Retaining old afpd.conf file.";  \
+       else \
+               sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+                       -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+                       -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+                       -e s@:INCDIR:@${INCDIR}@ \
+                       < ../../config/afpd.conf > ${ETCDIR}/afpd.conf; \
+       fi
+       @echo
+       @echo "Install is done.  Don't forget to add lines from"
+       @echo "services.atalk to /etc/services and to call rc.atalk"
+       @echo "in /etc/rc.  See README and README.FREEBSD for more"
+       @echo "information."
+
+clean :
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} clean); \
+       done
+
+depend :
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} DEFS="${DEFS}" depend); \
+       done
+
+# DO NOT DELETE THIS LINE
+
diff --git a/sys/generic/Makefile b/sys/generic/Makefile
new file mode 100644 (file)
index 0000000..23e5eb9
--- /dev/null
@@ -0,0 +1,142 @@
+# generic system. here are some useful defines.
+# system lookalikes:    -DBSD4_4 -D__svr4__ -DMACOSX_SERVER
+# statfs/quota support: -DNO_QUOTA_SUPPORT -DUSE_QUOTA_H -DUSE_UFS_QUOTA_H
+#                       -DHAVE_2ARG_DBTOB -DHAVE_DQB_BTIMELIMIT
+#                       -DUSE_OLD_RQUOTA (really old rquota support)
+#                       -DUSE_VFS_H -DUSE_STATFS_H -DUSE_STATVFS_H
+#                       -DUSE_MNTTAB_H -DUSE_MNTENT_H -DUSE_MOUNT_H
+# integer sizes:        -D_ISOC9X_SOURCE -DHAVE_64BIT_LONGS -DHAVE_32BIT_LONGS
+# other 64-bit issues:  -DTRY_64BITOFF_T (for byte locks)
+# afp/ddp:              -DNO_DDP
+# to get sys/cdefs.h:   -I../../sys/generic
+# misc:                 -DUSE_GETHOSTID -DNEED_GETUSERSHELL -DHAVE_BROKEN_CPP
+#                       -DNEED_RQUOTA -DNO_STRUCT_TM_GMTOFF
+#                       -DHAVE_IFNAMEINDEX
+#
+# plug-in uam support needs to access the run-time library
+# loader. here are the relevant options for that:
+# -DDLSYM_PREPEND_UNDERSCORE
+# -DNO_DLFCN_H (you'll need to make libatalk/util/module.c do the right thing)
+#
+# here's some flags appropriate for gnu gcc.
+# CSHAREDFLAGS=-fPIC
+# LDSHARED=gcc
+# LDSHAREDFLAGS=-shared
+# LDFLAGS_EXPORT=-rdynamic
+# LIBSHARED=-ldl
+#
+# the following combinations are not guaranteed to do anything, but
+# you might have luck with them. make sure that you comment out
+# TCPWRAPDIR, PAMDIR, or DESDIR/CRYPTODIR if you don't have the
+# respective parts installed.
+#
+# digital unix: 
+#   -DHAVE_64BIT_LONGS -DNO_DDP -DUSE_OLD_RQUOTA -DUSE_MOUNT_H \
+#   -DUSE_UFS_QUOTA_H -I../../sys/generic
+#  LIBSHARED=-ldl
+#  LDSHAREDFLAGS=-shared -expect_unresolved '*' -s
+#  uncomment the two touch lines in libatalk/Makefile to get around a
+#  problem with ar. use gcc.
+#
+# i think this will work for machten, but i'm not sure:
+#   -DBSD4_4 -DNO_QUOTA_SUPPORT -DNO_DDP
+#
+# sgi irix:
+#   -DUSE_MNTENT_H -DHAVE_DQB_BTIMELIMIT -DUSE_QUOTA_H -DNO_DDP \
+#   -DNEED_GETUSERSHELL -DUSE_STATVFS_H -DUSE_OLD_RQUOTA -DNO_STRUCT_TM_GMTOFF
+#   change RANLIB in libatalk/Makefile from 'ranlib' to 'echo'
+#
+# aix:
+#   -DHAVE_32BIT_LONGS -DHAVE_IFNAMEINDEX -DNO_DDP -DUSE_OLD_RQUOTA \
+#   -I../../sys/generic
+#   ADDLIBS=-lbsd
+#   use cc.
+#
+DEFS=-DHAVE_64BIT_LONGS -DNO_DDP -DUSE_OLD_RQUOTA -DUSE_MOUNT_H \
+   -DUSE_UFS_QUOTA_H -I../../sys/generic
+OPTOPTS=       -O2 -fomit-frame-pointer
+#CC =   cc
+CC=    gcc
+CSHAREDFLAGS=-fPIC
+LDSHARED=gcc
+LDSHAREDFLAGS=-Wl,-E
+INSTALL=       install
+AFPLIBS=
+#ADDLIBS=-lbsd
+LIBSHARED=-ldl
+
+# some oses have -lcrypt but shouldn't use it. uncomment the following
+# line if that's the case.
+#USE_CRYPTLIB=no
+
+ALL=   ../../libatalk ../../include ../../bin ../../etc ../../man
+
+oops:
+       @echo "Read README again.  Don't type 'make' here."
+       @exit 1
+
+all:   ${ALL}
+
+../../bin ../../etc:   ../../libatalk
+
+${ALL}:        FRC
+       cd $@; ${MAKE} ${MFLAGS} CC="${CC}" \
+           ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+           SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+           ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+           DESTDIR="${DESTDIR}" AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" \
+           AFPLIBS="${AFPLIBS}" LDSHARED="${LDSHARED}" \
+           LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+           LDSHAREDFLAGS="${LDSHAREDFLAGS}" CSHAREDFLAGS="${CSHAREDFLAGS}" \
+           LIBSHARED="${LIBSHARED}" USE_CRYPTLIB="${USE_CRYPTLIB}" \
+           all
+
+FRC:
+
+install :
+       -mkdir ${DESTDIR} ${SBINDIR} ${BINDIR} ${ETCDIR} ${LIBDIR}
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} CC="${CC}" \
+               ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+               SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+               ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+               AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" AFPLIBS="${AFPLIBS}" \
+               LDSHARED="${LDSHARED}" LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+               LDSHAREDFLAGS="${LDSHAREDFLAGS}" \
+               CSHAREDFLAGS="${CSHAREDFLAGS}" LIBSHARED="${LIBSHARED}" \
+               INSTALL="${INSTALL}" $@); \
+       done
+       rm -f ${ETCDIR}/rc.atalk
+       sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+               -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+               -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+               -e s@:INCDIR:@${INCDIR}@ \
+           < ../../distrib/scripts/rc.atalk.bsd > ${ETCDIR}/rc.atalk
+       chmod 744 ${ETCDIR}/rc.atalk 
+       if [ -f ${ETCDIR}/afpd.conf ]; then \
+               echo "Retaining old afpd.conf file.";  \
+       else \
+               sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+                       -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+                       -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+                       -e s@:INCDIR:@${INCDIR}@ \
+                       < ../../config/afpd.conf > ${ETCDIR}/afpd.conf; \
+       fi
+       @echo
+       @echo "Install is done.  Don't forget to add lines from"
+       @echo "services.atalk to /etc/services and to call rc.atalk"
+       @echo "in /etc/rc.  See README and README.GENERIC for more"
+       @echo "information."
+
+clean :
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} clean); \
+       done
+
+depend :
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} DEFS="${DEFS}" depend); \
+       done
+
+# DO NOT DELETE THIS LINE
+
diff --git a/sys/generic/sys/cdefs.h b/sys/generic/sys/cdefs.h
new file mode 100644 (file)
index 0000000..a5eaede
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef _SYS_CDEFS_H
+#define _SYS_CDEFS_H 1
+
+#ifdef __STDC__
+#define __P(args)    args  
+#else
+#define __P(args)    ()
+#endif
+
+#endif /* sys/cdefs.h */
diff --git a/sys/linux/Makefile b/sys/linux/Makefile
new file mode 100644 (file)
index 0000000..cce91f5
--- /dev/null
@@ -0,0 +1,138 @@
+# Linux specific defines, passed to subdirectories.
+#DEFS= -DTRY_64BITOFF_T -DNO_STRUCT_TM_GMTOFF -DHAVE_IFNAMEINDEX 
+DEFS=$$OSDEFS $$MACHINEDEFS $$QUOTADEF
+OPTOPTS=-O2 -fomit-frame-pointer -fsigned-char -Wunused -Wuninitialized 
+#OPTOPTS=      -g -fsigned-char
+CC=    gcc 
+CSHAREDFLAGS=-fPIC
+LDSHARED=ld
+LDSHAREDFLAGS=-shared
+LDFLAGS_EXPORT=-rdynamic
+LIBSHARED=-ldl
+
+AFPLIBS=
+ADDLIBS=
+
+INSTALL=       install 
+
+ALL=   ../../libatalk ../../include ../../bin ../../etc ../../man
+
+oops:
+       @echo "Read README again.  Don't type 'make' here."
+       @exit 1
+
+all:   ${ALL}
+       sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+               -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+               -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+               -e s@:INCDIR:@${INCDIR}@ \
+       < ../../distrib/initscripts/rc.atalk.redhat > atalk
+       sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+               -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+               -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+               -e s@:INCDIR:@${INCDIR}@ \
+               < ../../config/afpd.conf > afpd.conf
+
+../../bin ../../etc:   ../../libatalk
+
+${ALL}:        FRC
+       @case ${OSVERSION} in \
+       1.0*) ;; \
+       2.0*) ;; \
+       *) OSDEFS=-DSENDFILE_FLAVOR_LINUX ;; \
+       esac; \
+       if [ x"${MACHINETYPE}" = x"alpha" ]; then \
+          MACHINEDEFS=-DHAVE_GCC_MEMCPY_BUG;  \
+       fi; \
+       if [ ! -f /usr/include/sys/quota.h ]; then \
+          QUOTADEF=-DNEED_QUOTACTL_WRAPPER; \
+       fi; \
+       cd $@; ${MAKE} ${MFLAGS} CC="${CC}" \
+           ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+           SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+           ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+           DESTDIR="${DESTDIR}" AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" \
+           AFPLIBS="${AFPLIBS}" LDSHARED="${LDSHARED}" \
+           LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+           LDSHAREDFLAGS="${LDSHAREDFLAGS}" CSHAREDFLAGS="${CSHAREDFLAGS}" \
+           LIBSHARED="${LIBSHARED}" \
+           all
+
+FRC:
+
+${ETCDIR}:
+       -mkdir -p ${ETCDIR}
+
+install-sysv:
+       if [ -d ${INSTALL_PREFIX}/etc/rc.d/init.d ]; then \
+         ${INSTALL} -m744 atalk ${INSTALL_PREFIX}/etc/rc.d/init.d/atalk; \
+         rm -f ${INSTALL_PREFIX}/etc/rc.d/init.d/atalk.init \
+               ${INSTALL_PREFIX}/etc/rc.d/rc?.d/[SK]??atalk; \
+       fi; \
+       if [ x"${INSTALL_PREFIX}" = x ]; then \
+               /sbin/chkconfig --add atalk; \
+       fi; \
+       if [ -f ${ETCDIR}/netatalk.conf ]; then \
+               echo "Retaining old netatalk.conf file."; \
+       else \
+               ${INSTALL} -m644 ../../config/netatalk.conf ${ETCDIR}; \
+       fi
+
+install-bsd:
+       -if [ -f ${ETCDIR}/rc.atalk ]; then \
+               echo "Retaining old rc.atalk file."; \
+       else if [ ! -d /etc/rc.d/init.d ]; then \
+       sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+               -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+               -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+               -e s@:INCDIR:@${INCDIR}@ \
+               < ../../distrib/initscripts/rc.atalk.bsd > ${ETCDIR}/rc.atalk; \
+       fi \
+       fi
+
+install : ${ETCDIR} install-sysv install-bsd
+       -mkdir ${DESTDIR} ${SBINDIR} ${BINDIR} ${LIBDIR}
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} CC="${CC}" \
+               ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+               SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+               ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+               AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" AFPLIBS="${AFPLIBS}" \
+               LDSHARED="${LDSHARED}" LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+               LDSHAREDFLAGS="${LDSHAREDFLAGS}" \
+               CSHAREDFLAGS="${CSHAREDFLAGS}" LIBSHARED="${LIBSHARED}" \
+               INSTALL="${INSTALL}" $@); \
+       done
+       if [ -d ${INSTALL_PREFIX}/etc/pam.d -a \
+            ! -f ${INSTALL_PREFIX}/etc/pam.d/netatalk ]; then \
+               ${INSTALL} -m644 ../../config/netatalk.pamd \
+                                ${INSTALL_PREFIX}/etc/pam.d/netatalk; \
+               echo "PAM netatalk file installed."; \
+       fi 
+       if [ -f ${ETCDIR}/afpd.conf ]; then \
+               echo "Retaining old afpd.conf file.";  \
+       else \
+               ${INSTALL} -m644 afpd.conf ${ETCDIR}/afpd.conf; \
+       fi
+       @echo
+       @echo "Install is done.  Don't forget to add lines from"
+       @echo "services.atalk to /etc/services."
+       if [ ! -d /etc/rc.d/init.d ]; then \
+               echo "Don't forget to call rc.atalk in /etc/rc."; \
+       fi
+       @echo "See README and README.LINUX for more information."
+
+
+clean :
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} clean); \
+       done
+       -rm -f atalk afpd.conf
+
+depend :
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} DEFS="${DEFS}" depend); \
+       done
+
+# DO NOT DELETE THIS LINE
+
diff --git a/sys/netatalk/aarp.c b/sys/netatalk/aarp.c
new file mode 100644 (file)
index 0000000..ece86b1
--- /dev/null
@@ -0,0 +1,796 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/syslog.h>
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/time.h>
+#ifndef _IBMR2
+#include <sys/kernel.h>
+#endif _IBMR2
+#include <net/if.h>
+#include <net/route.h>
+#include <net/af.h>
+#include <netinet/in.h>
+#undef s_net
+#include <netinet/if_ether.h>
+#ifdef _IBMR2
+#include <netinet/in_netarp.h>
+#include <net/spl.h>
+#include <sys/errno.h>
+#include <sys/err_rec.h>
+#endif _IBMR2
+
+#include "at.h"
+#include "at_var.h"
+#include "aarp.h"
+#include "ddp_var.h"
+#include "endian.h"
+#include "phase2.h"
+
+#ifdef GATEWAY
+#define AARPTAB_BSIZ   16
+#define AARPTAB_NB     37
+#else
+#define AARPTAB_BSIZ   9
+#define AARPTAB_NB     19
+#endif GATEWAY
+#define AARPTAB_SIZE   (AARPTAB_BSIZ * AARPTAB_NB)
+struct aarptab         aarptab[AARPTAB_SIZE];
+int                    aarptab_size = AARPTAB_SIZE;
+
+#define AARPTAB_HASH(a) \
+    ((((a).s_net << 8 ) + (a).s_node ) % AARPTAB_NB )
+
+#define AARPTAB_LOOK(aat,addr) { \
+    int                n; \
+    aat = &aarptab[ AARPTAB_HASH(addr) * AARPTAB_BSIZ ]; \
+    for ( n = 0; n < AARPTAB_BSIZ; n++, aat++ ) \
+       if ( aat->aat_ataddr.s_net == (addr).s_net && \
+            aat->aat_ataddr.s_node == (addr).s_node ) \
+           break; \
+       if ( n >= AARPTAB_BSIZ ) \
+           aat = 0; \
+}
+
+#define AARPT_AGE      (60 * 1)
+#define AARPT_KILLC    20
+#define AARPT_KILLI    3
+
+#ifdef sun
+extern struct ether_addr       etherbroadcastaddr;
+#else sun
+extern u_char                  etherbroadcastaddr[6];
+#endif sun
+
+u_char atmulticastaddr[ 6 ] = {
+    0x09, 0x00, 0x07, 0xff, 0xff, 0xff,
+};
+
+u_char at_org_code[ 3 ] = {
+    0x08, 0x00, 0x07,
+};
+u_char aarp_org_code[ 3 ] = {
+    0x00, 0x00, 0x00,
+};
+
+aarptimer()
+{
+    struct aarptab     *aat;
+    int                        i, s;
+
+    timeout( aarptimer, (caddr_t)0, AARPT_AGE * hz );
+    aat = aarptab;
+    for ( i = 0; i < AARPTAB_SIZE; i++, aat++ ) {
+       if ( aat->aat_flags == 0 || ( aat->aat_flags & ATF_PERM ))
+           continue;
+       if ( ++aat->aat_timer < (( aat->aat_flags & ATF_COM ) ?
+               AARPT_KILLC : AARPT_KILLI ))
+           continue;
+       s = splimp();
+       aarptfree( aat );
+       splx( s );
+    }
+}
+
+struct ifaddr *
+at_ifawithnet( sat, ifa )
+    struct sockaddr_at *sat;
+    struct ifaddr      *ifa;
+{
+    struct at_ifaddr   *aa;
+
+    for (; ifa; ifa = ifa->ifa_next ) {
+#ifdef BSD4_4
+       if ( ifa->ifa_addr->sa_family != AF_APPLETALK ) {
+           continue;
+       }
+       if ( satosat( ifa->ifa_addr )->sat_addr.s_net ==
+               sat->sat_addr.s_net ) {
+           break;
+       }
+#else BSD4_4
+       if ( ifa->ifa_addr.sa_family != AF_APPLETALK ) {
+           continue;
+       }
+       aa = (struct at_ifaddr *)ifa;
+       if ( ntohs( sat->sat_addr.s_net ) >= ntohs( aa->aa_firstnet ) &&
+               ntohs( sat->sat_addr.s_net ) <= ntohs( aa->aa_lastnet )) {
+           break;
+       }
+#endif BSD4_4
+    }
+    return( ifa );
+}
+
+aarpwhohas( ac, sat )
+    struct arpcom      *ac;
+    struct sockaddr_at *sat;
+{
+    struct mbuf                *m;
+    struct ether_header        *eh;
+    struct ether_aarp  *ea;
+    struct at_ifaddr   *aa;
+    struct llc         *llc;
+    struct sockaddr    sa;
+
+#ifdef BSD4_4
+    if (( m = m_gethdr( M_DONTWAIT, MT_DATA )) == NULL ) {
+       return;
+    }
+    m->m_len = sizeof( *ea );
+    m->m_pkthdr.len = sizeof( *ea );
+    MH_ALIGN( m, sizeof( *ea ));
+#else BSD4_4
+    if (( m = m_get( M_DONTWAIT, MT_DATA )) == NULL ) {
+       return;
+    }
+    m->m_len = sizeof( *ea );
+    m->m_off = MMAXOFF - sizeof( *ea );
+#endif BSD4_4
+
+    ea = mtod( m, struct ether_aarp *);
+    bzero((caddr_t)ea, sizeof( *ea ));
+
+    ea->aarp_hrd = htons( AARPHRD_ETHER );
+    ea->aarp_pro = htons( ETHERTYPE_AT );
+    ea->aarp_hln = sizeof( ea->aarp_sha );
+    ea->aarp_pln = sizeof( ea->aarp_spu );
+    ea->aarp_op = htons( AARPOP_REQUEST );
+#ifdef sun
+    bcopy((caddr_t)&ac->ac_enaddr, (caddr_t)ea->aarp_sha,
+           sizeof( ea->aarp_sha ));
+#else sun
+    bcopy((caddr_t)ac->ac_enaddr, (caddr_t)ea->aarp_sha,
+           sizeof( ea->aarp_sha ));
+#endif sun
+
+    /*
+     * We need to check whether the output ethernet type should
+     * be phase 1 or 2. We have the interface that we'll be sending
+     * the aarp out. We need to find an AppleTalk network on that
+     * interface with the same address as we're looking for. If the
+     * net is phase 2, generate an 802.2 and SNAP header.
+     */
+    if (( aa = (struct at_ifaddr *)at_ifawithnet( sat, ac->ac_if.if_addrlist ))
+           == NULL ) {
+       m_freem( m );
+       return;
+    }
+
+    eh = (struct ether_header *)sa.sa_data;
+
+    if ( aa->aa_flags & AFA_PHASE2 ) {
+#ifdef sun
+       bcopy((caddr_t)atmulticastaddr, (caddr_t)&eh->ether_dhost,
+               sizeof( eh->ether_dhost ));
+#else sun
+       bcopy((caddr_t)atmulticastaddr, (caddr_t)eh->ether_dhost,
+               sizeof( eh->ether_dhost ));
+#endif sun
+#if defined( sun ) && defined( i386 )
+       eh->ether_type = htons( sizeof( struct llc ) +
+               sizeof( struct ether_aarp ));
+#else sun i386
+       eh->ether_type = sizeof( struct llc ) + sizeof( struct ether_aarp );
+#endif sun i386
+#ifdef BSD4_4
+       M_PREPEND( m, sizeof( struct llc ), M_WAIT );
+#else BSD4_4
+       m->m_len += sizeof( struct llc );
+       m->m_off -= sizeof( struct llc );
+#endif BSD4_4
+       llc = mtod( m, struct llc *);
+       llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP;
+       llc->llc_control = LLC_UI;
+       bcopy( aarp_org_code, llc->llc_org_code, sizeof( aarp_org_code ));
+       llc->llc_ether_type = htons( ETHERTYPE_AARP );
+
+       
+       bcopy( &AA_SAT( aa )->sat_addr.s_net, ea->aarp_spnet,
+               sizeof( ea->aarp_spnet ));
+       ea->aarp_spnode = AA_SAT( aa )->sat_addr.s_node;
+       bcopy( &sat->sat_addr.s_net, ea->aarp_tpnet,
+               sizeof( ea->aarp_tpnet ));
+       ea->aarp_tpnode = sat->sat_addr.s_node;
+    } else {
+#ifdef sun
+       bcopy((caddr_t)&etherbroadcastaddr, (caddr_t)&eh->ether_dhost,
+               sizeof( eh->ether_dhost ));
+#else sun
+       bcopy((caddr_t)etherbroadcastaddr, (caddr_t)eh->ether_dhost,
+               sizeof( eh->ether_dhost ));
+#endif sun
+#if defined( sun ) && defined( i386 )
+       eh->ether_type = htons( ETHERTYPE_AARP );
+#else sun i386
+       eh->ether_type = ETHERTYPE_AARP;
+#endif sun i386
+
+       ea->aarp_spa = AA_SAT( aa )->sat_addr.s_node;
+       ea->aarp_tpa = sat->sat_addr.s_node;
+    }
+
+#ifdef BSD4_4
+    sa.sa_len = sizeof( struct sockaddr );
+#endif BSD4_4
+    sa.sa_family = AF_UNSPEC;
+    (*ac->ac_if.if_output)(&ac->ac_if, m, &sa );
+}
+
+aarpresolve( ac, m, destsat, desten )
+    struct arpcom      *ac;
+    struct mbuf                *m;
+    struct sockaddr_at *destsat;
+#ifdef sun
+    struct ether_addr  *desten;
+#else sun
+    u_char             *desten;
+#endif sun
+{
+    struct at_ifaddr   *aa;
+    struct ifaddr      ifa;
+    struct aarptab     *aat;
+    int                        s;
+
+    if ( at_broadcast( destsat )) {
+       if (( aa = (struct at_ifaddr *)at_ifawithnet( destsat,
+               ((struct ifnet *)ac)->if_addrlist )) == NULL ) {
+           m_freem( m );
+           return( 0 );
+       }
+       if ( aa->aa_flags & AFA_PHASE2 ) {
+           bcopy( (caddr_t)atmulticastaddr, (caddr_t)desten,
+                   sizeof( atmulticastaddr ));
+       } else {
+#ifdef sun
+           bcopy( (caddr_t)&etherbroadcastaddr, (caddr_t)desten,
+                   sizeof( etherbroadcastaddr ));
+#else sun
+           bcopy( (caddr_t)etherbroadcastaddr, (caddr_t)desten,
+                   sizeof( etherbroadcastaddr ));
+#endif sun
+       }
+       return( 1 );
+    }
+
+    s = splimp();
+    AARPTAB_LOOK( aat, destsat->sat_addr );
+    if ( aat == 0 ) {                  /* No entry */
+       aat = aarptnew( &destsat->sat_addr );
+       if ( aat == 0 ) {
+           panic( "aarpresolve: no free entry" );
+       }
+       aat->aat_hold = m;
+       aarpwhohas( ac, destsat );
+       splx( s );
+       return( 0 );
+    }
+    /* found an entry */
+    aat->aat_timer = 0;
+    if ( aat->aat_flags & ATF_COM ) {  /* entry is COMplete */
+       bcopy( (caddr_t)aat->aat_enaddr, (caddr_t)desten,
+               sizeof( aat->aat_enaddr ));
+       splx( s );
+       return( 1 );
+    }
+    /* entry has not completed */
+    if ( aat->aat_hold ) {
+       m_freem( aat->aat_hold );
+    }
+    aat->aat_hold = m;
+    aarpwhohas( ac, destsat );
+    splx( s );
+    return( 0 );
+}
+
+aarpinput( ac, m )
+    struct arpcom      *ac;
+    struct mbuf                *m;
+{
+    struct arphdr      *ar;
+
+    if ( ac->ac_if.if_flags & IFF_NOARP )
+       goto out;
+
+#ifndef BSD4_4
+    IF_ADJ( m );
+#endif BSD4_4
+
+    if ( m->m_len < sizeof( struct arphdr )) {
+       goto out;
+    }
+
+    ar = mtod( m, struct arphdr *);
+    if ( ntohs( ar->ar_hrd ) != AARPHRD_ETHER ) {
+       goto out;
+    }
+    
+    if ( m->m_len < sizeof( struct arphdr ) + 2 * ar->ar_hln +
+           2 * ar->ar_pln ) {
+       goto out;
+    }
+    
+    switch( ntohs( ar->ar_pro )) {
+    case ETHERTYPE_AT :
+       at_aarpinput( ac, m );
+       return;
+
+    default:
+       break;
+    }
+
+out:
+    m_freem( m );
+}
+
+
+at_aarpinput( ac, m )
+    struct arpcom      *ac;
+    struct mbuf                *m;
+{
+    struct mbuf                *m0;
+    struct ether_aarp  *ea;
+    struct at_ifaddr   *aa;
+    struct aarptab     *aat;
+    struct ether_header        *eh;
+    struct llc         *llc;
+    struct sockaddr_at sat;
+    struct sockaddr    sa;
+    struct at_addr     spa, tpa, ma;
+    int                        op, s;
+    u_short            net;
+
+    ea = mtod( m, struct ether_aarp *);
+
+    /* Check to see if from my hardware address */
+#ifdef sun
+    if ( !bcmp(( caddr_t )ea->aarp_sha, ( caddr_t )&ac->ac_enaddr,
+           sizeof( ac->ac_enaddr ))) {
+       m_freem( m );
+       return;
+    }
+#else sun
+    if ( !bcmp(( caddr_t )ea->aarp_sha, ( caddr_t )ac->ac_enaddr,
+           sizeof( ac->ac_enaddr ))) {
+       m_freem( m );
+       return;
+    }
+#endif sun
+
+    /*
+     * Check if from broadcast address.  This could be a more robust
+     * check, since we could look for multicasts.
+     */
+#ifdef sun
+    if ( !bcmp(( caddr_t )ea->aarp_sha, ( caddr_t )&etherbroadcastaddr,
+           sizeof( etherbroadcastaddr ))) {
+       log( LOG_ERR, "aarp: source is broadcast!\n" );
+       m_freem( m );
+       return;
+    }
+#else sun
+    if ( !bcmp(( caddr_t )ea->aarp_sha, ( caddr_t )etherbroadcastaddr,
+           sizeof( etherbroadcastaddr ))) {
+#ifndef _IBMR2
+#ifdef ultrix
+       mprintf( LOG_ERR,
+#else ultrix
+       log( LOG_ERR,
+#endif ultrix
+               "aarp: source is broadcast!\n" );
+#endif _IBMR2
+       m_freem( m );
+       return;
+    }
+#endif sun
+
+    op = ntohs( ea->aarp_op );
+    bcopy( ea->aarp_tpnet, &net, sizeof( net ));
+
+    if ( net != 0 ) {
+       sat.sat_family = AF_APPLETALK;
+       sat.sat_addr.s_net = net;
+       if (( aa = (struct at_ifaddr *)at_ifawithnet( &sat,
+               ac->ac_if.if_addrlist )) == NULL ) {
+           m_freem( m );
+           return;
+       }
+       bcopy( ea->aarp_spnet, &spa.s_net, sizeof( spa.s_net ));
+       bcopy( ea->aarp_tpnet, &tpa.s_net, sizeof( tpa.s_net ));
+    } else {
+       /*
+        * Since we don't know the net, we just look for the first
+        * phase 1 address on the interface.
+        */
+       for ( aa = (struct at_ifaddr *)ac->ac_if.if_addrlist; aa;
+               aa = (struct at_ifaddr *)aa->aa_ifa.ifa_next ) {
+           if ( AA_SAT( aa )->sat_family == AF_APPLETALK &&
+                   ( aa->aa_flags & AFA_PHASE2 ) == 0 ) {
+               break;
+           }
+       }
+       if ( aa == NULL ) {
+           m_freem( m );
+           return;
+       }
+       tpa.s_net = spa.s_net = AA_SAT( aa )->sat_addr.s_net;
+    }
+
+    spa.s_node = ea->aarp_spnode;
+    tpa.s_node = ea->aarp_tpnode;
+    ma.s_net = AA_SAT( aa )->sat_addr.s_net;
+    ma.s_node = AA_SAT( aa )->sat_addr.s_node;
+
+    /*
+     * This looks like it's from us.
+     */
+    if ( spa.s_net == ma.s_net && spa.s_node == ma.s_node ) {
+       if ( aa->aa_flags & AFA_PROBING ) {
+           /*
+            * We're probing, someone either responded to our probe, or
+            * probed for the same address we'd like to use. Change the
+            * address we're probing for.
+            */
+           untimeout( aarpprobe, ac );
+           wakeup( aa );
+           m_freem( m );
+           return;
+       } else if ( op != AARPOP_PROBE ) {
+           /*
+            * This is not a probe, and we're not probing. This means
+            * that someone's saying they have the same source address
+            * as the one we're using. Get upset...
+            */
+#ifndef _IBMR2
+#ifdef ultrix
+           mprintf( LOG_ERR,
+#else ultrix
+           log( LOG_ERR,
+#endif ultrix
+                   "aarp: duplicate AT address!! %x:%x:%x:%x:%x:%x\n",
+                   ea->aarp_sha[ 0 ], ea->aarp_sha[ 1 ], ea->aarp_sha[ 2 ],
+                   ea->aarp_sha[ 3 ], ea->aarp_sha[ 4 ], ea->aarp_sha[ 5 ]);
+#endif _IBMR2
+           m_freem( m );
+           return;
+       }
+    }
+
+    AARPTAB_LOOK( aat, spa );
+    if ( aat ) {
+       if ( op == AARPOP_PROBE ) {
+           /*
+            * Someone's probing for spa, dealocate the one we've got,
+            * so that if the prober keeps the address, we'll be able
+            * to arp for him.
+            */
+           aarptfree( aat );
+           m_freem( m );
+           return;
+       }
+
+       bcopy(( caddr_t )ea->aarp_sha, ( caddr_t )aat->aat_enaddr,
+               sizeof( ea->aarp_sha ));
+       aat->aat_flags |= ATF_COM;
+       if ( aat->aat_hold ) {
+#ifdef _IBMR2
+           /*
+            * Like in ddp_output(), we can't rely on the if_output
+            * routine to resolve AF_APPLETALK addresses, on the rs6k.
+            * So, we fill the destination ethernet address here.
+            *
+            * This should really be replaced with something like
+            * rsif_output(). XXX Will have to be for phase 2.
+            */
+            /* XXX maybe fill in the rest of the frame header */
+           sat.sat_family = AF_UNSPEC;
+           bcopy( aat->aat_enaddr, (*(struct sockaddr *)&sat).sa_data,
+                   sizeof( aat->aat_enaddr ));
+#else _IBMR2
+           sat.sat_family = AF_APPLETALK;
+           sat.sat_addr = spa;
+#endif _IBMR2
+           (*ac->ac_if.if_output)( &ac->ac_if, aat->aat_hold,
+                   (struct sockaddr *)&sat );
+           aat->aat_hold = 0;
+       }
+    }
+
+    if ( aat == 0 && tpa.s_net == ma.s_net && tpa.s_node == ma.s_node
+           && op != AARPOP_PROBE ) {
+       if ( aat = aarptnew( &spa )) {
+           bcopy(( caddr_t )ea->aarp_sha, ( caddr_t )aat->aat_enaddr,
+                   sizeof( ea->aarp_sha ));
+           aat->aat_flags |= ATF_COM;
+       }
+    }
+
+    /*
+     * Don't respond to responses, and never respond if we're
+     * still probing.
+     */
+    if ( tpa.s_net != ma.s_net || tpa.s_node != ma.s_node ||
+           op == AARPOP_RESPONSE || ( aa->aa_flags & AFA_PROBING )) {
+       m_freem( m );
+       return;
+    }
+
+    bcopy(( caddr_t )ea->aarp_sha, ( caddr_t )ea->aarp_tha,
+           sizeof( ea->aarp_sha ));
+#ifdef sun
+    bcopy(( caddr_t )&ac->ac_enaddr, ( caddr_t )ea->aarp_sha,
+           sizeof( ea->aarp_sha ));
+#else sun
+    bcopy(( caddr_t )ac->ac_enaddr, ( caddr_t )ea->aarp_sha,
+           sizeof( ea->aarp_sha ));
+#endif sun
+
+    eh = (struct ether_header *)sa.sa_data;
+#ifdef sun
+    bcopy(( caddr_t )ea->aarp_tha, ( caddr_t )&eh->ether_dhost,
+           sizeof( eh->ether_dhost ));
+#else sun
+    bcopy(( caddr_t )ea->aarp_tha, ( caddr_t )eh->ether_dhost,
+           sizeof( eh->ether_dhost ));
+#endif sun
+
+    if ( aa->aa_flags & AFA_PHASE2 ) {
+#if defined( sun ) && defined( i386 )
+       eh->ether_type = htons( sizeof( struct llc ) +
+               sizeof( struct ether_aarp ));
+#else sun i386
+       eh->ether_type = sizeof( struct llc ) + sizeof( struct ether_aarp );
+#endif sun i386
+#ifdef BSD4_4
+       M_PREPEND( m, sizeof( struct llc ), M_DONTWAIT );
+       if ( m == NULL ) {
+           m_freem( m );
+           return;
+       }
+#else BSD4_4
+       MGET( m0, M_DONTWAIT, MT_HEADER );
+       if ( m0 == NULL ) {
+           m_freem( m );
+           return;
+       }
+       m0->m_next = m;
+       m = m0;
+       m->m_off = MMAXOFF - sizeof( struct llc );
+       m->m_len = sizeof ( struct llc );
+#endif BSD4_4
+       llc = mtod( m, struct llc *);
+       llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP;
+       llc->llc_control = LLC_UI;
+       bcopy( aarp_org_code, llc->llc_org_code, sizeof( aarp_org_code ));
+       llc->llc_ether_type = htons( ETHERTYPE_AARP );
+
+       bcopy( ea->aarp_spnet, ea->aarp_tpnet, sizeof( ea->aarp_tpnet ));
+       bcopy( &ma.s_net, ea->aarp_spnet, sizeof( ea->aarp_spnet ));
+    } else {
+#if defined( sun ) && defined( i386 )
+       eh->ether_type = htons( ETHERTYPE_AARP );
+#else sun i386
+       eh->ether_type = ETHERTYPE_AARP;
+#endif sun i386
+    }
+
+    ea->aarp_tpnode = ea->aarp_spnode;
+    ea->aarp_spnode = ma.s_node;
+    ea->aarp_op = htons( AARPOP_RESPONSE );
+
+#ifdef BSD4_4
+    sa.sa_len = sizeof( struct sockaddr );
+#endif BSD4_4
+    sa.sa_family = AF_UNSPEC;
+    (*ac->ac_if.if_output)( &ac->ac_if, m, &sa );
+    return;
+}
+
+aarptfree( aat )
+    struct aarptab     *aat;
+{
+
+    if ( aat->aat_hold )
+       m_freem( aat->aat_hold );
+    aat->aat_hold = 0;
+    aat->aat_timer = aat->aat_flags = 0;
+    aat->aat_ataddr.s_net = 0;
+    aat->aat_ataddr.s_node = 0;
+}
+
+    struct aarptab *
+aarptnew( addr )
+    struct at_addr     *addr;
+{
+    int                        n;
+    int                        oldest = -1;
+    struct aarptab     *aat, *aato = NULL;
+    static int         first = 1;
+
+    if ( first ) {
+       first = 0;
+       timeout( aarptimer, (caddr_t)0, hz );
+    }
+    aat = &aarptab[ AARPTAB_HASH( *addr ) * AARPTAB_BSIZ ];
+    for ( n = 0; n < AARPTAB_BSIZ; n++, aat++ ) {
+       if ( aat->aat_flags == 0 )
+           goto out;
+       if ( aat->aat_flags & ATF_PERM )
+           continue;
+       if ((int) aat->aat_timer > oldest ) {
+           oldest = aat->aat_timer;
+           aato = aat;
+       }
+    }
+    if ( aato == NULL )
+       return( NULL );
+    aat = aato;
+    aarptfree( aat );
+out:
+    aat->aat_ataddr = *addr;
+    aat->aat_flags = ATF_INUSE;
+    return( aat );
+}
+
+aarpprobe( ac )
+    struct arpcom      *ac;
+{
+    struct mbuf                *m;
+    struct ether_header        *eh;
+    struct ether_aarp  *ea;
+    struct at_ifaddr   *aa;
+    struct llc         *llc;
+    struct sockaddr    sa;
+
+    /*
+     * We need to check whether the output ethernet type should
+     * be phase 1 or 2. We have the interface that we'll be sending
+     * the aarp out. We need to find an AppleTalk network on that
+     * interface with the same address as we're looking for. If the
+     * net is phase 2, generate an 802.2 and SNAP header.
+     */
+    for ( aa = (struct at_ifaddr *)ac->ac_if.if_addrlist; aa;
+           aa = (struct at_ifaddr *)aa->aa_ifa.ifa_next ) {
+       if ( AA_SAT( aa )->sat_family == AF_APPLETALK &&
+               ( aa->aa_flags & AFA_PROBING )) {
+           break;
+       }
+    }
+    if ( aa == NULL ) {                /* serious error XXX */
+       printf( "aarpprobe why did this happen?!\n" );
+       return;
+    }
+
+    if ( aa->aa_probcnt <= 0 ) {
+       aa->aa_flags &= ~AFA_PROBING;
+       wakeup( aa );
+       return;
+    } else {
+       timeout( aarpprobe, (caddr_t)ac, hz / 5 );
+    }
+
+#ifdef BSD4_4
+    if (( m = m_gethdr( M_DONTWAIT, MT_DATA )) == NULL ) {
+       return;
+    }
+    m->m_len = sizeof( *ea );
+    m->m_pkthdr.len = sizeof( *ea );
+    MH_ALIGN( m, sizeof( *ea ));
+#else BSD4_4
+    if (( m = m_get( M_DONTWAIT, MT_DATA )) == NULL ) {
+       return;
+    }
+    m->m_len = sizeof( *ea );
+    m->m_off = MMAXOFF - sizeof( *ea );
+#endif BSD4_4
+
+    ea = mtod( m, struct ether_aarp *);
+    bzero((caddr_t)ea, sizeof( *ea ));
+
+    ea->aarp_hrd = htons( AARPHRD_ETHER );
+    ea->aarp_pro = htons( ETHERTYPE_AT );
+    ea->aarp_hln = sizeof( ea->aarp_sha );
+    ea->aarp_pln = sizeof( ea->aarp_spu );
+    ea->aarp_op = htons( AARPOP_PROBE );
+#ifdef sun
+    bcopy((caddr_t)&ac->ac_enaddr, (caddr_t)ea->aarp_sha,
+           sizeof( ea->aarp_sha ));
+#else sun
+    bcopy((caddr_t)ac->ac_enaddr, (caddr_t)ea->aarp_sha,
+           sizeof( ea->aarp_sha ));
+#endif sun
+
+    eh = (struct ether_header *)sa.sa_data;
+
+    if ( aa->aa_flags & AFA_PHASE2 ) {
+#ifdef sun
+       bcopy((caddr_t)atmulticastaddr, (caddr_t)&eh->ether_dhost,
+               sizeof( eh->ether_dhost ));
+#else sun
+       bcopy((caddr_t)atmulticastaddr, (caddr_t)eh->ether_dhost,
+               sizeof( eh->ether_dhost ));
+#endif sun
+#if defined( sun ) && defined( i386 )
+       eh->ether_type = htons( sizeof( struct llc ) +
+               sizeof( struct ether_aarp ));
+#else sun i386
+       eh->ether_type = sizeof( struct llc ) + sizeof( struct ether_aarp );
+#endif sun i386
+#ifdef BSD4_4
+       M_PREPEND( m, sizeof( struct llc ), M_WAIT );
+#else BSD4_4
+       m->m_len += sizeof( struct llc );
+       m->m_off -= sizeof( struct llc );
+#endif BSD4_4
+       llc = mtod( m, struct llc *);
+       llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP;
+       llc->llc_control = LLC_UI;
+       bcopy( aarp_org_code, llc->llc_org_code, sizeof( aarp_org_code ));
+       llc->llc_ether_type = htons( ETHERTYPE_AARP );
+
+       bcopy( &AA_SAT( aa )->sat_addr.s_net, ea->aarp_spnet,
+               sizeof( ea->aarp_spnet ));
+       bcopy( &AA_SAT( aa )->sat_addr.s_net, ea->aarp_tpnet,
+               sizeof( ea->aarp_tpnet ));
+       ea->aarp_spnode = ea->aarp_tpnode = AA_SAT( aa )->sat_addr.s_node;
+    } else {
+#ifdef sun
+       bcopy((caddr_t)&etherbroadcastaddr, (caddr_t)&eh->ether_dhost,
+               sizeof( eh->ether_dhost ));
+#else sun
+       bcopy((caddr_t)etherbroadcastaddr, (caddr_t)eh->ether_dhost,
+               sizeof( eh->ether_dhost ));
+#endif sun
+#if defined( sun ) && defined( i386 )
+       eh->ether_type = htons( ETHERTYPE_AARP );
+#else sun i386
+       eh->ether_type = ETHERTYPE_AARP;
+#endif sun i386
+       ea->aarp_spa = ea->aarp_tpa = AA_SAT( aa )->sat_addr.s_node;
+    }
+
+#ifdef BSD4_4
+    sa.sa_len = sizeof( struct sockaddr );
+#endif BSD4_4
+    sa.sa_family = AF_UNSPEC;
+    (*ac->ac_if.if_output)(&ac->ac_if, m, &sa );
+    aa->aa_probcnt--;
+}
+
+aarp_clean()
+{
+    struct aarptab     *aat;
+    int                        i;
+
+    untimeout( aarptimer, 0 );
+    for ( i = 0, aat = aarptab; i < AARPTAB_SIZE; i++, aat++ ) {
+       if ( aat->aat_hold ) {
+           m_freem( aat->aat_hold );
+       }
+    }
+}
diff --git a/sys/netatalk/aarp.h b/sys/netatalk/aarp.h
new file mode 100644 (file)
index 0000000..646a13a
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ */
+
+/*
+ * This structure is used for both phase 1 and 2. Under phase 1
+ * the net is not filled in. It is in phase 2. In both cases, the
+ * hardware address length is (for some unknown reason) 4. If
+ * anyone at Apple could program their way out of paper bag, it
+ * would be 1 and 3 respectively for phase 1 and 2.
+ */
+union aapa {
+    u_char             ap_pa[4];
+    struct ap_node {
+       u_char          an_zero;
+       u_char          an_net[2];
+       u_char          an_node;
+    } ap_node;
+};
+
+struct ether_aarp {
+    struct arphdr      eaa_hdr;
+    u_char             aarp_sha[6];
+    union aapa         aarp_spu;
+    u_char             aarp_tha[6];
+    union aapa         aarp_tpu;
+};
+#define aarp_hrd       eaa_hdr.ar_hrd
+#define aarp_pro       eaa_hdr.ar_pro
+#define aarp_hln       eaa_hdr.ar_hln
+#define aarp_pln       eaa_hdr.ar_pln
+#define aarp_op                eaa_hdr.ar_op
+#define aarp_spa       aarp_spu.ap_node.an_node
+#define aarp_tpa       aarp_tpu.ap_node.an_node
+#define aarp_spnet     aarp_spu.ap_node.an_net
+#define aarp_tpnet     aarp_tpu.ap_node.an_net
+#define aarp_spnode    aarp_spu.ap_node.an_node
+#define aarp_tpnode    aarp_tpu.ap_node.an_node
+
+struct aarptab {
+    struct at_addr     aat_ataddr;
+    u_char             aat_enaddr[ 6 ];
+    u_char             aat_timer;
+    u_char             aat_flags;
+    struct mbuf                *aat_hold;
+};
+
+#define AARPHRD_ETHER  0x0001
+
+#define AARPOP_REQUEST 0x01
+#define AARPOP_RESPONSE        0x02
+#define AARPOP_PROBE   0x03
+
+#ifdef KERNEL
+struct aarptab         *aarptnew();
+int                    aarpprobe();
+#endif
diff --git a/sys/netatalk/at.h b/sys/netatalk/at.h
new file mode 100644 (file)
index 0000000..4f07587
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ *
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#ifndef __AT_HEADER__
+#define __AT_HEADER__
+
+#ifdef linux /* pull in the linux header */
+#include <sys/socket.h>
+#include <asm/types.h>
+#include <linux/atalk.h>
+#else
+
+#include <sys/types.h>
+#include <netinet/in.h> /* so that we can deal with sun's s_net #define */
+
+/*
+ * Supported protocols
+ */
+#ifdef ATPROTO_DDP
+#undef ATPROTO_DDP
+#endif
+#define ATPROTO_DDP    0
+#define ATPROTO_AARP   254
+
+/*
+ * Ethernet types, for DIX.
+ * These should really be in some global header file, but we can't
+ * count on them being there, and it's annoying to patch system files.
+ */
+#define ETHERTYPE_AT   0x809B          /* AppleTalk protocol */
+#define ETHERTYPE_AARP 0x80F3          /* AppleTalk ARP */
+
+#define DDP_MAXSZ      587
+
+/*
+ * If ATPORT_FIRST <= Port < ATPORT_RESERVED,
+ * Port was created by a privileged process.
+ * If ATPORT_RESERVED <= Port < ATPORT_LAST,
+ * Port was not necessarily created by a
+ * privileged process.
+ */
+#define ATPORT_FIRST   1
+#define ATPORT_RESERVED        128
+#define ATPORT_LAST    254 /* 254 is reserved on ether/tokentalk networks */
+
+/*
+ * AppleTalk address.
+ */
+struct at_addr {
+#ifdef s_net
+#undef s_net
+#endif
+    u_short    s_net;
+    u_char     s_node;
+};
+
+#define ATADDR_ANYNET  (u_short)0x0000
+#define ATADDR_ANYNODE (u_char)0x00
+#define ATADDR_ANYPORT (u_char)0x00
+#define ATADDR_BCAST   (u_char)0xff            /* There is no BCAST for NET */
+
+/*
+ * Socket address, AppleTalk style.  We keep magic information in the 
+ * zero bytes.  There are three types, NONE, CONFIG which has the phase
+ * and a net range, and IFACE which has the network address of an
+ * interface.  IFACE may be filled in by the client, and is filled in
+ * by the kernel.
+ */
+struct sockaddr_at {
+#ifdef BSD4_4
+    u_char             sat_len;
+    u_char             sat_family;
+#else BSD4_4
+    short              sat_family;
+#endif BSD4_4
+    u_char             sat_port;
+    struct at_addr     sat_addr;
+#ifdef notdef
+    struct {
+       u_char          sh_type;
+# define SATHINT_NONE  0
+# define SATHINT_CONFIG        1
+# define SATHINT_IFACE 2
+       union {
+           char                su_zero[ 7 ];   /* XXX check size */
+           struct {
+               u_char          sr_phase;
+               u_short         sr_firstnet, sr_lastnet;
+           } su_range;
+           u_short             su_interface;
+       } sh_un;
+    } sat_hints;
+#else notdef
+    char               sat_zero[ 8 ];
+#endif notdef
+};
+
+struct netrange {
+    u_char             nr_phase;
+    u_short            nr_firstnet;
+    u_short            nr_lastnet;
+};
+
+#ifdef KERNEL
+extern struct domain   atalkdomain;
+extern struct protosw  atalksw[];
+#endif
+
+#endif /* linux */
+#endif __AT_HEADER__
diff --git a/sys/netatalk/at_control.c b/sys/netatalk/at_control.c
new file mode 100644 (file)
index 0000000..a57c2aa
--- /dev/null
@@ -0,0 +1,580 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#ifdef ibm032
+#include <sys/dir.h>
+#endif ibm032
+#include <sys/user.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/mbuf.h>
+#ifndef _IBMR2
+#include <sys/kernel.h>
+#endif _IBMR2
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <net/if.h>
+#include <net/af.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#undef s_net
+#include <netinet/if_ether.h>
+#ifdef _IBMR2
+#include <net/spl.h>
+#endif _IBMR2
+
+#include "at.h"
+#include "at_var.h"
+#include "aarp.h"
+#include "phase2.h"
+
+#ifdef BSD4_4
+# define sateqaddr(a,b)        ((a)->sat_len == (b)->sat_len && \
+                   (a)->sat_family == (b)->sat_family && \
+                   (a)->sat_addr.s_net == (b)->sat_addr.s_net && \
+                   (a)->sat_addr.s_node == (b)->sat_addr.s_node )
+#else BSD4_4
+atalk_hash( sat, hp )
+    struct sockaddr_at *sat;
+    struct afhash      *hp;
+{
+    hp->afh_nethash = sat->sat_addr.s_net;
+    hp->afh_hosthash = ( sat->sat_addr.s_net << 8 ) +
+           sat->sat_addr.s_node;
+}
+
+/*
+ * Note the magic to get ifa_ifwithnet() to work without adding an
+ * ifaddr entry for each net in our local range.
+ */
+atalk_netmatch( sat1, sat2 )
+    struct sockaddr_at *sat1, *sat2;
+{
+    struct at_ifaddr   *aa;
+
+    for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
+       if ( AA_SAT( aa ) == sat1 ) {
+           break;
+       }
+    }
+    if ( aa ) {
+       return( ntohs( aa->aa_firstnet ) <= ntohs( sat2->sat_addr.s_net ) &&
+               ntohs( aa->aa_lastnet ) >= ntohs( sat2->sat_addr.s_net ));
+    }
+    return( sat1->sat_addr.s_net == sat2->sat_addr.s_net );
+}
+#endif BSD4_4
+
+at_control( cmd, data, ifp )
+    int                        cmd;
+    caddr_t            data;
+    struct ifnet       *ifp;
+{
+    struct ifreq       *ifr = (struct ifreq *)data;
+    struct sockaddr_at *sat;
+    struct netrange    *nr;
+#ifdef BSD4_4
+    struct at_aliasreq *ifra = (struct at_aliasreq *)data;
+    struct at_ifaddr   *aa0;
+#endif BSD4_4
+    struct at_ifaddr   *aa = 0;
+    struct mbuf                *m;
+    struct ifaddr      *ifa;
+
+    if ( ifp ) {
+       for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
+           if ( aa->aa_ifp == ifp ) break;
+       }
+    }
+
+    switch ( cmd ) {
+#ifdef BSD4_4
+    case SIOCAIFADDR:
+    case SIOCDIFADDR:
+       if ( ifra->ifra_addr.sat_family == AF_APPLETALK ) {
+           for ( ; aa; aa = aa->aa_next ) {
+               if ( aa->aa_ifp == ifp &&
+                       sateqaddr( &aa->aa_addr, &ifra->ifra_addr )) {
+                   break;
+               }
+           }
+       }
+       if ( cmd == SIOCDIFADDR && aa == 0 ) {
+           return( EADDRNOTAVAIL );
+       }
+       /*FALLTHROUGH*/
+#endif BSD4_4
+
+    case SIOCSIFADDR:
+#ifdef BSD4_4
+       /*
+        * What a great idea this is: Let's reverse the meaning of
+        * the return...
+        */
+       if ( suser( u.u_cred, &u.u_acflag )) {
+           return( EPERM );
+       }
+#else BSD4_4
+       if ( !suser()) {
+           return( EPERM );
+       }
+#endif BSD4_4
+
+       sat = satosat( &ifr->ifr_addr );
+       nr = (struct netrange *)sat->sat_zero;
+       if ( nr->nr_phase == 1 ) {
+           for ( ; aa; aa = aa->aa_next ) {
+               if ( aa->aa_ifp == ifp &&
+                       ( aa->aa_flags & AFA_PHASE2 ) == 0 ) {
+                   break;
+               }
+           }
+       } else {                /* default to phase 2 */
+           for ( ; aa; aa = aa->aa_next ) {
+               if ( aa->aa_ifp == ifp && ( aa->aa_flags & AFA_PHASE2 )) {
+                   break;
+               }
+           }
+       }
+
+       if ( ifp == 0 )
+           panic( "at_control" );
+
+       if ( aa == (struct at_ifaddr *) 0 ) {
+           m = m_getclr( M_WAIT, MT_IFADDR );
+           if ( m == (struct mbuf *)NULL ) {
+               return( ENOBUFS );
+           }
+
+           if (( aa = at_ifaddr ) != NULL ) {
+               /*
+                * Don't let the loopback be first, since the first
+                * address is the machine's default address for
+                * binding.
+                */
+               if ( at_ifaddr->aa_ifp->if_flags & IFF_LOOPBACK ) {
+                   aa = mtod( m, struct at_ifaddr *);
+                   aa->aa_next = at_ifaddr;
+                   at_ifaddr = aa;
+               } else {
+                   for ( ; aa->aa_next; aa = aa->aa_next )
+                       ;
+                   aa->aa_next = mtod( m, struct at_ifaddr *);
+               }
+           } else {
+               at_ifaddr = mtod( m, struct at_ifaddr *);
+           }
+
+           aa = mtod( m, struct at_ifaddr *);
+
+           if (( ifa = ifp->if_addrlist ) != NULL ) {
+               for ( ; ifa->ifa_next; ifa = ifa->ifa_next )
+                   ;
+               ifa->ifa_next = (struct ifaddr *)aa;
+           } else {
+               ifp->if_addrlist = (struct ifaddr *)aa;
+           }
+
+#ifdef BSD4_4
+           aa->aa_ifa.ifa_addr = (struct sockaddr *)&aa->aa_addr;
+           aa->aa_ifa.ifa_dstaddr = (struct sockaddr *)&aa->aa_addr;
+           aa->aa_ifa.ifa_netmask = (struct sockaddr *)&aa->aa_netmask;
+#endif BSD4_4
+
+           /*
+            * Set/clear the phase 2 bit.
+            */
+           if ( nr->nr_phase == 1 ) {
+               aa->aa_flags &= ~AFA_PHASE2;
+           } else {
+               aa->aa_flags |= AFA_PHASE2;
+           }
+           aa->aa_ifp = ifp;
+       } else {
+           at_scrub( ifp, aa );
+       }
+       break;
+
+    case SIOCGIFADDR :
+       sat = satosat( &ifr->ifr_addr );
+       nr = (struct netrange *)sat->sat_zero;
+       if ( nr->nr_phase == 1 ) {
+           for ( ; aa; aa = aa->aa_next ) {
+               if ( aa->aa_ifp == ifp &&
+                       ( aa->aa_flags & AFA_PHASE2 ) == 0 ) {
+                   break;
+               }
+           }
+       } else {                /* default to phase 2 */
+           for ( ; aa; aa = aa->aa_next ) {
+               if ( aa->aa_ifp == ifp && ( aa->aa_flags & AFA_PHASE2 )) {
+                   break;
+               }
+           }
+       }
+
+       if ( aa == (struct at_ifaddr *) 0 )
+           return( EADDRNOTAVAIL );
+       break;
+    }
+
+    switch ( cmd ) {
+    case SIOCGIFADDR:
+#ifdef BSD4_4
+       *(struct sockaddr_at *)&ifr->ifr_addr = aa->aa_addr;
+#else BSD4_4
+       ifr->ifr_addr = aa->aa_addr;
+#endif BSD4_4
+       break;
+
+    case SIOCSIFADDR:
+       return( at_ifinit( ifp, aa, (struct sockaddr_at *)&ifr->ifr_addr ));
+
+#ifdef BSD4_4
+    case SIOCAIFADDR:
+       if ( sateqaddr( &ifra->ifra_addr, &aa->aa_addr )) {
+           return( 0 );
+       }
+       return( at_ifinit( ifp, aa, (struct sockaddr_at *)&ifr->ifr_addr ));
+
+    case SIOCDIFADDR:
+       at_scrub( ifp, aa );
+       if (( ifa = ifp->if_addrlist ) == (struct ifaddr *)aa ) {
+           ifp->if_addrlist = ifa->ifa_next;
+       } else {
+           while ( ifa->ifa_next && ( ifa->ifa_next != (struct ifaddr *)aa )) {
+               ifa = ifa->ifa_next;
+           }
+           if ( ifa->ifa_next ) {
+               ifa->ifa_next = ((struct ifaddr *)aa)->ifa_next;
+           } else {
+               panic( "at_control" );
+           }
+       }
+
+       aa0 = aa;
+       if ( aa0 == ( aa = at_ifaddr )) {
+           at_ifaddr = aa->aa_next;
+       } else {
+           while ( aa->aa_next && ( aa->aa_next != aa0 )) {
+               aa = aa->aa_next;
+           }
+           if ( aa->aa_next ) {
+               aa->aa_next = aa0->aa_next;
+           } else {
+               panic( "at_control" );
+           }
+       }
+       m_free( dtom( aa0 ));
+       break;
+#endif BSD4_4
+
+    default:
+       if ( ifp == 0 || ifp->if_ioctl == 0 )
+           return( EOPNOTSUPP );
+       return( (*ifp->if_ioctl)( ifp, cmd, data ));
+    }
+    return( 0 );
+}
+
+at_scrub( ifp, aa )
+    struct ifnet       *ifp;
+    struct at_ifaddr   *aa;
+{
+#ifndef BSD4_4
+    struct sockaddr_at netsat;
+    int                        error;
+    u_short            net;
+#endif BSD4_4
+
+    if ( aa->aa_flags & AFA_ROUTE ) {
+#ifdef BSD4_4
+       if (( error = rtinit( &(aa->aa_ifa), RTM_DELETE,
+               ( ifp->if_flags & IFF_LOOPBACK ) ? RTF_HOST : 0 )) != 0 ) {
+           return( error );
+       }
+       aa->aa_ifa.ifa_flags &= ~IFA_ROUTE;
+#else BSD4_4
+       if ( ifp->if_flags & IFF_LOOPBACK ) {
+           rtinit( &aa->aa_addr, &aa->aa_addr, SIOCDELRT, RTF_HOST );
+       } else {
+           bzero( &netsat, sizeof( struct sockaddr_at ));
+           netsat.sat_family = AF_APPLETALK;
+           netsat.sat_addr.s_node = ATADDR_ANYNODE;
+
+           /*
+            * If the range is the full 0-fffe range, just use
+            * the default route.
+            */
+           if ( aa->aa_firstnet == htons( 0x0000 ) &&
+                   aa->aa_lastnet == htons( 0xfffe )) {
+               netsat.sat_addr.s_net = 0;
+               rtinit((struct sockaddr *)&netsat, &aa->aa_addr,
+                       (int)SIOCDELRT, 0 );
+           } else {
+               for ( net = ntohs( aa->aa_firstnet );
+                       net <= ntohs( aa->aa_lastnet ); net++ ) {
+                   netsat.sat_addr.s_net = htons( net );
+                   rtinit((struct sockaddr *)&netsat, &aa->aa_addr,
+                           (int)SIOCDELRT, 0 );
+               }
+           }
+       }
+#endif BSD4_4
+       aa->aa_flags &= ~AFA_ROUTE;
+    }
+    return( 0 );
+}
+
+extern struct timeval  time;
+
+at_ifinit( ifp, aa, sat )
+    struct ifnet       *ifp;
+    struct at_ifaddr   *aa;
+    struct sockaddr_at *sat;
+{
+    struct netrange    nr, onr;
+#ifdef BSD4_4
+    struct sockaddr_at oldaddr;
+#else BSD4_4
+    struct sockaddr    oldaddr;
+#endif BSD4_4
+    struct sockaddr_at netaddr;
+    int                        s = splimp(), error = 0, i, j, netinc, nodeinc, nnets;
+    u_short            net;
+
+    oldaddr = aa->aa_addr;
+    bzero( AA_SAT( aa ), sizeof( struct sockaddr_at ));
+    bcopy( sat->sat_zero, &nr, sizeof( struct netrange ));
+    nnets = ntohs( nr.nr_lastnet ) - ntohs( nr.nr_firstnet ) + 1;
+
+    onr.nr_firstnet = aa->aa_firstnet;
+    onr.nr_lastnet = aa->aa_lastnet;
+    aa->aa_firstnet = nr.nr_firstnet;
+    aa->aa_lastnet = nr.nr_lastnet;
+
+    /*
+     * We could eliminate the need for a second phase 1 probe (post
+     * autoconf) if we check whether we're resetting the node. Note
+     * that phase 1 probes use only nodes, not net.node pairs.  Under
+     * phase 2, both the net and node must be the same.
+     */
+    if ( ifp->if_flags & IFF_LOOPBACK ) {
+#ifdef BSD4_4
+       AA_SAT( aa )->sat_len = sat->sat_len;
+#endif BSD4_4
+       AA_SAT( aa )->sat_family = AF_APPLETALK;
+       AA_SAT( aa )->sat_addr.s_net = sat->sat_addr.s_net;
+       AA_SAT( aa )->sat_addr.s_node = sat->sat_addr.s_node;
+    } else {
+       aa->aa_flags |= AFA_PROBING;
+       AA_SAT( aa )->sat_family = AF_APPLETALK;
+       if ( aa->aa_flags & AFA_PHASE2 ) {
+           if ( sat->sat_addr.s_net == ATADDR_ANYNET ) {
+               if ( nnets != 1 ) {
+                   net = ntohs( nr.nr_firstnet ) + time.tv_sec % ( nnets - 1 );
+               } else {
+                   net = ntohs( nr.nr_firstnet );
+               }
+           } else {
+               if ( ntohs( sat->sat_addr.s_net ) < ntohs( nr.nr_firstnet ) ||
+                       ntohs( sat->sat_addr.s_net ) > ntohs( nr.nr_lastnet )) {
+                   aa->aa_addr = oldaddr;
+                   aa->aa_firstnet = onr.nr_firstnet;
+                   aa->aa_lastnet = onr.nr_lastnet;
+                   return( EINVAL );
+               }
+               net = ntohs( sat->sat_addr.s_net );
+           }
+       } else {
+           net = ntohs( sat->sat_addr.s_net );
+       }
+
+       if ( sat->sat_addr.s_node == ATADDR_ANYNODE ) {
+           AA_SAT( aa )->sat_addr.s_node = time.tv_sec;
+       } else {
+           AA_SAT( aa )->sat_addr.s_node = sat->sat_addr.s_node;
+       }
+
+       for ( i = nnets, netinc = 1; i > 0; net = ntohs( nr.nr_firstnet ) +
+               (( net - ntohs( nr.nr_firstnet ) + netinc ) % nnets ), i-- ) {
+           AA_SAT( aa )->sat_addr.s_net = htons( net );
+
+           for ( j = 0, nodeinc = time.tv_sec | 1; j < 256;
+                   j++, AA_SAT( aa )->sat_addr.s_node += nodeinc ) {
+               if ( AA_SAT( aa )->sat_addr.s_node > 253 ||
+                       AA_SAT( aa )->sat_addr.s_node < 1 ) {
+                   continue;
+               }
+               aa->aa_probcnt = 10;
+               timeout( aarpprobe, (caddr_t)ifp, hz / 5 );
+               splx( s );
+               if ( sleep( aa, PSLEP|PCATCH )) {
+                   printf( "at_ifinit why did this happen?!\n" );
+                   aa->aa_addr = oldaddr;
+                   aa->aa_firstnet = onr.nr_firstnet;
+                   aa->aa_lastnet = onr.nr_lastnet;
+                   return( EINTR );
+               }
+               s = splimp();
+               if (( aa->aa_flags & AFA_PROBING ) == 0 ) {
+                   break;
+               }
+           }
+           if (( aa->aa_flags & AFA_PROBING ) == 0 ) {
+               break;
+           }
+           /* reset node for next network */
+           AA_SAT( aa )->sat_addr.s_node = time.tv_sec;
+       }
+
+       if ( aa->aa_flags & AFA_PROBING ) {
+           aa->aa_addr = oldaddr;
+           aa->aa_firstnet = onr.nr_firstnet;
+           aa->aa_lastnet = onr.nr_lastnet;
+           splx( s );
+           return( EADDRINUSE );
+       }
+    }
+
+    if ( ifp->if_ioctl &&
+           ( error = (*ifp->if_ioctl)( ifp, SIOCSIFADDR, aa ))) {
+       splx( s );
+       aa->aa_addr = oldaddr;
+       aa->aa_firstnet = onr.nr_firstnet;
+       aa->aa_lastnet = onr.nr_lastnet;
+       return( error );
+    }
+
+#ifdef BSD4_4
+    aa->aa_netmask.sat_len = 6;
+    aa->aa_netmask.sat_family = AF_APPLETALK;
+    aa->aa_netmask.sat_addr.s_net = 0xffff;
+    aa->aa_netmask.sat_addr.s_node = 0;
+#endif BSD4_4
+
+    if ( ifp->if_flags & IFF_LOOPBACK ) {
+#ifndef BSD4_4
+       rtinit( &aa->aa_addr, &aa->aa_addr, (int)SIOCADDRT,
+               RTF_HOST|RTF_UP );
+#else BSD4_4
+       error = rtinit( &(aa->aa_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP );
+#endif BSD4_4
+    } else {
+#ifndef BSD4_4
+       /*
+        * rtrequest looks for point-to-point links first. The
+        * broadaddr is in the same spot as the destaddr. So, if
+        * ATADDR_ANYNET is 0, and we don't fill in the broadaddr, we
+        * get 0.0 routed out the ether interface.  So, initialize the
+        * broadaddr, even tho we don't use it.
+        *
+        * We *could* use the broadaddr field to reduce some of the
+        * sockaddr_at overloading that we've done.  E.g. Just send
+        * to INTERFACE-NET.255, and have the kernel reroute that
+        * to broadaddr, which would be 0.255 for phase 2 interfaces,
+        * and IFACE-NET.255 for phase 1 interfaces.
+        */
+       ((struct sockaddr_at *)&aa->aa_broadaddr)->sat_addr.s_net =
+               sat->sat_addr.s_net;
+       ((struct sockaddr_at *)&aa->aa_broadaddr)->sat_addr.s_node =
+               ATADDR_BCAST;
+
+       bzero( &netaddr, sizeof( struct sockaddr_at ));
+       netaddr.sat_family = AF_APPLETALK;
+       netaddr.sat_addr.s_node = ATADDR_ANYNODE;
+       if (( aa->aa_flags & AFA_PHASE2 ) == 0 ) {
+           netaddr.sat_addr.s_net = AA_SAT( aa )->sat_addr.s_net;
+           rtinit((struct sockaddr *)&netaddr, &aa->aa_addr,
+                   (int)SIOCADDRT, RTF_UP );
+       } else {
+           /*
+            * If the range is the full 0-fffe range, just use
+            * the default route.
+            */
+           if ( aa->aa_firstnet == htons( 0x0000 ) &&
+                   aa->aa_lastnet == htons( 0xfffe )) {
+               netaddr.sat_addr.s_net = 0;
+               rtinit((struct sockaddr *)&netaddr, &aa->aa_addr,
+                       (int)SIOCADDRT, RTF_UP );
+           } else {
+               for ( net = ntohs( aa->aa_firstnet );
+                       net <= ntohs( aa->aa_lastnet ); net++ ) {
+                   netaddr.sat_addr.s_net = htons( net );
+                   rtinit((struct sockaddr *)&netaddr, &aa->aa_addr,
+                           (int)SIOCADDRT, RTF_UP );
+               }
+           }
+       }
+#else BSD4_4
+       error = rtinit( &(aa->aa_ifa), (int)RTM_ADD, RTF_UP );
+#endif BSD4_4
+    }
+    if ( error ) {
+       aa->aa_addr = oldaddr;
+       aa->aa_firstnet = onr.nr_firstnet;
+       aa->aa_lastnet = onr.nr_lastnet;
+       splx( s );
+       return( error );
+    }
+
+#ifdef BSD4_4
+    aa->aa_ifa.ifa_flags |= IFA_ROUTE;
+#endif BSD4_4
+    aa->aa_flags |= AFA_ROUTE;
+    splx( s );
+    return( 0 );
+}
+
+at_broadcast( sat )
+    struct sockaddr_at *sat;
+{
+    struct at_ifaddr   *aa;
+
+    if ( sat->sat_addr.s_node != ATADDR_BCAST ) {
+       return( 0 );
+    }
+    if ( sat->sat_addr.s_net == 0 ) {
+       return( 1 );
+    } else {
+       for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
+           if (( aa->aa_ifp->if_flags & IFF_BROADCAST ) &&
+                ( ntohs( sat->sat_addr.s_net ) >= ntohs( aa->aa_firstnet ) &&
+                ntohs( sat->sat_addr.s_net ) <= ntohs( aa->aa_lastnet ))) {
+               return( 1 );
+           }
+       }
+    }
+    return( 0 );
+}
+
+aa_clean()
+{
+    struct at_ifaddr   *aa;
+    struct ifaddr      *ifa;
+    struct ifnet       *ifp;
+
+    while ( aa = at_ifaddr ) {
+       ifp = aa->aa_ifp;
+       at_scrub( ifp, aa );
+       at_ifaddr = aa->aa_next;
+       if (( ifa = ifp->if_addrlist ) == (struct ifaddr *)aa ) {
+           ifp->if_addrlist = ifa->ifa_next;
+       } else {
+           while ( ifa->ifa_next &&
+                   ( ifa->ifa_next != (struct ifaddr *)aa )) {
+               ifa = ifa->ifa_next;
+           }
+           if ( ifa->ifa_next ) {
+               ifa->ifa_next = ((struct ifaddr *)aa)->ifa_next;
+           } else {
+               panic( "at_entry" );
+           }
+       }
+    }
+}
diff --git a/sys/netatalk/at_proto.c b/sys/netatalk/at_proto.c
new file mode 100644 (file)
index 0000000..f1d6f5e
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ *
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#include <sys/protosw.h>
+#include <sys/domain.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "at.h"
+
+extern int             ddp_usrreq();
+extern int             ddp_output();
+extern int             ddp_init();
+
+#ifdef ultrix
+extern int             ddp_ifoutput();
+extern int             ddp_ifinput();
+extern int             ddp_ifioctl();
+#endif ultrix
+
+struct protosw         atalksw[] = {
+    {
+       /* Identifiers */
+       SOCK_DGRAM,     &atalkdomain,   ATPROTO_DDP,    PR_ATOMIC|PR_ADDR,
+       /*
+        * protocol-protocol interface.
+        * fields are pr_input, pr_output, pr_ctlinput, and pr_ctloutput.
+        * pr_input can be called from the udp protocol stack for iptalk
+        * packets bound for a local socket.
+        * pr_output can be used by higher level appletalk protocols, should
+        * they be included in the kernel.
+        */
+       0,              ddp_output,     0,              0,
+       /* socket-protocol interface. */
+       ddp_usrreq,
+       /* utility routines. */
+       ddp_init,       0,              0,              0,
+#ifdef ultrix
+       /* interface hooks */
+       ddp_ifoutput,   ddp_ifinput,    ddp_ifioctl,    0,
+#endif ultrix
+    },
+};
+
+struct domain          atalkdomain = {
+    AF_APPLETALK,      "appletalk",    0,      0,      0,      atalksw,
+    &atalksw[ sizeof( atalksw ) / sizeof( atalksw[ 0 ] ) ]
+};
diff --git a/sys/netatalk/at_var.h b/sys/netatalk/at_var.h
new file mode 100644 (file)
index 0000000..606c241
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ *
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+/*
+ * For phase2, we need to keep not only our address on an interface,
+ * but also the legal networks on the interface.
+ */
+struct at_ifaddr {
+    struct ifaddr      aa_ifa;
+# define aa_ifp                        aa_ifa.ifa_ifp
+#ifdef BSD4_4
+    struct sockaddr_at aa_addr;
+    struct sockaddr_at aa_broadaddr;
+    struct sockaddr_at aa_netmask;
+#else BSD4_4
+# define aa_addr               aa_ifa.ifa_addr
+# define aa_broadaddr          aa_ifa.ifa_broadaddr
+# define aa_dstaddr            aa_ifa.ifa_dstaddr
+#endif BSD4_4
+    int                        aa_flags;
+    u_short            aa_firstnet, aa_lastnet;
+    int                        aa_probcnt;
+    struct at_ifaddr   *aa_next;
+};
+
+#ifdef BSD4_4
+struct at_aliasreq {
+       char    ifra_name[IFNAMSIZ];            /* if name, e.g. "en0" */
+       struct  sockaddr_at ifra_addr;
+       struct  sockaddr_at ifra_broadaddr;
+#define ifra_dstaddr ifra_broadaddr
+       struct  sockaddr_at ifra_mask;
+};
+#endif BSD4_4
+
+#define AA_SAT(aa) \
+    ((struct sockaddr_at *)&((struct at_ifaddr *)(aa))->aa_addr)
+#define satosat(sa)    ((struct sockaddr_at *)(sa))
+
+#define AFA_ROUTE      0x0001
+#define AFA_PROBING    0x0002
+#define AFA_PHASE2     0x0004
+
+#ifdef KERNEL
+struct at_ifaddr       *at_ifaddr;
+struct ifqueue         atintrq1, atintrq2;
+int                    atdebug;
+#endif
diff --git a/sys/netatalk/ddp.h b/sys/netatalk/ddp.h
new file mode 100644 (file)
index 0000000..5a20d5e
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ */
+
+/*
+ * <-1byte(8bits) ->
+ * +---------------+
+ * | 0 | hopc  |len|
+ * +---------------+
+ * | len (cont)    |
+ * +---------------+
+ * |               |
+ * +- DDP csum    -+
+ * |               |
+ * +---------------+
+ * |               |
+ * +- Dest NET    -+
+ * |               |
+ * +---------------+
+ * |               |
+ * +- Src NET     -+
+ * |               |
+ * +---------------+
+ * | Dest NODE     |
+ * +---------------+
+ * | Src NODE      |
+ * +---------------+
+ * | Dest PORT     |
+ * +---------------+
+ * | Src PORT      |
+ * +---------------+
+ *
+ * On Apples, there is also a ddp_type field, after src_port. However,
+ * under this unix implementation, user level processes need to be able
+ * to set the ddp_type. In later revisions, the ddp_type may only be
+ * available in a raw_appletalk interface.
+ */
+
+#ifndef _NETATALK_DDP_H
+#define _NETATALK_DDP_H 1
+
+#include <netatalk/endian.h>
+
+struct elaphdr {
+    u_char     el_dnode;
+    u_char     el_snode;
+    u_char     el_type;
+};
+
+#define        SZ_ELAPHDR      3
+
+#define ELAP_DDPSHORT  0x01
+#define ELAP_DDPEXTEND 0x02
+
+/*
+ * Extended DDP header. Includes sickness for dealing with arbitrary
+ * bitfields on a little-endian arch.
+ */
+struct ddpehdr {
+    union {
+       struct {
+#if BYTE_ORDER == BIG_ENDIAN
+    unsigned           dub_pad:2;
+    unsigned           dub_hops:4;
+    unsigned           dub_len:10;
+    unsigned           dub_sum:16;
+#else
+#if BYTE_ORDER == LITTLE_ENDIAN
+    unsigned           dub_sum:16;
+    unsigned           dub_len:10;
+    unsigned           dub_hops:4;
+    unsigned           dub_pad:2;
+#else
+    OOPS!
+#endif
+#endif
+       } du_bits;
+       unsigned        du_bytes;
+    } deh_u;
+#define deh_pad                deh_u.du_bits.dub_pad
+#define deh_hops       deh_u.du_bits.dub_hops
+#define deh_len                deh_u.du_bits.dub_len
+#define deh_sum                deh_u.du_bits.dub_sum
+#define deh_bytes      deh_u.du_bytes
+    u_short            deh_dnet;
+    u_short            deh_snet;
+    u_char             deh_dnode;
+    u_char             deh_snode;
+    u_char             deh_dport;
+    u_char             deh_sport;
+};
+
+#define SZ_DDPEHDR      12
+
+#define DDP_MAXHOPS    15
+
+struct ddpshdr {
+    union {
+       struct {
+#if BYTE_ORDER == BIG_ENDIAN
+    unsigned           dub_pad:6;
+    unsigned           dub_len:10;
+    unsigned           dub_dport:8;
+    unsigned           dub_sport:8;
+#endif
+#if BYTE_ORDER == LITTLE_ENDIAN
+    unsigned           dub_sport:8;
+    unsigned           dub_dport:8;
+    unsigned           dub_len:10;
+    unsigned           dub_pad:6;
+#endif
+       } du_bits;
+       unsigned        du_bytes;
+    } dsh_u;
+#define dsh_pad                dsh_u.du_bits.dub_pad
+#define dsh_len                dsh_u.du_bits.dub_len
+#define dsh_dport      dsh_u.du_bits.dub_dport
+#define dsh_sport      dsh_u.du_bits.dub_sport
+#define dsh_bytes      dsh_u.du_bytes
+};
+#define SZ_DDPSHDR      4
+
+#endif /* netatalk/ddp.h */
diff --git a/sys/netatalk/ddp_input.c b/sys/netatalk/ddp_input.c
new file mode 100644 (file)
index 0000000..8cb4dd5
--- /dev/null
@@ -0,0 +1,401 @@
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/syslog.h>
+#include <net/if.h>
+#include <net/route.h>
+#ifdef _IBMR2
+#include <net/spl.h>
+#endif _IBMR2
+
+#include "at.h"
+#include "at_var.h"
+#include "endian.h"
+#include "ddp.h"
+#include "ddp_var.h"
+
+int            ddp_forward = 1;
+int            ddp_firewall = 0;
+extern int     ddp_cksum;
+extern u_short at_cksum();
+
+/*
+ * Could probably merge these two code segments a little better...
+ */
+atintr()
+{
+    struct elaphdr     *elhp, elh;
+    struct ifnet       *ifp;
+    struct mbuf                *m;
+    struct at_ifaddr   *aa;
+    int                        s;
+
+    for (;;) {
+#ifndef _IBMR2
+       s = splimp();
+#endif _IBMR2
+
+#ifdef BSD4_4
+       IF_DEQUEUE( &atintrq2, m );
+#else BSD4_4
+       IF_DEQUEUEIF( &atintrq2, m, ifp );
+#endif BSD4_4
+
+#ifndef _IBMR2
+       splx( s );
+#endif _IBMR2
+
+       if ( m == 0 ) {                 /* no more queued packets */
+           break;
+       }
+
+#ifdef BSD4_4
+       ifp = m->m_pkthdr.rcvif;
+#endif BSD4_4
+       for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
+           if ( aa->aa_ifp == ifp && ( aa->aa_flags & AFA_PHASE2 )) {
+               break;
+           }
+       }
+       if ( aa == NULL ) {             /* ifp not an appletalk interface */
+           m_freem( m );
+           continue;
+       }
+
+       ddp_input( m, ifp, (struct elaphdr *)NULL, 2 );
+    }
+
+    for (;;) {
+#ifndef _IBMR2
+       s = splimp();
+#endif _IBMR2
+
+#ifdef BSD4_4
+       IF_DEQUEUE( &atintrq1, m );
+#else BSD4_4
+       IF_DEQUEUEIF( &atintrq1, m, ifp );
+#endif BSD4_4
+
+#ifndef _IBMR2
+       splx( s );
+#endif _IBMR2
+
+       if ( m == 0 ) {                 /* no more queued packets */
+           break;
+       }
+
+#ifdef BSD4_4
+       ifp = m->m_pkthdr.rcvif;
+#endif BSD4_4
+       for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
+           if ( aa->aa_ifp == ifp && ( aa->aa_flags & AFA_PHASE2 ) == 0 ) {
+               break;
+           }
+       }
+       if ( aa == NULL ) {             /* ifp not an appletalk interface */
+           m_freem( m );
+           continue;
+       }
+
+       if ( m->m_len < SZ_ELAPHDR &&
+               (( m = m_pullup( m, SZ_ELAPHDR )) == 0 )) {
+           ddpstat.ddps_tooshort++;
+           continue;
+       }
+
+       elhp = mtod( m, struct elaphdr *);
+       m_adj( m, SZ_ELAPHDR );
+
+       if ( elhp->el_type == ELAP_DDPEXTEND ) {
+           ddp_input( m, ifp, (struct elaphdr *)NULL, 1 );
+       } else {
+           bcopy((caddr_t)elhp, (caddr_t)&elh, SZ_ELAPHDR );
+           ddp_input( m, ifp, &elh, 1 );
+       }
+    }
+    return;
+}
+
+struct route   forwro;
+
+ddp_input( m, ifp, elh, phase )
+    struct mbuf                *m;
+    struct ifnet       *ifp;
+    struct elaphdr     *elh;
+    int                        phase;
+{
+    struct sockaddr_at from, to;
+    struct ddpshdr     *dsh, ddps;
+    struct at_ifaddr   *aa;
+    struct ddpehdr     *deh, ddpe;
+#ifndef BSD4_4
+    struct mbuf                *mp;
+#endif BSD4_4
+    struct ddpcb       *ddp;
+    int                        dlen, mlen;
+    u_short            cksum;
+
+    bzero( (caddr_t)&from, sizeof( struct sockaddr_at ));
+    if ( elh ) {
+       ddpstat.ddps_short++;
+
+       if ( m->m_len < sizeof( struct ddpshdr ) &&
+               (( m = m_pullup( m, sizeof( struct ddpshdr ))) == 0 )) {
+           ddpstat.ddps_tooshort++;
+           return;
+       }
+
+       dsh = mtod( m, struct ddpshdr *);
+       bcopy( (caddr_t)dsh, (caddr_t)&ddps, sizeof( struct ddpshdr ));
+       ddps.dsh_bytes = ntohl( ddps.dsh_bytes );
+       dlen = ddps.dsh_len;
+
+       to.sat_addr.s_net = 0;
+       to.sat_addr.s_node = elh->el_dnode;
+       to.sat_port = ddps.dsh_dport;
+       from.sat_addr.s_net = 0;
+       from.sat_addr.s_node = elh->el_snode;
+       from.sat_port = ddps.dsh_sport;
+
+       for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
+           if ( aa->aa_ifp == ifp && ( aa->aa_flags & AFA_PHASE2 ) == 0 &&
+                   ( AA_SAT( aa )->sat_addr.s_node == to.sat_addr.s_node ||
+                   to.sat_addr.s_node == ATADDR_BCAST )) {
+               break;
+           }
+       }
+       if ( aa == NULL ) {
+           m_freem( m );
+           return;
+       }
+    } else {
+       ddpstat.ddps_long++;
+
+       if ( m->m_len < sizeof( struct ddpehdr ) &&
+               (( m = m_pullup( m, sizeof( struct ddpehdr ))) == 0 )) {
+           ddpstat.ddps_tooshort++;
+           return;
+       }
+
+       deh = mtod( m, struct ddpehdr *);
+       bcopy( (caddr_t)deh, (caddr_t)&ddpe, sizeof( struct ddpehdr ));
+       ddpe.deh_bytes = ntohl( ddpe.deh_bytes );
+       dlen = ddpe.deh_len;
+
+       if (( cksum = ddpe.deh_sum ) == 0 ) {
+           ddpstat.ddps_nosum++;
+       }
+
+       from.sat_addr.s_net = ddpe.deh_snet;
+       from.sat_addr.s_node = ddpe.deh_snode;
+       from.sat_port = ddpe.deh_sport;
+       to.sat_addr.s_net = ddpe.deh_dnet;
+       to.sat_addr.s_node = ddpe.deh_dnode;
+       to.sat_port = ddpe.deh_dport;
+
+       if ( to.sat_addr.s_net == 0 ) {
+           for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
+               if ( phase == 1 && ( aa->aa_flags & AFA_PHASE2 )) {
+                   continue;
+               }
+               if ( phase == 2 && ( aa->aa_flags & AFA_PHASE2 ) == 0 ) {
+                   continue;
+               }
+               if ( aa->aa_ifp == ifp &&
+                       ( AA_SAT( aa )->sat_addr.s_node == to.sat_addr.s_node ||
+                       to.sat_addr.s_node == ATADDR_BCAST ||
+                       ( ifp->if_flags & IFF_LOOPBACK ))) {
+                   break;
+               }
+           }
+       } else {
+           for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
+               if ( to.sat_addr.s_net == aa->aa_firstnet &&
+                       to.sat_addr.s_node == 0 ) {
+                   break;
+               }
+               if (( ntohs( to.sat_addr.s_net ) < ntohs( aa->aa_firstnet ) ||
+                       ntohs( to.sat_addr.s_net ) > ntohs( aa->aa_lastnet )) &&
+                       ( ntohs( to.sat_addr.s_net ) < 0xff00 ||
+                       ntohs( to.sat_addr.s_net ) > 0xfffe)) {
+                   continue;
+               }
+               if ( to.sat_addr.s_node != AA_SAT( aa )->sat_addr.s_node &&
+                       to.sat_addr.s_node != ATADDR_BCAST ) {
+                   continue;
+               }
+               break;
+           }
+       }
+    }
+
+    /*
+     * Adjust the length, removing any padding that may have been added
+     * at a link layer.  We do this before we attempt to forward a packet,
+     * possibly on a different media.
+     */
+#ifdef BSD4_4
+    mlen = m->m_pkthdr.len;
+#else BSD4_4
+    for ( mlen = 0, mp = m; mp; mp = mp->m_next ) {
+       mlen += mp->m_len;
+    }
+#endif BSD4_4
+    if ( mlen < dlen ) {
+       ddpstat.ddps_toosmall++;
+       m_freem( m );
+       return;
+    }
+    if ( mlen > dlen ) {
+       m_adj( m, dlen - mlen );
+    }
+
+    /*
+     * XXX Should we deliver broadcasts locally, also, or rely on the
+     * link layer to give us a copy?  For the moment, the latter.
+     */
+    if ( aa == NULL || ( to.sat_addr.s_node == ATADDR_BCAST &&
+           aa->aa_ifp != ifp && ( ifp->if_flags & IFF_LOOPBACK ) == 0 )) {
+       if ( ddp_forward == 0 ) {
+           m_freem( m );
+           return;
+       }
+       if ( forwro.ro_rt && ( satosat( &forwro.ro_dst )->sat_addr.s_net !=
+               to.sat_addr.s_net ||
+               satosat( &forwro.ro_dst )->sat_addr.s_node !=
+               to.sat_addr.s_node )) {
+#ifdef ultrix
+           rtfree( forwro.ro_rt );
+#else ultrix
+           RTFREE( forwro.ro_rt );
+#endif ultrix
+           forwro.ro_rt = (struct rtentry *)0;
+       }
+       if ( forwro.ro_rt == (struct rtentry *)0 ||
+            forwro.ro_rt->rt_ifp == (struct ifnet *)0 ) {
+#ifdef BSD4_4
+           forwro.ro_dst.sa_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+           forwro.ro_dst.sa_family = AF_APPLETALK;
+           satosat( &forwro.ro_dst )->sat_addr.s_net = to.sat_addr.s_net;
+           satosat( &forwro.ro_dst )->sat_addr.s_node = to.sat_addr.s_node;
+           rtalloc( &forwro );
+       }
+
+       if ( to.sat_addr.s_net != satosat( &forwro.ro_dst )->sat_addr.s_net &&
+               ddpe.deh_hops == DDP_MAXHOPS ) {
+           m_freem( m );
+           return;
+       }
+
+       if ( ddp_firewall &&
+               ( forwro.ro_rt == NULL || ( forwro.ro_rt->rt_ifp != ifp &&
+               forwro.ro_rt->rt_ifp != at_ifaddr->aa_ifp ))) {
+           m_freem( m );
+           return;
+       }
+
+       ddpe.deh_hops++;
+       ddpe.deh_bytes = htonl( ddpe.deh_bytes );
+       bcopy( (caddr_t)&ddpe, (caddr_t)deh, sizeof( u_short ));
+       if ( ddp_route( m, &forwro )) {
+           ddpstat.ddps_cantforward++;
+       } else {
+           ddpstat.ddps_forward++;
+       }
+       return;
+    }
+
+#ifdef BSD4_4
+    from.sat_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+    from.sat_family = AF_APPLETALK;
+
+    if ( elh ) {
+       m_adj( m, sizeof( struct ddpshdr ));
+    } else {
+       if ( ddp_cksum && cksum && cksum != at_cksum( m, sizeof( int ))) {
+           ddpstat.ddps_badsum++;
+           m_freem( m );
+           return;
+       }
+       m_adj( m, sizeof( struct ddpehdr ));
+    }
+
+    if (( ddp = ddp_search( &from, &to, aa )) == NULL ) {
+       m_freem( m );
+       return;
+    }
+
+    if ( sbappendaddr( &ddp->ddp_socket->so_rcv, (struct sockaddr *)&from,
+           m, (struct mbuf *)0 ) == 0 ) {
+       ddpstat.ddps_nosockspace++;
+       m_freem( m );
+       return;
+    }
+    sorwakeup( ddp->ddp_socket );
+}
+
+m_printm( m )
+    struct mbuf        *m;
+{
+    for (; m; m = m->m_next ) {
+       bprint( mtod( m, char * ), m->m_len );
+    }
+}
+
+#define BPXLEN 48
+#define BPALEN 16
+#include <ctype.h>
+char   hexdig[] = "0123456789ABCDEF";
+
+bprint( data, len )
+    char       *data;
+    int                len;
+{
+    char       xout[ BPXLEN ], aout[ BPALEN ];
+    int                i = 0;
+
+    bzero( xout, BPXLEN );
+    bzero( aout, BPALEN );
+
+    for ( ;; ) {
+       if ( len < 1 ) {
+           if ( i != 0 ) {
+               printf( "%s\t%s\n", xout, aout );
+           }
+           printf( "%s\n", "(end)" );
+           break;
+       }
+
+       xout[ (i*3) ] = hexdig[ ( *data & 0xf0 ) >> 4 ];
+       xout[ (i*3) + 1 ] = hexdig[ *data & 0x0f ];
+
+       if ( (u_char)*data < 0x7f && (u_char)*data > 0x20 ) {
+           aout[ i ] = *data;
+       } else {
+           aout[ i ] = '.';
+       }
+
+       xout[ (i*3) + 2 ] = ' ';
+
+       i++;
+       len--;
+       data++;
+
+       if ( i > BPALEN - 2 ) {
+           printf( "%s\t%s\n", xout, aout );
+           bzero( xout, BPXLEN );
+           bzero( aout, BPALEN );
+           i = 0;
+           continue;
+       }
+    }
+}
diff --git a/sys/netatalk/ddp_output.c b/sys/netatalk/ddp_output.c
new file mode 100644 (file)
index 0000000..b37c051
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#undef s_net
+#include <netinet/if_ether.h>
+
+#include "at.h"
+#include "at_var.h"
+#include "endian.h"
+#include "ddp.h"
+#include "ddp_var.h"
+
+u_short        at_cksum();
+int    ddp_cksum = 1;
+
+ddp_output( ddp, m )
+    struct ddpcb       *ddp;
+    struct mbuf                *m;
+{
+#ifndef BSD4_4
+    struct mbuf                *m0;
+    int                        len;
+#endif BSD4_4
+    struct ifnet       *ifp;
+    struct at_ifaddr   *aa = NULL;
+    struct ddpehdr     *deh;
+    u_short            net;
+
+#ifdef BSD4_4
+    M_PREPEND( m, sizeof( struct ddpehdr ), M_WAIT );
+#else BSD4_4
+    for ( len = 0, m0 = m; m; m = m->m_next ) {
+       len += m->m_len;
+    }
+    MGET( m, M_WAIT, MT_HEADER );
+    if ( m == 0 ) {
+       m_freem( m0 );
+       return( ENOBUFS );
+    }
+    m->m_next = m0;
+#endif BSD4_4
+
+#ifndef BSD4_4
+# define align(a)      (((a)+3)&0xfc)
+    m->m_off = MMINOFF + align( SZ_ELAPHDR );
+    m->m_len = sizeof( struct ddpehdr );
+#endif BSD4_4
+
+    deh = mtod( m, struct ddpehdr *);
+    deh->deh_pad = 0;
+    deh->deh_hops = 0;
+
+#ifdef BSD4_4
+    deh->deh_len = m->m_pkthdr.len;
+#else BSD4_4
+    deh->deh_len = len + sizeof( struct ddpehdr );
+#endif BSD4_4
+
+    deh->deh_dnet = ddp->ddp_fsat.sat_addr.s_net;
+    deh->deh_dnode = ddp->ddp_fsat.sat_addr.s_node;
+    deh->deh_dport = ddp->ddp_fsat.sat_port;
+    deh->deh_snet = ddp->ddp_lsat.sat_addr.s_net;
+    deh->deh_snode = ddp->ddp_lsat.sat_addr.s_node;
+    deh->deh_sport = ddp->ddp_lsat.sat_port;
+
+    /*
+     * The checksum calculation is done after all of the other bytes have
+     * been filled in.
+     */
+    if ( ddp_cksum ) {
+       deh->deh_sum = at_cksum( m, sizeof( int ));
+    } else {
+       deh->deh_sum = 0;
+    }
+    deh->deh_bytes = htonl( deh->deh_bytes );
+
+    return( ddp_route( m, &ddp->ddp_route ));
+}
+
+    u_short
+at_cksum( m, skip )
+    struct mbuf        *m;
+    int                skip;
+{
+    u_char     *data, *end;
+    u_int32_t  cksum = 0;
+
+    for (; m; m = m->m_next ) {
+       for ( data = mtod( m, u_char * ), end = data + m->m_len; data < end;
+               data++ ) {
+           if ( skip ) {
+               skip--;
+               continue;
+           }
+           cksum = ( cksum + *data ) << 1;
+           if ( cksum & 0x00010000 ) {
+               cksum++;
+           }
+           cksum &= 0x0000ffff;
+       }
+    }
+
+    if ( cksum == 0 ) {
+       cksum = 0x0000ffff;
+    }
+    return( (u_short)cksum );
+}
+
+ddp_route( m, ro )
+    struct mbuf                *m;
+    struct route       *ro;
+{
+    struct sockaddr_at gate;
+    struct elaphdr     *elh;
+    struct mbuf                *m0;
+    struct at_ifaddr   *aa = NULL;
+    struct ifnet       *ifp;
+    int                        mlen;
+    u_short            net;
+
+    if ( ro->ro_rt && ( ifp = ro->ro_rt->rt_ifp )) {
+#ifdef BSD4_4
+       net = satosat( ro->ro_rt->rt_gateway )->sat_addr.s_net;
+#else BSD4_4
+       net = satosat( &ro->ro_rt->rt_gateway )->sat_addr.s_net;
+#endif BSD4_4
+       for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
+           if ( aa->aa_ifp == ifp &&
+                   ntohs( net ) >= ntohs( aa->aa_firstnet ) &&
+                   ntohs( net ) <= ntohs( aa->aa_lastnet )) {
+               break;
+           }
+       }
+    }
+    if ( aa == NULL ) {
+       m_freem( m );
+       return( EINVAL );
+    }
+
+    /*
+     * There are several places in the kernel where data is added to
+     * an mbuf without ensuring that the mbuf pointer is aligned.
+     * This is bad for transition routing, since phase 1 and phase 2
+     * packets end up poorly aligned due to the three byte elap header.
+     */
+    if ( aa->aa_flags & AFA_PHASE2 ) {
+       for ( mlen = 0, m0 = m; m0; m0 = m0->m_next ) {
+           mlen += m0->m_len;
+       }
+       if (( m = m_pullup( m, MIN( MLEN, mlen ))) == 0 ) {
+           return( ENOBUFS );
+       }
+    } else {
+# ifdef notdef
+#ifdef BSD4_4
+       M_PREPEND( m, SZ_ELAPHDR, M_DONTWAIT );
+       if ( m == NULL ) {
+           return( ENOBUFS );
+       }
+#else BSD4_4
+       m->m_off -= SZ_ELAPHDR;
+       m->m_len += SZ_ELAPHDR;
+#endif BSD4_4
+# endif notdef
+
+       MGET( m0, M_WAIT, MT_HEADER );
+       if ( m0 == 0 ) {
+           m_freem( m );
+           return( ENOBUFS );
+       }
+       m0->m_next = m;
+       m0->m_off = MMINOFF + align( sizeof( struct ether_header ));
+       m0->m_len = SZ_ELAPHDR;
+       m = m0;
+
+       elh = mtod( m, struct elaphdr *);
+       elh->el_snode = satosat( &aa->aa_addr )->sat_addr.s_node;
+       elh->el_type = ELAP_DDPEXTEND;
+       if ( ntohs( satosat( &ro->ro_dst )->sat_addr.s_net ) >=
+               ntohs( aa->aa_firstnet ) &&
+               ntohs( satosat( &ro->ro_dst )->sat_addr.s_net ) <=
+               ntohs( aa->aa_lastnet )) {
+           elh->el_dnode = satosat( &ro->ro_dst )->sat_addr.s_node;
+       } else {
+#ifdef BSD4_4
+           elh->el_dnode = satosat( ro->ro_rt->rt_gateway )->sat_addr.s_node;
+#else BSD4_4
+           elh->el_dnode = satosat( &ro->ro_rt->rt_gateway )->sat_addr.s_node;
+#endif BSD4_4
+       }
+    }
+
+    if ( ntohs( satosat( &ro->ro_dst )->sat_addr.s_net ) >=
+           ntohs( aa->aa_firstnet ) &&
+           ntohs( satosat( &ro->ro_dst )->sat_addr.s_net ) <=
+           ntohs( aa->aa_lastnet )) {
+       gate = *satosat( &ro->ro_dst );
+    } else {
+#ifdef BSD4_4
+       gate = *satosat( ro->ro_rt->rt_gateway );
+#else BSD4_4
+       gate = *satosat( &ro->ro_rt->rt_gateway );
+#endif BSD4_4
+    }
+    ro->ro_rt->rt_use++;
+
+#ifdef ultrix
+    /*
+     * SAIEW: We can't make changes to net/if_loop.c, so we don't route
+     * further than this: if it's going to go through the lookback,
+     * short-circuit to ddp_input(). Who needs queuing?
+     *
+     * Note: Passing NULL for the elaphdr is cool, since we'll only ever
+     * try to send long form ddp throught the loopback.
+     */
+    if ( ifp->if_flags & IFF_LOOPBACK ) {
+#ifdef notdef
+       m->m_off += SZ_ELAPHDR;
+       m->m_len -= SZ_ELAPHDR;
+#endif notdef
+       ddp_input( m, ifp, (struct elaphdr *)NULL, 2 );
+       return( 0 );
+    }
+#endif ultrix
+
+#ifdef _IBMR2
+    /*
+     * We can't make changes to the interface routines on RS6ks, and
+     * they don't provide hooks for if_output, so we just resolve
+     * our address here, and pass the packet as a raw ethernet packet.
+     * This doesn't work particularly well, if we aren't *on* ethernet,
+     * but it's ok for the moment.
+     */
+    if ( ! ( ifp->if_flags & IFF_LOOPBACK )) {
+       struct ether_header     eh;
+
+       if ( !aarpresolve(( struct arpcom *)ifp, m,
+               &gate, eh.ether_dhost )) {
+           return( 0 );
+       }
+       eh.ether_type = htons( ETHERTYPE_AT );
+       gate.sat_family = AF_UNSPEC;
+       bcopy( &eh, (*(struct sockaddr *)&gate).sa_data,
+               sizeof( (*(struct sockaddr *)&gate).sa_data ));
+    }
+#endif _IBMR2
+    return((*ifp->if_output)( ifp, m, &gate ));
+}
diff --git a/sys/netatalk/ddp_usrreq.c b/sys/netatalk/ddp_usrreq.c
new file mode 100644 (file)
index 0000000..f678b87
--- /dev/null
@@ -0,0 +1,530 @@
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#ifdef ibm032
+#include <sys/dir.h>
+#endif ibm032
+#include <sys/user.h>
+#include <sys/mbuf.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <net/if.h>
+#include <net/route.h>
+#ifdef _IBMR2
+#include <net/spl.h>
+#endif _IBMR2
+
+#include "at.h"
+#include "at_var.h"
+#include "ddp_var.h"
+#include "endian.h"
+
+struct ddpcb   *ddpcb = NULL;
+u_int32_t              ddp_sendspace = DDP_MAXSZ; /* Max ddp size + 1 (ddp_type) */
+u_int32_t              ddp_recvspace = 10 * ( 587 + sizeof( struct sockaddr_at ));
+
+/*ARGSUSED*/
+ddp_usrreq( so, req, m, addr, rights )
+    struct socket      *so;
+    int                        req;
+    struct mbuf                *m, *addr, *rights;
+{
+    struct ddpcb       *ddp;
+    int                        error = 0;
+
+    ddp = sotoddpcb( so );
+
+    if ( req == PRU_CONTROL ) {
+       return( at_control( (int) m, (caddr_t) addr,
+               (struct ifnet *) rights ));
+    }
+
+    if ( rights && rights->m_len ) {
+       error = EINVAL;
+       goto release;
+    }
+
+    if ( ddp == NULL && req != PRU_ATTACH ) {
+       error = EINVAL;
+       goto release;
+    }
+
+    switch ( req ) {
+    case PRU_ATTACH :
+       if ( ddp != NULL ) {
+           error = EINVAL;
+           break;
+       }
+       if (( error = at_pcballoc( so )) != 0 ) {
+           break;
+       }
+       error = soreserve( so, ddp_sendspace, ddp_recvspace );
+       break;
+
+    case PRU_DETACH :
+       at_pcbdetach( so, ddp );
+       break;
+
+    case PRU_BIND :
+       error = at_pcbsetaddr( ddp, addr );
+       break;
+    
+    case PRU_SOCKADDR :
+       at_sockaddr( ddp, addr );
+       break;
+
+    case PRU_CONNECT:
+       if ( ddp->ddp_fsat.sat_port != ATADDR_ANYPORT ) {
+           error = EISCONN;
+           break;
+       }
+
+       error = at_pcbconnect( ddp, addr );
+       if ( error == 0 )
+           soisconnected( so );
+       break;
+
+    case PRU_DISCONNECT:
+       if ( ddp->ddp_fsat.sat_addr.s_node == ATADDR_ANYNODE ) {
+           error = ENOTCONN;
+           break;
+       }
+       at_pcbdisconnect( ddp );
+       soisdisconnected( so );
+       break;
+
+    case PRU_SHUTDOWN:
+       socantsendmore( so );
+       break;
+
+    case PRU_SEND: {
+       int     s;
+
+       if ( addr ) {
+           if ( ddp->ddp_fsat.sat_port != ATADDR_ANYPORT ) {
+               error = EISCONN;
+               break;
+           }
+
+           s = splnet();
+           error = at_pcbconnect( ddp, addr );
+           if ( error ) {
+               splx( s );
+               break;
+           }
+       } else {
+           if ( ddp->ddp_fsat.sat_port == ATADDR_ANYPORT ) {
+               error = ENOTCONN;
+               break;
+           }
+       }
+
+       error = ddp_output( ddp, m );
+       m = NULL;
+       if ( addr ) {
+           at_pcbdisconnect( ddp );
+           splx( s );
+       }
+       }
+       break;
+
+    case PRU_ABORT:
+       soisdisconnected( so );
+       at_pcbdetach( so, ddp );
+       break;
+
+    case PRU_LISTEN:
+    case PRU_CONNECT2:
+    case PRU_ACCEPT:
+    case PRU_SENDOOB:
+    case PRU_FASTTIMO:
+    case PRU_SLOWTIMO:
+    case PRU_PROTORCV:
+    case PRU_PROTOSEND:
+       error = EOPNOTSUPP;
+       break;
+
+    case PRU_RCVD:
+    case PRU_RCVOOB:
+       /*
+        * Don't mfree. Good architecture...
+        */
+       return( EOPNOTSUPP );
+
+    case PRU_SENSE:
+       /*
+        * 1. Don't return block size.
+        * 2. Don't mfree.
+        */
+       return( 0 );
+
+    default:
+       error = EOPNOTSUPP;
+    }
+
+release:
+    if ( m != NULL ) {
+       m_freem( m );
+    }
+    return( error );
+}
+
+at_sockaddr( ddp, addr )
+    struct ddpcb       *ddp;
+    struct mbuf                *addr;
+{
+    struct sockaddr_at *sat;
+
+    addr->m_len = sizeof( struct sockaddr_at );
+    sat = mtod( addr, struct sockaddr_at *);
+    *sat = ddp->ddp_lsat;
+}
+
+at_pcbsetaddr( ddp, addr )
+    struct ddpcb       *ddp;
+    struct mbuf                *addr;
+{
+    struct sockaddr_at lsat, *sat;
+    struct at_ifaddr   *aa;
+    struct ddpcb       *ddpp;
+
+    if ( ddp->ddp_lsat.sat_port != ATADDR_ANYPORT ) { /* shouldn't be bound */
+       return( EINVAL );
+    }
+
+    if ( addr != 0 ) {                 /* validate passed address */
+       sat = mtod( addr, struct sockaddr_at *);
+       if ( addr->m_len != sizeof( *sat )) {
+           return( EINVAL );
+       }
+       if ( sat->sat_family != AF_APPLETALK ) {
+           return( EAFNOSUPPORT );
+       }
+
+       if ( sat->sat_addr.s_node != ATADDR_ANYNODE ||
+               sat->sat_addr.s_net != ATADDR_ANYNET ) {
+           for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
+               if (( sat->sat_addr.s_net == AA_SAT( aa )->sat_addr.s_net ) &&
+                ( sat->sat_addr.s_node == AA_SAT( aa )->sat_addr.s_node )) {
+                   break;
+               }
+           }
+           if ( !aa ) {
+               return( EADDRNOTAVAIL );
+           }
+       }
+
+       if ( sat->sat_port != ATADDR_ANYPORT ) {
+           if ( sat->sat_port < ATPORT_FIRST ||
+                   sat->sat_port >= ATPORT_LAST ) {
+               return( EINVAL );
+           }
+#ifdef BSD4_4
+           if ( sat->sat_port < ATPORT_RESERVED &&
+                   suser( u.u_cred, &u.u_acflag )) {
+               return( EACCES );
+           }
+#else BSD4_4
+           if ( sat->sat_port < ATPORT_RESERVED && ( !suser())) {
+               return( EACCES );
+           }
+#endif BSD4_4
+       }
+    } else {
+       bzero( (caddr_t)&lsat, sizeof( struct sockaddr_at ));
+       lsat.sat_family = AF_APPLETALK;
+       sat = &lsat;
+    }
+
+    if ( sat->sat_addr.s_node == ATADDR_ANYNODE &&
+           sat->sat_addr.s_net == ATADDR_ANYNET ) {
+       if ( at_ifaddr == NULL ) {
+           return( EADDRNOTAVAIL );
+       }
+       sat->sat_addr = AA_SAT( at_ifaddr )->sat_addr;
+    }
+    ddp->ddp_lsat = *sat;
+
+    /*
+     * Choose port.
+     */
+    if ( sat->sat_port == ATADDR_ANYPORT ) {
+       for ( sat->sat_port = ATPORT_RESERVED;
+               sat->sat_port < ATPORT_LAST; sat->sat_port++ ) {
+           if ( ddp_ports[ sat->sat_port - 1 ] == 0 ) {
+               break;
+           }
+       }
+       if ( sat->sat_port == ATPORT_LAST ) {
+           return( EADDRNOTAVAIL );
+       }
+       ddp->ddp_lsat.sat_port = sat->sat_port;
+       ddp_ports[ sat->sat_port - 1 ] = ddp;
+    } else {
+       for ( ddpp = ddp_ports[ sat->sat_port - 1 ]; ddpp;
+               ddpp = ddpp->ddp_pnext ) {
+           if ( ddpp->ddp_lsat.sat_addr.s_net == sat->sat_addr.s_net &&
+                   ddpp->ddp_lsat.sat_addr.s_node == sat->sat_addr.s_node ) {
+               break;
+           }
+       }
+       if ( ddpp != NULL ) {
+           return( EADDRINUSE );
+       }
+       ddp->ddp_pnext = ddp_ports[ sat->sat_port - 1 ];
+       ddp_ports[ sat->sat_port - 1 ] = ddp;
+       if ( ddp->ddp_pnext ) {
+           ddp->ddp_pnext->ddp_pprev = ddp;
+       }
+    }
+
+    return( 0 );
+}
+
+at_pcbconnect( ddp, addr )
+    struct ddpcb       *ddp;
+    struct mbuf                *addr;
+{
+    struct sockaddr_at *sat = mtod( addr, struct sockaddr_at *);
+    struct route       *ro;
+    struct at_ifaddr   *aa = 0;
+    struct ifnet       *ifp;
+    u_short            hintnet = 0, net;
+
+    if ( addr->m_len != sizeof( *sat ))
+       return( EINVAL );
+    if ( sat->sat_family != AF_APPLETALK ) {
+       return( EAFNOSUPPORT );
+    }
+
+    /*
+     * Under phase 2, network 0 means "the network".  We take "the
+     * network" to mean the network the control block is bound to.
+     * If the control block is not bound, there is an error.
+     */
+    if ( sat->sat_addr.s_net == 0 && sat->sat_addr.s_node != ATADDR_ANYNODE ) {
+       if ( ddp->ddp_lsat.sat_port == ATADDR_ANYPORT ) {
+           return( EADDRNOTAVAIL );
+       }
+       hintnet = ddp->ddp_lsat.sat_addr.s_net;
+    }
+
+    ro = &ddp->ddp_route;
+    /*
+     * If we've got an old route for this pcb, check that it is valid.
+     * If we've changed our address, we may have an old "good looking"
+     * route here.  Attempt to detect it.
+     */
+    if ( ro->ro_rt ) {
+       if ( hintnet ) {
+           net = hintnet;
+       } else {
+           net = sat->sat_addr.s_net;
+       }
+       aa = 0;
+       if ( ifp = ro->ro_rt->rt_ifp ) {
+           for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
+               if ( aa->aa_ifp == ifp &&
+                       ntohs( net ) >= ntohs( aa->aa_firstnet ) &&
+                       ntohs( net ) <= ntohs( aa->aa_lastnet )) {
+                   break;
+               }
+           }
+       }
+       if ( aa == NULL || ( satosat( &ro->ro_dst )->sat_addr.s_net !=
+               ( hintnet ? hintnet : sat->sat_addr.s_net ) ||
+               satosat( &ro->ro_dst )->sat_addr.s_node !=
+               sat->sat_addr.s_node )) {
+#ifdef ultrix
+           rtfree( ro->ro_rt );
+#else ultrix
+           RTFREE( ro->ro_rt );
+#endif ultrix
+           ro->ro_rt = (struct rtentry *)0;
+       }
+    }
+
+    /*
+     * If we've got no route for this interface, try to find one.
+     */
+    if ( ro->ro_rt == (struct rtentry *)0 ||
+        ro->ro_rt->rt_ifp == (struct ifnet *)0 ) {
+#ifdef BSD4_4
+       ro->ro_dst.sa_len = sizeof( struct sockaddr_at );
+#endif BSD4_4
+       ro->ro_dst.sa_family = AF_APPLETALK;
+       if ( hintnet != 0 ) {
+           satosat( &ro->ro_dst )->sat_addr.s_net = hintnet;
+       } else {
+           satosat( &ro->ro_dst )->sat_addr.s_net = sat->sat_addr.s_net;
+       }
+       satosat( &ro->ro_dst )->sat_addr.s_node = sat->sat_addr.s_node;
+       rtalloc( ro );
+    }
+
+    /*
+     * Make sure any route that we have has a valid interface.
+     */
+    aa = 0;
+    if ( ro->ro_rt && ( ifp = ro->ro_rt->rt_ifp )) {
+       for ( aa = at_ifaddr; aa; aa = aa->aa_next ) {
+           if ( aa->aa_ifp == ifp ) {
+               break;
+           }
+       }
+    }
+    if ( aa == 0 ) {
+       return( ENETUNREACH );
+    }
+
+    ddp->ddp_fsat = *sat;
+    if ( ddp->ddp_lsat.sat_port == ATADDR_ANYPORT ) {
+       return( at_pcbsetaddr( ddp, (struct mbuf *)0 ));
+    }
+    return( 0 );
+}
+
+at_pcbdisconnect( ddp )
+    struct ddpcb       *ddp;
+{
+    ddp->ddp_fsat.sat_addr.s_net = ATADDR_ANYNET;
+    ddp->ddp_fsat.sat_addr.s_node = ATADDR_ANYNODE;
+    ddp->ddp_fsat.sat_port = ATADDR_ANYPORT;
+}
+
+at_pcballoc( so )
+    struct socket      *so;
+{
+    struct ddpcb       *ddp;
+    struct mbuf                *m;
+
+    m = m_getclr( M_WAIT, MT_PCB );
+    ddp = mtod( m, struct ddpcb * );
+    ddp->ddp_lsat.sat_port = ATADDR_ANYPORT;
+
+    ddp->ddp_next = ddpcb;
+    ddp->ddp_prev = NULL;
+    ddp->ddp_pprev = NULL;
+    ddp->ddp_pnext = NULL;
+    if ( ddpcb ) {
+       ddpcb->ddp_prev = ddp;
+    }
+    ddpcb = ddp;
+
+    ddp->ddp_socket = so;
+    so->so_pcb = (caddr_t)ddp;
+    return( 0 );
+}
+
+at_pcbdetach( so, ddp )
+    struct socket      *so;
+    struct ddpcb       *ddp;
+{
+    soisdisconnected( so );
+    so->so_pcb = 0;
+    sofree( so );
+
+    /* remove ddp from ddp_ports list */
+    if ( ddp->ddp_lsat.sat_port != ATADDR_ANYPORT &&
+           ddp_ports[ ddp->ddp_lsat.sat_port - 1 ] != NULL ) {
+       if ( ddp->ddp_pprev != NULL ) {
+           ddp->ddp_pprev->ddp_pnext = ddp->ddp_pnext;
+       } else {
+           ddp_ports[ ddp->ddp_lsat.sat_port - 1 ] = ddp->ddp_pnext;
+       }
+       if ( ddp->ddp_pnext != NULL ) {
+           ddp->ddp_pnext->ddp_pprev = ddp->ddp_pprev;
+       }
+    }
+
+    if ( ddp->ddp_route.ro_rt ) {
+       rtfree( ddp->ddp_route.ro_rt );
+    }
+
+    if ( ddp->ddp_prev ) {
+       ddp->ddp_prev->ddp_next = ddp->ddp_next;
+    } else {
+       ddpcb = ddp->ddp_next;
+    }
+    if ( ddp->ddp_next ) {
+       ddp->ddp_next->ddp_prev = ddp->ddp_prev;
+    }
+
+    (void) m_free( dtom( ddp ));
+}
+
+/*
+ * For the moment, this just find the pcb with the correct local address.
+ * In the future, this will actually do some real searching, so we can use
+ * the sender's address to do de-multiplexing on a single port to many
+ * sockets (pcbs).
+ */
+    struct ddpcb *
+ddp_search( from, to, aa )
+    struct sockaddr_at *from, *to;
+    struct at_ifaddr   *aa;
+{
+    struct ddpcb       *ddp;
+
+    /*
+     * Check for bad ports.
+     */
+    if ( to->sat_port < ATPORT_FIRST || to->sat_port >= ATPORT_LAST ) {
+       return( NULL );
+    }
+
+    /*
+     * Make sure the local address matches the sent address.  What about
+     * the interface?
+     */
+    for ( ddp = ddp_ports[ to->sat_port - 1 ]; ddp; ddp = ddp->ddp_pnext ) {
+       /* XXX should we handle 0.YY? */
+
+       /* XXXX.YY to socket on destination interface */
+       if ( to->sat_addr.s_net == ddp->ddp_lsat.sat_addr.s_net &&
+               to->sat_addr.s_node == ddp->ddp_lsat.sat_addr.s_node ) {
+           break;
+       }
+
+       /* 0.255 to socket on receiving interface */
+       if ( to->sat_addr.s_node == ATADDR_BCAST && ( to->sat_addr.s_net == 0 ||
+               to->sat_addr.s_net == ddp->ddp_lsat.sat_addr.s_net ) &&
+               ddp->ddp_lsat.sat_addr.s_net == AA_SAT( aa )->sat_addr.s_net ) {
+           break;
+       }
+
+       /* XXXX.0 to socket on destination interface */
+       if ( to->sat_addr.s_net == aa->aa_firstnet &&
+               to->sat_addr.s_node == 0 &&
+               ntohs( ddp->ddp_lsat.sat_addr.s_net ) >=
+               ntohs( aa->aa_firstnet ) &&
+               ntohs( ddp->ddp_lsat.sat_addr.s_net ) <=
+               ntohs( aa->aa_lastnet )) {
+           break;
+       }
+    }
+    return( ddp );
+}
+
+ddp_init()
+{
+    atintrq1.ifq_maxlen = IFQ_MAXLEN;
+    atintrq2.ifq_maxlen = IFQ_MAXLEN;
+}
+
+ddp_clean()
+{
+    struct ddpcb       *ddp;
+
+    for ( ddp = ddpcb; ddp; ddp = ddp->ddp_next ) {
+       at_pcbdetach( ddp->ddp_socket, ddp );
+    }
+}
diff --git a/sys/netatalk/ddp_var.h b/sys/netatalk/ddp_var.h
new file mode 100644 (file)
index 0000000..3a8f623
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#ifndef _NETATALK_DDP_VAR_H
+#define _NETATALK_DDP_VAR_H 1
+
+#include <netatalk/endian.h>
+
+struct ddpcb {
+    struct sockaddr_at ddp_fsat, ddp_lsat;
+    struct route       ddp_route;
+    struct socket      *ddp_socket;
+    struct ddpcb       *ddp_prev, *ddp_next;
+    struct ddpcb       *ddp_pprev, *ddp_pnext;
+};
+
+#define sotoddpcb(so)  ((struct ddpcb *)(so)->so_pcb)
+
+struct ddpstat {
+    u_int32_t  ddps_short;             /* short header packets received */
+    u_int32_t  ddps_long;              /* long header packets received */
+    u_int32_t  ddps_nosum;             /* no checksum */
+    u_int32_t  ddps_badsum;            /* bad checksum */
+    u_int32_t  ddps_tooshort;          /* packet too short */
+    u_int32_t  ddps_toosmall;          /* not enough data */
+    u_int32_t  ddps_forward;           /* packets forwarded */
+    u_int32_t  ddps_encap;             /* packets encapsulated */
+    u_int32_t  ddps_cantforward;       /* packets rcvd for unreachable dest */
+    u_int32_t  ddps_nosockspace;       /* no space in sockbuf for packet */
+};
+
+#ifdef KERNEL
+struct ddpcb           *ddp_ports[ ATPORT_LAST ];
+struct ddpcb           *ddpcb;
+struct ddpstat         ddpstat;
+struct ddpcb           *ddp_search();
+#endif
+
+#endif /* netatalk/ddp_var.h */
diff --git a/sys/netatalk/endian.h b/sys/netatalk/endian.h
new file mode 100644 (file)
index 0000000..e602ec5
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ * This file handles both byte ordering and integer sizes.
+ */
+
+#  ifndef _ATALK_ENDIAN_H_
+#define _ATALK_ENDIAN_H_
+
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#ifdef _IBMR2
+#include <sys/machine.h>
+#endif /*_IBMR2*/
+
+#ifdef _ISOC9X_SOURCE
+#include <inttypes.h>
+typedef uint8_t        u_int8_t;
+typedef uint16_t       u_int16_t;
+typedef uint32_t       u_int32_t;
+typedef uint64_t       u_int64_t;
+#else
+
+/* handle sunos and solaris */
+#ifdef sun
+#ifdef BSD4_3
+#include <sys/bitypes.h>
+#else
+/* solaris and sunos don't consistently define u_int*_t. */
+typedef unsigned char      u_int8_t;
+typedef unsigned short     u_int16_t;
+typedef unsigned int       u_int32_t;
+typedef int                int32_t;
+#endif
+
+typedef unsigned long long u_int64_t;
+
+#ifndef _SSIZE_T
+#define _SSIZE_T
+typedef int ssize_t;
+#endif /* ssize_t */
+
+#else /* sun */
+
+/* luckily ultrix is dead. as a result, we know what the sizes of
+ * various types are forever. this makes some assumptions about integer
+ * sizes. */
+#if defined (ultrix) || defined(HAVE_32BIT_LONGS) || defined(HAVE_64BIT_LONGS)
+typedef unsigned char  u_int8_t;
+typedef unsigned short u_int16_t;
+typedef unsigned int   u_int32_t;
+typedef int            int32_t;
+#endif
+
+#ifdef ultrix
+typedef int            ssize_t;
+#endif
+
+#ifdef HAVE_64BIT_LONGS
+typedef unsigned long u_int64_t;
+#else
+/* check for long long support. currently, i assume that if 64-bit
+ * ints exist that their made available via long long */
+#ifdef linux
+#include <endian.h> /* i think this is here for libc4 */
+#else 
+#if defined(HAVE_32BIT_LONGS) && !(defined(BSD4_4) || \
+                                 defined(NO_LARGE_VOL_SUPPORT))
+typedef unsigned long long  u_int64_t;
+#endif
+#endif /* linux */
+#endif /* HAVE_64BIT_LONGS */
+#endif /* sun */
+#endif /* ISOC9X */
+
+# ifndef BYTE_ORDER
+#define LITTLE_ENDIAN  1234
+#define BIG_ENDIAN     4321
+#define PDP_ENDIAN     3412
+
+#ifdef sun
+#if defined(i386) || defined(_LITTLE_ENDIAN)
+#define BYTE_ORDER     LITTLE_ENDIAN
+#else /*i386*/
+#define BYTE_ORDER     BIG_ENDIAN
+#endif /*i386*/
+#else
+#ifdef MIPSEB
+#define BYTE_ORDER     BIG_ENDIAN
+#else
+#ifdef MIPSEL
+#define BYTE_ORDER     LITTLE_ENDIAN
+#else
+#error Like, what is your byte order, man?
+#endif /*MIPSEL*/
+#endif /*MIPSEB*/
+#endif /*sun*/
+# endif /*BYTE_ORDER*/
+
+# ifndef ntohl
+# if defined( sun ) || defined( ultrix ) || defined( _IBMR2 )
+#if BYTE_ORDER == BIG_ENDIAN
+#define ntohl(x)       (x)
+#define ntohs(x)       (x)
+#define htonl(x)       (x)
+#define htons(x)       (x)
+
+#else
+#if defined( mips ) && defined( KERNEL )
+#define        ntohl(x)        nuxi_l(x)
+#define        ntohs(x)        nuxi_s(x)
+#define        htonl(x)        nuxi_l(x)
+#define        htons(x)        nuxi_s(x)
+
+#else /*mips KERNEL*/
+
+#if !( defined( sun ) && defined( i386 ))
+unsigned short ntohs(), htons();
+unsigned int  ntohl(), htonl();
+#endif
+
+#endif /*mips KERNEL*/
+#endif /*BYTE_ORDER*/
+# endif /*sun ultrix _IBMR2*/
+# endif /*ntohl*/
+
+#  endif /*_ATALK_ENDIAN_H_*/
diff --git a/sys/netatalk/phase2.h b/sys/netatalk/phase2.h
new file mode 100644 (file)
index 0000000..54eac74
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ */
+
+# if defined( ultrix ) || defined( BSD4_4 )
+#include <net/if_llc.h>
+# else ultrix BSD4_4
+
+#if defined( sun ) && !defined( __svr4__ )
+#include <net/if_ieee802.h>
+#endif sun __svr4__
+
+/*
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ *      @(#)if_llc.h   7.2 (Berkeley) 6/28/90
+ */
+
+/*
+ * IEEE 802.2 Link Level Control headers, for use in conjunction with
+ * 802.{3,4,5} media access control methods.
+ *
+ * Headers here do not use bit fields due to shortcommings in many
+ * compilers.
+ */
+
+struct llc {
+       u_char  llc_dsap;
+       u_char  llc_ssap;
+       union {
+           struct {
+               u_char control;
+               u_char format_id;
+               u_char class;
+               u_char window_x2;
+           } type_u;
+           struct {
+               u_char num_snd_x2;
+               u_char num_rcv_x2;
+           } type_i;
+           struct {
+               u_char control;
+               u_char num_rcv_x2;
+           } type_s;
+           struct {
+               u_char control;
+               u_char org_code[3];
+               u_short ether_type;
+           } type_snap;
+       } llc_un;
+};
+#define llc_control llc_un.type_u.control
+#define llc_fid llc_un.type_u.format_id
+#define llc_class llc_un.type_u.class
+#define llc_window llc_un.type_u.window_x2
+#define llc_org_code llc_un.type_snap.org_code
+#define llc_ether_type llc_un.type_snap.ether_type
+
+#define LLC_UI         0x3
+#define LLC_UI_P       0x13
+#define LLC_XID                0xaf
+#define LLC_XID_P      0xbf
+#define LLC_TEST       0xe3
+#define LLC_TEST_P     0xf3
+
+#define LLC_ISO_LSAP   0xfe
+#define LLC_SNAP_LSAP  0xaa
+
+# endif ultrix BSD4_4
+
+#if defined( sun ) || defined( ibm032 )
+#define SIOCPHASE1     _IOW(i, 100, struct ifreq)      /* AppleTalk phase 1 */
+#define SIOCPHASE2     _IOW(i, 101, struct ifreq)      /* AppleTalk phase 2 */
+#endif sun ibm032
+
+#if defined( ultrix ) || defined( BSD4_4 ) || defined( _IBMR2 )
+#define SIOCPHASE1     _IOW('i', 100, struct ifreq)    /* AppleTalk phase 1 */
+#define SIOCPHASE2     _IOW('i', 101, struct ifreq)    /* AppleTalk phase 2 */
+#endif ultrix BSD4_4 _IBMR2
diff --git a/sys/netbsd/Makefile b/sys/netbsd/Makefile
new file mode 100644 (file)
index 0000000..c31d3b4
--- /dev/null
@@ -0,0 +1,91 @@
+# NetBSD specific defines, passed to subdirectories.
+DEFS=  -DBSD4_4 -DDLSYM_PREPEND_UNDERSCORE
+OPTOPTS=       -O2
+CC=    gcc -I../../sys/netbsd/ -I/usr/include/kerberosIV
+CSHAREDFLAGS=-fPIC
+LDSHARED=ld
+LDSHAREDFLAGS=$$MACHLDSHAREDFLAGS
+#LDFLAGS_EXPORT=-Wl,-E
+#LIBSHARED=-ldl
+INSTALL=       install
+AFPLIBS =
+ADDLIBS =
+
+ALL=   ../../libatalk ../../include ../../bin ../../etc ../../man
+
+oops:
+       @echo "Read README again.  Don't type 'make' here."
+       @exit 1
+
+all:   ${ALL}
+
+../../bin ../../etc:   ../../libatalk
+
+${ALL}:        FRC
+       @case ${MACHINETYPE} in \
+       alpha|mips|powerpc) MACHLDSHAREDFLAGS=-shared ;; \
+       *) MACHLDSHAREDFLAGS=-Bshareable ;; \
+       esac; \
+       cd $@; ${MAKE} ${MFLAGS} CC="${CC}" \
+           ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+           SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+           ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+           DESTDIR="${DESTDIR}" AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" \
+           AFPLIBS="${AFPLIBS}" LDSHARED="${LDSHARED}" \
+           LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+           LDSHAREDFLAGS="${LDSHAREDFLAGS}" CSHAREDFLAGS="${CSHAREDFLAGS}" \
+           LIBSHARED="${LIBSHARED}" \
+           all
+
+FRC:
+
+install :
+       -mkdir ${DESTDIR} ${SBINDIR} ${BINDIR} ${ETCDIR} ${LIBDIR}
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} CC="${CC}" \
+               ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+               SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+               ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+               AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" AFPLIBS="${AFPLIBS}" \
+               LDSHARED="${LDSHARED}" LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+               LDSHAREDFLAGS="${LDSHAREDFLAGS}" \
+               CSHAREDFLAGS="${CSHAREDFLAGS}" LIBSHARED="${LIBSHARED}" \
+               INSTALL="${INSTALL}" $@); \
+       done
+       rm -f ${ETCDIR}/rc.atalk
+       sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+               -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+               -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+               -e s@:INCDIR:@${INCDIR}@ \
+           < ../../distrib/initscripts/rc.atalk.bsd > ${ETCDIR}/rc.atalk
+       if [ -f ${ETCDIR}/afpd.conf ]; then \
+               echo "Retaining old afpd.conf file.";  \
+       else \
+               sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+                       -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+                       -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+                       -e s@:INCDIR:@${INCDIR}@ \
+                       < ../../config/afpd.conf > ${ETCDIR}/afpd.conf; \
+       fi
+       @echo
+       @echo "Install is done.  You might need to add services from"
+       @echo "services.atalk to /etc/services. The ddp services need to be"
+       @echo "added for NetBSD versions earlier than 1.3, and the AFP over"
+       @echo "TCP services need to be added for NetBSD 1.3.X, and for"
+       @echo "1.3-current dated June 27, 1998 and earlier."
+       @echo
+       @echo "Don't forget to call rc.atalk in /etc/rc.local.  See README"
+       @echo "and README.NETBSD for more information."
+
+clean :
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} clean); \
+       done
+
+depend :
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} DEFS="${DEFS}" depend); \
+       done
+
+# DO NOT DELETE THIS LINE
+
diff --git a/sys/netbsd/netatalk/endian.h b/sys/netbsd/netatalk/endian.h
new file mode 100644 (file)
index 0000000..a1957e2
--- /dev/null
@@ -0,0 +1,9 @@
+
+/*
+ * netatalk/endian.h
+ *
+ * This file is a dummy replacement for endian.h. NetBSD includes
+ * machine/endian.h in sys/types.h, and it was decided that a
+ * second include file was not needed. This file is included to
+ * make all the source code compile correctly.
+ */
diff --git a/sys/openbsd/Makefile b/sys/openbsd/Makefile
new file mode 100644 (file)
index 0000000..c25583c
--- /dev/null
@@ -0,0 +1,83 @@
+# OpenBSD specific defines, passed to subdirectories.
+DEFS=  -DBSD4_4
+OPTOPTS=       -O2
+CC=    gcc 
+CSHAREDFLAGS=-fPIC
+LDSHARED=ld
+LDSHAREDFLAGS=-Bforcearchive -shared
+#LDFLAGS_EXPORT=-Wl,-E
+#LIBSHARED=-ldl
+INSTALL=       install
+AFPLIBS=
+ADDLIBS=
+
+ALL=   ../../libatalk ../../include ../../bin ../../etc ../../man
+
+oops:
+       @echo "Read README again.  Don't type 'make' here."
+       @exit 1
+
+all:   ${ALL}
+
+../../bin ../../etc:   ../../libatalk
+
+${ALL}:        FRC
+       cd $@; ${MAKE} ${MFLAGS} CC="${CC}" \
+           ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+           SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+           ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+           DESTDIR="${DESTDIR}" AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" \
+           AFPLIBS="${AFPLIBS}" LDSHARED="${LDSHARED}" \
+           LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+           LDSHAREDFLAGS="${LDSHAREDFLAGS}" CSHAREDFLAGS="${CSHAREDFLAGS}" \
+           LIBSHARED="${LIBSHARED}" \
+           all
+
+FRC:
+
+install :
+       -mkdir ${DESTDIR} ${SBINDIR} ${BINDIR} ${ETCDIR} ${LIBDIR}
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} CC="${CC}" \
+               ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+               SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+               ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+               AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" AFPLIBS="${AFPLIBS}" \
+               LDSHARED="${LDSHARED}" LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+               LDSHAREDFLAGS="${LDSHAREDFLAGS}" \
+               CSHAREDFLAGS="${CSHAREDFLAGS}" LIBSHARED="${LIBSHARED}" \
+               INSTALL="${INSTALL}" $@); \
+       done
+       rm -f ${ETCDIR}/rc.atalk
+       sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+               -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+               -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+               -e s@:INCDIR:@${INCDIR}@ \
+           < ../../distrib/initscripts/rc.atalk.bsd > ${ETCDIR}/rc.atalk
+       if [ -f ${ETCDIR}/afpd.conf ]; then \
+               echo "Retaining old afpd.conf file.";  \
+       else \
+               sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+                       -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+                       -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+                       -e s@:INCDIR:@${INCDIR}@ \
+                       < ../../config/afpd.conf > ${ETCDIR}/afpd.conf; \
+       fi
+       @echo
+       @echo "Install is done.  Don't forget to add lines from"
+       @echo "services.atalk to /etc/services and to call rc.atalk"
+       @echo "in /etc/rc.  See README and README.OPENBSD for more"
+       @echo "information."
+
+clean :
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} clean); \
+       done
+
+depend :
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} DEFS="${DEFS}" depend); \
+       done
+
+# DO NOT DELETE THIS LINE
+
diff --git a/sys/osx/Makefile b/sys/osx/Makefile
new file mode 100644 (file)
index 0000000..57df17a
--- /dev/null
@@ -0,0 +1,95 @@
+# stuff for os x server. This needs some include files from the darwin
+# source to work properly. namely, it needs /usr/include/at,
+# /usr/include/h, and
+# /System/Library/Frameworks/LibcAT/Headers/at_proto.h. The first two are
+# found in the kernel sources while the last one is found in LibcAT.
+#
+# notes: apple's dynamic library loader is pretty braindead.
+#        you cannot do a kill -HUP afpd as a result. in addition, you
+#        can't load both uams_randnum.so and uams_dhx.so at the same time
+#        as they use the same libraries. 
+#       don't do tail /var/log/system.log a lot. it will cause os x 
+#       server to crash. it's probably a race somewhere.
+#
+DEFS=-DBSD4_4 -DHAVE_BROKEN_CPP -DHAVE_2ARG_DBTOB -DNO_DLFCN_H \
+       -DMACOSX_SERVER 
+OPTOPTS=       -O2 -fomit-frame-pointer
+CC =   cc
+#CC=   gcc
+CSHAREDFLAGS=-fPIC -fno-common
+LDSHARED=cc
+LDSHAREDFLAGS=-bundle -undefined suppress
+INSTALL=       install
+ADDLIBS=-framework LibcAT
+
+ALL=   ../../libatalk ../../include ../../bin ../../etc ../../man
+
+oops:
+       @echo "Read README again.  Don't type 'make' here."
+       @exit 1
+
+all:   ${ALL}
+
+../../bin ../../etc:   ../../libatalk
+
+${ALL}:        FRC
+       cd $@; ${MAKE} ${MFLAGS} CC="${CC}" \
+           ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+           SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+           ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+           DESTDIR="${DESTDIR}" AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" \
+           AFPLIBS="${AFPLIBS}" LDSHARED="${LDSHARED}" \
+           LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+           LDSHAREDFLAGS="${LDSHAREDFLAGS}" CSHAREDFLAGS="${CSHAREDFLAGS}" \
+           LIBSHARED="${LIBSHARED}" \
+           all
+
+FRC:
+
+install :
+       -mkdir ${DESTDIR} ${SBINDIR} ${BINDIR} ${ETCDIR} ${LIBDIR}
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} CC="${CC}" \
+               ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+               SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+               ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+               AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" AFPLIBS="${AFPLIBS}" \
+               LDSHARED="${LDSHARED}" LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+               LDSHAREDFLAGS="${LDSHAREDFLAGS}" \
+               CSHAREDFLAGS="${CSHAREDFLAGS}" LIBSHARED="${LIBSHARED}" \
+               INSTALL="${INSTALL}" $@); \
+       done
+       rm -f ${ETCDIR}/rc.atalk
+       sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+               -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+               -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+               -e s@:INCDIR:@${INCDIR}@ \
+           < ../../distrib/initscripts/rc.atalk.bsd > ${ETCDIR}/rc.atalk
+       chmod 744 ${ETCDIR}/rc.atalk
+       if [ -f ${ETCDIR}/afpd.conf ]; then \
+               echo "Retaining old afpd.conf file.";  \
+       else \
+               sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+                       -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+                       -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+                       -e s@:INCDIR:@${INCDIR}@ \
+                       < ../../config/afpd.conf > ${ETCDIR}/afpd.conf; \
+       fi
+       @echo
+       @echo "Install is done.  Don't forget to add lines from"
+       @echo "services.atalk to /etc/services and to call rc.atalk"
+       @echo "in /etc/rc.  See README and README.GENERIC for more"
+       @echo "information. Also, you do not need to start up atalkd."
+
+clean :
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} clean); \
+       done
+
+depend :
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} DEFS="${DEFS}" depend); \
+       done
+
+# DO NOT DELETE THIS LINE
+
diff --git a/sys/solaris/Makefile b/sys/solaris/Makefile
new file mode 100644 (file)
index 0000000..3576c47
--- /dev/null
@@ -0,0 +1,194 @@
+# Solaris specific defines, passed to subdirectories.
+# To use Sun CC, uncomment the CC and KFLAGS variables.
+
+# uncomment for 64-bit sparc kernel
+#SPARC64=true
+
+# solaris has -lcrypt, but we shouldn't use it. this may only be true 
+# for solaris 2.5.1
+USE_CRYPTLIB=no
+
+# use gcc (gcc-2.95 on 64-bit sparc)
+CC=            gcc
+KCFLAGS=       -D_KERNEL -Wall -Wstrict-prototypes ${KGCCFLAGS} # -mcpu=ultrasparc
+OPTOPTS=       -O
+CSHAREDFLAGS=   -fPIC
+LDSHARED=      gcc
+LDSHAREDFLAGS=  -shared
+
+# use Sun CC (for a 64-bit kernel, uncomment " -xarch=v9 -xregs=no%appl ") 
+#CC=           cc
+#KCFLAGS=      -D_KERNEL # -xarch=v9 -xregs=no%appl 
+#OPTOPTS=      -fast -xO5 -xstrconst
+#CSHAREDFLAGS=   -KPIC
+#LDSHARED=       cc
+#LDSHAREDFLAGS=  -G
+#LDFLAGS_EXPORT=
+
+LIBSHARED=      -ldl
+
+# -D_ISOC9X_SOURCE is handled by OSVERSION. basically, it's not needed
+# with 2.5.1.
+# add -DHAVE_IFNAMEINDEX if you're using solaris 8.
+DEFS=  -DNO_STRUCT_TM_GMTOFF -D__svr4__ -DSOLARIS -I../../sys/generic \
+       ${OSDEFS} ${MACHINEDEFS}
+INSTALL=       /usr/ucb/install
+AFPLIBS=
+ADDLIBS=       -lsocket -lnsl
+
+# Local build stuff.
+
+SRC= linkage.c tpi.c dlpi.c ioc.c if.c aarp.c ddp.c sock.c rt.c
+OBJ= linkage.o tpi.o dlpi.o ioc.o if.o aarp.o ddp.o sock.o rt.o
+
+INCPATH=       -I../../include -I../netatalk
+CFLAGS=        ${DEFS} ${OPTOPTS} ${INCPATH} ${KCFLAGS} 
+
+ALL=   ../../libatalk ../../include ../../bin ../../etc ../../man
+
+oops:
+       @echo "Read README again.  Don't type 'make' here."
+       @exit 1
+
+all :  kernel ${ALL}
+
+kernel :       FRC
+       @case `uname -p` in \
+           i386) \
+               PROCESSOR=i386; \
+               KGCCFLAGS=""; \
+               ;; \
+           sparc) \
+               PROCESSOR=sparc; \
+               KGCCFLAGS="-mno-app-regs -munaligned-doubles \
+                       -fpcc-struct-return" \
+               ;; \
+           *) echo "Unknown processor type..."; exit 1 \
+               ;; \
+       esac; \
+       if [ x"${OSVERSION}" != x"5.5.1" ]; then \
+               OSDEFS=-D_ISOC9X_SOURCE; \
+       fi; \
+       echo "Making $@ for $$PROCESSOR..."; \
+       ${MAKE} ${MFLAGS} \
+           SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}"\
+           ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+           MANDIR="${MANDIR}" DESTDIR="${DESTDIR}" AFSDIR="${AFSDIR}" \
+           KRBDIR="${KRBDIR}" OSDEFS="$${OSDEFS}" \
+           MACHINEDEFS="$${MACHINEDEFS}" \
+           AFPLIBS="${AFPLIBS}" KGCCFLAGS="$${KGCCFLAGS}" ddp
+
+FRC: 
+
+ddp :  ${OBJ}
+       ld -r -o ddp ${OBJ}
+
+linkage.o : linkage.c
+       ${CC} ${CFLAGS} -DVERSION=\"`cat ../../VERSION`\" -c linkage.c
+
+../../bin ../../etc:   ../../libatalk
+
+${ALL}:        FRC
+       if [ x"${OSVERSION}" != x"5.5.1" ]; then \
+         OSDEFS=-D_ISOC9X_SOURCE; \
+       fi; \
+       cd $@; ${MAKE} ${MFLAGS} CC="${CC}" \
+           ADDLIBS="${ADDLIBS}" DEFS="${DEFS} $${OSDEFS}" \
+           OPTOPTS="${OPTOPTS}" \
+           SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+           ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+           DESTDIR="${DESTDIR}" AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" \
+           AFPLIBS="${AFPLIBS}" LDSHARED="${LDSHARED}" \
+           LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+           LDSHAREDFLAGS="${LDSHAREDFLAGS}" CSHAREDFLAGS="${CSHAREDFLAGS}" \
+           LIBSHARED="${LIBSHARED}" USE_CRYPTLIB="${USE_CRYPTLIB}" \
+           OSDEFS="$${OSDEFS}" MACHINEDEFS="$${MACHINEDEFS}" \
+           all
+
+FRC:
+
+kuninstall : FRC
+       rm -f /etc/rc2.d/S79atalk /etc/rc0.d/K79atalk
+       if [ x"${SPARC64}" != x ] ; then \
+               ${RM} /usr/kernel/drv/sparcv9/ddp; \
+               ${RM} /usr/kernel/strmod/sparcv9/ddp; \
+       else \
+               ${RM} /usr/kernel/drv/ddp; \
+               ${RM} /usr/kernel/strmod/ddp; \
+       fi
+       ${RM} /usr/kernel/drv/ddp.conf
+       -rem_drv ddp
+       sync;sync;sync
+
+kinstall : kernel kuninstall
+       if [ x"${SPARC64}" != x ]; then \
+               ${INSTALL} -c ddp /usr/kernel/drv/sparcv9/ddp; \
+               ln /usr/kernel/drv/sparcv9/ddp /usr/kernel/strmod/sparcv9/ddp; \
+       else \
+               ${INSTALL} -c ddp /usr/kernel/drv/ddp; \
+               ln /usr/kernel/drv/ddp /usr/kernel/strmod/ddp; \
+       fi
+       ${INSTALL} -c ddp.conf /usr/kernel/drv/ddp.conf
+       add_drv -m '* 0666 root sys' ddp
+       sync;sync;sync
+       if [ -f /etc/init.d/atalk ]; then \
+               echo "Preserving existing /etc/init.d/atalk settings."; \
+       else \
+       sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+               -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+               -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+               -e s@:INCDIR:@${INCDIR}@ \
+           < ../../distrib/initscripts/rc.atalk.sysv > /etc/init.d/atalk; \
+       fi
+       chmod 744 /etc/init.d/atalk
+       -ln -s ../init.d/atalk /etc/rc2.d/S79atalk
+       -ln -s ../init.d/atalk /etc/rc0.d/K79atalk
+
+install :
+       -mkdir ${DESTDIR} ${SBINDIR} ${BINDIR} ${ETCDIR} ${LIBDIR}
+       sed -e s@:BINDIR:@${BINDIR}@ < ../../lp2pap.sh > ${RESDIR}/lp2pap.sh
+       chmod 744 ${RESDIR}/lp2pap.sh
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} CC="${CC}" \
+               ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+               SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+               ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+               AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" \
+               AFPLIBS="${AFPLIBS}" \
+               LDSHARED="${LDSHARED}" LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+               LDSHAREDFLAGS="${LDSHAREDFLAGS}" \
+               CSHAREDFLAGS="${CSHAREDFLAGS}" LIBSHARED="${LIBSHARED}" \
+               OSDEFS="$${OSDEFS}" MACHINEDEFS="$${MACHINEDEFS}" \
+               INSTALL="${INSTALL}" $@); \
+       done
+       if [ -f ${ETCDIR}/afpd.conf ]; then \
+               echo "Retaining old afpd.conf file.";  \
+       else \
+               sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+                       -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+                       -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+                       -e s@:INCDIR:@${INCDIR}@ \
+                       < ../../config/afpd.conf > ${ETCDIR}/afpd.conf; \
+       fi
+       @echo
+       @echo "Install is done.  Don't forget to add lines from"
+       @echo "services.atalk to /etc/services and to call rc.atalk"
+       @echo "in /etc/rc.  See README and README.SOLARIS for more"
+       @echo "information."
+
+clean : sysclean
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} clean); \
+       done
+
+klean sysclean :
+       rm -f a.out core* *.o *.bak *[Ee]rrs tags
+       rm -f ddp
+
+depend :
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} DEFS="${DEFS}" depend); \
+       done
+
+# DO NOT DELETE THIS LINE
+
diff --git a/sys/solaris/aarp.c b/sys/solaris/aarp.c
new file mode 100644 (file)
index 0000000..02a8524
--- /dev/null
@@ -0,0 +1,343 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/byteorder.h>
+#include <sys/errno.h>
+#include <sys/stream.h>
+#include <sys/ethernet.h>
+#include <sys/kmem.h>
+#include <sys/cmn_err.h>
+#include <sys/ddi.h>
+#include <netinet/arp.h>
+#include <net/if.h>
+
+#include <string.h>
+
+#include <netatalk/at.h>
+#include <netatalk/aarp.h>
+#include <netatalk/phase2.h>
+
+#include "if.h"
+
+struct aarplist {
+    struct aarplist    *aal_next, *aal_prev;
+    struct at_addr     aal_addr;
+    u_char             aal_hwaddr[ ETHERADDRL ];
+    u_char             aal_age;
+    u_char             aal_flags;
+    mblk_t             *aal_m;
+};
+
+    struct aarplist *
+aarp_find( struct atif_data *aid, ushort net, unchar node )
+{
+    struct aarplist    *aal;
+
+    for ( aal = aid->aid_aarplist; aal != NULL; aal = aal->aal_next ) {
+       if ( aal->aal_addr.s_net == net && aal->aal_addr.s_node == node ) {
+           break;
+       }
+    }
+    return( aal );
+}
+
+    struct aarplist *
+aarp_alloc( struct atif_data *aid, ushort net, unchar node )
+{
+    struct aarplist    *aal;
+
+    for ( aal = aid->aid_aarplist; aal != NULL; aal = aal->aal_next ) {
+       if ( aal->aal_addr.s_net == net && aal->aal_addr.s_node == node ) {
+           return( aal );
+       }
+    }
+
+    if ( aid->aid_aarpflist == NULL ) {
+        if (( aal = (struct aarplist *)kmem_alloc( sizeof( struct aarplist ),
+                KM_NOSLEEP )) == NULL ) {
+            return( NULL );
+        }
+    } else {
+       aal = aid->aid_aarpflist;
+       aid->aid_aarpflist = aal->aal_next;
+       if ( aid->aid_aarpflist != NULL ) {
+           aid->aid_aarpflist->aal_prev = NULL;
+       }
+    }
+
+    aal->aal_addr.s_net = net;
+    aal->aal_addr.s_node = node;
+    bzero( aal->aal_hwaddr, sizeof( aal->aal_hwaddr ));
+    aal->aal_age = 0;
+    aal->aal_flags = 0;
+    aal->aal_m = NULL;
+
+    aal->aal_next = aid->aid_aarplist;
+    aal->aal_prev = NULL;
+    if ( aid->aid_aarplist != NULL ) {
+       aid->aid_aarplist->aal_prev = aal;
+    }
+    aid->aid_aarplist = aal;
+
+    return( aal );
+}
+
+/*
+ * Move entry to free list.
+ */
+    void
+aarp_free( struct atif_data *aid, struct aarplist *aal )
+{
+    if ( aal->aal_next != NULL ) {
+       aal->aal_next->aal_prev = aal->aal_prev;
+    }
+    if ( aal->aal_prev != NULL ) {
+       aal->aal_prev->aal_next = aal->aal_next;
+    }
+    if ( aid->aid_aarplist == aal ) {
+       aid->aid_aarplist = aal->aal_next;
+    }
+
+    if ( aal->aal_m != NULL ) {
+       freemsg( aal->aal_m );
+       aal->aal_m = NULL;
+    }
+
+    aal->aal_prev = NULL;
+    aal->aal_next = aid->aid_aarpflist;
+    if ( aid->aid_aarpflist != NULL ) {
+       aid->aid_aarpflist->aal_prev = aal;
+    }
+    aid->aid_aarpflist = aal;
+    return;
+}
+
+    void
+aarp_timeout( struct atif_data *aid )
+{
+    struct aarplist    *aal, *p;
+
+    aid->aid_aarptimeo = qtimeout( aid->aid_q, aarp_timeout,
+           (caddr_t)aid, 60 * hz );
+    for ( aal = aid->aid_aarplist; aal != NULL; aal = p ) {
+       p = aal->aal_next;
+       if ( ++aal->aal_age < (( aal->aal_flags ) ? 5 : 3 )) {
+           continue;
+       }
+       aarp_free( aid, aal );
+    }
+    return;
+}
+
+    void
+aarp_init( struct atif_data *aid )
+{
+    aid->aid_aarptimeo = qtimeout( aid->aid_q, aarp_timeout,
+           (caddr_t)aid, 60 * hz );
+    return;
+}
+
+    void
+aarp_clean( struct atif_data *aid )
+{
+    struct aarplist *aal, *p;
+
+    if ( aid->aid_aarptimeo != 0 ) {
+       quntimeout( aid->aid_q, aid->aid_aarptimeo );
+       aid->aid_aarptimeo = 0;
+    }
+
+    for ( aal = aid->aid_aarplist; aal != NULL; aal = p ) {
+       p = aal->aal_next;
+       if ( aal->aal_m != NULL ) {
+           freemsg( aal->aal_m );
+           aal->aal_m = NULL;
+       }
+       kmem_free( aal, sizeof( struct aarplist ));
+    }
+    aid->aid_aarplist = NULL;
+
+    for ( aal = aid->aid_aarpflist; aal != NULL; aal = p ) {
+       p = aal->aal_next;
+       if ( aal->aal_m != NULL ) {
+           freemsg( aal->aal_m );
+           aal->aal_m = NULL;
+       }
+       kmem_free( aal, sizeof( struct aarplist ));
+    }
+    aid->aid_aarpflist = NULL;
+
+    return;
+}
+
+    int
+aarp_rput( queue_t *q, mblk_t *m )
+{
+    struct atif_data   *aid = (struct atif_data *)q->q_ptr;
+    struct ether_aarp  *ea;
+    struct aarplist    *aal;
+    ushort             tpnet, spnet, op;
+
+    if ( m->b_wptr - m->b_rptr < sizeof( struct ether_aarp )) {
+       cmn_err( CE_NOTE, "aarp_rput short packet\n" );
+       goto done;
+    }
+
+    ea = (struct ether_aarp *)m->b_rptr;
+
+    if ( ea->aarp_hrd != htons( AARPHRD_ETHER ) ||
+           ea->aarp_pro != htons( ETHERTYPE_AT ) ||
+           ea->aarp_hln != sizeof( ea->aarp_sha ) ||
+           ea->aarp_pln != sizeof( ea->aarp_spu )) {
+       cmn_err( CE_NOTE, "aarp_rput bad constants\n" );
+       goto done;
+    }
+
+    if ( bcmp( ea->aarp_sha, aid->aid_hwaddr, sizeof( ea->aarp_sha )) == 0 ) {
+       goto done;
+    }
+
+    op = ntohs( ea->aarp_op );
+    bcopy( ea->aarp_tpnet, &tpnet, sizeof( tpnet ));
+    bcopy( ea->aarp_spnet, &spnet, sizeof( spnet ));
+
+    if ( aid->aid_flags & AIDF_PROBING ) {
+       if ( tpnet == aid->aid_sat.sat_addr.s_net &&
+               ea->aarp_tpnode == aid->aid_sat.sat_addr.s_node ) {
+           aid->aid_flags &= ~AIDF_PROBING;
+           aid->aid_flags |= AIDF_PROBEFAILED;
+           cmn_err( CE_NOTE, "aarp_rput probe collision %s\n", aid->aid_name );
+       }
+    } else {
+       if ( tpnet == aid->aid_sat.sat_addr.s_net &&
+               ea->aarp_tpnode == aid->aid_sat.sat_addr.s_node ) {
+           switch ( op ) {
+           case AARPOP_REQUEST :
+               aal = aarp_alloc( aid, spnet, ea->aarp_spnode );
+               bcopy( ea->aarp_sha, aal->aal_hwaddr, sizeof( ea->aarp_sha ));
+               aal->aal_age = 0;
+               aal->aal_flags = 1;             /* complete */
+           case AARPOP_PROBE :
+               aarp_send( aid, AARPOP_RESPONSE, ea->aarp_sha,
+                       spnet, ea->aarp_spnode );
+               break;
+
+           case AARPOP_RESPONSE :
+               if (( aal =
+                       aarp_find( aid, spnet, ea->aarp_spnode )) == NULL ) {
+                   break;
+               }
+               bcopy( ea->aarp_sha, aal->aal_hwaddr, sizeof( ea->aarp_sha ));
+               aal->aal_age = 0;
+               aal->aal_flags = 1;             /* complete */
+               if ( aal->aal_m != NULL ) {
+                   dl_unitdata_req( WR( q ), aal->aal_m, ETHERTYPE_AT,
+                           aal->aal_hwaddr );
+                   aal->aal_m = NULL;
+               }
+               break;
+
+           default :
+               cmn_err( CE_NOTE, "aarp_rput bad op %X\n", op );
+               break;
+           }
+       } else {
+           switch ( op ) {
+           case AARPOP_REQUEST :
+               break;
+           case AARPOP_PROBE :
+               if (( aal =
+                       aarp_find( aid, spnet, ea->aarp_spnode )) != NULL ) {
+                   aarp_free( aid, aal );
+               }
+               break;
+
+           case AARPOP_RESPONSE :
+               cmn_err( CE_NOTE, "aarp_rput someone using our address\n" );
+               break;
+
+           default :
+               cmn_err( CE_NOTE, "aarp_rput bad op %X\n", op );
+               break;
+           }
+       }
+    }
+
+done :
+    freemsg( m );
+    return( 0 );
+}
+
+    void
+aarp_send( struct atif_data *aid, int op, caddr_t hwaddr,
+       ushort net, unchar node )
+{
+    mblk_t             *m;
+    struct ether_aarp  *ea;
+
+    if (( m = allocb( sizeof( struct ether_aarp ), BPRI_HI )) == NULL ) {
+       return;
+    }
+    m->b_wptr = m->b_rptr + sizeof( struct ether_aarp );
+    ea = (struct ether_aarp *)m->b_rptr;
+    bzero( (caddr_t)ea, sizeof( struct ether_aarp ));
+
+    ea->aarp_hrd = htons( AARPHRD_ETHER );
+    ea->aarp_pro = htons( ETHERTYPE_AT );
+    ea->aarp_hln = sizeof( ea->aarp_sha );
+    ea->aarp_pln = sizeof( ea->aarp_spu );
+    ea->aarp_op = htons( op );
+    bcopy( aid->aid_hwaddr, ea->aarp_sha, sizeof( ea->aarp_sha ));
+
+    if ( hwaddr == NULL ) {
+       bzero( ea->aarp_tha, sizeof( ea->aarp_tha ));
+    } else {
+       bcopy( hwaddr, ea->aarp_tha, sizeof( ea->aarp_tha ));
+    }
+
+    ea->aarp_tpnode = node;
+    bcopy( &aid->aid_sat.sat_addr.s_net, ea->aarp_spnet,
+           sizeof( ea->aarp_spnet ));
+    bcopy( &net, ea->aarp_tpnet, sizeof( ea->aarp_tpnet ));
+    ea->aarp_spnode = aid->aid_sat.sat_addr.s_node;
+    ea->aarp_tpnode = node;
+
+    if ( hwaddr == NULL ) {
+       dl_unitdata_req( WR( aid->aid_q ), m, ETHERTYPE_AARP,
+               at_multicastaddr );
+    } else {
+       dl_unitdata_req( WR( aid->aid_q ), m, ETHERTYPE_AARP, hwaddr );
+    }
+    return;
+}
+
+    int
+aarp_resolve( struct atif_data *aid, mblk_t *m, struct sockaddr_at *sat )
+{
+    struct aarplist    *aal;
+
+    if ( sat->sat_addr.s_node == ATADDR_BCAST ) {
+       dl_unitdata_req( WR( aid->aid_q ), m, ETHERTYPE_AT, at_multicastaddr );
+       return( 0 );
+    }
+
+    if (( aal = aarp_alloc( aid, sat->sat_addr.s_net, sat->sat_addr.s_node )) ==
+           NULL ) {
+       freemsg( m );
+       return( ENOMEM );
+    }
+    aal->aal_age = 0;
+
+    if ( aal->aal_flags ) {    /* complete */
+       dl_unitdata_req( WR( aid->aid_q ), m, ETHERTYPE_AT, aal->aal_hwaddr );
+    } else {
+       /* send aarp request */
+       if ( aal->aal_m != NULL ) {
+           freemsg( aal->aal_m );
+       }
+       /* either freed above, in timeout, or sent in aarp_rput() */
+       aal->aal_m = m; 
+       aarp_send( aid, AARPOP_REQUEST, NULL,
+               sat->sat_addr.s_net, sat->sat_addr.s_node );
+    }
+    return( 0 );
+}
diff --git a/sys/solaris/ddp.c b/sys/solaris/ddp.c
new file mode 100644 (file)
index 0000000..ea3285e
--- /dev/null
@@ -0,0 +1,64 @@
+#include <sys/types.h>
+#include <sys/stream.h>
+#include <sys/cmn_err.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/tihdr.h>
+#include <sys/ddi.h>
+#include <sys/ethernet.h>
+#include <net/if.h>
+
+#include <string.h>
+
+#include <netatalk/endian.h>
+#include <netatalk/ddp.h>
+#include <netatalk/at.h>
+
+#include "if.h"
+#include "sock.h"
+
+    int
+ddp_rput( struct atif_data *aid, mblk_t *m )
+{
+    struct atif_data   *daid;
+    struct ddpehdr     *deh;
+    struct sockaddr_at sat;
+    struct sock_data   *sd;
+
+    if ( m->b_wptr - m->b_rptr < sizeof( struct ddpehdr )) {
+       cmn_err( CE_NOTE, "ddp_rput short packet\n" );
+       freemsg( m );
+       return( EINVAL );
+    }
+
+    deh = (struct ddpehdr *)m->b_rptr;
+
+    sat.sat_addr.s_net = deh->deh_dnet;
+    sat.sat_addr.s_node = deh->deh_dnode;
+    sat.sat_port = deh->deh_dport;
+
+    if (( daid = if_dest( aid, &sat )) != NULL ) {
+       if (( sd = sock_dest( daid, &sat )) != NULL ) {
+           if ( sd->sd_state != TS_IDLE ) {
+               freemsg( m );
+               return( EHOSTDOWN );
+           }
+           bzero( (caddr_t)&sat, sizeof( struct sockaddr_at ));
+           sat.sat_family = AF_APPLETALK;
+           sat.sat_addr.s_net = deh->deh_snet;
+           sat.sat_addr.s_node = deh->deh_snode;
+           sat.sat_port = deh->deh_sport;
+           adjmsg( m, sizeof( struct ddpehdr ));
+           t_unitdata_ind( WR( sd->sd_q ), m, &sat );
+       } else {
+           /* toss it */
+           freemsg( m );
+           return( EHOSTDOWN );
+       }
+    } else {
+       /* route it */
+       freemsg( m );
+       return( ENETUNREACH );
+    }
+    return( 0 );
+}
diff --git a/sys/solaris/ddp.conf b/sys/solaris/ddp.conf
new file mode 100644 (file)
index 0000000..e0257ad
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Netatalk driver's configuration file
+#
+
+name="ddp" parent="pseudo" instance=0;
diff --git a/sys/solaris/dlpi.c b/sys/solaris/dlpi.c
new file mode 100644 (file)
index 0000000..e70b465
--- /dev/null
@@ -0,0 +1,473 @@
+#include <sys/types.h>
+#include <sys/kmem.h>
+#include <sys/stream.h>
+#include <sys/conf.h>
+#include <sys/modctl.h>
+#include <sys/cmn_err.h>
+#include <sys/ddi.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/dlpi.h>
+#include <sys/ethernet.h>
+#include <sys/byteorder.h>
+#include <sys/sunddi.h>
+#include <net/if.h>
+#include <errno.h>
+
+#include <netatalk/phase2.h>
+#include <netatalk/at.h>
+
+#include "ioc.h"
+#include "if.h"
+
+u_char at_multicastaddr[ ETHERADDRL ] = {
+    0x09, 0x00, 0x07, 0xff, 0xff, 0xff,
+};
+u_char at_org_code[ 3 ] = {
+    0x08, 0x00, 0x07,
+};
+u_char aarp_org_code[ 3 ] = {
+    0x00, 0x00, 0x00,
+};
+
+    static int
+dlpi_open( queue_t *q, dev_t *dev, int oflag, int sflag, cred_t *cred )
+{
+    struct atif_data   *aid;
+    int                        err = 0;
+
+    if (( err = drv_priv( cred )) != 0 ) {
+       return( err );
+    }
+    if (( aid = if_alloc( q )) == NULL ) {
+       return( ENOMEM );
+    }
+    q->q_ptr = (void *)aid;
+
+    qprocson( q );
+    return( err );
+}
+
+    static int
+dlpi_close( queue_t *q, int oflag, cred_t *cred )
+{
+    struct atif_data   *aid = (struct atif_data *)q->q_ptr;
+
+    qprocsoff( q );
+    if_free( aid );
+    return( 0 );
+}
+
+    static int
+dl_bind_req( queue_t *q, ulong sap )
+{
+    union DL_primitives        *dl;
+    mblk_t             *m;
+
+    if (( m = allocb( DL_BIND_REQ_SIZE, BPRI_HI )) == NULL ) {
+       return( ENOMEM );
+    }
+    m->b_wptr = m->b_rptr + DL_BIND_REQ_SIZE;
+    m->b_datap->db_type = M_PROTO;
+
+    dl = (union DL_primitives *)m->b_rptr;
+    dl->dl_primitive = DL_BIND_REQ;
+    dl->bind_req.dl_sap = sap;
+    dl->bind_req.dl_max_conind = 0;
+    dl->bind_req.dl_service_mode = DL_CLDLS;
+    dl->bind_req.dl_conn_mgmt = 0;
+    dl->bind_req.dl_xidtest_flg = 0;   /* XXX */
+    putnext( q, m );
+    return( 0 );
+}
+
+    static int
+dl_attach_req( queue_t *q, ulong ppa )
+{
+    union DL_primitives        *dl;
+    mblk_t             *m;
+
+    if (( m = allocb( DL_ATTACH_REQ_SIZE, BPRI_HI )) == NULL ) {
+       return( ENOMEM );
+    }
+    m->b_wptr = m->b_rptr + DL_ATTACH_REQ_SIZE;
+    m->b_datap->db_type = M_PROTO;
+
+    dl = (union DL_primitives *)m->b_rptr;
+    dl->dl_primitive = DL_ATTACH_REQ;
+    dl->attach_req.dl_ppa = ppa;
+    putnext( q, m );
+    return( 0 );
+}
+
+    int
+dl_enabmulti_req( queue_t *q, caddr_t addr )
+{
+    union DL_primitives        *dl;
+    mblk_t             *m;
+
+    if (( m = allocb( DL_ENABMULTI_REQ_SIZE + ETHERADDRL, BPRI_HI )) == NULL ) {
+       return( ENOMEM );
+    }
+    m->b_wptr = m->b_rptr + DL_ENABMULTI_REQ_SIZE;
+    m->b_datap->db_type = M_PROTO;
+
+    dl = (union DL_primitives *)m->b_rptr;
+    dl->dl_primitive = DL_ENABMULTI_REQ;
+    dl->enabmulti_req.dl_addr_length = ETHERADDRL;
+    dl->enabmulti_req.dl_addr_offset = m->b_wptr - m->b_rptr;
+    bcopy( addr, m->b_wptr, ETHERADDRL );
+    m->b_wptr += ETHERADDRL;
+    putnext( q, m );
+    return( 0 );
+}
+
+    int
+dl_unitdata_req( queue_t *q, mblk_t *m0, ushort type, caddr_t addr )
+{
+    union DL_primitives        *dl;
+    struct llc         *llc;
+    mblk_t             *m1, *m;
+    ushort              len;
+
+    /* len = msgdsize( m0 ) + sizeof( struct llc ); */
+
+    if (( m1 = allocb( sizeof( struct llc ), BPRI_HI )) == NULL ) {
+       cmn_err( CE_NOTE, "dl_unitdate_req NOMEM 1\n" );
+       return( ENOMEM );
+    }
+    m1->b_wptr = m1->b_rptr + sizeof( struct llc );
+    m1->b_datap->db_type = M_DATA;
+    llc = (struct llc *)m1->b_rptr;
+
+    llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP;
+    llc->llc_control = LLC_UI;
+    if ( type == ETHERTYPE_AARP ) {
+       bcopy( aarp_org_code, llc->llc_org_code, sizeof( aarp_org_code ));
+    } else if ( type == ETHERTYPE_AT ) {
+       bcopy( at_org_code, llc->llc_org_code, sizeof( aarp_org_code ));
+    } else {
+       cmn_err( CE_NOTE, "dl_unitdate_req type %X\n", type );
+       return( EINVAL );
+    }
+    llc->llc_ether_type = htons( type );
+    linkb( m1, m0 );
+
+    if (( m = allocb( DL_UNITDATA_REQ_SIZE + ETHERADDRL + sizeof( ushort ),
+                     BPRI_HI )) == NULL ) {
+       cmn_err( CE_NOTE, "dl_unitdate_req NOMEM 2\n" );
+       return( ENOMEM );
+    }
+    m->b_wptr = m->b_rptr + DL_UNITDATA_REQ_SIZE;
+    m->b_datap->db_type = M_PROTO;
+    linkb( m, m1 );
+
+    dl = (union DL_primitives *)m->b_rptr;
+    dl->dl_primitive = DL_UNITDATA_REQ;
+    dl->unitdata_req.dl_dest_addr_length = ETHERADDRL + sizeof ( ushort );
+    dl->unitdata_req.dl_dest_addr_offset = m->b_wptr - m->b_rptr;
+
+    bcopy(addr, m->b_wptr, ETHERADDRL );
+    m->b_wptr += ETHERADDRL;
+    len = 0;
+    bcopy( &len, m->b_wptr, sizeof( ushort ));
+    m->b_wptr += sizeof( ushort );
+    putnext( q, m );
+    return( 0 );
+}
+
+    static int
+dlpi_rput( queue_t *q, mblk_t *m )
+{
+    struct atif_data   *aid = (struct atif_data *)q->q_ptr;
+    union DL_primitives        *dl;
+    mblk_t             *m0;
+    struct llc         *llc;
+
+    switch ( m->b_datap->db_type ) {
+    case M_IOCNAK :
+       putnext( q, m );
+       return( 0 );
+
+    case M_PCPROTO :
+    case M_PROTO :
+       if ( m->b_wptr - m->b_rptr < sizeof( dl->dl_primitive )) {
+           break;
+       }
+       dl = (union DL_primitives *)m->b_rptr;
+       switch ( dl->dl_primitive ) {
+       case DL_UNITDATA_IND :
+           if ( m->b_wptr - m->b_rptr < sizeof( DL_UNITDATA_IND_SIZE )) {
+               break;
+           }
+           if (( m0 = unlinkb( m )) == NULL ) {
+               break;
+           }
+           if ( m0->b_wptr - m0->b_rptr < sizeof( struct llc )) {
+               freemsg( m0 );
+               break;
+           }
+           llc = (struct llc *)m0->b_rptr;
+           if ( llc->llc_dsap != LLC_SNAP_LSAP ||
+                   llc->llc_ssap != LLC_SNAP_LSAP ||
+                   llc->llc_control != LLC_UI ) {
+               freemsg( m0 );
+               break;
+           }
+
+           if ( bcmp( llc->llc_org_code, at_org_code,
+                   sizeof( at_org_code )) == 0 &&
+                   ntohs( llc->llc_ether_type ) == ETHERTYPE_AT ) {
+               adjmsg( m0, sizeof( struct llc ));
+               ddp_rput( aid, m0 );
+           } else if ( bcmp( llc->llc_org_code, aarp_org_code,
+                   sizeof( aarp_org_code )) == 0 &&
+                   ntohs( llc->llc_ether_type ) == ETHERTYPE_AARP ) {
+               adjmsg( m0, sizeof( struct llc ));
+               aarp_rput( q, m0 );
+           } else {
+               freemsg( m0 );
+           }
+           break;
+
+       case DL_OK_ACK :
+           if ( m->b_wptr - m->b_rptr < sizeof( DL_OK_ACK_SIZE )) {
+               break;
+           }
+           switch ( dl->ok_ack.dl_correct_primitive ) {
+           case DL_ATTACH_REQ :
+               if ( aid->aid_state != DL_ATTACH_PENDING ) {
+                   cmn_err( CE_NOTE, "dlpi_rput DL_OK_ACK attach state %d\n",
+                           aid->aid_state );
+                   break;
+               }
+               if ( aid->aid_c.c_type != IF_UNITSEL ) {
+                   cmn_err( CE_NOTE, "dlpi_rput DL_OK_ACK attach context %x\n",
+                           aid->aid_c.c_type );
+                   break;
+               }
+
+               if ( WR(q)->q_next == NULL || WR(q)->q_next->q_qinfo == NULL ||
+                       WR(q)->q_next->q_qinfo->qi_minfo == NULL ||
+                       WR(q)->q_next->q_qinfo->qi_minfo->mi_idname == NULL ) {
+                   cmn_err( CE_NOTE, "dlpi_rput can't get interface name\n" );
+                   break;
+               }
+
+               if_name( aid, WR(q)->q_next->q_qinfo->qi_minfo->mi_idname,
+                       aid->aid_c.c_u.u_unit.uu_ppa );
+
+               aid->aid_state = DL_BIND_PENDING;
+
+#ifdef i386
+               /*
+                * As of Solaris 7 (nice name), the i386 arch needs to be
+                * bound as 0 to receive 802 frames.  However, in the same
+                * OS, Sparcs must be bound as ETHERMTU (or at least not 0)
+                * to receive the same frames.  A bug?  In the Solaris 7
+                * (nice name) kernel?
+                */
+               dl_bind_req( WR( q ), 0 );
+#else /* i386 */
+               dl_bind_req( WR( q ), ETHERMTU );
+#endif /* i386 */
+               break;
+
+           case DL_ENABMULTI_REQ :
+               if ( aid->aid_c.c_type != SIOCADDMULTI ) {
+                   cmn_err( CE_NOTE,
+                           "dlpi_rput DL_OK_ACK enabmulti context %x\n",
+                           aid->aid_c.c_type );
+                   break;
+               }
+
+               ioc_ok_ack( aid->aid_c.c_u.u_multi.um_q,
+                       aid->aid_c.c_u.u_multi.um_m, 0 );
+               aid->aid_c.c_type = 0;
+               aid->aid_c.c_u.u_multi.um_q = NULL;
+               aid->aid_c.c_u.u_multi.um_m = 0;
+               break;
+
+           default :
+               cmn_err( CE_CONT, "!dlpi_rput DL_OK_ACK unhandled %d\n",
+                       dl->ok_ack.dl_correct_primitive );
+               break;
+           }
+           break;
+
+       case DL_BIND_ACK :
+           if ( m->b_wptr - m->b_rptr < sizeof( DL_BIND_ACK_SIZE )) {
+               break;
+           }
+           if ( aid->aid_state != DL_BIND_PENDING ) {
+               break;
+           }
+           if ( aid->aid_c.c_type != IF_UNITSEL ) {
+               break;
+           }
+           bcopy( m->b_rptr + dl->bind_ack.dl_addr_offset, aid->aid_hwaddr, 
+                   dl->bind_ack.dl_addr_length );
+           aid->aid_state = DL_IDLE;
+           ioc_ok_ack( WR(q), aid->aid_c.c_u.u_unit.uu_m, 0 );
+           aid->aid_c.c_type = 0;
+           aid->aid_c.c_u.u_unit.uu_m = NULL;
+           aid->aid_c.c_u.u_unit.uu_ppa = 0;
+           break;
+
+       case DL_ERROR_ACK :
+           if ( m->b_wptr - m->b_rptr < sizeof( DL_ERROR_ACK_SIZE )) {
+               break;
+           }
+
+           switch ( aid->aid_c.c_type ) {
+           case IF_UNITSEL :
+               if ( dl->error_ack.dl_errno == DL_SYSERR ) {
+                   ioc_error_ack( WR(q), aid->aid_c.c_u.u_unit.uu_m,
+                           dl->error_ack.dl_unix_errno );
+               } else {
+                   cmn_err( CE_CONT, "dlpi_rput DL_ERROR_ACK 0x%x\n",
+                           dl->error_ack.dl_errno );
+                   ioc_error_ack( WR(q), aid->aid_c.c_u.u_unit.uu_m, EINVAL );
+               }
+               aid->aid_c.c_type = 0;
+               aid->aid_c.c_u.u_unit.uu_m = NULL;
+               aid->aid_c.c_u.u_unit.uu_ppa = 0;
+               break;
+
+           default :
+               cmn_err( CE_NOTE, "dlpi_rput DL_ERROR_ACK unhandled %d %d %d\n",
+                       dl->error_ack.dl_error_primitive,
+                       dl->error_ack.dl_errno, dl->error_ack.dl_unix_errno );
+               break;
+           }
+           break;
+
+       default :
+           cmn_err( CE_NOTE, "dlpi_rput M_PCPROTO 0x%x\n", dl->dl_primitive );
+           break;
+       }
+       break;
+
+    default :
+       cmn_err( CE_NOTE, "dlpi_rput 0x%X\n", m->b_datap->db_type );
+       break;
+    }
+
+    freemsg( m );
+    return( 0 );
+}
+
+    static int
+dlpi_wput( queue_t *q, mblk_t *m )
+{
+    struct atif_data           *aid = (struct atif_data *)RD(q)->q_ptr;
+    struct iocblk              *ioc;
+    int                                rc;
+
+    switch ( m->b_datap->db_type ) {
+    case M_IOCTL :
+       if ( m->b_wptr - m->b_rptr < sizeof( struct iocblk )) {
+           freemsg( m );
+           break;
+       }
+       ioc = (struct iocblk *)m->b_rptr;
+       switch ( ioc->ioc_cmd ) {
+       case IF_UNITSEL :
+           if ( ioc->ioc_count != TRANSPARENT ) {
+               cmn_err( CE_NOTE, "dlpi_wput IF_UNITSEL non-TRANSPARENT\n" );
+               ioc_error_ack( q, m, EINVAL );
+               break;
+           }
+           if ( m->b_cont == NULL ) {
+               cmn_err( CE_NOTE, "dlpi_wput IF_UNITSEL no arg\n" );
+               ioc_error_ack( q, m, EINVAL );
+               break;
+           }
+           if ( aid->aid_state != DL_UNATTACHED ) {
+               cmn_err( CE_NOTE, "dlpi_wput IF_UNITSEL already attached\n" );
+               ioc_error_ack( q, m, EINVAL );
+               break;
+           }
+           if ( aid->aid_c.c_type != 0 ) {
+               cmn_err( CE_NOTE, "dlpi_wput IF_UNITSEL context %x\n",
+                       aid->aid_c.c_type );
+               ioc_error_ack( q, m, EINVAL );
+               break;
+           }
+
+           aid->aid_state = DL_ATTACH_PENDING;
+           aid->aid_c.c_type = IF_UNITSEL;
+           aid->aid_c.c_u.u_unit.uu_m = m;
+           aid->aid_c.c_u.u_unit.uu_ppa = *(ulong *)m->b_cont->b_rptr;
+           if (( rc = dl_attach_req( q, aid->aid_c.c_u.u_unit.uu_ppa )) < 0 ) {
+               ioc_error_ack( q, m, rc );
+               break;
+           }
+           break;
+
+       default :
+           cmn_err( CE_NOTE, "dlpi_wput M_IOCTL 0x%X\n", ioc->ioc_cmd );
+           putnext( q, m );
+           break;
+       }
+       break;
+
+    default :
+       cmn_err( CE_NOTE, "dlpi_wput 0x%X\n", m->b_datap->db_type );
+       freemsg( m );
+       break;
+    }
+
+    return( 0 );
+}
+
+static struct module_info      dlpi_info = {
+    0,
+    "ddp",
+    0,
+    1500,
+    3000,
+    64
+};
+
+static struct qinit            dlpi_rinit = {
+    dlpi_rput,         /* qi_putp */
+    NULL,              /* qi_srvp */
+    dlpi_open,         /* qi_qopen */
+    dlpi_close,                /* qi_qclose */
+    NULL,
+    &dlpi_info,                /* qi_minfo */
+    NULL,
+};
+
+static struct qinit            dlpi_winit = {
+    dlpi_wput,         /* qi_putp */
+    NULL,              /* qi_srvp */
+    NULL,              /* qi_qopen */
+    NULL,              /* qi_qclose */
+    NULL,
+    &dlpi_info,                /* qi_minfo */
+    NULL,
+};
+
+static struct streamtab                dlpi_stream = {
+    &dlpi_rinit,
+    &dlpi_winit,
+    NULL,
+    NULL
+};
+
+static struct fmodsw           dlpi_fmodsw = {
+    "ddp",
+    &dlpi_stream,
+    D_NEW | D_MP | D_MTPERMOD
+};
+
+/*
+ * DDP Streams module.  This module is pushed on DLPI drivers by atalkd.
+ */
+struct modlstrmod              dlpi_lstrmod = {
+    &mod_strmodops,
+    "DDP Streams module",
+    &dlpi_fmodsw,
+};
diff --git a/sys/solaris/if.c b/sys/solaris/if.c
new file mode 100644 (file)
index 0000000..810e748
--- /dev/null
@@ -0,0 +1,368 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/stream.h>
+#include <sys/kmem.h>
+#include <sys/dlpi.h>
+#include <sys/cmn_err.h>
+#include <sys/errno.h>
+#include <sys/byteorder.h>
+#include <sys/ethernet.h>
+#include <sys/ddi.h>
+#include <net/if.h>
+#include <netinet/arp.h>
+#include <string.h>
+
+#include <netatalk/at.h>
+#include <netatalk/aarp.h>
+
+#include "if.h"
+#include "rt.h"
+#include "ioc.h"
+
+static struct atif_data        *interfaces = NULL;
+
+    struct atif_data *
+if_primary()
+{
+    return( interfaces );
+}
+
+    struct atif_data *
+if_alloc( queue_t *q )
+{
+    struct atif_data   *aid;
+
+    if (( aid = kmem_zalloc( sizeof( struct atif_data ), KM_SLEEP )) == NULL ) {
+       return( NULL );
+    }
+    aid->aid_q = q;
+    aid->aid_state = DL_UNATTACHED;
+
+    return( aid );
+}
+
+/*
+ * Name an interface, insert it in our list of interfaces.  If this is the
+ * first interface, create the loopback interface.  If it's not the first
+ * interfaces, keep the first interface the same, i.e. the first configured
+ * interface should be the primary interface.
+ */
+    int
+if_name( struct atif_data *aid, char *name, ulong ppa )
+{
+    sprintf( aid->aid_name, "%s%ld", name, ppa );
+
+    if ( interfaces == NULL ) {                /* create fake loopback */
+       if (( interfaces = if_alloc( NULL )) == NULL ) {
+           return( ENOMEM );
+       }
+       strcpy( interfaces->aid_name, "lo0" );
+       interfaces->aid_state = DL_IDLE;
+       bzero( interfaces->aid_hwaddr, sizeof( interfaces->aid_hwaddr ));
+       interfaces->aid_flags = AIDF_LOOPBACK;
+       interfaces->aid_c.c_type = 0;
+
+       aid->aid_next = interfaces;
+       aid->aid_next->aid_prev = aid;
+       interfaces = aid;
+    } else {
+       aid->aid_next = interfaces->aid_next;
+       aid->aid_prev = interfaces;
+       aid->aid_next->aid_prev = aid;
+       interfaces->aid_next = aid;
+    }
+
+    aarp_init( aid );
+    return( 0 );
+}
+
+    void
+if_free( struct atif_data *aid )
+{
+    if ( aid->aid_c.c_type != 0 ) {
+       cmn_err( CE_NOTE, "if_free context %x\n", aid->aid_c.c_type );
+       return;
+    }
+
+    aarp_clean( aid );
+
+    if ( aid->aid_next != NULL ) {
+       aid->aid_next->aid_prev = aid->aid_prev;
+    }
+    if ( aid->aid_prev != NULL ) {
+       aid->aid_prev->aid_next = aid->aid_next;
+    }
+    if ( aid == interfaces ) {
+       interfaces = aid->aid_next;
+    }
+    kmem_free( aid, sizeof( struct atif_data ));
+
+    if ( interfaces != NULL && interfaces->aid_next == NULL ) {
+       kmem_free( interfaces, sizeof( struct atif_data ));
+       interfaces = NULL;
+    }
+    return;
+}
+
+    int
+if_getaddr( char *name, struct sockaddr_at *sat )
+{
+    struct atif_data   *aid;
+
+    for ( aid = interfaces; aid != NULL; aid = aid->aid_next ) {
+       if ( strcmp( name, aid->aid_name ) == 0 ) {
+           break;
+       }
+    }
+    if ( aid == NULL ) {
+       return( EADDRNOTAVAIL );
+    }
+
+    bcopy( &aid->aid_sat, sat, sizeof( struct sockaddr_at ));
+    return( 0 );
+}
+
+    int
+if_addmulti( queue_t *q, mblk_t *m, char *name, struct sockaddr *sa )
+{
+    struct atif_data   *aid;
+
+    for ( aid = interfaces; aid != NULL; aid = aid->aid_next ) {
+       if ( strcmp( name, aid->aid_name ) == 0 ) {
+           break;
+       }
+    }
+    if ( aid == NULL ) {
+       return( EADDRNOTAVAIL );
+    }
+
+    if ( aid->aid_c.c_type != 0 ) {
+       cmn_err( CE_NOTE, "if_addmulti context %x\n", aid->aid_c.c_type );
+       return( EINVAL );
+    }
+
+    aid->aid_c.c_type = SIOCADDMULTI;
+    aid->aid_c.c_u.u_multi.um_q = q;
+    aid->aid_c.c_u.u_multi.um_m = m;
+    dl_enabmulti_req( WR( aid->aid_q ), sa->sa_data );
+
+    return( 0 );
+}
+
+    void
+if_pickaddr( struct atif_data *aid )
+{
+    if ( aid->aid_c.c_type != SIOCSIFADDR ) {
+       cmn_err( CE_NOTE, "if_pickaddr context %x\n", aid->aid_c.c_type );
+       return;
+    }
+
+    if ( aid->aid_flags & AIDF_PROBEFAILED ) {
+       aid->aid_flags &= ~AIDF_PROBEFAILED;
+       /* choose new address */
+       for (;;) {
+           if ( aid->aid_c.c_u.u_addr.ua_nodecnt == 0 ) {
+               /* if we've exausted all addresses, fail */
+               if ( aid->aid_c.c_u.u_addr.ua_netcnt == 0 ) {
+                   ioc_error_ack( aid->aid_c.c_u.u_addr.ua_q,
+                           aid->aid_c.c_u.u_addr.ua_m, EADDRINUSE );
+                   aid->aid_c.c_type = 0;
+                   aid->aid_c.c_u.u_addr.ua_q = NULL;
+                   aid->aid_c.c_u.u_addr.ua_m = NULL;
+                   aid->aid_c.c_u.u_addr.ua_probecnt = 0;
+                   aid->aid_c.c_u.u_addr.ua_netcnt = 0;
+                   aid->aid_c.c_u.u_addr.ua_nodecnt = 0;
+               } else {
+                   aid->aid_c.c_u.u_addr.ua_nodecnt = 256;
+                   aid->aid_c.c_u.u_addr.ua_netcnt--;
+                   if ( ntohs(aid->aid_sat.sat_addr.s_net) >
+                           ntohs(aid->aid_nr.nr_lastnet) ) {
+                       aid->aid_sat.sat_addr.s_net = aid->aid_nr.nr_firstnet;
+                   } else
+                     aid->aid_sat.sat_addr.s_net = 
+                       htons(ntohs(aid->aid_sat.sat_addr.s_net) + 1);
+               }
+           } else {
+               aid->aid_sat.sat_addr.s_node++;
+               aid->aid_c.c_u.u_addr.ua_nodecnt--;
+               if ( aid->aid_sat.sat_addr.s_node == 0 || 
+                       aid->aid_sat.sat_addr.s_node == 255 || 
+                       aid->aid_sat.sat_addr.s_node == 254 ) {
+                   continue;
+               }
+               break;
+           }
+       }
+    }
+    if ( aid->aid_c.c_u.u_addr.ua_probecnt-- <= 0 ) {
+       aid->aid_flags &= ~AIDF_PROBING;
+       /* worked, send ioctl reponse */
+       ioc_ok_ack( aid->aid_c.c_u.u_addr.ua_q, aid->aid_c.c_u.u_addr.ua_m, 0 );
+       aid->aid_c.c_type = 0;
+       aid->aid_c.c_u.u_addr.ua_q = NULL;
+       aid->aid_c.c_u.u_addr.ua_m = NULL;
+       aid->aid_c.c_u.u_addr.ua_probecnt = 0;
+       aid->aid_c.c_u.u_addr.ua_netcnt = 0;
+       aid->aid_c.c_u.u_addr.ua_nodecnt = 0;
+       return;
+    }
+
+    aarp_send( aid, AARPOP_PROBE, NULL,
+           aid->aid_sat.sat_addr.s_net, aid->aid_sat.sat_addr.s_node );
+    qtimeout( aid->aid_q, if_pickaddr, (caddr_t)aid, hz / 5 );
+}
+
+    int
+if_setaddr( queue_t *q, mblk_t *m, char *name, struct sockaddr_at *sat )
+{
+    struct atif_data   *aid;
+    struct netrange    nr;
+    ulong              time;
+
+    for ( aid = interfaces; aid != NULL; aid = aid->aid_next ) {
+       if ( strcmp( name, aid->aid_name ) == 0 ) {
+           break;
+       }
+    }
+    if ( aid == NULL ) {
+       return( EADDRNOTAVAIL );
+    }
+
+    if ( aid->aid_c.c_type != 0 ) {
+       cmn_err( CE_NOTE, "if_setaddr context %x\n", aid->aid_c.c_type );
+       return( EINVAL );
+    }
+
+    bcopy( sat->sat_zero, &nr, sizeof( struct netrange ));
+
+    if ( aid->aid_flags & AIDF_LOOPBACK ) {
+       aid->aid_sat = *sat;
+       aid->aid_nr = nr;
+
+       /* routes? */
+
+       ioc_ok_ack( q, m, 0 );
+       return( 0 );
+    }
+
+    drv_getparm( TIME, &time );
+    if ( sat->sat_addr.s_net == ATADDR_ANYNET ) {
+       if ( nr.nr_lastnet == nr.nr_firstnet ) {
+           sat->sat_addr.s_net = nr.nr_firstnet;
+       } else {
+           sat->sat_addr.s_net = htons(ntohs(nr.nr_firstnet) + time %
+                   (ntohs(nr.nr_lastnet) - ntohs(nr.nr_firstnet)));
+       }
+    } else {
+       if ( ntohs( sat->sat_addr.s_net ) < ntohs( nr.nr_firstnet ) ||
+               ntohs( sat->sat_addr.s_net ) > ntohs( nr.nr_lastnet )) {
+           return( EINVAL );
+       }
+    }
+
+    if ( sat->sat_addr.s_node == ATADDR_ANYNODE ) {
+       sat->sat_addr.s_node = time;
+    }
+
+    aid->aid_flags |= AIDF_PROBING;
+    aid->aid_sat = *sat;
+    aid->aid_nr = nr;
+
+    aid->aid_c.c_type = SIOCSIFADDR;
+    aid->aid_c.c_u.u_addr.ua_q = q;
+    aid->aid_c.c_u.u_addr.ua_m = m;
+    aid->aid_c.c_u.u_addr.ua_probecnt = 10;
+    aid->aid_c.c_u.u_addr.ua_netcnt = ntohs(nr.nr_lastnet) - 
+      ntohs(nr.nr_firstnet);
+    aid->aid_c.c_u.u_addr.ua_nodecnt = 256;
+    qtimeout( aid->aid_q, if_pickaddr, (caddr_t)aid, 0 );
+    return( 0 );
+}
+
+/*
+ * These three routines are all a big mess...
+ */
+    struct atif_data *
+if_dest( struct atif_data *aid, struct sockaddr_at *sat )
+{
+    struct atif_data   *dest;
+
+    for ( dest = interfaces; dest != NULL; dest = dest->aid_next ) {
+       if ((( sat->sat_addr.s_net == 0 && aid == dest ) ||
+               ( ntohs(sat->sat_addr.s_net) >= 
+                 ntohs(dest->aid_nr.nr_firstnet) &&
+               ntohs(sat->sat_addr.s_net) <= 
+                 ntohs(dest->aid_nr.nr_lastnet) )) &&
+               ( sat->sat_addr.s_node == dest->aid_sat.sat_addr.s_node ||
+               sat->sat_addr.s_node == ATADDR_BCAST )) {
+           break;
+       }
+    }
+    if ( dest == NULL ) {
+       return( NULL );
+    }
+    return( dest );
+}
+
+    struct atif_data *
+if_withaddr( struct sockaddr_at *sat )
+{
+    struct atif_data   *dest;
+
+    for ( dest = interfaces; dest != NULL; dest = dest->aid_next ) {
+       if ( sat->sat_addr.s_net == dest->aid_sat.sat_addr.s_net &&
+               sat->sat_addr.s_node == dest->aid_sat.sat_addr.s_node ) {
+           break;
+       }
+    }
+    return( dest );
+}
+
+    struct atif_data *
+if_withnet( struct sockaddr_at *sat )
+{
+    struct atif_data   *dest;
+
+    for ( dest = interfaces; dest != NULL; dest = dest->aid_next ) {
+       if ( ntohs(sat->sat_addr.s_net) >= ntohs(dest->aid_nr.nr_firstnet) &&
+               ntohs(sat->sat_addr.s_net) <= ntohs(dest->aid_nr.nr_lastnet)) {
+           break;
+       }
+    }
+    return( dest );
+}
+
+    int
+if_route( struct atif_data *aid, mblk_t *m, struct sockaddr_at *sat )
+{
+    struct sockaddr_at gate;
+
+    if ( sat->sat_addr.s_net == 0 ) {
+       if ( sat->sat_addr.s_node == 0 ) {
+           aid = if_withaddr( sat );
+       }
+       if ( aid == NULL ) {
+           freemsg( m );
+           return( ENETUNREACH );
+       }
+       gate = *sat;
+    } else {
+       if ( rt_gate( sat, &gate ) < 0 ) {      /* no route */
+           gate = *sat;
+       }
+       if (( aid = if_withaddr( &gate )) == NULL ) {
+           if (( aid = if_withnet( &gate )) == NULL ) {
+               freemsg( m );
+               return( ENETUNREACH );
+           }
+       }
+    }
+
+    if ( aid->aid_flags & AIDF_LOOPBACK ) {
+       return( ddp_rput( aid, m ));
+    } else {
+       /* the aarp layer is expected to send broadcast packets appropriately */
+       return( aarp_resolve( aid, m, &gate ));
+    }
+}
diff --git a/sys/solaris/if.h b/sys/solaris/if.h
new file mode 100644 (file)
index 0000000..f1ed708
--- /dev/null
@@ -0,0 +1,77 @@
+struct atif_data {
+    struct atif_data   *aid_next, *aid_prev;
+    char               aid_name[ IFNAMSIZ ];
+    unchar             aid_hwaddr[ ETHERADDRL ];
+    queue_t            *aid_q;                         /* RD() side */
+    int                        aid_state;
+    int                        aid_flags;
+    struct sockaddr_at aid_sat;
+    struct netrange    aid_nr;
+    struct aarplist    *aid_aarplist, *aid_aarpflist;
+    /* solaris 7 wants timeout_id_t, but solaris 2.6 doesn't have that.
+     * so, we compromise with an unsigned long as we know that's big
+     * enough to hold a pointer. */
+    unsigned long      aid_aarptimeo;
+    /*
+     * A little bit of cleverness, to overcome the inability of
+     * streams to sleep.  The type of context must be checked before
+     * the data is accessed.  The atif_data can't be freed if the
+     * type is non-zero.
+     */
+    struct {
+       int             c_type;                 /* ioctl command */
+       union {
+           struct {                            /* unit select */
+               mblk_t          *uu_m;
+               ulong           uu_ppa;
+           }           u_unit;
+           struct {                            /* set addr */
+               mblk_t          *ua_m;
+               queue_t         *ua_q;
+               int             ua_probecnt;
+               int             ua_netcnt;
+               int             ua_nodecnt;
+           }           u_addr;
+           struct {                            /* add multi */
+               mblk_t          *um_m;
+               queue_t         *um_q;
+           }           u_multi;
+       }               c_u;
+    }                  aid_c;
+};
+
+#define AIDF_LOOPBACK  (1<<0)
+#define AIDF_PROBING   (1<<1)
+#define AIDF_PROBEFAILED       (1<<2)
+
+extern u_char  at_multicastaddr[ ETHERADDRL ];
+extern u_char  at_org_code[ 3 ];
+extern u_char  aarp_org_code[ 3 ];
+
+int                    if_setaddr( queue_t *, mblk_t *, char *,
+                               struct sockaddr_at * );
+int                    if_getaddr(  char *, struct sockaddr_at * );
+int                    if_addmulti( queue_t *, mblk_t *, char *,
+                               struct sockaddr * );
+
+struct atif_data       *if_alloc( queue_t * );
+void                   if_free( struct atif_data * );
+int                    if_name( struct atif_data *, char *, ulong );
+int                    if_attach( struct atif_data *, char * );
+struct atif_data       *if_primary( void );
+struct atif_data       *if_dest( struct atif_data *, struct sockaddr_at * );
+struct atif_data       *if_withaddr( struct sockaddr_at * );
+struct atif_data       *if_withnet( struct sockaddr_at * );
+int                    if_route( struct atif_data *, mblk_t *,
+                               struct sockaddr_at * );
+
+int                    dl_unitdata_req( queue_t *, mblk_t *, ushort, caddr_t );
+int                    dl_enabmulti_req( queue_t *, caddr_t );
+void                   aarp_send( struct atif_data *, int, caddr_t,
+                               ushort, unchar );
+int                    aarp_rput( queue_t *, mblk_t * );
+int                    aarp_resolve( struct atif_data *, mblk_t *,
+                               struct sockaddr_at *);
+void                   aarp_init( struct atif_data * );
+void                   aarp_clean( struct atif_data * );
+int                    ddp_rput( struct atif_data *, mblk_t * );
diff --git a/sys/solaris/ioc.c b/sys/solaris/ioc.c
new file mode 100644 (file)
index 0000000..936336f
--- /dev/null
@@ -0,0 +1,126 @@
+#include <sys/types.h>
+#include <sys/stream.h>
+#include <sys/cmn_err.h>
+
+#include <string.h>
+
+#include "ioc.h"
+
+    void
+ioc_ok_ack( queue_t *q, mblk_t *m, int rval )
+{
+    struct iocblk      *ioc;
+    mblk_t             *m0;
+
+    if (( m0 = unlinkb( m )) != NULL ) {
+       freemsg( m0 );
+    }
+
+    if ( m->b_wptr - m->b_rptr < sizeof( struct iocblk )) {
+       cmn_err( CE_CONT, "ioc_ok_ack too small\n" );
+       freemsg( m );
+       return;
+    }
+    m->b_datap->db_type = M_IOCACK;
+    m->b_wptr = m->b_rptr + sizeof( struct iocblk );
+    ioc = (struct iocblk *)m->b_rptr;
+    ioc->ioc_error = 0;
+    ioc->ioc_count = 0;
+    ioc->ioc_rval = rval;
+    qreply( q, m );
+    return;
+}
+
+    void
+ioc_error_ack( queue_t *q, mblk_t *m, int errno )
+{
+    struct iocblk      *ioc;
+    mblk_t             *m0;
+
+    if (( m0 = unlinkb( m )) != NULL ) {
+       freemsg( m0 );
+    }
+
+    if ( m->b_wptr - m->b_rptr < sizeof( struct iocblk )) {
+       cmn_err( CE_CONT, "ioc_error_ack too small\n" );
+       freemsg( m );
+       return;
+    }
+    m->b_datap->db_type = M_IOCNAK;
+    m->b_wptr = m->b_rptr + sizeof( struct iocblk );
+    ioc = (struct iocblk *)m->b_rptr;
+    ioc->ioc_error = errno;
+    ioc->ioc_count = 0;
+    ioc->ioc_rval = -1;
+    qreply( q, m );
+    return;
+}
+
+    void
+ioc_copyin( queue_t *q, mblk_t *m, mblk_t *private, caddr_t addr, uint size )
+{
+    struct copyreq     *cq;
+    mblk_t             *m0;
+
+    if (( m0 = unlinkb( m )) != NULL ) {
+       freemsg( m0 );
+    }
+
+#ifdef notdef
+    /* supposedly this will fit anyway */
+    if ( m->b_wptr - m->b_rptr < sizeof( struct copyreq )) {
+       cmn_err( CE_CONT, "ioc_copyin too small\n" );
+       freemsg( m );
+       return;
+    }
+#endif notdef
+    m->b_datap->db_type = M_COPYIN;
+    m->b_wptr = m->b_rptr + sizeof( struct copyreq );
+    cq = (struct copyreq *)m->b_rptr;
+    cq->cq_addr = addr;
+    cq->cq_size = size;
+    cq->cq_flag = 0;
+    cq->cq_private = private;
+    qreply( q, m );
+    return;
+}
+
+    void
+ioc_copyout( queue_t *q, mblk_t *m, mblk_t *private, caddr_t data,
+       caddr_t addr, uint size )
+{
+    struct copyreq     *cq;
+    mblk_t             *m0;
+
+    if (( m0 = unlinkb( m )) != NULL ) {
+       freemsg( m0 );
+    }
+
+#ifdef notdef
+    /* supposedly this will fit anyway */
+    if ( m->b_wptr - m->b_rptr < sizeof( struct copyreq )) {
+       cmn_err( CE_CONT, "ioc_copyout too small\n" );
+       freemsg( m );
+       return;
+    }
+#endif notdef
+    if (( m0 = allocb( size, BPRI_MED )) == NULL ) {
+       cmn_err( CE_CONT, "ioc_copyout nomem\n" );
+       freemsg( m );
+       return;
+    }
+    m0->b_wptr = m0->b_rptr + size;
+    bcopy( data, m0->b_rptr, size );
+    linkb( m, m0 );
+
+    m->b_datap->db_type = M_COPYOUT;
+    m->b_wptr = m->b_rptr + sizeof( struct copyreq );
+    cq = (struct copyreq *)m->b_rptr;
+    cq->cq_addr = addr;
+    cq->cq_size = size;
+    cq->cq_flag = 0;
+    cq->cq_private = private;
+
+    qreply( q, m );
+    return;
+}
diff --git a/sys/solaris/ioc.h b/sys/solaris/ioc.h
new file mode 100644 (file)
index 0000000..4b1424a
--- /dev/null
@@ -0,0 +1,4 @@
+void ioc_ok_ack( queue_t *, mblk_t *, int );
+void ioc_error_ack( queue_t *, mblk_t *, int );
+void ioc_copyin( queue_t *, mblk_t *, mblk_t *, caddr_t, uint );
+void ioc_copyout( queue_t *, mblk_t *, mblk_t *, caddr_t, caddr_t, uint );
diff --git a/sys/solaris/linkage.c b/sys/solaris/linkage.c
new file mode 100644 (file)
index 0000000..7917607
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Linkage information.  Mostly this is Solaris specific, but not all.
+ * Code to do real work is in other files, this file just has the crap
+ * to get the real code loaded and called.
+ */
+
+#include <sys/types.h>
+#include <sys/modctl.h>
+#include <sys/cmn_err.h>
+
+char   *netatalk_version = VERSION;
+
+extern struct modldrv tpi_ldrv;
+extern struct modldrv dlpi_lstrmod;
+
+static struct modlinkage       ddp_linkage = {
+    MODREV_1,
+    {
+       (void *)&tpi_ldrv,
+       (void *)&dlpi_lstrmod,
+       NULL,
+    }
+};
+
+/*
+ * While these are code, they're mostly related to linkage, so
+ * we leave them here.
+ */
+    int
+_init( void )
+{
+    cmn_err( CE_CONT, "?netatalk %s\n", netatalk_version );
+    return( mod_install( &ddp_linkage ));
+}
+
+    int
+ _info( struct modinfo *modinfop )
+{
+    return( mod_info( &ddp_linkage, modinfop ));
+}
+
+    int
+_fini( void )
+{
+    return( mod_remove( &ddp_linkage ));
+}
diff --git a/sys/solaris/rt.c b/sys/solaris/rt.c
new file mode 100644 (file)
index 0000000..95bd1e2
--- /dev/null
@@ -0,0 +1,125 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/cmn_err.h>
+#include <sys/kmem.h>
+#include <net/route.h>
+#include <netatalk/at.h>
+#include <errno.h>
+
+#include "rt.h"
+
+struct rtab {
+    struct rtab                *r_next, *r_prev;
+    struct sockaddr_at r_dst;
+    struct sockaddr_at r_gate;
+};
+
+static struct rtab     *rt_net = NULL;
+static struct rtab     *rt_host = NULL;
+
+    int
+rt_add( struct sockaddr_at *dst, struct sockaddr_at *gate, int flags )
+{
+    struct rtab                *r;
+    struct rtab                *rtab;
+
+    if ( flags & RTF_HOST ) {
+       rtab = rt_host;
+    } else {
+       rtab = rt_net;
+    }
+    for ( r = rtab; r != NULL; r = r->r_next ) {
+       if (( r->r_dst.sat_addr.s_net == dst->sat_addr.s_net ) &&
+               (( flags & RTF_HOST ) ?
+               r->r_dst.sat_addr.s_node == dst->sat_addr.s_node : 1 )) {
+           return( EEXIST );
+       }
+    }
+
+    if (( r = kmem_alloc( sizeof( struct rtab ), KM_NOSLEEP )) == NULL ) {
+       return( ENOMEM );
+    }
+    r->r_dst = *dst;
+    r->r_gate = *gate;
+
+    r->r_prev = NULL;
+    r->r_next = rtab;
+    if ( rtab != NULL ) {
+       rtab->r_prev = r;
+    }
+    if ( flags & RTF_HOST ) {
+       rt_host = r;
+    } else {
+       rt_net = r;
+    }
+    return( 0 );
+}
+
+    int
+rt_del( struct sockaddr_at *dst, struct sockaddr_at *gate, int flags )
+{
+    struct rtab                *r;
+    struct rtab                *rtab;
+
+    if ( flags & RTF_HOST ) {
+       rtab = rt_host;
+    } else {
+       rtab = rt_net;
+    }
+    for ( r = rtab; r != NULL; r = r->r_next ) {
+       if (( r->r_dst.sat_addr.s_net == dst->sat_addr.s_net ) &&
+               (( flags & RTF_HOST ) ?
+               r->r_dst.sat_addr.s_node == dst->sat_addr.s_node : 1 )) {
+           break;
+       }
+    }
+    if ( r == NULL ) {
+       return( ESRCH );
+    }
+
+    if ( r == rtab ) {
+       if ( flags & RTF_HOST ) {
+           rt_host = r->r_next;
+       } else {
+           rt_net = r->r_next;
+       }
+    }
+    if ( r->r_next != NULL ) {
+       r->r_next->r_prev = r->r_prev;
+    }
+    if ( r->r_prev != NULL ) {
+       r->r_prev->r_next = r->r_next;
+    }
+    kmem_free( r, sizeof( struct rtab ));
+    return( 0 );
+}
+
+    int
+rt_gate( struct sockaddr_at *dst, struct sockaddr_at *gate )
+{
+    struct rtab                *r;
+
+    for ( r = rt_host; r != NULL; r = r->r_next ) {
+       if ( r->r_dst.sat_addr.s_net == dst->sat_addr.s_net &&
+               r->r_dst.sat_addr.s_node == dst->sat_addr.s_node ) {
+           break;
+       }
+    }
+    if ( r != NULL ) {
+       *gate = r->r_gate;
+       return( 0 );
+    }
+
+    for ( r = rt_net; r != NULL; r = r->r_next ) {
+       if ( r->r_dst.sat_addr.s_net == dst->sat_addr.s_net ) {
+           break;
+       }
+    }
+    if ( r == NULL ) {
+       return( -1 );
+    }
+
+    *gate = r->r_gate;
+    return( 0 );
+}
diff --git a/sys/solaris/rt.h b/sys/solaris/rt.h
new file mode 100644 (file)
index 0000000..ef7e17f
--- /dev/null
@@ -0,0 +1,3 @@
+int    rt_add( struct sockaddr_at *, struct sockaddr_at *, int );
+int    rt_del( struct sockaddr_at *, struct sockaddr_at *, int );
+int    rt_gate( struct sockaddr_at *, struct sockaddr_at * );
diff --git a/sys/solaris/sock.c b/sys/solaris/sock.c
new file mode 100644 (file)
index 0000000..2c19799
--- /dev/null
@@ -0,0 +1,126 @@
+#include <sys/types.h>
+#include <sys/stream.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/kmem.h>
+#include <sys/cmn_err.h>
+#include <sys/tihdr.h>
+#include <sys/ethernet.h>
+#include <net/if.h>
+
+#include <string.h>
+
+#include <netatalk/at.h>
+
+#include "if.h"
+#include "sock.h"
+
+static struct sock_data        *sockets = NULL;
+
+    struct sock_data *
+sock_alloc( queue_t *q )
+{
+    struct sock_data   *sd;
+
+    if (( sd = kmem_alloc( sizeof( struct sock_data ), KM_SLEEP )) == NULL ) {
+       return( NULL );
+    }
+    sd->sd_state = TS_UNBND;
+    sd->sd_q = q;
+    sd->sd_next = sd->sd_prev = NULL;
+    bzero( (caddr_t)&sd->sd_sat, sizeof( struct sockaddr_at ));
+
+    sd->sd_next = sockets;
+    if ( sockets != NULL ) {
+       sockets->sd_prev = sd;
+    }
+    sockets = sd;
+
+    return( sd );
+}
+
+    void
+sock_free( struct sock_data *sd )
+{
+    if ( sd == sockets ) {
+       sockets = sd->sd_next;
+    }
+    if ( sd->sd_next != NULL ) {
+       sd->sd_next->sd_prev = sd->sd_prev;
+    }
+    if ( sd->sd_prev != NULL ) {
+       sd->sd_prev->sd_next = sd->sd_next;
+    }
+    kmem_free( sd, sizeof( struct sock_data ));
+    return;
+}
+
+    struct sock_data *
+sock_dest( struct atif_data *aid, struct sockaddr_at *sat )
+{
+    struct sock_data   *sd;
+
+    for ( sd = sockets; sd != NULL; sd = sd->sd_next ) {
+       if ( sat->sat_port == sd->sd_sat.sat_port &&
+               /* huh? */
+               aid->aid_sat.sat_addr.s_net == sd->sd_sat.sat_addr.s_net &&
+               ( sat->sat_addr.s_node == sd->sd_sat.sat_addr.s_node ||
+               sat->sat_addr.s_node == ATADDR_BCAST )) {
+           break;
+       }
+    }
+    return( sd );
+}
+
+/*
+ * This is a change in semantics.  The port must be ATADDR_ANYPORT for
+ * ATADDR_ANYNET/NODE to not mean the loopback.
+ */
+    int
+sock_bind( struct sock_data *sd, struct sockaddr_at *sat )
+{
+    struct atif_data   *paid;
+    struct sock_data   *psd;
+    struct sockaddr_at psat;
+    u_short            port;
+
+    psat = *sat;
+    if ( psat.sat_family != AF_APPLETALK ) {
+       cmn_err( CE_CONT, "sock_bind non-AppleTalk\n" );
+       return( EPROTOTYPE );
+    }
+
+    if ( psat.sat_port == ATADDR_ANYPORT ) {
+       if ( psat.sat_addr.s_net == ATADDR_ANYNET &&
+               psat.sat_addr.s_node == ATADDR_ANYNODE ) {
+           /* chose primary interface */
+           if (( paid = if_primary()) == NULL ) {
+               return( EADDRNOTAVAIL );
+           }
+           psat.sat_addr.s_net = paid->aid_sat.sat_addr.s_net;
+           psat.sat_addr.s_node = paid->aid_sat.sat_addr.s_node;
+       }
+
+       /* pick unused port */
+       for ( port = ATPORT_RESERVED; port < ATPORT_LAST; port++ ) {
+           for ( psd = sockets; psd != NULL; psd = psd->sd_next ) {
+               if ( port == psd->sd_sat.sat_port &&
+                       psat.sat_addr.s_net == psd->sd_sat.sat_addr.s_net &&
+                       psat.sat_addr.s_node == psd->sd_sat.sat_addr.s_node ) {
+                   break;
+               }
+           }
+           if ( psd == NULL ) {
+               break;
+           }
+       }
+       if ( psd != NULL ) {
+           return( EADDRINUSE );
+       }
+       psat.sat_port = port;
+    }
+
+    sd->sd_sat = psat;
+    sd->sd_state = TS_IDLE;
+    return( 0 );
+}
diff --git a/sys/solaris/sock.h b/sys/solaris/sock.h
new file mode 100644 (file)
index 0000000..2397bd7
--- /dev/null
@@ -0,0 +1,12 @@
+struct sock_data {
+    struct sock_data   *sd_next, *sd_prev;
+    int                        sd_state;
+    queue_t            *sd_q;
+    struct sockaddr_at sd_sat;
+};
+
+struct sock_data       *sock_alloc( queue_t * );
+void                   sock_free( struct sock_data * );
+struct sock_data       *sock_dest( struct atif_data *, struct sockaddr_at * );
+int                    sock_bind( struct sock_data *, struct sockaddr_at * );
+void           t_unitdata_ind( queue_t *, mblk_t *, struct sockaddr_at * );
diff --git a/sys/solaris/tpi.c b/sys/solaris/tpi.c
new file mode 100644 (file)
index 0000000..078ebe5
--- /dev/null
@@ -0,0 +1,714 @@
+#include <sys/types.h>
+#include <sys/kmem.h>
+#include <sys/conf.h>
+#include <sys/stream.h>
+#include <sys/devops.h>
+#include <sys/modctl.h>
+#include <sys/ddi.h>
+#include <sys/stat.h>
+#include <sys/sockio.h>
+#include <sys/socket.h>
+#include <sys/tihdr.h>
+#include <sys/tiuser.h>
+#include <sys/timod.h>
+#include <sys/sunddi.h>
+#include <sys/ethernet.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <errno.h>
+
+#include <netatalk/endian.h>
+#include <netatalk/at.h>
+#include <netatalk/ddp.h>
+
+#include "ioc.h"
+#include "if.h"
+#include "sock.h"
+#include "rt.h"
+
+    static int
+tpi_getinfo( dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp )
+{
+    *resultp = NULL;
+    return( DDI_FAILURE );
+}
+
+    static int
+tpi_identify( dev_info_t *dip )
+{
+    if ( strcmp( ddi_get_name( dip ), "ddp" ) == 0 ) {
+       return( DDI_IDENTIFIED );
+    } else {
+       return( DDI_NOT_IDENTIFIED );
+    }
+}
+
+    static int
+tpi_attach( dev_info_t *dip, ddi_attach_cmd_t cmd )
+{
+    int                rc;
+
+    if ( cmd != DDI_ATTACH ) {
+       return( DDI_FAILURE );
+    }
+
+    if (( rc = ddi_create_minor_node( dip, "ddp", S_IFCHR, 0, DDI_PSEUDO,
+           CLONE_DEV )) != DDI_SUCCESS ) {
+       /* undo anything */
+    }
+    return( rc );
+}
+
+    static int
+tpi_detach( dev_info_t *dip, ddi_detach_cmd_t cmd )
+{
+    if ( cmd != DDI_DETACH ) {
+       return( DDI_FAILURE );
+    }
+
+    ddi_remove_minor_node( dip, "ddp" );
+
+    return( DDI_SUCCESS );
+}
+
+    static int
+tpi_open( queue_t *q, dev_t *dev, int oflag, int sflag, cred_t *cred )
+{
+    static minor_t     minor = 1;
+
+    if ( sflag != CLONEOPEN ) {
+       return( EINVAL );
+    }
+    if (( q->q_ptr = (void *)sock_alloc( q )) == NULL ) {
+       return( ENOMEM );
+    }
+
+    *dev = makedevice( getmajor( *dev ), minor++ );
+    qprocson( q );
+    return( 0 );
+}
+
+    static int
+tpi_close( queue_t *q, int oflag, cred_t *cred )
+{
+    struct sock_data   *sd = (struct sock_data *)q->q_ptr;
+
+    qprocsoff( q );
+    sock_free( sd );
+    return( 0 );
+}
+
+    static int
+tpi_rput( queue_t *q,  mblk_t *m )
+{
+    cmn_err( CE_NOTE, "tpi_rput dp_type = 0x%X\n", m->b_datap->db_type );
+    freemsg( m );
+    return( 0 );
+}
+
+    void
+t_bind_ack( queue_t *q, struct sockaddr_at *sat )
+{
+    mblk_t             *m;
+    struct T_bind_ack  *t;
+
+    if (( m = allocb( sizeof( struct T_bind_ack ) +
+           sizeof( struct sockaddr_at ), BPRI_HI )) == NULL ) {
+       return;
+    }
+    m->b_wptr = m->b_rptr + sizeof( struct T_bind_ack );
+    m->b_datap->db_type = M_PCPROTO;
+
+    t = (struct T_bind_ack *)m->b_rptr;
+    t->PRIM_type = T_BIND_ACK;
+    t->ADDR_length = sizeof( struct sockaddr_at );
+    t->ADDR_offset = m->b_wptr - m->b_rptr;
+    t->CONIND_number = 0;
+
+    bcopy( (caddr_t)sat, m->b_wptr, sizeof( struct sockaddr_at ));
+    m->b_wptr += sizeof( struct sockaddr_at );
+
+    qreply( q, m );
+    return;
+}
+
+    void
+t_ok_ack( queue_t *q, long prim )
+{
+    mblk_t             *m;
+    struct T_ok_ack    *t;
+
+
+    if (( m = allocb( sizeof( struct T_ok_ack ), BPRI_HI )) == NULL ) {
+       return;
+    }
+    m->b_wptr = m->b_rptr + sizeof( struct T_ok_ack );
+    m->b_datap->db_type = M_PCPROTO;
+
+    t = (struct T_ok_ack *)m->b_rptr;
+    t->PRIM_type = T_OK_ACK;
+    t->CORRECT_prim = prim;
+    qreply( q, m );
+    return;
+}
+
+    void
+t_error_ack( queue_t *q, long prim, long terror, long uerror )
+{
+    mblk_t             *m;
+    struct T_error_ack *t;
+
+
+    if (( m = allocb( sizeof( struct T_error_ack ), BPRI_HI )) == NULL ) {
+       return;
+    }
+    m->b_wptr = m->b_rptr + sizeof( struct T_error_ack );
+    m->b_datap->db_type = M_PCPROTO;
+
+    t = (struct T_error_ack *)m->b_rptr;
+    t->PRIM_type = T_ERROR_ACK;
+    t->ERROR_prim = prim;
+    t->TLI_error = terror;
+    t->UNIX_error = uerror;
+    qreply( q, m );
+    return;
+}
+
+    void
+t_info_ack( queue_t *q, long state )
+{
+    mblk_t             *m;
+    struct T_info_ack  *t;
+
+
+    if (( m = allocb( sizeof( struct T_info_ack ), BPRI_HI )) == NULL ) {
+       return;
+    }
+    m->b_wptr = m->b_rptr + sizeof( struct T_info_ack );
+    m->b_datap->db_type = M_PCPROTO;
+
+    t = (struct T_info_ack *)m->b_rptr;
+    t->PRIM_type = T_INFO_ACK;
+    t->TSDU_size = 586;
+    t->ETSDU_size = -2;
+    t->CDATA_size = -2;
+    t->DDATA_size = -2;
+    t->ADDR_size = sizeof( struct sockaddr_at );
+    t->OPT_size = 64;
+    t->TIDU_size = 1024;
+    t->SERV_type = T_CLTS;
+    t->CURRENT_state = state;
+    t->PROVIDER_flag = 0;
+    qreply( q, m );
+    return;
+}
+
+    void
+t_unitdata_ind( queue_t *q, mblk_t *m0, struct sockaddr_at *sat )
+{
+    mblk_t                     *m;
+    struct T_unitdata_ind      *t;
+
+    if (( m = allocb( sizeof( struct T_unitdata_ind ) +
+           sizeof( struct sockaddr_at ), BPRI_HI )) == NULL ) {
+       return;
+    }
+    m->b_wptr = m->b_rptr + sizeof( struct T_unitdata_ind );
+    m->b_datap->db_type = M_PROTO;
+
+    t = (struct T_unitdata_ind *)m->b_rptr;
+    t->PRIM_type = T_UNITDATA_IND;
+    t->SRC_length = sizeof( struct sockaddr_at );
+    t->SRC_offset = m->b_wptr - m->b_rptr;
+    bcopy( (caddr_t)sat, m->b_wptr, sizeof( struct sockaddr_at ));
+    m->b_wptr += sizeof( struct sockaddr_at );
+    t->OPT_length = 0;
+    t->OPT_offset = 0;
+    linkb( m, m0 );
+
+    qreply( q, m );
+    return;
+}
+
+struct ioc_state {
+    int                is_state;
+    int                is_count;
+    caddr_t    is_addr;
+};
+
+    static int
+tpi_wput( queue_t *q,  mblk_t *m )
+{
+    struct sock_data   *sd = (struct sock_data *)RD(q)->q_ptr;
+    union T_primitives *tl;
+    struct iocblk      *ioc;
+    struct copyresp    *cp;
+    struct ioc_state   *is;
+    struct ddpehdr     *deh;
+    mblk_t             *m0;
+    struct sockaddr_at sat;
+    struct netbuf      nb;
+    struct rtentry     rt;
+    struct ifreq       ifr;
+    int                        err;
+
+    switch ( m->b_datap->db_type ) {
+    case M_PCPROTO :
+    case M_PROTO :
+       if ( m->b_wptr - m->b_rptr < sizeof( tl->type )) {
+           freemsg( m );
+           break;
+       }
+       tl = (union T_primitives *)m->b_rptr;
+       switch ( tl->type ) {
+       case T_INFO_REQ :
+           t_info_ack( q, sd->sd_state );
+           freemsg( m );
+           break;
+
+       case T_UNBIND_REQ :
+           if ( m->b_wptr - m->b_rptr < sizeof( struct T_unbind_req )) {
+               freemsg( m );
+               break;
+           }
+           if ( sd->sd_state != TS_IDLE ) {
+               t_error_ack( q, T_BIND_REQ, TOUTSTATE, 0 );
+               freemsg( m );
+               break;
+           }
+           bzero( (caddr_t)&sd->sd_sat, sizeof( struct sockaddr_at ));
+           sd->sd_state = TS_UNBND;
+           t_ok_ack( q, T_UNBIND_REQ );
+           break;
+
+       case T_BIND_REQ :
+           if ( m->b_wptr - m->b_rptr < sizeof( struct T_bind_req )) {
+               freemsg( m );
+               break;
+           }
+           if ( sd->sd_state != TS_UNBND ) {
+               t_error_ack( q, T_BIND_REQ, TOUTSTATE, 0 );
+               freemsg( m );
+               break;
+           }
+
+           if ( tl->bind_req.ADDR_length == 0 ) {
+               bzero( (caddr_t)&sat, sizeof( struct sockaddr_at ));
+               sat.sat_family = AF_APPLETALK;
+           } else {
+               if ( tl->bind_req.ADDR_length != sizeof( struct sockaddr ) ||
+                       m->b_wptr - m->b_rptr <
+                       tl->bind_req.ADDR_offset + tl->bind_req.ADDR_length ) {
+                   cmn_err( CE_CONT, "tpi_wput T_BIND_REQ wierd\n" );
+                   freemsg( m );
+                   break;
+               }
+               sat = *(struct sockaddr_at *)(m->b_rptr +
+                       tl->bind_req.ADDR_offset );
+           }
+
+           if (( err = sock_bind( sd, &sat )) != 0 ) {
+               t_error_ack( q, T_BIND_REQ, TSYSERR, err );
+           } else {
+               /* seems like we must return the requested address */
+               t_bind_ack( q, &sat );
+           }
+           freemsg( m );
+           break;
+
+       case T_UNITDATA_REQ :
+           if ( m->b_wptr - m->b_rptr < sizeof( struct T_unitdata_req )) {
+               freemsg( m );
+               break;
+           }
+           if ( sd->sd_state != TS_IDLE ) {
+               cmn_err( CE_NOTE, "tpi_wput unitdata on unbound socket\n" );
+               t_error_ack( q, T_UNITDATA_REQ, TOUTSTATE, 0 );
+               freemsg( m );
+               break;
+           }
+           if ( tl->unitdata_req.DEST_length != sizeof( struct sockaddr )) {
+               cmn_err( CE_NOTE, "tpi_wput T_UNITDATA_REQ %d\n",
+                       tl->unitdata_req.DEST_length );
+               freemsg( m );
+               break;
+           }
+
+#ifdef notdef
+           /*
+            * Sometimes, the socket layer gives us crap...  Sound like a bug?
+            */
+           if ( m->b_rptr + tl->unitdata_req.DEST_offset +
+                   tl->unitdata_req.DEST_length > m->b_wptr ) {
+cmn_err( CE_CONT, "tpi_wput T_UNITDATA_REQ mblk size %X %X\n", m->b_rptr + tl->unitdata_req.DEST_offset + tl->unitdata_req.DEST_length, m->b_wptr );
+               freemsg( m );
+               break;
+           }
+#endif notdef
+
+           sat = *(struct sockaddr_at *)(m->b_rptr +
+                   tl->unitdata_req.DEST_offset );
+           if ( sat.sat_family != AF_APPLETALK ) {
+               cmn_err( CE_CONT, "tpi_wput non-AppleTalk\n" );
+               freemsg( m );
+               break;
+           }
+
+           if ( m->b_wptr - m->b_rptr < sizeof( struct ddpehdr )) {
+               cmn_err( CE_CONT, "tpi_wput m too short\n" );
+               freemsg( m );
+               break;
+           }
+           m->b_wptr = m->b_rptr + sizeof( struct ddpehdr );
+           m->b_datap->db_type = M_DATA;
+           deh = (struct ddpehdr *)m->b_rptr;
+           deh->deh_pad = 0;
+           deh->deh_hops = 0;
+           deh->deh_len = msgdsize( m );
+
+           deh->deh_dnet = sat.sat_addr.s_net;
+           deh->deh_dnode = sat.sat_addr.s_node;
+           deh->deh_dport = sat.sat_port;
+
+           deh->deh_snet = sd->sd_sat.sat_addr.s_net;
+           deh->deh_snode = sd->sd_sat.sat_addr.s_node;
+           deh->deh_sport = sd->sd_sat.sat_port;
+
+           deh->deh_sum = 0;                   /* XXX */
+           deh->deh_bytes = htonl( deh->deh_bytes );
+           return( if_route( if_withaddr( &sd->sd_sat ), m, &sat ));
+
+       default :
+           /* cmn_err( CE_NOTE, "tpi_wput M_PCPROTO 0x%X\n", tl->type ); */
+           t_error_ack( q, tl->type, TNOTSUPPORT, 0 );
+           freemsg( m );
+           break;
+       }
+       break;
+
+    case M_IOCTL :
+       if ( m->b_wptr - m->b_rptr < sizeof( struct iocblk )) {
+           freemsg( m );
+           break;
+       }
+       ioc = (struct iocblk *)m->b_rptr;
+       if ( ioc->ioc_count != TRANSPARENT ) {
+           cmn_err( CE_CONT, "tpi_wput non-TRANSPARENT %X\n", ioc->ioc_cmd );
+           ioc_error_ack( q, m, EINVAL );
+           break;
+       }
+       if ( m->b_cont == NULL ) {
+           cmn_err( CE_CONT, "tpi_wput M_IOCTL no arg\n" );
+           ioc_error_ack( q, m, EINVAL );
+           break;
+       }
+
+       /* de-allocated after M_IOCDATA processing */
+       if (( m0 = allocb( sizeof( struct ioc_state ), BPRI_HI )) == NULL ) {
+           cmn_err( CE_CONT, "tpi_wput m0 no mem\n" );
+           ioc_error_ack( q, m, EINVAL );
+           break;
+       }
+       m0->b_wptr = m->b_rptr + sizeof( struct ioc_state );
+       is = (struct ioc_state *)m0->b_rptr;
+
+       switch ( ioc->ioc_cmd ) {
+       case SIOCADDRT :
+       case SIOCDELRT :
+           if (( err = drv_priv( ioc->ioc_cr )) != 0 ) {
+               ioc_error_ack( q, m, err );
+               break;
+           }
+           is->is_state = M_COPYIN;
+           is->is_addr = *(caddr_t *)m->b_cont->b_rptr;
+           ioc_copyin( q, m, m0, is->is_addr, sizeof( struct rtentry ));
+           break;
+
+       case SIOCADDMULTI :
+       case SIOCSIFADDR :
+           if (( err = drv_priv( ioc->ioc_cr )) != 0 ) {
+               ioc_error_ack( q, m, err );
+               break;
+           }
+
+       case SIOCGIFADDR :
+           is->is_state = M_COPYIN;
+           is->is_addr = *(caddr_t *)m->b_cont->b_rptr;
+           ioc_copyin( q, m, m0, is->is_addr, sizeof( struct ifreq ));
+           break;
+
+       case TI_GETMYNAME :
+           is->is_state = M_COPYIN;
+           is->is_addr = *(caddr_t *)m->b_cont->b_rptr;
+           ioc_copyin( q, m, m0, is->is_addr, sizeof( struct netbuf ));
+           break;
+
+       default :
+           ioc_error_ack( q, m, EINVAL );
+           break;
+       }
+       break;
+
+    case M_IOCDATA :
+       if ( m->b_wptr - m->b_rptr < sizeof( struct copyresp )) {
+           freemsg( m );
+           break;
+       }
+       cp = (struct copyresp *)m->b_rptr;
+       if ( cp->cp_rval != 0 ) {
+           cmn_err( CE_CONT, "tpi_wput IOCDATA failed %d\n", cp->cp_rval );
+           freemsg( m );
+           break;
+       }
+
+       if (( m0 = cp->cp_private ) == NULL ) {
+           cmn_err( CE_CONT, "tpi_wput IOCDATA no state\n" );
+           ioc_error_ack( q, m, EINVAL );
+           break;
+       }
+       if ( m0->b_wptr - m0->b_rptr < sizeof( struct ioc_state )) {
+           cmn_err( CE_CONT, "tpi_wput IOCDATA private too short\n" );
+           ioc_error_ack( q, m, EINVAL );
+           break;
+       }
+       is = (struct ioc_state *)m0->b_rptr;
+
+       switch ( cp->cp_cmd ) {
+       case TI_GETMYNAME :
+           switch ( is->is_state ) {
+           case M_COPYIN :
+               if ( m->b_cont == NULL ) {
+                   cmn_err( CE_CONT, "tpi_wput TI_GETMYNAME COPYIN no arg\n" );
+                   ioc_error_ack( q, m, EINVAL );
+                   break;
+               }
+               nb = *(struct netbuf *)m->b_cont->b_rptr;
+               nb.len = sizeof( struct sockaddr_at );
+               /* copy out netbuf */
+               is->is_state = M_COPYOUT;
+               is->is_count = 1;
+               ioc_copyout( q, m, m0, (caddr_t)&nb, is->is_addr,
+                       sizeof( struct netbuf ));
+               is->is_addr = nb.buf;
+               return( 0 );
+
+           case M_COPYOUT :
+               switch ( is->is_count ) {
+               case 1 :
+                   /* copy out address to nb.buf */
+                   is->is_state = M_COPYOUT;
+                   is->is_count = 2;
+                   ioc_copyout( q, m, m0, (caddr_t)&sd->sd_sat, is->is_addr,
+                           sizeof( struct sockaddr_at ));
+                   return( 0 );
+
+               case 2 :
+                   ioc_ok_ack( q, m, 0 );
+                   break;
+
+               default :
+                   cmn_err( CE_NOTE, "tpi_wput TI_GETMYNAME count %d\n",
+                           is->is_count );
+                   ioc_error_ack( q, m, EINVAL );
+                   break;
+               }
+               break;
+
+           default :
+               cmn_err( CE_NOTE, "tpi_wput TI_GETMYNAME state %d\n",
+                       is->is_state );
+               ioc_error_ack( q, m, EINVAL );
+               break;
+           }
+           break;
+
+       case SIOCADDRT :        /* manipulate routing table */
+       case SIOCDELRT :
+           if (( err = drv_priv( cp->cp_cr )) != 0 ) {
+               ioc_error_ack( q, m, err );
+               break;
+           }
+           if ( is->is_state != M_COPYIN ) {
+               cmn_err( CE_CONT, "tpi_wput SIOC(ADD|DEL)RT bad state\n" );
+               freemsg( m );
+               break;
+           }
+
+           rt = *(struct rtentry *)m->b_cont->b_rptr;
+
+           if ( cp->cp_cmd == SIOCADDRT ) {
+               err = rt_add( (struct sockaddr_at *)&rt.rt_dst,
+                       (struct sockaddr_at *)&rt.rt_gateway, rt.rt_flags );
+           } else if ( cp->cp_cmd == SIOCDELRT ) {
+               err = rt_del( (struct sockaddr_at *)&rt.rt_dst,
+                       (struct sockaddr_at *)&rt.rt_gateway, rt.rt_flags );
+           } else {
+               cmn_err( CE_CONT, "tpi_wput SIOC(ADD|DEL)RT bad cmd\n" );
+               freemsg( m );
+               break;
+           }
+           if ( err != 0 ) {
+               ioc_error_ack( q, m, err );
+           } else {
+               ioc_ok_ack( q, m, 0 );
+           }
+           break;
+
+       /*
+        * These both require lower messages to be sent.
+        */
+       case SIOCADDMULTI :
+       case SIOCSIFADDR :
+           if (( err = drv_priv( cp->cp_cr )) != 0 ) {
+               ioc_error_ack( q, m, err );
+               break;
+           }
+           if ( is->is_state != M_COPYIN ) {
+               cmn_err( CE_CONT, "tpi_wput SIOCSIFADDR bad state\n" );
+               freemsg( m );
+               break;
+           }
+
+           ifr = *(struct ifreq *)m->b_cont->b_rptr;
+
+           /* initiate command, pass q and m (current context to be saved */
+           if ( cp->cp_cmd == SIOCSIFADDR ) {
+               err = if_setaddr( q, m, ifr.ifr_name,
+                       (struct sockaddr_at *)&ifr.ifr_addr );
+           } else {
+               err = if_addmulti( q, m, ifr.ifr_name, &ifr.ifr_addr );
+           }
+           if ( err != 0 ) {
+               ioc_error_ack( q, m, err );
+               break;
+           }
+           break;
+
+       case SIOCGIFADDR :      /* get interface address */
+           switch ( is->is_state ) {
+           case M_COPYOUT :
+               /* ack the original ioctl */
+               ioc_ok_ack( q, m, 0 );
+               break;
+
+           case M_COPYIN :
+               if ( m->b_cont == NULL ) {
+                   cmn_err( CE_CONT, "tpi_wput SIOCGIFADDR COPYIN no arg\n" );
+                   ioc_error_ack( q, m, EINVAL );
+                   break;
+               }
+
+               /* size??? */
+               ifr = *(struct ifreq *)m->b_cont->b_rptr;
+               if (( err = if_getaddr( ifr.ifr_name,
+                       (struct sockaddr_at *)&ifr.ifr_addr )) != 0 ) {
+                   ioc_error_ack( q, m, err );
+               }
+               is->is_state = M_COPYOUT;
+               ioc_copyout( q, m, m0, (caddr_t)&ifr, is->is_addr,
+                       sizeof( struct ifreq ));
+               return( 0 );    /* avoid freemsg( m0 ) below */
+
+           default :
+               cmn_err( CE_CONT, "tpi_wput SIOCGIFADDR bad state\n" );
+               freemsg( m );
+               break;
+           }
+           break;
+
+       default :
+           cmn_err( CE_NOTE, "tpi_wput M_IOCDATA 0x%X\n", cp->cp_cmd );
+           ioc_error_ack( q, m, EINVAL );
+           break;
+       }
+       freemsg( m0 );
+       break;
+
+    default :
+       cmn_err( CE_NOTE, "!tpi_wput dp_type = 0x%X\n", m->b_datap->db_type );
+       freemsg( m );
+       break;
+    }
+
+    return( 0 );
+}
+
+static struct module_info      tpi_info = {
+    0,                 /* XXX */
+    "ddp",
+    0,
+    1500,
+    3000,
+    64
+};
+
+static struct qinit            tpi_rinit = {
+    tpi_rput,                  /* qi_putp */
+    NULL,                      /* qi_srvp */
+    tpi_open,                  /* qi_qopen */
+    tpi_close,                 /* qi_qclose */
+    NULL,
+    &tpi_info,                 /* qi_minfo */
+    NULL,
+};
+
+static struct qinit            tpi_winit = {
+    tpi_wput,                  /* qi_putp */
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    &tpi_info,
+    NULL,
+};
+
+static struct streamtab                tpi_stream = {
+    &tpi_rinit,
+    &tpi_winit,
+    NULL,
+    NULL
+};
+
+static struct cb_ops           tpi_cbops = {
+    nulldev,           /* cb_open */
+    nulldev,           /* cb_close */
+    nodev,
+    nodev,
+    nodev,
+    nodev,
+    nodev,
+    nodev,
+    nodev,
+    nodev,
+    nodev,
+    nochpoll,
+    ddi_prop_op,
+    &tpi_stream,
+    D_NEW | D_MP | D_MTPERMOD, /* cb_flag */
+    CB_REV,            /* cb_rev */
+    nodev,             /* cb_aread */
+    nodev,             /* cb_awrite */
+};
+
+static struct dev_ops          tpi_devops = {
+    DEVO_REV,
+    0,
+    tpi_getinfo,
+    tpi_identify,
+    nulldev,
+    tpi_attach,
+    tpi_detach,
+    nodev,
+    &tpi_cbops,
+    (struct bus_ops *)NULL,
+    NULL,
+};
+
+/*
+ * DDP Streams device.  This device is opened by socket().
+ */
+struct modldrv                 tpi_ldrv = {
+    &mod_driverops,
+    "DDP Streams device",
+    &tpi_devops,
+};
diff --git a/sys/sunos/Makefile b/sys/sunos/Makefile
new file mode 100644 (file)
index 0000000..e489af1
--- /dev/null
@@ -0,0 +1,148 @@
+# Sun specific defines, passed to subdirectories.
+DEFS= -DBSD4_3 -DUSE_OLD_RQUOTA -DUSE_UFS_QUOTA_H -DUSE_MNTENT_H \
+       -DDLSYM_PREPEND_UNDERSCORE
+OPTOPTS=       -O
+CC=    gcc
+INSTALL=       install
+# use -lbind instead of -lresolv if you're using BIND >= 8.x
+AFPLIBS=-lresolv
+ADDLIBS=
+
+#CSHAREDFLAGS=   -pic
+CSHAREDFLAGS=   -fPIC
+
+#LDFLAGS_EXPORT=
+LDSHARED=      ld
+LDSHAREDFLAGS=  -assert pure-text
+LIBSHARED=      -ldl
+
+
+# source for kernel module
+SRC=   at_sun.c aarp.c at_control.c at_proto.c ddp_input.c ddp_output.c \
+       ddp_usrreq.c
+OBJ=   at_sun.o aarp.o at_control.o at_proto.o ddp_input.o ddp_output.o \
+       ddp_usrreq.o
+
+INCPATH = -I../../include -I../netatalk
+CFLAGS=        ${DEFS} ${OPTOPTS} ${INCPATH}
+
+ALL=   ../../libatalk ../../include ../../bin ../../etc ../../man
+
+all:   kernel ${ALL}
+
+kernel : netatalk.o
+
+netatalk.o:    ${OBJ}
+       ${LD} -r -o netatalk.o ${OBJ}
+
+at_sun.o:      at_sun.c
+       ${CC} ${CFLAGS} -DKERNEL -D`arch -k` -c at_sun.c
+
+aarp.o:        ../netatalk/aarp.c
+       ${CC} ${CFLAGS} -DKERNEL -D`arch -k` -c ../netatalk/aarp.c
+
+at_control.o:  ../netatalk/at_control.c
+       ${CC} ${CFLAGS} -DKERNEL -D`arch -k` -c ../netatalk/at_control.c
+
+at_proto.o:    ../netatalk/at_proto.c
+       ${CC} ${CFLAGS} -DKERNEL -D`arch -k` -c ../netatalk/at_proto.c
+
+ddp_input.o:   ../netatalk/ddp_input.c
+       ${CC} ${CFLAGS} -DKERNEL -D`arch -k` -c ../netatalk/ddp_input.c
+
+ddp_output.o:  ../netatalk/ddp_output.c
+       ${CC} ${CFLAGS} -DKERNEL -D`arch -k` -c ../netatalk/ddp_output.c
+
+ddp_usrreq.o:  ../netatalk/ddp_usrreq.c
+       ${CC} ${CFLAGS} -DKERNEL -D`arch -k` -c ../netatalk/ddp_usrreq.c
+
+../../bin ../../etc:   ../../libatalk
+
+${ALL}:        FRC
+       cd $@; ${MAKE} ${MFLAGS} CC="${CC}" \
+           ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+           SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+           ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+           AFPLIBS="${AFPLIBS}" LDSHARED="${LDSHARED}" \
+           LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+           LDSHAREDFLAGS="${LDSHAREDFLAGS}" CSHAREDFLAGS="${CSHAREDFLAGS}" \
+           LIBSHARED="${LIBSHARED}" \
+           all
+
+FRC:
+
+kinstall :     kernel
+       -mkdir ${DESTDIR}
+       -mkdir ${ETCDIR}
+       ${INSTALL} -c netatalk.o ${ETCDIR}
+
+install :      kinstall
+       -mkdir ${DESTDIR}
+       -mkdir ${SBINDIR}
+       -mkdir ${BINDIR}
+       -mkdir ${ETCDIR}
+       -mkdir ${LIBDIR}
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} CC="${CC}" \
+               ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+               SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+               ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+               DESTDIR="${DESTDIR}" MANDIR="${MANDIR}" AFPLIBS="${AFPLIBS}" \
+               AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" \
+               LDSHARED="${LDSHARED}" LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+               LDSHAREDFLAGS="${LDSHAREDFLAGS}" \
+               CSHAREDFLAGS="${CSHAREDFLAGS}" LIBSHARED="${LIBSHARED}" \
+               INSTALL="${INSTALL}" $@); \
+       done
+       rm -f ${ETCDIR}/rc.atalk
+       sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+               -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+               -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+               -e s@:INCDIR:@${INCDIR}@ -e 's@^##@@' \
+           < ../../distrib/initscripts/rc.atalk.bsd > ${ETCDIR}/rc.atalk
+       if [ -f ${ETCDIR}/afpd.conf ]; then \
+               echo "Retaining old afpd.conf file.";  \
+       else \
+               sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+                       -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+                       -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+                       -e s@:INCDIR:@${INCDIR}@ \
+                       < ../../config/afpd.conf > ${ETCDIR}/afpd.conf; \
+       fi
+       @echo
+       @echo "Install is done.  Don't forget to add lines from"
+       @echo "services.atalk to /etc/services and to call rc.atalk"
+       @echo "in /etc/rc.  See README and README.SUN for more"
+       @echo "information."
+
+clean : sysclean
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} clean); \
+       done
+
+sysclean :
+       rm -f a.out core* *.o *.bak *[Ee]rrs tags
+       rm -f netatalk.o
+
+depend :
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} DEFS=${DEFS} depend); \
+       done
+       for i in ${SRC} ; do \
+           ${CC} -M ${DEFS} ${INCPATH} $$i | \
+           awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+               else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+               else rec = rec " " $$2 } } \
+               END { print rec } ' >> makedep; done
+       sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+       cat makedep >> Makefile.tmp
+       rm makedep
+       echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+       echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+       echo '# see make depend above' >> Makefile.tmp
+       rm -f Makefile.bak
+       cp Makefile Makefile.bak
+       mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
diff --git a/sys/sunos/at_sun.c b/sys/sunos/at_sun.c
new file mode 100644 (file)
index 0000000..507d9ef
--- /dev/null
@@ -0,0 +1,374 @@
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/domain.h>
+#include <sys/errno.h>
+
+#include <net/if_arp.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <net/af.h>
+#include <net/netisr.h>
+
+#include <sun/vddrv.h>
+
+#include <netinet/in.h>
+#undef s_net
+#include <netinet/if_ether.h>
+
+#include <netatalk/at.h>
+#include <netatalk/at_var.h>
+#include <netatalk/ddp_var.h>
+#include <netatalk/phase2.h>
+
+struct vdlat {
+    int                vda_magic;
+    char       *vda_name;
+}      atvdl = {
+    VDMAGIC_USER, "netatalk"
+};
+
+struct ifqueue *atef_input();
+int            atef_output();
+extern int     atintr();
+
+extern u_char  aarp_org_code[ 3 ];
+extern u_char  at_org_code[ 3 ];
+
+/*
+ * Entries in this table are inserted into the ether_families linked
+ * list at the beginnning. As such, they will be searched by the input
+ * and output routines opposite to the order here.
+ *
+ * In order to do both phase 1 and phase 2 during output, we have a
+ * special entry (the AF_APPLETALK entry) whose ethertype field is
+ * changed by the output function, to reflect the actual link layer
+ * to be used. This ether_family entry is never seen during Ethernet
+ * input, since the earlier entry captures all packets (it is seen
+ * during loopback input, in that the input function is called directly
+ * on loopback output).
+ */
+struct ether_family    ether_atalk[] = {
+    {
+       AF_APPLETALK,   0,      /* Changed by atef_output() */
+       atef_input,     atef_output,    atintr,
+    },
+    {
+       0,              ETHERTYPE_AARP,
+       atef_input,     0,              0,
+    },
+    {
+       0,              ETHERTYPE_AT,
+       atef_input,     0,              atintr,
+    },
+    {
+       0,              EF_8023_TYPE,
+       atef_input,     0,              atintr,
+    },
+};
+int    ether_atalkN = sizeof( ether_atalk ) / sizeof( ether_atalk[ 0 ] );
+
+extern struct ether_family     *ether_families;
+
+extern int                     atalk_hash(), atalk_netmatch();
+extern int                     null_hash(), null_netmatch();
+
+xxxinit( cmd, vdd, vdi, vds )
+    unsigned int       cmd;
+    struct vddrv       *vdd;
+    addr_t             vdi;
+    struct vdstat      *vds;
+{
+    struct ether_family        *ef;
+    struct domain      *dom;
+    struct protosw     *pr;
+    int                        i;
+
+    switch( cmd ) {
+       case VDLOAD :
+           vdd->vdd_vdtab = (struct vdlinkage *)&atvdl;
+
+           /*
+            * Register with the network-interface layer (ethernet).
+            */
+           for ( i = 0; i < ether_atalkN; i++ ) {
+               ether_register( &ether_atalk[ i ] );
+           }
+
+           /*
+            * Register with the socket layer.
+            */
+           atalkdomain.dom_next = domains;
+           domains = &atalkdomain;
+           if ( atalkdomain.dom_init ) {
+               (*atalkdomain.dom_init)();
+           }
+           for ( pr = atalkdomain.dom_protosw;
+                   pr < atalkdomain.dom_protoswNPROTOSW; pr++ ) {
+               if ( pr->pr_init ) {
+                   (*pr->pr_init)();
+               }
+           }
+
+           /*
+            * Cobble ourselves into the routing table.
+            */
+           afswitch[ AF_APPLETALK ].af_hash = atalk_hash;
+           afswitch[ AF_APPLETALK ].af_netmatch = atalk_netmatch;
+           return( 0 );
+
+       case VDUNLOAD :
+           /*
+            * Make sure that there are no open appletalk sockets.
+            */
+           if ( ddpcb != NULL ) {
+               return( EMFILE );
+           }
+
+           /*
+            * There is no ether_unregister(), so we'll have to do it
+            * our selves...
+            */
+           for ( i = 0; i < ether_atalkN; i++ ) {
+               if ( ether_families == &ether_atalk[ i ] ) {
+                   ether_families = ether_families->ef_next;
+                   continue;
+               } else {
+                   for ( ef = ether_families; ef->ef_next; ef = ef->ef_next ) {
+                       if ( ef->ef_next == &ether_atalk[ i ] ) {
+                           ef->ef_next = ef->ef_next->ef_next;
+                           break;
+                       }
+                   }
+               }
+           }
+
+           /*
+            * Remove aarp timers and held packets.
+            */
+           aarp_clean();
+
+           /*
+            * Remove AppleTalk interface addresses.
+            */
+           aa_clean();
+
+           /*
+            * Remove our routines from the routing table.
+            */
+           afswitch[ AF_APPLETALK ].af_hash = null_hash;
+           afswitch[ AF_APPLETALK ].af_netmatch = null_netmatch;
+
+           /*
+            * Remove atalkdomain from the domains list.
+            * Unlikely, but someone may have registered after us.
+            */
+           if ( domains == &atalkdomain ) {
+               domains = domains->dom_next;
+           } else {
+               for ( dom = domains; dom->dom_next; dom = dom->dom_next ) {
+                   if ( dom->dom_next == &atalkdomain ) {
+                       dom->dom_next = dom->dom_next->dom_next;
+                       break;
+                   }
+               }
+           }
+           return( 0 );
+
+       case VDSTAT :
+           return( 0 );
+       default :
+           return( EIO );
+    }
+}
+
+/*
+ * Input routine for netatalk on suns.  There are five possible
+ * packets.  First, packets received on the loopback interface
+ * are immediately sent to the phase 1 interrupt queue (this will
+ * have to change if we ever do a phase 2 only version).  Second,
+ * IEEE802 packet are sent to either the aarpinput() routine or
+ * the phase 2 interrupt queue.  Finally, DIX packets are sent
+ * to either aarpinput() or the phase 1 interrupt queue.
+ */
+    struct ifqueue *
+atef_input( ifp, m, header )
+    struct ifnet       *ifp;
+    struct mbuf                *m;
+    struct ether_header        *header;
+{
+    struct llc         llc;
+    struct mbuf                *n = 0;
+
+    /*
+     * Check first for LOOPBACK flag, since loopback code passes NULL for
+     * the header.
+     */
+    if ( ifp->if_flags & IFF_LOOPBACK ) {
+       return( &atintrq2 );
+    }
+
+    /*
+     * Before SunOS 4.1, the ether_type was passed as is from the
+     * packet.  After SunOS 4.1, the ether_type is swapped in
+     * do_protocol(), before the ether_family routines are called.
+     */
+#if defined( sun ) && defined( i386 )
+    header->ether_type = ntohs( header->ether_type );
+#endif sun i386
+
+    if ( header->ether_type <= ETHERMTU ) {    /* IEEE802 */
+       /*
+        * We need to remove the interface pointer from the beginning of this
+        * packet.  We can't always use IF_ADJ(), since it can (and will,
+        * very often) MFREE() the first mbuf in our chain.  If IF_ADJ()
+        * would free the first mbuf, we just advance our pointer to the
+        * next mbuf.  Since our calling routine passes m by value, we're
+        * not actually losing m.  Later, we don't need to put the interface
+        * pointer back on, since the caller still has it in its copy of m.
+        */
+       if ( m->m_len == sizeof( struct ifnet * )) {
+           n = m;
+           m = m->m_next;
+       } else {
+           IF_ADJ( m );
+       }
+
+       /*
+        * We can't call m_pullup(), since we need to preserve
+        * the value of m.
+        */
+       if ( m->m_len < sizeof( struct llc )) {
+printf( "atef_input size llc\n" );
+           ( n ) ? m_freem( n ) : m_freem( m );
+           return( 0 );
+       }
+       bcopy( mtod( m, caddr_t ), &llc, sizeof( struct llc ));
+       if ( llc.llc_dsap != LLC_SNAP_LSAP || llc.llc_ssap != LLC_SNAP_LSAP ||
+               llc.llc_control != LLC_UI ) {
+           ( n ) ? m_freem( n ) : m_freem( m );
+           return( 0 );
+       }
+
+       /*
+        * See IF_ADJ() above.  Here we prepend ifp to the mbuf chain.  If we
+        * didn't remove it earlier, we don't replace it here.
+        */
+       if ( n ) {
+           m_adj( m, sizeof( struct llc ));
+       } else {
+           m_adj( m, sizeof( struct llc ) - sizeof( struct ifnet *));
+           if ( m->m_len < sizeof( struct ifnet * )) {
+printf( "atef_input too small!\n" );
+               m_freem( m );
+               return( 0 );
+           }
+           *mtod( m, struct ifnet ** ) = ifp;
+       }
+
+       if ( ntohs( llc.llc_ether_type ) == ETHERTYPE_AT &&
+               bcmp( llc.llc_org_code, at_org_code,
+               sizeof( at_org_code )) == 0 ) {
+           return( &atintrq2 );
+       }
+
+       /* do we really want to pass m, here?  what happened to n? XXX */
+       if ( ntohs( llc.llc_ether_type ) == ETHERTYPE_AARP &&
+               bcmp( llc.llc_org_code, aarp_org_code,
+               sizeof( aarp_org_code )) == 0 ) {
+           aarpinput( ifp, n ? n : m );
+           return( 0 );
+       }
+
+    } else {                                   /* DIX */
+       switch ( header->ether_type ) {
+       case ETHERTYPE_AT :
+           return( &atintrq1 );
+
+       case ETHERTYPE_AARP :
+           aarpinput( ifp, m );
+           return( 0 );
+       }
+    }
+
+    ( n ) ? m_freem( n ) : m_freem( m );
+    return( 0 );
+}
+
+/*
+ * If the destination is on a 802.3 wire, do phase 2 encapsulation,
+ * adding the 802.2 and SNAP headers.  Always fill in the edst with the
+ * ethernet address of the destination.
+ */
+atef_output( dst, m, ifp, edst )
+    struct sockaddr_at *dst;
+    struct mbuf                *m;
+    struct ifnet       *ifp;
+    struct ether_addr  *edst;
+{
+    struct at_ifaddr   *aa;
+    struct mbuf                *m0;
+    struct llc         llc;
+    int                        s;
+
+    s = splimp();
+    if ( !aarpresolve( ifp, m, dst, edst )) {
+       (void) splx( s );
+       return( 1 );
+    }
+    (void) splx( s );
+
+    /*
+     * ifaddr is the first thing in at_ifaddr
+     */
+    if (( aa = (struct at_ifaddr *)at_ifawithnet( dst, ifp->if_addrlist ))
+           == 0 ) {
+       m_freem( m );
+       return( 1 );
+    }
+
+    /*
+     * In the phase 2 case, we need to prepend an mbuf for the llc header.
+     * Since we must preserve the value of m, which is passed to us by
+     * value, we m_copy() the first mbuf, and use it for our llc header.
+     *
+     * We could worry about leaving space for the ether header, but
+     * since we'll have to go through all sorts of hoops, including a
+     * possibly large copy, there's really no sense.
+     */
+    if ( aa->aa_flags & AFA_PHASE2 ) {
+       if ( M_HASCL( m ) || m->m_off - MMINOFF < sizeof( struct llc )) {
+           if (( m0 = m_copy( m, 0, m->m_len )) == 0 ) {
+               m_freem( m );
+               return( 1 );
+           }
+           if ( M_HASCL( m )) {        /* m is a cluster */
+               int s = splimp();
+
+               mclput( m );
+               splx( s );
+           }
+
+           m0->m_next = m->m_next;
+           m->m_next = m0;
+           m->m_off = MMAXOFF - sizeof( struct llc );
+           m->m_len = sizeof( struct llc );
+       } else {
+           m->m_off -= sizeof( struct llc );
+           m->m_len += sizeof( struct llc );
+       }
+
+       llc.llc_dsap = llc.llc_ssap = LLC_SNAP_LSAP;
+       llc.llc_control = LLC_UI;
+       bcopy( at_org_code, llc.llc_org_code, sizeof( at_org_code ));
+       llc.llc_ether_type = htons( ETHERTYPE_AT );
+       bcopy( &llc, mtod( m, caddr_t ), sizeof( struct llc ));
+       ether_atalk[ 0 ].ef_ethertype = EF_8023_TYPE;
+       return( 0 );
+    } else {
+       ether_atalk[ 0 ].ef_ethertype = ETHERTYPE_AT;
+       return( 0 );
+    }
+}
diff --git a/sys/ultrix/Makefile b/sys/ultrix/Makefile
new file mode 100644 (file)
index 0000000..e3aa638
--- /dev/null
@@ -0,0 +1,143 @@
+# Ultrix specific defines, passed to subdirectories.
+# i believe that the current setup will break with this.
+#DEFS=
+DEFS=-I../../sys/generic -DUSE_OLD_RQUOTA
+OPTOPTS=
+#CC=   cc 
+CC = gcc 
+CSHAREDFLAGS=   -fPIC
+LDSHARED=      gcc
+LDSHAREDFLAGS=  -shared
+LDFLAGS_EXPORT=-rdynamic
+
+INSTALL=       install
+
+LIBSHARED=      -ldl
+AFPLIBS= 
+ADDLIBS=
+
+INCPATH = -I../../include -I../netatalk
+CFLAGS=        ${DEFS} ${OPTOPTS} ${INCPATH}
+
+ALL=   ../../libatalk ../../include ../../bin ../../etc ../../man
+
+oops:
+       @echo "Read README again.  Don't type 'make' here."
+       @exit 1
+
+all:    ${ALL}
+
+../../bin ../../etc:   ../../libatalk
+
+${ALL}:        FRC
+       cd $@; ${MAKE} ${MFLAGS} CC="${CC}" \
+           ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+           SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+           ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+           DESTDIR="${DESTDIR}" AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" \
+           AFPLIBS="${AFPLIBS}" LDSHARED="${LDSHARED}" \
+           LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+           LDSHAREDFLAGS="${LDSHAREDFLAGS}" CSHAREDFLAGS="${CSHAREDFLAGS}" \
+           LIBSHARED="${LIBSHARED}" \
+           all
+
+FRC: kpatch-4.3 kpatch-4.4
+
+kpatch-4.3:
+       -ln -s kpatch-4.2 kpatch-4.3
+kpatch-4.4:
+       -ln -s kpatch-4.2 kpatch-4.4
+
+install :
+       -mkdir ${DESTDIR}
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} CC="${CC}" \
+               ADDLIBS="${ADDLIBS}" DEFS="${DEFS}" OPTOPTS="${OPTOPTS}" \
+               SBINDIR="${SBINDIR}" BINDIR="${BINDIR}" RESDIR="${RESDIR}" \
+               ETCDIR="${ETCDIR}" LIBDIR="${LIBDIR}" INCDIR="${INCDIR}" \
+               DESTDIR="${DESTDIR}" MANDIR="${MANDIR}" \
+               AFSDIR="${AFSDIR}" KRBDIR="${KRBDIR}" AFPLIBS="${AFPLIBS}" \
+               LDSHARED="${LDSHARED}" LDFLAGS_EXPORT="${LDFLAGS_EXPORT}" \
+               LDSHAREDFLAGS="${LDSHAREDFLAGS}" \
+               CSHAREDFLAGS="${CSHAREDFLAGS}" LIBSHARED="${LIBSHARED}" \
+               INSTALL="${INSTALL}" $@); \
+       done
+       rm -f ${ETCDIR}/rc.atalk
+       sed -e s@:DESTDIR:@${DESTDIR}@ -e s@:SBINDIR:@${SBINDIR}@ \
+               -e s@:BINDIR:@${BINDIR}@ -e s@:RESDIR:@${RESDIR}@ \
+               -e s@:ETCDIR:@${ETCDIR}@ -e s@:LIBDIR:@${LIBDIR}@ \
+               -e s@:INCDIR:@${INCDIR}@ \
+           < ../../distrib/initscripts/rc.atalk.bsd > ${ETCDIR}/rc.atalk
+       @echo
+       @echo "Install is done.  Don't forget to add lines from"
+       @echo "services.atalk to /etc/services and to call rc.atalk
+       @echo "in /etc/rc.  Next, install the kernel patches."
+       @echo "See README.ULTRIX for more information."
+
+kpatch :
+       @echo "WARNING!!!  This patches your kernel!!!"
+       @echo -n "(hit control-c with in 10 seconds, to stop)"
+       @sleep 10
+       @echo
+       @echo
+       @if grep -s -w atalk /sys/conf/files; then \
+           echo "You already have a version of netatalk installed."; \
+           echo "You will have to remove this old version.  See"; \
+           echo "README.ULTRIX for specific instructions."; \
+           exit 1; \
+       else \
+           case `/bin/uname -r` in \
+               4.1) echo -n "Applying 4.1 patches..."; \
+                   patch -s -d /sys -p0 < kpatch-4.1;  \
+                   echo " done."; \
+                   ;; \
+               4.2|4.3|4.4) echo -n "Applying 4.2/4.3/4.4 patches..."; \
+                   patch -s -d /sys -p0 < kpatch-4.2;  \
+                   echo " done."; \
+                   ;; \
+               *) echo "Unknown release of Ultrix"; exit 1; \
+                   ;; \
+           esac; \
+       fi
+       @echo
+       @echo "Next, install the netatalk kernel files.  See README.ULTRIX"
+       @echo "for specific instructions."
+
+kinstall :
+       @echo "Copying netatalk to kernel building area..."
+       -mkdir /sys/net/netatalk
+       cp ../netatalk/*.[ch] *.[ch] /sys/net/netatalk
+       @echo "Done."
+       @echo
+       @echo "Next, make a new kernel.  See README.ULTRIX"
+       @echo "for specific instructions."
+
+clean :
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} clean); \
+       done
+
+sysclean :
+
+depend :
+       for i in ${ALL}; \
+           do (cd $$i; ${MAKE} ${MFLAGS} DEFS="${DEFS}" depend); \
+       done
+       for i in ${SRC} ; do \
+           ${CC} -M ${DEFS} ${INCPATH} $$i | \
+           awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+               else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+               else rec = rec " " $$2 } } \
+               END { print rec } ' >> makedep; done
+       sed -n '1,/^# DO NOT DELETE THIS LINE/p' Makefile > Makefile.tmp
+       cat makedep >> Makefile.tmp
+       rm makedep
+       echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.tmp
+       echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.tmp
+       echo '# see make depend above' >> Makefile.tmp
+       rm -f Makefile.bak
+       cp Makefile Makefile.bak
+       mv Makefile.tmp Makefile
+
+# DO NOT DELETE THIS LINE
+
diff --git a/sys/ultrix/at_ultrix.c b/sys/ultrix/at_ultrix.c
new file mode 100644 (file)
index 0000000..e53fb39
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#include <sys/types.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <net/if.h>
+#include <net/if_llc.h>
+#include <net/if_to_proto.h>
+#include <net/netisr.h>
+#include <netinet/in.h>
+#undef s_net
+#include <netinet/if_ether.h>
+
+#include "at.h"
+#include "at_var.h"
+
+extern u_char  at_org_code[ 3 ];
+extern u_char  aarp_org_code[ 3 ];
+
+/*
+ * This is the magic input routine, for all AppleTalk related packets.
+ * It will pick up *all* packets received, on all interfaces, apparently.
+ * If it turns out that receiving all packets in this fashion causes
+ * DLI to not receive packets what it should, we may need to call DLI
+ * directly from within the AppleTalk input routines.  Ick.
+ */
+struct mbuf *
+ddp_ifinput( m, ifp, inq, eh )
+    struct mbuf                *m;
+    struct ifnet       *ifp;
+    struct ifqueue     **inq;
+    struct ether_header        *eh;
+{
+    struct llc         llc;
+    struct if_family   *ifam;
+
+    switch ( eh->ether_type ) {
+    case ETHERTYPE_AT :
+       *inq = &atintrq1;
+       smp_lock( &(*inq)->lk_ifqueue, LK_RETRY );
+       schednetisr( NETISR_AT );
+       return( m );
+
+    case ETHERTYPE_AARP :
+       aarpinput( ifp, m );
+       return( 0 );
+
+    default :
+       if ( eh->ether_type <= ETHERMTU ) {             /* ieee802 */
+           if ( m->m_len < sizeof( struct llc )) {
+               break;
+           }
+
+           bcopy( mtod( m, caddr_t ), &llc, sizeof( struct llc ));
+           if ( llc.llc_dsap != LLC_SNAP_LSAP ||
+                   llc.llc_ssap != LLC_SNAP_LSAP ||
+                   llc.llc_control != LLC_UI ) {
+               break;
+           }
+
+           if ( bcmp( llc.llc_org_code, at_org_code,
+                   sizeof( at_org_code )) == 0 &&
+                   ntohs( llc.llc_ether_type ) == ETHERTYPE_AT ) {
+               m_adj( m, sizeof( struct llc ));
+               *inq = &atintrq2;
+               smp_lock( &(*inq)->lk_ifqueue, LK_RETRY );
+               schednetisr( NETISR_AT );
+               return( m );
+           }
+
+           if ( bcmp( llc.llc_org_code, aarp_org_code,
+                   sizeof( aarp_org_code )) == 0 &&
+                   ntohs( llc.llc_ether_type ) == ETHERTYPE_AARP ) {
+               m_adj( m, sizeof( struct llc ));
+               aarpinput( ifp, m );
+               return( 0 );
+           }
+       }
+    }
+
+    /*
+     * Check is anyone else wants this packet.
+     */
+    for ( ifam = if_family; ifam->domain != -1; ifam++ ) {
+       if (( eh->ether_type == ifam->if_type || ifam->if_type == -1 ) &&
+               ifam->prswitch &&
+               ifam->prswitch->pr_ifinput != (int (*)())ddp_ifinput ) {
+           break;
+       }
+    }
+    if ( ifam->domain != -1 && ifam->prswitch->pr_ifinput ) {
+       return( (struct mbuf *)(*ifam->prswitch->pr_ifinput)( m, ifp,
+               inq, eh ));
+    }
+
+    m_freem( m );
+    return( 0 );
+}
+
+/*
+ * Fill in type and odst. odst is the media output address, i.e.
+ * the MAC layer address. Type is the MAC type. Should be 0 to
+ * indicate IEEE addressing.
+ *
+ * Stupidly enough, there's no way to say "can't send this now."
+ * So, we just let the first packet go into the air. Not much
+ * else to be done, except maybe bitch at DEC. Note: we're not
+ * passing the mbuf to aarpresolve() -- that way it doesn't get
+ * mfree-ed twice.
+ */
+ddp_ifoutput( ifp, m, dst, type, odst )
+    struct ifnet       *ifp;
+    struct mbuf                *m;
+    struct sockaddr_at *dst;
+    short              *type;
+    char               *odst;
+{
+    struct at_ifaddr   *aa;
+    struct llc         *llc;
+    struct mbuf                *m0;
+
+    if ( !aarpresolve( ifp, 0, dst, odst )) {
+       *type = 0xffff;
+       return( 0 );
+    }
+
+    if (( aa = (struct at_ifaddr *)at_ifawithnet( dst, ifp->if_addrlist ))
+           == 0 ) {
+       *type = 0xffff;
+       return( 0 );
+    }
+
+    if ( aa->aa_flags & AFA_PHASE2 ) {
+       /*
+        * This code needs to be modeled after the similar code in
+        * at_sun.c -- you can't just MGET() and bcopy(), since we might be
+        * dealing with mbufs which are really pages.
+        */
+       MGET( m0, M_WAIT, MT_HEADER );
+       if ( m0 == 0 ) {
+           *type = 0xffff;
+           return( 0 );
+       }
+       m0->m_next = m->m_next;
+       m0->m_off = m->m_off;
+       m0->m_len = m->m_len;
+       bcopy( mtod( m, caddr_t ), mtod( m0, caddr_t ), m->m_len );
+       m->m_next = m0;
+       m->m_off = MMINOFF;
+       m->m_len = sizeof( struct llc );
+
+       llc = mtod( m, struct llc *);
+       llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP;
+       llc->llc_control = LLC_UI;
+       bcopy( at_org_code, llc->llc_org_code, sizeof( at_org_code ));
+       llc->llc_ether_type = htons( ETHERTYPE_AT );
+
+       /*
+        * Set the type to be the length of the packet, instead of 0.
+        * Ultrix used to put the length in the packet when we set type
+        * to 0, however, now we do it ourselves.
+        */
+       for ( *type = 0; m; m = m->m_next ) {
+           *type += m->m_len;
+       }
+    } else {
+       *type = ETHERTYPE_AT;
+    }
+
+    return( 1 );
+}
+
+ddp_ifioctl( ifp, cmd, data )
+    struct ifnet       *ifp;
+    int                        cmd;
+    caddr_t            data;
+{
+    switch( cmd ) {
+    case SIOCSIFADDR :
+       aarpwhohas((struct arpcom *)ifp,
+               &AA_SAT((struct ifaddr *)data)->sat_addr );
+       break;
+    default :
+       return( EINVAL );
+    }
+    return( 0 );
+}
diff --git a/sys/ultrix/kpatch-4.1 b/sys/ultrix/kpatch-4.1
new file mode 100644 (file)
index 0000000..8a30e43
--- /dev/null
@@ -0,0 +1,151 @@
+*** ../sys.old/conf/files      Fri Jul  6 10:19:49 1990
+--- ./conf/files       Thu Mar 12 17:33:58 1992
+***************
+*** 75,80 ****
+--- 75,87 ----
+  net/netinet/tcp_timer.c      optional inet Binary
+  net/netinet/tcp_usrreq.c     optional inet Binary
+  net/netinet/udp_usrreq.c     optional inet Binary
++ net/netatalk/aarp.c  optional atalk
++ net/netatalk/at_control.c    optional atalk
++ net/netatalk/at_proto.c      optional atalk
++ net/netatalk/at_ultrix.c     optional atalk
++ net/netatalk/ddp_input.c     optional atalk
++ net/netatalk/ddp_output.c    optional atalk
++ net/netatalk/ddp_usrreq.c    optional atalk
+  net/netbsc/bsc_pcb.c optional bsc Binary
+  net/netbsc/bsc_proto.c       optional bsc 
+  net/netbsc/bsc_states.c      optional bsc Binary
+*** ../sys.old/data/af_data.c  Fri Jul  6 09:40:50 1990
+--- ./data/af_data.c   Thu Mar 12 17:34:03 1992
+***************
+*** 69,74 ****
+--- 69,82 ----
+  #define AFNS AFNULL
+  #endif NS
+  
++ #ifdef ATALK
++ extern int atalk_hash(), atalk_netmatch();
++ #define AFATALK \
++      { atalk_hash,   atalk_netmatch }
++ #else
++ #define AFATALK      AFNULL
++ #endif
++ 
+  #ifdef BINARY
+  
+  extern       struct afswitch afswitch[];
+***************
+*** 78,83 ****
+  struct afswitch afswitch[AF_MAX] = {
+       AFNULL, AFNULL, AFINET, AFINET, AFPUP,
+       AFNULL, AFNS,   AFNULL, AFNULL, AFNULL,
+!      AFNULL
+  };
+  #endif
+--- 86,92 ----
+  struct afswitch afswitch[AF_MAX] = {
+       AFNULL, AFNULL, AFINET, AFINET, AFPUP,
+       AFNULL, AFNS,   AFNULL, AFNULL, AFNULL,
+!      AFNULL, AFNULL, AFNULL, AFNULL, AFNULL,
+!      AFNULL, AFATALK, AFNULL, AFNULL, AFNULL,
+  };
+  #endif
+*** ../sys.old/data/if_to_proto_data.c Fri Jul  6 09:40:26 1990
+--- ./data/if_to_proto_data.c  Thu Jun  3 11:53:44 1993
+***************
+*** 135,140 ****
+--- 135,156 ----
+          { ETHERTYPE_RC,         AF_DLI,         0,              0 }
+  #endif
+  
++ /*
++  * Hook for netatalk.  We receive all packets.  If we're not interested
++  * in the packet, we do a normal search on if_family for someone else
++  * who might want the packet.
++  * (Not yet. XXX)
++  */
++ #ifdef ATALK
++ #include "atalk.h"
++ #undef s_net
++ #include "../net/netatalk/at.h"
++ #define ETHER_ATALK \
++      { -1,                   AF_APPLETALK,   ATPROTO_DDP,    0 }
++ #else ATALK
++ #define ETHER_ATALK  IFNULL
++ #endif ATALK
++ 
+  #ifdef       BINARY
+  
+  extern struct if_family if_family[];
+***************
+*** 145,151 ****
+  
+  struct if_family if_family[] = {
+!      ETHER_DECNET,   ETHER_LAT,      ETHER_APPLE, ETHER_APPLEARP,
+       ETHER_NS,       ETHER_DLI,      IFEND
+  };
+  
+  #endif
+--- 162,168 ----
+  
+  struct if_family if_family[] = {
+!      ETHER_ATALK,    ETHER_DECNET,   ETHER_LAT,      ETHER_APPLE, ETHER_APPLEARP,
+       ETHER_NS,       ETHER_DLI,      IFEND
+  };
+  
+  #endif
+*** ../sys.old/data/uipc_domain_data.c Fri Jul  6 09:40:44 1990
+--- ./data/uipc_domain_data.c  Thu Mar 12 17:38:18 1992
+***************
+*** 107,112 ****
+--- 107,116 ----
+       ADDDOMAIN(ccitt);
+  #endif CCITT
+  #endif
++ #ifdef ATALK
++ #include "atalk.h"
++      ADDDOMAIN(atalk);
++ #endif ATALK
+  
+       for (dp = domains; dp; dp = dp->dom_next)
+               for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
+*** ../sys.old/net/net/conf_net.c      Fri Jul  6 10:03:25 1990
+--- ./net/net/conf_net.c       Thu Mar 12 17:39:11 1992
+***************
+*** 236,241 ****
+--- 236,245 ----
+  extern int ddpintr();
+  #endif
+  
++ #ifdef ATALK
++ extern int atintr();
++ #endif
++ 
+  #include "../net/net/netisr.h" 
+  /*
+   * table of interrupt vectors - scanned in locore when sofware 
+***************
+*** 280,285 ****
+--- 284,292 ----
+  #ifdef OSI
+          {NETISR_OSI,osiintr},
+  #endif OSI
++ #ifdef ATALK
++      {NETISR_AT,atintr},
++ #endif ATALK
+       {-1     ,0}
+  };
+  
+*** ../sys.old/net/net/netisr.h        Fri Jul  6 10:03:28 1990
+--- ./net/net/netisr.h Thu Mar 12 17:39:45 1992
+***************
+*** 66,71 ****
+--- 66,72 ----
+  #define NETISR_DLI   13              /* same as AF_DLI */
+  #define NETISR_LAT   14              /* same as AF_LAT */
+  #define NETISR_BSC   15              /* same as AF_BSC */
++ #define NETISR_AT    16              /* same as AF_APPLETALK */
+  #define NETISR_OSI      19              /* same as AF_OSI */
+  
+  #define      schednetisr(anisr)      { set_bit_atomic(anisr,&netisr); setsoftnet(); }
diff --git a/sys/ultrix/kpatch-4.2 b/sys/ultrix/kpatch-4.2
new file mode 100644 (file)
index 0000000..0c8fa8e
--- /dev/null
@@ -0,0 +1,143 @@
+*** ../sys.old/conf/files      Wed Jun 19 14:23:30 1991
+--- ./conf/files       Fri Mar  6 18:06:45 1992
+***************
+*** 75,80 ****
+--- 75,87 ----
+  net/netinet/tcp_timer.c      optional inet Binary
+  net/netinet/tcp_usrreq.c     optional inet Binary
+  net/netinet/udp_usrreq.c     optional inet Binary
++ net/netatalk/aarp.c  optional atalk
++ net/netatalk/at_control.c    optional atalk
++ net/netatalk/at_proto.c      optional atalk
++ net/netatalk/at_ultrix.c     optional atalk
++ net/netatalk/ddp_input.c     optional atalk
++ net/netatalk/ddp_output.c    optional atalk
++ net/netatalk/ddp_usrreq.c    optional atalk
+  net/netbsc/bsc_pcb.c optional bsc Binary
+  net/netbsc/bsc_proto.c       optional bsc 
+  net/netbsc/bsc_states.c      optional bsc Binary
+*** ../sys.old/data/af_data.c  Mon Apr 29 15:45:53 1991
+--- ./data/af_data.c   Sun Mar  8 22:25:15 1992
+***************
+*** 69,74 ****
+--- 69,82 ----
+  #define AFNS AFNULL
+  #endif NS
+  
++ #ifdef ATALK
++ extern int atalk_hash(), atalk_netmatch();
++ #define AFATALK \
++      { atalk_hash,   atalk_netmatch }
++ #else
++ #define AFATALK      AFNULL
++ #endif
++ 
+  #ifdef BINARY
+  
+  extern       struct afswitch afswitch[];
+***************
+*** 78,83 ****
+  struct afswitch afswitch[AF_MAX] = {
+       AFNULL, AFNULL, AFINET, AFINET, AFPUP,
+       AFNULL, AFNS,   AFNULL, AFNULL, AFNULL,
+!      AFNULL
+  };
+  #endif
+--- 86,92 ----
+  struct afswitch afswitch[AF_MAX] = {
+       AFNULL, AFNULL, AFINET, AFINET, AFPUP,
+       AFNULL, AFNS,   AFNULL, AFNULL, AFNULL,
+!      AFNULL, AFNULL, AFNULL, AFNULL, AFNULL,
+!      AFNULL, AFATALK, AFNULL, AFNULL, AFNULL,
+  };
+  #endif
+*** ../sys.old/data/if_to_proto_data.c Mon Apr 29 15:46:34 1991
+--- ./data/if_to_proto_data.c  Thu Jun  3 11:53:44 1993
+***************
+*** 125,130 ****
+--- 125,143 ----
+          { ETHERTYPE_RC,         AF_DLI,         0,              0 }
+  #endif
+  
++ /*
++  * Hook for netatalk.  We receive all packets.  If we're not interested
++  * in the packet, we do a normal search on if_family for someone else
++  * who might want the packet.
++  */
++ #ifdef ATALK
++ #include "atalk.h"
++ #undef s_net
++ #include "../net/netatalk/at.h"
++ #define ETHER_ATALK \
++      { -1,                   AF_APPLETALK,   ATPROTO_DDP,    0 }
++ #endif ATALK
++ 
+  #ifdef       BINARY
+  
+  extern struct if_family if_family[];
+***************
+*** 134,139 ****
+--- 148,156 ----
+  /* INET specific stuff is kept in drivers for now */
+  
+  struct if_family if_family[] = {
++ #ifdef ETHER_ATALK
++      ETHER_ATALK,
++ #endif
+  #ifdef ETHER_DECNET
+       ETHER_DECNET,
+  #endif
+*** ../sys.old/data/uipc_domain_data.c Mon Apr 29 15:47:08 1991
+--- ./data/uipc_domain_data.c  Mon Mar  2 15:24:28 1992
+***************
+*** 123,128 ****
+--- 123,132 ----
+       ADDDOMAIN(x25);
+  #endif XXXVNATV
+  #endif X25_DONE
++ #ifdef ATALK
++ #include "atalk.h"
++      ADDDOMAIN(atalk);
++ #endif ATALK
+  
+  #endif
+  
+*** ../sys.old/net/net/conf_net.c      Mon Apr 29 16:16:23 1991
+--- ./net/net/conf_net.c       Sun Mar  8 22:28:32 1992
+***************
+*** 233,238 ****
+--- 233,242 ----
+  extern int ddpintr();
+  #endif
+  
++ #ifdef ATALK
++ extern int atintr();
++ #endif
++ 
+  #include "../net/net/netisr.h" 
+  /*
+   * table of interrupt vectors - scanned in locore when sofware 
+***************
+*** 272,277 ****
+--- 276,284 ----
+  #if NLAT == 1
+       {NETISR_LAT,latintr},
+  #endif /* NLAT */
++ #ifdef ATALK
++      {NETISR_AT,atintr},
++ #endif ATALK
+  #ifdef APPLETALK
+       {NETISR_DDP,ddpintr},
+  #endif APPLETALK
+*** ../sys.old/net/net/netisr.h        Mon Apr 29 16:16:21 1991
+--- ./net/net/netisr.h Sun Mar  8 22:29:04 1992
+***************
+*** 72,77 ****
+--- 72,78 ----
+  #define NETISR_DLI   13              /* same as AF_DLI */
+  #define NETISR_LAT   14              /* same as AF_LAT */
+  #define NETISR_BSC   15              /* same as AF_BSC */
++ #define NETISR_AT    16              /* same as AF_APPLETALK */
+  #define NETISR_DLO      19              /* same as AF_OSI */
+  
+  #define      schednetisr(anisr)      { set_bit_atomic(anisr,&netisr); setsoftnet(); }