Cygnus implementation of GSSAPI-based ftp client and server.
authorMark Eichin <eichin@mit.edu>
Sun, 14 Jan 1996 08:33:16 +0000 (08:33 +0000)
committerMark Eichin <eichin@mit.edu>
Sun, 14 Jan 1996 08:33:16 +0000 (08:33 +0000)
See README.gssftp for history and details.
Bugs, comments to <eichin@cygnus.com>. Be sure to forward any changes
made at MIT as well.

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@7312 dc483132-0cff-0310-8789-dd5450dbe970

37 files changed:
src/appl/gssftp/ChangeLog [new file with mode: 0644]
src/appl/gssftp/Makefile.in [new file with mode: 0644]
src/appl/gssftp/README.gssftp [new file with mode: 0644]
src/appl/gssftp/arpa/ChangeLog [new file with mode: 0644]
src/appl/gssftp/arpa/ftp.h [new file with mode: 0644]
src/appl/gssftp/arpa/telnet.h [new file with mode: 0644]
src/appl/gssftp/configure.in [new file with mode: 0644]
src/appl/gssftp/ftp/ChangeLog [new file with mode: 0644]
src/appl/gssftp/ftp/Makefile.in [new file with mode: 0644]
src/appl/gssftp/ftp/cmds.c [new file with mode: 0644]
src/appl/gssftp/ftp/cmdtab.c [new file with mode: 0644]
src/appl/gssftp/ftp/configure.in [new file with mode: 0644]
src/appl/gssftp/ftp/domacro.c [new file with mode: 0644]
src/appl/gssftp/ftp/ftp.M [new file with mode: 0644]
src/appl/gssftp/ftp/ftp.c [new file with mode: 0644]
src/appl/gssftp/ftp/ftp_var.h [new file with mode: 0644]
src/appl/gssftp/ftp/getpass.c [new file with mode: 0644]
src/appl/gssftp/ftp/glob.c [new file with mode: 0644]
src/appl/gssftp/ftp/main.c [new file with mode: 0644]
src/appl/gssftp/ftp/pathnames.h [new file with mode: 0644]
src/appl/gssftp/ftp/pclose.c [new file with mode: 0644]
src/appl/gssftp/ftp/radix.c [new file with mode: 0644]
src/appl/gssftp/ftp/ruserpass.c [new file with mode: 0644]
src/appl/gssftp/ftp/secure.c [new file with mode: 0644]
src/appl/gssftp/ftp/secure.h [new file with mode: 0644]
src/appl/gssftp/ftpd/CHANGES [new file with mode: 0644]
src/appl/gssftp/ftpd/ChangeLog [new file with mode: 0644]
src/appl/gssftp/ftpd/Makefile.in [new file with mode: 0644]
src/appl/gssftp/ftpd/configure.in [new file with mode: 0644]
src/appl/gssftp/ftpd/ftpcmd.y [new file with mode: 0644]
src/appl/gssftp/ftpd/ftpd.M [new file with mode: 0644]
src/appl/gssftp/ftpd/ftpd.c [new file with mode: 0644]
src/appl/gssftp/ftpd/logwtmp.c [new file with mode: 0644]
src/appl/gssftp/ftpd/pathnames.h [new file with mode: 0644]
src/appl/gssftp/ftpd/popen.c [new file with mode: 0644]
src/appl/gssftp/ftpd/secure.h [new file with mode: 0644]
src/appl/gssftp/ftpd/vers.c [new file with mode: 0644]

diff --git a/src/appl/gssftp/ChangeLog b/src/appl/gssftp/ChangeLog
new file mode 100644 (file)
index 0000000..ea47f84
--- /dev/null
@@ -0,0 +1,9 @@
+Sun Jan 14 03:19:55 1996  Mark Eichin  <eichin@cygnus.com>
+
+       * README.gssftp: new file, documents history of this implementation.
+
+Sat Sep 30 16:28:34 1995  Mark Eichin  <eichin@cygnus.com>
+
+       * configure.in, Makefile.in: New files controlling the
+       construction of the GSSAPI implementation of ftp.
+
diff --git a/src/appl/gssftp/Makefile.in b/src/appl/gssftp/Makefile.in
new file mode 100644 (file)
index 0000000..76388e8
--- /dev/null
@@ -0,0 +1,2 @@
+CFLAGS = $(CCOPTS)
+LDFLAGS = -g
diff --git a/src/appl/gssftp/README.gssftp b/src/appl/gssftp/README.gssftp
new file mode 100644 (file)
index 0000000..e0dff52
--- /dev/null
@@ -0,0 +1,45 @@
+Notes on "Secure FTP" Implementation
+===============================================
+Mark Eichin <eichin@cygnus.com>, Cygnus Support
+last modified: 1995 Jan 14
+===============================================
+
+This implementation is supplied by Cygnus Support for inclusion in the MIT
+Kerberos V5 Release. 
+
+Copyrights:
+The original BSD ftp implementation is:
+ * Copyright (c) 1980, 1983, 1985, 1988, 1989, 1990, 1991 Regents of the
+   University of California.
+
+History and Credits (as of 1995 Jan 14)
+================================================
+
+Steve Lunt at Bellcore developed the original V4 kerberized ftp. He
+also started writing the IETF ftpsec draft at the time. This was
+available to the public, and Cygnus eventually incorporated it into
+CNS V4.
+
+Steve Lunt left Bellcore, and dropped out of the computer security
+field altogether, after handing the draft off to Marc Horowitz at
+OpenVision, who was working on a commercial GSSAPI implementation.
+
+Marc Horowitz left OpenVision and is back at MIT currently; in the
+mean time, Cygnus took the V4 ftp and upgraded it to use GSSAPI and
+draft-08, as well as integrating it into the Kerberos V5 autoconf
+based configuration scheme.
+
+Bill Schoofs <wjs@cray.com> supplied corrections to the implementation
+to more correctly match draft 8, as well as correcting some of the
+remaining KERBEROS_V4 code.
+
+Karri Balk - Contractor <kbalk@cup.hp.com> supplied additional
+corrections based on interoperation testing with non-free
+implementations.
+
+Marc Horowitz has indicated that a draft 9 is forthcoming, with some
+clarifications based on experience with this implementation.
+
+No other free implementation of draft-8 is known at this time. 
+
+
diff --git a/src/appl/gssftp/arpa/ChangeLog b/src/appl/gssftp/arpa/ChangeLog
new file mode 100644 (file)
index 0000000..41d73c0
--- /dev/null
@@ -0,0 +1,5 @@
+Sun Jan 14 01:51:18 1996  Bill Schoofs <wjs@cray.com>
+
+       * arpa/ftp.h: define PROT_E and add Confidential to levelnames,
+       so as to return more precise errors.
+
diff --git a/src/appl/gssftp/arpa/ftp.h b/src/appl/gssftp/arpa/ftp.h
new file mode 100644 (file)
index 0000000..e20285c
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 1983, 1989 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.
+ *
+ *     @(#)ftp.h       5.6 (Berkeley) 4/3/91
+ */
+
+#ifndef _FTP_H_
+#define        _FTP_H_
+
+/* Definitions for FTP; see RFC-765. */
+
+/*
+ * Reply codes.
+ */
+#define PRELIM         1       /* positive preliminary */
+#define COMPLETE       2       /* positive completion */
+#define CONTINUE       3       /* positive intermediate */
+#define TRANSIENT      4       /* transient negative completion */
+#define ERROR          5       /* permanent negative completion */
+
+/*
+ * Type codes
+ */
+#define        TYPE_A          1       /* ASCII */
+#define        TYPE_E          2       /* EBCDIC */
+#define        TYPE_I          3       /* image */
+#define        TYPE_L          4       /* local byte size */
+
+#ifdef FTP_NAMES
+char *typenames[] =  {"0", "ASCII", "EBCDIC", "Image", "Local" };
+#endif
+
+/*
+ * Form codes
+ */
+#define        FORM_N          1       /* non-print */
+#define        FORM_T          2       /* telnet format effectors */
+#define        FORM_C          3       /* carriage control (ASA) */
+#ifdef FTP_NAMES
+char *formnames[] =  {"0", "Nonprint", "Telnet", "Carriage-control" };
+#endif
+
+/*
+ * Structure codes
+ */
+#define        STRU_F          1       /* file (no record structure) */
+#define        STRU_R          2       /* record structure */
+#define        STRU_P          3       /* page structure */
+#ifdef FTP_NAMES
+char *strunames[] =  {"0", "File", "Record", "Page" };
+#endif
+
+/*
+ * Mode types
+ */
+#define        MODE_S          1       /* stream */
+#define        MODE_B          2       /* block */
+#define        MODE_C          3       /* compressed */
+#ifdef FTP_NAMES
+char *modenames[] =  {"0", "Stream", "Block", "Compressed" };
+#endif
+
+/*
+ * Protection levels
+ */
+#define        PROT_C          1       /* clear */
+#define        PROT_S          2       /* safe */
+#define        PROT_P          3       /* private */
+#define        PROT_E          4       /* confidential */
+
+#ifdef FTP_NAMES
+char *levelnames[] =  {"0", "Clear", "Safe", "Private", "Confidential" };
+#endif
+
+#if defined(KERBEROS) && defined(NOENCRYPTION)
+/* define away krb_rd_priv and krb_mk_priv.  Don't need them anyway. */
+/* This might not be the best place for this ... */
+#define krb_rd_priv(o,l,ses,s,h,c,m) krb_rd_safe(o,l,s,h,c,m)
+#define krb_mk_priv(i,o,l,ses,s,h,c) krb_mk_safe(i,o,l,s,h,c)
+#endif
+
+/*
+ * Record Tokens
+ */
+#define        REC_ESC         '\377'  /* Record-mode Escape */
+#define        REC_EOR         '\001'  /* Record-mode End-of-Record */
+#define REC_EOF                '\002'  /* Record-mode End-of-File */
+
+/*
+ * Block Header
+ */
+#define        BLK_EOR         0x80    /* Block is End-of-Record */
+#define        BLK_EOF         0x40    /* Block is End-of-File */
+#define BLK_ERRORS     0x20    /* Block is suspected of containing errors */
+#define        BLK_RESTART     0x10    /* Block is Restart Marker */
+
+#define        BLK_BYTECOUNT   2       /* Bytes in this block */
+
+#endif /* !_FTP_H_ */
diff --git a/src/appl/gssftp/arpa/telnet.h b/src/appl/gssftp/arpa/telnet.h
new file mode 100644 (file)
index 0000000..019bfeb
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+ * 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.
+ *
+ *     @(#)telnet.h    5.14 (Berkeley) 4/3/91
+ */
+
+#ifndef _TELNET_H_
+#define        _TELNET_H_
+
+/*
+ * Definitions for the TELNET protocol.
+ */
+#define        IAC     255             /* interpret as command: */
+#define        DONT    254             /* you are not to use option */
+#define        DO      253             /* please, you use option */
+#define        WONT    252             /* I won't use option */
+#define        WILL    251             /* I will use option */
+#define        SB      250             /* interpret as subnegotiation */
+#define        GA      249             /* you may reverse the line */
+#define        EL      248             /* erase the current line */
+#define        EC      247             /* erase the current character */
+#define        AYT     246             /* are you there */
+#define        AO      245             /* abort output--but let prog finish */
+#define        IP      244             /* interrupt process--permanently */
+#define        BREAK   243             /* break */
+#define        DM      242             /* data mark--for connect. cleaning */
+#define        NOP     241             /* nop */
+#define        SE      240             /* end sub negotiation */
+#define EOR     239             /* end of record (transparent mode) */
+#define        ABORT   238             /* Abort process */
+#define        SUSP    237             /* Suspend process */
+#define        xEOF    236             /* End of file: EOF is already used... */
+
+#define SYNCH  242             /* for telfunc calls */
+
+#ifdef TELCMDS
+char *telcmds[] = {
+       "EOF", "SUSP", "ABORT", "EOR",
+       "SE", "NOP", "DMARK", "BRK", "IP", "AO", "AYT", "EC",
+       "EL", "GA", "SB", "WILL", "WONT", "DO", "DONT", "IAC", 0,
+};
+#else
+extern char *telcmds[];
+#endif
+
+#define        TELCMD_FIRST    xEOF
+#define        TELCMD_LAST     IAC
+#define        TELCMD_OK(x)    ((x) <= TELCMD_LAST && (x) >= TELCMD_FIRST)
+#define        TELCMD(x)       telcmds[(x)-TELCMD_FIRST]
+
+/* telnet options */
+#define TELOPT_BINARY  0       /* 8-bit data path */
+#define TELOPT_ECHO    1       /* echo */
+#define        TELOPT_RCP      2       /* prepare to reconnect */
+#define        TELOPT_SGA      3       /* suppress go ahead */
+#define        TELOPT_NAMS     4       /* approximate message size */
+#define        TELOPT_STATUS   5       /* give status */
+#define        TELOPT_TM       6       /* timing mark */
+#define        TELOPT_RCTE     7       /* remote controlled transmission and echo */
+#define TELOPT_NAOL    8       /* negotiate about output line width */
+#define TELOPT_NAOP    9       /* negotiate about output page size */
+#define TELOPT_NAOCRD  10      /* negotiate about CR disposition */
+#define TELOPT_NAOHTS  11      /* negotiate about horizontal tabstops */
+#define TELOPT_NAOHTD  12      /* negotiate about horizontal tab disposition */
+#define TELOPT_NAOFFD  13      /* negotiate about formfeed disposition */
+#define TELOPT_NAOVTS  14      /* negotiate about vertical tab stops */
+#define TELOPT_NAOVTD  15      /* negotiate about vertical tab disposition */
+#define TELOPT_NAOLFD  16      /* negotiate about output LF disposition */
+#define TELOPT_XASCII  17      /* extended ascic character set */
+#define        TELOPT_LOGOUT   18      /* force logout */
+#define        TELOPT_BM       19      /* byte macro */
+#define        TELOPT_DET      20      /* data entry terminal */
+#define        TELOPT_SUPDUP   21      /* supdup protocol */
+#define        TELOPT_SUPDUPOUTPUT 22  /* supdup output */
+#define        TELOPT_SNDLOC   23      /* send location */
+#define        TELOPT_TTYPE    24      /* terminal type */
+#define        TELOPT_EOR      25      /* end or record */
+#define        TELOPT_TUID     26      /* TACACS user identification */
+#define        TELOPT_OUTMRK   27      /* output marking */
+#define        TELOPT_TTYLOC   28      /* terminal location number */
+#define        TELOPT_3270REGIME 29    /* 3270 regime */
+#define        TELOPT_X3PAD    30      /* X.3 PAD */
+#define        TELOPT_NAWS     31      /* window size */
+#define        TELOPT_TSPEED   32      /* terminal speed */
+#define        TELOPT_LFLOW    33      /* remote flow control */
+#define TELOPT_LINEMODE        34      /* Linemode option */
+#define TELOPT_XDISPLOC        35      /* X Display Location */
+#define TELOPT_ENVIRON 36      /* Environment variables */
+#define        TELOPT_AUTHENTICATION 37/* Authenticate */
+#define        TELOPT_ENCRYPT  38      /* Encryption option */
+#define        TELOPT_EXOPL    255     /* extended-options-list */
+
+
+#define        NTELOPTS        (1+TELOPT_ENCRYPT)
+#ifdef TELOPTS
+char *telopts[NTELOPTS+1] = {
+       "BINARY", "ECHO", "RCP", "SUPPRESS GO AHEAD", "NAME",
+       "STATUS", "TIMING MARK", "RCTE", "NAOL", "NAOP",
+       "NAOCRD", "NAOHTS", "NAOHTD", "NAOFFD", "NAOVTS",
+       "NAOVTD", "NAOLFD", "EXTEND ASCII", "LOGOUT", "BYTE MACRO",
+       "DATA ENTRY TERMINAL", "SUPDUP", "SUPDUP OUTPUT",
+       "SEND LOCATION", "TERMINAL TYPE", "END OF RECORD",
+       "TACACS UID", "OUTPUT MARKING", "TTYLOC",
+       "3270 REGIME", "X.3 PAD", "NAWS", "TSPEED", "LFLOW",
+       "LINEMODE", "XDISPLOC", "ENVIRON", "AUTHENTICATION",
+       "ENCRYPT",
+       0,
+};
+#define        TELOPT_FIRST    TELOPT_BINARY
+#define        TELOPT_LAST     TELOPT_ENCRYPT
+#define        TELOPT_OK(x)    ((x) <= TELOPT_LAST && (x) >= TELOPT_FIRST)
+#define        TELOPT(x)       telopts[(x)-TELOPT_FIRST]
+#endif
+
+/* sub-option qualifiers */
+#define        TELQUAL_IS      0       /* option is... */
+#define        TELQUAL_SEND    1       /* send option */
+#define        TELQUAL_INFO    2       /* ENVIRON: informational version of IS */
+#define        TELQUAL_REPLY   2       /* AUTHENTICATION: client version of IS */
+#define        TELQUAL_NAME    3       /* AUTHENTICATION: client version of IS */
+
+/*
+ * LINEMODE suboptions
+ */
+
+#define        LM_MODE         1
+#define        LM_FORWARDMASK  2
+#define        LM_SLC          3
+
+#define        MODE_EDIT       0x01
+#define        MODE_TRAPSIG    0x02
+#define        MODE_ACK        0x04
+#define MODE_SOFT_TAB  0x08
+#define MODE_LIT_ECHO  0x10
+
+#define        MODE_MASK       0x1f
+
+/* Not part of protocol, but needed to simplify things... */
+#define MODE_FLOW              0x0100
+#define MODE_ECHO              0x0200
+#define MODE_INBIN             0x0400
+#define MODE_OUTBIN            0x0800
+#define MODE_FORCE             0x1000
+
+#define        SLC_SYNCH       1
+#define        SLC_BRK         2
+#define        SLC_IP          3
+#define        SLC_AO          4
+#define        SLC_AYT         5
+#define        SLC_EOR         6
+#define        SLC_ABORT       7
+#define        SLC_EOF         8
+#define        SLC_SUSP        9
+#define        SLC_EC          10
+#define        SLC_EL          11
+#define        SLC_EW          12
+#define        SLC_RP          13
+#define        SLC_LNEXT       14
+#define        SLC_XON         15
+#define        SLC_XOFF        16
+#define        SLC_FORW1       17
+#define        SLC_FORW2       18
+
+#define        NSLC            18
+
+/*
+ * For backwards compatability, we define SLC_NAMES to be the
+ * list of names if SLC_NAMES is not defined.
+ */
+#define        SLC_NAMELIST    "0", "SYNCH", "BRK", "IP", "AO", "AYT", "EOR", \
+                       "ABORT", "EOF", "SUSP", "EC", "EL", "EW", "RP", \
+                       "LNEXT", "XON", "XOFF", "FORW1", "FORW2", 0,
+#ifdef SLC_NAMES
+char *slc_names[] = {
+       SLC_NAMELIST
+};
+#else
+extern char *slc_names[];
+#define        SLC_NAMES SLC_NAMELIST
+#endif
+
+#define        SLC_NAME_OK(x)  ((x) >= 0 && (x) < NSLC)
+#define SLC_NAME(x)    slc_names[x]
+
+#define        SLC_NOSUPPORT   0
+#define        SLC_CANTCHANGE  1
+#define        SLC_VARIABLE    2
+#define        SLC_DEFAULT     3
+#define        SLC_LEVELBITS   0x03
+
+#define        SLC_FUNC        0
+#define        SLC_FLAGS       1
+#define        SLC_VALUE       2
+
+#define        SLC_ACK         0x80
+#define        SLC_FLUSHIN     0x40
+#define        SLC_FLUSHOUT    0x20
+
+#define        ENV_VALUE       0
+#define        ENV_VAR         1
+#define        ENV_ESC         2
+
+/*
+ * AUTHENTICATION suboptions
+ */
+
+/*
+ * Who is authenticating who ...
+ */
+#define        AUTH_WHO_CLIENT         0       /* Client authenticating server */
+#define        AUTH_WHO_SERVER         1       /* Server authenticating client */
+#define        AUTH_WHO_MASK           1
+
+/*
+ * amount of authentication done
+ */
+#define        AUTH_HOW_ONE_WAY        0
+#define        AUTH_HOW_MUTUAL         2
+#define        AUTH_HOW_MASK           2
+
+#define        AUTHTYPE_NULL           0
+#define        AUTHTYPE_KERBEROS_V4    1
+#define        AUTHTYPE_KERBEROS_V5    2
+#define        AUTHTYPE_SPX            3
+#define        AUTHTYPE_MINK           4
+#define        AUTHTYPE_CNT            5
+
+#define        AUTHTYPE_TEST           99
+
+#ifdef AUTH_NAMES
+char *authtype_names[] = {
+       "NULL", "KERBEROS_V4", "KERBEROS_V5", "SPX", "MINK", 0,
+};
+#else
+extern char *authtype_names[];
+#endif
+
+#define        AUTHTYPE_NAME_OK(x)     ((x) >= 0 && (x) < AUTHTYPE_CNT)
+#define        AUTHTYPE_NAME(x)        authtype_names[x]
+
+/*
+ * ENCRYPTion suboptions
+ */
+#define        ENCRYPT_IS              0       /* I pick encryption type ... */
+#define        ENCRYPT_SUPPORT         1       /* I support encryption types ... */
+#define        ENCRYPT_REPLY           2       /* Initial setup response */
+#define        ENCRYPT_START           3       /* Am starting to send encrypted */
+#define        ENCRYPT_END             4       /* Am ending encrypted */
+#define        ENCRYPT_REQSTART        5       /* Request you start encrypting */
+#define        ENCRYPT_REQEND          6       /* Request you send encrypting */
+#define        ENCRYPT_ENC_KEYID       7
+#define        ENCRYPT_DEC_KEYID       8
+#define        ENCRYPT_CNT             9
+
+#define        ENCTYPE_ANY             0
+#define        ENCTYPE_DES_CFB64       1
+#define        ENCTYPE_DES_OFB64       2
+#define        ENCTYPE_CNT             3
+
+#ifdef ENCRYPT_NAMES
+char *encrypt_names[] = {
+       "IS", "SUPPORT", "REPLY", "START", "END",
+       "REQUEST-START", "REQUEST-END", "ENC-KEYID", "DEC-KEYID",
+       0,
+};
+char *enctype_names[] = {
+       "ANY", "DES_CFB64",  "DES_OFB64",  0,
+};
+#else
+extern char *encrypt_names[];
+extern char *enctype_names[];
+#endif
+
+
+#define        ENCRYPT_NAME_OK(x)      ((x) >= 0 && (x) < ENCRYPT_CNT)
+#define        ENCRYPT_NAME(x)         encrypt_names[x]
+
+#define        ENCTYPE_NAME_OK(x)      ((x) >= 0 && (x) < ENCTYPE_CNT)
+#define        ENCTYPE_NAME(x)         enctype_names[x]
+
+#endif /* !_TELNET_H_ */
diff --git a/src/appl/gssftp/configure.in b/src/appl/gssftp/configure.in
new file mode 100644 (file)
index 0000000..373180b
--- /dev/null
@@ -0,0 +1,5 @@
+AC_INIT(configure.in)
+CONFIG_RULES
+CONFIG_DIRS(ftp ftpd)
+DO_SUBDIRS
+V5_AC_OUTPUT_MAKEFILE
diff --git a/src/appl/gssftp/ftp/ChangeLog b/src/appl/gssftp/ftp/ChangeLog
new file mode 100644 (file)
index 0000000..f1ff4ff
--- /dev/null
@@ -0,0 +1,219 @@
+Sun Jan 14 01:54:35 1996  Bill Schoofs <wjs@cray.com>
+
+       * Makefile.in (DEFINES): define NOCONFIDENTIAL for future use.
+       * ftp.c (command): recognize 533, not 402, for 'server unwilling
+       to accept' 
+       (getreply): recognize 633 for confidential reply, and then don't
+       support it.
+       
+Tue Jan  2 19:17:47 1996  Mark Eichin  <eichin@cygnus.com>
+
+       * pclose.c: test HAVE_GETDTABLESIZE instead of hpux.
+       * configure.in: set HAVE_GETDTABLESIZE.
+
+Fri Oct 20 11:59:32 1995  Mark W. Eichin  <eichin@cygnus.com>
+
+       * ftp.c (do_auth): synthesize channel bindings from myctladdr and
+       hisctladdr, based on changes pending for draft 9.
+
+Thu Oct 19 04:47:36 1995  Mark W. Eichin  <eichin@cygnus.com>
+
+       * configure.in: check for POSIX_TERMIOS just like appl/bsd does.
+
+Wed Oct  4 19:24:39 1995  Mark Eichin  <eichin@cygnus.com>
+
+       * ftp, pclose, ruserpass.c, secure.c: don't ever declare malloc.
+
+Sun Oct  1 03:30:30 1995  Mark Eichin  <eichin@cygnus.com>
+
+       * ftp.c (do_auth): accept ADAT 3yz response. Clean up loops, add
+       lots of debugging messages.
+
+Sun Oct  1 00:56:55 1995  Mark Eichin  <eichin@cygnus.com>
+
+       * Makefile.in: use FTP_BUFSIZ everywhere and make it large for
+       now.
+       * ftp.c: only look at "host" for now. Report error parsing
+       name. Handle gssapi error reporting better.
+       
+
+Sat Sep 30 22:26:37 1995  Mark Eichin  <eichin@cygnus.com>
+
+       * ftp.c, secure.c: correct gssapi includes. Fix some typos and
+       missing declarations.
+
+Sat Sep 30 21:31:09 1995  Mark Eichin  <eichin@cygnus.com>
+
+       * Makefile.in (depend, install): change to double colon rules.
+       * cmds.c: no conf.h, check HAVE_GETCWD, use krb5_sigtype.
+       * configure.in: check KRB5_SIGTYPE, CHECK_SIGPROCMASK,
+       CHECK_WAIT_TYPE, and getcwd.
+       * ftp.c: no conf.h, use krb5_sigtype.
+       (secure_command): use user_gss_error.
+       (do_auth): cycle through gss_services.
+       (user_gss_error): new function, decomposes GSSAPI errors and sends
+       them to standard error.
+       (secure_gss_error): hook for secure.c common functions to get the
+       correct error routine.
+       * getpass.c, main.c: no conf.h, use krb5_sigtype.
+       * pclose.c: no conf.h, use krb5_sigtype.
+       (mypclose): obey USE_SIGPROCMASK.
+       * secure.c (secure_getbyte): use generic secure_gss_error.
+       
+Sat Sep 30 16:43:28 1995  Mark Eichin  <eichin@cygnus.com>
+
+       * configure.in, Makefile.in: new files for port to GSSAPI and
+       build within the Kerberos V5 build tree.
+       * ftp.c, secure.c: GSSAPI authentication changes based on the IETF
+       CAT working group ***DRAFT*** FTP Security specification, draft
+       number 8, appendix I.
+
+
+**** previous change logs from CNS V4 modifications of Steve Lunt's
+     draft-3 ftp client, which this is based on. ****
+       
+Wed Jul 26 21:01:42 1995  Ken Raeburn  <raeburn@cygnus.com>
+
+       * domacro.c: Include string.h.
+       * secure.c: Ditto.
+
+Mon Jul 10 14:54:41 1995  Michael Meissner  <meissner@tiktok.cygnus.com>
+
+       * glob.c (matchdir): #if 0 code that uses a private member of the
+       DIR structure to check whether a filename passed to opendir is a
+       directory or not.
+
+Fri May 26 19:36:12 1995  Mark Eichin  <eichin@cygnus.com>
+
+       * glob.c (matchdir): open "." explicitly if gpath is null.
+
+Fri May 19 16:11:07 1995  Mark Eichin  <eichin@cygnus.com>
+
+       * ftp.c (ptransfer): be sure that printf(%g) gets a float, not an
+       implicit double, by doing the whole calculation in the variable.
+
+Thu Feb  2 13:40:04 1995  Ian Lance Taylor  <ian@cygnus.com>
+
+       * ftp.c: Don't try to use IP_TOS if the IP_TOS argument
+       (IPTOS_LOWDELAY, etc.) is not defined.
+
+Wed Jan 18 14:07:33 1995  Ian Lance Taylor  <ian@sanguine.cygnus.com>
+
+       * ftp.1: Include man1/tmac.doc.
+
+       * ftp.c (initconn): If the PASV command is rejected, turn off
+       passive mode and try again.
+
+Wed Jan  4 11:21:34 1995  Ian Lance Taylor  <ian@tweedledumb.cygnus.com>
+
+       * cmds.c, ftp.c: Use mygetpass instead of getpass.
+
+       * ruserpass.c: Don't include <utmp.h>.  Don't declare getlogin,
+       getpass, or getuttmp.
+
+Thu Dec 29 15:19:44 1994  Mark Eichin  <eichin@cygnus.com>
+
+       * cmds.c (setpeer): add || defined(linux) to the NBBY == 8 check,
+       since this code is appropriate under linux.
+
+Thu Dec 29 14:11:37 1994  Mark Eichin  <eichin@cygnus.com>
+
+       * cmds.c (siteidle): renamed idle() to avoid conflict with linux
+       idle(void).
+       * cmdtab.c: rename declaration and cmdtab entry.
+
+Tue Dec 27 13:29:08 1994  Ian Lance Taylor  <ian@sanguine.cygnus.com>
+
+       * ftp.c: If STDARG is defined, or if __STDC__ is defined and
+       VARARGS is not defined, include <stdarg.h>, instead of
+       <varargs.h>.
+       (command): Use <stdarg.h> routines if STDARG || (__STDC__ && !
+       VARARGS).
+       (secure_error): Likewise.
+       * ftp_var.h (command): Declare if STDARG || (__STDC__ && !
+       VARARGS).
+       * secure.c (secure_error): Likewise.
+
+       * secure.h (hisaddr): Define as hisdataaddr.
+       * ftp.c (hisdataaddr): New global variable.
+       (initconn): Set hisdataaddr to data_addr.
+       (dataconn): Use hisdataaddr instead of local variable from.
+
+Fri Dec 23 15:18:12 1994  Ian Lance Taylor  <ian@sanguine.cygnus.com>
+
+       * cmds.c (unix): Define if _AIX is defined (AIX compiler does not
+       predefine unix).
+
+       * ftp.c (login): When choosing the default login name, use the
+       values of the environment variables LOGNAME and then USER in
+       preference to calling getlogin.
+
+Thu Dec 22 14:59:34 1994  Ian Lance Taylor  <ian@sanguine.cygnus.com>
+
+       * cmds.c (gettype): Sometimes type will be zero, which requires
+       special handling.
+
+       * main.c: Include <krb.h>.
+       (main): Support new option: -k.
+       * ftp.c (realm): New global variable.
+       (do_auth): Remove local variable realm; use new global instead.
+       Don't call krb_realmofhost if realm is set.
+       * ftp.1: Document -k.
+
+Fri Dec 16 10:53:08 1994  Ian Lance Taylor  <ian@cygnus.com>
+
+       Fixes for Alpha OSF/1:
+       * cmds.c: Redefine sig_t to my_sig_t to avoid header file
+       conflict.
+       * ftp.c: Likewise.
+
+       Fixes for SCO:
+       * cmdtab.c: Include <stdio.h> before ftp_var.h.
+       * domacro.c: Move include of <stdio.h> before include of
+       ftp_var.h.  Don't include <sys/ttychars.h>.
+       * main.c: Move include of <stdio.h> before include of ftp_var.h.
+
+       Fixes for AIX:
+       * cmds.c (mput): Use 0 instead of NULL when an integer is
+       expected.
+       (getit, mget): Likewise.
+       * ftp_var.h (strncpy, strncat, strcat, strcpy): Don't declare.
+       * ruserpass.c (strcpy): Don't declare.
+       * secure.c: Include <netinet/in.h>.
+
+       Fixes for Irix 4:
+       * ftp_var.h: Unless DEFINITIONS is defined, declare variables
+       rather than defining them.
+       * ftp.c: Define DEFINITIONS before including ftp_var.h.
+       (recvrequest): If NOSTBLKSIZE is defined, use BUFSIZ instead of
+       st_blksize.
+       * getpass.c: Put note after #endif in /* */
+       * pclose.c: Likewise.
+       * ruserpass.c (token): Move before ruserpass.
+       (ruserpass): Don't declare token.
+
+       General fixes to make it compile on Solaris: Use sigtype for
+       signal handler return values, including conf.h where needed.  Add
+       a dummy argument to signal handler functions. Replace index,
+       rindex, bzero and bcopy with ANSI C functions.  Cast Kerberos
+       routine arguments to avoid warnings.  Also:
+       * cmds.c: Include <string.h>.  If POSIX is defined, include
+       unistd.h, otherwise define getcwd to call getwd.
+       (lcd): Call getcwd instead of getwd.
+       (shell): If WAIT_USES_INT, use int instead of union wait.
+       * ftp.c: Include <string.h>.
+       (L_SET, L_INCR): Define if not defined.
+
+       * ftp_var.h (index, rindex): Don't declare.
+       * main.c: Inclue <string.h>.
+       * pclose.c (getdtablesize): New function on hpux or __svr4__.
+       * radix.c (radix_encode): Cast strcmp arguments to avoid warnings.
+       * ruserpass.c: Include <string.h>.  If POSIX, include <stdlib.h>
+       and don't declare malloc.
+       (MAXHOSTNAMELEN): Define if not defined.
+       (index): Don't declare.
+
+Thu Dec 15 16:13:44 1994  Ian Lance Taylor  <ian@sanguine.cygnus.com>
+
+       * Initial checkin.  Based on Steve Lunt's ftp program, which was
+        based on BSD code.
diff --git a/src/appl/gssftp/ftp/Makefile.in b/src/appl/gssftp/ftp/Makefile.in
new file mode 100644 (file)
index 0000000..06ee2da
--- /dev/null
@@ -0,0 +1,52 @@
+#
+# appl/gssftp/ftp/Makefile.in
+#
+CFLAGS = -DGSSAPI -DFTP_BUFSIZ=10240 $(CCOPTS) $(DEFS) $(LOCALINCLUDE)
+
+COMERRLIB=$(BUILDTOP)/util/et/libcom_err.a
+
+SRCS   = cmds.c cmdtab.c domacro.c ftp.c getpass.c glob.c main.c pclose.c \
+         radix.c ruserpass.c secure.c 
+
+
+OBJS   = cmds.o cmdtab.o domacro.o ftp.o getpass.o glob.o main.o pclose.o \
+         radix.o ruserpass.o secure.o
+
+KLIB = -lgssapi_krb5 -lkrb5 -lcrypto $(COMERRLIB)
+DEPKLIB = $(TOPLIBD)/gssapi/libgssapi_krb5.a $(TOPLIBD)/libkrb5.a \
+               $(TOPLIBD)/libcrypto.a $(COMERRLIB)
+
+LOCALINCLUDE = -I$(srcdir)/..
+DEFINES = -DGSSAPI -DNOCONFIDENTIAL
+
+all::  ftp
+
+ftp:   $(OBJS) $(DEPKLIB)
+       $(LD) $(LDFLAGS) $(LDARGS) -o ftp $(OBJS) $(KLIB) $(LIBS)
+
+clean::
+       $(RM) ftp
+
+depend::
+
+install::
+       $(INSTALL_PROGRAM) ftp $(DESTDIR)$(CLIENT_BINDIR)/ftp
+       $(INSTALL_DATA) $(srcdir)/ftp.M ${DESTDIR}$(CLIENT_MANDIR)/ftp.1
+
+ftp.o cmds.o main.o:   $(srcdir)/../arpa/ftp.h
+ftp.o cmds.o cmdtab.o domacro.o main.o ruserpass.o: $(srcdir)/ftp_var.h
+secure.o: secure.h
+
+cmds.o: $(srcdir)/cmds.c
+cmdtab.o: $(srcdir)/cmdtab.c
+ftp.o: $(srcdir)/ftp.c
+getpass.o: $(srcdir)/getpass.c
+glob.o: $(srcdir)/glob.c
+main.o: $(srcdir)/main.c
+pclose.o: $(srcdir)/pclose.c
+ruserpass.o: $(srcdir)/ruserpass.c
+domacro.o: $(srcdir)/domacro.c
+radix.o: $(srcdir)/radix.c
+secure.o: $(srcdir)/secure.c
+
+# NOPOSTFIX
diff --git a/src/appl/gssftp/ftp/cmds.c b/src/appl/gssftp/ftp/cmds.c
new file mode 100644 (file)
index 0000000..de7e4b1
--- /dev/null
@@ -0,0 +1,2337 @@
+/*
+ * Copyright (c) 1985, 1989 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[] = "@(#)cmds.c     5.26 (Berkeley) 3/5/91";
+#endif /* not lint */
+
+/*
+ * FTP User Program -- Command Routines.
+ */
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+
+#include <arpa/ftp.h>
+
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <time.h>
+#include <netinet/in.h>
+
+#ifdef HAVE_GETCWD
+#define getwd(x) getcwd(x,MAXPATHLEN)
+#endif
+
+#include "ftp_var.h"
+#include "pathnames.h"
+
+#define sig_t my_sig_t
+#define sigtype krb5_sigtype
+typedef sigtype (*sig_t)();
+
+extern char *globerr;
+extern char **ftpglob();
+extern char *home;
+extern char *remglob();
+extern char *getenv();
+#define strerror(error) (sys_errlist[error])
+extern char *sys_errlist[];
+extern int  errno;
+extern off_t restart_point;
+extern char reply_string[];
+
+char *mname;
+jmp_buf jabort;
+char *dotrans(), *domap();
+
+extern char *auth_type;
+extern int do_auth();
+
+/*
+ * `Another' gets another argument, and stores the new argc and argv.
+ * It reverts to the top level (via main.c's intr()) on EOF/error.
+ *
+ * Returns false if no new arguments have been added.
+ */
+another(pargc, pargv, prompt)
+       int *pargc;
+       char ***pargv;
+       char *prompt;
+{
+       int len = strlen(line), ret;
+       extern sig_t intr();
+
+       if (len >= sizeof(line) - 3) {
+               printf("sorry, arguments too long\n");
+               intr();
+       }
+       printf("(%s) ", prompt);
+       line[len++] = ' ';
+       if (fgets(&line[len], sizeof(line) - len, stdin) == NULL)
+               intr();
+       len += strlen(&line[len]);
+       if (len > 0 && line[len - 1] == '\n')
+               line[len - 1] = '\0';
+       makeargv();
+       ret = margc > *pargc;
+       *pargc = margc;
+       *pargv = margv;
+       return (ret);
+}
+
+/*
+ * Connect to peer server and
+ * auto-login, if possible.
+ */
+setpeer(argc, argv)
+       int argc;
+       char *argv[];
+{
+       char *host, *hookup();
+       short port;
+
+       if (connected) {
+               printf("Already connected to %s, use close first.\n",
+                       hostname);
+               code = -1;
+               return;
+       }
+       if (argc < 2)
+               (void) another(&argc, &argv, "to");
+       if (argc < 2 || argc > 3) {
+               printf("usage: %s host-name [port]\n", argv[0]);
+               code = -1;
+               return;
+       }
+       port = sp->s_port;
+       if (argc > 2) {
+               port = atoi(argv[2]);
+               if (port <= 0) {
+                       printf("%s: bad port number-- %s\n", argv[1], argv[2]);
+                       printf ("usage: %s host-name [port]\n", argv[0]);
+                       code = -1;
+                       return;
+               }
+               port = htons(port);
+       }
+       host = hookup(argv[1], port);
+       if (host) {
+               int overbose;
+
+               connected = 1;
+               /*
+                * Set up defaults for FTP.
+                */
+               level = PROT_C;
+               type = TYPE_A;
+               curtype = TYPE_A;
+               form = FORM_N;
+               mode = MODE_S;
+               stru = STRU_F;
+               (void) strcpy(bytename, "8"), bytesize = 8;
+               if (autologin)
+                       (void) login(argv[1]);
+
+               if (0) {
+                 setpbsz(1<<20);
+                 level = PROT_P;
+                 if (command("PROT P") != COMPLETE)
+                   fprintf(stderr, "auto PROT P setting failed\n");
+               }
+
+#ifndef unix
+#ifdef _AIX
+#define unix
+#endif
+#endif
+
+#if defined(unix) && (NBBY == 8 || defined(linux))
+/*
+ * this ifdef is to keep someone form "porting" this to an incompatible
+ * system and not checking this out. This way they have to think about it.
+ */
+               overbose = verbose;
+               if (debug == 0)
+                       verbose = -1;
+               if (command("SYST") == COMPLETE && overbose) {
+                       register char *cp, c;
+                       cp = strchr(reply_string+4, ' ');
+                       if (cp == NULL)
+                               cp = strchr(reply_string+4, '\r');
+                       if (cp) {
+                               if (cp[-1] == '.')
+                                       cp--;
+                               c = *cp;
+                               *cp = '\0';
+                       }
+
+                       printf("Remote system type is %s.\n",
+                               reply_string+4);
+                       if (cp)
+                               *cp = c;
+               }
+               if (!strncmp(reply_string, "215 UNIX Type: L8", 17)) {
+                       if (proxy)
+                               unix_proxy = 1;
+                       else
+                               unix_server = 1;
+                       /*
+                        * Set type to 0 (not specified by user),
+                        * meaning binary by default, but don't bother
+                        * telling server.  We can use binary
+                        * for text files unless changed by the user.
+                        */
+                       type = 0;
+                       if (overbose)
+                           printf("Using %s mode to transfer files.\n",
+                               "binary");
+               } else {
+                       if (proxy)
+                               unix_proxy = 0;
+                       else
+                               unix_server = 0;
+                       if (overbose && 
+                           !strncmp(reply_string, "215 TOPS20", 10))
+                               printf(
+"Remember to set tenex mode when transfering binary files from this machine.\n");
+               }
+               verbose = overbose;
+#endif /* unix */
+       }
+}
+
+struct levels {
+       char    *p_name;
+       char    *p_mode;
+       int     p_level;
+} levels[] = {
+       { "clear",      "C",    PROT_C },
+       { "safe",       "S",    PROT_S },
+#ifndef NOENCRYPTION
+       { "private",    "P",    PROT_P },
+#endif
+       0
+};
+
+char *
+getlevel()
+{
+       register struct levels *p;
+
+       for (p = levels; p->p_level != level; p++);
+       return(p->p_name);
+}
+
+/*
+ * Set protection level.
+ */
+setlevel(argc, argv)
+       char *argv[];
+{
+       register struct levels *p;
+       int comret;
+
+       if (argc > 2) {
+               char *sep;
+
+               printf("usage: %s [", argv[0]);
+               sep = " ";
+               for (p = levels; p->p_name; p++) {
+                       printf("%s%s", sep, p->p_name);
+                       if (*sep == ' ')
+                               sep = " | ";
+               }
+               printf(" ]\n");
+               code = -1;
+               return;
+       }
+       if (argc < 2) {
+               printf("Using %s protection level to transfer files.\n",
+                       getlevel());
+               code = 0;
+               return;
+       }
+       for (p = levels; p->p_name; p++)
+               if (strcmp(argv[1], p->p_name) == 0)
+                       break;
+       if (p->p_name == 0) {
+               printf("%s: unknown protection level\n", argv[1]);
+               code = -1;
+               return;
+       }
+       if (!auth_type) {
+               if (strcmp(p->p_name, "clear"))
+                       printf("Cannot set protection level to %s\n", argv[1]);
+               return;
+       }
+       /* Start with a PBSZ of 1 meg */
+       if (p->p_level != PROT_C) setpbsz(1<<20);
+       comret = command("PROT %s", p->p_mode);
+       if (comret == COMPLETE)
+               level = p->p_level;
+}
+
+char *plevel[] = {
+       "protect",
+       "",
+       0
+};
+
+/*
+ * Set clear protection level.
+ */
+/*VARARGS*/
+setclear()
+{
+       plevel[1] = "clear";
+       setlevel(2, plevel);
+}
+
+/*
+ * Set safe protection level.
+ */
+/*VARARGS*/
+setsafe()
+{
+       plevel[1] = "safe";
+       setlevel(2, plevel);
+}
+
+#ifndef NOENCRYPTION
+/*
+ * Set private protection level.
+ */
+/*VARARGS*/
+setprivate()
+{
+       plevel[1] = "private";
+       setlevel(2, plevel);
+}
+#endif
+
+struct types {
+       char    *t_name;
+       char    *t_mode;
+       int     t_type;
+       char    *t_arg;
+} types[] = {
+       { "ascii",      "A",    TYPE_A, 0 },
+       { "binary",     "I",    TYPE_I, 0 },
+       { "image",      "I",    TYPE_I, 0 },
+       { "ebcdic",     "E",    TYPE_E, 0 },
+       { "tenex",      "L",    TYPE_L, bytename },
+       0
+};
+
+char *
+gettype()
+{
+       register struct types *p;
+       int t;
+
+       t = type;
+       if (t == 0)
+               t = TYPE_I;
+       for (p = types; p->t_type != t; p++);
+       return(p->t_name);
+}
+
+/*
+ * Set transfer type.
+ */
+settype(argc, argv)
+       int argc;
+       char *argv[];
+{
+       register struct types *p;
+       int comret;
+
+       if (argc > 2) {
+               char *sep;
+
+               printf("usage: %s [", argv[0]);
+               sep = " ";
+               for (p = types; p->t_name; p++) {
+                       printf("%s%s", sep, p->t_name);
+                       sep = " | ";
+               }
+               printf(" ]\n");
+               code = -1;
+               return;
+       }
+       if (argc < 2) {
+               printf("Using %s mode to transfer files.\n", gettype());
+               code = 0;
+               return;
+       }
+       for (p = types; p->t_name; p++)
+               if (strcmp(argv[1], p->t_name) == 0)
+                       break;
+       if (p->t_name == 0) {
+               printf("%s: unknown mode\n", argv[1]);
+               code = -1;
+               return;
+       }
+       if ((p->t_arg != NULL) && (*(p->t_arg) != '\0'))
+               comret = command ("TYPE %s %s", p->t_mode, p->t_arg);
+       else
+               comret = command("TYPE %s", p->t_mode);
+       if (comret == COMPLETE)
+               curtype = type = p->t_type;
+}
+
+/*
+ * Internal form of settype; changes current type in use with server
+ * without changing our notion of the type for data transfers.
+ * Used to change to and from ascii for listings.
+ */
+changetype(newtype, show)
+       int newtype, show;
+{
+       register struct types *p;
+       int comret, oldverbose = verbose;
+
+       if (newtype == 0)
+               newtype = TYPE_I;
+       if (newtype == curtype)
+               return;
+       if (debug == 0 && show == 0)
+               verbose = 0;
+       for (p = types; p->t_name; p++)
+               if (newtype == p->t_type)
+                       break;
+       if (p->t_name == 0) {
+               printf("ftp: internal error: unknown type %d\n", newtype);
+               return;
+       }
+       if (newtype == TYPE_L && bytename[0] != '\0')
+               comret = command("TYPE %s %s", p->t_mode, bytename);
+       else
+               comret = command("TYPE %s", p->t_mode);
+       if (comret == COMPLETE)
+               curtype = newtype;
+       verbose = oldverbose;
+}
+
+char *stype[] = {
+       "type",
+       "",
+       0
+};
+
+/*
+ * Set binary transfer type.
+ */
+/*VARARGS*/
+setbinary()
+{
+       stype[1] = "binary";
+       settype(2, stype);
+}
+
+/*
+ * Set ascii transfer type.
+ */
+/*VARARGS*/
+setascii()
+{
+       stype[1] = "ascii";
+       settype(2, stype);
+}
+
+/*
+ * Set tenex transfer type.
+ */
+/*VARARGS*/
+settenex()
+{
+       stype[1] = "tenex";
+       settype(2, stype);
+}
+
+char *
+getmode()
+{
+       return("stream");
+}
+
+/*
+ * Set file transfer mode.
+ */
+/*ARGSUSED*/
+setmode(argc, argv)
+       int argc;
+       char *argv[];
+{
+
+       printf("We only support %s mode, sorry.\n", getmode());
+       code = -1;
+}
+
+char *
+getform()
+{
+       return("non-print");
+}
+
+/*
+ * Set file transfer format.
+ */
+/*ARGSUSED*/
+setform(argc, argv)
+       int argc;
+       char *argv[];
+{
+
+       printf("We only support %s format, sorry.\n", getform());
+       code = -1;
+}
+
+char *
+getstruct()
+{
+       return("file");
+}
+
+/*
+ * Set file transfer structure.
+ */
+/*ARGSUSED*/
+setstruct(argc, argv)
+       int argc;
+       char *argv[];
+{
+
+       printf("We only support %s structure, sorry.\n", getstruct());
+       code = -1;
+}
+
+/*
+ * Send a single file.
+ */
+put(argc, argv)
+       int argc;
+       char *argv[];
+{
+       char *cmd;
+       int loc = 0;
+       char *oldargv1, *oldargv2;
+
+       if (argc == 2) {
+               argc++;
+               argv[2] = argv[1];
+               loc++;
+       }
+       if (argc < 2 && !another(&argc, &argv, "local-file"))
+               goto usage;
+       if (argc < 3 && !another(&argc, &argv, "remote-file")) {
+usage:
+               printf("usage: %s local-file remote-file\n", argv[0]);
+               code = -1;
+               return;
+       }
+       oldargv1 = argv[1];
+       oldargv2 = argv[2];
+       if (!globulize(&argv[1])) {
+               code = -1;
+               return;
+       }
+       /*
+        * If "globulize" modifies argv[1], and argv[2] is a copy of
+        * the old argv[1], make it a copy of the new argv[1].
+        */
+       if (argv[1] != oldargv1 && argv[2] == oldargv1) {
+               argv[2] = argv[1];
+       }
+       cmd = (argv[0][0] == 'a') ? "APPE" : ((sunique) ? "STOU" : "STOR");
+       if (loc && ntflag) {
+               argv[2] = dotrans(argv[2]);
+       }
+       if (loc && mapflag) {
+               argv[2] = domap(argv[2]);
+       }
+       sendrequest(cmd, argv[1], argv[2],
+           argv[1] != oldargv1 || argv[2] != oldargv2);
+}
+
+/*
+ * Send multiple files.
+ */
+mput(argc, argv)
+       int argc;
+       char **argv;
+{
+       extern jmp_buf jabort;
+       register int i;
+       sig_t oldintr;
+       int ointer;
+       char *tp;
+       sigtype mabort();
+
+       if (argc < 2 && !another(&argc, &argv, "local-files")) {
+               printf("usage: %s local-files\n", argv[0]);
+               code = -1;
+               return;
+       }
+       mname = argv[0];
+       mflag = 1;
+       oldintr = signal(SIGINT, mabort);
+       (void) setjmp(jabort);
+       if (proxy) {
+               char *cp, *tp2, tmpbuf[MAXPATHLEN];
+
+               while ((cp = remglob(argv,0)) != NULL) {
+                       if (*cp == 0) {
+                               mflag = 0;
+                               continue;
+                       }
+                       if (mflag && confirm(argv[0], cp)) {
+                               tp = cp;
+                               if (mcase) {
+                                       while (*tp && !islower(*tp)) {
+                                               tp++;
+                                       }
+                                       if (!*tp) {
+                                               tp = cp;
+                                               tp2 = tmpbuf;
+                                               while ((*tp2 = *tp) != 0) {
+                                                    if (isupper(*tp2)) {
+                                                       *tp2 = 'a' + *tp2 - 'A';
+                                                    }
+                                                    tp++;
+                                                    tp2++;
+                                               }
+                                       }
+                                       tp = tmpbuf;
+                               }
+                               if (ntflag) {
+                                       tp = dotrans(tp);
+                               }
+                               if (mapflag) {
+                                       tp = domap(tp);
+                               }
+                               sendrequest((sunique) ? "STOU" : "STOR",
+                                   cp, tp, cp != tp || !interactive);
+                               if (!mflag && fromatty) {
+                                       ointer = interactive;
+                                       interactive = 1;
+                                       if (confirm("Continue with","mput")) {
+                                               mflag++;
+                                       }
+                                       interactive = ointer;
+                               }
+                       }
+               }
+               (void) signal(SIGINT, oldintr);
+               mflag = 0;
+               return;
+       }
+       for (i = 1; i < argc; i++) {
+               register char **cpp, **gargs;
+
+               if (!doglob) {
+                       if (mflag && confirm(argv[0], argv[i])) {
+                               tp = (ntflag) ? dotrans(argv[i]) : argv[i];
+                               tp = (mapflag) ? domap(tp) : tp;
+                               sendrequest((sunique) ? "STOU" : "STOR",
+                                   argv[i], tp, tp != argv[i] || !interactive);
+                               if (!mflag && fromatty) {
+                                       ointer = interactive;
+                                       interactive = 1;
+                                       if (confirm("Continue with","mput")) {
+                                               mflag++;
+                                       }
+                                       interactive = ointer;
+                               }
+                       }
+                       continue;
+               }
+               gargs = ftpglob(argv[i]);
+               if (globerr != NULL) {
+                       printf("%s\n", globerr);
+                       if (gargs) {
+                               blkfree(gargs);
+                               free((char *)gargs);
+                       }
+                       continue;
+               }
+               for (cpp = gargs; cpp && *cpp != NULL; cpp++) {
+                       if (mflag && confirm(argv[0], *cpp)) {
+                               tp = (ntflag) ? dotrans(*cpp) : *cpp;
+                               tp = (mapflag) ? domap(tp) : tp;
+                               sendrequest((sunique) ? "STOU" : "STOR",
+                                   *cpp, tp, *cpp != tp || !interactive);
+                               if (!mflag && fromatty) {
+                                       ointer = interactive;
+                                       interactive = 1;
+                                       if (confirm("Continue with","mput")) {
+                                               mflag++;
+                                       }
+                                       interactive = ointer;
+                               }
+                       }
+               }
+               if (gargs != NULL) {
+                       blkfree(gargs);
+                       free((char *)gargs);
+               }
+       }
+       (void) signal(SIGINT, oldintr);
+       mflag = 0;
+}
+
+reget(argc, argv)
+       int argc;
+       char *argv[];
+{
+       (void) getit(argc, argv, 1, "r+w");
+}
+
+get(argc, argv)
+       int argc;
+       char *argv[];
+{
+       (void) getit(argc, argv, 0, restart_point ? "r+w" : "w" );
+}
+
+/*
+ * Receive one file.
+ */
+getit(argc, argv, restartit, mode)
+       int argc;
+       char *argv[];
+       char *mode;
+{
+       int loc = 0;
+       char *oldargv1, *oldargv2;
+
+       if (argc == 2) {
+               argc++;
+               argv[2] = argv[1];
+               loc++;
+       }
+       if (argc < 2 && !another(&argc, &argv, "remote-file"))
+               goto usage;
+       if (argc < 3 && !another(&argc, &argv, "local-file")) {
+usage:
+               printf("usage: %s remote-file [ local-file ]\n", argv[0]);
+               code = -1;
+               return (0);
+       }
+       oldargv1 = argv[1];
+       oldargv2 = argv[2];
+       if (!globulize(&argv[2])) {
+               code = -1;
+               return (0);
+       }
+       if (loc && mcase) {
+               char *tp = argv[1], *tp2, tmpbuf[MAXPATHLEN];
+
+               while (*tp && !islower(*tp)) {
+                       tp++;
+               }
+               if (!*tp) {
+                       tp = argv[2];
+                       tp2 = tmpbuf;
+                       while ((*tp2 = *tp) != 0) {
+                               if (isupper(*tp2)) {
+                                       *tp2 = 'a' + *tp2 - 'A';
+                               }
+                               tp++;
+                               tp2++;
+                       }
+                       argv[2] = tmpbuf;
+               }
+       }
+       if (loc && ntflag)
+               argv[2] = dotrans(argv[2]);
+       if (loc && mapflag)
+               argv[2] = domap(argv[2]);
+       if (restartit) {
+               struct stat stbuf;
+               int ret;
+
+               ret = stat(argv[2], &stbuf);
+               if (restartit == 1) {
+                       if (ret < 0) {
+                               fprintf(stderr, "local: %s: %s\n", argv[2],
+                                       strerror(errno));
+                               return (0);
+                       }
+                       restart_point = stbuf.st_size;
+               } else {
+                       if (ret == 0) {
+                               int overbose;
+
+                               overbose = verbose;
+                               if (debug == 0)
+                                       verbose = -1;
+                               if (command("MDTM %s", argv[1]) == COMPLETE) {
+                                       int yy, mo, day, hour, min, sec;
+                                       struct tm *tm;
+                                       verbose = overbose;
+                                       sscanf(reply_string,
+                                           "%*s %04d%02d%02d%02d%02d%02d",
+                                           &yy, &mo, &day, &hour, &min, &sec);
+                                       tm = gmtime(&stbuf.st_mtime);
+                                       tm->tm_mon++;
+                                       if (tm->tm_year > yy%100)
+                                               return (1);
+                                       else if (tm->tm_year == yy%100) {
+                                               if (tm->tm_mon > mo)
+                                                       return (1);
+                                       } else if (tm->tm_mon == mo) {
+                                               if (tm->tm_mday > day)
+                                                       return (1);
+                                       } else if (tm->tm_mday == day) {
+                                               if (tm->tm_hour > hour)
+                                                       return (1);
+                                       } else if (tm->tm_hour == hour) {
+                                               if (tm->tm_min > min)
+                                                       return (1);
+                                       } else if (tm->tm_min == min) {
+                                               if (tm->tm_sec > sec)
+                                                       return (1);
+                                       }
+                               } else {
+                                       printf("%s\n", reply_string);
+                                       verbose = overbose;
+                                       return (0);
+                               }
+                       }
+               }
+       }
+
+       recvrequest("RETR", argv[2], argv[1], mode,
+           argv[1] != oldargv1 || argv[2] != oldargv2);
+       restart_point = 0;
+       return (0);
+}
+
+sigtype
+mabort(sig)
+       int sig;
+{
+       int ointer;
+       extern jmp_buf jabort;
+
+       printf("\n");
+       (void) fflush(stdout);
+       if (mflag && fromatty) {
+               ointer = interactive;
+               interactive = 1;
+               if (confirm("Continue with", mname)) {
+                       interactive = ointer;
+                       longjmp(jabort,0);
+               }
+               interactive = ointer;
+       }
+       mflag = 0;
+       longjmp(jabort,0);
+}
+
+/*
+ * Get multiple files.
+ */
+mget(argc, argv)
+       int argc;
+       char **argv;
+{
+       extern jmp_buf jabort;
+       sig_t oldintr;
+       int ointer;
+       char *cp, *tp, *tp2, tmpbuf[MAXPATHLEN];
+       sigtype mabort();
+
+       if (argc < 2 && !another(&argc, &argv, "remote-files")) {
+               printf("usage: %s remote-files\n", argv[0]);
+               code = -1;
+               return;
+       }
+       mname = argv[0];
+       mflag = 1;
+       oldintr = signal(SIGINT,mabort);
+       (void) setjmp(jabort);
+       while ((cp = remglob(argv,proxy)) != NULL) {
+               if (*cp == '\0') {
+                       mflag = 0;
+                       continue;
+               }
+               if (mflag && confirm(argv[0], cp)) {
+                       tp = cp;
+                       if (mcase) {
+                               while (*tp && !islower(*tp)) {
+                                       tp++;
+                               }
+                               if (!*tp) {
+                                       tp = cp;
+                                       tp2 = tmpbuf;
+                                       while ((*tp2 = *tp) != 0) {
+                                               if (isupper(*tp2)) {
+                                                       *tp2 = 'a' + *tp2 - 'A';
+                                               }
+                                               tp++;
+                                               tp2++;
+                                       }
+                               }
+                               tp = tmpbuf;
+                       }
+                       if (ntflag) {
+                               tp = dotrans(tp);
+                       }
+                       if (mapflag) {
+                               tp = domap(tp);
+                       }
+                       recvrequest("RETR", tp, cp, "w",
+                           tp != cp || !interactive);
+                       if (!mflag && fromatty) {
+                               ointer = interactive;
+                               interactive = 1;
+                               if (confirm("Continue with","mget")) {
+                                       mflag++;
+                               }
+                               interactive = ointer;
+                       }
+               }
+       }
+       (void) signal(SIGINT,oldintr);
+       mflag = 0;
+}
+
+char *
+remglob(argv,doswitch)
+       char *argv[];
+       int doswitch;
+{
+       char temp[16];
+       static char buf[MAXPATHLEN];
+       static FILE *ftemp = NULL;
+       static char **args;
+       int oldverbose, oldhash;
+       char *cp, *mode;
+
+       if (!mflag) {
+               if (!doglob) {
+                       args = NULL;
+               }
+               else {
+                       if (ftemp) {
+                               (void) fclose(ftemp);
+                               ftemp = NULL;
+                       }
+               }
+               return(NULL);
+       }
+       if (!doglob) {
+               if (args == NULL)
+                       args = argv;
+               if ((cp = *++args) == NULL)
+                       args = NULL;
+               return (cp);
+       }
+       if (ftemp == NULL) {
+               (void) strcpy(temp, _PATH_TMP);
+               (void) mktemp(temp);
+               oldverbose = verbose, verbose = 0;
+               oldhash = hash, hash = 0;
+               if (doswitch) {
+                       pswitch(!proxy);
+               }
+               for (mode = "w"; *++argv != NULL; mode = "a")
+                       recvrequest ("NLST", temp, *argv, mode, 0);
+               if (doswitch) {
+                       pswitch(!proxy);
+               }
+               verbose = oldverbose; hash = oldhash;
+               ftemp = fopen(temp, "r");
+               (void) unlink(temp);
+               if (ftemp == NULL) {
+                       printf("can't find list of remote files, oops\n");
+                       return (NULL);
+               }
+       }
+       if (fgets(buf, sizeof (buf), ftemp) == NULL) {
+               (void) fclose(ftemp), ftemp = NULL;
+               return (NULL);
+       }
+       if ((cp = strchr(buf, '\n')) != NULL)
+               *cp = '\0';
+       return (buf);
+}
+
+char *
+onoff(bool)
+       int bool;
+{
+
+       return (bool ? "on" : "off");
+}
+
+cstatus()
+{
+       if (!connected) {
+               printf(proxy ? "No proxy connection.\n" : "Not connected.\n");
+               return;
+       }
+       printf("Connected %sto %s.\n",
+               proxy ? "for proxy commands " : "", hostname);
+       if (auth_type) printf("Authentication type: %s\n", auth_type);
+       printf("Protection Level: %s\n", getlevel());
+       printf("Passive mode %s\n", onoff(passivemode));
+       printf("Mode: %s; Type: %s; Form: %s; Structure: %s\n",
+               getmode(), gettype(), getform(), getstruct());
+       printf("Store unique: %s; Receive unique: %s\n", onoff(sunique),
+               onoff(runique));
+       printf("Case: %s; CR stripping: %s\n",onoff(mcase),onoff(crflag));
+       if (ntflag) {
+               printf("Ntrans: (in) %s (out) %s\n", ntin,ntout);
+       }
+       else {
+               printf("Ntrans: off\n");
+       }
+       if (mapflag) {
+               printf("Nmap: (in) %s (out) %s\n", mapin, mapout);
+       }
+       else {
+               printf("Nmap: off\n");
+       }
+}
+
+/*
+ * Show status.
+ */
+/*ARGSUSED*/
+status(argc, argv)
+       char *argv[];
+{
+       int i;
+
+       cstatus();
+       if (!proxy) {
+               pswitch(1);
+               if (connected) putchar('\n');
+               cstatus(argc,argv);
+               if (connected) putchar('\n');
+               pswitch(0);
+       }
+       printf("Hash mark printing: %s; Use of PORT cmds: %s\n",
+               onoff(hash), onoff(sendport));
+       printf("Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s\n", 
+               onoff(verbose), onoff(bell), onoff(interactive),
+               onoff(doglob));
+       if (macnum > 0) {
+               printf("Macros:\n");
+               for (i=0; i<macnum; i++) {
+                       printf("\t%s\n",macros[i].mac_name);
+               }
+       }
+       code = 0;
+}
+
+/*
+ * Set beep on cmd completed mode.
+ */
+/*VARARGS*/
+setbell()
+{
+
+       bell = !bell;
+       printf("Bell mode %s.\n", onoff(bell));
+       code = bell;
+}
+
+/*
+ * Turn on packet tracing.
+ */
+/*VARARGS*/
+settrace()
+{
+
+       trace = !trace;
+       printf("Packet tracing %s.\n", onoff(trace));
+       code = trace;
+}
+
+/*
+ * Toggle hash mark printing during transfers.
+ */
+/*VARARGS*/
+sethash()
+{
+
+       hash = !hash;
+       printf("Hash mark printing %s", onoff(hash));
+       code = hash;
+       if (hash)
+               printf(" (%d bytes/hash mark)", 1024);
+       printf(".\n");
+}
+
+/*
+ * Turn on printing of server echo's.
+ */
+/*VARARGS*/
+setverbose()
+{
+
+       verbose = !verbose;
+       printf("Verbose mode %s.\n", onoff(verbose));
+       code = verbose;
+}
+
+/*
+ * Toggle PORT cmd use before each data connection.
+ */
+/*VARARGS*/
+setport()
+{
+
+       sendport = !sendport;
+       printf("Use of PORT cmds %s.\n", onoff(sendport));
+       code = sendport;
+}
+
+/*
+ * Turn on interactive prompting
+ * during mget, mput, and mdelete.
+ */
+/*VARARGS*/
+setprompt()
+{
+
+       interactive = !interactive;
+       printf("Interactive mode %s.\n", onoff(interactive));
+       code = interactive;
+}
+
+/*
+ * Toggle metacharacter interpretation
+ * on local file names.
+ */
+/*VARARGS*/
+setglob()
+{
+       
+       doglob = !doglob;
+       printf("Globbing %s.\n", onoff(doglob));
+       code = doglob;
+}
+
+/*
+ * Set debugging mode on/off and/or
+ * set level of debugging.
+ */
+/*VARARGS*/
+setdebug(argc, argv)
+       int argc;
+       char *argv[];
+{
+       int val;
+
+       if (argc > 1) {
+               val = atoi(argv[1]);
+               if (val < 0) {
+                       printf("%s: bad debugging value.\n", argv[1]);
+                       code = -1;
+                       return;
+               }
+       } else
+               val = !debug;
+       debug = val;
+       if (debug)
+               options |= SO_DEBUG;
+       else
+               options &= ~SO_DEBUG;
+       printf("Debugging %s (debug=%d).\n", onoff(debug), debug);
+       code = debug > 0;
+}
+
+/*
+ * Set current working directory
+ * on remote machine.
+ */
+cd(argc, argv)
+       int argc;
+       char *argv[];
+{
+
+       if (argc < 2 && !another(&argc, &argv, "remote-directory")) {
+               printf("usage: %s remote-directory\n", argv[0]);
+               code = -1;
+               return;
+       }
+       if (command("CWD %s", argv[1]) == ERROR && code == 500) {
+               if (verbose)
+                       printf("CWD command not recognized, trying XCWD\n");
+               (void) command("XCWD %s", argv[1]);
+       }
+}
+
+/*
+ * Set current working directory
+ * on local machine.
+ */
+lcd(argc, argv)
+       int argc;
+       char *argv[];
+{
+       char buf[MAXPATHLEN];
+
+       if (argc < 2)
+               argc++, argv[1] = home;
+       if (argc != 2) {
+               printf("usage: %s local-directory\n", argv[0]);
+               code = -1;
+               return;
+       }
+       if (!globulize(&argv[1])) {
+               code = -1;
+               return;
+       }
+       if (chdir(argv[1]) < 0) {
+               fprintf(stderr, "local: %s: %s\n", argv[1], strerror(errno));
+               code = -1;
+               return;
+       }
+       printf("Local directory now %s\n", getcwd(buf, sizeof buf));
+       code = 0;
+}
+
+/*
+ * Delete a single file.
+ */
+delete(argc, argv)
+       int argc;
+       char *argv[];
+{
+
+       if (argc < 2 && !another(&argc, &argv, "remote-file")) {
+               printf("usage: %s remote-file\n", argv[0]);
+               code = -1;
+               return;
+       }
+       (void) command("DELE %s", argv[1]);
+}
+
+/*
+ * Delete multiple files.
+ */
+mdelete(argc, argv)
+       int argc;
+       char **argv;
+{
+       extern jmp_buf jabort;
+       sig_t oldintr;
+       int ointer;
+       char *cp;
+       sigtype mabort();
+
+       if (argc < 2 && !another(&argc, &argv, "remote-files")) {
+               printf("usage: %s remote-files\n", argv[0]);
+               code = -1;
+               return;
+       }
+       mname = argv[0];
+       mflag = 1;
+       oldintr = signal(SIGINT, mabort);
+       (void) setjmp(jabort);
+       while ((cp = remglob(argv,0)) != NULL) {
+               if (*cp == '\0') {
+                       mflag = 0;
+                       continue;
+               }
+               if (mflag && confirm(argv[0], cp)) {
+                       (void) command("DELE %s", cp);
+                       if (!mflag && fromatty) {
+                               ointer = interactive;
+                               interactive = 1;
+                               if (confirm("Continue with", "mdelete")) {
+                                       mflag++;
+                               }
+                               interactive = ointer;
+                       }
+               }
+       }
+       (void) signal(SIGINT, oldintr);
+       mflag = 0;
+}
+
+/*
+ * Rename a remote file.
+ */
+renamefile(argc, argv)
+       int argc;
+       char *argv[];
+{
+
+       if (argc < 2 && !another(&argc, &argv, "from-name"))
+               goto usage;
+       if (argc < 3 && !another(&argc, &argv, "to-name")) {
+usage:
+               printf("%s from-name to-name\n", argv[0]);
+               code = -1;
+               return;
+       }
+       if (command("RNFR %s", argv[1]) == CONTINUE)
+               (void) command("RNTO %s", argv[2]);
+}
+
+/*
+ * Get a directory listing
+ * of remote files.
+ */
+ls(argc, argv)
+       int argc;
+       char *argv[];
+{
+       char *cmd;
+
+       if (argc < 2)
+               argc++, argv[1] = NULL;
+       if (argc < 3)
+               argc++, argv[2] = "-";
+       if (argc > 3) {
+               printf("usage: %s remote-directory local-file\n", argv[0]);
+               code = -1;
+               return;
+       }
+       cmd = argv[0][0] == 'n' ? "NLST" : "LIST";
+       if (strcmp(argv[2], "-") && !globulize(&argv[2])) {
+               code = -1;
+               return;
+       }
+       if (strcmp(argv[2], "-") && *argv[2] != '|')
+               if (!globulize(&argv[2]) || !confirm("output to local-file:", argv[2])) {
+                       code = -1;
+                       return;
+       }
+       recvrequest(cmd, argv[2], argv[1], "w", 0);
+}
+
+/*
+ * Get a directory listing
+ * of multiple remote files.
+ */
+mls(argc, argv)
+       int argc;
+       char **argv;
+{
+       extern jmp_buf jabort;
+       sig_t oldintr;
+       int ointer, i;
+       char *cmd, mode[1], *dest;
+       sigtype mabort();
+
+       if (argc < 2 && !another(&argc, &argv, "remote-files"))
+               goto usage;
+       if (argc < 3 && !another(&argc, &argv, "local-file")) {
+usage:
+               printf("usage: %s remote-files local-file\n", argv[0]);
+               code = -1;
+               return;
+       }
+       dest = argv[argc - 1];
+       argv[argc - 1] = NULL;
+       if (strcmp(dest, "-") && *dest != '|')
+               if (!globulize(&dest) ||
+                   !confirm("output to local-file:", dest)) {
+                       code = -1;
+                       return;
+       }
+       cmd = argv[0][1] == 'l' ? "NLST" : "LIST";
+       mname = argv[0];
+       mflag = 1;
+       oldintr = signal(SIGINT, mabort);
+       (void) setjmp(jabort);
+       for (i = 1; mflag && i < argc-1; ++i) {
+               *mode = (i == 1) ? 'w' : 'a';
+               recvrequest(cmd, dest, argv[i], mode, 0);
+               if (!mflag && fromatty) {
+                       ointer = interactive;
+                       interactive = 1;
+                       if (confirm("Continue with", argv[0])) {
+                               mflag ++;
+                       }
+                       interactive = ointer;
+               }
+       }
+       (void) signal(SIGINT, oldintr);
+       mflag = 0;
+}
+
+/*
+ * Do a shell escape
+ */
+/*ARGSUSED*/
+shell(argc, argv)
+       int argc;
+       char **argv;
+{
+       int pid;
+       sig_t old1, old2;
+       char shellnam[40], *shell, *namep; 
+#ifdef WAIT_USES_INT
+       int status;
+#else
+       union wait status;
+#endif
+
+       old1 = signal (SIGINT, SIG_IGN);
+       old2 = signal (SIGQUIT, SIG_IGN);
+       if ((pid = fork()) == 0) {
+               for (pid = 3; pid < 20; pid++)
+                       (void) close(pid);
+               (void) signal(SIGINT, SIG_DFL);
+               (void) signal(SIGQUIT, SIG_DFL);
+               shell = getenv("SHELL");
+               if (shell == NULL)
+                       shell = "/bin/sh";
+               namep = strrchr(shell,'/');
+               if (namep == NULL)
+                       namep = shell;
+               (void) strcpy(shellnam,"-");
+               (void) strcat(shellnam, ++namep);
+               if (strcmp(namep, "sh") != 0)
+                       shellnam[0] = '+';
+               if (debug) {
+                       printf ("%s\n", shell);
+                       (void) fflush (stdout);
+               }
+               if (argc > 1) {
+                       execl(shell,shellnam,"-c",altarg,(char *)0);
+               }
+               else {
+                       execl(shell,shellnam,(char *)0);
+               }
+               perror(shell);
+               code = -1;
+               exit(1);
+               }
+       if (pid > 0)
+               while (wait(&status) != pid)
+                       ;
+       (void) signal(SIGINT, old1);
+       (void) signal(SIGQUIT, old2);
+       if (pid == -1) {
+               perror("Try again later");
+               code = -1;
+       }
+       else {
+               code = 0;
+       }
+       return (0);
+}
+
+/*
+ * Send new user information (re-login)
+ */
+user(argc, argv)
+       int argc;
+       char **argv;
+{
+       char acct[80], *mygetpass();
+       int n, aflag = 0;
+
+       if (argc < 2)
+               (void) another(&argc, &argv, "username");
+       if (argc < 2 || argc > 4) {
+               printf("usage: %s username [password] [account]\n", argv[0]);
+               code = -1;
+               return (0);
+       }
+       n = command("USER %s", argv[1]);
+       if (n == COMPLETE)
+               n = command("PASS dummy");
+       else if (n == CONTINUE) {
+#ifndef NOENCRYPTION
+               int oldlevel;
+#endif
+               if (argc < 3 )
+                       argv[2] = mygetpass("Password: "), argc++;
+#ifndef NOENCRYPTION
+               if ((oldlevel = level) == PROT_S) level = PROT_P;
+#endif
+               n = command("PASS %s", argv[2]);
+#ifndef NOENCRYPTION
+               /* level may have changed */
+               if (level == PROT_P) level = oldlevel;
+#endif
+       }
+       if (n == CONTINUE) {
+               if (argc < 4) {
+                       printf("Account: "); (void) fflush(stdout);
+                       (void) fgets(acct, sizeof(acct) - 1, stdin);
+                       acct[strlen(acct) - 1] = '\0';
+                       argv[3] = acct; argc++;
+               }
+               n = command("ACCT %s", argv[3]);
+               aflag++;
+       }
+       if (n != COMPLETE) {
+               fprintf(stdout, "Login failed.\n");
+               return (0);
+       }
+       if (!aflag && argc == 4) {
+               (void) command("ACCT %s", argv[3]);
+       }
+       return (1);
+}
+
+/*
+ * Print working directory.
+ */
+/*VARARGS*/
+pwd()
+{
+       int oldverbose = verbose;
+
+       /*
+        * If we aren't verbose, this doesn't do anything!
+        */
+       verbose = 1;
+       if (command("PWD") == ERROR && code == 500) {
+               printf("PWD command not recognized, trying XPWD\n");
+               (void) command("XPWD");
+       }
+       verbose = oldverbose;
+}
+
+/*
+ * Make a directory.
+ */
+makedir(argc, argv)
+       int argc;
+       char *argv[];
+{
+
+       if (argc < 2 && !another(&argc, &argv, "directory-name")) {
+               printf("usage: %s directory-name\n", argv[0]);
+               code = -1;
+               return;
+       }
+       if (command("MKD %s", argv[1]) == ERROR && code == 500) {
+               if (verbose)
+                       printf("MKD command not recognized, trying XMKD\n");
+               (void) command("XMKD %s", argv[1]);
+       }
+}
+
+/*
+ * Remove a directory.
+ */
+removedir(argc, argv)
+       int argc;
+       char *argv[];
+{
+
+       if (argc < 2 && !another(&argc, &argv, "directory-name")) {
+               printf("usage: %s directory-name\n", argv[0]);
+               code = -1;
+               return;
+       }
+       if (command("RMD %s", argv[1]) == ERROR && code == 500) {
+               if (verbose)
+                       printf("RMD command not recognized, trying XRMD\n");
+               (void) command("XRMD %s", argv[1]);
+       }
+}
+
+/*
+ * Send a line, verbatim, to the remote machine.
+ */
+quote(argc, argv)
+       int argc;
+       char *argv[];
+{
+
+       if (argc < 2 && !another(&argc, &argv, "command line to send")) {
+               printf("usage: %s line-to-send\n", argv[0]);
+               code = -1;
+               return;
+       }
+       quote1("", argc, argv);
+}
+
+/*
+ * Send a SITE command to the remote machine.  The line
+ * is sent verbatim to the remote machine, except that the
+ * word "SITE" is added at the front.
+ */
+site(argc, argv)
+       int argc;
+       char *argv[];
+{
+
+       if (argc < 2 && !another(&argc, &argv, "arguments to SITE command")) {
+               printf("usage: %s line-to-send\n", argv[0]);
+               code = -1;
+               return;
+       }
+       quote1("SITE ", argc, argv);
+}
+
+/*
+ * Turn argv[1..argc) into a space-separated string, then prepend initial text.
+ * Send the result as a one-line command and get response.
+ */
+quote1(initial, argc, argv)
+       char *initial;
+       int argc;
+       char **argv;
+{
+       register int i, len;
+       char buf[FTP_BUFSIZ];           /* must be >= sizeof(line) */
+
+       (void) strcpy(buf, initial);
+       if (argc > 1) {
+               len = strlen(buf);
+               len += strlen(strcpy(&buf[len], argv[1]));
+               for (i = 2; i < argc; i++) {
+                       buf[len++] = ' ';
+                       len += strlen(strcpy(&buf[len], argv[i]));
+               }
+       }
+       if (command(buf) == PRELIM) {
+               while (getreply(0) == PRELIM);
+       }
+}
+
+do_chmod(argc, argv)
+       int argc;
+       char *argv[];
+{
+
+       if (argc < 2 && !another(&argc, &argv, "mode"))
+               goto usage;
+       if (argc < 3 && !another(&argc, &argv, "file-name")) {
+usage:
+               printf("usage: %s mode file-name\n", argv[0]);
+               code = -1;
+               return;
+       }
+       (void) command("SITE CHMOD %s %s", argv[1], argv[2]);
+}
+
+do_umask(argc, argv)
+       int argc;
+       char *argv[];
+{
+       int oldverbose = verbose;
+
+       verbose = 1;
+       (void) command(argc == 1 ? "SITE UMASK" : "SITE UMASK %s", argv[1]);
+       verbose = oldverbose;
+}
+
+siteidle(argc, argv)
+       int argc;
+       char *argv[];
+{
+       int oldverbose = verbose;
+
+       verbose = 1;
+       (void) command(argc == 1 ? "SITE IDLE" : "SITE IDLE %s", argv[1]);
+       verbose = oldverbose;
+}
+
+/*
+ * Ask the other side for help.
+ */
+rmthelp(argc, argv)
+       int argc;
+       char *argv[];
+{
+       int oldverbose = verbose;
+
+       verbose = 1;
+       (void) command(argc == 1 ? "HELP" : "HELP %s", argv[1]);
+       verbose = oldverbose;
+}
+
+/*
+ * Terminate session and exit.
+ */
+/*VARARGS*/
+quit()
+{
+
+       if (connected)
+               disconnect();
+       pswitch(1);
+       if (connected) {
+               disconnect();
+       }
+       exit(0);
+}
+
+/*
+ * Terminate session, but don't exit.
+ */
+disconnect()
+{
+       extern FILE *cout;
+       extern int data;
+
+       if (!connected)
+               return;
+       (void) command("QUIT");
+       if (cout) {
+               (void) fclose(cout);
+       }
+       cout = NULL;
+       connected = 0;
+       data = -1;
+       if (!proxy) {
+               macnum = 0;
+       }
+       auth_type = NULL;
+       level = PROT_C;
+}
+
+confirm(cmd, file)
+       char *cmd, *file;
+{
+       char line[FTP_BUFSIZ];
+
+       if (!interactive)
+               return (1);
+       printf("%s %s? ", cmd, file);
+       (void) fflush(stdout);
+       if (fgets(line, sizeof line, stdin) == NULL)
+               return (0);
+       return (*line != 'n' && *line != 'N');
+}
+
+fatal(msg)
+       char *msg;
+{
+
+       fprintf(stderr, "ftp: %s\n", msg);
+       exit(1);
+}
+
+/*
+ * Glob a local file name specification with
+ * the expectation of a single return value.
+ * Can't control multiple values being expanded
+ * from the expression, we return only the first.
+ */
+globulize(cpp)
+       char **cpp;
+{
+       char **globbed;
+
+       if (!doglob)
+               return (1);
+       globbed = ftpglob(*cpp);
+       if (globerr != NULL) {
+               printf("%s: %s\n", *cpp, globerr);
+               if (globbed) {
+                       blkfree(globbed);
+                       free((char *)globbed);
+               }
+               return (0);
+       }
+       if (globbed) {
+               *cpp = *globbed++;
+               /* don't waste too much memory */
+               if (*globbed) {
+                       blkfree(globbed);
+                       free((char *)globbed);
+               }
+       }
+       return (1);
+}
+
+account(argc,argv)
+       int argc;
+       char **argv;
+{
+       char acct[50], *mygetpass(), *ap;
+
+       if (argc > 1) {
+               ++argv;
+               --argc;
+               (void) strncpy(acct,*argv,49);
+               acct[49] = '\0';
+               while (argc > 1) {
+                       --argc;
+                       ++argv;
+                       (void) strncat(acct,*argv, 49-strlen(acct));
+               }
+               ap = acct;
+       }
+       else {
+               ap = mygetpass("Account:");
+       }
+       (void) command("ACCT %s", ap);
+}
+
+jmp_buf abortprox;
+
+sigtype
+proxabort(sig)
+       int sig;
+{
+       extern int proxy;
+
+       if (!proxy) {
+               pswitch(1);
+       }
+       if (connected) {
+               proxflag = 1;
+       }
+       else {
+               proxflag = 0;
+       }
+       pswitch(0);
+       longjmp(abortprox,1);
+}
+
+doproxy(argc,argv)
+       int argc;
+       char *argv[];
+{
+       extern struct cmd cmdtab[];
+       extern jmp_buf abortprox;
+       register struct cmd *c;
+       struct cmd *getcmd();
+       sig_t oldintr;
+       sigtype proxabort();
+
+       if (argc < 2 && !another(&argc, &argv, "command")) {
+               printf("usage: %s command\n", argv[0]);
+               code = -1;
+               return;
+       }
+       c = getcmd(argv[1]);
+       if (c == (struct cmd *) -1) {
+               printf("?Ambiguous command\n");
+               (void) fflush(stdout);
+               code = -1;
+               return;
+       }
+       if (c == 0) {
+               printf("?Invalid command\n");
+               (void) fflush(stdout);
+               code = -1;
+               return;
+       }
+       if (!c->c_proxy) {
+               printf("?Invalid proxy command\n");
+               (void) fflush(stdout);
+               code = -1;
+               return;
+       }
+       if (setjmp(abortprox)) {
+               code = -1;
+               return;
+       }
+       oldintr = signal(SIGINT, proxabort);
+       pswitch(1);
+       if (c->c_conn && !connected) {
+               printf("Not connected\n");
+               (void) fflush(stdout);
+               pswitch(0);
+               (void) signal(SIGINT, oldintr);
+               code = -1;
+               return;
+       }
+       (*c->c_handler)(argc-1, argv+1);
+       if (connected) {
+               proxflag = 1;
+       }
+       else {
+               proxflag = 0;
+       }
+       pswitch(0);
+       (void) signal(SIGINT, oldintr);
+}
+
+setcase()
+{
+       mcase = !mcase;
+       printf("Case mapping %s.\n", onoff(mcase));
+       code = mcase;
+}
+
+setcr()
+{
+       crflag = !crflag;
+       printf("Carriage Return stripping %s.\n", onoff(crflag));
+       code = crflag;
+}
+
+setntrans(argc,argv)
+       int argc;
+       char *argv[];
+{
+       if (argc == 1) {
+               ntflag = 0;
+               printf("Ntrans off.\n");
+               code = ntflag;
+               return;
+       }
+       ntflag++;
+       code = ntflag;
+       (void) strncpy(ntin, argv[1], 16);
+       ntin[16] = '\0';
+       if (argc == 2) {
+               ntout[0] = '\0';
+               return;
+       }
+       (void) strncpy(ntout, argv[2], 16);
+       ntout[16] = '\0';
+}
+
+char *
+dotrans(name)
+       char *name;
+{
+       static char new[MAXPATHLEN];
+       char *cp1, *cp2 = new;
+       register int i, ostop, found;
+
+       for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++);
+       for (cp1 = name; *cp1; cp1++) {
+               found = 0;
+               for (i = 0; *(ntin + i) && i < 16; i++) {
+                       if (*cp1 == *(ntin + i)) {
+                               found++;
+                               if (i < ostop) {
+                                       *cp2++ = *(ntout + i);
+                               }
+                               break;
+                       }
+               }
+               if (!found) {
+                       *cp2++ = *cp1;
+               }
+       }
+       *cp2 = '\0';
+       return(new);
+}
+
+setnmap(argc, argv)
+       int argc;
+       char *argv[];
+{
+       char *cp;
+
+       if (argc == 1) {
+               mapflag = 0;
+               printf("Nmap off.\n");
+               code = mapflag;
+               return;
+       }
+       if (argc < 3 && !another(&argc, &argv, "mapout")) {
+               printf("Usage: %s [mapin mapout]\n",argv[0]);
+               code = -1;
+               return;
+       }
+       mapflag = 1;
+       code = 1;
+       cp = strchr(altarg, ' ');
+       if (proxy) {
+               while(*++cp == ' ');
+               altarg = cp;
+               cp = strchr(altarg, ' ');
+       }
+       *cp = '\0';
+       (void) strncpy(mapin, altarg, MAXPATHLEN - 1);
+       while (*++cp == ' ');
+       (void) strncpy(mapout, cp, MAXPATHLEN - 1);
+}
+
+char *
+domap(name)
+       char *name;
+{
+       static char new[MAXPATHLEN];
+       register char *cp1 = name, *cp2 = mapin;
+       char *tp[9], *te[9];
+       int i, toks[9], toknum = 0, match = 1;
+
+       for (i=0; i < 9; ++i) {
+               toks[i] = 0;
+       }
+       while (match && *cp1 && *cp2) {
+               switch (*cp2) {
+                       case '\\':
+                               if (*++cp2 != *cp1) {
+                                       match = 0;
+                               }
+                               break;
+                       case '$':
+                               if (*(cp2+1) >= '1' && (*cp2+1) <= '9') {
+                                       if (*cp1 != *(++cp2+1)) {
+                                               toks[toknum = *cp2 - '1']++;
+                                               tp[toknum] = cp1;
+                                               while (*++cp1 && *(cp2+1)
+                                                       != *cp1);
+                                               te[toknum] = cp1;
+                                       }
+                                       cp2++;
+                                       break;
+                               }
+                               /* FALLTHROUGH */
+                       default:
+                               if (*cp2 != *cp1) {
+                                       match = 0;
+                               }
+                               break;
+               }
+               if (match && *cp1) {
+                       cp1++;
+               }
+               if (match && *cp2) {
+                       cp2++;
+               }
+       }
+       if (!match && *cp1) /* last token mismatch */
+       {
+               toks[toknum] = 0;
+       }
+       cp1 = new;
+       *cp1 = '\0';
+       cp2 = mapout;
+       while (*cp2) {
+               match = 0;
+               switch (*cp2) {
+                       case '\\':
+                               if (*(cp2 + 1)) {
+                                       *cp1++ = *++cp2;
+                               }
+                               break;
+                       case '[':
+LOOP:
+                               if (*++cp2 == '$' && isdigit(*(cp2+1))) { 
+                                       if (*++cp2 == '0') {
+                                               char *cp3 = name;
+
+                                               while (*cp3) {
+                                                       *cp1++ = *cp3++;
+                                               }
+                                               match = 1;
+                                       }
+                                       else if (toks[toknum = *cp2 - '1']) {
+                                               char *cp3 = tp[toknum];
+
+                                               while (cp3 != te[toknum]) {
+                                                       *cp1++ = *cp3++;
+                                               }
+                                               match = 1;
+                                       }
+                               }
+                               else {
+                                       while (*cp2 && *cp2 != ',' && 
+                                           *cp2 != ']') {
+                                               if (*cp2 == '\\') {
+                                                       cp2++;
+                                               }
+                                               else if (*cp2 == '$' &&
+                                                       isdigit(*(cp2+1))) {
+                                                       if (*++cp2 == '0') {
+                                                          char *cp3 = name;
+
+                                                          while (*cp3) {
+                                                               *cp1++ = *cp3++;
+                                                          }
+                                                       }
+                                                       else if (toks[toknum =
+                                                           *cp2 - '1']) {
+                                                          char *cp3=tp[toknum];
+
+                                                          while (cp3 !=
+                                                                 te[toknum]) {
+                                                               *cp1++ = *cp3++;
+                                                          }
+                                                       }
+                                               }
+                                               else if (*cp2) {
+                                                       *cp1++ = *cp2++;
+                                               }
+                                       }
+                                       if (!*cp2) {
+                                               printf("nmap: unbalanced brackets\n");
+                                               return(name);
+                                       }
+                                       match = 1;
+                                       cp2--;
+                               }
+                               if (match) {
+                                       while (*++cp2 && *cp2 != ']') {
+                                             if (*cp2 == '\\' && *(cp2 + 1)) {
+                                                       cp2++;
+                                             }
+                                       }
+                                       if (!*cp2) {
+                                               printf("nmap: unbalanced brackets\n");
+                                               return(name);
+                                       }
+                                       break;
+                               }
+                               switch (*++cp2) {
+                                       case ',':
+                                               goto LOOP;
+                                       case ']':
+                                               break;
+                                       default:
+                                               cp2--;
+                                               goto LOOP;
+                               }
+                               break;
+                       case '$':
+                               if (isdigit(*(cp2 + 1))) {
+                                       if (*++cp2 == '0') {
+                                               char *cp3 = name;
+
+                                               while (*cp3) {
+                                                       *cp1++ = *cp3++;
+                                               }
+                                       }
+                                       else if (toks[toknum = *cp2 - '1']) {
+                                               char *cp3 = tp[toknum];
+
+                                               while (cp3 != te[toknum]) {
+                                                       *cp1++ = *cp3++;
+                                               }
+                                       }
+                                       break;
+                               }
+                               /* intentional drop through */
+                       default:
+                               *cp1++ = *cp2;
+                               break;
+               }
+               cp2++;
+       }
+       *cp1 = '\0';
+       if (!*new) {
+               return(name);
+       }
+       return(new);
+}
+
+setsunique()
+{
+       sunique = !sunique;
+       printf("Store unique %s.\n", onoff(sunique));
+       code = sunique;
+}
+
+setrunique()
+{
+       runique = !runique;
+       printf("Receive unique %s.\n", onoff(runique));
+       code = runique;
+}
+
+/* change directory to perent directory */
+cdup()
+{
+       if (command("CDUP") == ERROR && code == 500) {
+               if (verbose)
+                       printf("CDUP command not recognized, trying XCUP\n");
+               (void) command("XCUP");
+       }
+}
+
+/* restart transfer at specific point */
+restart(argc, argv)
+       int argc;
+       char *argv[];
+{
+       extern long atol();
+       if (argc != 2)
+               printf("restart: offset not specified\n");
+       else {
+               restart_point = atol(argv[1]);
+               printf("restarting at %ld. %s\n", restart_point,
+                   "execute get, put or append to initiate transfer");
+       }
+}
+
+/* show remote system type */
+syst()
+{
+       (void) command("SYST");
+}
+
+macdef(argc, argv)
+       int argc;
+       char *argv[];
+{
+       char *tmp;
+       int c;
+
+       if (macnum == 16) {
+               printf("Limit of 16 macros have already been defined\n");
+               code = -1;
+               return;
+       }
+       if (argc < 2 && !another(&argc, &argv, "macro name")) {
+               printf("Usage: %s macro_name\n",argv[0]);
+               code = -1;
+               return;
+       }
+       if (interactive) {
+               printf("Enter macro line by line, terminating it with a null line\n");
+       }
+       (void) strncpy(macros[macnum].mac_name, argv[1], 8);
+       if (macnum == 0) {
+               macros[macnum].mac_start = macbuf;
+       }
+       else {
+               macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
+       }
+       tmp = macros[macnum].mac_start;
+       while (tmp != macbuf+4096) {
+               if ((c = getchar()) == EOF) {
+                       printf("macdef:end of file encountered\n");
+                       code = -1;
+                       return;
+               }
+               if ((*tmp = c) == '\n') {
+                       if (tmp == macros[macnum].mac_start) {
+                               macros[macnum++].mac_end = tmp;
+                               code = 0;
+                               return;
+                       }
+                       if (*(tmp-1) == '\0') {
+                               macros[macnum++].mac_end = tmp - 1;
+                               code = 0;
+                               return;
+                       }
+                       *tmp = '\0';
+               }
+               tmp++;
+       }
+       while (1) {
+               while ((c = getchar()) != '\n' && c != EOF)
+                       /* LOOP */;
+               if (c == EOF || getchar() == '\n') {
+                       printf("Macro not defined - 4k buffer exceeded\n");
+                       code = -1;
+                       return;
+               }
+       }
+}
+
+/*
+ * get size of file on remote machine
+ */
+sizecmd(argc, argv)
+       int argc;
+       char *argv[];
+{
+
+       if (argc < 2 && !another(&argc, &argv, "filename")) {
+               printf("usage: %s filename\n", argv[0]);
+               code = -1;
+               return;
+       }
+       (void) command("SIZE %s", argv[1]);
+}
+
+/*
+ * get last modification time of file on remote machine
+ */
+modtime(argc, argv)
+       int argc;
+       char *argv[];
+{
+       int overbose;
+
+       if (argc < 2 && !another(&argc, &argv, "filename")) {
+               printf("usage: %s filename\n", argv[0]);
+               code = -1;
+               return;
+       }
+       overbose = verbose;
+       if (debug == 0)
+               verbose = -1;
+       if (command("MDTM %s", argv[1]) == COMPLETE) {
+               int yy, mo, day, hour, min, sec;
+               sscanf(reply_string, "%*s %04d%02d%02d%02d%02d%02d", &yy, &mo,
+                       &day, &hour, &min, &sec);
+               /* might want to print this in local time */
+               printf("%s\t%02d/%02d/%04d %02d:%02d:%02d GMT\n", argv[1],
+                       mo, day, yy, hour, min, sec);
+       } else
+               printf("%s\n", reply_string);
+       verbose = overbose;
+}
+
+/*
+ * show status on reomte machine
+ */
+rmtstatus(argc, argv)
+       int argc;
+       char *argv[];
+{
+       (void) command(argc > 1 ? "STAT %s" : "STAT" , argv[1]);
+}
+
+/*
+ * get file if modtime is more recent than current file
+ */
+newer(argc, argv)
+       int argc;
+       char *argv[];
+{
+       if (getit(argc, argv, -1, "w"))
+               printf("Local file \"%s\" is newer than remote file \"%s\"\n",
+                       argv[1], argv[2]);
+}
+
+#ifndef NO_PASSIVE_MODE
+/*
+ * Start up passive mode interaction
+ */
+
+/*VARARGS*/
+setpassive()
+{
+
+       passivemode = !passivemode;
+       printf("Passive mode %s.\n", onoff(passivemode));
+       code = passivemode;
+}
+#endif
diff --git a/src/appl/gssftp/ftp/cmdtab.c b/src/appl/gssftp/ftp/cmdtab.c
new file mode 100644 (file)
index 0000000..39d171d
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 1985, 1989 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[] = "@(#)cmdtab.c   5.10 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+#include <stdio.h>
+#include "ftp_var.h"
+
+/*
+ * User FTP -- Command Tables.
+ */
+int    setascii(), setbell(), setbinary(), setdebug(), setform();
+int    setglob(), sethash(), setmode(), setpeer(), setport();
+int    setprompt(), setstruct();
+int    settenex(), settrace(), settype(), setverbose();
+int    setlevel(), setclear(), setsafe();
+#ifndef NOENCRYPTION
+int    setprivate();
+#endif
+int    disconnect(), restart(), reget(), syst();
+int    cd(), lcd(), delete(), mdelete(), user();
+int    ls(), mls(), get(), mget(), help(), append(), put(), mput();
+int    quit(), renamefile(), status();
+int    quote(), rmthelp(), shell(), site();
+int    pwd(), makedir(), removedir(), setcr();
+int    account(), doproxy(), reset(), setcase(), setntrans(), setnmap();
+int    setsunique(), setrunique(), cdup(), macdef(), domacro();
+int    sizecmd(), modtime(), newer(), rmtstatus();
+int    do_chmod(), do_umask(), siteidle();
+#ifndef NO_PASSIVE_MODE
+int    setpassive();
+#endif
+
+char   accounthelp[] = "send account command to remote server";
+char   appendhelp[] =  "append to a file";
+char   asciihelp[] =   "set ascii transfer type";
+char   beephelp[] =    "beep when command completed";
+char   binaryhelp[] =  "set binary transfer type";
+char   casehelp[] =    "toggle mget upper/lower case id mapping";
+char   cdhelp[] =      "change remote working directory";
+char   cduphelp[] =    "change remote working directory to parent directory";
+char   chmodhelp[] =   "change file permissions of remote file";
+char   clearhelp[] =   "set clear protection level for file transfer";
+char   connecthelp[] = "connect to remote ftp";
+char   crhelp[] =      "toggle carriage return stripping on ascii gets";
+char   deletehelp[] =  "delete remote file";
+char   debughelp[] =   "toggle/set debugging mode";
+char   dirhelp[] =     "list contents of remote directory";
+char   disconhelp[] =  "terminate ftp session";
+char   domachelp[] =   "execute macro";
+char   formhelp[] =    "set file transfer format";
+char   globhelp[] =    "toggle metacharacter expansion of local file names";
+char   hashhelp[] =    "toggle printing `#' for each buffer transferred";
+char   helphelp[] =    "print local help information";
+char   idlehelp[] =    "get (set) idle timer on remote side";
+char   lcdhelp[] =     "change local working directory";
+char   levelhelp[] =   "set protection level for file transfer";
+char   lshelp[] =      "list contents of remote directory";
+char   macdefhelp[] =  "define a macro";
+char   mdeletehelp[] = "delete multiple files";
+char   mdirhelp[] =    "list contents of multiple remote directories";
+char   mgethelp[] =    "get multiple files";
+char   mkdirhelp[] =   "make directory on the remote machine";
+char   mlshelp[] =     "list contents of multiple remote directories";
+char   modtimehelp[] = "show last modification time of remote file";
+char   modehelp[] =    "set file transfer mode";
+char   mputhelp[] =    "send multiple files";
+char   newerhelp[] =   "get file if remote file is newer than local file ";
+char   nlisthelp[] =   "nlist contents of remote directory";
+char   nmaphelp[] =    "set templates for default file name mapping";
+char   ntranshelp[] =  "set translation table for default file name mapping";
+char   porthelp[] =    "toggle use of PORT cmd for each data connection";
+#ifndef NOENCRYPTION
+char   privatehelp[] = "set private protection level for file transfer";
+#endif
+char   prompthelp[] =  "force interactive prompting on multiple commands";
+char   proxyhelp[] =   "issue command on alternate connection";
+char   pwdhelp[] =     "print working directory on remote machine";
+char   quithelp[] =    "terminate ftp session and exit";
+char   quotehelp[] =   "send arbitrary ftp command";
+char   receivehelp[] = "receive file";
+char   regethelp[] =   "get file restarting at end of local file";
+char   remotehelp[] =  "get help from remote server";
+char   renamehelp[] =  "rename file";
+char   restarthelp[]=  "restart file transfer at bytecount";
+char   rmdirhelp[] =   "remove directory on the remote machine";
+char   rmtstatushelp[]="show status of remote machine";
+char   runiquehelp[] = "toggle store unique for local files";
+char   resethelp[] =   "clear queued command replies";
+char   safehelp[] =    "set safe protection level for file transfer";
+char   sendhelp[] =    "send one file";
+char   sitehelp[] =    "send site specific command to remote server\n\t\tTry \"rhelp site\" or \"site help\" for more information";
+char   shellhelp[] =   "escape to the shell";
+char   sizecmdhelp[] = "show size of remote file";
+char   statushelp[] =  "show current status";
+char   structhelp[] =  "set file transfer structure";
+char   suniquehelp[] = "toggle store unique on remote machine";
+char   systemhelp[] =  "show remote system type";
+char   tenexhelp[] =   "set tenex file transfer type";
+char   tracehelp[] =   "toggle packet tracing";
+char   typehelp[] =    "set file transfer type";
+char   umaskhelp[] =   "get (set) umask on remote side";
+char   userhelp[] =    "send new user information";
+char   verbosehelp[] = "toggle verbose mode";
+#ifndef NO_PASSIVE_MODE
+char   setpassivehelp[] = "enter passive transfer mode";
+#endif
+
+struct cmd cmdtab[] = {
+       { "!",          shellhelp,      0,      0,      0,      shell },
+       { "$",          domachelp,      1,      0,      0,      domacro },
+       { "account",    accounthelp,    0,      1,      1,      account},
+       { "append",     appendhelp,     1,      1,      1,      put },
+       { "ascii",      asciihelp,      0,      1,      1,      setascii },
+       { "bell",       beephelp,       0,      0,      0,      setbell },
+       { "binary",     binaryhelp,     0,      1,      1,      setbinary },
+       { "bye",        quithelp,       0,      0,      0,      quit },
+       { "case",       casehelp,       0,      0,      1,      setcase },
+       { "cd",         cdhelp,         0,      1,      1,      cd },
+       { "cdup",       cduphelp,       0,      1,      1,      cdup },
+       { "chmod",      chmodhelp,      0,      1,      1,      do_chmod },
+       { "clear",      clearhelp,      0,      1,      1,      setclear },
+       { "close",      disconhelp,     0,      1,      1,      disconnect },
+       { "cr",         crhelp,         0,      0,      0,      setcr },
+       { "delete",     deletehelp,     0,      1,      1,      delete },
+       { "debug",      debughelp,      0,      0,      0,      setdebug },
+       { "dir",        dirhelp,        1,      1,      1,      ls },
+       { "disconnect", disconhelp,     0,      1,      1,      disconnect },
+       { "form",       formhelp,       0,      1,      1,      setform },
+       { "get",        receivehelp,    1,      1,      1,      get },
+       { "glob",       globhelp,       0,      0,      0,      setglob },
+       { "hash",       hashhelp,       0,      0,      0,      sethash },
+       { "help",       helphelp,       0,      0,      1,      help },
+       { "idle",       idlehelp,       0,      1,      1,      siteidle },
+       { "image",      binaryhelp,     0,      1,      1,      setbinary },
+       { "lcd",        lcdhelp,        0,      0,      0,      lcd },
+       { "ls",         lshelp,         1,      1,      1,      ls },
+       { "macdef",     macdefhelp,     0,      0,      0,      macdef },
+       { "mdelete",    mdeletehelp,    1,      1,      1,      mdelete },
+       { "mdir",       mdirhelp,       1,      1,      1,      mls },
+       { "mget",       mgethelp,       1,      1,      1,      mget },
+       { "mkdir",      mkdirhelp,      0,      1,      1,      makedir },
+       { "mls",        mlshelp,        1,      1,      1,      mls },
+       { "mode",       modehelp,       0,      1,      1,      setmode },
+       { "modtime",    modtimehelp,    0,      1,      1,      modtime },
+       { "mput",       mputhelp,       1,      1,      1,      mput },
+       { "newer",      newerhelp,      1,      1,      1,      newer },
+       { "nmap",       nmaphelp,       0,      0,      1,      setnmap },
+       { "nlist",      nlisthelp,      1,      1,      1,      ls },
+       { "ntrans",     ntranshelp,     0,      0,      1,      setntrans },
+       { "open",       connecthelp,    0,      0,      1,      setpeer },
+#ifndef NO_PASSIVE_MODE
+       { "passive",    setpassivehelp, 0,      0,      0,      setpassive },
+#endif
+#ifndef NOENCRYPTION
+       { "private",    privatehelp,    0,      1,      1,      setprivate },
+#endif
+       { "prompt",     prompthelp,     0,      0,      0,      setprompt },
+       { "protect",    levelhelp,      0,      1,      1,      setlevel },
+       { "proxy",      proxyhelp,      0,      0,      1,      doproxy },
+       { "sendport",   porthelp,       0,      0,      0,      setport },
+       { "put",        sendhelp,       1,      1,      1,      put },
+       { "pwd",        pwdhelp,        0,      1,      1,      pwd },
+       { "quit",       quithelp,       0,      0,      0,      quit },
+       { "quote",      quotehelp,      1,      1,      1,      quote },
+       { "recv",       receivehelp,    1,      1,      1,      get },
+       { "reget",      regethelp,      1,      1,      1,      reget },
+       { "rstatus",    rmtstatushelp,  0,      1,      1,      rmtstatus },
+       { "rhelp",      remotehelp,     0,      1,      1,      rmthelp },
+       { "rename",     renamehelp,     0,      1,      1,      renamefile },
+       { "reset",      resethelp,      0,      1,      1,      reset },
+       { "restart",    restarthelp,    1,      1,      1,      restart },
+       { "rmdir",      rmdirhelp,      0,      1,      1,      removedir },
+       { "runique",    runiquehelp,    0,      0,      1,      setrunique },
+       { "safe",       safehelp,       0,      1,      1,      setsafe },
+       { "send",       sendhelp,       1,      1,      1,      put },
+       { "site",       sitehelp,       0,      1,      1,      site },
+       { "size",       sizecmdhelp,    1,      1,      1,      sizecmd },
+       { "status",     statushelp,     0,      0,      1,      status },
+       { "struct",     structhelp,     0,      1,      1,      setstruct },
+       { "system",     systemhelp,     0,      1,      1,      syst },
+       { "sunique",    suniquehelp,    0,      0,      1,      setsunique },
+       { "tenex",      tenexhelp,      0,      1,      1,      settenex },
+       { "trace",      tracehelp,      0,      0,      0,      settrace },
+       { "type",       typehelp,       0,      1,      1,      settype },
+       { "user",       userhelp,       0,      1,      1,      user },
+       { "umask",      umaskhelp,      0,      1,      1,      do_umask },
+       { "verbose",    verbosehelp,    0,      0,      0,      setverbose },
+       { "?",          helphelp,       0,      0,      1,      help },
+       { 0 },
+};
+
+int    NCMDS = (sizeof (cmdtab) / sizeof (cmdtab[0])) - 1;
diff --git a/src/appl/gssftp/ftp/configure.in b/src/appl/gssftp/ftp/configure.in
new file mode 100644 (file)
index 0000000..a03170b
--- /dev/null
@@ -0,0 +1,12 @@
+AC_INIT(ftp.c)
+CONFIG_RULES
+AC_CONST
+AC_PROG_INSTALL
+KRB5_SIGTYPE
+CHECK_SIGPROCMASK
+CHECK_WAIT_TYPE
+AC_FUNC_VFORK
+AC_HAVE_FUNCS(getcwd getdtablesize)
+AC_HEADER_CHECK(termios.h,AC_FUNC_CHECK(cfsetispeed,AC_DEFINE(POSIX_TERMIOS)))
+V5_USE_SHARED_LIB
+V5_AC_OUTPUT_MAKEFILE
diff --git a/src/appl/gssftp/ftp/domacro.c b/src/appl/gssftp/ftp/domacro.c
new file mode 100644 (file)
index 0000000..9bc277b
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 1985 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[] = "@(#)domacro.c  1.8 (Berkeley) 9/28/90";
+#endif /* not lint */
+
+#include <stdio.h>
+
+#include "ftp_var.h"
+
+#include <signal.h>
+#include <errno.h>
+#include <ctype.h>
+#include <string.h>
+
+domacro(argc, argv)
+       int argc;
+       char *argv[];
+{
+       register int i, j;
+       register char *cp1, *cp2;
+       int count = 2, loopflg = 0;
+       char line2[200];
+       extern char **glob(), *globerr;
+       struct cmd *getcmd(), *c;
+       extern struct cmd cmdtab[];
+
+       if (argc < 2 && !another(&argc, &argv, "macro name")) {
+               printf("Usage: %s macro_name.\n", argv[0]);
+               code = -1;
+               return;
+       }
+       for (i = 0; i < macnum; ++i) {
+               if (!strncmp(argv[1], macros[i].mac_name, 9)) {
+                       break;
+               }
+       }
+       if (i == macnum) {
+               printf("'%s' macro not found.\n", argv[1]);
+               code = -1;
+               return;
+       }
+       (void) strcpy(line2, line);
+TOP:
+       cp1 = macros[i].mac_start;
+       while (cp1 != macros[i].mac_end) {
+               while (isspace(*cp1)) {
+                       cp1++;
+               }
+               cp2 = line;
+               while (*cp1 != '\0') {
+                     switch(*cp1) {
+                           case '\\':
+                                *cp2++ = *++cp1;
+                                break;
+                           case '$':
+                                if (isdigit(*(cp1+1))) {
+                                   j = 0;
+                                   while (isdigit(*++cp1)) {
+                                         j = 10*j +  *cp1 - '0';
+                                   }
+                                   cp1--;
+                                   if (argc - 2 >= j) {
+                                       (void) strcpy(cp2, argv[j+1]);
+                                       cp2 += strlen(argv[j+1]);
+                                   }
+                                   break;
+                                }
+                                if (*(cp1+1) == 'i') {
+                                       loopflg = 1;
+                                       cp1++;
+                                       if (count < argc) {
+                                          (void) strcpy(cp2, argv[count]);
+                                          cp2 += strlen(argv[count]);
+                                       }
+                                       break;
+                               }
+                               /* intentional drop through */
+                           default:
+                               *cp2++ = *cp1;
+                               break;
+                     }
+                     if (*cp1 != '\0') {
+                        cp1++;
+                     }
+               }
+               *cp2 = '\0';
+               makeargv();
+               c = getcmd(margv[0]);
+               if (c == (struct cmd *)-1) {
+                       printf("?Ambiguous command\n");
+                       code = -1;
+               }
+               else if (c == 0) {
+                       printf("?Invalid command\n");
+                       code = -1;
+               }
+               else if (c->c_conn && !connected) {
+                       printf("Not connected.\n");
+                       code = -1;
+               }
+               else {
+                       if (verbose) {
+                               printf("%s\n",line);
+                       }
+                       (*c->c_handler)(margc, margv);
+                       if (bell && c->c_bell) {
+                               (void) putchar('\007');
+                       }
+                       (void) strcpy(line, line2);
+                       makeargv();
+                       argc = margc;
+                       argv = margv;
+               }
+               if (cp1 != macros[i].mac_end) {
+                       cp1++;
+               }
+       }
+       if (loopflg && ++count < argc) {
+               goto TOP;
+       }
+}
diff --git a/src/appl/gssftp/ftp/ftp.M b/src/appl/gssftp/ftp/ftp.M
new file mode 100644 (file)
index 0000000..4629183
--- /dev/null
@@ -0,0 +1,1200 @@
+.\" Copyright (c) 1985, 1989, 1990 The 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.
+.\"
+.\"    @(#)ftp.1       6.18 (Berkeley) 7/30/91
+.\"
+.so man1/tmac.doc
+.Dd July 30, 1991
+.Dt FTP 1
+.Os BSD 4.2
+.Sh NAME
+.Nm ftp
+.Nd
+.Tn ARPANET
+file transfer program
+.Sh SYNOPSIS
+.Nm ftp
+.Op Fl v
+.Op Fl d
+.Op Fl i
+.Op Fl n
+.Op Fl g
+.Op Fl k Ar realm
+.Op Ar host
+.Sh DESCRIPTION
+.Nm Ftp
+is the user interface to the
+.Tn ARPANET
+standard File Transfer Protocol.
+The program allows a user to transfer files to and from a
+remote network site.
+.Pp
+Options may be specified at the command line, or to the
+command interpreter.
+.Bl -tag -width flag
+.It Fl v
+Verbose option forces
+.Nm ftp
+to show all responses from the remote server, as well
+as report on data transfer statistics.
+.It Fl n
+Restrains
+.Nm ftp
+from attempting \*(Lqauto-login\*(Rq upon initial connection.
+If auto-login is enabled,
+.Nm ftp
+attempts to authenticate to the
+.Tn FTP
+server by sending the
+.Dv AUTH
+command, using whichever authentication types are locally supported.
+Once an authentication type is accepted, an authentication protocol
+will proceed by issuing
+.Dv ADAT
+commands.
+.Nm ftp
+then
+will check the
+.Pa .netrc
+(see below) file in the user's home directory for an entry describing
+an account on the remote machine.
+If no entry exists,
+.Nm ftp
+will prompt for the remote machine login name (default is the user
+identity on the local machine), and, if necessary, prompt for a password
+and an account with which to login.
+.It Fl i
+Turns off interactive prompting during
+multiple file transfers.
+.It Fl d
+Enables debugging.
+.It Fl g
+Disables file name globbing.
+.It Fl k Ar realm
+When using Kerberos V4 authentication, get tickets in
+.Ar realm.
+.El
+.Pp
+The client host with which
+.Nm ftp
+is to communicate may be specified on the command line.
+If this is done,
+.Nm ftp
+will immediately attempt to establish a connection to an
+.Tn FTP
+server on that host; otherwise,
+.Nm ftp
+will enter its command interpreter and await instructions
+from the user.
+When
+.Nm ftp
+is awaiting commands from the user the prompt
+.Ql ftp>
+is provided to the user.
+The following commands are recognized
+by
+.Nm ftp  :
+.Bl -tag -width Fl
+.It Ic \&! Op Ar command Op Ar args
+Invoke an interactive shell on the local machine.
+If there are arguments, the first is taken to be a command to execute
+directly, with the rest of the arguments as its arguments.
+.It Ic \&$ Ar macro-name Op Ar args
+Execute the macro
+.Ar macro-name
+that was defined with the
+.Ic macdef
+command.
+Arguments are passed to the macro unglobbed.
+.It Ic account Op Ar passwd
+Supply a supplemental password required by a remote system for access
+to resources once a login has been successfully completed.
+If no argument is included, the user will be prompted for an account
+password in a non-echoing input mode.
+.It Ic append Ar local-file Op Ar remote-file
+Append a local file to a file on the remote machine.
+If
+.Ar remote-file
+is left unspecified, the local file name is used in naming the
+remote file after being altered by any
+.Ic ntrans
+or
+.Ic nmap
+setting.
+File transfer uses the current settings for
+.Ic type  ,
+.Ic format ,
+.Ic mode  ,
+and
+.Ic structure .
+.It Ic ascii
+Set the file transfer
+.Ic type
+to network
+.Tn ASCII .
+This is the default type.
+.It Ic bell
+Arrange that a bell be sounded after each file transfer
+command is completed.
+.It Ic binary
+Set the file transfer
+.Ic type
+to support binary image transfer.
+.It Ic bye
+Terminate the
+.Tn FTP
+session with the remote server
+and exit
+.Nm ftp  .
+An end of file will also terminate the session and exit.
+.It Ic case
+Toggle remote computer file name case mapping during
+.Ic mget
+commands.
+When
+.Ic case
+is on (default is off), remote computer file names with all letters in
+upper case are written in the local directory with the letters mapped
+to lower case.
+.It Ic \&cd Ar remote-directory
+Change the working directory on the remote machine
+to
+.Ar remote-directory  .
+.It Ic cdup
+Change the remote machine working directory to the parent of the
+current remote machine working directory.
+.It Ic chmod Ar mode file-name
+Change the permission modes of the file
+.Ar file-name
+on the remote
+system to
+.Ar mode  .
+.It Ic clear
+Set the protection level on data transfers to \*(Lqclear\*(Rq.
+If no
+.Dv ADAT
+command succeeded, then this is the default protection level.
+.It Ic close
+Terminate the
+.Tn FTP
+session with the remote server, and
+return to the command interpreter.
+Any defined macros are erased.
+.It Ic \&cr
+Toggle carriage return stripping during
+ascii type file retrieval.
+Records are denoted by a carriage return/linefeed sequence
+during ascii type file transfer.
+When
+.Ic \&cr
+is on (the default), carriage returns are stripped from this
+sequence to conform with the
+.Ux
+single linefeed record
+delimiter.
+Records on
+.Pf non\- Ns Ux
+remote systems may contain single linefeeds;
+when an ascii type transfer is made, these linefeeds may be
+distinguished from a record delimiter only when
+.Ic \&cr
+is off.
+.It Ic delete Ar remote-file
+Delete the file
+.Ar remote-file
+on the remote machine.
+.It Ic debug Op Ar debug-value
+Toggle debugging mode.
+If an optional
+.Ar debug-value
+is specified it is used to set the debugging level.
+When debugging is on,
+.Nm ftp
+prints each command sent to the remote machine, preceded
+by the string
+.Ql \-\->
+.It Xo
+.Ic dir
+.Op Ar remote-directory
+.Op Ar local-file
+.Xc
+Print a listing of the directory contents in the
+directory,
+.Ar remote-directory  ,
+and, optionally, placing the output in
+.Ar local-file  .
+If interactive prompting is on,
+.Nm ftp
+will prompt the user to verify that the last argument is indeed the
+target local file for receiving
+.Ic dir
+output.
+If no directory is specified, the current working
+directory on the remote machine is used.
+If no local
+file is specified, or
+.Ar local-file
+is
+.Fl  ,
+output comes to the terminal.
+.It Ic disconnect
+A synonym for
+.Ar close  .
+.It Ic form Ar format
+Set the file transfer
+.Ic form
+to
+.Ar format  .
+The default format is \*(Lqfile\*(Rq.
+.It Ic get Ar remote-file Op Ar local-file
+Retrieve the
+.Ar remote-file
+and store it on the local machine.
+If the local
+file name is not specified, it is given the same
+name it has on the remote machine, subject to
+alteration by the current
+.Ic case  ,
+.Ic ntrans ,
+and
+.Ic nmap
+settings.
+The current settings for
+.Ic type  ,
+.Ic form ,
+.Ic mode  ,
+and
+.Ic structure
+are used while transferring the file.
+.It Ic glob
+Toggle filename expansion for
+.Ic mdelete  ,
+.Ic mget
+and
+.Ic mput  .
+If globbing is turned off with
+.Ic glob  ,
+the file name arguments
+are taken literally and not expanded.
+Globbing for
+.Ic mput
+is done as in
+.Xr csh 1 .
+For
+.Ic mdelete
+and
+.Ic mget  ,
+each remote file name is expanded
+separately on the remote machine and the lists are not merged.
+Expansion of a directory name is likely to be
+different from expansion of the name of an ordinary file:
+the exact result depends on the foreign operating system and ftp server,
+and can be previewed by doing
+.Ql mls remote-files \-
+Note:
+.Ic mget
+and
+.Ic mput
+are not meant to transfer
+entire directory subtrees of files.
+That can be done by
+transferring a
+.Xr tar 1
+archive of the subtree (in binary mode).
+.It Ic hash
+Toggle hash-sign (``#'') printing for each data block
+transferred.
+The size of a data block is 1024 bytes.
+.It Ic help Op Ar command
+Print an informative message about the meaning of
+.Ar command  .
+If no argument is given,
+.Nm ftp
+prints a list of the known commands.
+.It Ic idle Op Ar seconds
+Set the inactivity timer on the remote server to
+.Ar seconds
+seconds.
+If
+.Ar seconds
+is omitted, the current inactivity timer is printed.
+.It Ic lcd Op Ar directory
+Change the working directory on the local machine.
+If
+no
+.Ar directory
+is specified, the user's home directory is used.
+.It Xo
+.Ic \&ls
+.Op Ar remote-directory
+.Op Ar local-file
+.Xc
+Print a listing of the contents of a
+directory on the remote machine.
+The listing includes any system-dependent information that the server
+chooses to include; for example, most
+.Ux
+systems will produce
+output from the command
+.Ql ls \-l .
+(See also
+.Ic nlist . )
+If
+.Ar remote-directory
+is left unspecified, the current working directory is used.
+If interactive prompting is on,
+.Nm ftp
+will prompt the user to verify that the last argument is indeed the
+target local file for receiving
+.Ic \&ls
+output.
+If no local file is specified, or if
+.Ar local-file
+is
+.Sq Fl ,
+the output is sent to the terminal.
+.It Ic macdef Ns Ar macro-name
+Define a macro.
+Subsequent lines are stored as the macro
+.Ar macro-name  ;
+a null line (consecutive newline characters
+in a file or
+carriage returns from the terminal) terminates macro input mode.
+There is a limit of 16 macros and 4096 total characters in all
+defined macros.
+Macros remain defined until a
+.Ic close
+command is executed.
+The macro processor interprets `$' and `\e' as special characters.
+A `$' followed by a number (or numbers) is replaced by the
+corresponding argument on the macro invocation command line.
+A `$' followed by an `i' signals that macro processor that the
+executing macro is to be looped.
+On the first pass `$i' is
+replaced by the first argument on the macro invocation command line,
+on the second pass it is replaced by the second argument, and so on.
+A `\e' followed by any character is replaced by that character.
+Use the `\e' to prevent special treatment of the `$'.
+.It Ic mdelete Op Ar remote-files
+Delete the
+.Ar remote-files
+on the remote machine.
+.It Ic mdir Ar remote-files local-file
+Like
+.Ic dir  ,
+except multiple remote files may be specified.
+If interactive prompting is on,
+.Nm ftp
+will prompt the user to verify that the last argument is indeed the
+target local file for receiving
+.Ic mdir
+output.
+.It Ic mget Ar remote-files
+Expand the
+.Ar remote-files
+on the remote machine
+and do a
+.Ic get
+for each file name thus produced.
+See
+.Ic glob
+for details on the filename expansion.
+Resulting file names will then be processed according to
+.Ic case  ,
+.Ic ntrans ,
+and
+.Ic nmap
+settings.
+Files are transferred into the local working directory,
+which can be changed with
+.Ql lcd directory ;
+new local directories can be created with
+.Ql "\&! mkdir directory" .
+.It Ic mkdir Ar directory-name
+Make a directory on the remote machine.
+.It Ic mls Ar remote-files local-file
+Like
+.Ic nlist  ,
+except multiple remote files may be specified,
+and the
+.Ar local-file
+must be specified.
+If interactive prompting is on,
+.Nm ftp
+will prompt the user to verify that the last argument is indeed the
+target local file for receiving
+.Ic mls
+output.
+.It Ic mode Op Ar mode-name
+Set the file transfer
+.Ic mode
+to
+.Ar mode-name  .
+The default mode is \*(Lqstream\*(Rq mode.
+.It Ic modtime Ar file-name
+Show the last modification time of the file on the remote machine.
+.It Ic mput Ar local-files
+Expand wild cards in the list of local files given as arguments
+and do a
+.Ic put
+for each file in the resulting list.
+See
+.Ic glob
+for details of filename expansion.
+Resulting file names will then be processed according to
+.Ic ntrans
+and
+.Ic nmap
+settings.
+.It Ic newer Ar file-name
+Get the file only if the modification time of the remote file is more
+recent that the file on the current system.
+If the file does not
+exist on the current system, the remote file is considered
+.Ic newer  .
+Otherwise, this command is identical to
+.Ar get  .
+.It Xo
+.Ic nlist
+.Op Ar remote-directory
+.Op Ar local-file
+.Xc
+Print a  list of the files in a
+directory on the remote machine.
+If
+.Ar remote-directory
+is left unspecified, the current working directory is used.
+If interactive prompting is on,
+.Nm ftp
+will prompt the user to verify that the last argument is indeed the
+target local file for receiving
+.Ic nlist
+output.
+If no local file is specified, or if
+.Ar local-file
+is
+.Fl  ,
+the output is sent to the terminal.
+.It Ic nmap Op Ar inpattern outpattern
+Set or unset the filename mapping mechanism.
+If no arguments are specified, the filename mapping mechanism is unset.
+If arguments are specified, remote filenames are mapped during
+.Ic mput
+commands and
+.Ic put
+commands issued without a specified remote target filename.
+If arguments are specified, local filenames are mapped during
+.Ic mget
+commands and
+.Ic get
+commands issued without a specified local target filename.
+This command is useful when connecting to a
+.No non\- Ns Ux
+remote computer
+with different file naming conventions or practices.
+The mapping follows the pattern set by
+.Ar inpattern
+and
+.Ar outpattern  .
+.Op Ar Inpattern
+is a template for incoming filenames (which may have already been
+processed according to the
+.Ic ntrans
+and
+.Ic case
+settings).
+Variable templating is accomplished by including the
+sequences `$1', `$2', ..., `$9' in
+.Ar inpattern  .
+Use `\\' to prevent this special treatment of the `$' character.
+All other characters are treated literally, and are used to determine the
+.Ic nmap
+.Op Ar inpattern
+variable values.
+For example, given
+.Ar inpattern
+$1.$2 and the remote file name "mydata.data", $1 would have the value
+"mydata", and $2 would have the value "data".
+The
+.Ar outpattern
+determines the resulting mapped filename.
+The sequences `$1', `$2', ...., `$9' are replaced by any value resulting
+from the
+.Ar inpattern
+template.
+The sequence `$0' is replace by the original filename.
+Additionally, the sequence
+.Ql Op Ar seq1 , Ar seq2
+is replaced by
+.Op Ar seq1
+if
+.Ar seq1
+is not a null string; otherwise it is replaced by
+.Ar seq2 .
+For example, the command
+.Pp
+.Bd -literal -offset indent -compact
+nmap $1.$2.$3 [$1,$2].[$2,file]
+.Ed
+.Pp
+would yield
+the output filename "myfile.data" for input filenames "myfile.data" and
+"myfile.data.old", "myfile.file" for the input filename "myfile", and
+"myfile.myfile" for the input filename ".myfile".
+Spaces may be included in
+.Ar outpattern  ,
+as in the example: `nmap $1 sed "s/  *$//" > $1' .
+Use the `\e' character to prevent special treatment
+of the `$','[','[', and `,' characters.
+.It Ic ntrans Op Ar inchars Op Ar outchars
+Set or unset the filename character translation mechanism.
+If no arguments are specified, the filename character
+translation mechanism is unset.
+If arguments are specified, characters in
+remote filenames are translated during
+.Ic mput
+commands and
+.Ic put
+commands issued without a specified remote target filename.
+If arguments are specified, characters in
+local filenames are translated during
+.Ic mget
+commands and
+.Ic get
+commands issued without a specified local target filename.
+This command is useful when connecting to a
+.No non\- Ns Ux
+remote computer
+with different file naming conventions or practices.
+Characters in a filename matching a character in
+.Ar inchars
+are replaced with the corresponding character in
+.Ar outchars  .
+If the character's position in
+.Ar inchars
+is longer than the length of
+.Ar outchars  ,
+the character is deleted from the file name.
+.It Ic open Ar host Op Ar port
+Establish a connection to the specified
+.Ar host
+.Tn FTP
+server.
+An optional port number may be supplied,
+in which case,
+.Nm ftp
+will attempt to contact an
+.Tn FTP
+server at that port.
+If the
+.Ic auto-login
+option is on (default),
+.Nm ftp
+will attempt to authenticate to the
+.Tn FTP
+server by sending the
+.Dv AUTH
+command, using whichever authentication types which are locally supported.
+Once an authentication type is accepted, an authentication protocol
+will proceed by issuing
+.Dv ADAT
+commands.
+.Nm ftp
+will also attempt to automatically log the user in to
+the
+.Tn FTP
+server (see below).
+.It Ic private
+Set the protection level on data transfers to \*(Lqprivate\*(Rq.
+Data transmissions will now be
+confidentiality and integrity protected by encryption.
+If no
+.Dv ADAT
+command succeeded, then the only possible level is \*(Lqclear\*(Rq.
+.It Ic prompt
+Toggle interactive prompting.
+Interactive prompting
+occurs during multiple file transfers to allow the
+user to selectively retrieve or store files.
+If prompting is turned off (default is on), any
+.Ic mget
+or
+.Ic mput
+will transfer all files, and any
+.Ic mdelete
+will delete all files.
+.It Ic protect Op Ar protection-level
+Set the protection level on data transfers to
+.Ar protection-level .
+The valid protection levels are \*(Lqclear\*(Rq
+for unprotected data transmissions, \*(Lqsafe\*(Rq
+for data transmissions integrity
+protected by cryptographic checksum, and \*(Lqprivate\*(Rq
+for data transmissions confidentiality and integrity
+protected by encryption.
+If no
+.Dv ADAT
+command succeeded, then the only possible level is \*(Lqclear\*(Rq.
+If no level is specified, the current level is printed.
+The default protection level is \*(Lqclear\*(Rq.
+.It Ic proxy Ar ftp-command
+Execute an ftp command on a secondary control connection.
+This command allows simultaneous connection to two remote ftp
+servers for transferring files between the two servers.
+The first
+.Ic proxy
+command should be an
+.Ic open  ,
+to establish the secondary control connection.
+Enter the command "proxy ?" to see other ftp commands executable on the
+secondary connection.
+The following commands behave differently when prefaced by
+.Ic proxy  :
+.Ic open
+will not define new macros during the auto-login process,
+.Ic close
+will not erase existing macro definitions,
+.Ic get
+and
+.Ic mget
+transfer files from the host on the primary control connection
+to the host on the secondary control connection, and
+.Ic put  ,
+.Ic mput ,
+and
+.Ic append
+transfer files from the host on the secondary control connection
+to the host on the primary control connection.
+Third party file transfers depend upon support of the ftp protocol
+.Dv PASV
+command by the server on the secondary control connection.
+.It Ic put Ar local-file Op Ar remote-file
+Store a local file on the remote machine.
+If
+.Ar remote-file
+is left unspecified, the local file name is used
+after processing according to any
+.Ic ntrans
+or
+.Ic nmap
+settings
+in naming the remote file.
+File transfer uses the
+current settings for
+.Ic type  ,
+.Ic format ,
+.Ic mode  ,
+and
+.Ic structure  .
+.It Ic pwd
+Print the name of the current working directory on the remote
+machine.
+.It Ic quit
+A synonym for
+.Ic bye  .
+.It Ic quote Ar arg1 arg2 ...
+The arguments specified are sent, verbatim, to the remote
+.Tn FTP
+server.
+.It Ic recv Ar remote-file Op Ar local-file
+A synonym for get.
+.It Ic reget Ar remote-file Op Ar local-file
+Reget acts like get, except that if
+.Ar local-file
+exists and is
+smaller than
+.Ar remote-file  ,
+.Ar local-file
+is presumed to be
+a partially transferred copy of
+.Ar remote-file
+and the transfer
+is continued from the apparent point of failure.
+This command
+is useful when transferring very large files over networks that
+are prone to dropping connections.
+.It Ic remotehelp Op Ar command-name
+Request help from the remote
+.Tn FTP
+server.
+If a
+.Ar command-name
+is specified it is supplied to the server as well.
+.It Ic remotestatus Op Ar file-name
+With no arguments, show status of remote machine.
+If
+.Ar file-name
+is specified, show status of
+.Ar file-name
+on remote machine.
+.It Xo
+.Ic rename
+.Op Ar from
+.Op Ar to
+.Xc
+Rename the file
+.Ar from
+on the remote machine, to the file
+.Ar to  .
+.It Ic reset
+Clear reply queue.
+This command re-synchronizes command/reply sequencing with the remote
+ftp server.
+Resynchronization may be necessary following a violation of the ftp protocol
+by the remote server.
+.It Ic restart Ar marker
+Restart the immediately following
+.Ic get
+or
+.Ic put
+at the
+indicated
+.Ar marker  .
+On
+.Ux
+systems, marker is usually a byte
+offset into the file.
+.It Ic rmdir Ar directory-name
+Delete a directory on the remote machine.
+.It Ic runique
+Toggle storing of files on the local system with unique filenames.
+If a file already exists with a name equal to the target
+local filename for a
+.Ic get
+or
+.Ic mget
+command, a ".1" is appended to the name.
+If the resulting name matches another existing file,
+a ".2" is appended to the original name.
+If this process continues up to ".99", an error
+message is printed, and the transfer does not take place.
+The generated unique filename will be reported.
+Note that
+.Ic runique
+will not affect local files generated from a shell command
+(see below).
+The default value is off.
+.It Ic safe
+Set the protection level on data transfers to \*(Lqsafe\*(Rq.
+Data transmissions will now be
+integrity protected by cryptographic checksum.
+If no
+.Dv ADAT
+command succeeded, then the only possible level is \*(Lqclear\*(Rq.
+.It Ic send Ar local-file Op Ar remote-file
+A synonym for put.
+.It Ic sendport
+Toggle the use of
+.Dv PORT
+commands.
+By default,
+.Nm ftp
+will attempt to use a
+.Dv PORT
+command when establishing
+a connection for each data transfer.
+The use of
+.Dv PORT
+commands can prevent delays
+when performing multiple file transfers.
+If the
+.Dv PORT
+command fails,
+.Nm ftp
+will use the default data port.
+When the use of
+.Dv PORT
+commands is disabled, no attempt will be made to use
+.Dv PORT
+commands for each data transfer.
+This is useful
+for certain
+.Tn FTP
+implementations which do ignore
+.Dv PORT
+commands but, incorrectly, indicate they've been accepted.
+.It Ic site Ar arg1 arg2 ...
+The arguments specified are sent, verbatim, to the remote
+.Tn FTP
+server as a
+.Dv SITE
+command.
+.It Ic size Ar file-name
+Return size of
+.Ar file-name
+on remote machine.
+.It Ic status
+Show the current status of
+.Nm ftp  .
+.It Ic struct Op Ar struct-name
+Set the file transfer
+.Ar structure
+to
+.Ar struct-name .
+By default \*(Lqstream\*(Rq structure is used.
+.It Ic sunique
+Toggle storing of files on remote machine under unique file names.
+Remote ftp server must support ftp protocol
+.Dv STOU
+command for
+successful completion.
+The remote server will report unique name.
+Default value is off.
+.It Ic system
+Show the type of operating system running on the remote machine.
+.It Ic tenex
+Set the file transfer type to that needed to
+talk to
+.Tn TENEX
+machines.
+.It Ic trace
+Toggle packet tracing.
+.It Ic type Op Ar type-name
+Set the file transfer
+.Ic type
+to
+.Ar type-name  .
+If no type is specified, the current type
+is printed.
+The default type is network
+.Tn ASCII .
+.It Ic umask Op Ar newmask
+Set the default umask on the remote server to
+.Ar newmask  .
+If
+.Ar newmask
+is omitted, the current umask is printed.
+.It Xo
+.Ic user Ar user-name
+.Op Ar password
+.Op Ar account
+.Xc
+Identify yourself to the remote
+.Tn FTP
+server.
+If the
+.Ar password
+is not specified and the server requires it,
+.Nm ftp
+will prompt the user for it (after disabling local echo).
+If an
+.Ar account
+field is not specified, and the
+.Tn FTP
+server
+requires it, the user will be prompted for it.
+If an
+.Ar account
+field is specified, an account command will
+be relayed to the remote server after the login sequence
+is completed if the remote server did not require it
+for logging in.
+Unless
+.Nm ftp
+is invoked with \*(Lqauto-login\*(Rq disabled, this
+process is done automatically on initial connection to
+the
+.Tn FTP
+server.
+.It Ic verbose
+Toggle verbose mode.
+In verbose mode, all responses from
+the
+.Tn FTP
+server are displayed to the user.
+In addition,
+if verbose is on, when a file transfer completes, statistics
+regarding the efficiency of the transfer are reported.
+By default,
+verbose is on.
+.It Ic ? Op Ar command
+A synonym for help.
+.El
+.Pp
+Command arguments which have embedded spaces may be quoted with
+quote `"' marks.
+.Sh ABORTING A FILE TRANSFER
+To abort a file transfer, use the terminal interrupt key
+(usually Ctrl-C).
+Sending transfers will be immediately halted.
+Receiving transfers will be halted by sending a ftp protocol
+.Dv ABOR
+command to the remote server, and discarding any further data received.
+The speed at which this is accomplished depends upon the remote
+server's support for
+.Dv ABOR
+processing.
+If the remote server does not support the
+.Dv ABOR
+command, an
+.Ql ftp>
+prompt will not appear until the remote server has completed
+sending the requested file.
+.Pp
+The terminal interrupt key sequence will be ignored when
+.Nm ftp
+has completed any local processing and is awaiting a reply
+from the remote server.
+A long delay in this mode may result from the ABOR processing described
+above, or from unexpected behavior by the remote server, including
+violations of the ftp protocol.
+If the delay results from unexpected remote server behavior, the local
+.Nm ftp
+program must be killed by hand.
+.Sh FILE NAMING CONVENTIONS
+Files specified as arguments to
+.Nm ftp
+commands are processed according to the following rules.
+.Bl -enum
+.It
+If the file name
+.Sq Fl
+is specified, the
+.Ar stdin
+(for reading) or
+.Ar stdout
+(for writing) is used.
+.It
+If the first character of the file name is
+.Sq \&| ,
+the
+remainder of the argument is interpreted as a shell command.
+.Nm Ftp
+then forks a shell, using
+.Xr popen 3
+with the argument supplied, and reads (writes) from the stdout
+(stdin).
+If the shell command includes spaces, the argument
+must be quoted; e.g.
+\*(Lq" ls -lt"\*(Rq.
+A particularly
+useful example of this mechanism is: \*(Lqdir more\*(Rq.
+.It
+Failing the above checks, if ``globbing'' is enabled,
+local file names are expanded
+according to the rules used in the
+.Xr csh  1  ;
+c.f. the
+.Ic glob
+command.
+If the
+.Nm ftp
+command expects a single local file (.e.g.
+.Ic put  ) ,
+only the first filename generated by the "globbing" operation is used.
+.It
+For
+.Ic mget
+commands and
+.Ic get
+commands with unspecified local file names, the local filename is
+the remote filename, which may be altered by a
+.Ic case  ,
+.Ic ntrans ,
+or
+.Ic nmap
+setting.
+The resulting filename may then be altered if
+.Ic runique
+is on.
+.It
+For
+.Ic mput
+commands and
+.Ic put
+commands with unspecified remote file names, the remote filename is
+the local filename, which may be altered by a
+.Ic ntrans
+or
+.Ic nmap
+setting.
+The resulting filename may then be altered by the remote server if
+.Ic sunique
+is on.
+.El
+.Sh FILE TRANSFER PARAMETERS
+The FTP specification specifies many parameters which may
+affect a file transfer.
+The
+.Ic type
+may be one of \*(Lqascii\*(Rq, \*(Lqimage\*(Rq (binary),
+\*(Lqebcdic\*(Rq, and \*(Lqlocal byte size\*(Rq (for
+.Tn PDP Ns -10's
+and
+.Tn PDP Ns -20's
+mostly).
+.Nm Ftp
+supports the ascii and image types of file transfer,
+plus local byte size 8 for
+.Ic tenex
+mode transfers.
+.Pp
+.Nm Ftp
+supports only the default values for the remaining
+file transfer parameters:
+.Ic mode  ,
+.Ic form ,
+and
+.Ic struct  .
+.Sh THE .netrc FILE
+The
+.Pa .netrc
+file contains login and initialization information
+used by the auto-login process.
+It resides in the user's home directory.
+The following tokens are recognized; they may be separated by spaces,
+tabs, or new-lines:
+.Bl -tag -width password
+.It Ic machine Ar name
+Identify a remote machine
+.Ar name .
+The auto-login process searches the
+.Pa .netrc
+file for a
+.Ic machine
+token that matches the remote machine specified on the
+.Nm ftp
+command line or as an
+.Ic open
+command argument.
+Once a match is made, the subsequent
+.Pa .netrc
+tokens are processed,
+stopping when the end of file is reached or another
+.Ic machine
+or a
+.Ic default
+token is encountered.
+.It Ic default
+This is the same as
+.Ic machine
+.Ar name
+except that
+.Ic default
+matches any name.
+There can be only one
+.Ic default
+token, and it must be after all
+.Ic machine
+tokens.
+This is normally used as:
+.Pp
+.Dl default login anonymous password user@site
+.Pp
+thereby giving the user
+.Ar automatic
+anonymous ftp login to
+machines not specified in
+.Pa .netrc .
+This can be overridden
+by using the
+.Fl n
+flag to disable auto-login.
+.It Ic login Ar name
+Identify a user on the remote machine.
+If this token is present, the auto-login process will initiate
+a login using the specified
+.Ar name .
+.It Ic password Ar string
+Supply a password.
+If this token is present, the auto-login process will supply the
+specified string if the remote server requires a password as part
+of the login process.
+Note that if this token is present in the
+.Pa .netrc
+file for any user other
+than
+.Ar anonymous  ,
+.Nm ftp
+will abort the auto-login process if the
+.Pa .netrc
+is readable by
+anyone besides the user.
+.It Ic account Ar string
+Supply an additional account password.
+If this token is present, the auto-login process will supply the
+specified string if the remote server requires an additional
+account password, or the auto-login process will initiate an
+.Dv ACCT
+command if it does not.
+.It Ic macdef Ar name
+Define a macro.
+This token functions like the
+.Nm ftp
+.Ic macdef
+command functions.
+A macro is defined with the specified name; its contents begin with the
+next
+.Pa .netrc
+line and continue until a null line (consecutive new-line
+characters) is encountered.
+If a macro named
+.Ic init
+is defined, it is automatically executed as the last step in the
+auto-login process.
+.El
+.Sh ENVIRONMENT
+.Nm Ftp
+utilizes the following environment variables.
+.Bl -tag -width Fl
+.It Ev HOME
+For default location of a
+.Pa .netrc
+file, if one exists.
+.It Ev SHELL
+For default shell.
+.El
+.Sh SEE ALSO
+.Xr ftpd 8
+.Pp
+Lunt, S. J.,
+FTP Security Extensions,
+Internet Draft,
+November 1993.
+.Sh HISTORY
+The
+.Nm ftp
+command appeared in
+.Bx 4.2 .
+.Sh BUGS
+Correct execution of many commands depends upon proper behavior
+by the remote server.
+.Pp
+An error in the treatment of carriage returns
+in the
+.Bx 4.2
+ascii-mode transfer code
+has been corrected.
+This correction may result in incorrect transfers of binary files
+to and from
+.Bx 4.2
+servers using the ascii type.
+Avoid this problem by using the binary image type.
diff --git a/src/appl/gssftp/ftp/ftp.c b/src/appl/gssftp/ftp/ftp.c
new file mode 100644 (file)
index 0000000..6f778ab
--- /dev/null
@@ -0,0 +1,2147 @@
+/*
+ * Copyright (c) 1985, 1989 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[] = "@(#)ftp.c      5.38 (Berkeley) 4/22/91";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/file.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <arpa/ftp.h>
+#include <arpa/telnet.h>
+
+#include <stdio.h>
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <pwd.h>
+#ifndef STDARG
+#if defined(__STDC__) && ! defined(VARARGS)
+#define STDARG
+#endif
+#endif
+#ifdef STDARG
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#ifdef POSIX
+#include <stdlib.h>
+#endif
+
+#ifndef L_SET
+#define L_SET 0
+#endif
+#ifndef L_INCR
+#define L_INCR 1
+#endif
+
+#ifdef KERBEROS
+#include <krb.h>
+
+KTEXT_ST ticket;
+CREDENTIALS cred;
+Key_schedule schedule;
+MSG_DAT msg_data;
+#endif /* KERBEROS */
+#ifdef GSSAPI
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_generic.h>
+gss_ctx_id_t gcontext;
+#endif /* GSSAPI */
+
+
+static int kerror;     /* XXX needed for all auth types */
+
+char   *auth_type;     /* Authentication succeeded?  If so, what type? */
+
+unsigned int maxbuf, actualbuf;
+unsigned char *ucbuf;
+#define DEFINITIONS
+#include "ftp_var.h"
+
+#define sig_t my_sig_t
+#define sigtype krb5_sigtype
+typedef sigtype (*sig_t)();
+
+struct sockaddr_in hisctladdr;
+struct sockaddr_in hisdataaddr;
+struct sockaddr_in data_addr;
+int    data = -1;
+int    abrtflag = 0;
+int    ptflag = 0;
+struct sockaddr_in myctladdr;
+uid_t  getuid();
+sig_t  lostpeer();
+off_t  restart_point = 0;
+
+#define strerror(error) (sys_errlist[error])
+extern char *sys_errlist[];
+extern int connected, errno;
+
+#define herror()       printf("unknown host\n")
+
+FILE   *cin, *cout;
+FILE   *dataconn();
+
+char *
+hookup(host, port)
+       char *host;
+       int port;
+{
+       register struct hostent *hp = 0;
+       int s, len, tos;
+       static char hostnamebuf[80];
+
+       memset((char *)&hisctladdr, 0, sizeof (hisctladdr));
+       hisctladdr.sin_addr.s_addr = inet_addr(host);
+       if (hisctladdr.sin_addr.s_addr != -1) {
+               hisctladdr.sin_family = AF_INET;
+               (void) strncpy(hostnamebuf, host, sizeof(hostnamebuf));
+       } else {
+               hp = gethostbyname(host);
+               if (hp == NULL) {
+                       fprintf(stderr, "ftp: %s: ", host);
+                       herror();
+                       code = -1;
+                       return((char *) 0);
+               }
+               hisctladdr.sin_family = hp->h_addrtype;
+               memcpy((caddr_t)&hisctladdr.sin_addr, hp->h_addr_list[0],
+                   hp->h_length);
+               (void) strncpy(hostnamebuf, hp->h_name, sizeof(hostnamebuf));
+       }
+       hostname = hostnamebuf;
+       s = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
+       if (s < 0) {
+               perror("ftp: socket");
+               code = -1;
+               return (0);
+       }
+       hisctladdr.sin_port = port;
+       while (connect(s, (struct sockaddr *)&hisctladdr, sizeof (hisctladdr)) < 0) {
+               if (hp && hp->h_addr_list[1]) {
+                       int oerrno = errno;
+                       extern char *inet_ntoa();
+
+                       fprintf(stderr, "ftp: connect to address %s: ",
+                               inet_ntoa(hisctladdr.sin_addr));
+                       errno = oerrno;
+                       perror((char *) 0);
+                       hp->h_addr_list++;
+                       memcpy((caddr_t)&hisctladdr.sin_addr,
+                            hp->h_addr_list[0], hp->h_length);
+                       fprintf(stdout, "Trying %s...\n",
+                               inet_ntoa(hisctladdr.sin_addr));
+                       (void) close(s);
+                       s = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
+                       if (s < 0) {
+                               perror("ftp: socket");
+                               code = -1;
+                               return (0);
+                       }
+                       continue;
+               }
+               perror("ftp: connect");
+               code = -1;
+               goto bad;
+       }
+       len = sizeof (myctladdr);
+       if (getsockname(s, (struct sockaddr *)&myctladdr, &len) < 0) {
+               perror("ftp: getsockname");
+               code = -1;
+               goto bad;
+       }
+#ifdef IP_TOS
+#ifdef IPTOS_LOWDELAY
+       tos = IPTOS_LOWDELAY;
+       if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
+               perror("ftp: setsockopt TOS (ignored)");
+#endif
+#endif
+       cin = fdopen(s, "r");
+       cout = fdopen(s, "w");
+       if (cin == NULL || cout == NULL) {
+               fprintf(stderr, "ftp: fdopen failed.\n");
+               if (cin)
+                       (void) fclose(cin);
+               if (cout)
+                       (void) fclose(cout);
+               code = -1;
+               goto bad;
+       }
+       if (verbose)
+               printf("Connected to %s.\n", hostname);
+       if (getreply(0) > 2) {  /* read startup message from server */
+               if (cin)
+                       (void) fclose(cin);
+               if (cout)
+                       (void) fclose(cout);
+               code = -1;
+               goto bad;
+       }
+#ifdef SO_OOBINLINE
+       {
+       int on = 1;
+
+       if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on))
+               < 0 && debug) {
+                       perror("ftp: setsockopt");
+               }
+       }
+#endif /* SO_OOBINLINE */
+
+       return (hostname);
+bad:
+       (void) close(s);
+       return ((char *)0);
+}
+
+login(host)
+       char *host;
+{
+       char tmp[80];
+       char *user, *pass, *acct, *getenv(), *getlogin(), *mygetpass();
+       int n, aflag = 0;
+
+       do_auth();
+       user = pass = acct = 0;
+       if (ruserpass(host, &user, &pass, &acct) < 0) {
+               code = -1;
+               return(0);
+       }
+       while (user == NULL) {
+               char *myname;
+
+               myname = getenv("LOGNAME");
+               if (myname == NULL)
+                       myname = getenv("USER");
+               if (myname == NULL)
+                       myname = getlogin();
+               if (myname == NULL) {
+                       struct passwd *pp = getpwuid(getuid());
+
+                       if (pp != NULL)
+                               myname = pp->pw_name;
+               }
+               if (myname)
+                       printf("Name (%s:%s): ", host, myname);
+               else
+                       printf("Name (%s): ", host);
+               (void) fgets(tmp, sizeof(tmp) - 1, stdin);
+               tmp[strlen(tmp) - 1] = '\0';
+               if (*tmp == '\0')
+                       user = myname;
+               else
+                       user = tmp;
+       }
+       n = command("USER %s", user);
+       if (n == COMPLETE)
+               n = command("PASS dummy");
+       else if (n == CONTINUE) {
+#ifndef NOENCRYPTION
+               int oldlevel;
+#endif
+               if (pass == NULL)
+                       pass = mygetpass("Password:");
+#ifndef NOENCRYPTION
+               if ((oldlevel = level) == PROT_S) level = PROT_P;
+#endif
+               n = command("PASS %s", pass);
+#ifndef NOENCRYPTION
+               /* level may have changed */
+               if (level == PROT_P) level = oldlevel;
+#endif
+       }
+       if (n == CONTINUE) {
+               aflag++;
+               acct = mygetpass("Account:");
+               n = command("ACCT %s", acct);
+       }
+       if (n != COMPLETE) {
+               fprintf(stderr, "Login failed.\n");
+               return (0);
+       }
+       if (!aflag && acct != NULL)
+               (void) command("ACCT %s", acct);
+       if (proxy)
+               return(1);
+       for (n = 0; n < macnum; ++n) {
+               if (!strcmp("init", macros[n].mac_name)) {
+                       (void) strcpy(line, "$init");
+                       makeargv();
+                       domacro(margc, margv);
+                       break;
+               }
+       }
+       return (1);
+}
+
+sigtype
+cmdabort(sig)
+       int sig;
+{
+       extern jmp_buf ptabort;
+
+       printf("\n");
+       (void) fflush(stdout);
+       abrtflag++;
+       if (ptflag)
+               longjmp(ptabort,1);
+}
+
+secure_command(cmd)
+       char *cmd;
+{
+       char in[FTP_BUFSIZ], out[FTP_BUFSIZ];
+       int length;
+
+       if (auth_type) {
+               /*
+                * File protection level also determines whether
+                * commands are MIC or ENC.  Should be independent ...
+                */
+#ifdef KERBEROS
+               if (strcmp(auth_type, "KERBEROS_V4") == 0)
+                   if ((length = level == PROT_P ?
+                       krb_mk_priv((unsigned char *)cmd, (unsigned char *)out,
+                               strlen(cmd), schedule,
+                               &cred.session, &myctladdr, &hisctladdr)
+                     : krb_mk_safe((unsigned char *)cmd, (unsigned char *)out,
+                               strlen(cmd), &cred.session,
+                               &myctladdr, &hisctladdr)) == -1) {
+                       fprintf(stderr, "krb_mk_%s failed for KERBEROS_V4\n",
+                                       level == PROT_P ? "priv" : "safe");
+                       return(0);
+                   }
+#endif /* KERBEROS */
+#ifdef GSSAPI
+               /* secure_command (based on level) */
+               if (strcmp(auth_type, "GSSAPI") == 0) {
+                       gss_buffer_desc in_buf, out_buf;
+                       OM_uint32 maj_stat, min_stat;
+                       int conf_state;
+/* level = PROT_P; */
+                       in_buf.value = cmd;
+                       in_buf.length = strlen(cmd) + 1;
+                       maj_stat = gss_seal(&min_stat, gcontext,
+                                           (level==PROT_P), /* confidential */
+                                           GSS_C_QOP_DEFAULT,
+                                           &in_buf, &conf_state,
+                                           &out_buf);
+                       if (maj_stat != GSS_S_COMPLETE) {
+                               /* generally need to deal */
+                               user_gss_error(maj_stat, min_stat,
+                                              (level==PROT_P)?
+                                                "gss_seal ENC didn't complete":
+                                                "gss_seal MIC didn't complete");
+                       } else if ((level == PROT_P) && !conf_state) {
+                               fprintf(stderr, 
+                                       "GSSAPI didn't encrypt message");
+                       } else {
+                               if (debug)
+                                 fprintf(stderr, "sealed (%s) %d bytes\n",
+                                         level==PROT_P?"ENC":"MIC", 
+                                         out_buf.length);
+                               memcpy(out, out_buf.value, 
+                                      length=out_buf.length);
+                               gss_release_buffer(&min_stat, &out_buf);
+                       }
+               }
+#endif /* GSSAPI */
+               /* Other auth types go here ... */
+               if (kerror = radix_encode(out, in, &length, 0)) {
+                       fprintf(stderr,"Couldn't base 64 encode command (%s)\n",
+                                       radix_error(kerror));
+                       return(0);
+               }
+               fprintf(cout, "%s %s", level == PROT_P ? "ENC" : "MIC", in);
+               if(debug) 
+                 fprintf(stderr, "secure_command(%s)\nencoding %d bytes %s %s\n",
+                         cmd, length, level==PROT_P ? "ENC" : "MIC", in);
+       } else  fputs(cmd, cout);
+       fprintf(cout, "\r\n");
+       (void) fflush(cout);
+       return(1);
+}
+
+#ifdef STDARG
+command(char *fmt, ...)
+#else
+/*VARARGS*/
+command(va_alist)
+va_dcl
+#endif
+{
+       char in[FTP_BUFSIZ];
+       va_list ap;
+#ifndef STDARG
+       char *fmt;
+#endif
+       int r;
+       sig_t oldintr;
+       sigtype cmdabort();
+
+       abrtflag = 0;
+       if (debug) {
+               if (proxflag) printf("%s ", hostname);
+               printf("---> ");
+#ifdef STDARG
+               va_start(ap, fmt);
+#else
+               va_start(ap);
+               fmt = va_arg(ap, char *);
+#endif
+               if (strncmp("PASS ", fmt, 5) == 0)
+                       printf("PASS XXXX");
+               else 
+                       vfprintf(stdout, fmt, ap);
+               va_end(ap);
+               printf("\n");
+               (void) fflush(stdout);
+       }
+       if (cout == NULL) {
+               perror ("No control connection for command");
+               code = -1;
+               return (0);
+       }
+       oldintr = signal(SIGINT, cmdabort);
+#ifdef STDARG
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+       fmt = va_arg(ap, char *);
+#endif
+       vsprintf(in, fmt, ap);
+       va_end(ap);
+again: if (secure_command(in) == 0)
+               return(0);
+       cpend = 1;
+       r = getreply(!strcmp(fmt, "QUIT"));
+#ifndef NOENCRYPTION
+       if (r == 533 && level == PROT_P) {
+               fprintf(stderr,
+                       "ENC command not supported at server; retrying under MIC...\n");
+               level = PROT_S;
+               goto again;
+       }
+#endif
+       if (abrtflag && oldintr != SIG_IGN)
+               (*oldintr)(SIGINT);
+       (void) signal(SIGINT, oldintr);
+       return(r);
+}
+
+char reply_string[FTP_BUFSIZ];         /* last line of previous reply */
+
+/* for parsing replies to the ADAT command */
+char *reply_parse, reply_buf[FTP_BUFSIZ], *reply_ptr;
+
+#include <ctype.h>
+
+getreply(expecteof)
+       int expecteof;
+{
+       register int i, c, n;
+       register int dig;
+       register char *cp;
+       int originalcode = 0, continuation = 0;
+       sig_t oldintr;
+       int pflag = 0;
+       char *pt = pasv;
+       sigtype cmdabort();
+       char ibuf[FTP_BUFSIZ], obuf[FTP_BUFSIZ];
+       int safe = 0;
+       extern char *strpbrk(), *strstr();
+
+       ibuf[0] = '\0';
+       if (reply_parse) reply_ptr = reply_buf;
+       oldintr = signal(SIGINT, cmdabort);
+       for (;;) {
+               obuf[0] = '\0';
+               dig = n = code = i = 0;
+               cp = reply_string;
+               while ((c = ibuf[0] ? ibuf[i++] : getc(cin)) != '\n') {
+                       if (c == IAC) {     /* handle telnet commands */
+                               switch (c = getc(cin)) {
+                               case WILL:
+                               case WONT:
+                                       c = getc(cin);
+                                       fprintf(cout, "%c%c%c", IAC, DONT, c);
+                                       (void) fflush(cout);
+                                       break;
+                               case DO:
+                               case DONT:
+                                       c = getc(cin);
+                                       fprintf(cout, "%c%c%c", IAC, WONT, c);
+                                       (void) fflush(cout);
+                                       break;
+                               default:
+                                       break;
+                               }
+                               continue;
+                       }
+                       dig++;
+                       if (c == EOF) {
+                               if (expecteof) {
+                                       (void) signal(SIGINT,oldintr);
+                                       code = 221;
+                                       return (0);
+                               }
+                               lostpeer();
+                               if (verbose) {
+                                       printf("421 Service not available, remote server has closed connection\n");
+                                       (void) fflush(stdout);
+                               }
+                               code = 421;
+                               return(4);
+                       }
+                       if (n == 0)
+                               n = c;
+                       if (auth_type && !ibuf[0] &&
+                               (n == '6' || continuation)) {
+                           if (c != '\r' && dig > 4)
+                               obuf[i++] = c;
+                       } else {
+                           if (auth_type && !ibuf[0] && dig == 1 && verbose)
+                       printf("Unauthenticated reply received from server:\n");
+                           if (reply_parse) *reply_ptr++ = c;
+                           if (c != '\r' && (verbose > 0 ||
+                               (verbose > -1 && n == '5' && dig > 4))) {
+                                   if (proxflag &&
+                                       (dig == 1 || dig == 5 && verbose == 0))
+                                               printf("%s:",hostname);
+                                   (void) putchar(c);
+                           }
+                       }
+                       if (auth_type && !ibuf[0] && n != '6') continue;
+                       if (dig < 4 && isdigit(c))
+                               code = code * 10 + (c - '0');
+                       if (!pflag && code == 227)
+                               pflag = 1;
+                       if (dig > 4 && pflag == 1 && isdigit(c))
+                               pflag = 2;
+                       if (pflag == 2) {
+                               if (c != '\r' && c != ')')
+                                       *pt++ = c;
+                               else {
+                                       *pt = '\0';
+                                       pflag = 3;
+                               }
+                       }
+                       if (dig == 4 && c == '-' && n != '6') {
+                               if (continuation)
+                                       code = 0;
+                               continuation++;
+                       }
+                       if (cp < &reply_string[sizeof(reply_string) - 1])
+                               *cp++ = c;
+               }
+               if (auth_type && !ibuf[0] && n != '6')
+                       return(getreply(expecteof));
+               ibuf[0] = obuf[i] = '\0';
+               if (code && n == '6')
+                   if (code != 631 && code != 632 && code != 633)
+                       printf("Unknown reply: %d %s\n", code, obuf);
+                   else safe = code == 631;
+               if (obuf[0])    /* if there is a string to decode */
+                   if (!auth_type)
+                       printf("Cannot decode reply:\n%d %s\n", code, obuf);
+#ifdef NOENCRYPTION
+                   else if (code == 632)
+                       printf("Cannot decrypt %d reply: %s\n", code, obuf);
+#endif
+#ifdef NOCONFIDENTIAL
+                   else if (code == 633)
+                       printf("Cannot decrypt %d reply: %s\n", code, obuf);
+#endif
+                   else {
+                       int len;
+                       if (kerror = radix_encode(obuf, ibuf, &len, 1))
+                           printf("Can't base 64 decode reply %d (%s)\n\"%s\"\n",
+                                       code, radix_error(kerror), obuf);
+#ifdef KERBEROS
+                       else if (strcmp(auth_type, "KERBEROS_V4") == 0)
+                               if ((kerror = safe ?
+                                 krb_rd_safe((unsigned char *)ibuf, len,
+                                       &cred.session,
+                                       &hisctladdr, &myctladdr, &msg_data)
+                               : krb_rd_priv((unsigned char *)ibuf, len,
+                                       schedule, &cred.session,
+                                       &hisctladdr, &myctladdr, &msg_data))
+                               != KSUCCESS)
+                                 printf("%d reply %s! (krb_rd_%s: %s)\n", code,
+                                       safe ? "modified" : "garbled",
+                                       safe ? "safe" : "priv",
+                                       krb_get_err_text(kerror));
+                               else {
+                                 if (verbose) printf("%c:", safe ? 'S' : 'P');
+                                 memcpy(ibuf, msg_data.app_data,
+                                       msg_data.app_length);
+                                 strcpy(&ibuf[msg_data.app_length], "\r\n");
+                                 continue;
+                               }
+#endif
+#ifdef GSSAPI
+                       else if (strcmp(auth_type, "GSSAPI") == 0) {
+                               gss_buffer_desc xmit_buf, msg_buf;
+                               OM_uint32 maj_stat, min_stat;
+                               int conf_state;
+                               xmit_buf.value = ibuf;
+                               xmit_buf.length = len;
+                               /* decrypt/verify the message */
+                               conf_state = safe;
+                               maj_stat = gss_unseal(&min_stat, gcontext, 
+                                                     &xmit_buf, &msg_buf, 
+                                                     &conf_state, NULL);
+                               if (maj_stat != GSS_S_COMPLETE) {
+                                 user_gss_error(maj_stat, min_stat, 
+                                                "failed unsealing reply");
+                               } else {
+                                 memcpy(ibuf, msg_buf.value, 
+                                        msg_buf.length);
+                                 strcpy(&ibuf[msg_buf.length], "\r\n");
+                                 gss_release_buffer(&min_stat,&msg_buf);
+                                 continue;
+                               }
+                       }
+#endif
+                       /* Other auth types go here... */
+                   }
+               else
+               if (verbose > 0 || verbose > -1 && n == '5') {
+                       (void) putchar(c);
+                       (void) fflush (stdout);
+               }
+               if (continuation && code != originalcode) {
+                       if (originalcode == 0)
+                               originalcode = code;
+                       continue;
+               }
+               *cp = '\0';
+               if (n != '1')
+                       cpend = 0;
+               (void) signal(SIGINT,oldintr);
+               if (code == 421 || originalcode == 421)
+                       lostpeer();
+               if (abrtflag && oldintr != cmdabort && oldintr != SIG_IGN)
+                       (*oldintr)(SIGINT);
+               if (reply_parse) {
+                       *reply_ptr = '\0';
+                       if (reply_ptr = strstr(reply_buf, reply_parse)) {
+                               reply_parse = reply_ptr + strlen(reply_parse);
+                               if (reply_ptr = strpbrk(reply_parse, " \r"))
+                                       *reply_ptr = '\0';
+                       } else reply_parse = reply_ptr;
+               }
+               return (n - '0');
+       }
+}
+
+empty(mask, sec)
+       struct fd_set *mask;
+       int sec;
+{
+       struct timeval t;
+
+       t.tv_sec = (long) sec;
+       t.tv_usec = 0;
+       return(select(32, mask, (struct fd_set *) 0, (struct fd_set *) 0, &t));
+}
+
+jmp_buf        sendabort;
+
+sigtype
+abortsend(sig)
+       int sig;
+{
+
+       mflag = 0;
+       abrtflag = 0;
+       printf("\nsend aborted\nwaiting for remote to finish abort\n");
+       (void) fflush(stdout);
+       longjmp(sendabort, 1);
+}
+
+#ifdef STDARG
+secure_error(char *fmt, ...)
+#else
+/* VARARGS1 */
+secure_error(fmt, p1, p2, p3, p4, p5)
+       char *fmt;
+#endif
+{
+#ifdef STDARG
+       va_list ap;
+
+       va_start(ap, fmt);
+       vfprintf(stderr, fmt, ap);
+       va_end(ap);
+#else
+       fprintf(stderr, fmt, p1, p2, p3, p4, p5);
+#endif
+       putc('\n', stderr);
+}
+
+#define HASHBYTES 1024
+
+sendrequest(cmd, local, remote, printnames)
+       char *cmd, *local, *remote;
+       int printnames;
+{
+       struct stat st;
+       struct timeval start, stop;
+       register int c, d;
+       FILE *fin, *dout = 0, *popen();
+       int (*closefunc)(), pclose(), fclose();
+       sig_t oldintr, oldintp;
+       long bytes = 0, hashbytes = HASHBYTES;
+       char *lmode, buf[FTP_BUFSIZ], *bufp;
+       sigtype abortsend();
+
+       if (verbose && printnames) {
+               if (local && *local != '-')
+                       printf("local: %s ", local);
+               if (remote)
+                       printf("remote: %s\n", remote);
+       }
+       if (proxy) {
+               proxtrans(cmd, local, remote);
+               return;
+       }
+       if (curtype != type)
+               changetype(type, 0);
+       closefunc = NULL;
+       oldintr = NULL;
+       oldintp = NULL;
+       lmode = "w";
+       if (setjmp(sendabort)) {
+               while (cpend) {
+                       (void) getreply(0);
+               }
+               if (data >= 0) {
+                       (void) close(data);
+                       data = -1;
+               }
+               if (oldintr)
+                       (void) signal(SIGINT,oldintr);
+               if (oldintp)
+                       (void) signal(SIGPIPE,oldintp);
+               code = -1;
+               return;
+       }
+       oldintr = signal(SIGINT, abortsend);
+       if (strcmp(local, "-") == 0)
+               fin = stdin;
+       else if (*local == '|') {
+               oldintp = signal(SIGPIPE,SIG_IGN);
+               fin = popen(local + 1, "r");
+               if (fin == NULL) {
+                       perror(local + 1);
+                       (void) signal(SIGINT, oldintr);
+                       (void) signal(SIGPIPE, oldintp);
+                       code = -1;
+                       return;
+               }
+               closefunc = pclose;
+       } else {
+               fin = fopen(local, "r");
+               if (fin == NULL) {
+                       fprintf(stderr, "local: %s: %s\n", local,
+                               strerror(errno));
+                       (void) signal(SIGINT, oldintr);
+                       code = -1;
+                       return;
+               }
+               closefunc = fclose;
+               if (fstat(fileno(fin), &st) < 0 ||
+                   (st.st_mode&S_IFMT) != S_IFREG) {
+                       fprintf(stdout, "%s: not a plain file.\n", local);
+                       (void) signal(SIGINT, oldintr);
+                       fclose(fin);
+                       code = -1;
+                       return;
+               }
+       }
+       if (initconn()) {
+               (void) signal(SIGINT, oldintr);
+               if (oldintp)
+                       (void) signal(SIGPIPE, oldintp);
+               code = -1;
+               if (closefunc != NULL)
+                       (*closefunc)(fin);
+               return;
+       }
+       if (setjmp(sendabort))
+               goto abort;
+
+       if (restart_point &&
+           (strcmp(cmd, "STOR") == 0 || strcmp(cmd, "APPE") == 0)) {
+               if (fseek(fin, (long) restart_point, 0) < 0) {
+                       fprintf(stderr, "local: %s: %s\n", local,
+                               strerror(errno));
+                       restart_point = 0;
+                       if (closefunc != NULL)
+                               (*closefunc)(fin);
+                       return;
+               }
+               if (command("REST %ld", (long) restart_point)
+                       != CONTINUE) {
+                       restart_point = 0;
+                       if (closefunc != NULL)
+                               (*closefunc)(fin);
+                       return;
+               }
+               restart_point = 0;
+               lmode = "r+w";
+       }
+       if (remote) {
+               if (command("%s %s", cmd, remote) != PRELIM) {
+                       (void) signal(SIGINT, oldintr);
+                       if (oldintp)
+                               (void) signal(SIGPIPE, oldintp);
+                       if (closefunc != NULL)
+                               (*closefunc)(fin);
+                       return;
+               }
+       } else
+               if (command("%s", cmd) != PRELIM) {
+                       (void) signal(SIGINT, oldintr);
+                       if (oldintp)
+                               (void) signal(SIGPIPE, oldintp);
+                       if (closefunc != NULL)
+                               (*closefunc)(fin);
+                       return;
+               }
+       dout = dataconn(lmode);
+       if (dout == NULL)
+               goto abort;
+       (void) gettimeofday(&start, (struct timezone *)0);
+       oldintp = signal(SIGPIPE, SIG_IGN);
+       switch (curtype) {
+
+       case TYPE_I:
+       case TYPE_L:
+               errno = d = 0;
+               while ((c = read(fileno(fin), buf, sizeof (buf))) > 0) {
+                       bytes += c;
+                       for (bufp = buf; c > 0; c -= d, bufp += d)
+                               if ((d = secure_write(fileno(dout), bufp, c)) <= 0)
+                                       break;
+                       if (hash) {
+                               while (bytes >= hashbytes) {
+                                       (void) putchar('#');
+                                       hashbytes += HASHBYTES;
+                               }
+                               (void) fflush(stdout);
+                       }
+               }
+               if (hash && bytes > 0) {
+                       if (bytes < HASHBYTES)
+                               (void) putchar('#');
+                       (void) putchar('\n');
+                       (void) fflush(stdout);
+               }
+               if (c < 0)
+                       fprintf(stderr, "local: %s: %s\n", local,
+                               strerror(errno));
+               if (d < 0 || (d = secure_flush(fileno(dout))) < 0) {
+                       if (d == -1 && errno != EPIPE) 
+                               perror("netout");
+                       bytes = -1;
+               }
+               break;
+
+       case TYPE_A:
+               while ((c = getc(fin)) != EOF) {
+                       if (c == '\n') {
+                               while (hash && (bytes >= hashbytes)) {
+                                       (void) putchar('#');
+                                       (void) fflush(stdout);
+                                       hashbytes += HASHBYTES;
+                               }
+                               if (ferror(dout) ||
+                                   secure_putc('\r', dout) < 0)
+                                       break;
+                               bytes++;
+                       }
+                       if (secure_putc(c, dout) < 0)
+                               break;
+                       bytes++;
+       /*              if (c == '\r') {                                */
+       /*              (void)  putc('\0', dout);  /* this violates rfc */
+       /*                      bytes++;                                */
+       /*              }                                               */      
+               }
+               if (hash) {
+                       if (bytes < hashbytes)
+                               (void) putchar('#');
+                       (void) putchar('\n');
+                       (void) fflush(stdout);
+               }
+               if (ferror(fin))
+                       fprintf(stderr, "local: %s: %s\n", local,
+                               strerror(errno));
+               d = 0;
+               if (ferror(dout) || (d = secure_flush(fileno(dout))) < 0) {
+                       if ((ferror(dout) || d == -1) && errno != EPIPE)
+                               perror("netout");
+                       bytes = -1;
+               }
+               break;
+       }
+       (void) gettimeofday(&stop, (struct timezone *)0);
+       if (closefunc != NULL)
+               (*closefunc)(fin);
+       (void) fclose(dout);
+       (void) getreply(0);
+       (void) signal(SIGINT, oldintr);
+       if (oldintp)
+               (void) signal(SIGPIPE, oldintp);
+       if (bytes > 0)
+               ptransfer("sent", bytes, &start, &stop);
+       return;
+abort:
+       (void) gettimeofday(&stop, (struct timezone *)0);
+       (void) signal(SIGINT, oldintr);
+       if (oldintp)
+               (void) signal(SIGPIPE, oldintp);
+       if (!cpend) {
+               code = -1;
+               return;
+       }
+       if (data >= 0) {
+               (void) close(data);
+               data = -1;
+       }
+       if (dout)
+               (void) fclose(dout);
+       (void) getreply(0);
+       code = -1;
+       if (closefunc != NULL && fin != NULL)
+               (*closefunc)(fin);
+       if (bytes > 0)
+               ptransfer("sent", bytes, &start, &stop);
+}
+
+jmp_buf        recvabort;
+
+sigtype
+abortrecv(sig)
+     int sig;
+{
+
+       mflag = 0;
+       abrtflag = 0;
+       printf("\nreceive aborted\nwaiting for remote to finish abort\n");
+       (void) fflush(stdout);
+       longjmp(recvabort, 1);
+}
+
+recvrequest(cmd, local, remote, lmode, printnames)
+       char *cmd, *local, *remote, *lmode;
+{
+       FILE *fout, *din = 0, *popen();
+       int (*closefunc)(), pclose(), fclose();
+       sig_t oldintr, oldintp;
+       int is_retr, tcrflag, bare_lfs = 0;
+       char *gunique();
+       static int bufsize;
+       static char *buf;
+       int blksize;
+       long bytes = 0, hashbytes = HASHBYTES;
+       register int c, d;
+       struct timeval start, stop;
+       struct stat st;
+       off_t lseek();
+       sigtype abortrecv();
+
+       is_retr = strcmp(cmd, "RETR") == 0;
+       if (is_retr && verbose && printnames) {
+               if (local && *local != '-')
+                       printf("local: %s ", local);
+               if (remote)
+                       printf("remote: %s\n", remote);
+       }
+       if (proxy && is_retr) {
+               proxtrans(cmd, local, remote);
+               return;
+       }
+       closefunc = NULL;
+       oldintr = NULL;
+       oldintp = NULL;
+       tcrflag = !crflag && is_retr;
+       if (setjmp(recvabort)) {
+               while (cpend) {
+                       (void) getreply(0);
+               }
+               if (data >= 0) {
+                       (void) close(data);
+                       data = -1;
+               }
+               if (oldintr)
+                       (void) signal(SIGINT, oldintr);
+               code = -1;
+               return;
+       }
+       oldintr = signal(SIGINT, abortrecv);
+       if (strcmp(local, "-") && *local != '|') {
+               if (access(local, 2) < 0) {
+                       char *dir = strrchr(local, '/');
+
+                       if (errno != ENOENT && errno != EACCES) {
+                               fprintf(stderr, "local: %s: %s\n", local,
+                                       strerror(errno));
+                               (void) signal(SIGINT, oldintr);
+                               code = -1;
+                               return;
+                       }
+                       if (dir != NULL)
+                               *dir = 0;
+                       d = access(dir ? local : ".", 2);
+                       if (dir != NULL)
+                               *dir = '/';
+                       if (d < 0) {
+                               fprintf(stderr, "local: %s: %s\n", local,
+                                       strerror(errno));
+                               (void) signal(SIGINT, oldintr);
+                               code = -1;
+                               return;
+                       }
+                       if (!runique && errno == EACCES &&
+                           chmod(local, 0600) < 0) {
+                               fprintf(stderr, "local: %s: %s\n", local,
+                                       strerror(errno));
+                               (void) signal(SIGINT, oldintr);
+                               (void) signal(SIGINT, oldintr);
+                               code = -1;
+                               return;
+                       }
+                       if (runique && errno == EACCES &&
+                          (local = gunique(local)) == NULL) {
+                               (void) signal(SIGINT, oldintr);
+                               code = -1;
+                               return;
+                       }
+               }
+               else if (runique && (local = gunique(local)) == NULL) {
+                       (void) signal(SIGINT, oldintr);
+                       code = -1;
+                       return;
+               }
+       }
+       if (!is_retr) {
+               if (curtype != TYPE_A)
+                       changetype(TYPE_A, 0);
+       } else if (curtype != type)
+               changetype(type, 0);
+       if (initconn()) {
+               (void) signal(SIGINT, oldintr);
+               code = -1;
+               return;
+       }
+       if (setjmp(recvabort))
+               goto abort;
+       if (is_retr && restart_point &&
+           command("REST %ld", (long) restart_point) != CONTINUE)
+               return;
+       if (remote) {
+               if (command("%s %s", cmd, remote) != PRELIM) {
+                       (void) signal(SIGINT, oldintr);
+                       return;
+               }
+       } else {
+               if (command("%s", cmd) != PRELIM) {
+                       (void) signal(SIGINT, oldintr);
+                       return;
+               }
+       }
+       din = dataconn("r");
+       if (din == NULL)
+               goto abort;
+       if (strcmp(local, "-") == 0)
+               fout = stdout;
+       else if (*local == '|') {
+               oldintp = signal(SIGPIPE, SIG_IGN);
+               fout = popen(local + 1, "w");
+               if (fout == NULL) {
+                       perror(local+1);
+                       goto abort;
+               }
+               closefunc = pclose;
+       } else {
+               fout = fopen(local, lmode);
+               if (fout == NULL) {
+                       fprintf(stderr, "local: %s: %s\n", local,
+                               strerror(errno));
+                       goto abort;
+               }
+               closefunc = fclose;
+       }
+       blksize = FTP_BUFSIZ;
+#ifndef NOSTBLKSIZE
+       if (fstat(fileno(fout), &st) == 0 && st.st_blksize != 0)
+               blksize = st.st_blksize;
+#endif
+       if (blksize > bufsize) {
+               if (buf)
+                       (void) free(buf);
+               buf = (char *)malloc((unsigned)blksize);
+               if (buf == NULL) {
+                       perror("malloc");
+                       bufsize = 0;
+                       goto abort;
+               }
+               bufsize = blksize;
+       }
+       (void) gettimeofday(&start, (struct timezone *)0);
+       switch (curtype) {
+
+       case TYPE_I:
+       case TYPE_L:
+               if (restart_point &&
+                   lseek(fileno(fout), (long) restart_point, L_SET) < 0) {
+                       fprintf(stderr, "local: %s: %s\n", local,
+                               strerror(errno));
+                       if (closefunc != NULL)
+                               (*closefunc)(fout);
+                       return;
+               }
+               errno = d = 0;
+               while ((c = secure_read(fileno(din), buf, bufsize)) > 0) {
+                       if ((d = write(fileno(fout), buf, c)) != c)
+                               break;
+                       bytes += c;
+                       if (hash) {
+                               while (bytes >= hashbytes) {
+                                       (void) putchar('#');
+                                       hashbytes += HASHBYTES;
+                               }
+                               (void) fflush(stdout);
+                       }
+               }
+               if (hash && bytes > 0) {
+                       if (bytes < HASHBYTES)
+                               (void) putchar('#');
+                       (void) putchar('\n');
+                       (void) fflush(stdout);
+               }
+               if (c < 0) {
+                       if (c == -1 && errno != EPIPE)
+                               perror("netin");
+                       bytes = -1;
+               }
+               if (d < c) {
+                       if (d < 0)
+                               fprintf(stderr, "local: %s: %s\n", local,
+                                       strerror(errno));
+                       else
+                               fprintf(stderr, "%s: short write\n", local);
+               }
+               break;
+
+       case TYPE_A:
+               if (restart_point) {
+                       register int i, n, ch;
+
+                       if (fseek(fout, 0L, L_SET) < 0)
+                               goto done;
+                       n = restart_point;
+                       for (i = 0; i++ < n;) {
+                               if ((ch = getc(fout)) == EOF)
+                                       goto done;
+                               if (ch == '\n')
+                                       i++;
+                       }
+                       if (fseek(fout, 0L, L_INCR) < 0) {
+done:
+                               fprintf(stderr, "local: %s: %s\n", local,
+                                       strerror(errno));
+                               if (closefunc != NULL)
+                                       (*closefunc)(fout);
+                               return;
+                       }
+               }
+               while ((c = secure_getc(din)) >= 0) {
+                       if (c == '\n')
+                               bare_lfs++;
+                       while (c == '\r') {
+                               while (hash && (bytes >= hashbytes)) {
+                                       (void) putchar('#');
+                                       (void) fflush(stdout);
+                                       hashbytes += HASHBYTES;
+                               }
+                               bytes++;
+                               if ((c = secure_getc(din)) != '\n' || tcrflag) {
+                                       if (ferror(fout))
+                                               goto break2;
+                                       (void) putc('\r', fout);
+                                       if (c == '\0') {
+                                               bytes++;
+                                               goto contin2;
+                                       }
+                               }
+                       }
+                       if (c < 0) break;
+                       (void) putc(c, fout);
+                       bytes++;
+       contin2:        ;
+               }
+break2:
+               if (bare_lfs) {
+                       printf("WARNING! %d bare linefeeds received in ASCII mode\n", bare_lfs);
+                       printf("File may not have transferred correctly.\n");
+               }
+               if (hash) {
+                       if (bytes < hashbytes)
+                               (void) putchar('#');
+                       (void) putchar('\n');
+                       (void) fflush(stdout);
+               }
+               if (ferror(din)) {
+                       if (errno != EPIPE)
+                               perror("netin");
+                       bytes = -1;
+               }
+               if (ferror(fout) || c == -2) {
+                   if (c != -2)
+                       fprintf(stderr, "local: %s: %s\n", local,
+                               strerror(errno));
+                       bytes = -1;
+               }
+               break;
+       }
+       if (closefunc != NULL)
+               (*closefunc)(fout);
+       (void) signal(SIGINT, oldintr);
+       if (oldintp)
+               (void) signal(SIGPIPE, oldintp);
+       (void) gettimeofday(&stop, (struct timezone *)0);
+       (void) fclose(din);
+       (void) getreply(0);
+       if (bytes > 0 && is_retr)
+               ptransfer("received", bytes, &start, &stop);
+       return;
+abort:
+
+/* abort using RFC959 recommended IP,SYNC sequence  */
+
+       (void) gettimeofday(&stop, (struct timezone *)0);
+       if (oldintp)
+               (void) signal(SIGPIPE, oldintr);
+       (void) signal(SIGINT, SIG_IGN);
+       if (!cpend) {
+               code = -1;
+               (void) signal(SIGINT, oldintr);
+               return;
+       }
+
+       abort_remote(din);
+       code = -1;
+       if (data >= 0) {
+               (void) close(data);
+               data = -1;
+       }
+       if (closefunc != NULL && fout != NULL)
+               (*closefunc)(fout);
+       if (din)
+               (void) fclose(din);
+       if (bytes > 0)
+               ptransfer("received", bytes, &start, &stop);
+       (void) signal(SIGINT, oldintr);
+}
+
+/*
+ * Need to start a listen on the data channel before we send the command,
+ * otherwise the server's connect may fail.
+ */
+initconn()
+{
+       register char *p, *a;
+       int result, len, tmpno = 0;
+       int on = 1;
+#ifndef NO_PASSIVE_MODE
+       int a1,a2,a3,a4,p1,p2;
+
+       if (passivemode) {
+               data = socket(AF_INET, SOCK_STREAM, 0);
+               if (data < 0) {
+                       perror("ftp: socket");
+                       return(1);
+               }
+               if (options & SO_DEBUG &&
+                   setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof (on)) < 0)
+                       perror("ftp: setsockopt (ignored)");
+               if (command("PASV") != COMPLETE) {
+                       printf("Passive mode refused.  Turning off passive mode.\n");
+                       passivemode = 0;
+                       return initconn();
+               }
+
+/*
+ * What we've got at this point is a string of comma separated
+ * one-byte unsigned integer values, separated by commas.
+ * The first four are the an IP address. The fifth is the MSB
+ * of the port number, the sixth is the LSB. From that we'll
+ * prepare a sockaddr_in.
+ */
+
+               if (sscanf(pasv,"%d,%d,%d,%d,%d,%d",&a1,&a2,&a3,&a4,&p1,&p2) != 6) {
+                       printf("Passive mode address scan failure. Shouldn't happen!\n");
+                       return(1);
+               };
+
+               data_addr.sin_family = AF_INET;
+               data_addr.sin_addr.s_addr = htonl((a1<<24)|(a2<<16)|(a3<<8)|a4);
+               data_addr.sin_port = htons((p1<<8)|p2);
+
+               if (connect(data, (struct sockaddr *) &data_addr, sizeof(data_addr))<0) {
+                       perror("ftp: connect");
+                       return(1);
+               }
+#ifdef IP_TOS
+#ifdef IPTOS_THROUGHPUT
+       on = IPTOS_THROUGHPUT;
+       if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
+               perror("ftp: setsockopt TOS (ignored)");
+#endif
+#endif
+               hisdataaddr = data_addr;
+               return(0);
+       }
+#endif
+
+noport:
+       data_addr = myctladdr;
+       if (sendport)
+               data_addr.sin_port = 0; /* let system pick one */ 
+       if (data != -1)
+               (void) close(data);
+       data = socket(AF_INET, SOCK_STREAM, 0);
+       if (data < 0) {
+               perror("ftp: socket");
+               if (tmpno)
+                       sendport = 1;
+               return (1);
+       }
+       if (!sendport)
+               if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof (on)) < 0) {
+                       perror("ftp: setsockopt (reuse address)");
+                       goto bad;
+               }
+       if (bind(data, (struct sockaddr *)&data_addr, sizeof (data_addr)) < 0) {
+               perror("ftp: bind");
+               goto bad;
+       }
+       if (options & SO_DEBUG &&
+           setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof (on)) < 0)
+               perror("ftp: setsockopt (ignored)");
+       len = sizeof (data_addr);
+       if (getsockname(data, (struct sockaddr *)&data_addr, &len) < 0) {
+               perror("ftp: getsockname");
+               goto bad;
+       }
+       if (listen(data, 1) < 0)
+               perror("ftp: listen");
+       if (sendport) {
+               a = (char *)&data_addr.sin_addr;
+               p = (char *)&data_addr.sin_port;
+#define        UC(b)   (((int)b)&0xff)
+               result =
+                   command("PORT %d,%d,%d,%d,%d,%d",
+                     UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
+                     UC(p[0]), UC(p[1]));
+               if (result == ERROR && sendport == -1) {
+                       sendport = 0;
+                       tmpno = 1;
+                       goto noport;
+               }
+               return (result != COMPLETE);
+       }
+       if (tmpno)
+               sendport = 1;
+#ifdef IP_TOS
+#ifdef IPTOS_THROUGHPUT
+       on = IPTOS_THROUGHPUT;
+       if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
+               perror("ftp: setsockopt TOS (ignored)");
+#endif
+#endif
+       return (0);
+bad:
+       (void) close(data), data = -1;
+       if (tmpno)
+               sendport = 1;
+       return (1);
+}
+
+FILE *
+dataconn(lmode)
+       char *lmode;
+{
+       int s, fromlen = sizeof (hisdataaddr), tos;
+
+#ifndef NO_PASSIVE_MODE
+if (passivemode)
+       return (fdopen(data, lmode));
+#endif
+       s = accept(data, (struct sockaddr *) &hisdataaddr, &fromlen);
+       if (s < 0) {
+               perror("ftp: accept");
+               (void) close(data), data = -1;
+               return (NULL);
+       }
+       (void) close(data);
+       data = s;
+#ifdef IP_TOS
+#ifdef IPTOS_THROUGHPUT
+       tos = IPTOS_THROUGHPUT;
+       if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
+               perror("ftp: setsockopt TOS (ignored)");
+#endif
+#endif
+       return (fdopen(data, lmode));
+}
+
+ptransfer(direction, bytes, t0, t1)
+       char *direction;
+       long bytes;
+       struct timeval *t0, *t1;
+{
+       struct timeval td;
+       float s, kbs;
+
+       if (verbose) {
+               tvsub(&td, t1, t0);
+               s = td.tv_sec + (td.tv_usec / 1000000.);
+#define        nz(x)   ((x) == 0 ? 1 : (x))
+               kbs = (bytes / nz(s))/1024.0;
+               printf("%ld bytes %s in %.2g seconds (%.2g Kbytes/s)\n",
+                   bytes, direction, s, kbs);
+       }
+}
+
+/*tvadd(tsum, t0)
+       struct timeval *tsum, *t0;
+{
+
+       tsum->tv_sec += t0->tv_sec;
+       tsum->tv_usec += t0->tv_usec;
+       if (tsum->tv_usec > 1000000)
+               tsum->tv_sec++, tsum->tv_usec -= 1000000;
+} */
+
+tvsub(tdiff, t1, t0)
+       struct timeval *tdiff, *t1, *t0;
+{
+
+       tdiff->tv_sec = t1->tv_sec - t0->tv_sec;
+       tdiff->tv_usec = t1->tv_usec - t0->tv_usec;
+       if (tdiff->tv_usec < 0)
+               tdiff->tv_sec--, tdiff->tv_usec += 1000000;
+}
+
+sigtype
+psabort(sig)
+       int sig;
+{
+       extern int abrtflag;
+
+       abrtflag++;
+}
+
+pswitch(flag)
+       int flag;
+{
+       extern int proxy, abrtflag;
+       sig_t oldintr;
+       static struct comvars {
+               int connect;
+               char name[MAXHOSTNAMELEN];
+               struct sockaddr_in mctl;
+               struct sockaddr_in hctl;
+               FILE *in;
+               FILE *out;
+               int tpe;
+               int curtpe;
+               int cpnd;
+               int sunqe;
+               int runqe;
+               int mcse;
+               int ntflg;
+               char nti[17];
+               char nto[17];
+               int mapflg;
+               char mi[MAXPATHLEN];
+               char mo[MAXPATHLEN];
+               char *authtype;
+               int lvl;
+#ifdef KERBEROS
+               C_Block session;
+               Key_schedule schedule;
+#endif /* KERBEROS */
+       } proxstruct, tmpstruct;
+       struct comvars *ip, *op;
+
+       abrtflag = 0;
+       oldintr = signal(SIGINT, psabort);
+       if (flag) {
+               if (proxy)
+                       return;
+               ip = &tmpstruct;
+               op = &proxstruct;
+               proxy++;
+       } else {
+               if (!proxy)
+                       return;
+               ip = &proxstruct;
+               op = &tmpstruct;
+               proxy = 0;
+       }
+       ip->connect = connected;
+       connected = op->connect;
+       if (hostname) {
+               (void) strncpy(ip->name, hostname, sizeof(ip->name) - 1);
+               ip->name[strlen(ip->name)] = '\0';
+       } else
+               ip->name[0] = 0;
+       hostname = op->name;
+       ip->hctl = hisctladdr;
+       hisctladdr = op->hctl;
+       ip->mctl = myctladdr;
+       myctladdr = op->mctl;
+       ip->in = cin;
+       cin = op->in;
+       ip->out = cout;
+       cout = op->out;
+       ip->tpe = type;
+       type = op->tpe;
+       ip->curtpe = curtype;
+       curtype = op->curtpe;
+       ip->cpnd = cpend;
+       cpend = op->cpnd;
+       ip->sunqe = sunique;
+       sunique = op->sunqe;
+       ip->runqe = runique;
+       runique = op->runqe;
+       ip->mcse = mcase;
+       mcase = op->mcse;
+       ip->ntflg = ntflag;
+       ntflag = op->ntflg;
+       (void) strncpy(ip->nti, ntin, 16);
+       (ip->nti)[strlen(ip->nti)] = '\0';
+       (void) strcpy(ntin, op->nti);
+       (void) strncpy(ip->nto, ntout, 16);
+       (ip->nto)[strlen(ip->nto)] = '\0';
+       (void) strcpy(ntout, op->nto);
+       ip->mapflg = mapflag;
+       mapflag = op->mapflg;
+       (void) strncpy(ip->mi, mapin, MAXPATHLEN - 1);
+       (ip->mi)[strlen(ip->mi)] = '\0';
+       (void) strcpy(mapin, op->mi);
+       (void) strncpy(ip->mo, mapout, MAXPATHLEN - 1);
+       (ip->mo)[strlen(ip->mo)] = '\0';
+       (void) strcpy(mapout, op->mo);
+       ip->authtype = auth_type;
+       auth_type = op->authtype;
+       ip->lvl = level;
+       level = op->lvl;
+       if (!level)
+               level = 1;
+#ifdef KERBEROS
+       memcpy(ip->session, cred.session, sizeof(cred.session));
+       memcpy(cred.session, op->session, sizeof(cred.session));
+       memcpy(ip->schedule, schedule, sizeof(schedule));
+       memcpy(schedule, op->schedule, sizeof(schedule));
+#endif /* KERBEROS */
+       (void) signal(SIGINT, oldintr);
+       if (abrtflag) {
+               abrtflag = 0;
+               (*oldintr)(SIGINT);
+       }
+}
+
+jmp_buf ptabort;
+int ptabflg;
+
+sigtype
+abortpt(sig)
+       int sig;
+{
+       printf("\n");
+       (void) fflush(stdout);
+       ptabflg++;
+       mflag = 0;
+       abrtflag = 0;
+       longjmp(ptabort, 1);
+}
+
+proxtrans(cmd, local, remote)
+       char *cmd, *local, *remote;
+{
+       sig_t oldintr;
+       int secndflag = 0, prox_type, nfnd;
+       extern jmp_buf ptabort;
+       char *cmd2;
+       struct fd_set mask;
+       sigtype abortpt();
+
+       if (strcmp(cmd, "RETR"))
+               cmd2 = "RETR";
+       else
+               cmd2 = runique ? "STOU" : "STOR";
+       if ((prox_type = type) == 0) {
+               if (unix_server && unix_proxy)
+                       prox_type = TYPE_I;
+               else
+                       prox_type = TYPE_A;
+       }
+       if (curtype != prox_type)
+               changetype(prox_type, 1);
+       if (command("PASV") != COMPLETE) {
+               printf("proxy server does not support third party transfers.\n");
+               return;
+       }
+       pswitch(0);
+       if (!connected) {
+               printf("No primary connection\n");
+               pswitch(1);
+               code = -1;
+               return;
+       }
+       if (curtype != prox_type)
+               changetype(prox_type, 1);
+       if (command("PORT %s", pasv) != COMPLETE) {
+               pswitch(1);
+               return;
+       }
+       if (setjmp(ptabort))
+               goto abort;
+       oldintr = signal(SIGINT, abortpt);
+       if (command("%s %s", cmd, remote) != PRELIM) {
+               (void) signal(SIGINT, oldintr);
+               pswitch(1);
+               return;
+       }
+       sleep(2);
+       pswitch(1);
+       secndflag++;
+       if (command("%s %s", cmd2, local) != PRELIM)
+               goto abort;
+       ptflag++;
+       (void) getreply(0);
+       pswitch(0);
+       (void) getreply(0);
+       (void) signal(SIGINT, oldintr);
+       pswitch(1);
+       ptflag = 0;
+       printf("local: %s remote: %s\n", local, remote);
+       return;
+abort:
+       (void) signal(SIGINT, SIG_IGN);
+       ptflag = 0;
+       if (strcmp(cmd, "RETR") && !proxy)
+               pswitch(1);
+       else if (!strcmp(cmd, "RETR") && proxy)
+               pswitch(0);
+       if (!cpend && !secndflag) {  /* only here if cmd = "STOR" (proxy=1) */
+               if (command("%s %s", cmd2, local) != PRELIM) {
+                       pswitch(0);
+                       if (cpend)
+                               abort_remote((FILE *) NULL);
+               }
+               pswitch(1);
+               if (ptabflg)
+                       code = -1;
+               (void) signal(SIGINT, oldintr);
+               return;
+       }
+       if (cpend)
+               abort_remote((FILE *) NULL);
+       pswitch(!proxy);
+       if (!cpend && !secndflag) {  /* only if cmd = "RETR" (proxy=1) */
+               if (command("%s %s", cmd2, local) != PRELIM) {
+                       pswitch(0);
+                       if (cpend)
+                               abort_remote((FILE *) NULL);
+                       pswitch(1);
+                       if (ptabflg)
+                               code = -1;
+                       (void) signal(SIGINT, oldintr);
+                       return;
+               }
+       }
+       if (cpend)
+               abort_remote((FILE *) NULL);
+       pswitch(!proxy);
+       if (cpend) {
+               FD_ZERO(&mask);
+               FD_SET(fileno(cin), &mask);
+               if ((nfnd = empty(&mask, 10)) <= 0) {
+                       if (nfnd < 0) {
+                               perror("abort");
+                       }
+                       if (ptabflg)
+                               code = -1;
+                       lostpeer();
+               }
+               (void) getreply(0);
+               (void) getreply(0);
+       }
+       if (proxy)
+               pswitch(0);
+       pswitch(1);
+       if (ptabflg)
+               code = -1;
+       (void) signal(SIGINT, oldintr);
+}
+
+reset()
+{
+       struct fd_set mask;
+       int nfnd = 1;
+
+       FD_ZERO(&mask);
+       while (nfnd > 0) {
+               FD_SET(fileno(cin), &mask);
+               if ((nfnd = empty(&mask,0)) < 0) {
+                       perror("reset");
+                       code = -1;
+                       lostpeer();
+               }
+               else if (nfnd) {
+                       (void) getreply(0);
+               }
+       }
+}
+
+char *
+gunique(local)
+       char *local;
+{
+       static char new[MAXPATHLEN];
+       char *cp = strrchr(local, '/');
+       int d, count=0;
+       char ext = '1';
+
+       if (cp)
+               *cp = '\0';
+       d = access(cp ? local : ".", 2);
+       if (cp)
+               *cp = '/';
+       if (d < 0) {
+               fprintf(stderr, "local: %s: %s\n", local, strerror(errno));
+               return((char *) 0);
+       }
+       (void) strcpy(new, local);
+       cp = new + strlen(new);
+       *cp++ = '.';
+       while (!d) {
+               if (++count == 100) {
+                       printf("runique: can't find unique file name.\n");
+                       return((char *) 0);
+               }
+               *cp++ = ext;
+               *cp = '\0';
+               if (ext == '9')
+                       ext = '0';
+               else
+                       ext++;
+               if ((d = access(new, 0)) < 0)
+                       break;
+               if (ext != '0')
+                       cp--;
+               else if (*(cp - 2) == '.')
+                       *(cp - 1) = '1';
+               else {
+                       *(cp - 2) = *(cp - 2) + 1;
+                       cp--;
+               }
+       }
+       return(new);
+}
+
+#ifdef KERBEROS
+char realm[REALM_SZ + 1];
+#endif /* KERBEROS */
+
+#ifdef GSSAPI
+/* for testing, we don't have an ftp key yet */
+char* gss_services[] = { /* "ftp",*/ "host", 0 };
+#endif /* GSSAPI */
+
+do_auth()
+{
+       extern int setsafe();
+       int oldverbose;
+#ifdef KERBEROS
+       char *service, inst[INST_SZ];
+       u_long cksum, checksum = (u_long) getpid();
+#endif /* KERBEROS */
+#if defined(KERBEROS) || defined(GSSAPI)
+       u_char out_buf[FTP_BUFSIZ];
+       int i;
+#endif /* KERBEROS */
+
+       if (auth_type) return(1);       /* auth already succeeded */
+
+       /* Other auth types go here ... */
+
+#ifdef KERBEROS
+       if (command("AUTH %s", "KERBEROS_V4") == CONTINUE) {
+           if (verbose)
+               printf("%s accepted as authentication type\n", "KERBEROS_V4");
+
+           strcpy(inst, (char *) krb_get_phost(hostname));
+           if (realm[0] == '\0')
+               strcpy(realm, (char *) krb_realmofhost(hostname));
+           if ((kerror = krb_mk_req(&ticket, service = "ftp",
+                                       inst, realm, checksum))
+               && (kerror != KDC_PR_UNKNOWN ||
+               (kerror = krb_mk_req(&ticket, service = "rcmd",
+                                       inst, realm, checksum))))
+                       fprintf(stderr, "Kerberos V4 krb_mk_req failed: %s\n",
+                                       krb_get_err_text(kerror));
+           else if (kerror = krb_get_cred(service, inst, realm, &cred))
+                       fprintf(stderr, "Kerberos V4 krb_get_cred failed: %s\n",
+                                       krb_get_err_text(kerror));
+           else {
+               key_sched(cred.session, schedule);
+               reply_parse = "ADAT=";
+               oldverbose = verbose;
+               verbose = 0;
+               i = ticket.length;
+               if (kerror = radix_encode(ticket.dat, out_buf, &i, 0))
+                       fprintf(stderr, "Base 64 encoding failed: %s\n",
+                                       radix_error(kerror));
+               else if (command("ADAT %s", out_buf) != COMPLETE)
+                       fprintf(stderr, "Kerberos V4 authentication failed\n");
+               else if (!reply_parse)
+                       fprintf(stderr,
+                              "No authentication data received from server\n");
+               else if (kerror = radix_encode(reply_parse, out_buf, &i, 1))
+                       fprintf(stderr, "Base 64 decoding failed: %s\n",
+                                       radix_error(kerror));
+               else if (kerror = krb_rd_safe(out_buf, i, &cred.session,
+                                           &hisctladdr, &myctladdr, &msg_data))
+                       fprintf(stderr, "Kerberos V4 krb_rd_safe failed: %s\n",
+                                       krb_get_err_text(kerror));
+               else {
+                   /* fetch the (modified) checksum */
+                   (void) memcpy(&cksum, msg_data.app_data, sizeof(cksum));
+                   if (ntohl(cksum) == checksum + 1) {
+                       verbose = oldverbose;
+                       if (verbose)
+                          printf("Kerberos V4 authentication succeeded\n");
+                       reply_parse = NULL;
+                       auth_type = "KERBEROS_V4";
+                       return(1);
+                   } else fprintf(stderr,
+                               "Kerberos V4 mutual authentication failed\n");
+               }
+               verbose = oldverbose;
+               reply_parse = NULL;
+           }
+       } else  fprintf(stderr, "%s rejected as an authentication type\n",
+                               "KERBEROS_V4");
+#endif /* KERBEROS */
+#ifdef GSSAPI
+       if (command("AUTH %s", "GSSAPI") == CONTINUE) {
+         OM_uint32 maj_stat, min_stat;
+         gss_name_t target_name;
+         gss_buffer_desc send_tok, recv_tok, *token_ptr;
+         char stbuf[FTP_BUFSIZ];
+         char **service_name, **end_service_name;
+         int comcode;
+         struct gss_channel_bindings_struct chan;
+         chan.initiator_addrtype = GSS_C_AF_INET; /* OM_uint32  */ 
+         chan.initiator_address.length = 4;
+         chan.initiator_address.value = &myctladdr.sin_addr.s_addr;
+         chan.acceptor_addrtype = GSS_C_AF_INET; /* OM_uint32 */
+         chan.acceptor_address.length = 4;
+         chan.acceptor_address.value = &hisctladdr.sin_addr.s_addr;
+         chan.application_data.length = 0;
+         chan.application_data.value = 0;
+
+         for (end_service_name = gss_services; *end_service_name; )
+           end_service_name++;
+         end_service_name--;
+
+         if (verbose)
+           printf("%s accepted as authentication type\n", "GSSAPI");
+         
+         /* blob from gss-client */
+           
+         
+         for (service_name = gss_services; *service_name; service_name++) {
+           
+           /* ftp@hostname first, the host@hostname */
+           /* the V5 GSSAPI binding canonicalizes this for us... */
+           sprintf(stbuf, "%s@%s", *service_name, hostname);
+           if (debug)
+             fprintf(stderr, "Trying to authenticate to <%s>\n", stbuf);
+
+           send_tok.value = stbuf;
+           send_tok.length = strlen(stbuf) + 1;
+           maj_stat = gss_import_name(&min_stat, &send_tok,
+                                      gss_nt_service_name, &target_name);
+           
+           if (maj_stat != GSS_S_COMPLETE) {
+                   user_gss_error(maj_stat, min_stat, "parsing name");
+                   secure_error("name parsed <%s>\n", stbuf);
+                   continue;
+           }
+
+           token_ptr = GSS_C_NO_BUFFER;
+           gcontext = GSS_C_NO_CONTEXT; /* structure copy */
+           
+           do {
+             if (debug)
+               fprintf(stderr, "calling gss_init_sec_context\n");
+             maj_stat =
+               gss_init_sec_context(&min_stat,
+                                    GSS_C_NO_CREDENTIAL,
+                                    &gcontext,
+                                    target_name,
+                                    GSS_C_NULL_OID,
+                                    GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
+                                    0,
+                                    &chan,     /* channel bindings */
+                                    token_ptr,
+                                    NULL,      /* ignore mech type */
+                                    &send_tok,
+                                    NULL,      /* ignore ret_flags */
+                                    NULL);     /* ignore time_rec */
+             
+
+             if (maj_stat!=GSS_S_COMPLETE && maj_stat!=GSS_S_CONTINUE_NEEDED){
+               user_gss_error(maj_stat, min_stat, "initializing context");
+               (void) gss_release_name(&min_stat, &target_name);
+               /* could just be that we missed on the service name */
+               goto outer_loop;
+             }
+           
+             if (send_tok.length != 0) {
+               int len = send_tok.length;
+               reply_parse = "ADAT="; /* for command() later */
+               oldverbose = verbose;
+               verbose = 0;
+               kerror = radix_encode(send_tok.value, out_buf, &len, 0);
+               if (kerror)  {
+                 fprintf(stderr, "Base 64 encoding failed: %s\n",
+                         radix_error(kerror));
+               } else if ((comcode = command("ADAT %s", out_buf))!=COMPLETE
+                          /* && comcode != 3 (335)*/) {
+                 fprintf(stderr, "GSSAPI ADAT failed\n");
+                 /* force out of loop */
+                 maj_stat = GSS_S_FAILURE;
+               } else if (!reply_parse) {
+                 fprintf(stderr,
+                         "No authentication data received from server\n");
+                 if (maj_stat == GSS_S_COMPLETE) {
+                   fprintf(stderr, "...but no more was needed\n");
+                   goto gss_complete_loop;
+                 } else {
+                   user_gss_error(maj_stat, min_stat, "no reply, huh?");
+                   goto gss_complete_loop;
+                 }
+               } else if (kerror = radix_encode(reply_parse,out_buf,&i,1)) {
+                 fprintf(stderr, "Base 64 decoding failed: %s\n",
+                         radix_error(kerror));
+               } else {
+                 /* everything worked */
+                 token_ptr = &recv_tok;
+                 recv_tok.value = out_buf;
+                 recv_tok.length = i;
+                 continue;
+               }
+
+               /* get out of loop clean */
+             gss_complete_loop:
+               service_name = end_service_name;
+               gss_release_buffer(&min_stat, &send_tok);
+               gss_release_name(&min_stat, &target_name);
+               goto outer_loop;
+             }
+           } while (maj_stat == GSS_S_CONTINUE_NEEDED);
+    outer_loop:
+           ;
+         }
+         verbose = oldverbose;
+         if (maj_stat == GSS_S_COMPLETE) {
+           if (verbose)
+             printf("GSSAPI authentication succeeded\n");
+           reply_parse = NULL;
+           auth_type = "GSSAPI";
+           return(1);
+         } else {
+           fprintf(stderr, "GSSAPI authentication failed\n");
+           verbose = oldverbose;
+           reply_parse = NULL;
+         }
+       }
+#endif /* GSSAPI */
+
+       /* Other auth types go here ... */
+
+       return(0);
+}
+
+setpbsz(size)
+unsigned int size;
+{
+       int oldverbose;
+
+       if (ucbuf) (void) free(ucbuf);
+       actualbuf = size;
+       while ((ucbuf = (unsigned char *)malloc(actualbuf)) == NULL)
+               if (actualbuf)
+                       actualbuf >>= 2;
+               else {
+                       perror("Error while trying to malloc PROT buffer:");
+                       exit(1);
+               }
+       oldverbose = verbose;
+       verbose = 0;
+       reply_parse = "PBSZ=";
+       if (command("PBSZ %u", actualbuf) != COMPLETE)
+               fatal("Cannot set PROT buffer size");
+       if (reply_parse) {
+               if ((maxbuf = (unsigned int) atol(reply_parse)) > actualbuf)
+                       maxbuf = actualbuf;
+       } else  maxbuf = actualbuf;
+       reply_parse = NULL;
+       verbose = oldverbose;
+}
+
+abort_remote(din)
+FILE *din;
+{
+       char buf[FTP_BUFSIZ];
+       int nfnd;
+       struct fd_set mask;
+
+       /*
+        * send IAC in urgent mode instead of DM because 4.3BSD places oob mark
+        * after urgent byte rather than before as is protocol now
+        */
+       sprintf(buf, "%c%c%c", IAC, IP, IAC);
+       if (send(fileno(cout), buf, 3, MSG_OOB) != 3)
+               perror("abort");
+       putc(DM, cout);
+       (void) secure_command("ABOR");
+       FD_ZERO(&mask);
+       FD_SET(fileno(cin), &mask);
+       if (din) { 
+               FD_SET(fileno(din), &mask);
+       }
+       if ((nfnd = empty(&mask, 10)) <= 0) {
+               if (nfnd < 0) {
+                       perror("abort");
+               }
+               if (ptabflg)
+                       code = -1;
+               lostpeer();
+       }
+       if (din && FD_ISSET(fileno(din), &mask)) {
+               /* Security: No threat associated with this read. */
+               while (read(fileno(din), buf, FTP_BUFSIZ) > 0)
+                       /* LOOP */;
+       }
+       if (getreply(0) == ERROR && code == 552) {
+               /* 552 needed for nic style abort */
+               (void) getreply(0);
+       }
+       (void) getreply(0);
+}
+#ifdef GSSAPI
+user_gss_error(maj_stat, min_stat, s)
+OM_uint32 maj_stat, min_stat;
+char *s;
+{
+       /* a lot of work just to report the error */
+       OM_uint32 gmaj_stat, gmin_stat;
+       gss_buffer_desc msg;
+       int msg_ctx;
+       msg_ctx = 0;
+       while (!msg_ctx) {
+               gmaj_stat = gss_display_status(&gmin_stat, maj_stat,
+                                              GSS_C_GSS_CODE,
+                                              GSS_C_NULL_OID,
+                                              &msg_ctx, &msg);
+               if ((gmaj_stat == GSS_S_COMPLETE)||
+                   (gmaj_stat == GSS_S_CONTINUE_NEEDED)) {
+                       fprintf(stderr, "GSSAPI error major: %s\n",
+                               (char*)msg.value);
+                       (void) gss_release_buffer(&gmin_stat, &msg);
+               }
+               if (gmaj_stat != GSS_S_CONTINUE_NEEDED)
+                       break;
+       }
+       msg_ctx = 0;
+       while (!msg_ctx) {
+               gmaj_stat = gss_display_status(&gmin_stat, min_stat,
+                                              GSS_C_MECH_CODE,
+                                              GSS_C_NULL_OID,
+                                              &msg_ctx, &msg);
+               if ((gmaj_stat == GSS_S_COMPLETE)||
+                   (gmaj_stat == GSS_S_CONTINUE_NEEDED)) {
+                       fprintf(stderr, "GSSAPI error minor: %s\n",
+                               (char*)msg.value);
+                       (void) gss_release_buffer(&gmin_stat, &msg);
+               }
+               if (gmaj_stat != GSS_S_CONTINUE_NEEDED)
+                       break;
+       }
+       fprintf(stderr, "GSSAPI error: %s\n", s);
+}
+
+secure_gss_error(maj_stat, min_stat, s)
+     OM_uint32 maj_stat, min_stat;
+     char *s;
+{
+  return user_gss_error(maj_stat, min_stat, s);
+}
+#endif /* GSSAPI */
diff --git a/src/appl/gssftp/ftp/ftp_var.h b/src/appl/gssftp/ftp/ftp_var.h
new file mode 100644 (file)
index 0000000..afc7d36
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 1985, 1989 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.
+ *
+ *     @(#)ftp_var.h   5.9 (Berkeley) 6/1/90
+ */
+
+/*
+ * FTP global variables.
+ */
+
+#ifdef DEFINITIONS
+#define extern
+#endif
+
+/*
+ * Options and other state info.
+ */
+extern int     trace;          /* trace packets exchanged */
+extern int     hash;           /* print # for each buffer transferred */
+extern int     sendport;       /* use PORT cmd for each data connection */
+extern int     verbose;        /* print messages coming back from server */
+extern int     connected;      /* connected to server */
+extern int     fromatty;       /* input is from a terminal */
+extern int     interactive;    /* interactively prompt on m* cmds */
+extern int     debug;          /* debugging level */
+extern int     bell;           /* ring bell on cmd completion */
+extern int     doglob;         /* glob local file names */
+extern int     autologin;      /* establish user account on connection */
+extern int     proxy;          /* proxy server connection active */
+extern int     proxflag;       /* proxy connection exists */
+extern int     sunique;        /* store files on server with unique name */
+extern int     runique;        /* store local files with unique name */
+extern int     mcase;          /* map upper to lower case for mget names */
+extern int     ntflag;         /* use ntin ntout tables for name translation */
+extern int     mapflag;        /* use mapin mapout templates on file names */
+extern int     code;           /* return/reply code for ftp command */
+extern int     crflag;         /* if 1, strip car. rets. on ascii gets */
+extern char    pasv[64];       /* passive port for proxy data connection */
+#ifndef NO_PASSIVE_MODE
+extern int     passivemode;    /* passive mode enabled */
+#endif
+extern char    *altarg;        /* argv[1] with no shell-like preprocessing  */
+extern char    ntin[17];       /* input translation table */
+extern char    ntout[17];      /* output translation table */
+#include <sys/param.h>
+extern char    mapin[MAXPATHLEN];      /* input map template */
+extern char    mapout[MAXPATHLEN];     /* output map template */
+extern int     level;          /* protection level */
+extern int     type;           /* requested file transfer type */
+extern int     curtype;        /* current file transfer type */
+extern int     stru;           /* file transfer structure */
+extern int     form;           /* file transfer format */
+extern int     mode;           /* file transfer mode */
+extern char    bytename[32];   /* local byte size in ascii */
+extern int     bytesize;       /* local byte size in binary */
+
+extern char    *hostname;      /* name of host connected to */
+extern int     unix_server;    /* server is unix, can use binary for ascii */
+extern int     unix_proxy;     /* proxy is unix, can use binary for ascii */
+
+extern struct  servent *sp;    /* service spec for tcp/ftp */
+
+#include <setjmp.h>
+extern jmp_buf toplevel;       /* non-local goto stuff for cmd scanner */
+
+extern char    line[200];      /* input line buffer */
+extern char    *stringbase;    /* current scan point in line buffer */
+extern char    argbuf[200];    /* argument storage buffer */
+extern char    *argbase;       /* current storage point in arg buffer */
+extern int     margc;          /* count of arguments on input line */
+extern char    *margv[20];     /* args parsed from input line */
+extern int     cpend;           /* flag: if != 0, then pending server reply */
+extern int     mflag;          /* flag: if != 0, then active multi command */
+
+extern int     options;        /* used during socket creation */
+
+/*
+ * Format of command table.
+ */
+struct cmd {
+       char    *c_name;        /* name of command */
+       char    *c_help;        /* help string */
+       char    c_bell;         /* give bell when command completes */
+       char    c_conn;         /* must be connected to use command */
+       char    c_proxy;        /* proxy server may execute */
+       int     (*c_handler)(); /* function to call */
+};
+
+struct macel {
+       char mac_name[9];       /* macro name */
+       char *mac_start;        /* start of macro in macbuf */
+       char *mac_end;          /* end of macro in macbuf */
+};
+
+extern int macnum;             /* number of defined macros */
+extern struct macel macros[16];
+extern char macbuf[4096];
+
+#ifdef DEFINITIONS
+#undef extern
+#endif
+
+extern char *tail();
+extern char *remglob();
+extern int errno;
+extern char *mktemp();
+
+#if defined(STDARG) || (defined(__STDC__) && ! defined(VARARGS))
+extern command(char *, ...);
+#endif
diff --git a/src/appl/gssftp/ftp/getpass.c b/src/appl/gssftp/ftp/getpass.c
new file mode 100644 (file)
index 0000000..9020373
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 1985 Regents of the University of California.
+ * All rights reserved.  The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)getpass.c 1.1 90/04/28 SMI"; /* from UCB 5.4 3/7/86 */
+#endif /* not lint */
+
+#include <stdio.h>
+#include <signal.h>
+
+#if defined (POSIX) || defined (POSIX_TERMIOS)
+#include <termios.h>
+static struct termios ttyo, ttyb;
+#define stty(f, t) tcsetattr(f, TCSANOW, t)
+#define gtty(f, t) tcgetattr(f, t)
+#else
+#include <sgtty.h>
+static struct sgttyb ttyo, ttyb;
+#endif
+
+static FILE *fi;
+
+#define sig_t my_sig_t
+#define sigtype krb5_sigtype
+typedef sigtype (*sig_t)();
+
+static sigtype
+intfix(sig)
+       int sig;
+{
+       if (fi != NULL)
+               (void) stty(fileno(fi), &ttyo);
+       exit(SIGINT);
+}
+
+char *
+mygetpass(prompt)
+char *prompt;
+{
+       register char *p;
+       register c;
+       static char pbuf[50+1];
+       sigtype (*sig)();
+
+       if ((fi = fopen("/dev/tty", "r")) == NULL)
+               fi = stdin;
+       else
+               setbuf(fi, (char *)NULL);
+       sig = signal(SIGINT, intfix);
+       (void) gtty(fileno(fi), &ttyb);
+       ttyo = ttyb;
+#if defined (POSIX) || defined (POSIX_TERMIOS)
+       ttyb.c_lflag &= ~ECHO;
+#else
+       ttyb.sg_flags &= ~ECHO;
+#endif
+       (void) stty(fileno(fi), &ttyb);
+       fprintf(stderr, "%s", prompt); (void) fflush(stderr);
+       for (p=pbuf; (c = getc(fi))!='\n' && c!=EOF;) {
+               if (p < &pbuf[sizeof(pbuf)-1])
+                       *p++ = c;
+       }
+       *p = '\0';
+       fprintf(stderr, "\n"); (void) fflush(stderr);
+       (void) stty(fileno(fi), &ttyo);
+       (void) signal(SIGINT, sig);
+       if (fi != stdin)
+               (void) fclose(fi);
+       return(pbuf);
+}
diff --git a/src/appl/gssftp/ftp/glob.c b/src/appl/gssftp/ftp/glob.c
new file mode 100644 (file)
index 0000000..3a793d8
--- /dev/null
@@ -0,0 +1,698 @@
+/*
+ * Copyright (c) 1980 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[] = "@(#)glob.c     5.9 (Berkeley) 2/25/91";
+#endif /* not lint */
+
+/*
+ * C-shell glob for random programs.
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <dirent.h>
+
+#include <pwd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef POSIX
+#include <limits.h>
+#endif
+
+#ifdef ARG_MAX
+#undef NCARGS
+#define NCARGS ARG_MAX
+#endif
+
+#ifndef NCARGS
+#define NCARGS 4096
+#endif
+
+#define        QUOTE 0200
+#define        TRIM 0177
+#define        eq(a,b)         (strcmp(a, b)==0)
+#define        GAVSIZ          (NCARGS/6)
+#define        isdir(d)        ((d.st_mode & S_IFMT) == S_IFDIR)
+
+static char **gargv;           /* Pointer to the (stack) arglist */
+static int gargc;              /* Number args in gargv */
+static int gnleft;
+static short gflag;
+static int tglob();
+char   **ftpglob();
+char   *globerr;
+char   *home;
+extern int errno;
+static char *strspl(), *strend();
+char   **copyblk();
+
+static void acollect(), addpath(), collect(), expand(), Gcat();
+static void ginit(), matchdir(), rscan(), sort();
+static int amatch(), execbrc(), match();
+
+static int globcnt;
+
+char   *globchars = "`{[*?";
+
+static char *gpath, *gpathp, *lastgpathp;
+static int globbed;
+static char *entp;
+static char **sortbas;
+
+char **
+ftpglob(v)
+       register char *v;
+{
+       char agpath[FTP_BUFSIZ];
+       char *agargv[GAVSIZ];
+       char *vv[2];
+       vv[0] = v;
+       vv[1] = 0;
+       gflag = 0;
+       rscan(vv, tglob);
+       if (gflag == 0)
+               return (copyblk(vv));
+
+       globerr = 0;
+       gpath = agpath; gpathp = gpath; *gpathp = 0;
+       lastgpathp = &gpath[sizeof agpath - 2];
+       ginit(agargv); globcnt = 0;
+       collect(v);
+       if (globcnt == 0 && (gflag&1)) {
+               blkfree(gargv), gargv = 0;
+               return (0);
+       } else
+               return (gargv = copyblk(gargv));
+}
+
+static void
+ginit(agargv)
+       char **agargv;
+{
+
+       agargv[0] = 0; gargv = agargv; sortbas = agargv; gargc = 0;
+       gnleft = NCARGS - 4;
+}
+
+static void
+collect(as)
+       register char *as;
+{
+       if (eq(as, "{") || eq(as, "{}")) {
+               Gcat(as, "");
+               sort();
+       } else
+               acollect(as);
+}
+
+static void
+acollect(as)
+       register char *as;
+{
+       register int ogargc = gargc;
+
+       gpathp = gpath; *gpathp = 0; globbed = 0;
+       expand(as);
+       if (gargc != ogargc)
+               sort();
+}
+
+static void
+sort()
+{
+       register char **p1, **p2, *c;
+       char **Gvp = &gargv[gargc];
+
+       p1 = sortbas;
+       while (p1 < Gvp-1) {
+               p2 = p1;
+               while (++p2 < Gvp)
+                       if (strcmp(*p1, *p2) > 0)
+                               c = *p1, *p1 = *p2, *p2 = c;
+               p1++;
+       }
+       sortbas = Gvp;
+}
+
+static void
+expand(as)
+       char *as;
+{
+       register char *cs;
+       register char *sgpathp, *oldcs;
+       struct stat stb;
+
+       sgpathp = gpathp;
+       cs = as;
+       if (*cs == '~' && gpathp == gpath) {
+               addpath('~');
+               for (cs++; letter(*cs) || digit(*cs) || *cs == '-';)
+                       addpath(*cs++);
+               if (!*cs || *cs == '/') {
+                       if (gpathp != gpath + 1) {
+                               *gpathp = 0;
+                               if (gethdir(gpath + 1))
+                                       globerr = "Unknown user name after ~";
+                               (void) strcpy(gpath, gpath + 1);
+                       } else
+                               (void) strcpy(gpath, home);
+                       gpathp = strend(gpath);
+               }
+       }
+       while (!any(*cs, globchars)) {
+               if (*cs == 0) {
+                       if (!globbed)
+                               Gcat(gpath, "");
+                       else if (stat(gpath, &stb) >= 0) {
+                               Gcat(gpath, "");
+                               globcnt++;
+                       }
+                       goto endit;
+               }
+               addpath(*cs++);
+       }
+       oldcs = cs;
+       while (cs > as && *cs != '/')
+               cs--, gpathp--;
+       if (*cs == '/')
+               cs++, gpathp++;
+       *gpathp = 0;
+       if (*oldcs == '{') {
+               (void) execbrc(cs, ((char *)0));
+               return;
+       }
+       matchdir(cs);
+endit:
+       gpathp = sgpathp;
+       *gpathp = 0;
+}
+
+static void
+matchdir(pattern)
+       char *pattern;
+{
+       struct stat stb;
+       register struct dirent *dp;
+       DIR *dirp;
+
+       dirp = opendir(*gpath?gpath:".");
+       if (dirp == NULL) {
+               if (globbed)
+                       return;
+               goto patherr2;
+       }
+       /* This fails on systems where you can't inspect the contents of
+          the DIR structure.  If there are systems whose opendir does
+          not check for a directory, then use stat, not fstat. */
+#if 0
+       if (fstat(dirp->dd_fd, &stb) < 0)
+               goto patherr1;
+       if (!isdir(stb)) {
+               errno = ENOTDIR;
+               goto patherr1;
+       }
+#endif
+       while ((dp = readdir(dirp)) != NULL) {
+               if (dp->d_ino == 0)
+                       continue;
+               if (match(dp->d_name, pattern)) {
+                       Gcat(gpath, dp->d_name);
+                       globcnt++;
+               }
+       }
+       closedir(dirp);
+       return;
+
+patherr1:
+       closedir(dirp);
+patherr2:
+       globerr = "Bad directory components";
+}
+
+static int
+execbrc(p, s)
+       char *p, *s;
+{
+       char restbuf[FTP_BUFSIZ + 2];
+       register char *pe, *pm, *pl;
+       int brclev = 0;
+       char *lm, savec, *sgpathp;
+
+       for (lm = restbuf; *p != '{'; *lm++ = *p++)
+               continue;
+       for (pe = ++p; *pe; pe++)
+       switch (*pe) {
+
+       case '{':
+               brclev++;
+               continue;
+
+       case '}':
+               if (brclev == 0)
+                       goto pend;
+               brclev--;
+               continue;
+
+       case '[':
+               for (pe++; *pe && *pe != ']'; pe++)
+                       continue;
+               continue;
+       }
+pend:
+       brclev = 0;
+       for (pl = pm = p; pm <= pe; pm++)
+       switch (*pm & (QUOTE|TRIM)) {
+
+       case '{':
+               brclev++;
+               continue;
+
+       case '}':
+               if (brclev) {
+                       brclev--;
+                       continue;
+               }
+               goto doit;
+
+       case ','|QUOTE:
+       case ',':
+               if (brclev)
+                       continue;
+doit:
+               savec = *pm;
+               *pm = 0;
+               (void) strcpy(lm, pl);
+               (void) strcat(restbuf, pe + 1);
+               *pm = savec;
+               if (s == 0) {
+                       sgpathp = gpathp;
+                       expand(restbuf);
+                       gpathp = sgpathp;
+                       *gpathp = 0;
+               } else if (amatch(s, restbuf))
+                       return (1);
+               sort();
+               pl = pm + 1;
+               if (brclev)
+                       return (0);
+               continue;
+
+       case '[':
+               for (pm++; *pm && *pm != ']'; pm++)
+                       continue;
+               if (!*pm)
+                       pm--;
+               continue;
+       }
+       if (brclev)
+               goto doit;
+       return (0);
+}
+
+static int
+match(s, p)
+       char *s, *p;
+{
+       register int c;
+       register char *sentp;
+       char sglobbed = globbed;
+
+       if (*s == '.' && *p != '.')
+               return (0);
+       sentp = entp;
+       entp = s;
+       c = amatch(s, p);
+       entp = sentp;
+       globbed = sglobbed;
+       return (c);
+}
+
+static int
+amatch(s, p)
+       register char *s, *p;
+{
+       register int scc;
+       int ok, lc;
+       char *sgpathp;
+       struct stat stb;
+       int c, cc;
+
+       globbed = 1;
+       for (;;) {
+               scc = *s++ & TRIM;
+               switch (c = *p++) {
+
+               case '{':
+                       return (execbrc(p - 1, s - 1));
+
+               case '[':
+                       ok = 0;
+                       lc = 077777;
+                       while (cc = *p++) {
+                               if (cc == ']') {
+                                       if (ok)
+                                               break;
+                                       return (0);
+                               }
+                               if (cc == '-') {
+                                       if (lc <= scc && scc <= *p++)
+                                               ok++;
+                               } else
+                                       if (scc == (lc = cc))
+                                               ok++;
+                       }
+                       if (cc == 0)
+                               if (ok)
+                                       p--;
+                               else
+                                       return 0;
+                       continue;
+
+               case '*':
+                       if (!*p)
+                               return (1);
+                       if (*p == '/') {
+                               p++;
+                               goto slash;
+                       }
+                       s--;
+                       do {
+                               if (amatch(s, p))
+                                       return (1);
+                       } while (*s++);
+                       return (0);
+
+               case 0:
+                       return (scc == 0);
+
+               default:
+                       if (c != scc)
+                               return (0);
+                       continue;
+
+               case '?':
+                       if (scc == 0)
+                               return (0);
+                       continue;
+
+               case '/':
+                       if (scc)
+                               return (0);
+slash:
+                       s = entp;
+                       sgpathp = gpathp;
+                       while (*s)
+                               addpath(*s++);
+                       addpath('/');
+                       if (stat(gpath, &stb) == 0 && isdir(stb))
+                               if (*p == 0) {
+                                       Gcat(gpath, "");
+                                       globcnt++;
+                               } else
+                                       expand(p);
+                       gpathp = sgpathp;
+                       *gpathp = 0;
+                       return (0);
+               }
+       }
+}
+
+static
+Gmatch(s, p)
+       register char *s, *p;
+{
+       register int scc;
+       int ok, lc;
+       int c, cc;
+
+       for (;;) {
+               scc = *s++ & TRIM;
+               switch (c = *p++) {
+
+               case '[':
+                       ok = 0;
+                       lc = 077777;
+                       while (cc = *p++) {
+                               if (cc == ']') {
+                                       if (ok)
+                                               break;
+                                       return (0);
+                               }
+                               if (cc == '-') {
+                                       if (lc <= scc && scc <= *p++)
+                                               ok++;
+                               } else
+                                       if (scc == (lc = cc))
+                                               ok++;
+                       }
+                       if (cc == 0)
+                               if (ok)
+                                       p--;
+                               else
+                                       return 0;
+                       continue;
+
+               case '*':
+                       if (!*p)
+                               return (1);
+                       for (s--; *s; s++)
+                               if (Gmatch(s, p))
+                                       return (1);
+                       return (0);
+
+               case 0:
+                       return (scc == 0);
+
+               default:
+                       if ((c & TRIM) != scc)
+                               return (0);
+                       continue;
+
+               case '?':
+                       if (scc == 0)
+                               return (0);
+                       continue;
+
+               }
+       }
+}
+
+static void
+Gcat(s1, s2)
+       register char *s1, *s2;
+{
+       register int len = strlen(s1) + strlen(s2) + 1;
+
+       if (len >= gnleft || gargc >= GAVSIZ - 1)
+               globerr = "Arguments too long";
+       else {
+               gargc++;
+               gnleft -= len;
+               gargv[gargc] = 0;
+               gargv[gargc - 1] = strspl(s1, s2);
+       }
+}
+
+static void
+addpath(c)
+       char c;
+{
+
+       if (gpathp >= lastgpathp)
+               globerr = "Pathname too long";
+       else {
+               *gpathp++ = c;
+               *gpathp = 0;
+       }
+}
+
+static void
+rscan(t, f)
+       register char **t;
+       int (*f)();
+{
+       register char *p, c;
+
+       while (p = *t++) {
+               if (f == tglob)
+                       if (*p == '~')
+                               gflag |= 2;
+                       else if (eq(p, "{") || eq(p, "{}"))
+                               continue;
+               while (c = *p++)
+                       (*f)(c);
+       }
+}
+/*
+static
+scan(t, f)
+       register char **t;
+       int (*f)();
+{
+       register char *p, c;
+
+       while (p = *t++)
+               while (c = *p)
+                       *p++ = (*f)(c);
+} */
+
+static
+tglob(c)
+       register char c;
+{
+
+       if (any(c, globchars))
+               gflag |= c == '{' ? 2 : 1;
+       return (c);
+}
+/*
+static
+trim(c)
+       char c;
+{
+
+       return (c & TRIM);
+} */
+
+
+letter(c)
+       register char c;
+{
+
+       return (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_');
+}
+
+digit(c)
+       register char c;
+{
+
+       return (c >= '0' && c <= '9');
+}
+
+any(c, s)
+       register int c;
+       register char *s;
+{
+
+       while (*s)
+               if (*s++ == c)
+                       return(1);
+       return(0);
+}
+blklen(av)
+       register char **av;
+{
+       register int i = 0;
+
+       while (*av++)
+               i++;
+       return (i);
+}
+
+char **
+blkcpy(oav, bv)
+       char **oav;
+       register char **bv;
+{
+       register char **av = oav;
+
+       while (*av++ = *bv++)
+               continue;
+       return (oav);
+}
+
+blkfree(av0)
+       char **av0;
+{
+       register char **av = av0;
+
+       while (*av)
+               free(*av++);
+}
+
+static
+char *
+strspl(cp, dp)
+       register char *cp, *dp;
+{
+       register char *ep = malloc((unsigned)(strlen(cp) + strlen(dp) + 1));
+
+       if (ep == (char *)0)
+               fatal("Out of memory");
+       (void) strcpy(ep, cp);
+       (void) strcat(ep, dp);
+       return (ep);
+}
+
+char **
+copyblk(v)
+       register char **v;
+{
+       register char **nv = (char **)malloc((unsigned)((blklen(v) + 1) *
+                                               sizeof(char **)));
+       if (nv == (char **)0)
+               fatal("Out of memory");
+
+       return (blkcpy(nv, v));
+}
+
+static
+char *
+strend(cp)
+       register char *cp;
+{
+
+       while (*cp)
+               cp++;
+       return (cp);
+}
+/*
+ * Extract a home directory from the password file
+ * The argument points to a buffer where the name of the
+ * user whose home directory is sought is currently.
+ * We write the home directory of the user back there.
+ */
+gethdir(home)
+       char *home;
+{
+       register struct passwd *pp = getpwnam(home);
+
+       if (!pp || home + strlen(pp->pw_dir) >= lastgpathp)
+               return (1);
+       (void) strcpy(home, pp->pw_dir);
+       return (0);
+}
diff --git a/src/appl/gssftp/ftp/main.c b/src/appl/gssftp/ftp/main.c
new file mode 100644 (file)
index 0000000..4a6d88f
--- /dev/null
@@ -0,0 +1,553 @@
+/*
+ * Copyright (c) 1985, 1989 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
+char copyright[] =
+"@(#) Copyright (c) 1985, 1989 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c     5.18 (Berkeley) 3/1/91";
+#endif /* not lint */
+
+/*
+ * FTP User Program -- Command Interface.
+ */
+#include <stdio.h>
+#include "ftp_var.h"
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+
+#include <arpa/ftp.h>
+
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <pwd.h>
+
+#define sig_t my_sig_t
+#define sigtype krb5_sigtype
+typedef sigtype (*sig_t)();
+
+uid_t  getuid();
+sigtype        intr(), lostpeer();
+extern char *home;
+char   *getlogin();
+#ifdef KERBEROS
+#include <krb.h>
+struct servent staticsp;
+extern char realm[];
+#endif /* KERBEROS */
+
+main(argc, argv)
+       char *argv[];
+{
+       register char *cp;
+       int top;
+       struct passwd *pw = NULL;
+       char homedir[MAXPATHLEN];
+
+       sp = getservbyname("ftp", "tcp");
+       if (sp == 0) {
+               fprintf(stderr, "ftp: ftp/tcp: unknown service\n");
+               exit(1);
+       }
+#ifdef KERBEROS
+/* GDM need to static sp so that the information is not lost
+   when kerberos calls getservbyname */
+       memcpy(&staticsp,sp,sizeof(struct servent));
+       sp = &staticsp;
+#endif /* KERBEROS */
+       doglob = 1;
+       interactive = 1;
+       autologin = 1;
+       argc--, argv++;
+       while (argc > 0 && **argv == '-') {
+               for (cp = *argv + 1; *cp; cp++)
+                       switch (*cp) {
+
+                       case 'd':
+                               options |= SO_DEBUG;
+                               debug++;
+                               break;
+
+#ifdef KERBEROS
+                       case 'k':
+                               if (*++cp != '\0')
+                                       strncpy(realm, ++cp, REALM_SZ);
+                               else if (argc > 1) {
+                                       argc--, argv++;
+                                       strncpy(realm, *argv, REALM_SZ);
+                               }
+                               else
+                                       fprintf(stderr, "ftp: -k expects arguments\n");
+                               goto nextopt;
+#endif
+
+                       case 'v':
+                               verbose++;
+                               break;
+
+                       case 't':
+                               trace++;
+                               break;
+
+                       case 'i':
+                               interactive = 0;
+                               break;
+
+                       case 'n':
+                               autologin = 0;
+                               break;
+
+                       case 'g':
+                               doglob = 0;
+                               break;
+
+                       default:
+                               fprintf(stdout,
+                                 "ftp: %c: unknown option\n", *cp);
+                               exit(1);
+                       }
+       nextopt:
+               argc--, argv++;
+       }
+       fromatty = isatty(fileno(stdin));
+       if (fromatty)
+               verbose++;
+       cpend = 0;      /* no pending replies */
+       proxy = 0;      /* proxy not active */
+#ifndef NO_PASSIVE_MODE
+       passivemode = 1; /* passive mode active */
+#endif
+       crflag = 1;     /* strip c.r. on ascii gets */
+       sendport = -1;  /* not using ports */
+       /*
+        * Set up the home directory in case we're globbing.
+        */
+       cp = getlogin();
+       if (cp != NULL) {
+               pw = getpwnam(cp);
+       }
+       if (pw == NULL)
+               pw = getpwuid(getuid());
+       if (pw != NULL) {
+               home = homedir;
+               (void) strcpy(home, pw->pw_dir);
+       }
+       if (argc > 0) {
+               if (setjmp(toplevel))
+                       exit(0);
+               (void) signal(SIGINT, intr);
+               (void) signal(SIGPIPE, lostpeer);
+               setpeer(argc + 1, argv - 1);
+       }
+       top = setjmp(toplevel) == 0;
+       if (top) {
+               (void) signal(SIGINT, intr);
+               (void) signal(SIGPIPE, lostpeer);
+       }
+       for (;;) {
+               cmdscanner(top);
+               top = 1;
+       }
+}
+
+sigtype
+intr(sig)
+       int sig;
+{
+
+       longjmp(toplevel, 1);
+}
+
+sigtype
+lostpeer(sig)
+       int sig;
+{
+       extern FILE *cout;
+       extern int data;
+       extern char *auth_type;
+       extern int level;
+
+       if (connected) {
+               if (cout != NULL) {
+                       (void) shutdown(fileno(cout), 1+1);
+                       (void) fclose(cout);
+                       cout = NULL;
+               }
+               if (data >= 0) {
+                       (void) shutdown(data, 1+1);
+                       (void) close(data);
+                       data = -1;
+               }
+               connected = 0;
+               auth_type = NULL;
+               level = PROT_C;
+       }
+       pswitch(1);
+       if (connected) {
+               if (cout != NULL) {
+                       (void) shutdown(fileno(cout), 1+1);
+                       (void) fclose(cout);
+                       cout = NULL;
+               }
+               connected = 0;
+               auth_type = NULL;
+               level = PROT_C;
+       }
+       proxflag = 0;
+       pswitch(0);
+}
+
+/*char *
+tail(filename)
+       char *filename;
+{
+       register char *s;
+       
+       while (*filename) {
+               s = strrchr(filename, '/');
+               if (s == NULL)
+                       break;
+               if (s[1])
+                       return (s + 1);
+               *s = '\0';
+       }
+       return (filename);
+}
+*/
+/*
+ * Command parser.
+ */
+cmdscanner(top)
+       int top;
+{
+       register struct cmd *c;
+       register int l;
+       struct cmd *getcmd();
+       extern int help();
+
+       if (!top)
+               (void) putchar('\n');
+       for (;;) {
+               if (fromatty) {
+                       printf("ftp> ");
+                       (void) fflush(stdout);
+               }
+               if (fgets(line, sizeof line, stdin) == NULL)
+                       quit();
+               l = strlen(line);
+               if (l == 0)
+                       break;
+               if (line[--l] == '\n') {
+                       if (l == 0)
+                               break;
+                       line[l] = '\0';
+               } else if (l == sizeof(line) - 2) {
+                       printf("sorry, input line too long\n");
+                       while ((l = getchar()) != '\n' && l != EOF)
+                               /* void */;
+                       break;
+               } /* else it was a line without a newline */
+               makeargv();
+               if (margc == 0) {
+                       continue;
+               }
+               c = getcmd(margv[0]);
+               if (c == (struct cmd *)-1) {
+                       printf("?Ambiguous command\n");
+                       continue;
+               }
+               if (c == 0) {
+                       printf("?Invalid command\n");
+                       continue;
+               }
+               if (c->c_conn && !connected) {
+                       printf("Not connected.\n");
+                       continue;
+               }
+               (*c->c_handler)(margc, margv);
+               if (bell && c->c_bell)
+                       (void) putchar('\007');
+               if (c->c_handler != help)
+                       break;
+       }
+       (void) signal(SIGINT, intr);
+       (void) signal(SIGPIPE, lostpeer);
+}
+
+struct cmd *
+getcmd(name)
+       register char *name;
+{
+       extern struct cmd cmdtab[];
+       register char *p, *q;
+       register struct cmd *c, *found;
+       register int nmatches, longest;
+
+       longest = 0;
+       nmatches = 0;
+       found = 0;
+       for (c = cmdtab; p = c->c_name; c++) {
+               for (q = name; *q == *p++; q++)
+                       if (*q == 0)            /* exact match? */
+                               return (c);
+               if (!*q) {                      /* the name was a prefix */
+                       if (q - name > longest) {
+                               longest = q - name;
+                               nmatches = 1;
+                               found = c;
+                       } else if (q - name == longest)
+                               nmatches++;
+               }
+       }
+       if (nmatches > 1)
+               return ((struct cmd *)-1);
+       return (found);
+}
+
+/*
+ * Slice a string up into argc/argv.
+ */
+
+int slrflag;
+
+makeargv()
+{
+       char **argp;
+       char *slurpstring();
+
+       margc = 0;
+       argp = margv;
+       stringbase = line;              /* scan from first of buffer */
+       argbase = argbuf;               /* store from first of buffer */
+       slrflag = 0;
+       while (*argp++ = slurpstring())
+               margc++;
+}
+
+/*
+ * Parse string into argbuf;
+ * implemented with FSM to
+ * handle quoting and strings
+ */
+char *
+slurpstring()
+{
+       int got_one = 0;
+       register char *sb = stringbase;
+       register char *ap = argbase;
+       char *tmp = argbase;            /* will return this if token found */
+
+       if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
+               switch (slrflag) {      /* and $ as token for macro invoke */
+                       case 0:
+                               slrflag++;
+                               stringbase++;
+                               return ((*sb == '!') ? "!" : "$");
+                               /* NOTREACHED */
+                       case 1:
+                               slrflag++;
+                               altarg = stringbase;
+                               break;
+                       default:
+                               break;
+               }
+       }
+
+S0:
+       switch (*sb) {
+
+       case '\0':
+               goto OUT;
+
+       case ' ':
+       case '\t':
+               sb++; goto S0;
+
+       default:
+               switch (slrflag) {
+                       case 0:
+                               slrflag++;
+                               break;
+                       case 1:
+                               slrflag++;
+                               altarg = sb;
+                               break;
+                       default:
+                               break;
+               }
+               goto S1;
+       }
+
+S1:
+       switch (*sb) {
+
+       case ' ':
+       case '\t':
+       case '\0':
+               goto OUT;       /* end of token */
+
+       case '\\':
+               sb++; goto S2;  /* slurp next character */
+
+       case '"':
+               sb++; goto S3;  /* slurp quoted string */
+
+       default:
+               *ap++ = *sb++;  /* add character to token */
+               got_one = 1;
+               goto S1;
+       }
+
+S2:
+       switch (*sb) {
+
+       case '\0':
+               goto OUT;
+
+       default:
+               *ap++ = *sb++;
+               got_one = 1;
+               goto S1;
+       }
+
+S3:
+       switch (*sb) {
+
+       case '\0':
+               goto OUT;
+
+       case '"':
+               sb++; goto S1;
+
+       default:
+               *ap++ = *sb++;
+               got_one = 1;
+               goto S3;
+       }
+
+OUT:
+       if (got_one)
+               *ap++ = '\0';
+       argbase = ap;                   /* update storage pointer */
+       stringbase = sb;                /* update scan pointer */
+       if (got_one) {
+               return(tmp);
+       }
+       switch (slrflag) {
+               case 0:
+                       slrflag++;
+                       break;
+               case 1:
+                       slrflag++;
+                       altarg = (char *) 0;
+                       break;
+               default:
+                       break;
+       }
+       return((char *)0);
+}
+
+#define HELPINDENT (sizeof ("directory"))
+
+/*
+ * Help command.
+ * Call each command handler with argc == 0 and argv[0] == name.
+ */
+help(argc, argv)
+       int argc;
+       char *argv[];
+{
+       extern struct cmd cmdtab[];
+       register struct cmd *c;
+
+       if (argc == 1) {
+               register int i, j, w, k;
+               int columns, width = 0, lines;
+               extern int NCMDS;
+
+               printf("Commands may be abbreviated.  Commands are:\n\n");
+               for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
+                       int len = strlen(c->c_name);
+
+                       if (len > width)
+                               width = len;
+               }
+               width = (width + 8) &~ 7;
+               columns = 80 / width;
+               if (columns == 0)
+                       columns = 1;
+               lines = (NCMDS + columns - 1) / columns;
+               for (i = 0; i < lines; i++) {
+                       for (j = 0; j < columns; j++) {
+                               c = cmdtab + j * lines + i;
+                               if (c->c_name && (!proxy || c->c_proxy)) {
+                                       printf("%s", c->c_name);
+                               }
+                               else if (c->c_name) {
+                                       for (k=0; k < strlen(c->c_name); k++) {
+                                               (void) putchar(' ');
+                                       }
+                               }
+                               if (c + lines >= &cmdtab[NCMDS]) {
+                                       printf("\n");
+                                       break;
+                               }
+                               w = strlen(c->c_name);
+                               while (w < width) {
+                                       w = (w + 8) &~ 7;
+                                       (void) putchar('\t');
+                               }
+                       }
+               }
+               return;
+       }
+       while (--argc > 0) {
+               register char *arg;
+               arg = *++argv;
+               c = getcmd(arg);
+               if (c == (struct cmd *)-1)
+                       printf("?Ambiguous help command %s\n", arg);
+               else if (c == (struct cmd *)0)
+                       printf("?Invalid help command %s\n", arg);
+               else
+                       printf("%-*s\t%s\n", HELPINDENT,
+                               c->c_name, c->c_help);
+       }
+}
diff --git a/src/appl/gssftp/ftp/pathnames.h b/src/appl/gssftp/ftp/pathnames.h
new file mode 100644 (file)
index 0000000..7c0de5b
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 1989 The 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.
+ *
+ *     @(#)pathnames.h 5.2 (Berkeley) 6/1/90
+ */
+
+#undef _PATH_TMP
+#define        _PATH_TMP       "/tmp/ftpXXXXXX"
diff --git a/src/appl/gssftp/ftp/pclose.c b/src/appl/gssftp/ftp/pclose.c
new file mode 100644 (file)
index 0000000..e1e13e4
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 1980 Regents of the University of California.
+ * All rights reserved.  The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)pclose.c 1.1 90/04/28 SMI"; /* from UCB 1.2 3/7/86 */
+#endif /* not lint */
+
+#include <stdio.h>
+#include <signal.h>
+#include <sys/param.h>
+#include <sys/wait.h>
+#ifdef HAVE_VFORK_H
+#include <vfork.h>
+#endif
+#define sig_t my_sig_t
+#define sigtype krb5_sigtype
+typedef sigtype (*sig_t)();
+
+#define        tst(a,b)        (*mode == 'r'? (b) : (a))
+#define        RDR     0
+#define        WTR     1
+
+static int *popen_pid;
+static int nfiles;
+
+#ifndef HAVE_GETDTABLESIZE
+#include <sys/resource.h>
+int getdtablesize() {
+  struct rlimit rl;
+  getrlimit(RLIMIT_NOFILE, &rl);
+  return rl.rlim_cur;
+}
+#endif
+
+FILE *
+mypopen(cmd,mode)
+       char *cmd;
+       char *mode;
+{
+       int p[2];
+       int myside, hisside, pid;
+
+       if (nfiles <= 0)
+               nfiles = getdtablesize();
+       if (popen_pid == NULL) {
+               popen_pid = (int *)malloc((unsigned) nfiles * sizeof *popen_pid);
+               if (popen_pid == NULL)
+                       return (NULL);
+               for (pid = 0; pid < nfiles; pid++)
+                       popen_pid[pid] = -1;
+       }
+       if (pipe(p) < 0)
+               return (NULL);
+       myside = tst(p[WTR], p[RDR]);
+       hisside = tst(p[RDR], p[WTR]);
+       if ((pid = vfork()) == 0) {
+               /* myside and hisside reverse roles in child */
+               (void) close(myside);
+               if (hisside != tst(0, 1)) {
+                       (void) dup2(hisside, tst(0, 1));
+                       (void) close(hisside);
+               }
+               execl("/bin/sh", "sh", "-c", cmd, (char *)NULL);
+               _exit(127);
+       }
+       if (pid == -1) {
+               (void) close(myside);
+               (void) close(hisside);
+               return (NULL);
+       }
+       popen_pid[myside] = pid;
+       (void) close(hisside);
+       return (fdopen(myside, mode));
+}
+
+sigtype
+pabort(sig)
+       int sig;
+{
+       extern int mflag;
+
+       mflag = 0;
+}
+
+mypclose(ptr)
+       FILE *ptr;
+{
+       int child, pid;
+#ifdef USE_SIGPROCMASK
+       sigset_t old, new;
+#else
+       int omask;
+#endif
+       sigtype pabort(), (*istat)();
+#ifdef WAIT_USES_INT
+       int status;
+#else
+       union wait status;
+#endif
+
+       child = popen_pid[fileno(ptr)];
+       popen_pid[fileno(ptr)] = -1;
+       (void) fclose(ptr);
+       if (child == -1)
+               return (-1);
+       istat = signal(SIGINT, pabort);
+#ifdef USE_SIGPROCMASK
+       sigemptyset(&old);
+       sigemptyset(&new);
+       sigaddset(&new,SIGQUIT);
+       sigaddset(&new,SIGHUP);
+       sigprocmask(SIG_BLOCK, &new, &old);
+       while ((pid = wait(&status)) != child && pid != -1)
+               ;
+       sigprocmask(SIG_SETMASK, &old, NULL);
+#else
+       omask = sigblock(sigmask(SIGQUIT)|sigmask(SIGHUP));
+       while ((pid = wait(&status)) != child && pid != -1)
+               ;
+       sigsetmask(omask);
+#endif
+       (void) signal(SIGINT, istat);
+       return (pid == -1 ? -1 : 0);
+}
diff --git a/src/appl/gssftp/ftp/radix.c b/src/appl/gssftp/ftp/radix.c
new file mode 100644 (file)
index 0000000..c651ef5
--- /dev/null
@@ -0,0 +1,163 @@
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+static char *radixN =
+       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static char pad = '=';
+
+radix_encode(inbuf, outbuf, len, decode)
+unsigned char inbuf[], outbuf[];
+int *len, decode;
+{
+       int i,j,D;
+       char *p;
+       unsigned char c;
+
+       if (decode) {
+               for (i=0,j=0; inbuf[i] && inbuf[i] != pad; i++) {
+                   if ((p = strchr(radixN, inbuf[i])) == NULL) return(1);
+                   D = p - radixN;
+                   switch (i&3) {
+                       case 0:
+                           outbuf[j] = D<<2;
+                           break;
+                       case 1:
+                           outbuf[j++] |= D>>4;
+                           outbuf[j] = (D&15)<<4;
+                           break;
+                       case 2:
+                           outbuf[j++] |= D>>2;
+                           outbuf[j] = (D&3)<<6;
+                           break;
+                       case 3:
+                           outbuf[j++] |= D;
+                   }
+               }
+               switch (i&3) {
+                       case 1: return(3);
+                       case 2: if (D&15) return(3);
+                               if (strcmp((char *)&inbuf[i], "==")) return(2);
+                               break;
+                       case 3: if (D&3) return(3);
+                               if (strcmp((char *)&inbuf[i], "="))  return(2);
+               }
+               *len = j;
+       } else {
+               for (i=0,j=0; i < *len; i++)
+                   switch (i%3) {
+                       case 0:
+                           outbuf[j++] = radixN[inbuf[i]>>2];
+                           c = (inbuf[i]&3)<<4;
+                           break;
+                       case 1:
+                           outbuf[j++] = radixN[c|inbuf[i]>>4];
+                           c = (inbuf[i]&15)<<2;
+                           break;
+                       case 2:
+                           outbuf[j++] = radixN[c|inbuf[i]>>6];
+                           outbuf[j++] = radixN[inbuf[i]&63];
+                           c = 0;
+                   }
+               if (i%3) outbuf[j++] = radixN[c];
+               switch (i%3) {
+                       case 1: outbuf[j++] = pad;
+                       case 2: outbuf[j++] = pad;
+               }
+               outbuf[*len = j] = '\0';
+       }
+       return(0);
+}
+
+char *
+radix_error(e)
+{
+       switch (e) {
+           case 0:  return("Success");
+           case 1:  return("Bad character in encoding");
+           case 2:  return("Encoding not properly padded");
+           case 3:  return("Decoded # of bits not a multiple of 8");
+           default: return("Unknown error");
+       }
+}
+
+#ifdef STANDALONE
+usage(s)
+char *s;
+{
+       fprintf(stderr, "Usage: %s [ -d ] [ string ]\n", s);
+       exit(2);
+}
+
+static int n;
+
+putbuf(inbuf, outbuf, len, decode)
+unsigned char inbuf[], outbuf[];
+int len, decode;
+{
+       int c;
+
+       if (c = radix_encode(inbuf, outbuf, &len, decode)) {
+               fprintf(stderr, "Couldn't %scode input: %s\n",
+                               decode ? "de" : "en", radix_error(c));
+               exit(1);
+       }
+       if (decode)
+               write(1, outbuf, len);
+       else
+               for (c = 0; c < len;) {
+                       putchar(outbuf[c++]);
+                       if (++n%76 == 0) putchar('\n');
+               }
+}
+
+main(argc,argv)
+int argc;
+char *argv[];
+{
+       unsigned char *inbuf, *outbuf;
+       int c, len = 0, decode = 0;
+       extern int optind;
+
+       while ((c = getopt(argc, argv, "d")) != EOF)
+               switch(c) {
+                       default:
+                               usage(argv[0]);
+                       case 'd':
+                               decode++;
+               }
+
+       switch (argc - optind) {
+               case 0:
+                       inbuf  = (unsigned char *) malloc(5);
+                       outbuf = (unsigned char *) malloc(5);
+                       while ((c = getchar()) != EOF)
+                           if (c != '\n') {
+                               inbuf[len++] = c;
+                               if (len == (decode ? 4 : 3)) {
+                                       inbuf[len] = '\0';
+                                       putbuf(inbuf, outbuf, len, decode);
+                                       len=0;
+                               }
+                           }
+                       if (len) {
+                               inbuf[len] = '\0';
+                               putbuf(inbuf, outbuf, len, decode);
+                       }
+                       break;
+               case 1:
+                       inbuf = (unsigned char *)argv[optind];
+                       len = strlen(inbuf);
+                       outbuf = (unsigned char *)
+                               malloc((len * (decode?3:4)) / (decode?4:3) + 1);
+                       putbuf(inbuf, outbuf, len, decode);
+                       break;
+               default:
+                       fprintf(stderr, "Only one argument allowed\n");
+                       usage(argv[0]);
+       }
+       if (n%76) putchar('\n');
+       exit(0);
+}
+#endif /* STANDALONE */
diff --git a/src/appl/gssftp/ftp/ruserpass.c b/src/appl/gssftp/ftp/ruserpass.c
new file mode 100644 (file)
index 0000000..5587c6c
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 1985 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[] = "@(#)ruserpass.c        5.3 (Berkeley) 3/1/91";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef POSIX
+#include <stdlib.h>
+#endif
+#include <ctype.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include "ftp_var.h"
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+char   *renvlook(), *getenv();
+static FILE *cfile;
+
+#define        DEFAULT 1
+#define        LOGIN   2
+#define        PASSWD  3
+#define        ACCOUNT 4
+#define MACDEF  5
+#define        ID      10
+#define        MACH    11
+
+static char tokval[100];
+
+static struct toktab {
+       char *tokstr;
+       int tval;
+} toktab[]= {
+       "default",      DEFAULT,
+       "login",        LOGIN,
+       "password",     PASSWD,
+       "passwd",       PASSWD,
+       "account",      ACCOUNT,
+       "machine",      MACH,
+       "macdef",       MACDEF,
+       0,              0
+};
+
+
+static
+token()
+{
+       char *cp;
+       int c;
+       struct toktab *t;
+
+       if (feof(cfile))
+               return (0);
+       while ((c = getc(cfile)) != EOF &&
+           (c == '\n' || c == '\t' || c == ' ' || c == ','))
+               continue;
+       if (c == EOF)
+               return (0);
+       cp = tokval;
+       if (c == '"') {
+               while ((c = getc(cfile)) != EOF && c != '"') {
+                       if (c == '\\')
+                               c = getc(cfile);
+                       *cp++ = c;
+               }
+       } else {
+               *cp++ = c;
+               while ((c = getc(cfile)) != EOF
+                   && c != '\n' && c != '\t' && c != ' ' && c != ',') {
+                       if (c == '\\')
+                               c = getc(cfile);
+                       *cp++ = c;
+               }
+       }
+       *cp = 0;
+       if (tokval[0] == 0)
+               return (0);
+       for (t = toktab; t->tokstr; t++)
+               if (!strcmp(t->tokstr, tokval))
+                       return (t->tval);
+       return (ID);
+}
+
+ruserpass(host, aname, apass, aacct)
+       char *host, **aname, **apass, **aacct;
+{
+       char *hdir, buf[FTP_BUFSIZ], *tmp;
+       char myname[MAXHOSTNAMELEN], *mydomain;
+       int t, i, c, usedefault = 0;
+       struct stat stb;
+
+       hdir = getenv("HOME");
+       if (hdir == NULL)
+               hdir = ".";
+       (void) sprintf(buf, "%s/.netrc", hdir);
+       cfile = fopen(buf, "r");
+       if (cfile == NULL) {
+               if (errno != ENOENT)
+                       perror(buf);
+               return(0);
+       }
+       if (gethostname(myname, sizeof(myname)) < 0)
+               myname[0] = '\0';
+       if ((mydomain = strchr(myname, '.')) == NULL)
+               mydomain = "";
+next:
+       while ((t = token())) switch(t) {
+
+       case DEFAULT:
+               usedefault = 1;
+               /* FALL THROUGH */
+
+       case MACH:
+               if (!usedefault) {
+                       if (token() != ID)
+                               continue;
+                       /*
+                        * Allow match either for user's input host name
+                        * or official hostname.  Also allow match of 
+                        * incompletely-specified host in local domain.
+                        */
+                       if (strcasecmp(host, tokval) == 0)
+                               goto match;
+                       if (strcasecmp(hostname, tokval) == 0)
+                               goto match;
+                       if ((tmp = strchr(hostname, '.')) != NULL &&
+                           strcasecmp(tmp, mydomain) == 0 &&
+                           strncasecmp(hostname, tokval, tmp-hostname) == 0 &&
+                           tokval[tmp - hostname] == '\0')
+                               goto match;
+                       if ((tmp = strchr(host, '.')) != NULL &&
+                           strcasecmp(tmp, mydomain) == 0 &&
+                           strncasecmp(host, tokval, tmp - host) == 0 &&
+                           tokval[tmp - host] == '\0')
+                               goto match;
+                       continue;
+               }
+       match:
+               while ((t = token()) && t != MACH && t != DEFAULT) switch(t) {
+
+               case LOGIN:
+                       if (token())
+                               if (*aname == 0) { 
+                                       *aname = malloc((unsigned) strlen(tokval) + 1);
+                                       (void) strcpy(*aname, tokval);
+                               } else {
+                                       if (strcmp(*aname, tokval))
+                                               goto next;
+                               }
+                       break;
+               case PASSWD:
+                       if (strcmp(*aname, "anonymous") &&
+                           fstat(fileno(cfile), &stb) >= 0 &&
+                           (stb.st_mode & 077) != 0) {
+       fprintf(stderr, "Error - .netrc file not correct mode.\n");
+       fprintf(stderr, "Remove password or correct mode.\n");
+                               goto bad;
+                       }
+                       if (token() && *apass == 0) {
+                               *apass = malloc((unsigned) strlen(tokval) + 1);
+                               (void) strcpy(*apass, tokval);
+                       }
+                       break;
+               case ACCOUNT:
+                       if (fstat(fileno(cfile), &stb) >= 0
+                           && (stb.st_mode & 077) != 0) {
+       fprintf(stderr, "Error - .netrc file not correct mode.\n");
+       fprintf(stderr, "Remove account or correct mode.\n");
+                               goto bad;
+                       }
+                       if (token() && *aacct == 0) {
+                               *aacct = malloc((unsigned) strlen(tokval) + 1);
+                               (void) strcpy(*aacct, tokval);
+                       }
+                       break;
+               case MACDEF:
+                       if (proxy) {
+                               (void) fclose(cfile);
+                               return(0);
+                       }
+                       while ((c=getc(cfile)) != EOF && c == ' ' || c == '\t');
+                       if (c == EOF || c == '\n') {
+                               printf("Missing macdef name argument.\n");
+                               goto bad;
+                       }
+                       if (macnum == 16) {
+                               printf("Limit of 16 macros have already been defined\n");
+                               goto bad;
+                       }
+                       tmp = macros[macnum].mac_name;
+                       *tmp++ = c;
+                       for (i=0; i < 8 && (c=getc(cfile)) != EOF &&
+                           !isspace(c); ++i) {
+                               *tmp++ = c;
+                       }
+                       if (c == EOF) {
+                               printf("Macro definition missing null line terminator.\n");
+                               goto bad;
+                       }
+                       *tmp = '\0';
+                       if (c != '\n') {
+                               while ((c=getc(cfile)) != EOF && c != '\n');
+                       }
+                       if (c == EOF) {
+                               printf("Macro definition missing null line terminator.\n");
+                               goto bad;
+                       }
+                       if (macnum == 0) {
+                               macros[macnum].mac_start = macbuf;
+                       }
+                       else {
+                               macros[macnum].mac_start = macros[macnum-1].mac_end + 1;
+                       }
+                       tmp = macros[macnum].mac_start;
+                       while (tmp != macbuf + 4096) {
+                               if ((c=getc(cfile)) == EOF) {
+                               printf("Macro definition missing null line terminator.\n");
+                                       goto bad;
+                               }
+                               *tmp = c;
+                               if (*tmp == '\n') {
+                                       if (*(tmp-1) == '\0') {
+                                          macros[macnum++].mac_end = tmp - 1;
+                                          break;
+                                       }
+                                       *tmp = '\0';
+                               }
+                               tmp++;
+                       }
+                       if (tmp == macbuf + 4096) {
+                               printf("4K macro buffer exceeded\n");
+                               goto bad;
+                       }
+                       break;
+               default:
+       fprintf(stderr, "Unknown .netrc keyword %s\n", tokval);
+                       break;
+               }
+               goto done;
+       }
+done:
+       (void) fclose(cfile);
+       return(0);
+bad:
+       (void) fclose(cfile);
+       return(-1);
+}
diff --git a/src/appl/gssftp/ftp/secure.c b/src/appl/gssftp/ftp/secure.c
new file mode 100644 (file)
index 0000000..4451339
--- /dev/null
@@ -0,0 +1,382 @@
+/*
+ * Shared routines for client and server for
+ * secure read(), write(), getc(), and putc().
+ * Only one security context, thus only work on one fd at a time!
+ */
+
+#include "secure.h"    /* stuff which is specific to client or server */
+
+#ifdef KERBEROS
+#include <krb.h>
+
+CRED_DECL
+extern KTEXT_ST ticket;
+extern MSG_DAT msg_data;
+extern Key_schedule schedule;
+#endif /* KERBEROS */
+#ifdef GSSAPI
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_generic.h>
+extern gss_ctx_id_t gcontext;
+#endif /* GSSAPI */
+
+#include <arpa/ftp.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <errno.h>
+extern int errno;
+extern char *sys_errlist[];
+
+extern struct  sockaddr_in hisaddr;
+extern struct  sockaddr_in myaddr;
+extern int     level;
+extern char    *auth_type;
+
+#define MAX maxbuf
+extern unsigned int maxbuf;    /* maximum output buffer size */
+extern unsigned char *ucbuf;   /* cleartext buffer */
+static unsigned int nout, bufp;        /* number of chars in ucbuf,
+                                * pointer into ucbuf */
+
+#ifdef KERBEROS
+#define FUDGE_FACTOR 32                /* Amount of growth
+                                * from cleartext to ciphertext.
+                                * krb_mk_priv adds this # bytes.
+                                * Must be defined for each auth type.
+                                */
+#endif /* KERBEROS */
+
+#ifndef FUDGE_FACTOR           /* In case no auth types define it. */
+#define FUDGE_FACTOR 0
+#endif
+
+#ifdef KERBEROS
+/* XXX - The following must be redefined if KERBEROS_V4 is not used
+ * but some other auth type is.  They must have the same properties. */
+#define looping_write krb_net_write
+#define looping_read krb_net_read
+#endif
+
+/* perhaps use these in general, certainly use them for GSSAPI */
+
+#ifndef looping_write
+static int
+looping_write(fd, buf, len)
+    int fd;
+    register const char *buf;
+    int len;
+{
+    int cc;
+    register int wrlen = len;
+    do {
+       cc = write(fd, buf, wrlen);
+       if (cc < 0) {
+           if (errno == EINTR)
+               continue;
+           return(cc);
+       }
+       else {
+           buf += cc;
+           wrlen -= cc;
+       }
+    } while (wrlen > 0);
+    return(len);
+}
+#endif
+#ifndef looping_read
+static int
+looping_read(fd, buf, len)
+    int fd;
+    register char *buf;
+    register int len;
+{
+    int cc, len2 = 0;
+
+    do {
+       cc = read(fd, buf, len);
+       if (cc < 0) {
+           if (errno == EINTR)
+               continue;
+           return(cc);          /* errno is already set */
+       }               
+       else if (cc == 0) {
+           return(len2);
+       } else {
+           buf += cc;
+           len2 += cc;
+           len -= cc;
+       }
+    } while (len > 0);
+    return(len2);
+}
+#endif
+
+
+#if defined(STDARG) || (defined(__STDC__) && ! defined(VARARGS))
+extern secure_error(char *, ...);
+#else
+extern secure_error();
+#endif
+
+#define ERR    -2
+
+static
+secure_putbyte(fd, c)
+int fd;
+unsigned char c;
+{
+       int ret;
+
+       ucbuf[nout++] = c;
+       if (nout == MAX - FUDGE_FACTOR)
+               if (ret = secure_putbuf(fd, ucbuf, nout))
+                       return(ret);
+               else    nout = 0;
+       return(c);
+}
+
+/* returns:
+ *      0  on success
+ *     -1  on error (errno set)
+ *     -2  on security error
+ */
+secure_flush(fd)
+int fd;
+{
+       int ret;
+
+       if (level == PROT_C)
+               return(0);
+       if (nout)
+               if (ret = secure_putbuf(fd, ucbuf, nout))
+                       return(ret);
+       return(secure_putbuf(fd, "", nout = 0));
+}
+
+/* returns:
+ *     c>=0  on success
+ *     -1    on error
+ *     -2    on security error
+ */
+secure_putc(c, stream)
+char c;
+FILE *stream;
+{
+       if (level == PROT_C)
+               return(putc(c,stream));
+       return(secure_putbyte(fileno(stream), (unsigned char) c));
+}
+
+/* returns:
+ *     nbyte on success
+ *     -1  on error (errno set)
+ *     -2  on security error
+ */
+secure_write(fd, buf, nbyte)
+int fd;
+unsigned char *buf;
+unsigned int nbyte;
+{
+       unsigned int i;
+       int c;
+
+       if (level == PROT_C)
+               return(write(fd,buf,nbyte));
+       for (i=0; nbyte>0; nbyte--)
+               if ((c = secure_putbyte(fd, buf[i++])) < 0)
+                       return(c);
+       return(i);
+}
+
+/* returns:
+ *      0  on success
+ *     -1  on error (errno set)
+ *     -2  on security error
+ */
+secure_putbuf(fd, buf, nbyte)
+int fd;
+unsigned char *buf;
+unsigned int nbyte;
+{
+       static char *outbuf;            /* output ciphertext */
+       static unsigned int bufsize;    /* size of outbuf */
+       long length;
+       u_long net_len;
+
+       if (bufsize < nbyte + FUDGE_FACTOR) {
+               if (outbuf) (void) free(outbuf);
+               if (outbuf = malloc((unsigned) (nbyte + FUDGE_FACTOR)))
+                       bufsize = nbyte + FUDGE_FACTOR;
+               else {
+                       bufsize = 0;
+                       secure_error("%s (in malloc of PROT buffer)",
+                                       sys_errlist[errno]);
+                       return(ERR);
+               }
+       }
+       /* Other auth types go here ... */
+#ifdef KERBEROS
+       if (strcmp(auth_type, "KERBEROS_V4") == 0)
+         if ((length = level == PROT_P ?
+           krb_mk_priv(buf, (unsigned char *) outbuf, nbyte, schedule,
+                       SESSION, &myaddr, &hisaddr)
+         : krb_mk_safe(buf, (unsigned char *) outbuf, nbyte, SESSION,
+                       &myaddr, &hisaddr)) == -1) {
+               secure_error("krb_mk_%s failed for KERBEROS_V4",
+                               level == PROT_P ? "priv" : "safe");
+               return(ERR);
+         }
+#endif /* KERBEROS */
+#ifdef GSSAPI
+       if (strcmp(auth_type, "GSSAPI") == 0) {
+         gss_buffer_desc in_buf, out_buf;
+         OM_uint32 maj_stat, min_stat;
+         int conf_state;
+               
+         in_buf.value = buf;
+         in_buf.length = nbyte;
+         maj_stat = gss_seal(&min_stat, gcontext,
+                             (level == PROT_P), /* confidential */
+                             GSS_C_QOP_DEFAULT,
+                             &in_buf, &conf_state,
+                             &out_buf);
+         if (maj_stat != GSS_S_COMPLETE) {
+           /* generally need to deal */
+           /* ie. should loop, but for now just fail */
+           secure_gss_error(maj_stat, min_stat,
+                            level == PROT_P?
+                            "GSSAPI seal failed":
+                            "GSSAPI sign failed");
+           return(ERR);
+         }
+         memcpy(outbuf, out_buf.value, length=out_buf.length);
+         gss_release_buffer(&min_stat, &out_buf);
+
+       }
+#endif /* GSSAPI */
+       net_len = htonl((u_long) length);
+       if (looping_write(fd, &net_len, sizeof(net_len)) == -1) return(-1);
+       if (looping_write(fd, outbuf, length) != length) return(-1);
+       return(0);
+}
+
+static
+secure_getbyte(fd)
+int fd;
+{
+       /* number of chars in ucbuf, pointer into ucbuf */
+       static unsigned int nin, bufp;
+       int kerror;
+       u_long length;
+
+       if (nin == 0) {
+               if ((kerror = looping_read(fd, &length, sizeof(length)))
+                               != sizeof(length)) {
+                       secure_error("Couldn't read PROT buffer length: %d/%s",
+                                    kerror,
+                                    kerror == -1 ? sys_errlist[errno]
+                                    : "premature EOF");
+                       return(ERR);
+               }
+               if ((length = (u_long) ntohl(length)) > MAX) {
+                       secure_error("Length (%d) of PROT buffer > PBSZ=%u", 
+                                    length, MAX);
+                       return(ERR);
+               }
+               if ((kerror = looping_read(fd, ucbuf, length)) != length) {
+                       secure_error("Couldn't read %u byte PROT buffer: %s",
+                                       length, kerror == -1 ?
+                                       sys_errlist[errno] : "premature EOF");
+                       return(ERR);
+               }
+               /* Other auth types go here ... */
+#ifdef KERBEROS
+               if (strcmp(auth_type, "KERBEROS_V4") == 0) {
+                 if (kerror = level == PROT_P ?
+                   krb_rd_priv(ucbuf, length, schedule, SESSION,
+                               &hisaddr, &myaddr, &msg_data)
+                 : krb_rd_safe(ucbuf, length, SESSION,
+                               &hisaddr, &myaddr, &msg_data)) {
+                       secure_error("krb_rd_%s failed for KERBEROS_V4 (%s)",
+                                       level == PROT_P ? "priv" : "safe",
+                                       krb_get_err_text(kerror));
+                       return(ERR);
+                 }
+                 memcpy(ucbuf, msg_data.app_data, msg_data.app_length);
+                 nin = bufp = msg_data.app_length;
+               }
+#endif /* KERBEROS */
+#ifdef GSSAPI
+               if (strcmp(auth_type, "GSSAPI") == 0) {
+                 gss_buffer_desc xmit_buf, msg_buf;
+                 OM_uint32 maj_stat, min_stat;
+                 int conf_state;
+
+                 xmit_buf.value = ucbuf;
+                 xmit_buf.length = length;
+                 conf_state = (level == PROT_P);
+                 /* decrypt/verify the message */
+                 maj_stat = gss_unseal(&min_stat, gcontext, &xmit_buf,
+                                       &msg_buf, &conf_state, NULL);
+                 if (maj_stat != GSS_S_COMPLETE) {
+                   secure_gss_error(maj_stat, min_stat, 
+                                    (level == PROT_P)?
+                                    "failed unsealing ENC message":
+                                    "failed unsealing MIC message");
+                   return ERR;
+                 }
+
+                 memcpy(ucbuf, msg_buf.value, nin = bufp = msg_buf.length);
+                 gss_release_buffer(&min_stat, &msg_buf);
+             }
+#endif /* GSSAPI */
+               /* Other auth types go here ... */
+       }
+       if (nin == 0)
+               return(EOF);
+       else    return(ucbuf[bufp - nin--]);
+}
+
+/* returns:
+ *     c>=0 on success
+ *     -1   on EOF
+ *     -2   on security error
+ */
+secure_getc(stream)
+FILE *stream;
+{
+       if (level == PROT_C)
+               return(getc(stream));
+       return(secure_getbyte(fileno(stream)));
+}
+
+/* returns:
+ *     n>0 on success (n == # of bytes read)
+ *     0   on EOF
+ *     -1  on error (errno set), only for PROT_C
+ *     -2  on security error
+ */
+secure_read(fd, buf, nbyte)
+int fd;
+char *buf;
+int nbyte;
+{
+       static int c;
+       int i;
+
+       if (level == PROT_C)
+               return(read(fd,buf,nbyte));
+       if (c == EOF)
+               return(c = 0);
+       for (i=0; nbyte>0; nbyte--)
+               switch (c = secure_getbyte(fd)) {
+                       case ERR: return(c);
+                       case EOF: if (!i) c = 0;
+                                 return(i);
+                       default:  buf[i++] = c;
+               }
+       return(i);
+}
diff --git a/src/appl/gssftp/ftp/secure.h b/src/appl/gssftp/ftp/secure.h
new file mode 100644 (file)
index 0000000..694beda
--- /dev/null
@@ -0,0 +1,4 @@
+#define CRED_DECL      extern CREDENTIALS cred;
+#define SESSION                &cred.session
+#define myaddr         myctladdr
+#define hisaddr                hisdataaddr
diff --git a/src/appl/gssftp/ftpd/CHANGES b/src/appl/gssftp/ftpd/CHANGES
new file mode 100644 (file)
index 0000000..39c7ebd
--- /dev/null
@@ -0,0 +1,31 @@
+This version of ftpd has been fixed to conform to RFC959.
+
+Unfortunately, this conformance introduces a user visible change.  While
+technically, this is the fault of the client (ftp) instead of the server
+(ftpd), the change will be seen whenever an old ftp client calls a new ftpd
+server.
+
+The problem is that the old ftpd implemented the NLST command by execing
+/bin/ls.  This produced non-conformant output in some cases.  The new
+ftpd no longer executes /bin/ls for the NLST command as it has it's own
+built-in code.
+
+The user visible change in the ftp behavior is caused by the ftp client
+"knowing" that the daemon will exec /bin/ls.  This assumption should not
+have been made.
+
+When the old ftp client is used, one of the options is the "ls" command
+which sends the command NLST to the ftpd server.  The client should really
+be sending the LIST command.  The new ftp client has been corrected to do
+this.
+
+NLST should not normally be used directly by humans.  It is intended to
+interface with commands like mget or mput.
+
+Users who are not able to upgrade their ftp client may obtain the previous
+behavior, by using the command "dir" instead of "ls".
+
+These changes only apply to those sites using code derived from the Berkeley
+software releases (which means almost every UNIX based implementation will
+see this problem).
+
diff --git a/src/appl/gssftp/ftpd/ChangeLog b/src/appl/gssftp/ftpd/ChangeLog
new file mode 100644 (file)
index 0000000..16813ef
--- /dev/null
@@ -0,0 +1,232 @@
+Sun Jan 14 02:58:42 1996  Mark Eichin  <eichin@cygnus.com>
+
+       * ftpd.c (auth_data): call gss_release_cred on the server_creds we
+       acquired, before they leave scope.
+
+Sun Jan 14 02:47:19 1996  Karri Balk - Contractor <kbalk@cup.hp.com>
+
+       * ftpd.c (gss_services): list of gssapi service names to try.
+       (auth_data): loop over gss_services.
+
+Sun Jan 14 01:54:35 1996  Bill Schoofs <wjs@cray.com>
+
+       * Makefile.in (DEFINES): define NOCONFIDENTIAL for future use.
+       * ftpcmd.y (CCC): ditch broken CCC code, return proper 534 code.
+       (PBSZ): return proper code 501 for bad PBSZ argument
+       (PROT): recognize PROT E.
+       (cmdtab): add CCC so as to cleanly reject it.
+       (getline): reject CONF as unsupported (but put in code to
+       potentially recognize it in the future.) Reject protected commands
+       of auth_type isn't yet set.
+       * ftpd.c (setlevel): use 536, not 504, for invalid level, and
+       use levelnames to find the proper name.
+       (user): if kuserok succeeds, respond 232, not 231; if it fails,
+       respond 336 (though 331 might be more appropriate.)
+       (auth): fix spelling error.
+       
+Tue Jan  2 19:19:16 1996  Mark Eichin  <eichin@cygnus.com>
+
+       * ftpd.c: use HAVE_SETEUID and HAVE_SETRESUID to figure out how
+       to emulate seteuid instead of assuming hpux.
+       * configure.in: test for seteuid, setreuid and setresuid.
+
+Fri Oct 20 17:17:19 1995  Mark Eichin  <eichin@cygnus.com>
+
+       * ftpd.c (auth_data): supply correct channel bindings to accept,
+       matching the client changes.
+
+Thu Oct 19 12:22:28 1995  Mark W. Eichin  <eichin@cygnus.com>
+
+       * configure.in: check WITH_DBM_LNAME since we use an_to_ln.
+
+Wed Oct  4 19:26:50 1995  Mark Eichin  <eichin@cygnus.com>
+
+       * ftpd.c (user): use HAVE_GETUSERSHELL.
+       * configure.in: check for getusershell.
+
+Mon Oct  2 16:43:54 1995  Mark Eichin  <eichin@cygnus.com>
+
+       * popen.c (ftpd_popen): malloc all strings, not just globbed ones.
+
+Sun Oct  1 03:31:24 1995  Mark Eichin  <eichin@cygnus.com>
+
+       * ftpd.c (auth_data): acquire credentials (currently fixed for
+       service "host".) Fix loop reply logic. Add debugging syslogs. Set
+       auth_type *after* 235 success reply, so it doesn't get encrypted.
+
+Sun Oct  1 00:58:39 1995  Mark Eichin  <eichin@cygnus.com>
+
+       * Makefile.in: use FTP_BUFSIZ everywhere and make it large for
+       now.
+       * configure.in: check for headers need to build getdtablesize.
+       * ftpd.c (secure_reply): add GSSAPI hooks.
+       (reply_gss_error): better gssapi error reporting.
+       
+
+Sat Sep 30 22:26:25 1995  Mark Eichin  <eichin@cygnus.com>
+
+       * ftpd.c: correct gssapi includes. Fix type of client_name. Use
+       gss_ok instead of kerb_ok for GSSAPI case (to simplify future
+       combined code.) Fix some declarations. Fix arguments to
+       gss_accept_sec_context for type.
+       * ftpcmd.y: correct gssapi includes.
+       
+Sat Sep 30 21:40:30 1995  Mark Eichin  <eichin@cygnus.com>
+
+       * Makefile.in: hook setenv.c and getdtablesize.c from appl/bsd.
+       Use double-colon rules for clean, depend, install.
+       configure.in: check for yacc, SIGTYPE, UTMP, SIGPROCMASK,
+       WAIT_TYPE, getdtablesize, getcwd, setenv (using the test from
+       appl/bsd.)
+       * ftpcmd.y: no conf.h. declare level. Use krb5_sigtype directly.
+       * ftpd.c: use getcwd directly, make -s srvtab KERBEROS specific.
+       (user): return 331 as per draft-8, but suggest 53z.
+       (auth_data): return 535 and 335 and quote draft-8 as to why.
+       (secure_gss_error): generic interface for secure.c functions to
+       call reply_gss_error instead.
+       * logwtmp.c: no conf.h, check NO_UT_HOST.
+       * popen.c: no conf.h, no getdtablesize.
+       (ftpd_pclose): Obey USE_SIGPROCMASK.
+
+Sat Sep 30 16:43:28 1995  Mark Eichin  <eichin@cygnus.com>
+
+       * configure.in, Makefile.in: new files for port to GSSAPI and
+       build within the Kerberos V5 build tree.
+       * ftpcmd.y, ftpd.c, secure.c: GSSAPI authentication changes based
+       on the IETF CAT working group ***DRAFT*** FTP Security
+       specification, draft number 8, appendix I.
+
+
+**** previous change logs from CNS V4 modifications of Steve Lunt's
+     draft-3 ftp daemon, which this is based on. ****
+
+Wed Jul 26 21:03:13 1995  Ken Raeburn  <raeburn@cygnus.com>
+
+       * secure.c: Include string.h.
+
+       * ftpd.c (main): Cast signal() return value to long instead of
+       int; it's more likely to fit.
+
+Thu Feb  2 13:41:24 1995  Ian Lance Taylor  <ian@cygnus.com>
+
+       * ftpcmd.y (NBBY): Explicitly define if __pyrsoft and MIPSEB.
+       (cmd_list): In handling of SYST, undefine BSD if __svr4__ is
+       defined.
+
+       * ftpd.c: Don't try to use IP_TOS if the IP_TOS argument
+       (IPTOS_LOWDELAY, etc.) is not defined.
+
+Wed Jan 18 17:12:22 1995  Ian Lance Taylor  <ian@sanguine.cygnus.com>
+
+       * ftpd.8: Include man1/tmac.doc.
+
+Wed Jan 11 15:29:10 1995  Ian Lance Taylor  <ian@sanguine.cygnus.com>
+
+       * ftpd.c (authenticate): New variable.
+       (main): Handle -a (require authentication) option.
+       (user): If authenticate is set, reply with an error if kuserok
+       fails or if no Kerberos authentication was used.
+       * ftpcmd.y: Use check_login when parsing the PASV command.
+       * ftpd.8: Document new -a option.
+
+Tue Jan  3 01:25:57 1995  Mark Eichin  <eichin@cygnus.com>
+
+       * Makefile.in (clean): explicitly delete ftpcmd.c on clean.
+
+Thu Dec 29 15:17:12 1994  Mark Eichin  <eichin@cygnus.com>
+
+       * ftpcmd.y (rcmd): don't declare atol, since it isn't used here
+       anyhow, and it's a macro under linux.
+       (top level): #define NBBY 8 for linux.
+
+Thu Dec 29 14:51:41 1994  Mark Eichin  <eichin@cygnus.com>
+
+       * ftpd.c (statcmd): don't use NBBY check -- linux doesn't have it,
+       it is in no spec, and if it isn't 8, it won't work anyway. (Use
+       strcat instead of sprintf, while we're at it...)
+
+Tue Dec 27 16:29:24 1994  Ian Lance Taylor  <ian@sanguine.cygnus.com>
+
+       * ftpcmd.y (reply, lreply): Declare if STDARG || (__STDC__ && !
+       VARARGS).
+       * ftpd.c:  If STDARG is defined, or if __STDC__ is defined and
+       VARARGS is not defined, include <stdarg.h>.
+       (secure_error): Use <stdarg.h> routines if STDARG || (__STDC__ &&
+       ! VARARGS).
+       (reply, lreply): Likewise.
+       (setproctitle): Just use one argument.
+       * secure.c (secure_error): Declare if STDARG || (__STDC__ && !
+       VARARGS).
+
+Fri Dec 23 16:25:44 1994  Ian Lance Taylor  <ian@sanguine.cygnus.com>
+
+       * ftpcmd.y (unix): Define if _AIX is defined (AIX compiler does
+       not predefine unix).
+
+Thu Dec 22 15:05:14 1994  Ian Lance Taylor  <ian@sanguine.cygnus.com>
+
+       * ftpd.c (keyfile): New global variable.
+       (main): Move option processing before check of remote socket.  Add
+       new options -p, -r, and -s.  Handle -p by accepting a remote
+       connection.
+       (kpass): Use keyfile variable rather than KEYFILE.  Pass keyfile
+       explicitly to krb_rd_req.
+       (auth_data): Likewise.
+       * ftpd.8: Document new -p, -r, and -s options.
+
+Fri Dec 16 11:06:16 1994  Ian Lance Taylor  <ian@cygnus.com>
+
+       Fixes for HP/UX:
+       * ftpd.c: On HP/UX, define seteuid and setegid as macros which
+       call setresuid and setresgid.
+
+       Fixes for UnixWare:
+       * ftpd.c (main): Use a temporary variable rather than calling
+       htons(ntohs(X)).
+       * ftpcmd.y: Include conf.h.
+       (getline): Cast arguments to Kerberos routines to avoid warnings.
+       (toolong): Declare as type sigtype, and add dummy argument.
+
+       Fixes for SCO:
+       * cmdtab.y: Include <sys/types.h>.
+       * ftpd.c (initgroups): Define on SCO.
+       (main): Don't handle SIGURG if it is not defined.
+       (pass): Don't try to use crypt on SCO; instead, require Kerberos
+       password or anonymous login.
+
+       Fixes for AIX:
+       * ftpcmd.y (index): Don't define.
+       (strpbrk, strcpy): Don't declare.
+       * ftpd.c (index, rindex): Don't define.
+       * logwtmp.c (strncpy): Don't declare.
+       * secure.c: Include <netinet/in.h>.
+
+       Fixes for Ultrix:
+       * ftpd.c (main): Define LOG_NDELAY and LOG_DAEMON as zero if they
+       are not already defined by <syslog.h>.
+
+       Fixes for Irix 4:
+       * ftpd.c (retrieve): Don't refer to st_blksize if NOSTBLKSIZE is
+       defined.
+
+       * ftpcmd.y: Fix yacc code to use %union and %type.
+       (yylex): Assign to fields of yylval, rather than to yylval
+       directly.
+
+       General fixes to make it compile on Solaris: Use sigtype for
+       signal handler return values, including conf.h where needed.  Add
+       a dummy argument to signal handler functions. Replace index,
+       rindex, bzero and bcopy with ANSI C functions.  Cast Kerberos
+       routine arguments to avoid warnings.  Also:
+       * ftpd.c: Don't include <varargs.h>.  If POSIX is defined, include
+       unistd.h, otherwise define getcwd to call getwd.
+       (L_SET, L_INCR): Define if not defined.
+       (pwd): Use getcwd instead of getwd.  If POSIX, change the error
+       handling accordingly.
+       * popen.c (getdtablesize): New function on hpux or __svr4__.
+       (ftpd_pclose): If WAIT_USES_INT, use int instead of union wait.
+
+Thu Dec 15 16:13:44 1994  Ian Lance Taylor  <ian@sanguine.cygnus.com>
+
+       * Initial checkin.  Based on Steve Lunt's ftp program, which was
+        based on BSD code.
diff --git a/src/appl/gssftp/ftpd/Makefile.in b/src/appl/gssftp/ftpd/Makefile.in
new file mode 100644 (file)
index 0000000..edf174a
--- /dev/null
@@ -0,0 +1,69 @@
+#
+# appl/gssftp/ftpd/Makefile.in
+#
+CFLAGS = -DGSSAPI -DFTP_BUFSIZ=10240 $(CCOPTS) $(DEFS) $(LOCALINCLUDE)
+
+SETENVSRC=@SETENVSRC@
+SETENVOBJ=@SETENVOBJ@
+COMERRLIB=$(BUILDTOP)/util/et/libcom_err.a
+
+SRCS   = ftpd.c ftpcmd.y logwtmp.c popen.c vers.c \
+         $(srcdir)../ftp/glob.c \
+         $(srcdir)../ftp/radix.c \
+         $(srcdir)../ftp/secure.c \
+         $(srcdir)../../bsd/getdtablesize.c $(SETENVSRC)
+
+OBJS   = ftpd.o ftpcmd.o glob.o popen.o logwtmp.o vers.o radix.o \
+         secure.o getdtablesize.o $(SETENVOBJ)
+
+KLIB = -lgssapi_krb5 -lkrb5 -lcrypto $(COMERRLIB)
+DEPKLIB = $(TOPLIBD)/gssapi/libgssapi_krb5.a $(TOPLIBD)/libkrb5.a \
+               $(TOPLIBD)/libcrypto.a $(COMERRLIB)
+
+LOCALINCLUDE = -I$(srcdir)/..
+DEFINES = -DGSSAPI -DNOCONFIDENTIAL
+
+all::  ftpd
+
+ftpd:  $(OBJS) $(DEPKLIB)
+       $(LD) $(LDFLAGS) $(LDARGS) -o $@ $(OBJS) $(KLIB) $(LIBS)
+
+clean::
+       $(RM) ftp ftpcmd.c
+
+depend::
+
+install::
+       $(INSTALL_PROGRAM) ftpd $(DESTDIR)$(SERVER_BINDIR)/ftpd
+       $(INSTALL_DATA) $(srcdir)/ftpd.M ${DESTDIR}$(SERVER_MANDIR)/ftp.8
+
+
+ftpcmd.c: $(srcdir)/ftpcmd.y
+       $(RM) ftpcmd.c y.tab.c
+       $(YACC) $(srcdir)/ftpcmd.y
+       $(MV) y.tab.c ftpcmd.c
+
+glob.o: $(srcdir)/../ftp/glob.c
+       $(CC) -c $(CFLAGS) $(srcdir)/../ftp/glob.c
+radix.o: $(srcdir)/../ftp/radix.c
+       $(CC) -c $(CFLAGS) $(srcdir)/../ftp/radix.c
+secure.o: $(srcdir)/../ftp/secure.c
+       $(CC) -c $(CFLAGS) $(srcdir)/../ftp/secure.c
+
+getdtablesize.o: $(srcdir)/../../bsd/getdtablesize.c
+       $(CC) -c $(CFLAGS) $(srcdir)/../../bsd/getdtablesize.c
+
+setenv.o: $(srcdir)/../../bsd/setenv.c
+       $(CC) -c $(CFLAGS) $(srcdir)/../../bsd/setenv.c
+
+
+ftpd.o: $(srcdir)/pathnames.h
+secure.o: $(srcdir)/secure.h
+
+ftpd.o: $(srcdir)/ftpd.c
+ftpcmd.o: ftpcmd.c
+popen.o: $(srcdir)/popen.c
+logwtmp.o: $(srcdir)/logwtmp.c
+vers.o: $(srcdir)/vers.c
+
+# NOPOSTFIX
diff --git a/src/appl/gssftp/ftpd/configure.in b/src/appl/gssftp/ftpd/configure.in
new file mode 100644 (file)
index 0000000..9b4fd1f
--- /dev/null
@@ -0,0 +1,31 @@
+AC_INIT(ftpcmd.y)
+CONFIG_RULES
+AC_CONST
+AC_PROG_INSTALL
+AC_PROG_YACC
+KRB5_SIGTYPE
+CHECK_UTMP
+CHECK_SIGPROCMASK
+CHECK_WAIT_TYPE
+AC_FUNC_VFORK
+AC_CHECK_HEADERS(unistd.h stdlib.h string.h)
+AC_REPLACE_FUNCS(getdtablesize)
+AC_HAVE_FUNCS(getcwd getusershell seteuid setreuid setresuid)
+dnl 
+dnl copied from appl/bsd/configure.in
+AC_MSG_CHECKING([setenv])
+AC_CACHE_VAL(krb5_cv_setenv,
+[AC_TRY_LINK(
+[],[setenv("PATH","/bin",0);],
+krb5_cv_setenv=yes,krb5_cv_setenv=no)])
+AC_MSG_RESULT($krb5_cv_setenv)
+if test $krb5_cv_setenv = no; then
+SETENVSRC='$(srcdir)/../bsd/setenv.c'
+SETENVOBJ=setenv.o
+AC_SUBST([SETENVSRC])
+AC_SUBST([SETENVOBJ])
+fi
+dnl
+WITH_DBM_LNAME
+V5_USE_SHARED_LIB
+V5_AC_OUTPUT_MAKEFILE
diff --git a/src/appl/gssftp/ftpd/ftpcmd.y b/src/appl/gssftp/ftpd/ftpcmd.y
new file mode 100644 (file)
index 0000000..d89b9d7
--- /dev/null
@@ -0,0 +1,1454 @@
+/* -*- fundamental -*-
+ * Copyright (c) 1985, 1988 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.
+ *
+ *     @(#)ftpcmd.y    5.24 (Berkeley) 2/25/91
+ */
+
+/*
+ * Grammar for FTP commands.
+ * See RFC 959.
+ * See Also draft-ietf-cat-ftpsec-08.txt.
+ */
+
+%{
+
+#ifndef lint
+static char sccsid[] = "@(#)ftpcmd.y   5.24 (Berkeley) 2/25/91";
+#endif /* not lint */
+
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/ftp.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <syslog.h>
+#include <time.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+extern char *auth_type;
+
+unsigned int maxbuf, actualbuf;
+unsigned char *ucbuf;
+
+#if defined(STDARG) || (defined(__STDC__) && ! defined(VARARGS))
+extern reply(int, char *, ...);
+extern lreply(int, char *, ...);
+#endif
+
+static int kerror;     /* XXX needed for all auth types */
+#ifdef KERBEROS
+extern struct sockaddr_in his_addr, ctrl_addr;
+#include <krb.h>
+extern AUTH_DAT kdata;
+extern Key_schedule schedule;
+extern MSG_DAT msg_data;
+#endif /* KERBEROS */
+#ifdef GSSAPI
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_generic.h>
+extern gss_ctx_id_t gcontext;
+#endif
+
+#ifndef unix
+#ifdef _AIX
+#define unix
+#endif
+#endif
+
+#ifndef NBBY
+#ifdef linux
+#define NBBY 8
+#endif
+#ifdef __pyrsoft
+#ifdef MIPSEB
+#define NBBY 8
+#endif
+#endif
+#endif
+
+extern struct sockaddr_in data_dest;
+extern int logged_in;
+extern struct passwd *pw;
+extern int guest;
+extern int logging;
+extern int type;
+extern int form;
+extern int debug;
+extern int timeout;
+extern int maxtimeout;
+extern  int pdata;
+extern char hostname[], remotehost[];
+extern char proctitle[];
+extern char *globerr;
+extern int usedefault;
+extern  int transflag;
+extern  char tmpline[];
+char   **ftpglob();
+
+off_t  restart_point;
+
+static int cmd_type;
+static int cmd_form;
+static int cmd_bytesz;
+char   cbuf[FTP_BUFSIZ]; /* was 512 */
+char   *fromname;
+
+/* bison needs these decls up front */
+extern jmp_buf errcatch;
+
+#define        CMD     0       /* beginning of command */
+#define        ARGS    1       /* expect miscellaneous arguments */
+#define        STR1    2       /* expect SP followed by STRING */
+#define        STR2    3       /* expect STRING */
+#define        OSTR    4       /* optional SP then STRING */
+#define        ZSTR1   5       /* SP then optional STRING */
+#define        ZSTR2   6       /* optional STRING after SP */
+#define        SITECMD 7       /* SITE command */
+#define        NSTR    8       /* Number followed by a string */
+
+struct tab {
+       char    *name;
+       short   token;
+       short   state;
+       short   implemented;    /* 1 if command is implemented */
+       char    *help;
+};
+struct tab cmdtab[];
+struct tab sitetab[];
+%}
+
+%union { int num; char *str; }
+
+%token
+       A       B       C       E       F       I
+       L       N       P       R       S       T
+
+       SP      CRLF    COMMA   STRING  NUMBER
+
+       USER    PASS    ACCT    REIN    QUIT    PORT
+       PASV    TYPE    STRU    MODE    RETR    STOR
+       APPE    MLFL    MAIL    MSND    MSOM    MSAM
+       MRSQ    MRCP    ALLO    REST    RNFR    RNTO
+       ABOR    DELE    CWD     LIST    NLST    SITE
+       STAT    HELP    NOOP    MKD     RMD     PWD
+       CDUP    STOU    SMNT    SYST    SIZE    MDTM
+       AUTH    ADAT    PROT    PBSZ
+       CCC
+
+       UMASK   IDLE    CHMOD
+
+       LEXERR
+
+%type <num> NUMBER
+%type <num> form_code prot_code struct_code mode_code octal_number
+%type <num> check_login byte_size
+
+%type <str> STRING
+%type <str> password pathname username pathstring
+
+%start cmd_list
+
+%%
+
+cmd_list:      /* empty */
+       |       cmd_list cmd
+               = {
+                       fromname = (char *) 0;
+                       restart_point = (off_t) 0;
+               }
+       |       cmd_list rcmd
+       ;
+
+cmd:           USER SP username CRLF
+               = {
+                       user((char *) $3);
+                       free((char *) $3);
+               }
+       |       PASS SP password CRLF
+               = {
+                       pass((char *) $3);
+                       free((char *) $3);
+               }
+       |       PORT SP host_port CRLF
+               = {
+                       usedefault = 0;
+                       if (pdata >= 0) {
+                               (void) close(pdata);
+                               pdata = -1;
+                       }
+                       reply(200, "PORT command successful.");
+               }
+       |       PASV check_login CRLF
+               = {
+                       if ($2)
+                               passive();
+               }
+       |       PROT SP prot_code CRLF
+               = {
+                   if (maxbuf)
+                       setlevel ($3);
+                   else
+                       reply(503, "Must first set PBSZ");
+               }
+       |       CCC CRLF
+               = {
+                       reply(534, "CCC not supported");
+               }
+       |       PBSZ SP STRING CRLF
+               = {
+                       /* Others may want to do something more fancy here */
+                       if (!auth_type)
+                           reply(503, "Must first perform authentication");
+                       else if (strlen($3) > 10 ||
+                                strlen($3) == 10 && strcmp($3,"4294967296") >= 0)
+                           reply(501, "Bad value for PBSZ: %s", $3);
+                       else if (actualbuf >= (maxbuf =(unsigned int) atol($3)))
+                           reply(200, "PBSZ=%u", actualbuf);
+                       else {
+                           if (ucbuf) (void) free(ucbuf);
+                           actualbuf = (unsigned int) atol($3);
+                           /* I attempt what is asked for first, and if that
+                              fails, I try dividing by 4 */
+                           while ((ucbuf = (unsigned char *)malloc(actualbuf)) == NULL)
+                               if (actualbuf)
+                                   lreply(200, "Trying %u", actualbuf >>= 2);
+                               else {
+                                   perror_reply(421,
+                                       "Local resource failure: malloc");
+                                   dologout(1);
+                               }
+                           reply(200, "PBSZ=%u", maxbuf = actualbuf);
+                       }
+               }
+       |       TYPE SP type_code CRLF
+               = {
+                       switch (cmd_type) {
+
+                       case TYPE_A:
+                               if (cmd_form == FORM_N) {
+                                       reply(200, "Type set to A.");
+                                       type = cmd_type;
+                                       form = cmd_form;
+                               } else
+                                       reply(504, "Form must be N.");
+                               break;
+
+                       case TYPE_E:
+                               reply(504, "Type E not implemented.");
+                               break;
+
+                       case TYPE_I:
+                               reply(200, "Type set to I.");
+                               type = cmd_type;
+                               break;
+
+                       case TYPE_L:
+#if NBBY == 8
+                               if (cmd_bytesz == 8) {
+                                       reply(200,
+                                           "Type set to L (byte size 8).");
+                                       type = cmd_type;
+                               } else
+                                       reply(504, "Byte size must be 8.");
+#else /* NBBY == 8 */
+                               UNIMPLEMENTED for NBBY != 8
+#endif /* NBBY == 8 */
+                       }
+               }
+       |       STRU SP struct_code CRLF
+               = {
+                       switch ($3) {
+
+                       case STRU_F:
+                               reply(200, "STRU F ok.");
+                               break;
+
+                       default:
+                               reply(504, "Unimplemented STRU type.");
+                       }
+               }
+       |       MODE SP mode_code CRLF
+               = {
+                       switch ($3) {
+
+                       case MODE_S:
+                               reply(200, "MODE S ok.");
+                               break;
+
+                       default:
+                               reply(502, "Unimplemented MODE type.");
+                       }
+               }
+       |       ALLO SP NUMBER CRLF
+               = {
+                       reply(202, "ALLO command ignored.");
+               }
+       |       ALLO SP NUMBER SP R SP NUMBER CRLF
+               = {
+                       reply(202, "ALLO command ignored.");
+               }
+       |       RETR check_login SP pathname CRLF
+               = {
+                       if ($2 && $4 != NULL)
+                               retrieve((char *) 0, (char *) $4);
+                       if ($4 != NULL)
+                               free((char *) $4);
+               }
+       |       STOR check_login SP pathname CRLF
+               = {
+                       if ($2 && $4 != NULL)
+                               store((char *) $4, "w", 0);
+                       if ($4 != NULL)
+                               free((char *) $4);
+               }
+       |       APPE check_login SP pathname CRLF
+               = {
+                       if ($2 && $4 != NULL)
+                               store((char *) $4, "a", 0);
+                       if ($4 != NULL)
+                               free((char *) $4);
+               }
+       |       NLST check_login CRLF
+               = {
+                       if ($2)
+                               send_file_list(".");
+               }
+       |       NLST check_login SP STRING CRLF
+               = {
+                       if ($2 && $4 != NULL) 
+                               send_file_list((char *) $4);
+                       if ($4 != NULL)
+                               free((char *) $4);
+               }
+       |       LIST check_login CRLF
+               = {
+                       if ($2)
+                               retrieve("/bin/ls -lgA", "");
+               }
+       |       LIST check_login SP pathname CRLF
+               = {
+                       if ($2 && $4 != NULL)
+                               retrieve("/bin/ls -lgA %s", (char *) $4);
+                       if ($4 != NULL)
+                               free((char *) $4);
+               }
+       |       STAT check_login SP pathname CRLF
+               = {
+                       if ($2 && $4 != NULL)
+                               statfilecmd((char *) $4);
+                       if ($4 != NULL)
+                               free((char *) $4);
+               }
+       |       STAT CRLF
+               = {
+                       statcmd();
+               }
+       |       DELE check_login SP pathname CRLF
+               = {
+                       if ($2 && $4 != NULL)
+                               delete((char *) $4);
+                       if ($4 != NULL)
+                               free((char *) $4);
+               }
+       |       RNTO SP pathname CRLF
+               = {
+                       if (fromname) {
+                               renamecmd(fromname, (char *) $3);
+                               free(fromname);
+                               fromname = (char *) 0;
+                       } else {
+                               reply(503, "Bad sequence of commands.");
+                       }
+                       free((char *) $3);
+               }
+       |       ABOR CRLF
+               = {
+                       reply(225, "ABOR command successful.");
+               }
+       |       CWD check_login CRLF
+               = {
+                       if ($2)
+                               cwd(pw->pw_dir);
+               }
+       |       CWD check_login SP pathname CRLF
+               = {
+                       if ($2 && $4 != NULL)
+                               cwd((char *) $4);
+                       if ($4 != NULL)
+                               free((char *) $4);
+               }
+       |       HELP CRLF
+               = {
+                       help(cmdtab, (char *) 0);
+               }
+       |       HELP SP STRING CRLF
+               = {
+                       register char *cp = (char *)$3;
+
+                       if (strncasecmp(cp, "SITE", 4) == 0) {
+                               cp = (char *)$3 + 4;
+                               if (*cp == ' ')
+                                       cp++;
+                               if (*cp)
+                                       help(sitetab, cp);
+                               else
+                                       help(sitetab, (char *) 0);
+                       } else
+                               help(cmdtab, (char *) $3);
+               }
+       |       NOOP CRLF
+               = {
+                       reply(200, "NOOP command successful.");
+               }
+       |       MKD check_login SP pathname CRLF
+               = {
+                       if ($2 && $4 != NULL)
+                               makedir((char *) $4);
+                       if ($4 != NULL)
+                               free((char *) $4);
+               }
+       |       RMD check_login SP pathname CRLF
+               = {
+                       if ($2 && $4 != NULL)
+                               removedir((char *) $4);
+                       if ($4 != NULL)
+                               free((char *) $4);
+               }
+       |       PWD check_login CRLF
+               = {
+                       if ($2)
+                               pwd();
+               }
+       |       CDUP check_login CRLF
+               = {
+                       if ($2)
+                               cwd("..");
+               }
+       |       SITE SP HELP CRLF
+               = {
+                       help(sitetab, (char *) 0);
+               }
+       |       SITE SP HELP SP STRING CRLF
+               = {
+                       help(sitetab, (char *) $5);
+               }
+       |       SITE SP UMASK check_login CRLF
+               = {
+                       int oldmask;
+
+                       if ($4) {
+                               oldmask = umask(0);
+                               (void) umask(oldmask);
+                               reply(200, "Current UMASK is %03o", oldmask);
+                       }
+               }
+       |       SITE SP UMASK check_login SP octal_number CRLF
+               = {
+                       int oldmask;
+
+                       if ($4) {
+                               if (($6 == -1) || ($6 > 0777)) {
+                                       reply(501, "Bad UMASK value");
+                               } else {
+                                       oldmask = umask($6);
+                                       reply(200,
+                                           "UMASK set to %03o (was %03o)",
+                                           $6, oldmask);
+                               }
+                       }
+               }
+       |       SITE SP CHMOD check_login SP octal_number SP pathname CRLF
+               = {
+                       if ($4 && ($8 != NULL)) {
+                               if ($6 > 0777)
+                                       reply(501,
+                               "CHMOD: Mode value must be between 0 and 0777");
+                               else if (chmod((char *) $8, $6) < 0)
+                                       perror_reply(550, (char *) $8);
+                               else
+                                       reply(200, "CHMOD command successful.");
+                       }
+                       if ($8 != NULL)
+                               free((char *) $8);
+               }
+       |       SITE SP IDLE CRLF
+               = {
+                       reply(200,
+                           "Current IDLE time limit is %d seconds; max %d",
+                               timeout, maxtimeout);
+               }
+       |       SITE SP IDLE SP NUMBER CRLF
+               = {
+                       if ($5 < 30 || $5 > maxtimeout) {
+                               reply(501,
+                       "Maximum IDLE time must be between 30 and %d seconds",
+                                   maxtimeout);
+                       } else {
+                               timeout = $5;
+                               (void) alarm((unsigned) timeout);
+                               reply(200,
+                                   "Maximum IDLE time set to %d seconds",
+                                   timeout);
+                       }
+               }
+       |       STOU check_login SP pathname CRLF
+               = {
+                       if ($2 && $4 != NULL)
+                               store((char *) $4, "w", 1);
+                       if ($4 != NULL)
+                               free((char *) $4);
+               }
+       |       SYST CRLF
+               = {
+#ifdef unix
+#ifdef __svr4__
+#undef BSD
+#endif
+#ifdef BSD
+                       reply(215, "UNIX Type: L%d Version: BSD-%d",
+                               NBBY, BSD);
+#else /* BSD */
+                       reply(215, "UNIX Type: L%d", NBBY);
+#endif /* BSD */
+#else /* unix */
+                       reply(215, "UNKNOWN Type: L%d", NBBY);
+#endif /* unix */
+               }
+
+               /*
+                * SIZE is not in RFC959, but Postel has blessed it and
+                * it will be in the updated RFC.
+                *
+                * Return size of file in a format suitable for
+                * using with RESTART (we just count bytes).
+                */
+       |       SIZE check_login SP pathname CRLF
+               = {
+                       if ($2 && $4 != NULL)
+                               sizecmd((char *) $4);
+                       if ($4 != NULL)
+                               free((char *) $4);
+               }
+
+               /*
+                * MDTM is not in RFC959, but Postel has blessed it and
+                * it will be in the updated RFC.
+                *
+                * Return modification time of file as an ISO 3307
+                * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
+                * where xxx is the fractional second (of any precision,
+                * not necessarily 3 digits)
+                */
+       |       MDTM check_login SP pathname CRLF
+               = {
+                       if ($2 && $4 != NULL) {
+                               struct stat stbuf;
+                               if (stat((char *) $4, &stbuf) < 0)
+                                       perror_reply(550, "%s", (char *) $4);
+                               else if ((stbuf.st_mode&S_IFMT) != S_IFREG) {
+                                       reply(550, "%s: not a plain file.",
+                                               (char *) $4);
+                               } else {
+                                       register struct tm *t;
+                                       struct tm *gmtime();
+                                       t = gmtime(&stbuf.st_mtime);
+                                       reply(213,
+                                           "19%02d%02d%02d%02d%02d%02d",
+                                           t->tm_year, t->tm_mon+1, t->tm_mday,
+                                           t->tm_hour, t->tm_min, t->tm_sec);
+                               }
+                       }
+                       if ($4 != NULL)
+                               free((char *) $4);
+               }
+       |       AUTH SP STRING CRLF
+               = {
+                       auth((char *) $3);
+               }
+       |       ADAT SP STRING CRLF
+               = {
+                       auth_data((char *) $3);
+                       free((char *) $3);
+               }
+       |       QUIT CRLF
+               = {
+                       reply(221, "Goodbye.");
+                       dologout(0);
+               }
+       |       error CRLF
+               = {
+                       yyerrok;
+               }
+       ;
+rcmd:          RNFR check_login SP pathname CRLF
+               = {
+                       char *renamefrom();
+
+                       restart_point = (off_t) 0;
+                       if ($2 && $4) {
+                               fromname = renamefrom((char *) $4);
+                               if (fromname == (char *) 0 && $4) {
+                                       free((char *) $4);
+                               }
+                       }
+               }
+       |       REST SP byte_size CRLF
+               = {
+                       fromname = (char *) 0;
+                       restart_point = $3;
+                       reply(350, "Restarting at %ld. %s", restart_point,
+                           "Send STORE or RETRIEVE to initiate transfer.");
+               }
+       ;
+               
+username:      STRING
+       ;
+
+password:      /* empty */
+               = {
+                       *(char **)&($$) = (char *)calloc(1, sizeof(char));
+               }
+       |       STRING
+       ;
+
+byte_size:     NUMBER
+       ;
+
+host_port:     NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 
+               NUMBER COMMA NUMBER
+               = {
+                       register char *a, *p;
+
+                       a = (char *)&data_dest.sin_addr;
+                       a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
+                       p = (char *)&data_dest.sin_port;
+                       p[0] = $9; p[1] = $11;
+                       data_dest.sin_family = AF_INET;
+               }
+       ;
+
+form_code:     N
+       = {
+               $$ = FORM_N;
+       }
+       |       T
+       = {
+               $$ = FORM_T;
+       }
+       |       C
+       = {
+               $$ = FORM_C;
+       }
+       ;
+
+prot_code:     C
+       = {
+               $$ = PROT_C;
+       }
+       |       S
+       = {
+               $$ = PROT_S;
+       }
+       |       P
+       = {
+               $$ = PROT_P;
+       }
+       |       E
+       = {
+               $$ = PROT_E;
+       }
+       ;
+
+type_code:     A
+       = {
+               cmd_type = TYPE_A;
+               cmd_form = FORM_N;
+       }
+       |       A SP form_code
+       = {
+               cmd_type = TYPE_A;
+               cmd_form = $3;
+       }
+       |       E
+       = {
+               cmd_type = TYPE_E;
+               cmd_form = FORM_N;
+       }
+       |       E SP form_code
+       = {
+               cmd_type = TYPE_E;
+               cmd_form = $3;
+       }
+       |       I
+       = {
+               cmd_type = TYPE_I;
+       }
+       |       L
+       = {
+               cmd_type = TYPE_L;
+               cmd_bytesz = NBBY;
+       }
+       |       L SP byte_size
+       = {
+               cmd_type = TYPE_L;
+               cmd_bytesz = $3;
+       }
+       /* this is for a bug in the BBN ftp */
+       |       L byte_size
+       = {
+               cmd_type = TYPE_L;
+               cmd_bytesz = $2;
+       }
+       ;
+
+struct_code:   F
+       = {
+               $$ = STRU_F;
+       }
+       |       R
+       = {
+               $$ = STRU_R;
+       }
+       |       P
+       = {
+               $$ = STRU_P;
+       }
+       ;
+
+mode_code:     S
+       = {
+               $$ = MODE_S;
+       }
+       |       B
+       = {
+               $$ = MODE_B;
+       }
+       |       C
+       = {
+               $$ = MODE_C;
+       }
+       ;
+
+pathname:      pathstring
+       = {
+               /*
+                * Problem: this production is used for all pathname
+                * processing, but only gives a 550 error reply.
+                * This is a valid reply in some cases but not in others.
+                */
+               if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) {
+                       *(char **)&($$) = *ftpglob((char *) $1);
+                       if (globerr != NULL) {
+                               reply(550, globerr);
+                               $$ = NULL;
+                       }
+                       free((char *) $1);
+               } else
+                       $$ = $1;
+       }
+       ;
+
+pathstring:    STRING
+       ;
+
+octal_number:  NUMBER
+       = {
+               register int ret, dec, multby, digit;
+
+               /*
+                * Convert a number that was read as decimal number
+                * to what it would be if it had been read as octal.
+                */
+               dec = $1;
+               multby = 1;
+               ret = 0;
+               while (dec) {
+                       digit = dec%10;
+                       if (digit > 7) {
+                               ret = -1;
+                               break;
+                       }
+                       ret += digit * multby;
+                       multby *= 8;
+                       dec /= 10;
+               }
+               $$ = ret;
+       }
+       ;
+
+check_login:   /* empty */
+       = {
+               if (logged_in)
+                       $$ = 1;
+               else {
+                       reply(530, "Please login with USER and PASS.");
+                       $$ = 0;
+               }
+       }
+       ;
+
+%%
+
+struct tab cmdtab[] = {                /* In order defined in RFC 765 */
+       { "USER", USER, STR1, 1,        "<sp> username" },
+       { "PASS", PASS, ZSTR1, 1,       "<sp> password" },
+       { "ACCT", ACCT, STR1, 0,        "(specify account)" },
+       { "SMNT", SMNT, ARGS, 0,        "(structure mount)" },
+       { "REIN", REIN, ARGS, 0,        "(reinitialize server state)" },
+       { "QUIT", QUIT, ARGS, 1,        "(terminate service)", },
+       { "PORT", PORT, ARGS, 1,        "<sp> b0, b1, b2, b3, b4" },
+       { "PASV", PASV, ARGS, 1,        "(set server in passive mode)" },
+       { "TYPE", TYPE, ARGS, 1,        "<sp> [ A | E | I | L ]" },
+       { "STRU", STRU, ARGS, 1,        "(specify file structure)" },
+       { "MODE", MODE, ARGS, 1,        "(specify transfer mode)" },
+       { "RETR", RETR, STR1, 1,        "<sp> file-name" },
+       { "STOR", STOR, STR1, 1,        "<sp> file-name" },
+       { "APPE", APPE, STR1, 1,        "<sp> file-name" },
+       { "MLFL", MLFL, OSTR, 0,        "(mail file)" },
+       { "MAIL", MAIL, OSTR, 0,        "(mail to user)" },
+       { "MSND", MSND, OSTR, 0,        "(mail send to terminal)" },
+       { "MSOM", MSOM, OSTR, 0,        "(mail send to terminal or mailbox)" },
+       { "MSAM", MSAM, OSTR, 0,        "(mail send to terminal and mailbox)" },
+       { "MRSQ", MRSQ, OSTR, 0,        "(mail recipient scheme question)" },
+       { "MRCP", MRCP, STR1, 0,        "(mail recipient)" },
+       { "ALLO", ALLO, ARGS, 1,        "allocate storage (vacuously)" },
+       { "REST", REST, ARGS, 1,        "(restart command)" },
+       { "RNFR", RNFR, STR1, 1,        "<sp> file-name" },
+       { "RNTO", RNTO, STR1, 1,        "<sp> file-name" },
+       { "ABOR", ABOR, ARGS, 1,        "(abort operation)" },
+       { "DELE", DELE, STR1, 1,        "<sp> file-name" },
+       { "CWD",  CWD,  OSTR, 1,        "[ <sp> directory-name ]" },
+       { "XCWD", CWD,  OSTR, 1,        "[ <sp> directory-name ]" },
+       { "LIST", LIST, OSTR, 1,        "[ <sp> path-name ]" },
+       { "NLST", NLST, OSTR, 1,        "[ <sp> path-name ]" },
+       { "SITE", SITE, SITECMD, 1,     "site-cmd [ <sp> arguments ]" },
+       { "SYST", SYST, ARGS, 1,        "(get type of operating system)" },
+       { "STAT", STAT, OSTR, 1,        "[ <sp> path-name ]" },
+       { "HELP", HELP, OSTR, 1,        "[ <sp> <string> ]" },
+       { "NOOP", NOOP, ARGS, 1,        "" },
+       { "MKD",  MKD,  STR1, 1,        "<sp> path-name" },
+       { "XMKD", MKD,  STR1, 1,        "<sp> path-name" },
+       { "RMD",  RMD,  STR1, 1,        "<sp> path-name" },
+       { "XRMD", RMD,  STR1, 1,        "<sp> path-name" },
+       { "PWD",  PWD,  ARGS, 1,        "(return current directory)" },
+       { "XPWD", PWD,  ARGS, 1,        "(return current directory)" },
+       { "CDUP", CDUP, ARGS, 1,        "(change to parent directory)" },
+       { "XCUP", CDUP, ARGS, 1,        "(change to parent directory)" },
+       { "STOU", STOU, STR1, 1,        "<sp> file-name" },
+       { "AUTH", AUTH, STR1, 1,        "<sp> auth-type" },
+       { "ADAT", ADAT, STR1, 1,        "<sp> auth-data" },
+       { "PROT", PROT, ARGS, 1,        "<sp> protection-level" },
+       { "PBSZ", PBSZ, STR1, 1,        "<sp> buffer-size" },
+       { "CCC",  CCC,  ARGS, 1,        "(clear command channel)" },
+       { "SIZE", SIZE, OSTR, 1,        "<sp> path-name" },
+       { "MDTM", MDTM, OSTR, 1,        "<sp> path-name" },
+       { NULL,   0,    0,    0,        0 }
+};
+
+struct tab sitetab[] = {
+       { "UMASK", UMASK, ARGS, 1,      "[ <sp> umask ]" },
+       { "IDLE", IDLE, ARGS, 1,        "[ <sp> maximum-idle-time ]" },
+       { "CHMOD", CHMOD, NSTR, 1,      "<sp> mode <sp> file-name" },
+       { "HELP", HELP, OSTR, 1,        "[ <sp> <string> ]" },
+       { NULL,   0,    0,    0,        0 }
+};
+
+struct tab *
+lookup(p, cmd)
+       register struct tab *p;
+       char *cmd;
+{
+
+       for (; p->name != NULL; p++)
+               if (strcmp(cmd, p->name) == 0)
+                       return (p);
+       return (0);
+}
+
+#include <arpa/telnet.h>
+
+/*
+ * getline - a hacked up version of fgets to ignore TELNET escape codes.
+ */
+char *
+getline(s, n, iop)
+       char *s;
+       register FILE *iop;
+{
+       register c;
+       register char *cs;
+
+       cs = s;
+/* tmpline may contain saved command from urgent mode interruption */
+       for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
+               *cs++ = tmpline[c];
+               if (tmpline[c] == '\n') {
+                       *cs++ = '\0';
+                       if (debug)
+                               syslog(LOG_DEBUG, "command: %s", s);
+                       tmpline[0] = '\0';
+                       return(s);
+               }
+               if (c == 0)
+                       tmpline[0] = '\0';
+       }
+       while ((c = getc(iop)) != EOF) {
+               c &= 0377;
+               if (c == IAC) {
+                   if ((c = getc(iop)) != EOF) {
+                       c &= 0377;
+                       switch (c) {
+                       case WILL:
+                       case WONT:
+                               c = getc(iop);
+                               printf("%c%c%c", IAC, DONT, 0377&c);
+                               (void) fflush(stdout);
+                               continue;
+                       case DO:
+                       case DONT:
+                               c = getc(iop);
+                               printf("%c%c%c", IAC, WONT, 0377&c);
+                               (void) fflush(stdout);
+                               continue;
+                       case IAC:
+                               break;
+                       default:
+                               continue;       /* ignore command */
+                       }
+                   }
+               }
+               *cs++ = c;
+               if (--n <= 0 || c == '\n')
+                       break;
+       }
+       if (c == EOF && cs == s)
+               return (NULL);
+       *cs++ = '\0';
+       if (auth_type) {
+           char out[sizeof(cbuf)], *cp;
+           int len, mic;
+
+           if ((cs = strpbrk(s, " \r\n")))
+               *cs++ = '\0';
+           upper(s);
+#ifdef NOCONFIDENTIAL
+           if (!strcmp(s, "CONF")) {
+               reply(537, "CONF protected commands not supported.");
+               *s = '\0';
+               return(s);
+           }
+#endif
+           if ((mic = strcmp(s, "ENC")) && strcmp(s, "MIC")
+#ifndef NOCONFIDENTIAL
+               && strcmp(s, "CONF")
+#endif
+                                       ) {
+               reply(533, "All commands must be protected.");
+               syslog(LOG_ERR, "Unprotected command received");
+               *s = '\0';
+               return(s);
+           } else if (debug)
+               syslog(LOG_INFO, "command %s received (mic=%d)", s, mic);
+/* Some paranoid sites may want to require that commands be encrypted. */
+#ifdef PARANOID
+           if (mic) {
+               reply(533, "All commands must be ENC protected.  Retry command under ENC.");
+               *s = '\0';
+               return(s);
+           }
+#endif /* PARANOID */
+#ifdef NOENCRYPTION
+           if (!mic) {
+               reply(533, "ENC protection not supported.  Retry command under MIC.");
+               *s = '\0';
+               return(s);
+           }
+#endif /* NOENCRYPTION */
+           if ((cp = strpbrk(cs, " \r\n")))
+               *cp = '\0';
+           if (kerror = radix_encode(cs, out, &len, 1)) {
+               reply(501, "Can't base 64 decode argument to %s command (%s)",
+                     mic ? "MIC" : "ENC", radix_error(kerror));
+               *s = '\0';
+               return(s);
+           }
+           if (debug) syslog(LOG_DEBUG, "getline got %d from %s <%s>\n", 
+                             len, cs, mic?"MIC":"ENC");
+#ifdef KERBEROS
+           if (strcmp(auth_type, "KERBEROS_V4") == 0) {
+               if ((kerror = mic ?
+                   krb_rd_safe((unsigned char *)out, len, &kdata.session,
+                           &his_addr, &ctrl_addr, &msg_data)
+                 : krb_rd_priv((unsigned char *)out, len, schedule,
+                           &kdata.session, &his_addr, &ctrl_addr, &msg_data))
+                       != KSUCCESS) {
+                   reply(535, "%s! (%s)",
+                          mic ? "MIC command modified" : "ENC command garbled",
+                          krb_get_err_text(kerror));
+                   syslog(LOG_ERR,"%s failed: %s",
+                          mic ? "MIC krb_rd_safe" : "ENC krb_rd_priv",
+                          krb_get_err_text(kerror));
+                   *s = '\0';
+                   return(s);
+               }
+               (void) memcpy(s, msg_data.app_data, msg_data.app_length);
+               (void) strcpy(s+msg_data.app_length, "\r\n");
+           }
+#endif /* KERBEROS */
+#ifdef GSSAPI
+/* we know this is a MIC or ENC already, and out/len already has the bits */
+           if (strcmp(auth_type, "GSSAPI") == 0) {
+               gss_buffer_desc xmit_buf, msg_buf;
+               OM_uint32 maj_stat, min_stat;
+               int conf_state;
+
+               xmit_buf.value = out;
+               xmit_buf.length = len;
+               /* decrypt the message */
+               conf_state = !mic;
+               maj_stat = gss_unseal(&min_stat, gcontext, &xmit_buf,
+                                     &msg_buf, &conf_state, NULL);
+               if (maj_stat == GSS_S_CONTINUE_NEEDED) {
+                       if (debug) syslog(LOG_DEBUG, "%s-unseal continued", 
+                                         mic?"MIC":"ENC");
+                       reply(535, "%s-unseal continued, oops",
+                             mic?"MIC":"ENC");
+                       *s = 0; return s;
+               }
+               if (maj_stat != GSS_S_COMPLETE) {
+                       reply_gss_error(535, maj_stat, min_stat, 
+                                       mic? "failed unsealing MIC message":
+                                       "failed unsealing ENC message");
+                       *s = 0;
+                       return s;
+               }
+
+               memcpy(s, msg_buf.value, msg_buf.length);
+               strcpy(s+msg_buf.length-(s[msg_buf.length-1]?0:1), "\r\n");
+               gss_release_buffer(&min_stat, &msg_buf);
+           }
+#endif /* GSSAPI */
+           /* Other auth types go here ... */
+       }
+#if defined KERBEROS || defined GSSAPI /* or other auth types */
+       else {  /* !auth_type */
+           if ( (!(strncmp(s, "ENC", 3))) || (!(strncmp(s, "MIC", 3)))
+#ifndef NOCONFIDENTIAL
+                || (!(strncmp(s, "CONF", 4)))
+#endif
+                                        ) {
+                reply(503, "Must perform authentication before sending protected commands");
+                *s = '\0';
+                return(s);
+           }
+       }
+#endif /* KERBEROS */
+
+       if (debug)
+               syslog(LOG_DEBUG, "command: <%s>(%d)", s, strlen(s));
+       return (s);
+}
+
+static krb5_sigtype
+toolong(sig)
+       int sig;
+{
+       time_t now;
+
+       reply(421,
+         "Timeout (%d seconds): closing control connection.", timeout);
+       (void) time(&now);
+       if (logging) {
+               syslog(LOG_INFO,
+                       "User %s timed out after %d seconds at %s",
+                       (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now));
+       }
+       dologout(1);
+}
+
+yylex()
+{
+       static int cpos, state;
+       register char *cp, *cp2;
+       register struct tab *p;
+       int n;
+       char c, *copy();
+
+       for (;;) {
+               switch (state) {
+
+               case CMD:
+                       (void) signal(SIGALRM, toolong);
+                       (void) alarm((unsigned) timeout);
+                       if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
+                               reply(221, "You could at least say goodbye.");
+                               dologout(0);
+                       }
+                       (void) alarm(0);
+#ifdef SETPROCTITLE
+                       if (strncasecmp(cbuf, "PASS", 4) != NULL)
+                               setproctitle("%s: %s", proctitle, cbuf);
+#endif /* SETPROCTITLE */
+                       if ((cp = strchr(cbuf, '\r'))) {
+                               *cp++ = '\n';
+                               *cp = '\0';
+                       }
+                       if ((cp = strpbrk(cbuf, " \n")))
+                               cpos = cp - cbuf;
+                       if (cpos == 0)
+                               cpos = 4;
+                       c = cbuf[cpos];
+                       cbuf[cpos] = '\0';
+                       upper(cbuf);
+                       p = lookup(cmdtab, cbuf);
+                       cbuf[cpos] = c;
+                       if (p != 0) {
+                               if (p->implemented == 0) {
+                                       nack(p->name);
+                                       longjmp(errcatch,0);
+                                       /* NOTREACHED */
+                               }
+                               state = p->state;
+                               yylval.str = p->name;
+                               return (p->token);
+                       }
+                       break;
+
+               case SITECMD:
+                       if (cbuf[cpos] == ' ') {
+                               cpos++;
+                               return (SP);
+                       }
+                       cp = &cbuf[cpos];
+                       if ((cp2 = strpbrk(cp, " \n")))
+                               cpos = cp2 - cbuf;
+                       c = cbuf[cpos];
+                       cbuf[cpos] = '\0';
+                       upper(cp);
+                       p = lookup(sitetab, cp);
+                       cbuf[cpos] = c;
+                       if (p != 0) {
+                               if (p->implemented == 0) {
+                                       state = CMD;
+                                       nack(p->name);
+                                       longjmp(errcatch,0);
+                                       /* NOTREACHED */
+                               }
+                               state = p->state;
+                               yylval.str = p->name;
+                               return (p->token);
+                       }
+                       state = CMD;
+                       break;
+
+               case OSTR:
+                       if (cbuf[cpos] == '\n') {
+                               state = CMD;
+                               return (CRLF);
+                       }
+                       /* FALLTHROUGH */
+
+               case STR1:
+               case ZSTR1:
+               dostr1:
+                       if (cbuf[cpos] == ' ') {
+                               cpos++;
+                               state = state == OSTR ? STR2 : ++state;
+                               return (SP);
+                       }
+                       break;
+
+               case ZSTR2:
+                       if (cbuf[cpos] == '\n') {
+                               state = CMD;
+                               return (CRLF);
+                       }
+                       /* FALLTHROUGH */
+
+               case STR2:
+                       cp = &cbuf[cpos];
+                       n = strlen(cp);
+                       cpos += n - 1;
+                       /*
+                        * Make sure the string is nonempty and \n terminated.
+                        */
+                       if (n > 1 && cbuf[cpos] == '\n') {
+                               cbuf[cpos] = '\0';
+                               yylval.str = copy(cp);
+                               cbuf[cpos] = '\n';
+                               state = ARGS;
+                               return (STRING);
+                       }
+                       break;
+
+               case NSTR:
+                       if (cbuf[cpos] == ' ') {
+                               cpos++;
+                               return (SP);
+                       }
+                       if (isdigit(cbuf[cpos])) {
+                               cp = &cbuf[cpos];
+                               while (isdigit(cbuf[++cpos]))
+                                       ;
+                               c = cbuf[cpos];
+                               cbuf[cpos] = '\0';
+                               yylval.num = atoi(cp);
+                               cbuf[cpos] = c;
+                               state = STR1;
+                               return (NUMBER);
+                       }
+                       state = STR1;
+                       goto dostr1;
+
+               case ARGS:
+                       if (isdigit(cbuf[cpos])) {
+                               cp = &cbuf[cpos];
+                               while (isdigit(cbuf[++cpos]))
+                                       ;
+                               c = cbuf[cpos];
+                               cbuf[cpos] = '\0';
+                               yylval.num = atoi(cp);
+                               cbuf[cpos] = c;
+                               return (NUMBER);
+                       }
+                       switch (cbuf[cpos++]) {
+
+                       case '\n':
+                               state = CMD;
+                               return (CRLF);
+
+                       case ' ':
+                               return (SP);
+
+                       case ',':
+                               return (COMMA);
+
+                       case 'A':
+                       case 'a':
+                               return (A);
+
+                       case 'B':
+                       case 'b':
+                               return (B);
+
+                       case 'C':
+                       case 'c':
+                               return (C);
+
+                       case 'E':
+                       case 'e':
+                               return (E);
+
+                       case 'F':
+                       case 'f':
+                               return (F);
+
+                       case 'I':
+                       case 'i':
+                               return (I);
+
+                       case 'L':
+                       case 'l':
+                               return (L);
+
+                       case 'N':
+                       case 'n':
+                               return (N);
+
+                       case 'P':
+                       case 'p':
+                               return (P);
+
+                       case 'R':
+                       case 'r':
+                               return (R);
+
+                       case 'S':
+                       case 's':
+                               return (S);
+
+                       case 'T':
+                       case 't':
+                               return (T);
+
+                       }
+                       break;
+
+               default:
+                       fatal("Unknown state in scanner.");
+               }
+               yyerror((char *) 0);
+               state = CMD;
+               longjmp(errcatch,0);
+       }
+}
+
+upper(s)
+       register char *s;
+{
+       while (*s != '\0') {
+               if (islower(*s))
+                       *s = toupper(*s);
+               s++;
+       }
+}
+
+char *
+copy(s)
+       char *s;
+{
+       char *p;
+
+       p = malloc((unsigned) strlen(s) + 1);
+       if (p == NULL)
+               fatal("Ran out of memory.");
+       (void) strcpy(p, s);
+       return (p);
+}
+
+help(ctab, s)
+       struct tab *ctab;
+       char *s;
+{
+       register struct tab *c;
+       register int width, NCMDS;
+       char str[80];
+       char *type;
+
+       if (ctab == sitetab)
+               type = "SITE ";
+       else
+               type = "";
+       width = 0, NCMDS = 0;
+       for (c = ctab; c->name != NULL; c++) {
+               int len = strlen(c->name);
+
+               if (len > width)
+                       width = len;
+               NCMDS++;
+       }
+       width = (width + 8) &~ 7;
+       if (s == 0) {
+               register int i, j, w;
+               int columns, lines;
+
+               lreply(214, "The following %scommands are recognized %s.",
+                   type, "(* =>'s unimplemented)");
+               columns = 76 / width;
+               if (columns == 0)
+                       columns = 1;
+               lines = (NCMDS + columns - 1) / columns;
+               for (i = 0; i < lines; i++) {
+                       strcpy(str, "   ");
+                       for (j = 0; j < columns; j++) {
+                               c = ctab + j * lines + i;
+                               sprintf(&str[strlen(str)], "%s%c", c->name,
+                                       c->implemented ? ' ' : '*');
+                               if (c + lines >= &ctab[NCMDS])
+                                       break;
+                               w = strlen(c->name) + 1;
+                               while (w < width) {
+                                       strcat(str, " ");
+                                       w++;
+                               }
+                       }
+                       reply(0, "%s", str);
+               }
+               reply(214, "Direct comments to ftp-bugs@%s.", hostname);
+               return;
+       }
+       upper(s);
+       c = lookup(ctab, s);
+       if (c == (struct tab *)0) {
+               reply(502, "Unknown command %s.", s);
+               return;
+       }
+       if (c->implemented)
+               reply(214, "Syntax: %s%s %s", type, c->name, c->help);
+       else
+               reply(214, "%s%-*s\t%s; unimplemented.", type, width,
+                   c->name, c->help);
+}
+
+sizecmd(filename)
+char *filename;
+{
+       switch (type) {
+       case TYPE_L:
+       case TYPE_I: {
+               struct stat stbuf;
+               if (stat(filename, &stbuf) < 0 ||
+                   (stbuf.st_mode&S_IFMT) != S_IFREG)
+                       reply(550, "%s: not a plain file.", filename);
+               else
+                       reply(213, "%lu", stbuf.st_size);
+               break;}
+       case TYPE_A: {
+               FILE *fin;
+               register int c;
+               register long count;
+               struct stat stbuf;
+               fin = fopen(filename, "r");
+               if (fin == NULL) {
+                       perror_reply(550, filename);
+                       return;
+               }
+               if (fstat(fileno(fin), &stbuf) < 0 ||
+                   (stbuf.st_mode&S_IFMT) != S_IFREG) {
+                       reply(550, "%s: not a plain file.", filename);
+                       (void) fclose(fin);
+                       return;
+               }
+
+               count = 0;
+               while((c=getc(fin)) != EOF) {
+                       if (c == '\n')  /* will get expanded to \r\n */
+                               count++;
+                       count++;
+               }
+               (void) fclose(fin);
+
+               reply(213, "%ld", count);
+               break;}
+       default:
+               reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
+       }
+}
diff --git a/src/appl/gssftp/ftpd/ftpd.M b/src/appl/gssftp/ftpd/ftpd.M
new file mode 100644 (file)
index 0000000..2b4e995
--- /dev/null
@@ -0,0 +1,335 @@
+.\" Copyright (c) 1985, 1988, 1991 The 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.
+.\"
+.\"     @(#)ftpd.8     6.9 (Berkeley) 3/16/91
+.\"
+.so man1/tmac.doc
+.Dd March 16, 1991
+.Dt FTPD 8
+.Os BSD 4.2
+.Sh NAME
+.Nm ftpd
+.Nd
+.Tn DARPA
+Internet File Transfer Protocol server
+.Sh SYNOPSIS
+.Nm ftpd
+.Op Fl d
+.Op Fl l
+.Op Fl t Ar timeout
+.Op Fl T Ar maxtimeout
+.Op Fl p Ar port
+.Op Fl r Ar realm-file
+.Op Fl s Ar srvtab
+.Sh DESCRIPTION
+.Nm Ftpd
+is the
+.Tn DARPA
+Internet File Transfer Protocol
+server process.  The server uses the
+.Tn TCP
+protocol
+and listens at the port specified in the
+.Dq ftp
+service specification; see
+.Xr services 5 .
+.Pp
+Available options:
+.Bl -tag -width Ds
+.It Fl d
+Debugging information is written to the syslog.
+.It Fl l
+Each
+.Xr ftp 1
+session is logged in the syslog.
+.It Fl t
+The inactivity timeout period is set to
+.Ar timeout
+seconds (the default is 15 minutes).
+.It Fl T
+A client may also request a different timeout period;
+the maximum period allowed may be set to
+.Ar timeout
+seconds with the
+.Fl T
+option.
+The default limit is 2 hours.
+.It Fl a
+Only permit Kerberos authenticated or anonymous logins.
+.It Fl p Ar port
+Run as a server and accept a connection on
+.Ar port.
+Normally the ftp server is invoked by
+.Xr inetd 8 .
+.It Fl r Ar realm-file
+Sets the name of the
+.Pa krb.conf
+file to use.  The default value is normally
+.Pa /usr/kerberos/lib/krb.conf.
+.It Fl s Ar srvtab
+Sets the name of the
+.Pa srvtab
+file to use.  The default value is normally
+.Pa /etc/krb-srvtab.
+.El
+.Pp
+The ftp server currently supports the following ftp
+requests; case is not distinguished.
+.Bl -column "Request" -offset indent
+.It Request Ta "Description"
+.It ABOR Ta "abort previous command"
+.It ACCT Ta "specify account (ignored)"
+.It ADAT Ta "send an authentication protocol message"
+.It ALLO Ta "allocate storage (vacuously)"
+.It APPE Ta "append to a file"
+.It AUTH Ta "specify an authentication protocol to be performed"
+.It CDUP Ta "change to parent of current working directory"
+.It CWD Ta "change working directory"
+.It DELE Ta "delete a file"
+.It ENC Ta "send a privacy and integrity protected command (given in argument)"
+.It HELP Ta "give help information"
+.It LIST Ta "give list files in a directory" Pq Dq Li "ls -lgA"
+.It MIC Ta "send an integrity protected command (given in argument)"
+.It MKD Ta "make a directory"
+.It MDTM Ta "show last modification time of file"
+.It MODE Ta "specify data transfer" Em mode
+.It NLST Ta "give name list of files in directory"
+.It NOOP Ta "do nothing"
+.It PASS Ta "specify password"
+.It PASV Ta "prepare for server-to-server transfer"
+.It PBSZ Ta "specify a protection buffer size"
+.It PORT Ta "specify data connection port"
+.It PROT Ta "specify a protection level under which to protect data transfers"
+.It PWD Ta "print the current working directory"
+.It QUIT Ta "terminate session"
+.It REST Ta "restart incomplete transfer"
+.It RETR Ta "retrieve a file"
+.It RMD Ta "remove a directory"
+.It RNFR Ta "specify rename-from file name"
+.It RNTO Ta "specify rename-to file name"
+.It SITE Ta "non-standard commands (see next section)"
+.It SIZE Ta "return size of file"
+.It STAT Ta "return status of server"
+.It STOR Ta "store a file"
+.It STOU Ta "store a file with a unique name"
+.It STRU Ta "specify data transfer" Em structure
+.It SYST Ta "show operating system type of server system"
+.It TYPE Ta "specify data transfer" Em type
+.It USER Ta "specify user name"
+.It XCUP Ta "change to parent of current working directory (deprecated)"
+.It XCWD Ta "change working directory (deprecated)"
+.It XMKD Ta "make a directory (deprecated)"
+.It XPWD Ta "print the current working directory (deprecated)"
+.It XRMD Ta "remove a directory (deprecated)"
+.El
+.Pp
+The following non-standard or
+.Tn UNIX
+specific commands are supported
+by the
+SITE request.
+.Pp
+.Bl -column Request -offset indent
+.It Sy Request Ta Sy Description
+.It UMASK Ta change umask. Em E.g. SITE UMASK 002
+.It IDLE Ta set idle-timer. Em E.g. SITE IDLE 60
+.It CHMOD Ta change mode of a file. Em E.g.
+SITE CHMOD 755 filename
+.It HELP Ta give help information. Em E.g. SITE HELP
+.El
+.Pp
+The remaining ftp requests specified in Internet
+.%T "RFC 959"
+are
+recognized, but not implemented.
+MDTM and SIZE are not specified in
+.%T "RFC 959" ,
+but will appear in the next updated FTP RFC.
+.Pp
+The ftp server will abort an active file transfer only when the
+ABOR
+command is preceded by a Telnet "Interrupt Process" (IP)
+signal and a Telnet "Synch" signal in the command Telnet stream,
+as described in Internet
+.%T "RFC 959" .
+If a
+STAT
+command is received during a data transfer, preceded by a Telnet IP
+and Synch, transfer status will be returned.
+.Pp
+.Nm Ftpd
+interprets file names according to the
+.Dq globbing
+conventions used by
+.Xr csh 1 .
+This allows users to utilize the metacharacters
+.Dq Li \&*?[]{}~ .
+.Pp
+.Nm Ftpd
+authenticates users according to the following rules: 
+.Pp
+.Bl -enum -offset indent
+.It
+The user name must be in the password data base,
+.Pa /etc/passwd .
+.It
+An
+.Dv AUTH
+command must be accepted, the ensuing authentication protocol
+(conducted via
+.Dv ADAT
+commands and replies)
+must successfully complete, and the authenticated user must permitted
+access.  Otherwise, a valid password which is not null
+must be provided by the client.
+.It
+The user name must not appear in the file
+.Pa /etc/ftpusers .
+.It
+The user must have a standard shell returned by 
+.Xr getusershell 3 .
+.It
+If the user name is
+.Dq anonymous
+or
+.Dq ftp ,
+an
+anonymous ftp account must be present in the password
+file (user
+.Dq ftp ) .
+In this case the user is allowed
+to log in by specifying any password (by convention this
+is given as the client host's name).
+.El
+.Pp
+In the last case, 
+.Nm ftpd
+takes special measures to restrict the client's access privileges.
+The server performs a 
+.Xr chroot 2
+command to the home directory of the
+.Dq ftp
+user.
+In order that system security is not breached, it is recommended
+that the
+.Dq ftp
+subtree be constructed with care;  the following
+rules are recommended.
+.Bl -tag -width "~ftp/pub" -offset indent
+.It Pa ~ftp
+Make the home directory owned by
+.Dq ftp
+and unwritable by anyone.
+.It Pa ~ftp/bin
+Make this directory owned by the super-user and unwritable by
+anyone.  The program
+.Xr ls 1
+must be present to support the list command.  This
+program should have mode 111.
+.It Pa ~ftp/etc
+Make this directory owned by the super-user and unwritable by
+anyone.  The files
+.Xr passwd 5
+and
+.Xr group 5
+must be present for the 
+.Xr ls
+command to be able to produce owner names rather than numbers.
+The password field in
+.Xr passwd
+is not used, and should not contain real encrypted passwords.
+These files should be mode 444.
+.It Pa ~ftp/pub
+Make this directory mode 777 and owned by
+.Dq ftp .
+Users
+should then place files which are to be accessible via the
+anonymous account in this directory.
+.El
+.Pp
+If an
+.Dv ADAT
+command succeeds, the control channel must be either
+integrity or privacy protected.
+In this case, the
+.Dv MIC
+and
+.Dv ENC
+commands are the only commands allowed over the control channel.
+The argument to the
+.Dv MIC
+command is a base 64 encoded string which, when decoded, is an
+ftp command integrity protected with a cryptographic checksum.
+The argument to the
+.Dv ENC
+command is a base 64 encoded string which, when decoded, is an
+ftp command privacy and integrity protected with encryption.
+.Pp
+If an
+.Dv ADAT
+command succeeds,
+ftp replies will also be either integrity or privacy protected.
+.Pp
+If an
+.Dv ADAT
+command succeeds, the data channel can also be integrity or privacy protected.
+The
+.Dv PROT
+command accepts S for integrity and P for privacy protection.
+Unless an
+.Dv ADAT
+command succeeds, the only protection level accepted by the
+.Dv PROT
+command is C (clear).
+.Sh SEE ALSO
+.Xr ftp 1 ,
+.Xr getusershell 3 ,
+.Xr syslogd 8
+.Pp
+Lunt, S. J.,
+FTP Security Extensions,
+Internet Draft,
+November 1993.
+.Sh BUGS
+The anonymous account is inherently dangerous and should
+avoided when possible.
+.Pp
+The server must run as the super-user
+to create sockets with privileged port numbers.  It maintains
+an effective user id of the logged in user, reverting to
+the super-user only when binding addresses to sockets.  The
+possible security holes have been extensively
+scrutinized, but are possibly incomplete.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/src/appl/gssftp/ftpd/ftpd.c b/src/appl/gssftp/ftpd/ftpd.c
new file mode 100644 (file)
index 0000000..2a1bff3
--- /dev/null
@@ -0,0 +1,2301 @@
+/*
+ * Copyright (c) 1985, 1988, 1990 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
+char copyright[] =
+"@(#) Copyright (c) 1985, 1988, 1990 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)ftpd.c     5.40 (Berkeley) 7/2/91";
+#endif /* not lint */
+
+/*
+ * FTP server.
+ */
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/file.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+
+#define        FTP_NAMES
+#include <arpa/ftp.h>
+#include <arpa/inet.h>
+#include <arpa/telnet.h>
+
+#include <signal.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <time.h>
+#include <pwd.h>
+#include <setjmp.h>
+#include <netdb.h>
+#include <errno.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef STDARG
+#if defined(__STDC__) && ! defined(VARARGS)
+#define STDARG
+#endif
+#endif
+#ifdef STDARG
+#include <stdarg.h>
+#endif
+#include "pathnames.h"
+
+#ifndef L_SET
+#define L_SET 0
+#endif
+#ifndef L_INCR
+#define L_INCR 1
+#endif
+
+#define strerror(error)        (sys_errlist[error])
+extern char *sys_errlist[];
+
+extern char *mktemp ();
+
+#ifndef HAVE_SETEUID
+#ifdef HAVE_SETRESUID
+#define seteuid(e) setresuid(-1,e,-1)
+#define setegid(e) setresgid(-1,e,-1)
+#endif
+#endif
+
+#ifdef STDARG
+extern reply(int, char *, ...);
+extern lreply(int, char *, ...);
+#endif
+
+#ifdef KERBEROS
+#include <krb.h>
+
+AUTH_DAT kdata;
+KTEXT_ST ticket;
+MSG_DAT msg_data;
+Key_schedule schedule;
+int kerb_ok;   /* Kerberos authentication and authorization succeeded */
+char *keyfile = KEYFILE;
+#endif /* KERBEROS */
+
+#ifdef GSSAPI
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_generic.h>
+gss_ctx_id_t gcontext;
+gss_buffer_desc client_name;
+int gss_ok;    /* GSSAPI authentication and userok authorization succeeded */
+char* gss_services[] = { "ftp", "host", 0 };
+#endif /* GSSAPI */
+
+char *auth_type;       /* Authentication succeeded?  If so, what type? */
+static char *temp_auth_type;
+
+/*
+ * File containing login names
+ * NOT to be used on this machine.
+ * Commonly used to disallow uucp.
+ */
+extern int errno;
+extern char *crypt();
+extern char version[];
+extern char *home;             /* pointer to home directory for glob */
+extern FILE *ftpd_popen(), *fopen(), *freopen();
+extern int  ftpd_pclose(), fclose();
+extern char *getline();
+extern char cbuf[];
+extern off_t restart_point;
+
+struct sockaddr_in ctrl_addr;
+struct sockaddr_in data_source;
+struct sockaddr_in data_dest;
+struct sockaddr_in his_addr;
+struct sockaddr_in pasv_addr;
+
+int    data;
+jmp_buf        errcatch, urgcatch;
+int    logged_in;
+struct passwd *pw;
+int    debug;
+int    timeout = 900;    /* timeout after 15 minutes of inactivity */
+int    maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */
+int    logging;
+int    authenticate;
+int    guest;
+int    type;
+int    level;
+int    form;
+int    stru;                   /* avoid C keyword */
+int    mode;
+int    usedefault = 1;         /* for data transfers */
+int    pdata = -1;             /* for passive mode */
+int    transflag;
+off_t  file_size;
+off_t  byte_count;
+#if !defined(CMASK) || CMASK == 0
+#undef CMASK
+#define CMASK 027
+#endif
+int    defumask = CMASK;               /* default umask value */
+char   tmpline[FTP_BUFSIZ];
+char   hostname[MAXHOSTNAMELEN];
+char   remotehost[MAXHOSTNAMELEN];
+
+/*
+ * Timeout intervals for retrying connections
+ * to hosts that don't accept PORT cmds.  This
+ * is a kludge, but given the problems with TCP...
+ */
+#define        SWAITMAX        90      /* wait at most 90 seconds */
+#define        SWAITINT        5       /* interval between retries */
+
+int    swaitmax = SWAITMAX;
+int    swaitint = SWAITINT;
+
+void   lostconn(), myoob();
+FILE   *getdatasock(), *dataconn();
+
+#ifdef SETPROCTITLE
+char   **Argv = NULL;          /* pointer to argument vector */
+char   *LastArgv = NULL;       /* end of argv */
+char   proctitle[FTP_BUFSIZ];  /* initial part of title */
+#endif /* SETPROCTITLE */
+
+#ifdef __SCO__
+/* sco has getgroups and setgroups but no initgroups */
+int initgroups(char* name, gid_t basegid) {
+  gid_t others[NGROUPS_MAX+1];
+  int ngrps;
+
+  others[0] = basegid;
+  ngrps = getgroups(NGROUPS_MAX, others+1);
+  return setgroups(ngrps+1, others);
+}
+#endif
+
+main(argc, argv, envp)
+       int argc;
+       char *argv[];
+       char **envp;
+{
+       int addrlen, on = 1, tos, port = -1;
+       char *cp;
+
+       debug = 0;
+#ifdef SETPROCTITLE
+       /*
+        *  Save start and extent of argv for setproctitle.
+        */
+       Argv = argv;
+       while (*envp)
+               envp++;
+       LastArgv = envp[-1] + strlen(envp[-1]);
+#endif /* SETPROCTITLE */
+
+       argc--, argv++;
+       while (argc > 0 && *argv[0] == '-') {
+               for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
+
+               case 'v':
+                       debug = 1;
+                       break;
+
+               case 'd':
+                       debug = 1;
+                       break;
+
+               case 'l':
+                       logging = 1;
+                       break;
+
+               case 'a':
+                       authenticate = 1;
+                       break;
+
+               case 'p':
+                       if (*++cp != '\0')
+                               port = atoi(cp);
+                       else if (argc > 1) {
+                               argc--, argv++;
+                               port = atoi(*argv);
+                       }
+                       else
+                               fprintf(stderr, "ftpd: -p expects argument\n");
+                       goto nextopt;
+
+               case 'r':
+                       if (*++cp != '\0')
+                               setenv("KRB_CONF", cp, 1);
+                       else if (argc > 1) {
+                               argc--, argv++;
+                               setenv("KRB_CONF", *argv, 1);
+                       }
+                       else
+                               fprintf(stderr, "ftpd: -r expects argument\n");
+                       goto nextopt;
+
+#ifdef KERBEROS
+               case 's':
+                       if (*++cp != '\0')
+                               keyfile = cp;
+                       else if (argc > 1) {
+                               argc--, argv++;
+                               keyfile = *argv;
+                       }
+                       else
+                               fprintf(stderr, "ftpd: -s expects argument\n");
+                       goto nextopt;
+
+#endif /* KERBEROS */
+               case 't':
+                       timeout = atoi(++cp);
+                       if (maxtimeout < timeout)
+                               maxtimeout = timeout;
+                       goto nextopt;
+
+               case 'T':
+                       maxtimeout = atoi(++cp);
+                       if (timeout > maxtimeout)
+                               timeout = maxtimeout;
+                       goto nextopt;
+
+               case 'u':
+                   {
+                       int val = 0;
+
+                       while (*++cp && *cp >= '0' && *cp <= '9')
+                               val = val*8 + *cp - '0';
+                       if (*cp)
+                               fprintf(stderr, "ftpd: Bad value for -u\n");
+                       else
+                               defumask = val;
+                       goto nextopt;
+                   }
+
+               default:
+                       fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n",
+                            *cp);
+                       break;
+               }
+nextopt:
+               argc--, argv++;
+       }
+
+       if (port != -1) {
+               struct sockaddr_in sin;
+               int s, ns, sz;
+
+               /* Accept an incoming connection on port.  */
+               sin.sin_family = AF_INET;
+               sin.sin_addr.s_addr = INADDR_ANY;
+               sin.sin_port = htons(port);
+               s = socket(AF_INET, SOCK_STREAM, 0);
+               if (s < 0) {
+                       perror("socket");
+                       exit(1);
+               }
+               (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+                                 (char *)&on, sizeof(on));
+               if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) {
+                       perror("bind");
+                       exit(1);
+               }
+               if (listen(s, 1) < 0) {
+                       perror("listen");
+                       exit(1);
+               }
+               sz = sizeof sin;
+               ns = accept(s, (struct sockaddr *)&sin, &sz);
+               if (ns < 0) {
+                       perror("accept");
+                       exit(1);
+               }
+               (void) close(s);
+               (void) dup2(ns, 0);
+               (void) dup2(ns, 1);
+               (void) dup2(ns, 2);
+               if (ns > 2)
+                 (void) close(ns);
+       }
+
+       /*
+        * LOG_NDELAY sets up the logging connection immediately,
+        * necessary for anonymous ftp's that chroot and can't do it later.
+        */
+#ifndef LOG_NDELAY
+/* Ultrix syslog does not support NDELAY.  */
+#define LOG_NDELAY 0
+#endif
+#ifndef LOG_DAEMON
+#define LOG_DAEMON 0
+#endif
+       openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
+       addrlen = sizeof (his_addr);
+       if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) {
+               syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
+               exit(1);
+       }
+       addrlen = sizeof (ctrl_addr);
+       if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
+               syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
+               exit(1);
+       }
+#ifdef IP_TOS
+#ifdef IPTOS_LOWDELAY
+       tos = IPTOS_LOWDELAY;
+       if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
+               syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
+#endif
+#endif
+       port = ntohs(ctrl_addr.sin_port);
+       data_source.sin_port = htons(port - 1);
+
+       (void) freopen("/dev/null", "w", stderr);
+       (void) signal(SIGPIPE, lostconn);
+       (void) signal(SIGCHLD, SIG_IGN);
+#ifdef SIGURG
+       if ((long)signal(SIGURG, myoob) < 0)
+               syslog(LOG_ERR, "signal: %m");
+#endif
+
+       /* Try to handle urgent data inline */
+#ifdef SO_OOBINLINE
+       if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
+               syslog(LOG_ERR, "setsockopt: %m");
+#endif
+
+#ifdef F_SETOWN
+       if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
+               syslog(LOG_ERR, "fcntl F_SETOWN: %m");
+#endif
+       dolog(&his_addr);
+       /*
+        * Set up default state
+        */
+       data = -1;
+       level = PROT_C;
+       type = TYPE_A;
+       form = FORM_N;
+       stru = STRU_F;
+       mode = MODE_S;
+       tmpline[0] = '\0';
+       (void) gethostname(hostname, sizeof (hostname));
+       reply(220, "%s FTP server (%s) ready.", hostname, version);
+       (void) setjmp(errcatch);
+       for (;;)
+               (void) yyparse();
+       /* NOTREACHED */
+}
+
+void
+lostconn()
+{
+       if (debug)
+               syslog(LOG_DEBUG, "lost connection");
+       dologout(-1);
+}
+
+static char ttyline[20];
+
+/*
+ * Helper function for sgetpwnam().
+ */
+char *
+sgetsave(s)
+       char *s;
+{
+       char *new = malloc((unsigned) strlen(s) + 1);
+
+       if (new == NULL) {
+               perror_reply(421, "Local resource failure: malloc");
+               dologout(1);
+               /* NOTREACHED */
+       }
+       (void) strcpy(new, s);
+       return (new);
+}
+
+/*
+ * Save the result of a getpwnam.  Used for USER command, since
+ * the data returned must not be clobbered by any other command
+ * (e.g., globbing).
+ */
+struct passwd *
+sgetpwnam(name)
+       char *name;
+{
+       static struct passwd save;
+       register struct passwd *p;
+       char *sgetsave();
+
+       if ((p = getpwnam(name)) == NULL)
+               return (p);
+       if (save.pw_name) {
+               free(save.pw_name);
+               free(save.pw_passwd);
+               free(save.pw_gecos);
+               free(save.pw_dir);
+               free(save.pw_shell);
+       }
+       save = *p;
+       save.pw_name = sgetsave(p->pw_name);
+       save.pw_passwd = sgetsave(p->pw_passwd);
+       save.pw_gecos = sgetsave(p->pw_gecos);
+       save.pw_dir = sgetsave(p->pw_dir);
+       save.pw_shell = sgetsave(p->pw_shell);
+       return (&save);
+}
+
+setlevel(prot_level)
+int prot_level;
+{
+       switch (prot_level) {
+               case PROT_S:
+#ifndef NOENCRYPTION
+               case PROT_P:
+#endif
+                       if (auth_type)
+               case PROT_C:
+                               reply(200, "Protection level set to %s.",
+                                       (level = prot_level) == PROT_S ?
+                                               "Safe" : level == PROT_P ?
+                                               "Private" : "Clear");
+                       else
+               default:        reply(536, "%s protection level not supported.",
+                                       levelnames[prot_level]);
+       }
+}
+
+int login_attempts;            /* number of failed login attempts */
+int askpasswd;                 /* had user command, ask for passwd */
+
+/*
+ * USER command.
+ * Sets global passwd pointer pw if named account exists and is acceptable;
+ * sets askpasswd if a PASS command is expected.  If logged in previously,
+ * need to reset state.  If name is "ftp" or "anonymous", the name is not in
+ * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
+ * If account doesn't exist, ask for passwd anyway.  Otherwise, check user
+ * requesting login privileges.  Disallow anyone who does not have a standard
+ * shell as returned by getusershell().  Disallow anyone mentioned in the file
+ * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
+ */
+user(name)
+       char *name;
+{
+       register char *cp;
+       char *shell;
+#ifdef HAVE_GETUSERSHELL
+       char *getusershell();
+#endif
+
+       /* Some paranoid sites may want the client to authenticate
+        * before accepting the USER command.  If so, uncomment this:
+
+       if (!auth_type) {
+               reply(530,
+                       "Must perform authentication before identifying USER.");
+               return;
+        */
+       if (logged_in) {
+               if (guest) {
+                       reply(530, "Can't change user from guest login.");
+                       return;
+               }
+               end_login();
+       }
+
+       guest = 0;
+       if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
+               if (checkuser("ftp") || checkuser("anonymous"))
+                       reply(530, "User %s access denied.", name);
+               else if ((pw = sgetpwnam("ftp")) != NULL) {
+                       guest = 1;
+                       askpasswd = 1;
+                       reply(331, "Guest login ok, send ident as password.");
+               } else
+                       reply(530, "User %s unknown.", name);
+               return;
+       }
+       if (pw = sgetpwnam(name)) {
+               if ((shell = pw->pw_shell) == NULL || *shell == 0)
+                       shell = "/bin/sh";
+#ifdef HAVE_GETUSERSHELL
+               while ((cp = getusershell()) != NULL)
+                       if (strcmp(cp, shell) == 0)
+                               break;
+               /* endusershell(); */ /* this breaks on solaris 2.4 */
+#else
+               cp = shell;
+#endif
+               if (cp == NULL || checkuser(name)) {
+                       reply(530, "User %s access denied.", name);
+                       if (logging)
+                               syslog(LOG_NOTICE,
+                                   "FTP LOGIN REFUSED FROM %s, %s",
+                                   remotehost, name);
+                       pw = (struct passwd *) NULL;
+                       return;
+               }
+       }
+#ifdef KERBEROS
+       if (auth_type && strcmp(auth_type, "KERBEROS_V4") == 0) {
+               char buf[FTP_BUFSIZ];
+               kerb_ok = kuserok(&kdata,name) == 0;
+               if (! kerb_ok && authenticate) {
+                       reply(530, "User %s access denied.", name);
+                       if (logging)
+                               syslog(LOG_NOTICE,
+                                      "FTP KERBEROS LOGIN REFUSED FROM %s, %s",
+                                      remotehost, name);
+                       pw = (struct passwd *) NULL;
+                       return;
+               }
+               sprintf(buf, "Kerberos user %s%s%s@%s is%s authorized as %s%s",
+                       kdata.pname, *kdata.pinst ? "." : "",
+                       kdata.pinst, kdata.prealm,
+                       kerb_ok ? "" : " not",
+                       name, kerb_ok ? "" : "; Password required.");
+               reply(kerb_ok ? 232 : 336, "%s", buf);
+               syslog(kerb_ok ? LOG_INFO : LOG_ERR, "%s", buf);
+       } else
+#endif /* KERBEROS */
+#ifdef GSSAPI
+       if (auth_type && strcmp(auth_type, "GSSAPI") == 0) {
+               char buf[FTP_BUFSIZ];
+               gss_ok = ftpd_userok(&client_name, name) == 0;
+               if (! gss_ok && authenticate) {
+                       reply(530, "User %s access denied.", name);
+                       if (logging)
+                               syslog(LOG_NOTICE,
+                                      "FTP GSSAPI LOGIN REFUSED FROM %s, %s",
+                                      remotehost, name);
+                       pw = (struct passwd *) NULL;
+                       return;
+               }
+               sprintf(buf, "GSSAPI user %s is%s authorized as %s%s",
+                       client_name.value,
+                       gss_ok ? "" : " not",
+                       name, gss_ok ? "" : "; Password required.");
+               /* 232 is per draft-8, but why 331 not 53z? */
+               reply(gss_ok ? 232 : 331, "%s", buf);
+               syslog(gss_ok ? LOG_INFO : LOG_ERR, "%s", buf);
+       } else
+#endif /* GSSAPI */
+       /* Other auth types go here ... */
+       if (authenticate) {
+               reply(530, "User %s access denied: authentication required.",
+                     name);
+               if (logging)
+                       syslog(LOG_NOTICE,
+                              "FTP LOGIN REFUSED FROM %s, %s",
+                              remotehost, name);
+               pw = (struct passwd *) NULL;
+               return;
+       } else
+               reply(331, "Password required for %s.", name);
+       askpasswd = 1;
+       /*
+        * Delay before reading passwd after first failed
+        * attempt to slow down passwd-guessing programs.
+        */
+       if (login_attempts)
+               sleep((unsigned) login_attempts);
+}
+
+/*
+ * Check if a user is in the file _PATH_FTPUSERS
+ */
+checkuser(name)
+       char *name;
+{
+       register FILE *fd;
+       register char *p;
+       char line[FTP_BUFSIZ];
+
+       if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) {
+               while (fgets(line, sizeof(line), fd) != NULL)
+                       if ((p = strchr(line, '\n')) != NULL) {
+                               *p = '\0';
+                               if (line[0] == '#')
+                                       continue;
+                               if (strcmp(line, name) == 0)
+                                       return (1);
+                       }
+               (void) fclose(fd);
+       }
+       return (0);
+}
+
+/*
+ * Terminate login as previous user, if any, resetting state;
+ * used when USER command is given or login fails.
+ */
+end_login()
+{
+
+       (void) seteuid((uid_t)0);
+       if (logged_in)
+               logwtmp(ttyline, "", "");
+       pw = NULL;
+       logged_in = 0;
+       guest = 0;
+}
+
+#ifdef KERBEROS
+static char *services[] = { "ftp", "rcmd", NULL };
+
+kpass(name, passwd)
+char *name, *passwd;
+{
+       char **service;
+       char instance[INST_SZ];
+       char realm[REALM_SZ];
+       char tkt_file[20];
+       KTEXT_ST ticket;
+       AUTH_DAT authdata;
+       des_cblock key;
+       unsigned long faddr;
+       struct hostent *hp;
+
+       if (krb_get_lrealm(realm, 1) != KSUCCESS)
+               return(0);
+
+       strcpy(tkt_file, TKT_ROOT);
+       strcat(tkt_file, "_ftpdXXXXXX");
+       krb_set_tkt_string(mktemp(tkt_file));
+
+       (void) strncpy(instance, krb_get_phost(hostname), sizeof(instance));
+
+       if ((hp = gethostbyname(instance)) == NULL)
+               return(0);
+
+       memcpy((char *) &faddr, (char *)hp->h_addr, sizeof(faddr));
+
+       if (krb_get_pw_in_tkt(name, "", realm, "krbtgt", realm, 1, passwd)) {
+         for (service = services; *service; service++)
+           if (!read_service_key(*service, instance, realm, 0, keyfile, key)) {
+             (void) memset(key, 0, sizeof(key));
+             if (krb_mk_req(&ticket, *service, instance, realm, 33) ||
+                 krb_rd_req(&ticket, *service, instance, faddr, &authdata,keyfile)||
+                 kuserok(&authdata, name)) {
+               dest_tkt();
+               return(0);
+             } else {
+               dest_tkt();
+               return(1);
+             }
+           }
+         dest_tkt();
+         return(0);
+       }
+       dest_tkt();
+       return(1);
+}
+#endif /* KERBEROS */
+
+pass(passwd)
+       char *passwd;
+{
+       char *xpasswd, *salt;
+
+       if (logged_in || askpasswd == 0) {
+               reply(503, "Login with USER first.");
+               return;
+       }
+       askpasswd = 0;
+       if (
+#ifdef KERBEROS
+           !kerb_ok &&
+#endif /* KERBEROS */
+#ifdef GSSAPI
+           !gss_ok &&
+#endif /* GSSAPI */
+           !guest) {           /* "ftp" is only account allowed no password */
+               if (pw == NULL)
+                       salt = "xx";
+               else
+                       salt = pw->pw_passwd;
+#ifdef __SCO__
+               /* SCO does not provide crypt.  */
+               xpasswd = "";
+#else
+               xpasswd = crypt(passwd, salt);
+#endif
+#ifdef KERBEROS
+               /* null pw_passwd ok if Kerberos password ok */
+               if (pw == NULL ||
+                   (*pw->pw_passwd && strcmp(xpasswd, pw->pw_passwd) &&
+                       !kpass(pw->pw_name, passwd)) ||
+                   (!*pw->pw_passwd && !kpass(pw->pw_name, passwd))) {
+#else
+               /* The strcmp does not catch null passwords! */
+               if (pw == NULL || *pw->pw_passwd == '\0' ||
+                   strcmp(xpasswd, pw->pw_passwd)) {
+#endif /* KERBEROS */
+                       reply(530, "Login incorrect.");
+                       pw = NULL;
+                       if (login_attempts++ >= 5) {
+                               syslog(LOG_NOTICE,
+                                   "repeated login failures from %s",
+                                   remotehost);
+                               exit(0);
+                       }
+                       return;
+               }
+       }
+       login_attempts = 0;             /* this time successful */
+       (void) setegid((gid_t)pw->pw_gid);
+       (void) initgroups(pw->pw_name, pw->pw_gid);
+
+       /* open wtmp before chroot */
+       (void)sprintf(ttyline, "ftp%d", getpid());
+       logwtmp(ttyline, pw->pw_name, remotehost);
+       logged_in = 1;
+
+       if (guest) {
+               /*
+                * We MUST do a chdir() after the chroot. Otherwise
+                * the old current directory will be accessible as "."
+                * outside the new root!
+                */
+               if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
+                       reply(550, "Can't set guest privileges.");
+                       goto bad;
+               }
+       } else if (chdir(pw->pw_dir) < 0) {
+               if (chdir("/") < 0) {
+                       reply(530, "User %s: can't change directory to %s.",
+                           pw->pw_name, pw->pw_dir);
+                       goto bad;
+               } else
+                       lreply(230, "No directory! Logging in with home=/");
+       }
+       if (seteuid((uid_t)pw->pw_uid) < 0) {
+               reply(550, "Can't set uid.");
+               goto bad;
+       }
+       if (guest) {
+               reply(230, "Guest login ok, access restrictions apply.");
+#ifdef SETPROCTITLE
+               sprintf(proctitle, "%s: anonymous/%.*s", remotehost,
+                   sizeof(proctitle) - sizeof(remotehost) -
+                   sizeof(": anonymous/"), passwd);
+               setproctitle(proctitle);
+#endif /* SETPROCTITLE */
+               if (logging)
+                       syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
+                           remotehost, passwd);
+       } else {
+               reply(230, "User %s logged in.", pw->pw_name);
+#ifdef SETPROCTITLE
+               sprintf(proctitle, "%s: %s", remotehost, pw->pw_name);
+               setproctitle(proctitle);
+#endif /* SETPROCTITLE */
+               if (logging)
+                       syslog(LOG_INFO, "FTP LOGIN FROM %s, %s",
+                           remotehost, pw->pw_name);
+       }
+       home = pw->pw_dir;              /* home dir for globbing */
+       (void) umask(defumask);
+       return;
+bad:
+       /* Forget all about it... */
+       end_login();
+}
+
+retrieve(cmd, name)
+       char *cmd, *name;
+{
+       FILE *fin, *dout;
+       struct stat st;
+       int (*closefunc)();
+
+       if (cmd == 0) {
+               fin = fopen(name, "r"), closefunc = fclose;
+               st.st_size = 0;
+       } else {
+               char line[FTP_BUFSIZ];
+
+               (void) sprintf(line, cmd, name), name = line;
+               fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
+               st.st_size = -1;
+#ifndef NOSTBLKSIZE
+               st.st_blksize = FTP_BUFSIZ;
+#endif
+       }
+       if (fin == NULL) {
+               if (errno != 0)
+                       perror_reply(550, name);
+               return;
+       }
+       if (cmd == 0 &&
+           (fstat(fileno(fin), &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
+               reply(550, "%s: not a plain file.", name);
+               goto done;
+       }
+       if (restart_point) {
+               if (type == TYPE_A) {
+                       register int i, n, c;
+
+                       n = restart_point;
+                       i = 0;
+                       while (i++ < n) {
+                               if ((c=getc(fin)) == EOF) {
+                                       perror_reply(550, name);
+                                       goto done;
+                               }
+                               if (c == '\n')
+                                       i++;
+                       }       
+               } else if (lseek(fileno(fin), restart_point, L_SET) < 0) {
+                       perror_reply(550, name);
+                       goto done;
+               }
+       }
+       dout = dataconn(name, st.st_size, "w");
+       if (dout == NULL)
+               goto done;
+#ifndef NOSTBLKSIZE
+       send_data(fin, dout, st.st_blksize);
+#else
+       send_data(fin, dout, FTP_BUFSIZ);
+#endif
+       (void) fclose(dout);
+       data = -1;
+       pdata = -1;
+done:
+       (*closefunc)(fin);
+}
+
+store(name, mode, unique)
+       char *name, *mode;
+       int unique;
+{
+       FILE *fout, *din;
+       struct stat st;
+       int (*closefunc)();
+       char *gunique();
+
+       if (unique && stat(name, &st) == 0 &&
+           (name = gunique(name)) == NULL)
+               return;
+
+       if (restart_point)
+               mode = "r+w";
+       fout = fopen(name, mode);
+       closefunc = fclose;
+       if (fout == NULL) {
+               perror_reply(553, name);
+               return;
+       }
+       if (restart_point) {
+               if (type == TYPE_A) {
+                       register int i, n, c;
+
+                       n = restart_point;
+                       i = 0;
+                       while (i++ < n) {
+                               if ((c=getc(fout)) == EOF) {
+                                       perror_reply(550, name);
+                                       goto done;
+                               }
+                               if (c == '\n')
+                                       i++;
+                       }       
+                       /*
+                        * We must do this seek to "current" position
+                        * because we are changing from reading to
+                        * writing.
+                        */
+                       if (fseek(fout, 0L, L_INCR) < 0) {
+                               perror_reply(550, name);
+                               goto done;
+                       }
+               } else if (lseek(fileno(fout), restart_point, L_SET) < 0) {
+                       perror_reply(550, name);
+                       goto done;
+               }
+       }
+       din = dataconn(name, (off_t)-1, "r");
+       if (din == NULL)
+               goto done;
+       if (receive_data(din, fout) == 0) {
+               if (unique)
+                       reply(226, "Transfer complete (unique file name:%s).",
+                           name);
+               else
+                       reply(226, "Transfer complete.");
+       }
+       (void) fclose(din);
+       data = -1;
+       pdata = -1;
+done:
+       (*closefunc)(fout);
+}
+
+FILE *
+getdatasock(mode)
+       char *mode;
+{
+       int s, on = 1, tries;
+
+       if (data >= 0)
+               return (fdopen(data, mode));
+       (void) seteuid((uid_t)0);
+       s = socket(AF_INET, SOCK_STREAM, 0);
+       if (s < 0)
+               goto bad;
+       if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+           (char *) &on, sizeof (on)) < 0)
+               goto bad;
+       /* anchor socket to avoid multi-homing problems */
+       data_source.sin_family = AF_INET;
+       data_source.sin_addr = ctrl_addr.sin_addr;
+       for (tries = 1; ; tries++) {
+               if (bind(s, (struct sockaddr *)&data_source,
+                   sizeof (data_source)) >= 0)
+                       break;
+               if (errno != EADDRINUSE || tries > 10)
+                       goto bad;
+               sleep(tries);
+       }
+       (void) seteuid((uid_t)pw->pw_uid);
+#ifdef IP_TOS
+#ifdef IPTOS_THROUGHPUT
+       on = IPTOS_THROUGHPUT;
+       if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
+               syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
+#endif
+#endif
+       return (fdopen(s, mode));
+bad:
+       (void) seteuid((uid_t)pw->pw_uid);
+       (void) close(s);
+       return (NULL);
+}
+
+FILE *
+dataconn(name, size, mode)
+       char *name;
+       off_t size;
+       char *mode;
+{
+       char sizebuf[32];
+       FILE *file;
+       int retry = 0, tos;
+
+       file_size = size;
+       byte_count = 0;
+       if (size != (off_t) -1)
+               (void) sprintf (sizebuf, " (%ld bytes)", size);
+       else
+               (void) strcpy(sizebuf, "");
+       if (pdata >= 0) {
+               int s, fromlen = sizeof(data_dest);
+
+               s = accept(pdata, (struct sockaddr *)&data_dest, &fromlen);
+               if (s < 0) {
+                       reply(425, "Can't open data connection.");
+                       (void) close(pdata);
+                       pdata = -1;
+                       return(NULL);
+               }
+               (void) close(pdata);
+               pdata = s;
+#ifdef IP_TOS
+#ifdef IPTOS_LOWDELAY
+               tos = IPTOS_LOWDELAY;
+               (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos,
+                   sizeof(int));
+#endif
+#endif
+               reply(150, "Opening %s mode data connection for %s%s.",
+                    type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
+               return(fdopen(pdata, mode));
+       }
+       if (data >= 0) {
+               reply(125, "Using existing data connection for %s%s.",
+                   name, sizebuf);
+               usedefault = 1;
+               return (fdopen(data, mode));
+       }
+       if (usedefault)
+               data_dest = his_addr;
+       usedefault = 1;
+       file = getdatasock(mode);
+       if (file == NULL) {
+               reply(425, "Can't create data socket (%s,%d): %s.",
+                   inet_ntoa(data_source.sin_addr),
+                   ntohs(data_source.sin_port), strerror(errno));
+               return (NULL);
+       }
+       data = fileno(file);
+       while (connect(data, (struct sockaddr *)&data_dest,
+           sizeof (data_dest)) < 0) {
+               if (errno == EADDRINUSE && retry < swaitmax) {
+                       sleep((unsigned) swaitint);
+                       retry += swaitint;
+                       continue;
+               }
+               perror_reply(425, "Can't build data connection");
+               (void) fclose(file);
+               data = -1;
+               return (NULL);
+       }
+       reply(150, "Opening %s mode data connection for %s%s.",
+            type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
+       return (file);
+}
+
+#ifdef STDARG
+secure_error(char *fmt, ...)
+#else
+/* VARARGS1 */
+secure_error(fmt, p1, p2, p3, p4, p5)
+       char *fmt;
+#endif
+{
+       char buf[FTP_BUFSIZ];
+#ifdef STDARG
+       va_list ap;
+
+       va_start(ap, fmt);
+       vsprintf(buf, fmt, ap);
+       va_end(ap);
+#else
+       sprintf(buf, fmt, p1, p2, p3, p4, p5);
+#endif
+       reply(535, "%s", buf);
+       syslog(LOG_ERR, "%s", buf);
+}
+
+/*
+ * Tranfer the contents of "instr" to
+ * "outstr" peer using the appropriate
+ * encapsulation of the data subject
+ * to Mode, Structure, and Type.
+ *
+ * NB: Form isn't handled.
+ */
+send_data(instr, outstr, blksize)
+       FILE *instr, *outstr;
+       off_t blksize;
+{
+       register int c, cnt;
+       register char *buf;
+       int netfd, filefd;
+       int ret = 0;
+
+       transflag++;
+       if (setjmp(urgcatch)) {
+               transflag = 0;
+               return;
+       }
+       switch (type) {
+
+       case TYPE_A:
+               while ((c = getc(instr)) != EOF) {
+                       byte_count++;
+                       if (c == '\n') {
+                               if (ferror(outstr) ||
+                                   (ret = secure_putc('\r', outstr)) < 0)
+                                       goto data_err;
+                       }
+                       if ((ret = secure_putc(c, outstr)) < 0)
+                               goto data_err;
+               }
+               transflag = 0;
+               if (ferror(instr))
+                       goto file_err;
+               if (ferror(outstr) ||
+                   (ret = secure_flush(fileno(outstr))) < 0)
+                       goto data_err;
+               reply(226, "Transfer complete.");
+               return;
+
+       case TYPE_I:
+       case TYPE_L:
+               if ((buf = malloc((u_int)blksize)) == NULL) {
+                       transflag = 0;
+                       perror_reply(451, "Local resource failure: malloc");
+                       return;
+               }
+               netfd = fileno(outstr);
+               filefd = fileno(instr);
+               while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
+                   (ret = secure_write(netfd, buf, cnt)) == cnt)
+                       byte_count += cnt;
+               transflag = 0;
+               (void)free(buf);
+               if (cnt != 0) {
+                       if (cnt < 0)
+                               goto file_err;
+                       goto data_err;
+               }
+               if ((ret = secure_flush(netfd)) < 0)
+                       goto data_err;
+               reply(226, "Transfer complete.");
+               return;
+       default:
+               transflag = 0;
+               reply(550, "Unimplemented TYPE %d in send_data", type);
+               return;
+       }
+
+data_err:
+       transflag = 0;
+       if (ret != -2) perror_reply(426, "Data connection");
+       return;
+
+file_err:
+       transflag = 0;
+       perror_reply(551, "Error on input file");
+}
+
+/*
+ * Transfer data from peer to
+ * "outstr" using the appropriate
+ * encapulation of the data subject
+ * to Mode, Structure, and Type.
+ *
+ * N.B.: Form isn't handled.
+ */
+receive_data(instr, outstr)
+       FILE *instr, *outstr;
+{
+       register int c;
+       int cnt, bare_lfs = 0;
+       char buf[FTP_BUFSIZ];
+       int ret = 0;
+
+       transflag++;
+       if (setjmp(urgcatch)) {
+               transflag = 0;
+               return (-1);
+       }
+       switch (type) {
+
+       case TYPE_I:
+       case TYPE_L:
+               while ((cnt = secure_read(fileno(instr), buf, sizeof buf)) > 0) {
+                       if (write(fileno(outstr), buf, cnt) != cnt)
+                               goto file_err;
+                       byte_count += cnt;
+               }
+               transflag = 0;
+               ret = cnt;
+               if (cnt < 0)
+                       goto data_err;
+               return (0);
+
+       case TYPE_E:
+               reply(553, "TYPE E not implemented.");
+               transflag = 0;
+               return (-1);
+
+       case TYPE_A:
+               while ((c = secure_getc(instr)) >= 0) {
+                       byte_count++;
+                       if (c == '\n')
+                               bare_lfs++;
+                       while (c == '\r') {
+                               if (ferror(outstr))
+                                       goto data_err;
+                               if ((c = secure_getc(instr)) != '\n') {
+                                       (void) putc ('\r', outstr);
+                                       if (c == '\0')
+                                               goto contin2;
+                               }
+                       }
+                       if (c < 0) break;
+                       (void) putc(c, outstr);
+       contin2:        ;
+               }
+               fflush(outstr);
+               ret = c;
+               if (c == -2 || ferror(instr))
+                       goto data_err;
+               if (ferror(outstr))
+                       goto file_err;
+               transflag = 0;
+               if (bare_lfs) {
+                       lreply(226, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs);
+                       reply(0, "   File may not have transferred correctly.");
+               }
+               return (0);
+       default:
+               reply(550, "Unimplemented TYPE %d in receive_data", type);
+               transflag = 0;
+               return (-1);
+       }
+
+data_err:
+       transflag = 0;
+       if (ret != -2) perror_reply(426, "Data Connection");
+       return (-1);
+
+file_err:
+       transflag = 0;
+       perror_reply(452, "Error writing file");
+       return (-1);
+}
+
+statfilecmd(filename)
+       char *filename;
+{
+       char line[FTP_BUFSIZ];
+       FILE *fin;
+       int c;
+       char str[FTP_BUFSIZ], *p;
+
+       (void) sprintf(line, "/bin/ls -lgA %s", filename);
+       fin = ftpd_popen(line, "r");
+       lreply(211, "status of %s:", filename);
+       p = str;
+       while ((c = getc(fin)) != EOF) {
+               if (c == '\n') {
+                       if (ferror(stdout)){
+                               perror_reply(421, "control connection");
+                               (void) ftpd_pclose(fin);
+                               dologout(1);
+                               /* NOTREACHED */
+                       }
+                       if (ferror(fin)) {
+                               perror_reply(551, filename);
+                               (void) ftpd_pclose(fin);
+                               return;
+                       }
+                       *p = '\0';
+                       reply(0, "%s", str);
+                       p = str;
+               } else  *p++ = c;
+       }
+       if (p != str) {
+               *p = '\0';
+               reply(0, "%s", str);
+       }
+       (void) ftpd_pclose(fin);
+       reply(211, "End of Status");
+}
+
+statcmd()
+{
+       struct sockaddr_in *sin;
+       u_char *a, *p;
+       char str[FTP_BUFSIZ];
+
+       lreply(211, "%s FTP server status:", hostname, version);
+       reply(0, "     %s", version);
+       sprintf(str, "     Connected to %s", remotehost);
+       if (!isdigit(remotehost[0]))
+               sprintf(&str[strlen(str)], " (%s)", inet_ntoa(his_addr.sin_addr));
+       reply(0, "%s", str);
+       if (auth_type) reply(0, "     Authentication type: %s", auth_type);
+       if (logged_in) {
+               if (guest)
+                       reply(0, "     Logged in anonymously");
+               else
+                       reply(0, "     Logged in as %s", pw->pw_name);
+       } else if (askpasswd)
+               reply(0, "     Waiting for password");
+       else if (temp_auth_type)
+               reply(0, "     Waiting for authentication data");
+       else
+               reply(0, "     Waiting for user name");
+       reply(0, "     PROTection level: %s", levelnames[level]);
+       sprintf(str, "     TYPE: %s", typenames[type]);
+       if (type == TYPE_A || type == TYPE_E)
+               sprintf(&str[strlen(str)], ", FORM: %s", formnames[form]);
+       if (type == TYPE_L)
+#if 1
+               strcat(str, " 8");
+#else
+/* this is silly. -- eichin@cygnus.com */
+#if NBBY == 8
+               sprintf(&str[strlen(str)], " %d", NBBY);
+#else
+               sprintf(&str[strlen(str)], " %d", bytesize);    /* need definition! */
+#endif
+#endif
+       sprintf(&str[strlen(str)], "; STRUcture: %s; transfer MODE: %s",
+           strunames[stru], modenames[mode]);
+       reply(0, "%s", str);
+       if (data != -1)
+               strcpy(str, "     Data connection open");
+       else if (pdata != -1) {
+               strcpy(str, "     in Passive mode");
+               sin = &pasv_addr;
+               goto printaddr;
+       } else if (usedefault == 0) {
+               strcpy(str, "     PORT");
+               sin = &data_dest;
+printaddr:
+               a = (u_char *) &sin->sin_addr;
+               p = (u_char *) &sin->sin_port;
+#define UC(b) (((int) b) & 0xff)
+               sprintf(&str[strlen(str)], " (%d,%d,%d,%d,%d,%d)", UC(a[0]),
+                       UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
+#undef UC
+       } else
+               strcpy(str, "     No data connection");
+       reply(0, "%s", str);
+       reply(211, "End of status");
+}
+
+fatal(s)
+       char *s;
+{
+       reply(451, "Error in server: %s", s);
+       reply(221, "Closing connection due to server error.");
+       dologout(0);
+       /* NOTREACHED */
+}
+
+char cont_char = ' ';
+
+#ifdef STDARG
+reply(int n, char *fmt, ...)
+#else
+/* VARARGS2 */
+reply(n, fmt, p0, p1, p2, p3, p4, p5)
+       int n;
+       char *fmt;
+#endif
+{
+       char buf[FTP_BUFSIZ];
+#ifdef STDARG
+       va_list ap;
+
+       va_start(ap, fmt);
+       vsprintf(buf, fmt, ap);
+       va_end(ap);
+#else
+       sprintf(buf, fmt, p0, p1, p2, p3, p4, p5);
+#endif
+
+       if (auth_type) {
+               char in[FTP_BUFSIZ], out[FTP_BUFSIZ];
+               int length, kerror;
+               /*
+                * File protection level also determines whether
+                * replies are 631 or 632.  Should be independent ...
+                */
+               if (n) sprintf(in, "%d%c", n, cont_char);
+               else in[0] = '\0';
+               strcat(in, buf);
+#ifdef KERBEROS
+               if (strcmp(auth_type, "KERBEROS_V4") == 0)
+                 if ((length = level == PROT_P ?
+                   krb_mk_priv((unsigned char *)in, (unsigned char *)out,
+                               strlen(in), schedule, &kdata.session,
+                               &ctrl_addr, &his_addr)
+                 : krb_mk_safe((unsigned char *)in, (unsigned char *)out,
+                               strlen(in), &kdata.session,
+                               &ctrl_addr, &his_addr)) == -1) {
+                       syslog(LOG_ERR, "krb_mk_%s failed for KERBEROS_V4",
+                                       level == PROT_P ? "priv" : "safe");
+                       fputs(in,stdout);
+                 } else
+#endif /* KERBEROS */
+#ifdef GSSAPI
+               /* reply (based on level) */
+               if (strcmp(auth_type, "GSSAPI") == 0) {
+                       gss_buffer_desc in_buf, out_buf;
+                       OM_uint32 maj_stat, min_stat;
+                       int conf_state;
+               
+                       in_buf.value = in;
+                       in_buf.length = strlen(in) + 1;
+                       maj_stat = gss_seal(&min_stat, gcontext,
+                                           level == PROT_P, /* confidential */
+                                           GSS_C_QOP_DEFAULT,
+                                           &in_buf, &conf_state,
+                                           &out_buf);
+                       if (maj_stat != GSS_S_COMPLETE) {
+                               /* generally need to deal */
+                               secure_gss_error(maj_stat, min_stat,
+                                              (level==PROT_P)?
+                                                "gss_seal ENC didn't complete":
+                                                "gss_seal MIC didn't complete");
+                       } else if ((level == PROT_P) && !conf_state) {
+                               secure_error("GSSAPI didn't encrypt message");
+                       } else {
+                               memcpy(out, out_buf.value, 
+                                      length=out_buf.length);
+                               gss_release_buffer(&min_stat, &out_buf);
+                       }
+               }
+#endif /* GSSAPI */
+               /* Other auth types go here ... */
+               if (kerror = radix_encode(out, in, &length, 0)) {
+                       syslog(LOG_ERR, "Couldn't encode reply (%s)",
+                                       radix_error(kerror));
+                       fputs(in,stdout);
+               } else
+               printf("%s%c%s", level == PROT_P ? "632" : "631",
+                                n ? cont_char : '-', in);
+       } else {
+               if (n) printf("%d%c", n, cont_char);
+               fputs(buf, stdout);
+       }
+       printf("\r\n");
+       (void)fflush(stdout);
+       if (debug) {
+               if (n) syslog(LOG_DEBUG, "<--- %d%c", n, cont_char);
+               syslog(LOG_DEBUG, "%s", buf);
+       }
+}
+
+#ifdef STDARG
+lreply(int n, char *fmt, ...)
+#else
+/* VARARGS2 */
+lreply(n, fmt, p0, p1, p2, p3, p4, p5)
+       int n;
+       char *fmt;
+#endif
+{
+       char buf[FTP_BUFSIZ];
+#ifdef STDARG
+       va_list ap;
+
+       va_start(ap, fmt);
+       vsprintf(buf, fmt, ap);
+       va_end(ap);
+#else
+       sprintf(buf, fmt, p0, p1, p2, p3, p4, p5);
+#endif
+       cont_char = '-';
+       reply(n, "%s", buf);
+       cont_char = ' ';
+}
+
+ack(s)
+       char *s;
+{
+       reply(250, "%s command successful.", s);
+}
+
+nack(s)
+       char *s;
+{
+       reply(502, "%s command not implemented.", s);
+}
+
+/* ARGSUSED */
+yyerror(s)
+       char *s;
+{
+       char *cp;
+
+       if (cp = strchr(cbuf,'\n'))
+               *cp = '\0';
+       reply(500, "'%s': command not understood.", cbuf);
+}
+
+delete(name)
+       char *name;
+{
+       struct stat st;
+
+       if (stat(name, &st) < 0) {
+               perror_reply(550, name);
+               return;
+       }
+       if ((st.st_mode&S_IFMT) == S_IFDIR) {
+               if (rmdir(name) < 0) {
+                       perror_reply(550, name);
+                       return;
+               }
+               goto done;
+       }
+       if (unlink(name) < 0) {
+               perror_reply(550, name);
+               return;
+       }
+done:
+       ack("DELE");
+}
+
+cwd(path)
+       char *path;
+{
+       if (chdir(path) < 0)
+               perror_reply(550, path);
+       else
+               ack("CWD");
+}
+
+makedir(name)
+       char *name;
+{
+       if (mkdir(name, 0777) < 0)
+               perror_reply(550, name);
+       else
+               reply(257, "MKD command successful.");
+}
+
+removedir(name)
+       char *name;
+{
+       if (rmdir(name) < 0)
+               perror_reply(550, name);
+       else
+               ack("RMD");
+}
+
+pwd()
+{
+       char path[MAXPATHLEN + 1];
+
+       if (getcwd(path, sizeof path) == (char *)NULL)
+#ifdef POSIX
+               perror_reply(550, path);
+#else
+               reply(550, "%s.", path);
+#endif
+       else
+               reply(257, "\"%s\" is current directory.", path);
+}
+
+char *
+renamefrom(name)
+       char *name;
+{
+       struct stat st;
+
+       if (stat(name, &st) < 0) {
+               perror_reply(550, name);
+               return ((char *)0);
+       }
+       reply(350, "File exists, ready for destination name");
+       return (name);
+}
+
+renamecmd(from, to)
+       char *from, *to;
+{
+       if (rename(from, to) < 0)
+               perror_reply(550, "rename");
+       else
+               ack("RNTO");
+}
+
+dolog(sin)
+       struct sockaddr_in *sin;
+{
+       struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr,
+               sizeof (struct in_addr), AF_INET);
+       time_t t, time();
+       extern char *ctime();
+
+       if (hp)
+               (void) strncpy(remotehost, hp->h_name, sizeof (remotehost));
+       else
+               (void) strncpy(remotehost, inet_ntoa(sin->sin_addr),
+                   sizeof (remotehost));
+#ifdef SETPROCTITLE
+       sprintf(proctitle, "%s: connected", remotehost);
+       setproctitle(proctitle);
+#endif /* SETPROCTITLE */
+
+       if (logging) {
+               t = time((time_t *) 0);
+               syslog(LOG_INFO, "connection from %s at %s",
+                   remotehost, ctime(&t));
+       }
+}
+
+/*
+ * Record logout in wtmp file
+ * and exit with supplied status.
+ */
+dologout(status)
+       int status;
+{
+       if (logged_in) {
+               (void) seteuid((uid_t)0);
+               logwtmp(ttyline, "", "");
+       }
+       /* beware of flushing buffers after a SIGPIPE */
+       _exit(status);
+}
+
+void
+myoob()
+{
+       char *cp, *cs;
+       extern char *strpbrk();
+
+       /* only process if transfer occurring */
+       if (!transflag)
+               return;
+       cp = tmpline;
+       if (getline(cp, sizeof(tmpline), stdin) == NULL) {
+               reply(221, "You could at least say goodbye.");
+               dologout(0);
+       }
+       upper(cp);
+       if ((cs = strpbrk(cp, "\r\n")))
+               *cs++ = '\0';
+       if (strcmp(cp, "ABOR") == 0) {
+               tmpline[0] = '\0';
+               reply(426, "Transfer aborted. Data connection closed.");
+               reply(226, "Abort successful");
+               longjmp(urgcatch, 1);
+       }
+       if (strcmp(cp, "STAT") == 0) {
+               if (file_size != (off_t) -1)
+                       reply(213, "Status: %lu of %lu bytes transferred",
+                           byte_count, file_size);
+               else
+                       reply(213, "Status: %lu bytes transferred", byte_count);
+       }
+}
+
+/*
+ * Note: a response of 425 is not mentioned as a possible response to
+ *     the PASV command in RFC959. However, it has been blessed as
+ *     a legitimate response by Jon Postel in a telephone conversation
+ *     with Rick Adams on 25 Jan 89.
+ */
+passive()
+{
+       int len;
+       register char *p, *a;
+
+       pdata = socket(AF_INET, SOCK_STREAM, 0);
+       if (pdata < 0) {
+               perror_reply(425, "Can't open passive connection");
+               return;
+       }
+       pasv_addr = ctrl_addr;
+       pasv_addr.sin_port = 0;
+       (void) seteuid((uid_t)0);
+       if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) {
+               (void) seteuid((uid_t)pw->pw_uid);
+               goto pasv_error;
+       }
+       (void) seteuid((uid_t)pw->pw_uid);
+       len = sizeof(pasv_addr);
+       if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
+               goto pasv_error;
+       if (listen(pdata, 1) < 0)
+               goto pasv_error;
+       a = (char *) &pasv_addr.sin_addr;
+       p = (char *) &pasv_addr.sin_port;
+
+#define UC(b) (((int) b) & 0xff)
+
+       reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
+               UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
+       return;
+
+pasv_error:
+       (void) close(pdata);
+       pdata = -1;
+       perror_reply(425, "Can't open passive connection");
+       return;
+}
+
+/*
+ * Generate unique name for file with basename "local".
+ * The file named "local" is already known to exist.
+ * Generates failure reply on error.
+ */
+char *
+gunique(local)
+       char *local;
+{
+       static char new[MAXPATHLEN];
+       struct stat st;
+       char *cp = strrchr(local, '/');
+       int count = 0;
+
+       if (cp)
+               *cp = '\0';
+       if (stat(cp ? local : ".", &st) < 0) {
+               perror_reply(553, cp ? local : ".");
+               return((char *) 0);
+       }
+       if (cp)
+               *cp = '/';
+       (void) strcpy(new, local);
+       cp = new + strlen(new);
+       *cp++ = '.';
+       for (count = 1; count < 100; count++) {
+               (void) sprintf(cp, "%d", count);
+               if (stat(new, &st) < 0)
+                       return(new);
+       }
+       reply(452, "Unique file name cannot be created.");
+       return((char *) 0);
+}
+
+/*
+ * Format and send reply containing system error number.
+ */
+perror_reply(code, string)
+       int code;
+       char *string;
+{
+       reply(code, "%s: %s.", string, strerror(errno));
+}
+
+auth(type)
+char *type;
+{
+       if (auth_type)
+               reply(534, "Authentication type already set to %s", auth_type);
+       else
+#ifdef KERBEROS
+       if (strcmp(type, "KERBEROS_V4") == 0)
+               reply(334, "Using authentication type %s; ADAT must follow",
+                               temp_auth_type = type);
+       else
+#endif /* KERBEROS */
+#ifdef GSSAPI
+       if (strcmp(type, "GSSAPI") == 0)
+               reply(334, "Using authentication type %s; ADAT must follow",
+                               temp_auth_type = type);
+       else
+#endif /* KERBEROS */
+       /* Other auth types go here ... */
+               reply(504, "Unknown authentication type: %s", type);
+}
+
+auth_data(data)
+char *data;
+{
+       int kerror, length;
+#ifdef KERBEROS
+       int i;
+       static char *service;
+       char instance[INST_SZ];
+       u_long cksum;
+       char buf[FTP_BUFSIZ];
+       u_char out_buf[sizeof(buf)];
+#endif /* KERBEROS */
+#ifdef GSSAPI
+       OM_uint32 maj_stat, min_stat;
+       gss_OID mechid;
+       gss_buffer_desc tok, out_tok;
+       char gbuf[FTP_BUFSIZ];
+       u_char gout_buf[sizeof(gbuf)];
+#endif
+
+       if (auth_type) {
+               reply(503, "Authentication already established");
+               return(0);
+       }
+       if (!temp_auth_type) {
+               reply(503, "Must identify AUTH type before ADAT");
+               return(0);
+       }
+#ifdef KERBEROS
+    if (strcmp(temp_auth_type, "KERBEROS_V4") == 0) {
+       if (kerror = radix_encode(data, out_buf, &length, 1)) {
+               reply(501, "Couldn't decode ADAT (%s)",
+                               radix_error(kerror));
+               syslog(LOG_ERR, "Couldn't decode ADAT (%s)",
+                               radix_error(kerror));
+               return(0);
+       }
+       (void) memcpy((char *)ticket.dat, (char *)out_buf, ticket.length = length);
+       strcpy(instance, "*");
+       if (!service) {
+               char realm[REALM_SZ];
+               des_cblock key;
+
+               service = "ftp";
+               if (krb_get_lrealm(realm, 1) == KSUCCESS &&
+                   read_service_key(service, instance, realm, 0, keyfile, key))
+                       service = "rcmd";
+               else    (void) memset(key, 0, sizeof(key));
+       }
+       if (kerror = krb_rd_req(&ticket, service, instance,
+               his_addr.sin_addr.s_addr, &kdata, keyfile)) {
+               secure_error("ADAT: Kerberos V4 krb_rd_req: %s",
+                               krb_get_err_text(kerror));
+               return(0);
+       }
+       /* add one to the (formerly) sealed checksum, and re-seal it */
+       cksum = kdata.checksum + 1;
+       cksum = htonl(cksum);
+       key_sched(kdata.session,schedule);
+       if ((length = krb_mk_safe((u_char *)&cksum, out_buf, sizeof(cksum),
+                       &kdata.session,&ctrl_addr, &his_addr)) == -1) {
+               secure_error("ADAT: krb_mk_safe failed");
+               return(0);
+       }
+       if (kerror = radix_encode(out_buf, buf, &length, 0)) {
+               secure_error("Couldn't encode ADAT reply (%s)",
+                               radix_error(kerror));
+               return(0);
+       }
+       reply(235, "ADAT=%s", buf);
+       /* Kerberos V4 authentication succeeded */
+       auth_type = temp_auth_type;
+       temp_auth_type = NULL;
+       return(1);
+    }
+#endif /* KERBEROS */
+#ifdef GSSAPI
+    if (strcmp(temp_auth_type, "GSSAPI") == 0) {
+        int replied = 0;
+        gss_cred_id_t server_creds;     
+       gss_name_t client;
+       int ret_flags;
+       struct gss_channel_bindings_struct chan;
+       gss_buffer_desc name_buf;
+       gss_name_t server_name;
+       OM_uint32 maj_stat, min_stat;
+       char localname[MAXHOSTNAMELEN];
+       char service_name[MAXHOSTNAMELEN+10];
+       char **service;
+
+       chan.initiator_addrtype = GSS_C_AF_INET;
+       chan.initiator_address.length = 4;
+       chan.initiator_address.value = &his_addr.sin_addr.s_addr;
+       chan.acceptor_addrtype = GSS_C_AF_INET;
+       chan.acceptor_address.length = 4;
+       chan.acceptor_address.value = &ctrl_addr.sin_addr.s_addr;
+       chan.application_data.length = 0;
+       chan.application_data.value = 0;
+
+       if (kerror = radix_encode(data, gout_buf, &length, 1)) {
+               reply(501, "Couldn't decode ADAT (%s)",
+                               radix_error(kerror));
+               syslog(LOG_ERR, "Couldn't decode ADAT (%s)",
+                               radix_error(kerror));
+               return(0);
+       }
+       tok.value = gout_buf;
+       tok.length = length;
+       
+       if (gethostname(localname, MAXHOSTNAMELEN)) {
+               reply(501, "couldn't get local hostname (%d)\n", errno);
+               syslog(LOG_ERR, "Couldn't get local hostname (%d)", errno);
+               return 0;
+       }               
+
+       for (service = gss_services; *service; service++) {
+               sprintf(service_name, "%s@%s", *service, localname);
+               name_buf.value = service_name;
+               name_buf.length = strlen(name_buf.value) + 1;
+               if (debug) syslog(LOG_INFO, "importing <%s>", service_name);
+               maj_stat = gss_import_name(&min_stat, &name_buf, 
+                                          gss_nt_service_name,
+                                          &server_name);
+               if (maj_stat != GSS_S_COMPLETE) {
+                       reply_gss_error(501, maj_stat, min_stat,
+                                       "importing name");
+                       syslog(LOG_ERR, "gssapi error importing name");
+                       return 0;
+               }
+
+               maj_stat = gss_acquire_cred(&min_stat, server_name, 0,
+                                           GSS_C_NULL_OID_SET, GSS_C_ACCEPT,
+                                           &server_creds, NULL, NULL);
+               if (maj_stat != GSS_S_COMPLETE) {
+                       reply_gss_error(501, maj_stat, min_stat,
+                                       "acquiring credentials");
+                       syslog(LOG_ERR, "gssapi error acquiring credentials");
+                       return 0;
+               }
+               if (server_creds == GSS_C_NO_CREDENTIAL) {
+                       syslog(LOG_ERR, "acquire return GSS_C_NO_CREDENTIAL");
+               }
+               (void) gss_release_name(&min_stat, &server_name);
+               break;
+       }
+
+       gcontext = GSS_C_NO_CONTEXT;
+
+       maj_stat = gss_accept_sec_context(&min_stat,
+                                         &gcontext, /* context_handle */
+                                         server_creds, /* verifier_cred_handle */
+                                         &tok, /* input_token */
+                                         &chan, /* channel bindings */
+                                         &client, /* src_name */
+                                         &mechid, /* mech_type */
+                                         &out_tok, /* output_token */
+                                         &ret_flags,
+                                         NULL,         /* ignore time_rec */
+                                         NULL   /* ignore del_cred_handle */
+                                         );
+       if (maj_stat!=GSS_S_COMPLETE && maj_stat!=GSS_S_CONTINUE_NEEDED) {
+               reply_gss_error(535, maj_stat, min_stat,
+                               "accepting context");
+               syslog(LOG_ERR, "failed accepting context");
+               (void) gss_release_cred(&min_stat, &server_creds);
+               return 0;
+       }
+
+       if (out_tok.length) {
+               if (kerror = radix_encode(out_tok.value, gbuf, &out_tok.length, 0)) {
+                       secure_error("Couldn't encode ADAT reply (%s)",
+                                    radix_error(kerror));
+                       syslog(LOG_ERR, "couldn't encode ADAT reply");
+                       return(0);
+               }
+               if (maj_stat == GSS_S_COMPLETE) {
+                 reply(235, "ADAT=%s", gbuf);
+                 replied = 1;
+               } else {
+                 /* If the server accepts the security data, and
+                    requires additional data, it should respond with
+                    reply code 335. */
+                 reply(335, "ADAT=%s", gbuf);
+               }
+               (void) gss_release_buffer(&min_stat, &out_tok);
+       }
+       if (maj_stat == GSS_S_COMPLETE) {
+               /* GSSAPI authentication succeeded */
+               maj_stat = gss_display_name(&min_stat, client, &client_name, 
+                                           &mechid);
+               if (maj_stat != GSS_S_COMPLETE) {
+                       /* "If the server rejects the security data (if 
+                          a checksum fails, for instance), it should 
+                          respond with reply code 535." */
+                       reply_gss_error(535, maj_stat, min_stat,
+                                       "extracting GSSAPI identity name");
+                       syslog(LOG_ERR, "gssapi error extracting identity");
+                       (void) gss_release_cred(&min_stat, &server_creds);
+                       return 0;
+               }
+               /* If the server accepts the security data, but does
+                  not require any additional data (i.e., the security
+                  data exchange has completed successfully), it must
+                  respond with reply code 235. */
+               if (!replied) reply(235, "GSSAPI Authentication succeeded");
+
+               auth_type = temp_auth_type;
+               temp_auth_type = NULL;
+
+               (void) gss_release_cred(&min_stat, &server_creds);
+               return(1);
+       } else if (maj_stat == GSS_S_CONTINUE_NEEDED) {
+               /* If the server accepts the security data, and
+                  requires additional data, it should respond with
+                  reply code 335. */
+               reply(335, "more data needed");
+               (void) gss_release_cred(&min_stat, &server_creds);
+               return(0);
+       } else {
+               /* "If the server rejects the security data (if 
+                  a checksum fails, for instance), it should 
+                  respond with reply code 535." */
+               reply_gss_error(535, maj_stat, min_stat, 
+                               "GSSAPI failed processing ADAT");
+               syslog(LOG_ERR, "GSSAPI failed prossing ADAT");
+               (void) gss_release_cred(&min_stat, &server_creds);
+               return(0);
+       }
+    }
+#endif /* GSSAPI */
+       /* Other auth types go here ... */
+       /* Also need to check authorization, but that is done in user() */
+}
+
+static char *onefile[] = {
+       "",
+       0
+};
+
+/* returns:
+ *     n>=0 on success
+ *     -1 on error
+ *     -2 on security error
+ */
+secure_fprintf(stream, fmt, p1, p2, p3, p4, p5)
+FILE *stream;
+char *fmt;
+{
+       char s[FTP_BUFSIZ];
+
+       if (level == PROT_C)
+               return(fprintf(stream, fmt, p1, p2, p3, p4, p5));
+       sprintf(s, fmt, p1, p2, p3, p4, p5);
+       return(secure_write(fileno(stream), s, strlen(s)));
+}
+
+send_file_list(whichfiles)
+       char *whichfiles;
+{
+       struct stat st;
+       DIR *dirp = NULL;
+       struct dirent *dir;
+       FILE *dout = NULL;
+       register char **dirlist, *dirname;
+       int simple = 0;
+       char *strpbrk();
+       int ret = 0;
+
+       if (strpbrk(whichfiles, "~{[*?") != NULL) {
+               extern char **ftpglob(), *globerr;
+
+               globerr = NULL;
+               dirlist = ftpglob(whichfiles);
+               if (globerr != NULL) {
+                       reply(550, globerr);
+                       return;
+               } else if (dirlist == NULL) {
+                       errno = ENOENT;
+                       perror_reply(550, whichfiles);
+                       return;
+               }
+       } else {
+               onefile[0] = whichfiles;
+               dirlist = onefile;
+               simple = 1;
+       }
+
+       if (setjmp(urgcatch)) {
+               transflag = 0;
+               return;
+       }
+       while (dirname = *dirlist++) {
+               if (stat(dirname, &st) < 0) {
+                       /*
+                        * If user typed "ls -l", etc, and the client
+                        * used NLST, do what the user meant.
+                        */
+                       if (dirname[0] == '-' && *dirlist == NULL &&
+                           transflag == 0) {
+                               retrieve("/bin/ls %s", dirname);
+                               return;
+                       }
+                       perror_reply(550, whichfiles);
+                       if (dout != NULL) {
+                               (void) fclose(dout);
+                               transflag = 0;
+                               data = -1;
+                               pdata = -1;
+                       }
+                       return;
+               }
+
+               if ((st.st_mode&S_IFMT) == S_IFREG) {
+                       if (dout == NULL) {
+                               dout = dataconn("file list", (off_t)-1, "w");
+                               if (dout == NULL)
+                                       return;
+                               transflag++;
+                       }
+                       if ((ret = secure_fprintf(dout, "%s%s\n", dirname,
+                               type == TYPE_A ? "\r" : "")) < 0)
+                                       goto data_err;
+                       byte_count += strlen(dirname) + 1;
+                       continue;
+               } else if ((st.st_mode&S_IFMT) != S_IFDIR)
+                       continue;
+
+               if ((dirp = opendir(dirname)) == NULL)
+                       continue;
+
+               while ((dir = readdir(dirp)) != NULL) {
+                       char nbuf[MAXPATHLEN];
+
+                       if (dir->d_name[0] == '.' && dir->d_name[1] == '\0')
+                               continue;
+                       if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
+                           dir->d_name[2] == '\0')
+                               continue;
+
+                       sprintf(nbuf, "%s/%s", dirname, dir->d_name);
+
+                       /*
+                        * We have to do a stat to insure it's
+                        * not a directory or special file.
+                        */
+                       if (simple || (stat(nbuf, &st) == 0 &&
+                           (st.st_mode&S_IFMT) == S_IFREG)) {
+                               if (dout == NULL) {
+                                       dout = dataconn("file list", (off_t)-1,
+                                               "w");
+                                       if (dout == NULL)
+                                               return;
+                                       transflag++;
+                               }
+                               if (nbuf[0] == '.' && nbuf[1] == '/')
+                               {
+                                       if ((ret = secure_fprintf(dout, "%s%s\n", &nbuf[2],
+                                               type == TYPE_A ? "\r" : "")) < 0)
+                                                       goto data_err;
+                               }
+                               else
+                                       if ((ret = secure_fprintf(dout, "%s%s\n", nbuf,
+                                               type == TYPE_A ? "\r" : "")) < 0)
+                                                       goto data_err;
+                               byte_count += strlen(nbuf) + 1;
+                       }
+               }
+               (void) closedir(dirp);
+       }
+       ret = secure_write(fileno(dout), "", 0);
+data_err:
+       if (dout == NULL)
+               reply(550, "No files found.");
+       else if (ferror(dout) != 0 || ret == EOF)
+               perror_reply(550, "Data connection");
+       else if (ret != -2)
+               reply(226, "Transfer complete.");
+
+       transflag = 0;
+       if (dout != NULL)
+               (void) fclose(dout);
+       data = -1;
+       pdata = -1;
+}
+
+#ifdef SETPROCTITLE
+/*
+ * clobber argv so ps will show what we're doing.
+ * (stolen from sendmail)
+ * warning, since this is usually started from inetd.conf, it
+ * often doesn't have much of an environment or arglist to overwrite.
+ */
+
+setproctitle(buf)
+char *buf;
+{
+       register char *p, *bp, ch;
+       register int i;
+
+       /* make ps print our process name */
+       p = Argv[0];
+       *p++ = '-';
+
+       i = strlen(buf);
+       if (i > LastArgv - p - 2) {
+               i = LastArgv - p - 2;
+               buf[i] = '\0';
+       }
+       bp = buf;
+       while (ch = *bp++)
+               if (ch != '\n' && ch != '\r')
+                       *p++ = ch;
+       while (p < LastArgv)
+               *p++ = ' ';
+}
+#endif /* SETPROCTITLE */
+#ifdef GSSAPI
+reply_gss_error(code, maj_stat, min_stat, s)
+int code;
+OM_uint32 maj_stat, min_stat;
+char *s;
+{
+       /* a lot of work just to report the error */
+       OM_uint32 gmaj_stat, gmin_stat;
+       gss_buffer_desc msg;
+       int msg_ctx;
+       msg_ctx = 0;
+       while (!msg_ctx) {
+               gmaj_stat = gss_display_status(&gmin_stat, maj_stat,
+                                              GSS_C_GSS_CODE,
+                                              GSS_C_NULL_OID,
+                                              &msg_ctx, &msg);
+               if ((gmaj_stat == GSS_S_COMPLETE)||
+                   (gmaj_stat == GSS_S_CONTINUE_NEEDED)) {
+                       lreply(code, "GSSAPI error major: %s", 
+                              (char*)msg.value);
+                       (void) gss_release_buffer(&gmin_stat, &msg);
+               }
+               if (gmaj_stat != GSS_S_CONTINUE_NEEDED)
+                       break;
+       }
+       msg_ctx = 0;
+       while (!msg_ctx) {
+               gmaj_stat = gss_display_status(&gmin_stat, min_stat,
+                                              GSS_C_MECH_CODE,
+                                              GSS_C_NULL_OID,
+                                              &msg_ctx, &msg);
+               if ((gmaj_stat == GSS_S_COMPLETE)||
+                   (gmaj_stat == GSS_S_CONTINUE_NEEDED)) {
+                       lreply(code, "GSSAPI error minor: %s",
+                              (char*)msg.value);
+                       (void) gss_release_buffer(&gmin_stat, &msg);
+               }
+               if (gmaj_stat != GSS_S_CONTINUE_NEEDED)
+                       break;
+       }
+       reply(code, "GSSAPI error: %s", s);
+}
+
+secure_gss_error(maj_stat, min_stat, s)
+OM_uint32 maj_stat, min_stat;
+char *s;
+{
+  return reply_gss_error(535, maj_stat, min_stat, s);
+}
+
+
+#include <krb5.h>
+/* ftpd_userok -- hide details of getting the name and verifying it */
+/* returns 0 for OK */
+ftpd_userok(client_name, name)
+       gss_buffer_t client_name;
+       char *name;
+{
+       int retval = -1;
+       krb5_boolean k5ret;
+       krb5_context kc;
+       krb5_principal p;
+       krb5_error_code kerr;
+       krb5_init_context(&kc);
+
+       kerr = krb5_parse_name(kc, client_name->value, &p);
+       if (kerr) { retval = -1; goto fail; }
+       k5ret = krb5_kuserok(kc, p, name);
+       if (k5ret == TRUE)
+               retval = 0;
+       else 
+               retval = 1;
+       krb5_free_principal(kc, p);
+ fail:
+       krb5_free_context(kc);
+       return retval;
+}
+#endif /* GSSAPI */
diff --git a/src/appl/gssftp/ftpd/logwtmp.c b/src/appl/gssftp/ftpd/logwtmp.c
new file mode 100644 (file)
index 0000000..36f7e13
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 1988 The 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[] = "@(#)logwtmp.c  5.7 (Berkeley) 2/25/91";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <utmp.h>
+#include <unistd.h>
+#include <string.h>
+
+#ifdef WTMP_FILE
+#define WTMPFILE WTMP_FILE
+#endif
+
+#ifndef WTMPFILE
+#define        WTMPFILE        "/usr/adm/wtmp"
+#endif
+
+static int fd = -1;
+
+/*
+ * Modified version of logwtmp that holds wtmp file open
+ * after first call, for use with ftp (which may chroot
+ * after login, but before logout).
+ */
+logwtmp(line, name, host)
+       char *line, *name, *host;
+{
+       struct utmp ut;
+       struct stat buf;
+       time_t time();
+
+       if (fd < 0 && (fd = open(WTMPFILE, O_WRONLY|O_APPEND, 0)) < 0)
+               return;
+       if (fstat(fd, &buf) == 0) {
+               (void)strncpy(ut.ut_line, line, sizeof(ut.ut_line));
+               (void)strncpy(ut.ut_name, name, sizeof(ut.ut_name));
+#ifndef NO_UT_HOST
+               (void)strncpy(ut.ut_host, host, sizeof(ut.ut_host));
+#endif
+               (void)time(&ut.ut_time);
+               if (write(fd, (char *)&ut, sizeof(struct utmp)) !=
+                   sizeof(struct utmp))
+                       (void)ftruncate(fd, buf.st_size);
+       }
+}
diff --git a/src/appl/gssftp/ftpd/pathnames.h b/src/appl/gssftp/ftpd/pathnames.h
new file mode 100644 (file)
index 0000000..8669e46
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 1989 The 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.
+ *
+ *     @(#)pathnames.h 5.2 (Berkeley) 6/1/90
+ */
+
+#define        _PATH_FTPUSERS  "/etc/ftpusers"
diff --git a/src/appl/gssftp/ftpd/popen.c b/src/appl/gssftp/ftpd/popen.c
new file mode 100644 (file)
index 0000000..89f29a2
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 1988 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software written by Ken Arnold and
+ * published in UNIX Review, Vol. 6, No. 8.
+ *
+ * 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[] = "@(#)popen.c    5.9 (Berkeley) 2/25/91";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_VFORK_H
+#include <vfork.h>
+#endif
+
+/*
+ * Special version of popen which avoids call to shell.  This insures noone
+ * may create a pipe to a hidden program as a side effect of a list or dir
+ * command.
+ */
+static int *pids;
+static int fds;
+
+
+FILE *
+ftpd_popen(program, type)
+       char *program, *type;
+{
+       register char *cp;
+       FILE *iop;
+       int argc, gargc, pdes[2], pid;
+       char **pop, *argv[100], *gargv[1000], *vv[2];
+       extern char **ftpglob(), **copyblk();
+
+       if (*type != 'r' && *type != 'w' || type[1])
+               return(NULL);
+
+       if (!pids) {
+               if ((fds = getdtablesize()) <= 0)
+                       return(NULL);
+               if ((pids = (int *)malloc((u_int)(fds * sizeof(int)))) == NULL)
+                       return(NULL);
+               memset((char *)pids, 0, fds * sizeof(int));
+       }
+       if (pipe(pdes) < 0)
+               return(NULL);
+
+       /* break up string into pieces */
+       for (argc = 0, cp = program;; cp = NULL)
+               if (!(argv[argc++] = strtok(cp, " \t\n")))
+                       break;
+       for (argc = 0; argv[argc]; argc++) argv[argc] = strdup(argv[argc]);
+
+       /* glob each piece */
+       gargv[0] = argv[0];
+       for (gargc = argc = 1; argv[argc]; argc++) {
+               if (!(pop = ftpglob(argv[argc]))) {     /* globbing failed */
+                       vv[0] = argv[argc];
+                       vv[1] = NULL;
+                       pop = copyblk(vv);
+               }
+               argv[argc] = (char *)pop;               /* save to free later */
+               while (*pop && gargc < 1000)
+                       gargv[gargc++] = *pop++;
+       }
+       gargv[gargc] = NULL;
+
+       iop = NULL;
+       switch(pid = vfork()) {
+       case -1:                        /* error */
+               (void)close(pdes[0]);
+               (void)close(pdes[1]);
+               goto pfree;
+               /* NOTREACHED */
+       case 0:                         /* child */
+               if (*type == 'r') {
+                       if (pdes[1] != 1) {
+                               dup2(pdes[1], 1);
+                               dup2(pdes[1], 2);       /* stderr, too! */
+                               (void)close(pdes[1]);
+                       }
+                       (void)close(pdes[0]);
+               } else {
+                       if (pdes[0] != 0) {
+                               dup2(pdes[0], 0);
+                               (void)close(pdes[0]);
+                       }
+                       (void)close(pdes[1]);
+               }
+               execv(gargv[0], gargv);
+               _exit(1);
+       }
+       /* parent; assume fdopen can't fail...  */
+       if (*type == 'r') {
+               iop = fdopen(pdes[0], type);
+               (void)close(pdes[1]);
+       } else {
+               iop = fdopen(pdes[1], type);
+               (void)close(pdes[0]);
+       }
+       pids[fileno(iop)] = pid;
+
+pfree: for (argc = 1; argv[argc] != NULL; argc++) {
+               blkfree((char **)argv[argc]);
+               free((char *)argv[argc]);
+       }
+       return(iop);
+}
+
+ftpd_pclose(iop)
+       FILE *iop;
+{
+       register int fdes;
+#ifdef USE_SIGPROCMASK
+       sigset_t old, new;
+#else
+       int omask;
+#endif
+#ifdef WAIT_USES_INT
+       int stat_loc;
+#else
+       union wait stat_loc;
+#endif
+       int pid;
+
+       /*
+        * pclose returns -1 if stream is not associated with a
+        * `popened' command, or, if already `pclosed'.
+        */
+       if (pids == 0 || pids[fdes = fileno(iop)] == 0)
+               return(-1);
+       (void)fclose(iop);
+#ifdef USE_SIGPROCMASK
+       sigemptyset(&old);
+       sigemptyset(&new);
+       sigaddset(&new,SIGINT);
+       sigaddset(&new,SIGQUIT);
+       sigaddset(&new,SIGHUP);
+       sigprocmask(SIG_BLOCK, &new, &old);
+       while ((pid = wait((int *)&stat_loc)) != pids[fdes] && pid != -1);
+       sigprocmask(SIG_SETMASK, &old, NULL);
+#else
+       omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
+       while ((pid = wait((int *)&stat_loc)) != pids[fdes] && pid != -1);
+       sigsetmask(omask);
+#endif
+       pids[fdes] = 0;
+#ifdef WAIT_USES_INT
+       return(pid == -1 ? -1 : stat_loc);
+#else
+       return(pid == -1 ? -1 : stat_loc.w_status);
+#endif
+}
diff --git a/src/appl/gssftp/ftpd/secure.h b/src/appl/gssftp/ftpd/secure.h
new file mode 100644 (file)
index 0000000..d734479
--- /dev/null
@@ -0,0 +1,4 @@
+#define CRED_DECL      extern AUTH_DAT kdata;
+#define SESSION                &kdata.session
+#define myaddr         ctrl_addr
+#define hisaddr                data_dest
diff --git a/src/appl/gssftp/ftpd/vers.c b/src/appl/gssftp/ftpd/vers.c
new file mode 100644 (file)
index 0000000..76846bd
--- /dev/null
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 1990 The 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[] = "@(#)vers.c     5.1 (Berkeley) 6/24/90";
+#endif /* not lint */
+
+char version[] = "Version 5.60";